PageRenderTime 51ms CodeModel.GetById 10ms RepoModel.GetById 1ms app.codeStats 0ms

/spec/requests/api/members_spec.rb

https://gitlab.com/dcondrey/gitlab-ee
Ruby | 363 lines | 288 code | 69 blank | 6 comment | 8 complexity | b7f6f622d502c3a7567819668b3b408f MD5 | raw file
  1. require 'spec_helper'
  2. describe API::Members, api: true do
  3. include ApiHelpers
  4. let(:master) { create(:user) }
  5. let(:developer) { create(:user) }
  6. let(:access_requester) { create(:user) }
  7. let(:stranger) { create(:user) }
  8. let(:project) do
  9. project = create(:project, :public, creator_id: master.id, namespace: master.namespace)
  10. project.team << [developer, :developer]
  11. project.team << [master, :master]
  12. project.request_access(access_requester)
  13. project
  14. end
  15. let!(:group) do
  16. group = create(:group, :public)
  17. group.add_developer(developer)
  18. group.add_owner(master)
  19. group.request_access(access_requester)
  20. group
  21. end
  22. shared_examples 'GET /:sources/:id/members' do |source_type|
  23. context "with :sources == #{source_type.pluralize}" do
  24. it_behaves_like 'a 404 response when source is private' do
  25. let(:route) { get api("/#{source_type.pluralize}/#{source.id}/members", stranger) }
  26. end
  27. %i[master developer access_requester stranger].each do |type|
  28. context "when authenticated as a #{type}" do
  29. it 'returns 200' do
  30. user = public_send(type)
  31. get api("/#{source_type.pluralize}/#{source.id}/members", user)
  32. expect(response).to have_http_status(200)
  33. expect(json_response.size).to eq(2)
  34. expect(json_response.map { |u| u['id'] }).to match_array [master.id, developer.id]
  35. end
  36. end
  37. end
  38. it 'does not return invitees' do
  39. create(:"#{source_type}_member", invite_token: '123', invite_email: 'test@abc.com', source: source, user: nil)
  40. get api("/#{source_type.pluralize}/#{source.id}/members", developer)
  41. expect(response).to have_http_status(200)
  42. expect(json_response.size).to eq(2)
  43. expect(json_response.map { |u| u['id'] }).to match_array [master.id, developer.id]
  44. end
  45. it 'finds members with query string' do
  46. get api("/#{source_type.pluralize}/#{source.id}/members", developer), query: master.username
  47. expect(response).to have_http_status(200)
  48. expect(json_response.count).to eq(1)
  49. expect(json_response.first['username']).to eq(master.username)
  50. end
  51. end
  52. end
  53. shared_examples 'GET /:sources/:id/members/:user_id' do |source_type|
  54. context "with :sources == #{source_type.pluralize}" do
  55. it_behaves_like 'a 404 response when source is private' do
  56. let(:route) { get api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", stranger) }
  57. end
  58. context 'when authenticated as a non-member' do
  59. %i[access_requester stranger].each do |type|
  60. context "as a #{type}" do
  61. it 'returns 200' do
  62. user = public_send(type)
  63. get api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", user)
  64. expect(response).to have_http_status(200)
  65. # User attributes
  66. expect(json_response['id']).to eq(developer.id)
  67. expect(json_response['name']).to eq(developer.name)
  68. expect(json_response['username']).to eq(developer.username)
  69. expect(json_response['state']).to eq(developer.state)
  70. expect(json_response['avatar_url']).to eq(developer.avatar_url)
  71. expect(json_response['web_url']).to eq(Gitlab::Routing.url_helpers.user_url(developer))
  72. # Member attributes
  73. expect(json_response['access_level']).to eq(Member::DEVELOPER)
  74. end
  75. end
  76. end
  77. end
  78. end
  79. end
  80. shared_examples 'POST /:sources/:id/members' do |source_type|
  81. context "with :sources == #{source_type.pluralize}" do
  82. it_behaves_like 'a 404 response when source is private' do
  83. let(:route) do
  84. post api("/#{source_type.pluralize}/#{source.id}/members", stranger),
  85. user_id: access_requester.id, access_level: Member::MASTER
  86. end
  87. end
  88. context 'when authenticated as a non-member or member with insufficient rights' do
  89. %i[access_requester stranger developer].each do |type|
  90. context "as a #{type}" do
  91. it 'returns 403' do
  92. user = public_send(type)
  93. post api("/#{source_type.pluralize}/#{source.id}/members", user),
  94. user_id: access_requester.id, access_level: Member::MASTER
  95. expect(response).to have_http_status(403)
  96. end
  97. end
  98. end
  99. end
  100. context 'when authenticated as a master/owner' do
  101. context 'and new member is already a requester' do
  102. it 'transforms the requester into a proper member' do
  103. expect do
  104. post api("/#{source_type.pluralize}/#{source.id}/members", master),
  105. user_id: access_requester.id, access_level: Member::MASTER
  106. expect(response).to have_http_status(201)
  107. end.to change { source.members.count }.by(1)
  108. expect(source.requesters.count).to eq(0)
  109. expect(json_response['id']).to eq(access_requester.id)
  110. expect(json_response['access_level']).to eq(Member::MASTER)
  111. end
  112. end
  113. it 'creates a new member' do
  114. expect do
  115. post api("/#{source_type.pluralize}/#{source.id}/members", master),
  116. user_id: stranger.id, access_level: Member::DEVELOPER, expires_at: '2016-08-05'
  117. expect(response).to have_http_status(201)
  118. end.to change { source.members.count }.by(1)
  119. expect(json_response['id']).to eq(stranger.id)
  120. expect(json_response['access_level']).to eq(Member::DEVELOPER)
  121. expect(json_response['expires_at']).to eq('2016-08-05')
  122. end
  123. end
  124. it "returns #{source_type == 'project' ? 201 : 409} if member already exists" do
  125. post api("/#{source_type.pluralize}/#{source.id}/members", master),
  126. user_id: master.id, access_level: Member::MASTER
  127. expect(response).to have_http_status(source_type == 'project' ? 201 : 409)
  128. end
  129. it 'returns 400 when user_id is not given' do
  130. post api("/#{source_type.pluralize}/#{source.id}/members", master),
  131. access_level: Member::MASTER
  132. expect(response).to have_http_status(400)
  133. end
  134. it 'returns 400 when access_level is not given' do
  135. post api("/#{source_type.pluralize}/#{source.id}/members", master),
  136. user_id: stranger.id
  137. expect(response).to have_http_status(400)
  138. end
  139. it 'returns 422 when access_level is not valid' do
  140. post api("/#{source_type.pluralize}/#{source.id}/members", master),
  141. user_id: stranger.id, access_level: 1234
  142. expect(response).to have_http_status(422)
  143. end
  144. end
  145. end
  146. ## EE specific
  147. shared_examples 'POST /projects/:id/members with the project group membership locked' do
  148. context 'project in a group' do
  149. it 'returns a 405 method not allowed error when group membership lock is enabled' do
  150. group_with_membership_locked = create(:group, membership_lock: true)
  151. project = create(:project, group: group_with_membership_locked)
  152. project.group.add_owner(master)
  153. post api("/projects/#{project.id}/members", master),
  154. user_id: developer.id, access_level: Member::MASTER
  155. expect(response.status).to eq 405
  156. end
  157. end
  158. end
  159. ## EE specific
  160. shared_examples 'PUT /:sources/:id/members/:user_id' do |source_type|
  161. context "with :sources == #{source_type.pluralize}" do
  162. it_behaves_like 'a 404 response when source is private' do
  163. let(:route) do
  164. put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", stranger),
  165. access_level: Member::MASTER
  166. end
  167. end
  168. context 'when authenticated as a non-member or member with insufficient rights' do
  169. %i[access_requester stranger developer].each do |type|
  170. context "as a #{type}" do
  171. it 'returns 403' do
  172. user = public_send(type)
  173. put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", user),
  174. access_level: Member::MASTER
  175. expect(response).to have_http_status(403)
  176. end
  177. end
  178. end
  179. end
  180. context 'when authenticated as a master/owner' do
  181. it 'updates the member' do
  182. put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", master),
  183. access_level: Member::MASTER, expires_at: '2016-08-05'
  184. expect(response).to have_http_status(200)
  185. expect(json_response['id']).to eq(developer.id)
  186. expect(json_response['access_level']).to eq(Member::MASTER)
  187. expect(json_response['expires_at']).to eq('2016-08-05')
  188. end
  189. end
  190. it 'returns 409 if member does not exist' do
  191. put api("/#{source_type.pluralize}/#{source.id}/members/123", master),
  192. access_level: Member::MASTER
  193. expect(response).to have_http_status(404)
  194. end
  195. it 'returns 400 when access_level is not given' do
  196. put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", master)
  197. expect(response).to have_http_status(400)
  198. end
  199. it 'returns 422 when access level is not valid' do
  200. put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", master),
  201. access_level: 1234
  202. expect(response).to have_http_status(422)
  203. end
  204. end
  205. end
  206. shared_examples 'DELETE /:sources/:id/members/:user_id' do |source_type|
  207. context "with :sources == #{source_type.pluralize}" do
  208. it_behaves_like 'a 404 response when source is private' do
  209. let(:route) { delete api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", stranger) }
  210. end
  211. context 'when authenticated as a non-member or member with insufficient rights' do
  212. %i[access_requester stranger].each do |type|
  213. context "as a #{type}" do
  214. it 'returns 403' do
  215. user = public_send(type)
  216. delete api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", user)
  217. expect(response).to have_http_status(403)
  218. end
  219. end
  220. end
  221. end
  222. context 'when authenticated as a member and deleting themself' do
  223. it 'deletes the member' do
  224. expect do
  225. delete api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", developer)
  226. expect(response).to have_http_status(200)
  227. end.to change { source.members.count }.by(-1)
  228. end
  229. end
  230. context 'when authenticated as a master/owner' do
  231. context 'and member is a requester' do
  232. it "returns #{source_type == 'project' ? 200 : 404}" do
  233. expect do
  234. delete api("/#{source_type.pluralize}/#{source.id}/members/#{access_requester.id}", master)
  235. expect(response).to have_http_status(source_type == 'project' ? 200 : 404)
  236. end.not_to change { source.requesters.count }
  237. end
  238. end
  239. it 'deletes the member' do
  240. expect do
  241. delete api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", master)
  242. expect(response).to have_http_status(200)
  243. end.to change { source.members.count }.by(-1)
  244. end
  245. end
  246. it "returns #{source_type == 'project' ? 200 : 404} if member does not exist" do
  247. delete api("/#{source_type.pluralize}/#{source.id}/members/123", master)
  248. expect(response).to have_http_status(source_type == 'project' ? 200 : 404)
  249. end
  250. end
  251. end
  252. it_behaves_like 'GET /:sources/:id/members', 'project' do
  253. let(:source) { project }
  254. end
  255. it_behaves_like 'GET /:sources/:id/members', 'group' do
  256. let(:source) { group }
  257. end
  258. it_behaves_like 'GET /:sources/:id/members/:user_id', 'project' do
  259. let(:source) { project }
  260. end
  261. it_behaves_like 'GET /:sources/:id/members/:user_id', 'group' do
  262. let(:source) { group }
  263. end
  264. it_behaves_like 'POST /:sources/:id/members', 'project' do
  265. let(:source) { project }
  266. end
  267. ## EE specific
  268. it_behaves_like 'POST /projects/:id/members with the project group membership locked'
  269. ## EE specific
  270. it_behaves_like 'POST /:sources/:id/members', 'group' do
  271. let(:source) { group }
  272. end
  273. it_behaves_like 'PUT /:sources/:id/members/:user_id', 'project' do
  274. let(:source) { project }
  275. end
  276. it_behaves_like 'PUT /:sources/:id/members/:user_id', 'group' do
  277. let(:source) { group }
  278. end
  279. it_behaves_like 'DELETE /:sources/:id/members/:user_id', 'project' do
  280. let(:source) { project }
  281. end
  282. it_behaves_like 'DELETE /:sources/:id/members/:user_id', 'group' do
  283. let(:source) { group }
  284. end
  285. context 'Adding owner to project' do
  286. it 'returns 403' do
  287. expect do
  288. post api("/projects/#{project.id}/members", master),
  289. user_id: stranger.id, access_level: Member::OWNER
  290. expect(response).to have_http_status(422)
  291. end.to change { project.members.count }.by(0)
  292. end
  293. end
  294. end