notifications_spec.rb 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. # frozen_string_literal: true
  2. require 'rails_helper'
  3. RSpec.describe 'Notifications' do
  4. let(:user) { Fabricate(:user, account_attributes: { username: 'alice' }) }
  5. let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
  6. let(:scopes) { 'read:notifications write:notifications' }
  7. let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
  8. describe 'GET /api/v1/notifications/unread_count', :inline_jobs do
  9. subject do
  10. get '/api/v1/notifications/unread_count', headers: headers, params: params
  11. end
  12. let(:params) { {} }
  13. before do
  14. first_status = PostStatusService.new.call(user.account, text: 'Test')
  15. ReblogService.new.call(Fabricate(:account), first_status)
  16. PostStatusService.new.call(Fabricate(:account), text: 'Hello @alice')
  17. FavouriteService.new.call(Fabricate(:account), first_status)
  18. FavouriteService.new.call(Fabricate(:account), first_status)
  19. FollowService.new.call(Fabricate(:account), user.account)
  20. end
  21. it_behaves_like 'forbidden for wrong scope', 'write write:notifications'
  22. context 'with no options' do
  23. it 'returns expected notifications count' do
  24. subject
  25. expect(response).to have_http_status(200)
  26. expect(response.content_type)
  27. .to start_with('application/json')
  28. expect(response.parsed_body[:count]).to eq 5
  29. end
  30. end
  31. context 'with a read marker' do
  32. before do
  33. id = user.account.notifications.browserable.order(id: :desc).offset(2).first.id
  34. user.markers.create!(timeline: 'notifications', last_read_id: id)
  35. end
  36. it 'returns expected notifications count' do
  37. subject
  38. expect(response).to have_http_status(200)
  39. expect(response.content_type)
  40. .to start_with('application/json')
  41. expect(response.parsed_body[:count]).to eq 2
  42. end
  43. end
  44. context 'with exclude_types param' do
  45. let(:params) { { exclude_types: %w(mention) } }
  46. it 'returns expected notifications count' do
  47. subject
  48. expect(response).to have_http_status(200)
  49. expect(response.content_type)
  50. .to start_with('application/json')
  51. expect(response.parsed_body[:count]).to eq 4
  52. end
  53. end
  54. context 'with a user-provided limit' do
  55. let(:params) { { limit: 2 } }
  56. it 'returns a capped value' do
  57. subject
  58. expect(response).to have_http_status(200)
  59. expect(response.content_type)
  60. .to start_with('application/json')
  61. expect(response.parsed_body[:count]).to eq 2
  62. end
  63. end
  64. context 'when there are more notifications than the limit' do
  65. before do
  66. stub_const('Api::V1::NotificationsController::DEFAULT_NOTIFICATIONS_COUNT_LIMIT', 2)
  67. end
  68. it 'returns a capped value' 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[:count]).to eq Api::V1::NotificationsController::DEFAULT_NOTIFICATIONS_COUNT_LIMIT
  74. end
  75. end
  76. end
  77. describe 'GET /api/v1/notifications', :inline_jobs do
  78. subject do
  79. get '/api/v1/notifications', headers: headers, params: params
  80. end
  81. let(:bob) { Fabricate(:user) }
  82. let(:tom) { Fabricate(:user) }
  83. let(:params) { {} }
  84. before do
  85. first_status = PostStatusService.new.call(user.account, text: 'Test')
  86. ReblogService.new.call(bob.account, first_status)
  87. PostStatusService.new.call(bob.account, text: 'Hello @alice')
  88. PostStatusService.new.call(tom.account, text: 'Hello @alice', visibility: :direct) # Filtered by default
  89. FavouriteService.new.call(bob.account, first_status)
  90. FavouriteService.new.call(tom.account, first_status)
  91. FollowService.new.call(bob.account, user.account)
  92. end
  93. it_behaves_like 'forbidden for wrong scope', 'write write:notifications'
  94. context 'with no options' do
  95. it 'returns expected notification types', :aggregate_failures do
  96. subject
  97. expect(response).to have_http_status(200)
  98. expect(response.content_type)
  99. .to start_with('application/json')
  100. expect(response.parsed_body.size).to eq 5
  101. expect(body_json_types).to include('reblog', 'mention', 'favourite', 'follow')
  102. expect(response.parsed_body.any? { |x| x[:filtered] }).to be false
  103. end
  104. end
  105. context 'with include_filtered' do
  106. let(:params) { { include_filtered: true } }
  107. it 'returns expected notification types, including filtered notifications' do
  108. subject
  109. expect(response).to have_http_status(200)
  110. expect(response.content_type)
  111. .to start_with('application/json')
  112. expect(response.parsed_body.size).to eq 6
  113. expect(body_json_types).to include('reblog', 'mention', 'favourite', 'follow')
  114. expect(response.parsed_body.any? { |x| x[:filtered] }).to be true
  115. end
  116. end
  117. context 'with account_id param' do
  118. let(:params) { { account_id: tom.account.id } }
  119. it 'returns only notifications from specified user', :aggregate_failures do
  120. subject
  121. expect(response).to have_http_status(200)
  122. expect(response.content_type)
  123. .to start_with('application/json')
  124. expect(body_json_account_ids.uniq).to eq [tom.account.id.to_s]
  125. end
  126. def body_json_account_ids
  127. response.parsed_body.map { |x| x[:account][:id] }
  128. end
  129. end
  130. context 'with invalid account_id param' do
  131. let(:params) { { account_id: 'foo' } }
  132. it 'returns nothing', :aggregate_failures do
  133. subject
  134. expect(response).to have_http_status(200)
  135. expect(response.content_type)
  136. .to start_with('application/json')
  137. expect(response.parsed_body.size).to eq 0
  138. end
  139. end
  140. context 'with exclude_types param' do
  141. let(:params) { { exclude_types: %w(mention) } }
  142. it 'returns everything but excluded type', :aggregate_failures do
  143. subject
  144. expect(response).to have_http_status(200)
  145. expect(response.content_type)
  146. .to start_with('application/json')
  147. expect(response.parsed_body.size).to_not eq 0
  148. expect(body_json_types.uniq).to_not include 'mention'
  149. end
  150. end
  151. context 'with types param' do
  152. let(:params) { { types: %w(mention) } }
  153. it 'returns only requested type', :aggregate_failures do
  154. subject
  155. expect(response).to have_http_status(200)
  156. expect(response.content_type)
  157. .to start_with('application/json')
  158. expect(body_json_types.uniq).to eq ['mention']
  159. end
  160. end
  161. context 'with limit param' do
  162. let(:params) { { limit: 3 } }
  163. it 'returns the requested number of notifications paginated', :aggregate_failures do
  164. subject
  165. notifications = user.account.notifications.browserable.order(id: :asc)
  166. expect(response.parsed_body.size)
  167. .to eq(params[:limit])
  168. expect(response)
  169. .to include_pagination_headers(
  170. prev: api_v1_notifications_url(limit: params[:limit], min_id: notifications.last.id),
  171. next: api_v1_notifications_url(limit: params[:limit], max_id: notifications[2].id)
  172. )
  173. end
  174. end
  175. def body_json_types
  176. response.parsed_body.pluck(:type)
  177. end
  178. end
  179. describe 'GET /api/v1/notifications/:id' do
  180. subject do
  181. get "/api/v1/notifications/#{notification.id}", headers: headers
  182. end
  183. let(:notification) { Fabricate(:notification, account: user.account) }
  184. it_behaves_like 'forbidden for wrong scope', 'write write:notifications'
  185. it 'returns http success' do
  186. subject
  187. expect(response).to have_http_status(200)
  188. expect(response.content_type)
  189. .to start_with('application/json')
  190. end
  191. context 'when notification belongs to someone else' do
  192. let(:notification) { Fabricate(:notification) }
  193. it 'returns http not found' do
  194. subject
  195. expect(response).to have_http_status(404)
  196. expect(response.content_type)
  197. .to start_with('application/json')
  198. end
  199. end
  200. end
  201. describe 'POST /api/v1/notifications/:id/dismiss' do
  202. subject do
  203. post "/api/v1/notifications/#{notification.id}/dismiss", headers: headers
  204. end
  205. let!(:notification) { Fabricate(:notification, account: user.account) }
  206. it_behaves_like 'forbidden for wrong scope', 'read read:notifications'
  207. it 'destroys the notification' do
  208. subject
  209. expect(response).to have_http_status(200)
  210. expect(response.content_type)
  211. .to start_with('application/json')
  212. expect { notification.reload }.to raise_error(ActiveRecord::RecordNotFound)
  213. end
  214. context 'when notification belongs to someone else' do
  215. let(:notification) { Fabricate(:notification) }
  216. it 'returns http not found' do
  217. subject
  218. expect(response).to have_http_status(404)
  219. expect(response.content_type)
  220. .to start_with('application/json')
  221. end
  222. end
  223. end
  224. describe 'POST /api/v1/notifications/clear' do
  225. subject do
  226. post '/api/v1/notifications/clear', headers: headers
  227. end
  228. before do
  229. Fabricate(:notification, account: user.account)
  230. end
  231. it_behaves_like 'forbidden for wrong scope', 'read read:notifications'
  232. it 'clears notifications for the account' do
  233. subject
  234. expect(user.account.reload.notifications).to be_empty
  235. expect(response).to have_http_status(200)
  236. expect(response.content_type)
  237. .to start_with('application/json')
  238. end
  239. end
  240. end