This commit is contained in:
@@ -33,8 +33,8 @@ class NicoTagsController < ApplicationController
|
|||||||
return head :bad_request unless tag.nico?
|
return head :bad_request unless tag.nico?
|
||||||
|
|
||||||
linked_tag_names = params[:tags].to_s.split
|
linked_tag_names = params[:tags].to_s.split
|
||||||
linked_tags = Tag.normalise_tags(linked_tag_names, with_tagme: false,
|
linked_tags = Tag.normalise_tags!(linked_tag_names, with_tagme: false,
|
||||||
with_no_deerjikist: false)
|
with_no_deerjikist: false)
|
||||||
return head :bad_request if linked_tags.any? { |t| t.nico? }
|
return head :bad_request if linked_tags.any? { |t| t.nico? }
|
||||||
|
|
||||||
ApplicationRecord.transaction do
|
ApplicationRecord.transaction do
|
||||||
|
|||||||
@@ -147,12 +147,12 @@ class PostsController < ApplicationController
|
|||||||
|
|
||||||
post.reload
|
post.reload
|
||||||
render json: PostRepr.base(post), status: :created
|
render json: PostRepr.base(post), status: :created
|
||||||
|
rescue Tag::NicoTagNormalisationError
|
||||||
|
head :bad_request
|
||||||
rescue ArgumentError => e
|
rescue ArgumentError => e
|
||||||
render json: { errors: [e.message] }, status: :unprocessable_entity
|
render json: { errors: [e.message] }, status: :unprocessable_entity
|
||||||
rescue ActiveRecord::RecordInvalid => e
|
rescue ActiveRecord::RecordInvalid => e
|
||||||
render json: { errors: e.record.errors.full_messages }, status: :unprocessable_entity
|
render json: { errors: e.record.errors.full_messages }, status: :unprocessable_entity
|
||||||
rescue Tag::NicoTagNormalisationError
|
|
||||||
head :bad_request
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def viewed
|
def viewed
|
||||||
@@ -202,12 +202,12 @@ class PostsController < ApplicationController
|
|||||||
json = post.as_json
|
json = post.as_json
|
||||||
json['tags'] = build_tag_tree_for(post.tags)
|
json['tags'] = build_tag_tree_for(post.tags)
|
||||||
render json:, status: :ok
|
render json:, status: :ok
|
||||||
|
rescue Tag::NicoTagNormalisationError
|
||||||
|
head :bad_request
|
||||||
rescue ArgumentError => e
|
rescue ArgumentError => e
|
||||||
render json: { errors: [e.message] }, status: :unprocessable_entity
|
render json: { errors: [e.message] }, status: :unprocessable_entity
|
||||||
rescue ActiveRecord::RecordInvalid => e
|
rescue ActiveRecord::RecordInvalid => e
|
||||||
render json: { errors: e.record.errors.full_messages }, status: :unprocessable_entity
|
render json: { errors: e.record.errors.full_messages }, status: :unprocessable_entity
|
||||||
rescue Tag::NicoTagNormalisationError
|
|
||||||
head :bad_request
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def changes
|
def changes
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe PostImplication, type: :model do
|
||||||
|
let!(:post_record) do
|
||||||
|
Post.create!(
|
||||||
|
title: 'post',
|
||||||
|
url: 'https://example.com/post-implication-post'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:parent_post) do
|
||||||
|
Post.create!(
|
||||||
|
title: 'parent post',
|
||||||
|
url: 'https://example.com/post-implication-parent'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is valid with post and parent_post' do
|
||||||
|
implication = described_class.new(
|
||||||
|
post: post_record,
|
||||||
|
parent_post:
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(implication).to be_valid
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not allow same post as parent_post' do
|
||||||
|
implication = described_class.new(
|
||||||
|
post: post_record,
|
||||||
|
parent_post: post_record
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(implication).not_to be_valid
|
||||||
|
expect(implication.errors[:parent_post_id]).to be_present
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not allow duplicate pair' do
|
||||||
|
described_class.create!(
|
||||||
|
post: post_record,
|
||||||
|
parent_post:
|
||||||
|
)
|
||||||
|
|
||||||
|
duplicate = described_class.new(
|
||||||
|
post: post_record,
|
||||||
|
parent_post:
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(duplicate).not_to be_valid
|
||||||
|
expect(duplicate.errors[:post_id]).to be_present
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -19,7 +19,7 @@ RSpec.describe PostVersion, type: :model do
|
|||||||
url: post_record.url,
|
url: post_record.url,
|
||||||
thumbnail_base: post_record.thumbnail_base,
|
thumbnail_base: post_record.thumbnail_base,
|
||||||
tags: post_record.snapshot_tag_names.join(' '),
|
tags: post_record.snapshot_tag_names.join(' '),
|
||||||
parent: post_record.parent,
|
parent_post_ids: post_record.snapshot_parent_post_ids.join(' '),
|
||||||
original_created_from: post_record.original_created_from,
|
original_created_from: post_record.original_created_from,
|
||||||
original_created_before: post_record.original_created_before,
|
original_created_before: post_record.original_created_before,
|
||||||
created_at: Time.current,
|
created_at: Time.current,
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ RSpec.describe Tag, type: :model do
|
|||||||
url: post.url,
|
url: post.url,
|
||||||
thumbnail_base: post.thumbnail_base,
|
thumbnail_base: post.thumbnail_base,
|
||||||
tags: snapshot_tags(post),
|
tags: snapshot_tags(post),
|
||||||
parent: post.parent,
|
parent_post_ids: post.snapshot_parent_post_ids.join(' '),
|
||||||
original_created_from: post.original_created_from,
|
original_created_from: post.original_created_from,
|
||||||
original_created_before: post.original_created_before,
|
original_created_before: post.original_created_before,
|
||||||
created_at: Time.current,
|
created_at: Time.current,
|
||||||
|
|||||||
@@ -15,6 +15,31 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
Rack::Test::UploadedFile.new(StringIO.new('dummy'), 'image/jpeg', original_filename: 'dummy.jpg')
|
Rack::Test::UploadedFile.new(StringIO.new('dummy'), 'image/jpeg', original_filename: 'dummy.jpg')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def post_write_params params = { }
|
||||||
|
{ parent_post_ids: '' }.merge(params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_parent_post! title:, url:
|
||||||
|
Post.create!(title:, url:)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_post_version_for! post
|
||||||
|
PostVersion.create!(
|
||||||
|
post:,
|
||||||
|
version_no: 1,
|
||||||
|
event_type: 'create',
|
||||||
|
title: post.title,
|
||||||
|
url: post.url,
|
||||||
|
thumbnail_base: post.thumbnail_base,
|
||||||
|
tags: post.snapshot_tag_names.join(' '),
|
||||||
|
parent_post_ids: post.snapshot_parent_post_ids.join(' '),
|
||||||
|
original_created_from: post.original_created_from,
|
||||||
|
original_created_before: post.original_created_before,
|
||||||
|
created_at: post.created_at,
|
||||||
|
created_by_user: post.uploaded_user
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
let!(:tag_name) { TagName.create!(name: 'spec_tag') }
|
let!(:tag_name) { TagName.create!(name: 'spec_tag') }
|
||||||
let!(:tag) { Tag.create!(tag_name: tag_name, category: :general) }
|
let!(:tag) { Tag.create!(tag_name: tag_name, category: :general) }
|
||||||
|
|
||||||
@@ -457,6 +482,65 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
expect(json).to have_key('viewed')
|
expect(json).to have_key('viewed')
|
||||||
expect([true, false]).to include(json['viewed'])
|
expect([true, false]).to include(json['viewed'])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when post has parent, child, and sibling posts' do
|
||||||
|
let!(:parent_post) do
|
||||||
|
create_parent_post!(
|
||||||
|
title: 'shared parent post',
|
||||||
|
url: 'https://example.com/shared-parent-post'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:child_post) do
|
||||||
|
Post.create!(
|
||||||
|
title: 'child post',
|
||||||
|
url: 'https://example.com/show-child-post'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:sibling_post) do
|
||||||
|
Post.create!(
|
||||||
|
title: 'sibling post',
|
||||||
|
url: 'https://example.com/show-sibling-post'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
PostImplication.create!(
|
||||||
|
post: post_record,
|
||||||
|
parent_post:
|
||||||
|
)
|
||||||
|
|
||||||
|
PostImplication.create!(
|
||||||
|
post: child_post,
|
||||||
|
parent_post: post_record
|
||||||
|
)
|
||||||
|
|
||||||
|
PostImplication.create!(
|
||||||
|
post: sibling_post,
|
||||||
|
parent_post:
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns parent_posts, child_posts, and sibling_posts' do
|
||||||
|
get "/posts/#{post_record.id}"
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:ok)
|
||||||
|
|
||||||
|
parent_ids = json.fetch('parent_posts').map { |p| p.fetch('id') }
|
||||||
|
child_ids = json.fetch('child_posts').map { |p| p.fetch('id') }
|
||||||
|
|
||||||
|
expect(parent_ids).to include(parent_post.id)
|
||||||
|
expect(child_ids).to include(child_post.id)
|
||||||
|
|
||||||
|
sibling_posts_by_parent = json.fetch('sibling_posts')
|
||||||
|
siblings = sibling_posts_by_parent.fetch(parent_post.id.to_s)
|
||||||
|
|
||||||
|
sibling_ids = siblings.map { |p| p.fetch('id') }
|
||||||
|
expect(sibling_ids).to include(post_record.id)
|
||||||
|
expect(sibling_ids).to include(sibling_post.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when post does not exist' do
|
context 'when post does not exist' do
|
||||||
@@ -475,25 +559,28 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
|
|
||||||
it '401 when not logged in' do
|
it '401 when not logged in' do
|
||||||
sign_out
|
sign_out
|
||||||
post '/posts', params: { title: 't', url: 'https://example.com/x', tags: 'a', thumbnail: dummy_upload }
|
post '/posts', params: post_write_params(title: 't', url: 'https://example.com/x', tags: 'a',
|
||||||
|
thumbnail: dummy_upload)
|
||||||
|
|
||||||
expect(response).to have_http_status(:unauthorized)
|
expect(response).to have_http_status(:unauthorized)
|
||||||
end
|
end
|
||||||
|
|
||||||
it '403 when not member' do
|
it '403 when not member' do
|
||||||
sign_in_as(create(:user, role: 'guest'))
|
sign_in_as(create(:user, role: 'guest'))
|
||||||
post '/posts', params: { title: 't', url: 'https://example.com/x', tags: 'a', thumbnail: dummy_upload }
|
post '/posts', params: post_write_params(title: 't', url: 'https://example.com/x', tags: 'a',
|
||||||
|
thumbnail: dummy_upload)
|
||||||
expect(response).to have_http_status(:forbidden)
|
expect(response).to have_http_status(:forbidden)
|
||||||
end
|
end
|
||||||
|
|
||||||
it '201 and creates post + tags when member' do
|
it '201 and creates post + tags when member' do
|
||||||
sign_in_as(member)
|
sign_in_as(member)
|
||||||
|
|
||||||
post '/posts', params: {
|
post '/posts', params: post_write_params(
|
||||||
title: 'new post',
|
title: 'new post',
|
||||||
url: 'https://example.com/new',
|
url: 'https://example.com/new',
|
||||||
tags: 'spec_tag', # 既存タグ名を投げる
|
tags: 'spec_tag', # 既存タグ名を投げる
|
||||||
thumbnail: dummy_upload
|
thumbnail: dummy_upload
|
||||||
}
|
)
|
||||||
|
|
||||||
expect(response).to have_http_status(:created)
|
expect(response).to have_http_status(:created)
|
||||||
expect(json).to include('id', 'title', 'url')
|
expect(json).to include('id', 'title', 'url')
|
||||||
@@ -507,12 +594,12 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
it '201 and creates post + tags when member and tags have aliases' do
|
it '201 and creates post + tags when member and tags have aliases' do
|
||||||
sign_in_as(member)
|
sign_in_as(member)
|
||||||
|
|
||||||
post '/posts', params: {
|
post '/posts', params: post_write_params(
|
||||||
title: 'new post',
|
title: 'new post',
|
||||||
url: 'https://example.com/new',
|
url: 'https://example.com/new',
|
||||||
tags: 'manko', # 既存タグ名を投げる
|
tags: 'manko', # 既存タグ名を投げる
|
||||||
thumbnail: dummy_upload
|
thumbnail: dummy_upload
|
||||||
}
|
)
|
||||||
|
|
||||||
expect(response).to have_http_status(:created)
|
expect(response).to have_http_status(:created)
|
||||||
expect(json).to include('id', 'title', 'url')
|
expect(json).to include('id', 'title', 'url')
|
||||||
@@ -533,13 +620,14 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
it 'return 400' do
|
it 'return 400' do
|
||||||
sign_in_as(member)
|
sign_in_as(member)
|
||||||
|
|
||||||
post '/posts', params: {
|
post '/posts', params: post_write_params(
|
||||||
title: 'new post',
|
title: 'new post',
|
||||||
url: 'https://example.com/nico_tag',
|
url: 'https://example.com/nico-tag-post',
|
||||||
tags: 'nico:nico_tag',
|
tags: 'nico:nico_tag',
|
||||||
thumbnail: dummy_upload }
|
thumbnail: dummy_upload
|
||||||
|
)
|
||||||
|
|
||||||
expect(response).to have_http_status(:bad_request)
|
expect(response).to have_http_status(:bad_request), response.body
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -547,11 +635,11 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
it 'returns 422' do
|
it 'returns 422' do
|
||||||
sign_in_as(member)
|
sign_in_as(member)
|
||||||
|
|
||||||
post '/posts', params: {
|
post '/posts', params: post_write_params(
|
||||||
title: 'new post',
|
title: 'new post',
|
||||||
url: ' ',
|
url: ' ',
|
||||||
tags: 'spec_tag', # 既存タグ名を投げる
|
tags: 'spec_tag', # 既存タグ名を投げる
|
||||||
thumbnail: dummy_upload }
|
thumbnail: dummy_upload)
|
||||||
|
|
||||||
expect(response).to have_http_status(:unprocessable_entity)
|
expect(response).to have_http_status(:unprocessable_entity)
|
||||||
end
|
end
|
||||||
@@ -561,16 +649,156 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
it 'returns 422' do
|
it 'returns 422' do
|
||||||
sign_in_as(member)
|
sign_in_as(member)
|
||||||
|
|
||||||
post '/posts', params: {
|
post '/posts', params: post_write_params(
|
||||||
title: 'new post',
|
title: 'new post',
|
||||||
url: 'ぼざクリタグ広場',
|
url: 'ぼざクリタグ広場',
|
||||||
tags: 'spec_tag', # 既存タグ名を投げる
|
tags: 'spec_tag', # 既存タグ名を投げる
|
||||||
thumbnail: dummy_upload
|
thumbnail: dummy_upload)
|
||||||
}
|
|
||||||
|
|
||||||
expect(response).to have_http_status(:unprocessable_entity)
|
expect(response).to have_http_status(:unprocessable_entity)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when parent_post_ids is provided' do
|
||||||
|
let!(:parent_post_1) do
|
||||||
|
create_parent_post!(
|
||||||
|
title: 'parent post 1',
|
||||||
|
url: 'https://example.com/parent-post-1'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:parent_post_2) do
|
||||||
|
create_parent_post!(
|
||||||
|
title: 'parent post 2',
|
||||||
|
url: 'https://example.com/parent-post-2'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates post implications for parent posts' do
|
||||||
|
sign_in_as(member)
|
||||||
|
|
||||||
|
expect {
|
||||||
|
post '/posts', params: {
|
||||||
|
title: 'child post',
|
||||||
|
url: 'https://example.com/child-post',
|
||||||
|
tags: 'spec_tag',
|
||||||
|
parent_post_ids: "#{parent_post_1.id} #{parent_post_2.id}",
|
||||||
|
thumbnail: dummy_upload }
|
||||||
|
}.to change(PostImplication, :count).by(2)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:created)
|
||||||
|
|
||||||
|
created_post = Post.find(json.fetch('id'))
|
||||||
|
expect(created_post.parent_posts.order(:id).pluck(:id)).to eq(
|
||||||
|
[parent_post_1.id, parent_post_2.id].sort
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(PostImplication.exists?(
|
||||||
|
post_id: created_post.id,
|
||||||
|
parent_post_id: parent_post_1.id
|
||||||
|
)).to be(true)
|
||||||
|
|
||||||
|
expect(PostImplication.exists?(
|
||||||
|
post_id: created_post.id,
|
||||||
|
parent_post_id: parent_post_2.id
|
||||||
|
)).to be(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'deduplicates parent_post_ids' do
|
||||||
|
sign_in_as(member)
|
||||||
|
|
||||||
|
expect {
|
||||||
|
post '/posts', params: post_write_params(
|
||||||
|
title: 'dedup child post',
|
||||||
|
url: 'https://example.com/dedup-child-post',
|
||||||
|
tags: 'spec_tag',
|
||||||
|
parent_post_ids: "#{parent_post_1.id} #{parent_post_1.id}",
|
||||||
|
thumbnail: dummy_upload
|
||||||
|
)
|
||||||
|
}.to change(PostImplication, :count).by(1)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:created)
|
||||||
|
|
||||||
|
created_post = Post.find(json.fetch('id'))
|
||||||
|
expect(created_post.parent_posts.pluck(:id)).to eq([parent_post_1.id])
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'records parent_post_ids in post version' do
|
||||||
|
sign_in_as(member)
|
||||||
|
|
||||||
|
post '/posts', params: post_write_params(
|
||||||
|
title: 'versioned child post',
|
||||||
|
url: 'https://example.com/versioned-child-post',
|
||||||
|
tags: 'spec_tag',
|
||||||
|
parent_post_ids: "#{parent_post_1.id} #{parent_post_2.id}",
|
||||||
|
thumbnail: dummy_upload
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:created)
|
||||||
|
|
||||||
|
created_post = Post.find(json.fetch('id'))
|
||||||
|
version = PostVersion.find_by!(post: created_post, version_no: 1)
|
||||||
|
|
||||||
|
expect(version.parent_post_ids.split.map(&:to_i)).to eq(
|
||||||
|
[parent_post_1.id, parent_post_2.id].sort
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when parent_post_ids is missing' do
|
||||||
|
it 'returns 422' do
|
||||||
|
sign_in_as(member)
|
||||||
|
|
||||||
|
expect {
|
||||||
|
post '/posts', params: {
|
||||||
|
title: 'missing parent_post_ids',
|
||||||
|
url: 'https://example.com/missing-parent-post-ids',
|
||||||
|
tags: 'spec_tag',
|
||||||
|
thumbnail: dummy_upload }
|
||||||
|
}.not_to change(Post, :count)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unprocessable_entity)
|
||||||
|
expect(json.fetch('errors')).to be_present
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when parent_post_ids includes invalid token' do
|
||||||
|
it 'returns 422 and does not create post' do
|
||||||
|
sign_in_as(member)
|
||||||
|
|
||||||
|
expect {
|
||||||
|
post '/posts', params: post_write_params(
|
||||||
|
title: 'invalid parent ids',
|
||||||
|
url: 'https://example.com/invalid-parent-ids',
|
||||||
|
tags: 'spec_tag',
|
||||||
|
parent_post_ids: 'abc',
|
||||||
|
thumbnail: dummy_upload
|
||||||
|
)
|
||||||
|
}.not_to change(Post, :count)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unprocessable_entity)
|
||||||
|
expect(json.fetch('errors')).to be_present
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when parent_post_ids includes nonexistent post id' do
|
||||||
|
it 'returns 422 and does not create post implication' do
|
||||||
|
sign_in_as(member)
|
||||||
|
|
||||||
|
expect {
|
||||||
|
post '/posts', params: post_write_params(
|
||||||
|
title: 'missing parent post',
|
||||||
|
url: 'https://example.com/missing-parent-post',
|
||||||
|
tags: 'spec_tag',
|
||||||
|
parent_post_ids: '999999999',
|
||||||
|
thumbnail: dummy_upload
|
||||||
|
)
|
||||||
|
}.not_to change(PostImplication, :count)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unprocessable_entity)
|
||||||
|
expect(json.fetch('errors')).to be_present
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'PUT /posts/:id' do
|
describe 'PUT /posts/:id' do
|
||||||
@@ -578,13 +806,13 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
|
|
||||||
it '401 when not logged in' do
|
it '401 when not logged in' do
|
||||||
sign_out
|
sign_out
|
||||||
put "/posts/#{post_record.id}", params: { title: 'updated', tags: 'spec_tag' }
|
put "/posts/#{post_record.id}", params: post_write_params(title: 'updated', tags: 'spec_tag')
|
||||||
expect(response).to have_http_status(:unauthorized)
|
expect(response).to have_http_status(:unauthorized)
|
||||||
end
|
end
|
||||||
|
|
||||||
it '403 when not member' do
|
it '403 when not member' do
|
||||||
sign_in_as(create(:user, role: 'guest'))
|
sign_in_as(create(:user, role: 'guest'))
|
||||||
put "/posts/#{post_record.id}", params: { title: 'updated', tags: 'spec_tag' }
|
put "/posts/#{post_record.id}", params: post_write_params(title: 'updated', tags: 'spec_tag')
|
||||||
expect(response).to have_http_status(:forbidden)
|
expect(response).to have_http_status(:forbidden)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -595,10 +823,9 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
tn2 = TagName.create!(name: 'spec_tag_2')
|
tn2 = TagName.create!(name: 'spec_tag_2')
|
||||||
Tag.create!(tag_name: tn2, category: :general)
|
Tag.create!(tag_name: tn2, category: :general)
|
||||||
|
|
||||||
put "/posts/#{post_record.id}", params: {
|
put "/posts/#{post_record.id}", params: post_write_params(
|
||||||
title: 'updated title',
|
title: 'updated title',
|
||||||
tags: 'spec_tag_2'
|
tags: 'spec_tag_2')
|
||||||
}
|
|
||||||
|
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
expect(json).to have_key('tags')
|
expect(json).to have_key('tags')
|
||||||
@@ -619,11 +846,178 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
it 'return 400' do
|
it 'return 400' do
|
||||||
sign_in_as(member)
|
sign_in_as(member)
|
||||||
|
|
||||||
put "/posts/#{ post_record.id }", params: {
|
put "/posts/#{post_record.id}", params: post_write_params(
|
||||||
title: 'updated title',
|
title: 'updated title',
|
||||||
tags: 'nico:nico_tag' }
|
tags: 'nico:nico_tag'
|
||||||
|
)
|
||||||
|
|
||||||
expect(response).to have_http_status(:bad_request)
|
expect(response).to have_http_status(:bad_request), response.body
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when parent_post_ids is provided' do
|
||||||
|
let!(:old_parent_post) do
|
||||||
|
create_parent_post!(
|
||||||
|
title: 'old parent post',
|
||||||
|
url: 'https://example.com/old-parent-post'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:new_parent_post_1) do
|
||||||
|
create_parent_post!(
|
||||||
|
title: 'new parent post 1',
|
||||||
|
url: 'https://example.com/new-parent-post-1'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:new_parent_post_2) do
|
||||||
|
create_parent_post!(
|
||||||
|
title: 'new parent post 2',
|
||||||
|
url: 'https://example.com/new-parent-post-2'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
PostImplication.create!(
|
||||||
|
post: post_record,
|
||||||
|
parent_post: old_parent_post
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'replaces parent posts' do
|
||||||
|
sign_in_as(member)
|
||||||
|
|
||||||
|
put "/posts/#{post_record.id}", params: post_write_params(
|
||||||
|
title: 'updated title',
|
||||||
|
tags: 'spec_tag',
|
||||||
|
parent_post_ids: "#{new_parent_post_1.id} #{new_parent_post_2.id}"
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:ok)
|
||||||
|
|
||||||
|
expect(post_record.reload.parent_posts.order(:id).pluck(:id)).to eq(
|
||||||
|
[new_parent_post_1.id, new_parent_post_2.id].sort
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(PostImplication.exists?(
|
||||||
|
post_id: post_record.id,
|
||||||
|
parent_post_id: old_parent_post.id
|
||||||
|
)).to be(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'clears parent posts when parent_post_ids is blank' do
|
||||||
|
sign_in_as(member)
|
||||||
|
|
||||||
|
put "/posts/#{post_record.id}", params: post_write_params(
|
||||||
|
title: 'updated title',
|
||||||
|
tags: 'spec_tag',
|
||||||
|
parent_post_ids: ''
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:ok)
|
||||||
|
expect(post_record.reload.parent_posts).to be_empty
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'records changed parent_post_ids in post version' do
|
||||||
|
sign_in_as(member)
|
||||||
|
create_post_version_for!(post_record.reload)
|
||||||
|
|
||||||
|
put "/posts/#{post_record.id}", params: post_write_params(
|
||||||
|
title: 'updated title',
|
||||||
|
tags: 'spec_tag',
|
||||||
|
parent_post_ids: "#{new_parent_post_1.id} #{new_parent_post_2.id}"
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:ok)
|
||||||
|
|
||||||
|
version = post_record.reload.post_versions.order(:version_no).last
|
||||||
|
|
||||||
|
expect(version.version_no).to eq(2)
|
||||||
|
expect(version.parent_post_ids.split.map(&:to_i)).to eq(
|
||||||
|
[new_parent_post_1.id, new_parent_post_2.id].sort
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when parent_post_ids is missing' do
|
||||||
|
it 'returns 422' do
|
||||||
|
sign_in_as(member)
|
||||||
|
|
||||||
|
put "/posts/#{post_record.id}", params: {
|
||||||
|
title: 'updated title',
|
||||||
|
tags: 'spec_tag' }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unprocessable_entity)
|
||||||
|
expect(json.fetch('errors')).to be_present
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when parent_post_ids includes invalid token' do
|
||||||
|
it 'returns 422 and does not change parent posts' do
|
||||||
|
sign_in_as(member)
|
||||||
|
|
||||||
|
parent_post = create_parent_post!(
|
||||||
|
title: 'valid parent post',
|
||||||
|
url: 'https://example.com/valid-parent-post'
|
||||||
|
)
|
||||||
|
|
||||||
|
PostImplication.create!(
|
||||||
|
post: post_record,
|
||||||
|
parent_post:
|
||||||
|
)
|
||||||
|
|
||||||
|
put "/posts/#{post_record.id}", params: post_write_params(
|
||||||
|
title: 'updated title',
|
||||||
|
tags: 'spec_tag',
|
||||||
|
parent_post_ids: 'abc'
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unprocessable_entity)
|
||||||
|
expect(post_record.reload.parent_posts.pluck(:id)).to eq([parent_post.id])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when parent_post_ids includes nonexistent post id' do
|
||||||
|
it 'returns 422 and does not change parent posts' do
|
||||||
|
sign_in_as(member)
|
||||||
|
|
||||||
|
parent_post = create_parent_post!(
|
||||||
|
title: 'existing parent post',
|
||||||
|
url: 'https://example.com/existing-parent-post'
|
||||||
|
)
|
||||||
|
|
||||||
|
PostImplication.create!(
|
||||||
|
post: post_record,
|
||||||
|
parent_post:
|
||||||
|
)
|
||||||
|
|
||||||
|
put "/posts/#{post_record.id}", params: post_write_params(
|
||||||
|
title: 'updated title',
|
||||||
|
tags: 'spec_tag',
|
||||||
|
parent_post_ids: '999999999'
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unprocessable_entity)
|
||||||
|
expect(post_record.reload.parent_posts.pluck(:id)).to eq([parent_post.id])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when parent_post_ids includes self id' do
|
||||||
|
it 'returns 422 and does not create self implication' do
|
||||||
|
sign_in_as(member)
|
||||||
|
|
||||||
|
put "/posts/#{post_record.id}", params: post_write_params(
|
||||||
|
title: 'updated title',
|
||||||
|
tags: 'spec_tag',
|
||||||
|
parent_post_ids: post_record.id.to_s
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unprocessable_entity)
|
||||||
|
|
||||||
|
expect(PostImplication.exists?(
|
||||||
|
post_id: post_record.id,
|
||||||
|
parent_post_id: post_record.id
|
||||||
|
)).to be(false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -773,20 +1167,20 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
post.snapshot_tag_names.join(' ')
|
post.snapshot_tag_names.join(' ')
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_post_version!(post, version_no:, event_type:, created_by_user:, created_at:)
|
def create_post_version! post, version_no:, event_type:, created_by_user:, created_at:
|
||||||
PostVersion.create!(
|
PostVersion.create!(
|
||||||
post: post,
|
post:,
|
||||||
version_no: version_no,
|
version_no:,
|
||||||
event_type: event_type,
|
event_type:,
|
||||||
title: post.title,
|
title: post.title,
|
||||||
url: post.url,
|
url: post.url,
|
||||||
thumbnail_base: post.thumbnail_base,
|
thumbnail_base: post.thumbnail_base,
|
||||||
tags: snapshot_tags(post),
|
tags: snapshot_tags(post),
|
||||||
parent: post.parent,
|
parent_post_ids: post.snapshot_parent_post_ids.join(' '),
|
||||||
original_created_from: post.original_created_from,
|
original_created_from: post.original_created_from,
|
||||||
original_created_before: post.original_created_before,
|
original_created_before: post.original_created_before,
|
||||||
created_at: created_at,
|
created_at:,
|
||||||
created_by_user: created_by_user
|
created_by_user:
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1015,33 +1409,15 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
post.snapshot_tag_names.join(' ')
|
post.snapshot_tag_names.join(' ')
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_post_version_for!(post)
|
|
||||||
PostVersion.create!(
|
|
||||||
post: post,
|
|
||||||
version_no: 1,
|
|
||||||
event_type: 'create',
|
|
||||||
title: post.title,
|
|
||||||
url: post.url,
|
|
||||||
thumbnail_base: post.thumbnail_base,
|
|
||||||
tags: snapshot_tags(post),
|
|
||||||
parent: post.parent,
|
|
||||||
original_created_from: post.original_created_from,
|
|
||||||
original_created_before: post.original_created_before,
|
|
||||||
created_at: post.created_at,
|
|
||||||
created_by_user: post.uploaded_user
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'creates version 1 on POST /posts' do
|
it 'creates version 1 on POST /posts' do
|
||||||
sign_in_as(member)
|
sign_in_as(member)
|
||||||
|
|
||||||
expect do
|
expect do
|
||||||
post '/posts', params: {
|
post '/posts', params: post_write_params(
|
||||||
title: 'versioned post',
|
title: 'versioned post',
|
||||||
url: 'https://example.com/versioned-post',
|
url: 'https://example.com/versioned-post',
|
||||||
tags: 'spec_tag',
|
tags: 'spec_tag',
|
||||||
thumbnail: dummy_upload
|
thumbnail: dummy_upload)
|
||||||
}
|
|
||||||
end.to change(PostVersion, :count).by(1)
|
end.to change(PostVersion, :count).by(1)
|
||||||
|
|
||||||
expect(response).to have_http_status(:created)
|
expect(response).to have_http_status(:created)
|
||||||
@@ -1064,10 +1440,9 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
Tag.create!(tag_name: tag_name2, category: :general)
|
Tag.create!(tag_name: tag_name2, category: :general)
|
||||||
|
|
||||||
expect do
|
expect do
|
||||||
put "/posts/#{post_record.id}", params: {
|
put "/posts/#{post_record.id}", params: post_write_params(
|
||||||
title: 'updated title',
|
title: 'updated title',
|
||||||
tags: 'spec_tag_2'
|
tags: 'spec_tag_2')
|
||||||
}
|
|
||||||
end.to change(PostVersion, :count).by(1)
|
end.to change(PostVersion, :count).by(1)
|
||||||
|
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
@@ -1087,10 +1462,9 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
create_post_version_for!(post_record.reload)
|
create_post_version_for!(post_record.reload)
|
||||||
|
|
||||||
expect {
|
expect {
|
||||||
put "/posts/#{post_record.id}", params: {
|
put "/posts/#{post_record.id}", params: post_write_params(
|
||||||
title: post_record.title,
|
title: post_record.title,
|
||||||
tags: 'spec_tag'
|
tags: 'spec_tag')
|
||||||
}
|
|
||||||
}.not_to change(PostVersion, :count)
|
}.not_to change(PostVersion, :count)
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
|
|
||||||
@@ -1104,12 +1478,11 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
sign_in_as(member)
|
sign_in_as(member)
|
||||||
|
|
||||||
expect do
|
expect do
|
||||||
post '/posts', params: {
|
post '/posts', params: post_write_params(
|
||||||
title: 'invalid post',
|
title: 'invalid post',
|
||||||
url: 'ぼざクリタグ広場',
|
url: 'ぼざクリタグ広場',
|
||||||
tags: 'spec_tag',
|
tags: 'spec_tag',
|
||||||
thumbnail: dummy_upload
|
thumbnail: dummy_upload)
|
||||||
}
|
|
||||||
end.not_to change(PostVersion, :count)
|
end.not_to change(PostVersion, :count)
|
||||||
|
|
||||||
expect(response).to have_http_status(:unprocessable_entity)
|
expect(response).to have_http_status(:unprocessable_entity)
|
||||||
@@ -1120,12 +1493,11 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
create_post_version_for!(post_record)
|
create_post_version_for!(post_record)
|
||||||
|
|
||||||
expect do
|
expect do
|
||||||
put "/posts/#{post_record.id}", params: {
|
put "/posts/#{post_record.id}", params: post_write_params(
|
||||||
title: 'updated title',
|
title: 'updated title',
|
||||||
tags: 'spec_tag',
|
tags: 'spec_tag',
|
||||||
original_created_from: Time.zone.local(2020, 1, 2, 0, 0, 0).iso8601,
|
original_created_from: Time.zone.local(2020, 1, 2, 0, 0, 0).iso8601,
|
||||||
original_created_before: Time.zone.local(2020, 1, 1, 0, 0, 0).iso8601
|
original_created_before: Time.zone.local(2020, 1, 1, 0, 0, 0).iso8601)
|
||||||
}
|
|
||||||
end.not_to change(PostVersion, :count)
|
end.not_to change(PostVersion, :count)
|
||||||
|
|
||||||
expect(response).to have_http_status(:unprocessable_entity)
|
expect(response).to have_http_status(:unprocessable_entity)
|
||||||
@@ -1139,12 +1511,11 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
sign_in_as(member)
|
sign_in_as(member)
|
||||||
|
|
||||||
expect {
|
expect {
|
||||||
post '/posts', params: {
|
post '/posts', params: post_write_params(
|
||||||
title: 'tag versioned post',
|
title: 'tag versioned post',
|
||||||
url: 'https://example.com/tag-versioned-post',
|
url: 'https://example.com/tag-versioned-post',
|
||||||
tags: 'spec_tag',
|
tags: 'spec_tag',
|
||||||
thumbnail: dummy_upload
|
thumbnail: dummy_upload)
|
||||||
}
|
|
||||||
}.to change { tag.reload.tag_versions.count }.by(1)
|
}.to change { tag.reload.tag_versions.count }.by(1)
|
||||||
|
|
||||||
expect(response).to have_http_status(:created)
|
expect(response).to have_http_status(:created)
|
||||||
@@ -1164,10 +1535,9 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
tag2 = Tag.create!(tag_name: tag_name2, category: :general)
|
tag2 = Tag.create!(tag_name: tag_name2, category: :general)
|
||||||
|
|
||||||
expect {
|
expect {
|
||||||
put "/posts/#{post_record.id}", params: {
|
put "/posts/#{post_record.id}", params: post_write_params(
|
||||||
title: 'updated title',
|
title: 'updated title',
|
||||||
tags: 'spec_tag_2'
|
tags: 'spec_tag_2')
|
||||||
}
|
|
||||||
}.to change { tag2.reload.tag_versions.count }.by(1)
|
}.to change { tag2.reload.tag_versions.count }.by(1)
|
||||||
|
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ RSpec.describe "nico:sync" do
|
|||||||
url: post.url,
|
url: post.url,
|
||||||
thumbnail_base: post.thumbnail_base,
|
thumbnail_base: post.thumbnail_base,
|
||||||
tags: snapshot_tags(post),
|
tags: snapshot_tags(post),
|
||||||
parent: post.parent,
|
parent_post_ids: post.snapshot_parent_post_ids.join(' '),
|
||||||
original_created_from: post.original_created_from,
|
original_created_from: post.original_created_from,
|
||||||
original_created_before: post.original_created_before,
|
original_created_before: post.original_created_before,
|
||||||
created_at: Time.current,
|
created_at: Time.current,
|
||||||
|
|||||||
Reference in New Issue
Block a user