ぼざクリタグ広場 https://hub.nizika.monster
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

236 lines
7.1 KiB

  1. require 'rails_helper'
  2. require 'set'
  3. RSpec.describe 'Posts API', type: :request do
  4. # create / update で thumbnail.attach は走るが、
  5. # resized_thumbnail! が MiniMagick 依存でコケやすいので request spec ではスタブしとくのが無難。
  6. before do
  7. allow_any_instance_of(Post).to receive(:resized_thumbnail!).and_return(true)
  8. end
  9. def dummy_upload
  10. # 中身は何でもいい(加工処理はスタブしてる)
  11. Rack::Test::UploadedFile.new(StringIO.new('dummy'), 'image/jpeg', original_filename: 'dummy.jpg')
  12. end
  13. let!(:tag_name) { TagName.create!(name: 'spec_tag') }
  14. let!(:tag) { Tag.create!(tag_name: tag_name, category: 'general') }
  15. let!(:post_record) do
  16. Post.create!(title: 'spec post', url: 'https://example.com/spec').tap do |p|
  17. PostTag.create!(post: p, tag: tag)
  18. end
  19. end
  20. describe 'GET /posts' do
  21. it 'returns posts with tag name in JSON' do
  22. get '/posts'
  23. expect(response).to have_http_status(:ok)
  24. expect(json).to have_key('posts')
  25. expect(json['posts']).to be_an(Array)
  26. expect(json['posts']).not_to be_empty
  27. tags = json['posts'][0]['tags']
  28. expect(tags).to be_an(Array)
  29. expect(tags).not_to be_empty
  30. # Tag は name カラムを持たないので、API 側が methods: [:name] 等で出す必要がある
  31. expect(tags[0]).to have_key('name')
  32. expect(tags.map { |t| t['name'] }).to include('spec_tag')
  33. expect(tags[0]).to include('category')
  34. end
  35. end
  36. describe 'GET /posts/:id' do
  37. subject(:request) { get "/posts/#{post_id}" }
  38. context 'when post exists' do
  39. let(:post_id) { post_record.id }
  40. it 'returns post with tag tree + related + viewed' do
  41. request
  42. expect(response).to have_http_status(:ok)
  43. expect(json).to include('id' => post_record.id)
  44. expect(json).to have_key('tags')
  45. expect(json['tags']).to be_an(Array)
  46. # show は build_tag_tree_for を使うので、tags はツリー形式(children 付き)
  47. node = json['tags'][0]
  48. expect(node).to include('id', 'name', 'category', 'post_count', 'children')
  49. expect(node['name']).to eq('spec_tag')
  50. expect(json).to have_key('related')
  51. expect(json['related']).to be_an(Array)
  52. expect(json).to have_key('viewed')
  53. expect([true, false]).to include(json['viewed'])
  54. end
  55. end
  56. context 'when post does not exist' do
  57. let(:post_id) { 999_999_999 }
  58. it 'returns 404' do
  59. request
  60. expect(response).to have_http_status(:not_found)
  61. end
  62. end
  63. end
  64. describe 'POST /posts' do
  65. let(:member) { create(:user, :member) }
  66. it '401 when not logged in' do
  67. sign_out
  68. post '/posts', params: { title: 't', url: 'https://example.com/x', tags: 'a', thumbnail: dummy_upload }
  69. expect(response).to have_http_status(:unauthorized)
  70. end
  71. it '403 when not member' do
  72. sign_in_as(create(:user, role: 'guest'))
  73. post '/posts', params: { title: 't', url: 'https://example.com/x', tags: 'a', thumbnail: dummy_upload }
  74. expect(response).to have_http_status(:forbidden)
  75. end
  76. it '201 and creates post + tags when member' do
  77. sign_in_as(member)
  78. post '/posts', params: {
  79. title: 'new post',
  80. url: 'https://example.com/new',
  81. tags: 'spec_tag', # 既存タグ名を投げる
  82. thumbnail: dummy_upload
  83. }
  84. expect(response).to have_http_status(:created)
  85. expect(json).to include('id', 'title', 'url')
  86. # tags が name を含むこと(API 側の serialization が正しいこと)
  87. expect(json).to have_key('tags')
  88. expect(json['tags']).to be_an(Array)
  89. expect(json['tags'][0]).to have_key('name')
  90. end
  91. end
  92. describe 'PUT /posts/:id' do
  93. let(:member) { create(:user, :member) }
  94. it '401 when not logged in' do
  95. sign_out
  96. put "/posts/#{post_record.id}", params: { title: 'updated', tags: 'spec_tag' }
  97. expect(response).to have_http_status(:unauthorized)
  98. end
  99. it '403 when not member' do
  100. sign_in_as(create(:user, role: 'guest'))
  101. put "/posts/#{post_record.id}", params: { title: 'updated', tags: 'spec_tag' }
  102. expect(response).to have_http_status(:forbidden)
  103. end
  104. it '200 and updates title + resync tags when member' do
  105. sign_in_as(member)
  106. # 追加で別タグも作って、更新時に入れ替わることを見る
  107. tn2 = TagName.create!(name: 'spec_tag_2')
  108. Tag.create!(tag_name: tn2, category: 'general')
  109. put "/posts/#{post_record.id}", params: {
  110. title: 'updated title',
  111. tags: 'spec_tag_2'
  112. }
  113. expect(response).to have_http_status(:ok)
  114. expect(json).to have_key('tags')
  115. expect(json['tags']).to be_an(Array)
  116. # show と同様、update 後レスポンスもツリー形式
  117. names = json['tags'].map { |n| n['name'] }
  118. expect(names).to include('spec_tag_2')
  119. end
  120. end
  121. describe 'GET /posts/random' do
  122. it '404 when no posts' do
  123. PostTag.delete_all
  124. Post.delete_all
  125. get '/posts/random'
  126. expect(response).to have_http_status(:not_found)
  127. end
  128. it '200 and returns viewed boolean' do
  129. get '/posts/random'
  130. expect(response).to have_http_status(:ok)
  131. expect(json).to have_key('viewed')
  132. expect([true, false]).to include(json['viewed'])
  133. end
  134. end
  135. describe 'GET /posts/changes' do
  136. let(:member) { create(:user, :member) }
  137. it 'returns add/remove events (history) for a post' do
  138. # add
  139. tn2 = TagName.create!(name: 'spec_tag2')
  140. tag2 = Tag.create!(tag_name: tn2, category: 'general')
  141. pt = PostTag.create!(post: post_record, tag: tag2, created_user: member)
  142. # remove (discard)
  143. pt.discard_by!(member)
  144. get '/posts/changes', params: { id: post_record.id }
  145. expect(response).to have_http_status(:ok)
  146. expect(json).to include('changes', 'count')
  147. expect(json['changes']).to be_an(Array)
  148. expect(json['count']).to be >= 2
  149. types = json['changes'].map { |e| e['change_type'] }.uniq
  150. expect(types).to include('add')
  151. expect(types).to include('remove')
  152. end
  153. end
  154. describe 'POST /posts/:id/viewed' do
  155. let(:user) { create(:user) }
  156. it '401 when not logged in' do
  157. sign_out
  158. post "/posts/#{ post_record.id }/viewed"
  159. expect(response).to have_http_status(:unauthorized)
  160. end
  161. it '204 and marks viewed when logged in' do
  162. sign_in_as(user)
  163. post "/posts/#{ post_record.id }/viewed"
  164. expect(response).to have_http_status(:no_content)
  165. expect(user.reload.viewed?(post_record)).to be(true)
  166. end
  167. end
  168. describe 'DELETE /posts/:id/unviewed' do
  169. let(:user) { create(:user) }
  170. it '401 when not logged in' do
  171. sign_out
  172. delete "/posts/#{ post_record.id }/viewed"
  173. expect(response).to have_http_status(:unauthorized)
  174. end
  175. it '204 and unmarks viewed when logged in' do
  176. sign_in_as(user)
  177. # 先に viewed 付けてから外す
  178. user.viewed_posts << post_record
  179. delete "/posts/#{ post_record.id }/viewed"
  180. expect(response).to have_http_status(:no_content)
  181. expect(user.reload.viewed?(post_record)).to be(false)
  182. end
  183. end
  184. end