Reviewed-on: #432 Co-authored-by: miteruzo <miteruzo@naver.com>
このコミットはプルリクエスト #432 でマージされました。
このコミットが含まれているのは:
@@ -9,9 +9,9 @@ RSpec.describe 'Shared resource IDs across MySQL connections', type: :model do
|
||||
expect(ApplicationRecord.connection.adapter_name).to match(/mysql/i)
|
||||
expect(ApplicationRecord.connection_pool.size).to be >= 3
|
||||
@prefix = "shared_ids_#{ SecureRandom.hex(8) }"
|
||||
@names = [0, 1].map { create(:tag_name, name: "#{ @prefix }_#{ _1 }") }
|
||||
@tag = Tag.create!(tag_name_id: @names.first.id, category: :general)
|
||||
@names.first.update!(tag_id: @tag.id)
|
||||
@tag = create(:tag, primary_name: "#{ @prefix }_owner")
|
||||
@group = TagGroup.create!(name: "#{ @prefix }_owner", version_no: 1)
|
||||
@tag_ids = Queue.new
|
||||
@threads = []
|
||||
@release = Queue.new
|
||||
end
|
||||
@@ -23,17 +23,21 @@ RSpec.describe 'Shared resource IDs across MySQL connections', type: :model do
|
||||
raise 'Shared ID worker did not finish' unless thread.join(15)
|
||||
end
|
||||
ensure
|
||||
TagDescriptionVersion.where(tag_id: @tag.id).delete_all if @tag
|
||||
if @group
|
||||
TagGroupVersion.where(tag_group_id: @group.id).delete_all
|
||||
@group.delete
|
||||
end
|
||||
if @prefix
|
||||
ExternalTag.where(name: "#{ @prefix }_external").delete_all
|
||||
TagGroup.where(name: "#{ @prefix }_group").delete_all
|
||||
end
|
||||
TagDescription.where(tag_id: @tag.id).delete_all if @tag
|
||||
if @names
|
||||
ids = @names.map(&:id)
|
||||
TagName.where(id: ids).update_all(tag_id: nil)
|
||||
Tag.where(tag_name_id: ids).delete_all
|
||||
TagName.where(id: ids).delete_all
|
||||
end
|
||||
ids = []
|
||||
ids << @tag_ids.pop until @tag_ids.empty? if @tag_ids
|
||||
ids << @tag.id if @tag
|
||||
TagName.where(tag_id: ids).delete_all
|
||||
Tag.where(id: ids).delete_all
|
||||
end
|
||||
end
|
||||
|
||||
@@ -51,11 +55,19 @@ RSpec.describe 'Shared resource IDs across MySQL connections', type: :model do
|
||||
def create_resource kind
|
||||
case kind
|
||||
when :tag
|
||||
Tag.create!(tag_name_id: @names.last.id, category: :general)
|
||||
Tag.create!(category: :general).tap { @tag_ids << _1.id }
|
||||
when :external
|
||||
ExternalTag.create!(platform: :nico, name: "#{ @prefix }_external")
|
||||
when :description
|
||||
TagDescription.create!(tag: @tag, language_code: 'ja', body: '', version_no: 1)
|
||||
when :description_version
|
||||
TagDescriptionVersion.create!(
|
||||
tag_id: @tag.id, language_code: 'ja', body: 'body', version_no: 1,
|
||||
event_type: 'create', created_at: Time.current)
|
||||
when :group_version
|
||||
TagGroupVersion.create!(
|
||||
tag_group: @group, name: @group.name, version_no: 1, event_type: 'create',
|
||||
members: [], placements: [], created_at: Time.current)
|
||||
when :group
|
||||
TagGroup.create!(name: "#{ @prefix }_group", version_no: 1)
|
||||
end
|
||||
@@ -88,7 +100,8 @@ RSpec.describe 'Shared resource IDs across MySQL connections', type: :model do
|
||||
thread.value
|
||||
end
|
||||
|
||||
{ tags: [:external, :tag], wiki: [:description, :group] }.each do |space, kinds|
|
||||
{ tags: [:external, :tag], wiki: [:description, :group],
|
||||
wiki_history: [:description_version, :group_version] }.each do |space, kinds|
|
||||
[false, true].each do |rollback|
|
||||
it "serialises #{ space } inserts through #{ rollback ? 'rollback' : 'commit' }" do
|
||||
tables = SharedResourceIdAllocator::SPACES.fetch(space.to_s)
|
||||
@@ -96,7 +109,7 @@ RSpec.describe 'Shared resource IDs across MySQL connections', type: :model do
|
||||
ApplicationRecord.connection.select_value("SELECT MAX(id) FROM #{ table }").to_i
|
||||
end
|
||||
first, (first_id, connection_id, lock_name) = hold_creation(kinds.first, rollback:)
|
||||
expect(first_id).to eq(maxima.max + 1)
|
||||
expect(first_id).to be > maxima.max
|
||||
connection = ApplicationRecord.connection
|
||||
quoted_lock = connection.quote(lock_name)
|
||||
expect(connection.select_value("SELECT IS_USED_LOCK(#{ quoted_lock })").to_i)
|
||||
@@ -110,7 +123,7 @@ RSpec.describe 'Shared resource IDs across MySQL connections', type: :model do
|
||||
expect(await_queue(started)).not_to eq(connection_id)
|
||||
@release << true
|
||||
expect(result(first)).to eq(first_id)
|
||||
expect(result(second)).to eq(rollback ? first_id : first_id + 1)
|
||||
expect(result(second)).to be > first_id
|
||||
expect(connection.select_value("SELECT IS_FREE_LOCK(#{ quoted_lock })").to_i).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,6 +1,103 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe SharedResourceIdAllocator do
|
||||
let(:tag) { create(:tag) }
|
||||
|
||||
def resource table, id: nil, number: 1
|
||||
case table
|
||||
when 'tags'
|
||||
Tag.new(id:, category: :general)
|
||||
when 'external_tags'
|
||||
ExternalTag.new(id:, platform: :nico, name: "external #{ number }")
|
||||
when 'tag_descriptions'
|
||||
TagDescription.new(id:, tag:, language_code: 'ja', body: 'body', version_no: number)
|
||||
when 'tag_groups'
|
||||
TagGroup.new(id:, name: "group #{ number }", version_no: number)
|
||||
when 'tag_description_versions'
|
||||
TagDescriptionVersion.new(
|
||||
id:, tag_id: tag.id, language_code: 'ja', body: 'body', version_no: number,
|
||||
event_type: 'create', created_at: Time.current)
|
||||
when 'tag_group_versions'
|
||||
TagGroupVersion.new(
|
||||
id:, tag_group_id: group.id, name: group.name, version_no: number,
|
||||
event_type: 'create', members: [], placements: [], created_at: Time.current)
|
||||
end
|
||||
end
|
||||
|
||||
let(:group) { TagGroup.create!(name: 'history owner', version_no: 1) }
|
||||
|
||||
described_class::SPACES.each do |space, tables|
|
||||
tables.permutation.each do |first_table, second_table|
|
||||
it "keeps deleted #{ first_table } IDs reserved for #{ second_table }" do
|
||||
first = resource(first_table)
|
||||
first.save!
|
||||
used_id = first.id
|
||||
first.class.where(id: first.id).delete_all
|
||||
second = resource(second_table)
|
||||
second.save!
|
||||
expect(second.id).to be > used_id
|
||||
end
|
||||
|
||||
it "advances #{ space } after an explicit INSERT in #{ first_table }" do
|
||||
first = resource(first_table)
|
||||
first.save!
|
||||
high = first.id + 100
|
||||
explicit = resource(first_table, id: high, number: 2)
|
||||
first.class.where(id: first.id).delete_all
|
||||
explicit.save!
|
||||
explicit.class.where(id: explicit.id).delete_all
|
||||
second = resource(second_table)
|
||||
second.save!
|
||||
expect(second.id).to be > high
|
||||
end
|
||||
end
|
||||
|
||||
it "allows explicit overlapping Id.s in #{ space }" do
|
||||
first = resource(tables.first)
|
||||
first.save!
|
||||
|
||||
second = resource(tables.last, id: first.id)
|
||||
second.save!
|
||||
|
||||
expect(second.id).to eq(first.id)
|
||||
expect(first.reload.id).to eq(first.id)
|
||||
end
|
||||
|
||||
it "preserves historical #{ space } overlaps without repairing them" do
|
||||
first = resource(tables.first)
|
||||
first.save!
|
||||
second = resource(tables.last)
|
||||
second.save!
|
||||
second.class.where(id: second.id).update_all(id: first.id)
|
||||
second = second.class.find(first.id)
|
||||
snapshots = [first.reload.attributes, second.reload.attributes]
|
||||
next_record = resource(tables.last, number: 2)
|
||||
next_record.save!
|
||||
expect(next_record.id).to be > first.id
|
||||
expect(first.reload.attributes).to eq(snapshots.first)
|
||||
expect(second.reload.attributes).to eq(snapshots.last)
|
||||
end
|
||||
end
|
||||
|
||||
it 'reserves a Tag ID referenced only by remaining description history' do
|
||||
high = [Tag.maximum(:id).to_i, ExternalTag.maximum(:id).to_i].max + 1000
|
||||
TagDescriptionVersion.create!(
|
||||
tag_id: high, language_code: 'ja', version_no: 1, body: 'history',
|
||||
event_type: 'create', created_at: Time.current)
|
||||
expect(create(:tag).id).to be > high
|
||||
expect(create(:external_tag).id).to be > high
|
||||
end
|
||||
|
||||
it 'reserves a group ID referenced only by remaining group history' do
|
||||
high = [TagGroup.maximum(:id).to_i, TagDescription.maximum(:id).to_i].max + 1000
|
||||
version = resource('tag_group_versions')
|
||||
version.save!
|
||||
TagGroupVersion.where(id: version.id).update_all(tag_group_id: high)
|
||||
expect(TagGroup.create!(name: 'replacement', version_no: 1).id).to be > high
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.describe SharedResourceIdAllocator, 'lock lifecycle' do
|
||||
let(:record) { build(:external_tag) }
|
||||
let(:connection) { double('connection') }
|
||||
let(:transaction) { double('transaction', open?: true) }
|
||||
@@ -11,6 +108,14 @@ RSpec.describe SharedResourceIdAllocator do
|
||||
config = double('config', database: 'test_shared_ids')
|
||||
allow(connection).to receive_message_chain(:pool, :db_config).and_return(config)
|
||||
allow(connection).to receive(:quote) { "'#{ _1 }'" }
|
||||
allow(connection).to receive(:quote_column_name) { "`#{ _1 }`" }
|
||||
allow(connection).to receive(:execute) { sql << _1 }
|
||||
allow(connection).to receive(:select_all) do |statement|
|
||||
tables = described_class::SPACES.values.flatten.select do |table|
|
||||
statement.include?("'#{ table }'")
|
||||
end
|
||||
tables.map { { 'TABLE_NAME' => _1, 'ENGINE' => 'InnoDB', 'AUTO_INCREMENT' => 9 } }
|
||||
end
|
||||
allow(connection).to receive(:quote_table_name) { "`#{ _1 }`" }
|
||||
allow(connection).to receive(:select_value) do |statement|
|
||||
sql << statement
|
||||
@@ -35,7 +140,6 @@ RSpec.describe SharedResourceIdAllocator do
|
||||
described_class.assign!(record)
|
||||
expect(record.id).to eq(9)
|
||||
expect(sql.grep(/RELEASE_LOCK/)).to be_empty
|
||||
expect(sql.grep(/FOR UPDATE/).size).to eq(2)
|
||||
callbacks.fetch(event).call
|
||||
expect(sql.grep(/RELEASE_LOCK/).size).to eq(1)
|
||||
end
|
||||
@@ -49,22 +153,22 @@ RSpec.describe SharedResourceIdAllocator do
|
||||
end
|
||||
|
||||
it 'releases a successfully acquired lock when allocation fails and rolls back' do
|
||||
allow(connection).to receive(:select_value).with(/FOR UPDATE/).and_raise('query failed')
|
||||
allow(connection).to receive(:select_all).and_raise('query failed')
|
||||
expect { described_class.assign!(record) }.to raise_error(RuntimeError, 'query failed')
|
||||
callbacks.fetch(:after_rollback).call
|
||||
expect(sql.grep(/RELEASE_LOCK/).size).to eq(1)
|
||||
end
|
||||
|
||||
it 'preserves explicitly supplied historical IDs' do
|
||||
record.id = 5
|
||||
it 'accepts an explicit ID above the reserved position' do
|
||||
record.id = 50
|
||||
described_class.assign!(record)
|
||||
expect(record.id).to eq(5)
|
||||
expect(record.id).to eq(50)
|
||||
expect(sql.grep(/FOR UPDATE/)).to be_empty
|
||||
callbacks.fetch(:after_commit).call
|
||||
end
|
||||
|
||||
it 'uses the same named lock within each space and different locks between spaces' do
|
||||
locks = [Tag, ExternalTag, TagDescription, TagGroup].map do |model|
|
||||
locks = [Tag, ExternalTag, TagDescription, TagGroup, TagDescriptionVersion, TagGroupVersion].map do |model|
|
||||
allow(model).to receive(:connection).and_return(connection)
|
||||
allow(model).to receive(:current_transaction).and_return(transaction)
|
||||
allow(model).to receive(:uncached).and_yield
|
||||
@@ -76,7 +180,8 @@ RSpec.describe SharedResourceIdAllocator do
|
||||
end
|
||||
expect(locks[0]).to eq(locks[1])
|
||||
expect(locks[2]).to eq(locks[3])
|
||||
expect(locks[0]).not_to eq(locks[2])
|
||||
expect(locks[4]).to eq(locks[5])
|
||||
expect([locks[0], locks[2], locks[4]].uniq.size).to eq(3)
|
||||
end
|
||||
|
||||
it 'reports lock release failures' do
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
require 'digest'
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Wiki::Commit do
|
||||
include WikiRecords
|
||||
let(:user) { create_member_user! }
|
||||
|
||||
def create_page title:, body: 'initial body'
|
||||
seed_wiki_page(
|
||||
tag_name: create(:tag, primary_name: title).tag_name('ja'),
|
||||
body:,
|
||||
created_by_user: user,
|
||||
message: 'init')
|
||||
end
|
||||
|
||||
describe '.create_content!' do
|
||||
it 'rejects standalone Help creation without adding page or history' do
|
||||
name = create(:tag_name, name: 'Help:frozen_service')
|
||||
counts = [WikiPage.count, WikiRevision.count, WikiVersion.count]
|
||||
expect do
|
||||
described_class.create_content!(tag_name: name, body: 'initial', created_by_user: user)
|
||||
end.to raise_error(ActiveRecord::RecordInvalid)
|
||||
expect([WikiPage.count, WikiRevision.count, WikiVersion.count]).to eq(counts)
|
||||
end
|
||||
|
||||
it 'keeps the internal compatibility writer available' do
|
||||
name = create(:tag_name, name: 'タググループ:compatibility_service')
|
||||
page = nil
|
||||
expect do
|
||||
page = described_class.create_legacy_content!(
|
||||
tag_name: name, body: 'initial', created_by_user: user)
|
||||
end.to change(WikiPage, :count).by(1)
|
||||
.and change(WikiRevision, :count).by(1)
|
||||
.and change(WikiVersion, :count).by(1)
|
||||
expect(page.current_revision.body).to eq('initial')
|
||||
expect(page.wiki_versions.sole.body).to eq('initial')
|
||||
end
|
||||
|
||||
it 'rejects body that becomes blank after normalisation' do
|
||||
tag_name = create(:tag, primary_name: 'commit_integrity_blank').tag_name('ja')
|
||||
|
||||
expect {
|
||||
described_class.create_content!(
|
||||
tag_name:,
|
||||
body: "\r\n\r\n",
|
||||
created_by_user: user,
|
||||
message: 'blank')
|
||||
}
|
||||
.to raise_error(ActiveRecord::RecordInvalid)
|
||||
|
||||
expect(WikiPage.where(tag_name:)).not_to exist
|
||||
end
|
||||
end
|
||||
|
||||
describe '.content!' do
|
||||
it 'rejects standalone Help updates without modifying histories' do
|
||||
page = seed_wiki_page(tag_name: create(:tag_name, name: 'Help:standalone'),
|
||||
body: 'before', created_by_user: user)
|
||||
counts = [WikiRevision.count, WikiVersion.count]
|
||||
expect do
|
||||
described_class.content!(page:, body: 'after', created_user: user)
|
||||
end.to raise_error(ActiveRecord::RecordInvalid)
|
||||
expect(page.reload.body).to eq('before')
|
||||
expect([WikiRevision.count, WikiVersion.count]).to eq(counts)
|
||||
end
|
||||
|
||||
it 'updates page body, revision, and version' do
|
||||
page = create_page(title: 'commit_integrity_update', body: 'before')
|
||||
current_id = page.current_revision.id
|
||||
|
||||
expect {
|
||||
described_class.content!(
|
||||
page:,
|
||||
body: 'after',
|
||||
created_user: user,
|
||||
message: 'edit',
|
||||
base_revision_id: current_id)
|
||||
}
|
||||
.to change(WikiRevision, :count).by(1)
|
||||
.and change(WikiVersion, :count).by(1)
|
||||
|
||||
page.reload
|
||||
version = page.wiki_versions.order(:version_no).last
|
||||
|
||||
expect(page.body).to eq('after')
|
||||
expect(version.body).to eq('after')
|
||||
expect(version.reason).to eq('edit')
|
||||
end
|
||||
|
||||
it 'does not record tag_version on body-only wiki update' do
|
||||
tag_name = create(:tag_name, name: 'commit_integrity_linked_tag')
|
||||
tag = create(:tag, primary_tag_name: tag_name, category: :general)
|
||||
|
||||
page =
|
||||
seed_wiki_page(
|
||||
tag_name:,
|
||||
body: 'before',
|
||||
created_by_user: user,
|
||||
message: 'init')
|
||||
|
||||
TagVersionRecorder.record!(
|
||||
tag:,
|
||||
event_type: :create,
|
||||
created_by_user: user)
|
||||
|
||||
before_count = tag.reload.tag_versions.count
|
||||
|
||||
described_class.content!(
|
||||
page:,
|
||||
body: 'after',
|
||||
created_user: user,
|
||||
message: 'edit',
|
||||
base_revision_id: page.current_revision.id)
|
||||
|
||||
expect(tag.reload.tag_versions.count).to eq(before_count)
|
||||
end
|
||||
|
||||
it 'raises conflict and leaves page, revision, and version unchanged' do
|
||||
page = create_page(title: 'commit_integrity_conflict', body: 'first')
|
||||
stale_id = page.current_revision.id
|
||||
|
||||
described_class.content!(
|
||||
page:,
|
||||
body: 'second',
|
||||
created_user: user,
|
||||
message: 'second',
|
||||
base_revision_id: stale_id)
|
||||
|
||||
page.reload
|
||||
before_body = page.body
|
||||
before_revision_count = page.wiki_revisions.count
|
||||
before_version_count = page.wiki_versions.count
|
||||
|
||||
expect {
|
||||
described_class.content!(
|
||||
page:,
|
||||
body: 'third',
|
||||
created_user: user,
|
||||
message: 'stale',
|
||||
base_revision_id: stale_id)
|
||||
}
|
||||
.to raise_error(Wiki::Commit::Conflict)
|
||||
|
||||
page.reload
|
||||
expect(page.body).to eq(before_body)
|
||||
expect(page.wiki_revisions.count).to eq(before_revision_count)
|
||||
expect(page.wiki_versions.count).to eq(before_version_count)
|
||||
end
|
||||
|
||||
it 'deduplicates duplicated missing wiki lines' do
|
||||
page = create_page(title: 'commit_integrity_dedup', body: 'before')
|
||||
duplicated = 'commit_integrity_duplicate_line'
|
||||
|
||||
described_class.content!(
|
||||
page:,
|
||||
body: "#{ duplicated }\n#{ duplicated }",
|
||||
created_user: user,
|
||||
message: 'dedup',
|
||||
base_revision_id: page.current_revision.id)
|
||||
|
||||
revision = page.reload.current_revision
|
||||
|
||||
expect(WikiLine.where(body: duplicated).count).to eq(1)
|
||||
expect(revision.wiki_revision_lines.count).to eq(2)
|
||||
expect(revision.wiki_revision_lines.pluck(:wiki_line_id).uniq.size).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.redirect!' do
|
||||
it 'raises because redirect revisions are deprecated' do
|
||||
page = create_page(title: 'commit_integrity_redirect_source', body: 'source')
|
||||
target = create_page(title: 'commit_integrity_redirect_target', body: 'target')
|
||||
|
||||
expect {
|
||||
described_class.redirect!(
|
||||
page:,
|
||||
redirect_page: target,
|
||||
created_user: user,
|
||||
message: 'redirect',
|
||||
base_revision_id: page.current_revision.id)
|
||||
}
|
||||
.to raise_error(RuntimeError, '廃止しました.')
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,151 +0,0 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Wiki::Commit do
|
||||
include WikiRecords
|
||||
let(:user) { create_member_user! }
|
||||
|
||||
def create_page(title: 'commit_spec_page', body: 'initial body')
|
||||
tag_name = create(:tag, primary_name: title).tag_name('ja')
|
||||
|
||||
seed_wiki_page(
|
||||
tag_name:,
|
||||
body:,
|
||||
created_by_user: user,
|
||||
message: 'init')
|
||||
end
|
||||
|
||||
describe '.content!' do
|
||||
it 'stores normalised body in wiki_pages and wiki_versions' do
|
||||
page = create_page(title: 'commit_normalised_page')
|
||||
|
||||
described_class.content!(
|
||||
page:,
|
||||
body: "a\r\nb\r\n\r\n",
|
||||
created_user: user,
|
||||
message: 'init'
|
||||
)
|
||||
|
||||
page.reload
|
||||
version = page.wiki_versions.order(:version_no).last
|
||||
|
||||
expect(page.body).to eq("a\nb")
|
||||
expect(version.body).to eq("a\nb")
|
||||
expect(page.current_revision.lines_count).to eq(2)
|
||||
end
|
||||
|
||||
it 'deduplicates duplicated missing wiki lines before upsert' do
|
||||
page = create_page(title: 'commit_duplicate_line_page')
|
||||
duplicated = 'commit_duplicate_line'
|
||||
|
||||
described_class.content!(
|
||||
page:,
|
||||
body: "#{ duplicated }\n#{ duplicated }",
|
||||
created_user: user,
|
||||
message: 'init'
|
||||
)
|
||||
|
||||
page.reload
|
||||
|
||||
expect(WikiLine.where(body: duplicated).count).to eq(1)
|
||||
expect(page.current_revision.lines_count).to eq(2)
|
||||
expect(page.current_revision.wiki_revision_lines.count).to eq(2)
|
||||
end
|
||||
|
||||
it 'raises conflict when base_revision_id is stale' do
|
||||
page = create_page(title: 'commit_conflict_page')
|
||||
|
||||
first = described_class.content!(
|
||||
page:,
|
||||
body: 'first',
|
||||
created_user: user,
|
||||
message: 'first'
|
||||
)
|
||||
|
||||
described_class.content!(
|
||||
page:,
|
||||
body: 'second',
|
||||
created_user: user,
|
||||
message: 'second',
|
||||
base_revision_id: first.id
|
||||
)
|
||||
|
||||
expect {
|
||||
described_class.content!(
|
||||
page:,
|
||||
body: 'third',
|
||||
created_user: user,
|
||||
message: 'third',
|
||||
base_revision_id: first.id
|
||||
)
|
||||
}.to raise_error(Wiki::Commit::Conflict)
|
||||
end
|
||||
|
||||
it 'does not record tag version when corresponding tag has no versions' do
|
||||
tag_name = create(:tag_name, name: 'commit_linked_tag_without_versions')
|
||||
tag = create(:tag, primary_tag_name: tag_name, category: :general)
|
||||
|
||||
page =
|
||||
seed_wiki_page(
|
||||
tag_name:,
|
||||
body: 'before',
|
||||
created_by_user: user,
|
||||
message: 'init')
|
||||
|
||||
expect(tag.reload.tag_versions.count).to eq(0)
|
||||
|
||||
current_revision_id = page.current_revision.id
|
||||
|
||||
expect {
|
||||
described_class.content!(
|
||||
page:,
|
||||
body: 'after',
|
||||
created_user: user,
|
||||
message: 'edit',
|
||||
base_revision_id: current_revision_id)
|
||||
}.to change(WikiVersion, :count).by(1)
|
||||
|
||||
expect(tag.reload.tag_versions.count).to eq(0)
|
||||
end
|
||||
|
||||
it 'does not record tag version when corresponding tag has no versions' do
|
||||
tag_name = create(:tag_name, name: 'commit_linked_tag_without_versions')
|
||||
tag = create(:tag, primary_tag_name: tag_name, category: :general)
|
||||
|
||||
page =
|
||||
seed_wiki_page(
|
||||
tag_name:,
|
||||
body: 'before',
|
||||
created_by_user: user,
|
||||
message: 'init')
|
||||
|
||||
current_revision_id = page.current_revision.id
|
||||
|
||||
expect {
|
||||
described_class.content!(
|
||||
page:,
|
||||
body: 'after',
|
||||
created_user: user,
|
||||
message: 'edit',
|
||||
base_revision_id: current_revision_id)
|
||||
}.to change(WikiVersion, :count).by(1)
|
||||
|
||||
expect(tag.reload.tag_versions.count).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.redirect!' do
|
||||
it 'raises because redirect revisions are deprecated' do
|
||||
page = create_page(title: 'commit_redirect_source')
|
||||
target = create_page(title: 'commit_redirect_target')
|
||||
|
||||
expect {
|
||||
described_class.redirect!(
|
||||
page:,
|
||||
redirect_page: target,
|
||||
created_user: user,
|
||||
message: 'redirect'
|
||||
)
|
||||
}.to raise_error(RuntimeError, '廃止しました.')
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,100 +0,0 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe WikiVersionRecorder do
|
||||
include WikiRecords
|
||||
let(:user) { create_member_user! }
|
||||
|
||||
def create_page title:, body: 'body'
|
||||
seed_wiki_page(
|
||||
tag_name: create(:tag_name, name: title),
|
||||
body:,
|
||||
created_by_user: user,
|
||||
message: 'init')
|
||||
end
|
||||
|
||||
describe '.record!' do
|
||||
it 'records title, body, reason, user, and version number' do
|
||||
page = create_page(title: 'wiki_version_recorder_basic', body: 'body')
|
||||
|
||||
expect {
|
||||
described_class.record!(
|
||||
page:,
|
||||
event_type: :update,
|
||||
reason: 'manual reason',
|
||||
created_by_user: user)
|
||||
}
|
||||
.to change { page.reload.wiki_versions.count }.by(1)
|
||||
|
||||
version = page.wiki_versions.order(:version_no).last
|
||||
|
||||
expect(version).to have_attributes(
|
||||
version_no: 2,
|
||||
event_type: 'update',
|
||||
title: 'wiki_version_recorder_basic',
|
||||
body: 'body',
|
||||
reason: 'manual reason',
|
||||
created_by_user_id: user.id
|
||||
)
|
||||
end
|
||||
|
||||
it 'does not create duplicated update version for identical snapshot' do
|
||||
page = create_page(title: 'wiki_version_recorder_duplicate', body: 'body')
|
||||
|
||||
described_class.record!(
|
||||
page:,
|
||||
event_type: :update,
|
||||
reason: nil,
|
||||
created_by_user: user)
|
||||
|
||||
before_count = page.reload.wiki_versions.count
|
||||
|
||||
described_class.record!(
|
||||
page:,
|
||||
event_type: :update,
|
||||
reason: nil,
|
||||
created_by_user: user)
|
||||
|
||||
expect(page.reload.wiki_versions.count).to eq(before_count)
|
||||
end
|
||||
|
||||
it 'creates update version when title changes' do
|
||||
page = create_page(title: 'wiki_version_recorder_title_before', body: 'body')
|
||||
page.tag_name.update!(name: 'wiki_version_recorder_title_after')
|
||||
|
||||
expect {
|
||||
described_class.record!(
|
||||
page:,
|
||||
event_type: :update,
|
||||
reason: 'rename',
|
||||
created_by_user: user)
|
||||
}
|
||||
.to change { page.reload.wiki_versions.count }.by(1)
|
||||
|
||||
version = page.wiki_versions.order(:version_no).last
|
||||
|
||||
expect(version.title).to eq('wiki_version_recorder_title_after')
|
||||
expect(version.body).to eq('body')
|
||||
expect(version.reason).to eq('rename')
|
||||
end
|
||||
|
||||
it 'creates update version when body changes' do
|
||||
page = create_page(title: 'wiki_version_recorder_body', body: 'before')
|
||||
page.update!(body: 'after')
|
||||
|
||||
expect {
|
||||
described_class.record!(
|
||||
page:,
|
||||
event_type: :update,
|
||||
reason: 'body',
|
||||
created_by_user: user)
|
||||
}
|
||||
.to change { page.reload.wiki_versions.count }.by(1)
|
||||
|
||||
version = page.wiki_versions.order(:version_no).last
|
||||
|
||||
expect(version.title).to eq('wiki_version_recorder_body')
|
||||
expect(version.body).to eq('after')
|
||||
expect(version.reason).to eq('body')
|
||||
end
|
||||
end
|
||||
end
|
||||
新しいイシューから参照
ユーザーをブロックする