This commit is contained in:
2026-04-18 05:14:29 +09:00
parent 1a02ced17d
commit 9b46e97eaf
2 changed files with 227 additions and 15 deletions
+205
View File
@@ -756,6 +756,211 @@ RSpec.describe 'Posts API', type: :request do
end
end
describe 'GET /posts/versions' do
let(:member) { create(:user, :member, name: 'version member') }
let(:t_v1) { Time.zone.local(2020, 1, 1, 12, 0, 0) }
let(:t_v2) { Time.zone.local(2020, 1, 2, 12, 0, 0) }
let(:t_other) { Time.zone.local(2020, 1, 3, 12, 0, 0) }
let(:oc_from) { Time.zone.local(2019, 12, 31, 0, 0, 0) }
let(:oc_before) { Time.zone.local(2020, 1, 1, 0, 0, 0) }
let!(:tag_name2) { TagName.create!(name: 'spec_tag_2') }
let!(:tag2) { Tag.create!(tag_name: tag_name2, category: :general) }
def snapshot_tags(post)
post.snapshot_tag_names.join(' ')
end
def create_post_version!(post, version_no:, event_type:, created_by_user:, created_at:)
PostVersion.create!(
post: post,
version_no: version_no,
event_type: event_type,
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: created_at,
created_by_user: created_by_user
)
end
let!(:v1) do
travel_to(t_v1) do
create_post_version!(
post_record,
version_no: 1,
event_type: 'create',
created_by_user: member,
created_at: t_v1
)
end
end
let!(:v2) do
post_record.post_tags.kept.find_by!(tag: tag).discard_by!(member)
PostTag.create!(post: post_record, tag: tag2, created_user: member)
post_record.update!(
title: 'updated spec post',
original_created_from: oc_from,
original_created_before: oc_before
)
travel_to(t_v2) do
create_post_version!(
post_record.reload,
version_no: 2,
event_type: 'update',
created_by_user: member,
created_at: t_v2
)
end
end
let!(:other_post_version) do
other_post = Post.create!(
title: 'other versioned post',
url: 'https://example.com/other-versioned'
)
PostTag.create!(post: other_post, tag: tag)
travel_to(t_other) do
create_post_version!(
other_post,
version_no: 1,
event_type: 'create',
created_by_user: member,
created_at: t_other
)
end
end
it 'returns versions for the specified post in reverse chronological order' do
get '/posts/versions', params: { post: post_record.id }
expect(response).to have_http_status(:ok)
expect(json).to include('versions', 'count')
expect(json.fetch('count')).to eq(2)
versions = json.fetch('versions')
expect(versions.map { |v| v['post_id'] }.uniq).to eq([post_record.id])
expect(versions.map { |v| v['version_no'] }).to eq([2, 1])
latest = versions.first
expect(latest).to include(
'post_id' => post_record.id,
'version_no' => 2,
'event_type' => 'update',
'created_by_user' => {
'id' => member.id,
'name' => member.name
}
)
expect(latest.fetch('title')).to eq(
'current' => 'updated spec post',
'prev' => 'spec post'
)
expect(latest.fetch('url')).to eq(
'current' => 'https://example.com/spec',
'prev' => 'https://example.com/spec'
)
expect(latest.fetch('thumbnail')).to eq(
'current' => nil,
'prev' => nil
)
expect(latest.fetch('thumbnail_base')).to eq(
'current' => nil,
'prev' => nil
)
expect(latest.fetch('tags')).to include(
{ 'name' => 'spec_tag_2', 'type' => 'added' },
{ 'name' => 'spec_tag', 'type' => 'removed' }
)
expect(latest.fetch('original_created_from')).to eq(
'current' => oc_from.iso8601,
'prev' => nil
)
expect(latest.fetch('original_created_before')).to eq(
'current' => oc_before.iso8601,
'prev' => nil
)
expect(latest.fetch('created_at')).to eq(t_v2.iso8601)
first = versions.second
expect(first).to include(
'post_id' => post_record.id,
'version_no' => 1,
'event_type' => 'create',
'created_by_user' => {
'id' => member.id,
'name' => member.name
}
)
expect(first.fetch('title')).to eq(
'current' => 'spec post',
'prev' => nil
)
expect(first.fetch('tags')).to include(
{ 'name' => 'spec_tag', 'type' => 'added' }
)
expect(first.fetch('created_at')).to eq(t_v1.iso8601)
end
it 'filters versions by tag when the current snapshot includes the tag' do
get '/posts/versions', params: { post: post_record.id, tag: tag2.id }
expect(response).to have_http_status(:ok)
expect(json.fetch('count')).to eq(1)
versions = json.fetch('versions')
expect(versions.size).to eq(1)
expect(versions[0]['post_id']).to eq(post_record.id)
expect(versions[0]['version_no']).to eq(2)
expect(versions[0]['tags']).to include(
{ 'name' => 'spec_tag_2', 'type' => 'added' }
)
end
it 'matches tag filter against current tags snapshot only' do
get '/posts/versions', params: { post: post_record.id, tag: tag.id }
expect(response).to have_http_status(:ok)
expect(json.fetch('count')).to eq(1)
versions = json.fetch('versions')
expect(versions.size).to eq(1)
expect(versions[0]['version_no']).to eq(1)
expect(versions[0]['tags']).to include(
{ 'name' => 'spec_tag', 'type' => 'added' }
)
end
it 'returns empty when tag does not exist' do
get '/posts/versions', params: { tag: 999_999_999 }
expect(response).to have_http_status(:ok)
expect(json.fetch('versions')).to eq([])
expect(json.fetch('count')).to eq(0)
end
it 'clamps page and limit to at least 1' do
get '/posts/versions', params: { post: post_record.id, page: 0, limit: 0 }
expect(response).to have_http_status(:ok)
expect(json.fetch('count')).to eq(2)
versions = json.fetch('versions')
expect(versions.size).to eq(1)
expect(versions[0]['version_no']).to eq(2)
end
end
describe 'POST /posts/:id/viewed' do
let(:user) { create(:user) }