このコミットが含まれているのは:
2026-10-12 20:10:04 +09:00
コミット 2e98ed02e3
41個のファイルの変更、624行の追加、244行の削除
+2 -2
ファイルの表示
@@ -10,8 +10,8 @@ RSpec.describe 'Tag description Wiki compatibility', type: :request do
end
let!(:revisions) do
first = page.current_revision
second = Wiki::Commit.content!(page:, body: 'old-second', created_user: user)
third = Wiki::Commit.content!(page:, body: 'old-third', created_user: user)
second = Wiki::Commit.legacy_content!(page:, body: 'old-second', created_user: user)
third = Wiki::Commit.legacy_content!(page:, body: 'old-third', created_user: user)
[first, second, third]
end
let!(:tag) { create(:tag, primary_tag_name: tag_name) }
+47 -7
ファイルの表示
@@ -32,20 +32,38 @@ RSpec.describe 'Tag description Wiki dual writes', type: :request do
headers: headers
end
it 'creates a description without implicitly creating Wiki records' do
it 'creates a description with matching Wiki content and histories' do
expect { create_description }
.to change(WikiPage, :count).by(0)
.and change(WikiRevision, :count).by(0)
.and change(WikiVersion, :count).by(0)
.to change(WikiPage, :count).by(1)
.and change(WikiRevision, :count).by(1)
.and change(WikiVersion, :count).by(1)
.and change(TagDescription, :count).by(1)
.and change(TagDescriptionVersion, :count).by(1)
expect(response).to have_http_status(:created)
description = TagDescription.find_by!(tag_id: tag.id, language_code: 'ja')
expect(description).to have_attributes(body: 'initial', version_no: 1)
page = WikiPage.find_by!(tag_name:)
expect(page.body).to eq(description.body)
expect(page.current_revision.body).to eq(description.body)
expect(page.wiki_versions.sole.body).to eq(description.body)
expect(json.fetch('id')).to eq(description.id)
expect(TagDescriptionVersion.find_by!(tag_id: tag.id, language_code: 'ja'))
.to have_attributes(body: 'initial', version_no: 1, created_by_user_id: user.id)
end
it 'reuses an existing compatibility page and synchronises its histories' do
page = Wiki::Commit.create_legacy_content!(
tag_name:, body: 'legacy', created_by_user: user)
expect { create_description }
.to change(WikiPage, :count).by(0)
.and change(WikiRevision, :count).by(1)
.and change(WikiVersion, :count).by(1)
expect(response).to have_http_status(:created)
description = TagDescription.find_by!(tag_id: tag.id)
expect(page.reload.body).to eq(description.body)
expect(TagDescriptionVersion.find(page.current_revision.id).body).to eq(page.body)
end
['missing_description', 'orphan_description'].each do |title|
it "rejects the bare title #{ title } without writing any resource" do
create(:tag_name, name: title) if title == 'orphan_description'
@@ -70,7 +88,7 @@ RSpec.describe 'Tag description Wiki dual writes', type: :request do
end
end
[TagDescription, TagDescriptionVersion].each do |model|
[WikiPage, WikiRevision, WikiVersion, TagDescription, TagDescriptionVersion].each do |model|
it "rolls back new description creation when #{ model.name } persistence fails" do
before_state = stored_state
aggregate_failures do
@@ -81,6 +99,28 @@ RSpec.describe 'Tag description Wiki dual writes', type: :request do
end
end
it 'creates missing compatibility data without writing an ID-colliding Help page' do
description = TagDescription.create!(
id: 500, tag:, language_code: 'ja', body: 'before', version_no: 1)
help = WikiPage.create!(id: description.id,
tag_name: create(:tag_name, name: 'Help:unrelated'),
body: 'unrelated', created_user: user, updated_user: user)
Wiki::Commit.legacy_content!(page: help, body: help.body, created_user: user)
before_help = help.attributes
expect do
put "/wiki/#{ description.id }", params: { title: tag_name.name, body: 'after' },
headers: headers
end.to change(WikiPage, :count).by(1)
.and change(WikiRevision, :count).by(1)
.and change(WikiVersion, :count).by(1)
expect(response).to have_http_status(:ok)
expect(description.reload).to have_attributes(id: 500, body: 'after')
expect(help.reload.attributes).to eq(before_help)
compatibility = WikiPage.find_by!(tag_name:)
expect(compatibility.body).to eq(description.body)
expect(compatibility.current_revision.body).to eq(description.body)
end
describe 'PUT /wiki/:id' do
let!(:page) do
tag
@@ -144,10 +184,10 @@ RSpec.describe 'Tag description Wiki dual writes', type: :request do
page_state = page.attributes
description_state = description.attributes
update_description(body: 'before', title: 'Help:description_renamed')
update_description(body: 'before', title: 'description_renamed')
expect(response).to have_http_status(:ok)
expect(tag_name.reload.name).to eq('Help:description_renamed')
expect(tag_name.reload.name).to eq('description_renamed')
expect(WikiRevision.count).to eq(revision_count)
expect(WikiVersion.count).to eq(wiki_version_count)
expect(TagDescriptionVersion.count).to eq(description_version_count)
+9 -5
ファイルの表示
@@ -33,11 +33,11 @@ RSpec.describe 'Tag group Wiki facade writes', type: :request do
end
end
it 'creates the group and first snapshot without new Wiki records' do
it 'creates the group, snapshot and matching compatibility histories' do
expect { create_group }
.to change(WikiPage, :count).by(0)
.and change(WikiRevision, :count).by(0)
.and change(WikiVersion, :count).by(0)
.to change(WikiPage, :count).by(1)
.and change(WikiRevision, :count).by(1)
.and change(WikiVersion, :count).by(1)
expect(response).to have_http_status(:created)
group = group_rows('tag_groups', name: '新規').sole
expect(json).to include('id' => group.fetch('id'), 'title' => 'タググループ:新規')
@@ -45,6 +45,10 @@ RSpec.describe 'Tag group Wiki facade writes', type: :request do
version = group_version(group.fetch('id'), 1)
expect(version).to include('event_type' => 'create', 'created_by_user_id' => user.id)
expect(group).to include('created_by_user_id' => user.id, 'updated_by_user_id' => user.id)
page = WikiPage.joins(:tag_name).find_by!(tag_names: { name: 'タググループ:新規' })
expect(page.body).to eq('- 洗澡鹿')
expect(page.current_revision.body).to eq(page.body)
expect(page.wiki_versions.sole.body).to eq(page.body)
end
it 'keeps repeated placements but only one membership and one snapshot member' do
@@ -292,7 +296,7 @@ RSpec.describe 'Tag group Wiki facade writes', type: :request do
end
tables = TagGroupRecords::GROUP_TABLES
tables += ['wiki_pages', 'wiki_revisions', 'wiki_versions'] if operation == :update
tables += ['wiki_pages', 'wiki_revisions', 'wiki_versions']
tables.each do |table|
it "rolls back all writes after a real #{ table } write fails" do
expect_atomic_group_write(table) { write_group(operation, headers) }
+16 -15
ファイルの表示
@@ -71,17 +71,16 @@ RSpec.describe 'Tag group write integrity', type: :request do
expect(page.body).to eq('- 伊地知ニジカ')
end
it 'updates Help with a colliding group ID without altering the group' do
it 'rejects a standalone Help update despite a colliding group ID' do
page = create_help('Help body')
id = seed_group(name: '別グループ', id: page.id, members:, placements:)
group_before = TagGroup.find(id).attributes
version_before = group_version(id, 1)
put_wiki(id, title: 'Help:改名', body: 'Help after')
expect_no_writes { put_wiki(id, title: 'Help:改名', body: 'Help after') }
expect(response).to have_http_status(:ok)
expect(page.reload.body).to eq('Help after')
expect(page.title).to eq('Help:改名')
expect(page.reload.body).to eq('Help body')
expect(page.title).to eq('Help:既存')
expect(TagGroup.find(id).attributes).to eq(group_before)
expect(group_version(id, 1)).to eq(version_before)
end
@@ -99,7 +98,8 @@ RSpec.describe 'Tag group write integrity', type: :request do
it 'keeps a description update separate from a colliding group' do
page = Wiki::Commit.create_content!(
tag_name: tag.tag_name('ja'), body: 'description before', created_by_user: user)
id = seed_group(name: '更新', id: page.id, members:, placements:)
description = TagDescription.find_by!(tag_id: tag.id)
id = seed_group(name: '更新', id: description.id, members:, placements:)
group_before = TagGroup.find(id).attributes
put_wiki(id, title: '洗澡鹿', body: 'description after')
@@ -116,18 +116,19 @@ RSpec.describe 'Tag group write integrity', type: :request do
description = TagDescription.create!(
id:, tag:, language_code: 'ja', version_no: 1, body: 'description')
put_wiki(id, title: 'Help:既存', body: 'Help after')
expect_no_writes { put_wiki(id, title: 'Help:既存', body: 'Help after') }
expect(response).to have_http_status(:ok)
expect(page.reload.body).to eq('Help after')
expect(page.reload.body).to eq('Help before')
expect(description.reload.body).to eq('description')
expect(TagGroup.find(id).version_no).to eq(1)
expect_no_writes { put_wiki(id, title: '洗澡鹿', body: 'description after') }
put_wiki(id, title: '洗澡鹿', body: 'description after')
expect(response).to have_http_status(:ok)
expect(description.reload.body).to eq('description after')
put_wiki(id, title: 'タググループ:更新')
expect(response).to have_http_status(:ok)
expect(TagGroup.find(id).version_no).to eq(2)
expect(page.reload.body).to eq('Help after')
expect(description.reload.body).to eq('description')
expect(page.reload.body).to eq('Help before')
expect(description.reload.body).to eq('description after')
end
end
@@ -139,9 +140,9 @@ RSpec.describe 'Tag group write integrity', type: :request do
expect do
put "/tag_groups/#{ id }", params: native_payload(name: '改名'), headers:, as: :json
end.to change(WikiPage, :count).by(0)
.and change(WikiRevision, :count).by(0)
.and change(WikiVersion, :count).by(0)
end.to change(WikiPage, :count).by(1)
.and change(WikiRevision, :count).by(1)
.and change(WikiVersion, :count).by(1)
expect(response).to have_http_status(:ok)
expect(TagGroup.find(id).name).to eq('改名')
+5 -5
ファイルの表示
@@ -391,18 +391,18 @@ RSpec.describe 'Tag group native API', type: :request do
end
end
it 'creates native state without adding compatibility Wiki records' do
it 'creates native state and its compatibility histories' do
expect { native_write(:create) }
.to change(WikiPage, :count).by(0)
.and change(WikiRevision, :count).by(0)
.and change(WikiVersion, :count).by(0)
.to change(WikiPage, :count).by(1)
.and change(WikiRevision, :count).by(1)
.and change(WikiVersion, :count).by(1)
expect(response).to have_http_status(:created)
expect_group_snapshot(
json.fetch('id'), number: 1, members: snapshot_members,
placements:, description: '全体の説明')
end
[:update].each do |operation|
[:create, :update].each do |operation|
it "synchronises Wiki current state and histories on native #{ operation }" do
native_write(operation)
expect(response).to have_http_status(operation == :create ? :created : :ok)
+5 -7
ファイルの表示
@@ -11,7 +11,7 @@ RSpec.describe 'Wiki conflict handling', type: :request do
it 'returns 409 when base_revision_id is stale' do
page =
seed_wiki_page(
tag_name: create(:tag_name, name: 'wiki_conflict_request'),
tag_name: create(:tag, primary_name: 'wiki_conflict_request').tag_name('ja'),
body: 'first',
created_by_user: user,
message: 'init')
@@ -26,12 +26,10 @@ RSpec.describe 'Wiki conflict handling', type: :request do
base_revision_id: stale_id)
put "/wiki/#{ page.id }",
params: {
title: 'wiki_conflict_request',
body: 'third',
message: 'stale',
base_revision_id: stale_id,
},
params: { title: 'wiki_conflict_request',
body: 'third',
message: 'stale',
base_revision_id: stale_id },
headers: auth_headers(user)
expect(response).to have_http_status(:conflict)
+8 -4
ファイルの表示
@@ -7,17 +7,21 @@ RSpec.describe 'Wiki creation freeze', type: :request do
let!(:user) { create_member_user! }
let(:headers) { { 'X-Transfer-Code' => user.inheritance_code } }
it 'updates an independent group without implicitly creating a legacy page' do
it 'creates missing compatibility history when updating an independent group' do
id = seed_group(name: 'independent', description: 'before')
expect do
put "/tag_groups/#{ id }", params: {
name: 'independent', description: 'after', members: [], placements: [] },
headers:, as: :json
end.to change(WikiPage, :count).by(0)
.and change(WikiRevision, :count).by(0)
.and change(WikiVersion, :count).by(0)
end.to change(WikiPage, :count).by(1)
.and change(WikiRevision, :count).by(1)
.and change(WikiVersion, :count).by(1)
expect(response).to have_http_status(:ok)
expect(TagGroup.find(id).description).to eq('after')
page = WikiPage.joins(:tag_name).find_by!(tag_names: { name: 'タググループ:independent' })
expect(page.body).to include('after')
expect(page.current_revision.body).to eq(page.body)
expect(page.wiki_versions.sole.body).to eq(page.body)
end
context 'existing description write protection' do
+1 -1
ファイルの表示
@@ -11,7 +11,7 @@ RSpec.describe 'Wiki history integrity', type: :request do
def create_wiki_page title:, body: 'body', message: 'init', user: self.user
seed_wiki_page(
tag_name: create(:tag_name, name: title),
tag_name: create(:tag, primary_name: title).tag_name('ja'),
body:,
created_by_user: user,
message:)
+42
ファイルの表示
@@ -0,0 +1,42 @@
require 'rails_helper'
RSpec.describe 'Wiki resource ordering', type: :request do
include TagGroupRecords
let(:user) { create_member_user! }
it 'selects the latest twenty across both resources before applying the global limit' do
start = Time.utc(2026, 1, 1)
descriptions = 24.times.map do |index|
tag = create(:tag, primary_name: "rank_#{ index }")
TagDescription.create!(tag:, language_code: 'ja', body: 'current', version_no: 1,
updated_at: start + index.hours)
end
groups = [24, 25].map do |hour|
group = TagGroup.find(seed_group(name: "rank_group_#{ hour }"))
group.update_columns(updated_at: start + hour.hours)
group
end
get '/wiki', params: { title: 'rank' }
expect(response).to have_http_status(:ok)
expected = groups.reverse.map { ['tag_group', _1.id] } +
descriptions.last(18).reverse.map { ['tag_description', _1.id] }
expect(json.map { [_1.fetch('resource_type'), _1.fetch('id')] }).to eq(expected)
end
it 'breaks timestamp ties by resource kind and ID while retaining legacy overlaps' do
time = Time.utc(2026, 1, 1)
tag = create(:tag, primary_name: 'tied')
TagDescription.create!(id: 51, tag:, language_code: 'ja', body: 'current',
version_no: 1, updated_at: time)
group = TagGroup.find(seed_group(name: 'tied', id: 51))
group.update_columns(updated_at: time)
2.times do
get '/wiki', params: { title: 'tied' }
expect(json.map { [_1.fetch('resource_type'), _1.fetch('id')] })
.to eq([['tag_description', 51], ['tag_group', 51]])
end
end
end
+1 -1
ファイルの表示
@@ -129,7 +129,7 @@ RSpec.describe 'Wiki API', type: :request do
{ 'X-Transfer-Code' => user.inheritance_code }
end
let!(:test_tag_name) { create(:tag_name, name: 'TestPage') }
let!(:test_tag_name) { create(:tag, primary_name: 'TestPage').tag_name('ja') }
let!(:page) do
seed_wiki_page(
+9 -9
ファイルの表示
@@ -9,29 +9,29 @@ RSpec.describe 'Unmigrated Wiki compatibility', type: :request do
end
let!(:first_revision) { page.current_revision }
let!(:last_revision) do
Wiki::Commit.content!(page:, body: 'after', created_user: user,
message: 'legacy edit', base_revision_id: first_revision.id)
Wiki::Commit.legacy_content!(page:, body: 'after', created_user: user,
message: 'legacy edit', base_revision_id: first_revision.id)
end
before { expect(TagDescription.exists?(id: page.id)).to be(false) }
it 'keeps a Tag-linked legacy page in the legacy domain when its title changes' do
it 'rejects standalone updates even after a Tag is linked to the legacy page' do
create(:tag, primary_tag_name: tag_name)
original = page.reload.attributes
expect do
put "/wiki/#{ page.id }",
params: { title: 'legacy_tag_renamed', body: 'changed legacy body',
message: 'legacy rename', base_revision_id: last_revision.id },
headers: { 'X-Transfer-Code' => user.inheritance_code }
end.to change(WikiRevision, :count).by(1)
.and change(WikiVersion, :count).by(1)
end.to change(WikiRevision, :count).by(0)
.and change(WikiVersion, :count).by(0)
.and change(TagDescription, :count).by(0)
.and change(TagDescriptionVersion, :count).by(0)
expect(response).to have_http_status(:ok)
expect(page.reload).to have_attributes(body: 'changed legacy body')
expect(page.title).to eq('legacy_tag_renamed')
expect(page.current_revision).to have_attributes(message: 'legacy rename')
expect(response).to have_http_status(:unprocessable_entity)
expect(page.reload.attributes).to eq(original)
expect(page.current_revision).to have_attributes(message: 'legacy edit')
end
include TagGroupRecords