accounts_spec.rb 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. # frozen_string_literal: true
  2. require 'rails_helper'
  3. RSpec.describe '/api/v1/accounts' do
  4. let(:user) { Fabricate(:user) }
  5. let(:scopes) { '' }
  6. let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
  7. let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
  8. describe 'GET /api/v1/accounts?id[]=:id' do
  9. let(:account) { Fabricate(:account) }
  10. let(:other_account) { Fabricate(:account) }
  11. let(:scopes) { 'read:accounts' }
  12. it 'returns expected response' do
  13. get '/api/v1/accounts', headers: headers, params: { id: [account.id, other_account.id, 123_123] }
  14. expect(response).to have_http_status(200)
  15. expect(response.content_type)
  16. .to start_with('application/json')
  17. expect(response.parsed_body).to contain_exactly(
  18. hash_including(id: account.id.to_s),
  19. hash_including(id: other_account.id.to_s)
  20. )
  21. end
  22. end
  23. describe 'GET /api/v1/accounts/:id' do
  24. context 'when logged out' do
  25. let(:account) { Fabricate(:account) }
  26. it 'returns account entity as 200 OK', :aggregate_failures do
  27. get "/api/v1/accounts/#{account.id}"
  28. expect(response).to have_http_status(200)
  29. expect(response.content_type)
  30. .to start_with('application/json')
  31. expect(response.parsed_body[:id]).to eq(account.id.to_s)
  32. end
  33. end
  34. context 'when the account does not exist' do
  35. it 'returns http not found' do
  36. get '/api/v1/accounts/1'
  37. expect(response).to have_http_status(404)
  38. expect(response.content_type)
  39. .to start_with('application/json')
  40. expect(response.parsed_body[:error]).to eq('Record not found')
  41. end
  42. end
  43. context 'when logged in' do
  44. subject do
  45. get "/api/v1/accounts/#{account.id}", headers: headers
  46. end
  47. let(:account) { Fabricate(:account) }
  48. let(:scopes) { 'read:accounts' }
  49. it 'returns account entity as 200 OK', :aggregate_failures do
  50. subject
  51. expect(response).to have_http_status(200)
  52. expect(response.content_type)
  53. .to start_with('application/json')
  54. expect(response.parsed_body[:id]).to eq(account.id.to_s)
  55. end
  56. it_behaves_like 'forbidden for wrong scope', 'write:statuses'
  57. end
  58. end
  59. describe 'POST /api/v1/accounts' do
  60. subject do
  61. post '/api/v1/accounts', headers: headers, params: { username: 'test', password: '12345678', email: 'hello@world.tld', agreement: agreement }
  62. end
  63. let(:client_app) { Fabricate(:application) }
  64. let(:token) { Doorkeeper::AccessToken.find_or_create_for(application: client_app, resource_owner: nil, scopes: 'read write', use_refresh_token: false) }
  65. let(:agreement) { nil }
  66. context 'when given truthy agreement' do
  67. let(:agreement) { 'true' }
  68. it 'creates a user', :aggregate_failures do
  69. subject
  70. expect(response).to have_http_status(200)
  71. expect(response.content_type)
  72. .to start_with('application/json')
  73. expect(response.parsed_body[:access_token]).to_not be_blank
  74. user = User.find_by(email: 'hello@world.tld')
  75. expect(user).to_not be_nil
  76. expect(user.created_by_application_id).to eq client_app.id
  77. end
  78. end
  79. context 'when given no agreement' do
  80. it 'returns http unprocessable entity' do
  81. subject
  82. expect(response).to have_http_status(422)
  83. expect(response.content_type)
  84. .to start_with('application/json')
  85. end
  86. end
  87. end
  88. describe 'POST /api/v1/accounts/:id/follow' do
  89. let(:scopes) { 'write:follows' }
  90. let(:other_account) { Fabricate(:account, username: 'bob', locked: locked) }
  91. context 'when posting to an other account' do
  92. subject do
  93. post "/api/v1/accounts/#{other_account.id}/follow", headers: headers
  94. end
  95. context 'with unlocked account' do
  96. let(:locked) { false }
  97. it 'creates a following relation between user and target user', :aggregate_failures do
  98. subject
  99. expect(response).to have_http_status(200)
  100. expect(response.content_type)
  101. .to start_with('application/json')
  102. expect(response.parsed_body)
  103. .to include(
  104. following: true,
  105. requested: false
  106. )
  107. expect(user.account.following?(other_account)).to be true
  108. end
  109. it_behaves_like 'forbidden for wrong scope', 'read:accounts'
  110. end
  111. context 'with locked account' do
  112. let(:locked) { true }
  113. it 'creates a follow request relation between user and target user', :aggregate_failures do
  114. subject
  115. expect(response).to have_http_status(200)
  116. expect(response.content_type)
  117. .to start_with('application/json')
  118. expect(response.parsed_body)
  119. .to include(
  120. following: false,
  121. requested: true
  122. )
  123. expect(user.account.requested?(other_account)).to be true
  124. end
  125. it_behaves_like 'forbidden for wrong scope', 'read:accounts'
  126. end
  127. end
  128. context 'when user tries to follow their own account' do
  129. subject do
  130. post "/api/v1/accounts/#{other_account.id}/follow", headers: headers
  131. end
  132. let(:locked) { false }
  133. let(:other_account) { user.account }
  134. it 'returns http forbidden and error message' do
  135. subject
  136. error_msg = I18n.t('accounts.self_follow_error')
  137. expect(response).to have_http_status(403)
  138. expect(response.parsed_body[:error]).to eq(error_msg)
  139. end
  140. it_behaves_like 'forbidden for wrong scope', 'read:accounts'
  141. end
  142. context 'when modifying follow options' do
  143. let(:locked) { false }
  144. before do
  145. user.account.follow!(other_account, reblogs: false, notify: false)
  146. end
  147. it 'changes reblogs option' do
  148. post "/api/v1/accounts/#{other_account.id}/follow", headers: headers, params: { reblogs: true }
  149. expect(response.parsed_body).to include({
  150. following: true,
  151. showing_reblogs: true,
  152. notifying: false,
  153. })
  154. end
  155. it 'changes notify option' do
  156. post "/api/v1/accounts/#{other_account.id}/follow", headers: headers, params: { notify: true }
  157. expect(response.parsed_body).to include({
  158. following: true,
  159. showing_reblogs: false,
  160. notifying: true,
  161. })
  162. end
  163. it 'changes languages option' do
  164. post "/api/v1/accounts/#{other_account.id}/follow", headers: headers, params: { languages: %w(en es) }
  165. expect(response.parsed_body).to include({
  166. following: true,
  167. showing_reblogs: false,
  168. notifying: false,
  169. languages: match_array(%w(en es)),
  170. })
  171. end
  172. end
  173. end
  174. describe 'POST /api/v1/accounts/:id/unfollow' do
  175. subject do
  176. post "/api/v1/accounts/#{other_account.id}/unfollow", headers: headers
  177. end
  178. let(:scopes) { 'write:follows' }
  179. let(:other_account) { Fabricate(:account, username: 'bob') }
  180. before do
  181. user.account.follow!(other_account)
  182. end
  183. it 'removes the following relation between user and target user', :aggregate_failures do
  184. subject
  185. expect(response).to have_http_status(200)
  186. expect(response.content_type)
  187. .to start_with('application/json')
  188. expect(user.account.following?(other_account)).to be false
  189. end
  190. it_behaves_like 'forbidden for wrong scope', 'read:accounts'
  191. end
  192. describe 'POST /api/v1/accounts/:id/remove_from_followers' do
  193. subject do
  194. post "/api/v1/accounts/#{other_account.id}/remove_from_followers", headers: headers
  195. end
  196. let(:scopes) { 'write:follows' }
  197. let(:other_account) { Fabricate(:account, username: 'bob') }
  198. before do
  199. other_account.follow!(user.account)
  200. end
  201. it 'removes the followed relation between user and target user', :aggregate_failures do
  202. subject
  203. expect(response).to have_http_status(200)
  204. expect(response.content_type)
  205. .to start_with('application/json')
  206. expect(user.account.followed_by?(other_account)).to be false
  207. end
  208. it_behaves_like 'forbidden for wrong scope', 'read:accounts'
  209. end
  210. describe 'POST /api/v1/accounts/:id/block' do
  211. subject do
  212. post "/api/v1/accounts/#{other_account.id}/block", headers: headers
  213. end
  214. let(:scopes) { 'write:blocks' }
  215. let(:other_account) { Fabricate(:account, username: 'bob') }
  216. before do
  217. user.account.follow!(other_account)
  218. end
  219. it 'creates a blocking relation', :aggregate_failures do
  220. subject
  221. expect(response).to have_http_status(200)
  222. expect(response.content_type)
  223. .to start_with('application/json')
  224. expect(user.account.following?(other_account)).to be false
  225. expect(user.account.blocking?(other_account)).to be true
  226. end
  227. it_behaves_like 'forbidden for wrong scope', 'read:accounts'
  228. end
  229. describe 'POST /api/v1/accounts/:id/unblock' do
  230. subject do
  231. post "/api/v1/accounts/#{other_account.id}/unblock", headers: headers
  232. end
  233. let(:scopes) { 'write:blocks' }
  234. let(:other_account) { Fabricate(:account, username: 'bob') }
  235. before do
  236. user.account.block!(other_account)
  237. end
  238. it 'removes the blocking relation between user and target user', :aggregate_failures do
  239. subject
  240. expect(response).to have_http_status(200)
  241. expect(response.content_type)
  242. .to start_with('application/json')
  243. expect(user.account.blocking?(other_account)).to be false
  244. end
  245. it_behaves_like 'forbidden for wrong scope', 'read:accounts'
  246. end
  247. describe 'POST /api/v1/accounts/:id/mute' do
  248. subject do
  249. post "/api/v1/accounts/#{other_account.id}/mute", headers: headers
  250. end
  251. let(:scopes) { 'write:mutes' }
  252. let(:other_account) { Fabricate(:account, username: 'bob') }
  253. before do
  254. user.account.follow!(other_account)
  255. end
  256. it 'mutes notifications', :aggregate_failures do
  257. subject
  258. expect(response).to have_http_status(200)
  259. expect(response.content_type)
  260. .to start_with('application/json')
  261. expect(user.account.following?(other_account)).to be true
  262. expect(user.account.muting?(other_account)).to be true
  263. expect(user.account.muting_notifications?(other_account)).to be true
  264. end
  265. it_behaves_like 'forbidden for wrong scope', 'read:accounts'
  266. end
  267. describe 'POST /api/v1/accounts/:id/mute with notifications set to false' do
  268. subject do
  269. post "/api/v1/accounts/#{other_account.id}/mute", headers: headers, params: { notifications: false }
  270. end
  271. let(:scopes) { 'write:mutes' }
  272. let(:other_account) { Fabricate(:account, username: 'bob') }
  273. before do
  274. user.account.follow!(other_account)
  275. end
  276. it 'does not mute notifications', :aggregate_failures do
  277. subject
  278. expect(response).to have_http_status(200)
  279. expect(response.content_type)
  280. .to start_with('application/json')
  281. expect(user.account.following?(other_account)).to be true
  282. expect(user.account.muting?(other_account)).to be true
  283. expect(user.account.muting_notifications?(other_account)).to be false
  284. end
  285. it_behaves_like 'forbidden for wrong scope', 'read:accounts'
  286. end
  287. describe 'POST /api/v1/accounts/:id/mute with nonzero duration set' do
  288. subject do
  289. post "/api/v1/accounts/#{other_account.id}/mute", headers: headers, params: { duration: 300 }
  290. end
  291. let(:scopes) { 'write:mutes' }
  292. let(:other_account) { Fabricate(:account, username: 'bob') }
  293. before do
  294. user.account.follow!(other_account)
  295. end
  296. it 'mutes notifications', :aggregate_failures do
  297. subject
  298. expect(response).to have_http_status(200)
  299. expect(response.content_type)
  300. .to start_with('application/json')
  301. expect(user.account.following?(other_account)).to be true
  302. expect(user.account.muting?(other_account)).to be true
  303. expect(user.account.muting_notifications?(other_account)).to be true
  304. end
  305. it_behaves_like 'forbidden for wrong scope', 'read:accounts'
  306. end
  307. describe 'POST /api/v1/accounts/:id/unmute' do
  308. subject do
  309. post "/api/v1/accounts/#{other_account.id}/unmute", headers: headers
  310. end
  311. let(:scopes) { 'write:mutes' }
  312. let(:other_account) { Fabricate(:account, username: 'bob') }
  313. before do
  314. user.account.mute!(other_account)
  315. end
  316. it 'removes the muting relation between user and target user', :aggregate_failures do
  317. subject
  318. expect(response).to have_http_status(200)
  319. expect(response.content_type)
  320. .to start_with('application/json')
  321. expect(user.account.muting?(other_account)).to be false
  322. end
  323. it_behaves_like 'forbidden for wrong scope', 'read:accounts'
  324. end
  325. end