このコミットが含まれているのは:
@@ -102,6 +102,7 @@ RSpec.describe 'Tag group persistence constraints' do
|
|||||||
6.times { nodes = [{ 'tag_id' => tag.id, 'children' => nodes }] }
|
6.times { nodes = [{ 'tag_id' => tag.id, 'children' => nodes }] }
|
||||||
reject_statement do
|
reject_statement do
|
||||||
insert_group_row('tag_group_versions', {
|
insert_group_row('tag_group_versions', {
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
tag_group_id: group_id, version_no: 2, event_type: 'update', name: '制約確認',
|
tag_group_id: group_id, version_no: 2, event_type: 'update', name: '制約確認',
|
||||||
members: [{ 'tag_id' => tag.id, 'version_no' => tag.version_no }].to_json,
|
members: [{ 'tag_id' => tag.id, 'version_no' => tag.version_no }].to_json,
|
||||||
placements: nodes.to_json, created_at: Time.current })
|
placements: nodes.to_json, created_at: Time.current })
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ RSpec.describe 'TagVersion names database constraint' do
|
|||||||
|
|
||||||
def insert_snapshot!(snapshot)
|
def insert_snapshot!(snapshot)
|
||||||
TagVersion.insert_all!([{ tag_id: tag.id, version_no: 1, event_type: 'create',
|
TagVersion.insert_all!([{ tag_id: tag.id, version_no: 1, event_type: 'create',
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
name: 'snapshot_name', category: 'general',
|
name: 'snapshot_name', category: 'general',
|
||||||
aliases: '', parent_tag_ids: '', names: snapshot,
|
aliases: '', parent_tag_ids: '', names: snapshot,
|
||||||
created_at: Time.current }])
|
created_at: Time.current }])
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ RSpec.describe HistoryAccess do
|
|||||||
locale
|
locale
|
||||||
rows = ['ja', 'ain'].flat_map do |language|
|
rows = ['ja', 'ain'].flat_map do |language|
|
||||||
['create', 'discard', 'restore'].each_with_index.map do |event, index|
|
['create', 'discard', 'restore'].each_with_index.map do |event, index|
|
||||||
TagDescriptionVersion.create!(tag_id: tag.id, language_code: language,
|
TagDescriptionVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
|
tag_id: tag.id, language_code: language,
|
||||||
version_no: index + 1, event_type: language == 'ain' ? 'update' : event,
|
version_no: index + 1, event_type: language == 'ain' ? 'update' : event,
|
||||||
body: "#{ language } #{ index }", created_at: Time.utc(2024))
|
body: "#{ language } #{ index }", created_at: Time.utc(2024))
|
||||||
end
|
end
|
||||||
@@ -28,7 +30,9 @@ RSpec.describe HistoryAccess do
|
|||||||
public_metadata = described_class.set(user: nil) { described_class.authority }
|
public_metadata = described_class.set(user: nil) { described_class.authority }
|
||||||
admin = create(:user, :admin)
|
admin = create(:user, :admin)
|
||||||
admin_metadata = described_class.set(user: admin) { described_class.authority }
|
admin_metadata = described_class.set(user: admin) { described_class.authority }
|
||||||
TagVersion.create!(tag:, version_no: 1, event_type: 'discard', name: 'private',
|
TagVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
|
tag:, version_no: 1, event_type: 'discard', name: 'private',
|
||||||
category: 'general',
|
category: 'general',
|
||||||
names: { ja: { name: 'private', script_code: 'Jpan',
|
names: { ja: { name: 'private', script_code: 'Jpan',
|
||||||
auto_generated: false, aliases: [] } },
|
auto_generated: false, aliases: [] } },
|
||||||
@@ -52,7 +56,9 @@ RSpec.describe HistoryAccess do
|
|||||||
blob = ActiveStorage::Blob.create_before_direct_upload!(filename: 'private.png',
|
blob = ActiveStorage::Blob.create_before_direct_upload!(filename: 'private.png',
|
||||||
byte_size: 1, checksum: Base64.strict_encode64('checksum'), content_type: 'image/png')
|
byte_size: 1, checksum: Base64.strict_encode64('checksum'), content_type: 'image/png')
|
||||||
ActiveStorage::Attachment.create!(name: 'thumbnail', record: post, blob:)
|
ActiveStorage::Attachment.create!(name: 'thumbnail', record: post, blob:)
|
||||||
PostVersion.create!(post:, version_no: 1, event_type: 'discard', url: post.url,
|
PostVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
|
post:, version_no: 1, event_type: 'discard', url: post.url,
|
||||||
title: 'private', tags: '', tags_json: [], parent_post_ids: '', created_at: Time.utc(2024))
|
title: 'private', tags: '', tags_json: [], parent_post_ids: '', created_at: Time.utc(2024))
|
||||||
variant = ActiveStorage::VariantRecord.create!(blob:, variation_digest: 'spec-variation')
|
variant = ActiveStorage::VariantRecord.create!(blob:, variation_digest: 'spec-variation')
|
||||||
derived = ActiveStorage::Blob.create_before_direct_upload!(filename: 'derived.png',
|
derived = ActiveStorage::Blob.create_before_direct_upload!(filename: 'derived.png',
|
||||||
@@ -78,7 +84,9 @@ RSpec.describe 'Counts artifact policy and external references' do
|
|||||||
|
|
||||||
it 'does not expose a private linked tag name through an external history snapshot' do
|
it 'does not expose a private linked tag name through an external history snapshot' do
|
||||||
tag = create(:tag, primary_name: 'private-linked-name')
|
tag = create(:tag, primary_name: 'private-linked-name')
|
||||||
TagVersion.create!(tag:, version_no: 1, event_type: 'discard', name: 'private-linked-name',
|
TagVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
|
tag:, version_no: 1, event_type: 'discard', name: 'private-linked-name',
|
||||||
category: 'general', aliases: '', parent_tag_ids: '', created_at: Time.utc(2024),
|
category: 'general', aliases: '', parent_tag_ids: '', created_at: Time.utc(2024),
|
||||||
names: { ja: { name: 'private-linked-name', script_code: 'Jpan',
|
names: { ja: { name: 'private-linked-name', script_code: 'Jpan',
|
||||||
auto_generated: false, aliases: [] } })
|
auto_generated: false, aliases: [] } })
|
||||||
@@ -87,7 +95,9 @@ RSpec.describe 'Counts artifact policy and external references' do
|
|||||||
name: 'nico:public-external', linked_tags: 'private-linked-name',
|
name: 'nico:public-external', linked_tags: 'private-linked-name',
|
||||||
created_at: Time.utc(2024))
|
created_at: Time.utc(2024))
|
||||||
post = create(:post)
|
post = create(:post)
|
||||||
snapshot = PostVersion.create!(post:, version_no: 1, event_type: 'create',
|
snapshot = PostVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
|
post:, version_no: 1, event_type: 'create',
|
||||||
title: 'external reference', url: post.url, tags: '',
|
title: 'external reference', url: post.url, tags: '',
|
||||||
tags_json: [{ external_tag_id: external.id }], parent_post_ids: '',
|
tags_json: [{ external_tag_id: external.id }], parent_post_ids: '',
|
||||||
created_at: Time.utc(2024, 2))
|
created_at: Time.utc(2024, 2))
|
||||||
@@ -112,13 +122,17 @@ RSpec.describe 'Counts artifact policy and external references' do
|
|||||||
child = create(:tag, primary_name: 'referencing child')
|
child = create(:tag, primary_name: 'referencing child')
|
||||||
[[parent, 'discard', '', 'private parent'],
|
[[parent, 'discard', '', 'private parent'],
|
||||||
[child, 'create', parent.id.to_s, 'referencing child']].each do |tag, event, parents, name|
|
[child, 'create', parent.id.to_s, 'referencing child']].each do |tag, event, parents, name|
|
||||||
TagVersion.create!(tag:, version_no: 1, event_type: event, name:,
|
TagVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
|
tag:, version_no: 1, event_type: event, name:,
|
||||||
category: 'general', aliases: '', parent_tag_ids: parents, created_at: Time.utc(2023),
|
category: 'general', aliases: '', parent_tag_ids: parents, created_at: Time.utc(2023),
|
||||||
names: { ja: { name:, script_code: 'Jpan',
|
names: { ja: { name:, script_code: 'Jpan',
|
||||||
auto_generated: false, aliases: [] } })
|
auto_generated: false, aliases: [] } })
|
||||||
end
|
end
|
||||||
post = create(:post)
|
post = create(:post)
|
||||||
row = PostVersion.create!(post:, version_no: 1, event_type: 'create', title: 'snapshot',
|
row = PostVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
|
post:, version_no: 1, event_type: 'create', title: 'snapshot',
|
||||||
url: post.url, tags: '', parent_post_ids: '',
|
url: post.url, tags: '', parent_post_ids: '',
|
||||||
tags_json: [{ tag_id: child.id, version_no: 1, name: 'referencing child', sections: [] }],
|
tags_json: [{ tag_id: child.id, version_no: 1, name: 'referencing child', sections: [] }],
|
||||||
created_at: Time.utc(2024))
|
created_at: Time.utc(2024))
|
||||||
|
|||||||
@@ -23,9 +23,22 @@ RSpec.describe 'Locale activation', type: :model do
|
|||||||
.and_raise('backfill failed')
|
.and_raise('backfill failed')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'groups all tag histories from locale creation into one event' do
|
||||||
|
previous_ids = TagVersion.pluck(:id)
|
||||||
|
|
||||||
|
expect { build_locale.save! }.to change { event_rows.length }.by(1)
|
||||||
|
|
||||||
|
event = event_rows.last
|
||||||
|
expect(event).to include('event_type' => 'locales.create', 'created_by_user_id' => nil)
|
||||||
|
versions = TagVersion.where.not(id: previous_ids)
|
||||||
|
expect(versions.pluck(:tag_id)).to include(first.id, second.id)
|
||||||
|
expect(versions.pluck(:event_id).uniq).to eq([event.fetch('id')])
|
||||||
|
end
|
||||||
|
|
||||||
it 'rolls back a newly created locale and the preceding generated names' do
|
it 'rolls back a newly created locale and the preceding generated names' do
|
||||||
locale = build_locale
|
locale = build_locale
|
||||||
fail_second_generation!
|
fail_second_generation!
|
||||||
|
events = event_rows
|
||||||
|
|
||||||
expect { locale.save! }.to raise_error('backfill failed')
|
expect { locale.save! }.to raise_error('backfill failed')
|
||||||
|
|
||||||
@@ -33,6 +46,7 @@ RSpec.describe 'Locale activation', type: :model do
|
|||||||
expect(generated_names).to be_empty
|
expect(generated_names).to be_empty
|
||||||
expect(first.reload.name('ja')).to eq('activation_first')
|
expect(first.reload.name('ja')).to eq('activation_first')
|
||||||
expect(second.reload.name('ja')).to eq('activation_second')
|
expect(second.reload.name('ja')).to eq('activation_second')
|
||||||
|
expect(event_rows).to eq(events)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not backfill while the locale is inactive' do
|
it 'does not backfill while the locale is inactive' do
|
||||||
@@ -45,7 +59,14 @@ RSpec.describe 'Locale activation', type: :model do
|
|||||||
locale = build_locale(deprecated_at: Time.current)
|
locale = build_locale(deprecated_at: Time.current)
|
||||||
locale.save!
|
locale.save!
|
||||||
|
|
||||||
locale.update!(deprecated_at: nil)
|
previous_ids = TagVersion.pluck(:id)
|
||||||
|
expect { locale.update!(deprecated_at: nil) }.to change { event_rows.length }.by(1)
|
||||||
|
|
||||||
|
event = event_rows.last
|
||||||
|
expect(event).to include('event_type' => 'locales.restore', 'created_by_user_id' => nil)
|
||||||
|
versions = TagVersion.where.not(id: previous_ids)
|
||||||
|
expect(versions.pluck(:tag_id)).to include(first.id, second.id)
|
||||||
|
expect(versions.pluck(:event_id).uniq).to eq([event.fetch('id')])
|
||||||
|
|
||||||
expect(generated_names.pluck(:tag_id)).to contain_exactly(first.id, second.id)
|
expect(generated_names.pluck(:tag_id)).to contain_exactly(first.id, second.id)
|
||||||
expect(generated_names).to all(have_attributes(
|
expect(generated_names).to all(have_attributes(
|
||||||
@@ -57,11 +78,13 @@ RSpec.describe 'Locale activation', type: :model do
|
|||||||
locale.save!
|
locale.save!
|
||||||
previous_deprecated_at = locale.reload.deprecated_at
|
previous_deprecated_at = locale.reload.deprecated_at
|
||||||
fail_second_generation!
|
fail_second_generation!
|
||||||
|
events = event_rows
|
||||||
|
|
||||||
expect { locale.update!(deprecated_at: nil) }.to raise_error('backfill failed')
|
expect { locale.update!(deprecated_at: nil) }.to raise_error('backfill failed')
|
||||||
|
|
||||||
expect(locale.reload.deprecated_at).to eq(previous_deprecated_at)
|
expect(locale.reload.deprecated_at).to eq(previous_deprecated_at)
|
||||||
expect(generated_names).to be_empty
|
expect(generated_names).to be_empty
|
||||||
|
expect(event_rows).to eq(events)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'preserves a human primary through deactivation and repeated activation' do
|
it 'preserves a human primary through deactivation and repeated activation' do
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ RSpec.describe PostVersion, type: :model do
|
|||||||
|
|
||||||
let!(:post_version) do
|
let!(:post_version) do
|
||||||
PostVersion.create!(
|
PostVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
post: post_record,
|
post: post_record,
|
||||||
version_no: 1,
|
version_no: 1,
|
||||||
event_type: 'create',
|
event_type: 'create',
|
||||||
@@ -24,8 +25,7 @@ RSpec.describe PostVersion, type: :model do
|
|||||||
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,
|
||||||
created_by_user: nil
|
created_by_user: nil)
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'is read only after create' do
|
it 'is read only after create' do
|
||||||
|
|||||||
@@ -34,6 +34,31 @@ RSpec.describe TagNameSanitisationRule, type: :model do
|
|||||||
described_class.create!(priority: 10, source_pattern: 'foo_', replacement: 'foo')
|
described_class.create!(priority: 10, source_pattern: 'foo_', replacement: 'foo')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'groups renames, merges and affected post histories into one event' do
|
||||||
|
target = create(:tag, primary_name: 'foobar')
|
||||||
|
source = create(:tag, primary_name: 'temporary_source')
|
||||||
|
renamed = create(:tag, primary_name: 'temporary_rename')
|
||||||
|
source.tag_name('ja').update_columns(name: 'foo_bar')
|
||||||
|
renamed.tag_name('ja').update_columns(name: 'foo_baz')
|
||||||
|
post = create(:post)
|
||||||
|
PostTag.create!(post:, tag: source)
|
||||||
|
previous_tag_ids = TagVersion.pluck(:id)
|
||||||
|
previous_post_ids = PostVersion.pluck(:id)
|
||||||
|
|
||||||
|
expect { described_class.apply! }.to change { event_rows.length }.by(1)
|
||||||
|
|
||||||
|
event = event_rows.last
|
||||||
|
expect(event).to include(
|
||||||
|
'event_type' => 'tag_name_sanitisation_rules.apply', 'created_by_user_id' => nil)
|
||||||
|
tag_versions = TagVersion.where.not(id: previous_tag_ids)
|
||||||
|
expect(tag_versions.pluck(:tag_id).uniq).to contain_exactly(target.id, source.id, renamed.id)
|
||||||
|
expect(tag_versions.pluck(:event_type)).to include('discard', 'update')
|
||||||
|
expect(tag_versions.pluck(:event_id).uniq).to eq([event.fetch('id')])
|
||||||
|
post_versions = PostVersion.where.not(id: previous_post_ids)
|
||||||
|
expect(post_versions.pluck(:post_id).uniq).to eq([post.id])
|
||||||
|
expect(post_versions.pluck(:event_id).uniq).to eq([event.fetch('id')])
|
||||||
|
end
|
||||||
|
|
||||||
[:primary, :alias].each do |source_kind|
|
[:primary, :alias].each do |source_kind|
|
||||||
it "keeps the primary identity when a #{ source_kind } collides within its Tag" do
|
it "keeps the primary identity when a #{ source_kind } collides within its Tag" do
|
||||||
tag = create(:tag, primary_name: source_kind == :primary ? 'temporary' : 'foobar')
|
tag = create(:tag, primary_name: source_kind == :primary ? 'temporary' : 'foobar')
|
||||||
@@ -83,6 +108,7 @@ RSpec.describe TagNameSanitisationRule, type: :model do
|
|||||||
PostTag.create!(post:, tag: source)
|
PostTag.create!(post:, tag: source)
|
||||||
models = [Tag, TagName, TagVersion, TagDescription, TagDescriptionVersion, PostTag]
|
models = [Tag, TagName, TagVersion, TagDescription, TagDescriptionVersion, PostTag]
|
||||||
snapshots = models.map { |model| model.order(*Array(model.primary_key)).map(&:attributes) }
|
snapshots = models.map { |model| model.order(*Array(model.primary_key)).map(&:attributes) }
|
||||||
|
events = event_rows
|
||||||
|
|
||||||
expect { described_class.apply! }.to raise_error(ActiveRecord::RecordInvalid)
|
expect { described_class.apply! }.to raise_error(ActiveRecord::RecordInvalid)
|
||||||
|
|
||||||
@@ -90,6 +116,7 @@ RSpec.describe TagNameSanitisationRule, type: :model do
|
|||||||
model.order(*Array(model.primary_key)).map(&:attributes)
|
model.order(*Array(model.primary_key)).map(&:attributes)
|
||||||
end
|
end
|
||||||
expect(after_state).to eq(snapshots)
|
expect(after_state).to eq(snapshots)
|
||||||
|
expect(event_rows).to eq(events)
|
||||||
expect(post.reload.tags).to contain_exactly(source)
|
expect(post.reload.tags).to contain_exactly(source)
|
||||||
expect(target.reload.name('ja')).to eq('foobar')
|
expect(target.reload.name('ja')).to eq('foobar')
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -665,6 +665,7 @@ RSpec.describe Tag, type: :model do
|
|||||||
|
|
||||||
def create_post_version_for!(post, version_no: 1, event_type: 'create', created_by_user: nil)
|
def create_post_version_for!(post, version_no: 1, event_type: 'create', created_by_user: nil)
|
||||||
PostVersion.create!(
|
PostVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
post: post,
|
post: post,
|
||||||
version_no: version_no,
|
version_no: version_no,
|
||||||
event_type: event_type,
|
event_type: event_type,
|
||||||
@@ -677,8 +678,7 @@ RSpec.describe Tag, type: :model do
|
|||||||
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,
|
||||||
created_by_user: created_by_user
|
created_by_user: created_by_user)
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when post versions are enabled' do
|
context 'when post versions are enabled' do
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ RSpec.describe VersionRecord, type: :model do
|
|||||||
|
|
||||||
it 'makes TagVersion read only after create' do
|
it 'makes TagVersion read only after create' do
|
||||||
version = TagVersion.create!(
|
version = TagVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
tag: tag,
|
tag: tag,
|
||||||
version_no: 1,
|
version_no: 1,
|
||||||
event_type: 'create',
|
event_type: 'create',
|
||||||
@@ -25,6 +26,7 @@ RSpec.describe VersionRecord, type: :model do
|
|||||||
|
|
||||||
it 'prevents TagVersion destroy' do
|
it 'prevents TagVersion destroy' do
|
||||||
version = TagVersion.create!(
|
version = TagVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
tag: tag,
|
tag: tag,
|
||||||
version_no: 1,
|
version_no: 1,
|
||||||
event_type: 'create',
|
event_type: 'create',
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ RSpec.configure do |config|
|
|||||||
end
|
end
|
||||||
|
|
||||||
config.include TestRecords
|
config.include TestRecords
|
||||||
|
config.include EventRecords
|
||||||
config.include RakeTaskHelper
|
config.include RakeTaskHelper
|
||||||
|
|
||||||
# FactoryBot の create / create_list を使へるやぅにする
|
# FactoryBot の create / create_list を使へるやぅにする
|
||||||
|
|||||||
@@ -84,7 +84,9 @@ RSpec.describe 'Counts native GET acknowledgement', type: :request do
|
|||||||
expect(response).to have_http_status(:not_found)
|
expect(response).to have_http_status(:not_found)
|
||||||
job, file, attempt = ready_job('json')
|
job, file, attempt = ready_job('json')
|
||||||
record = create(:post)
|
record = create(:post)
|
||||||
PostVersion.create!(post: record, version_no: 1, event_type: 'discard', title: 'private',
|
PostVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
|
post: record, version_no: 1, event_type: 'discard', title: 'private',
|
||||||
url: record.url, tags: '', tags_json: [], parent_post_ids: '', created_at: Time.utc(2024))
|
url: record.url, tags: '', tags_json: [], parent_post_ids: '', created_at: Time.utc(2024))
|
||||||
get "/counts/export/files/#{ file }", params: { job:, attempt: }
|
get "/counts/export/files/#{ file }", params: { job:, attempt: }
|
||||||
expect(response).to have_http_status(:not_found)
|
expect(response).to have_http_status(:not_found)
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ RSpec.describe 'History boundaries shared with Counts', type: :request do
|
|||||||
|
|
||||||
before do
|
before do
|
||||||
['create', 'discard'].each_with_index do |event, index|
|
['create', 'discard'].each_with_index do |event, index|
|
||||||
TagDescriptionVersion.create!(tag_id: tag.id, language_code: 'ja',
|
TagDescriptionVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
|
tag_id: tag.id, language_code: 'ja',
|
||||||
version_no: index + 1, event_type: event, body: 'private body',
|
version_no: index + 1, event_type: event, body: 'private body',
|
||||||
created_at: Time.utc(2024))
|
created_at: Time.utc(2024))
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -10,12 +10,16 @@ RSpec.describe 'Legacy Counts API', type: :request do
|
|||||||
category: 'general', sections: [] }
|
category: 'general', sections: [] }
|
||||||
[1, 2, 3].each do |number|
|
[1, 2, 3].each do |number|
|
||||||
time = Time.utc(2024, number == 3 ? 2 : 1, number, 0, 0)
|
time = Time.utc(2024, number == 3 ? 2 : 1, number, 0, 0)
|
||||||
PostVersion.create!(post:, version_no: number,
|
PostVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
|
post:, version_no: number,
|
||||||
event_type: number == 1 ? 'create' : 'update',
|
event_type: number == 1 ? 'create' : 'update',
|
||||||
tags: '', url: post.url, title: "Snapshot #{ number }", parent_post_ids: '',
|
tags: '', url: post.url, title: "Snapshot #{ number }", parent_post_ids: '',
|
||||||
tags_json: number == 1 ? [snapshot] : [],
|
tags_json: number == 1 ? [snapshot] : [],
|
||||||
created_by_user: number == 3 ? nil : actor, created_at: time)
|
created_by_user: number == 3 ? nil : actor, created_at: time)
|
||||||
TagVersion.create!(tag:, version_no: number,
|
TagVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
|
tag:, version_no: number,
|
||||||
event_type: number == 1 ? 'create' : 'update',
|
event_type: number == 1 ? 'create' : 'update',
|
||||||
name: 'counts tag', category: 'general', aliases: '', parent_tag_ids: '',
|
name: 'counts tag', category: 'general', aliases: '', parent_tag_ids: '',
|
||||||
names: { ja: { name: 'counts tag', script_code: 'Jpan',
|
names: { ja: { name: 'counts tag', script_code: 'Jpan',
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ RSpec.describe 'Counts table API', type: :request do
|
|||||||
end
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
TagVersion.create!(tag:, version_no: 1, event_type: 'create', name: 'alpha',
|
TagVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
|
tag:, version_no: 1, event_type: 'create', name: 'alpha',
|
||||||
category: 'general', aliases: '', parent_tag_ids: '',
|
category: 'general', aliases: '', parent_tag_ids: '',
|
||||||
names: { ja: { name: 'alpha', script_code: 'Jpan', auto_generated: false, aliases: [] } },
|
names: { ja: { name: 'alpha', script_code: 'Jpan', auto_generated: false, aliases: [] } },
|
||||||
created_at: Time.utc(2023))
|
created_at: Time.utc(2023))
|
||||||
@@ -24,7 +26,9 @@ RSpec.describe 'Counts table API', type: :request do
|
|||||||
{ tag_id: tag.id, version_no: 1, name: 'alpha', category: 'general', sections: [] },
|
{ tag_id: tag.id, version_no: 1, name: 'alpha', category: 'general', sections: [] },
|
||||||
{ external_tag_id: external.id }]
|
{ external_tag_id: external.id }]
|
||||||
[1, 2, 3].each do |number|
|
[1, 2, 3].each do |number|
|
||||||
PostVersion.create!(post: record, version_no: number,
|
PostVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
|
post: record, version_no: number,
|
||||||
event_type: number == 1 ? 'create' : 'update', url: record.url,
|
event_type: number == 1 ? 'create' : 'update', url: record.url,
|
||||||
title: "snapshot #{ number }", tags: '', parent_post_ids: '',
|
title: "snapshot #{ number }", tags: '', parent_post_ids: '',
|
||||||
tags_json: snapshots.first(number == 1 ? 2 : (number == 2 ? 1 : 0)),
|
tags_json: snapshots.first(number == 1 ? 2 : (number == 2 ? 1 : 0)),
|
||||||
@@ -134,7 +138,9 @@ RSpec.describe 'Counts table API', type: :request do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it 'checks the current latest flag beyond the execution timestamp' do
|
it 'checks the current latest flag beyond the execution timestamp' do
|
||||||
PostVersion.create!(post: record, version_no: 4, event_type: 'update',
|
PostVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
|
post: record, version_no: 4, event_type: 'update',
|
||||||
url: record.url, title: 'after execution', tags: '', tags_json: [],
|
url: record.url, title: 'after execution', tags: '', tags_json: [],
|
||||||
parent_post_ids: '', created_at: Time.utc(2025, 2, 1))
|
parent_post_ids: '', created_at: Time.utc(2025, 2, 1))
|
||||||
result = query
|
result = query
|
||||||
@@ -188,12 +194,16 @@ RSpec.describe 'Counts table API', type: :request do
|
|||||||
tag.tag_names.find_by!(language_code: 'ja', primary_flg: true)
|
tag.tag_names.find_by!(language_code: 'ja', primary_flg: true)
|
||||||
.update!(name: 'renamed-alpha')
|
.update!(name: 'renamed-alpha')
|
||||||
create(:tag_name, :alias, tag:, name: 'alpha-alias')
|
create(:tag_name, :alias, tag:, name: 'alpha-alias')
|
||||||
TagVersion.create!(tag:, version_no: 2, event_type: 'update', name: 'renamed-alpha',
|
TagVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
|
tag:, version_no: 2, event_type: 'update', name: 'renamed-alpha',
|
||||||
category: 'general', aliases: 'alpha-alias', parent_tag_ids: '',
|
category: 'general', aliases: 'alpha-alias', parent_tag_ids: '',
|
||||||
names: { ja: { name: 'renamed-alpha', script_code: 'Jpan', auto_generated: false,
|
names: { ja: { name: 'renamed-alpha', script_code: 'Jpan', auto_generated: false,
|
||||||
aliases: [{ name: 'alpha-alias', script_code: 'Jpan' }] } },
|
aliases: [{ name: 'alpha-alias', script_code: 'Jpan' }] } },
|
||||||
created_at: Time.utc(2024, 2, 1))
|
created_at: Time.utc(2024, 2, 1))
|
||||||
PostVersion.create!(post: record, version_no: 4, event_type: 'update',
|
PostVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
|
post: record, version_no: 4, event_type: 'update',
|
||||||
url: record.url, title: 'after rename', tags: '', parent_post_ids: '',
|
url: record.url, title: 'after rename', tags: '', parent_post_ids: '',
|
||||||
tags_json: [{ tag_id: tag.id, version_no: 2, name: 'renamed-alpha',
|
tags_json: [{ tag_id: tag.id, version_no: 2, name: 'renamed-alpha',
|
||||||
category: 'general', sections: [] }],
|
category: 'general', sections: [] }],
|
||||||
@@ -257,7 +267,9 @@ RSpec.describe 'Counts table API', type: :request do
|
|||||||
|
|
||||||
it 'reports column overflow separately from malformed input' do
|
it 'reports column overflow separately from malformed input' do
|
||||||
101.times do |index|
|
101.times do |index|
|
||||||
PostVersion.create!(post: record, version_no: index + 4,
|
PostVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
|
post: record, version_no: index + 4,
|
||||||
event_type: 'update', url: record.url, title: "column #{ index }",
|
event_type: 'update', url: record.url, title: "column #{ index }",
|
||||||
tags: '', tags_json: [], parent_post_ids: '', created_at: Time.utc(2024, 3))
|
tags: '', tags_json: [], parent_post_ids: '', created_at: Time.utc(2024, 3))
|
||||||
end
|
end
|
||||||
@@ -301,7 +313,9 @@ RSpec.describe 'Counts table API', type: :request do
|
|||||||
sign_in_as(create(:user, role)) if role
|
sign_in_as(create(:user, role)) if role
|
||||||
# Equal timestamps deliberately require version ordering, not a date comparison.
|
# Equal timestamps deliberately require version ordering, not a date comparison.
|
||||||
['discard', 'restore', 'discard', 'restore'].each_with_index do |event, index|
|
['discard', 'restore', 'discard', 'restore'].each_with_index do |event, index|
|
||||||
PostVersion.create!(post: record, version_no: index + 4, event_type: event,
|
PostVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
|
post: record, version_no: index + 4, event_type: event,
|
||||||
url: record.url, title: event == 'restore' ? 'restored' : 'private snapshot',
|
url: record.url, title: event == 'restore' ? 'restored' : 'private snapshot',
|
||||||
tags: '', tags_json: [], parent_post_ids: '', created_at: Time.utc(2024, 3))
|
tags: '', tags_json: [], parent_post_ids: '', created_at: Time.utc(2024, 3))
|
||||||
end
|
end
|
||||||
@@ -329,7 +343,9 @@ RSpec.describe 'Counts table API', type: :request do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it 'does not resolve private condition names even when the result set is empty' do
|
it 'does not resolve private condition names even when the result set is empty' do
|
||||||
TagVersion.create!(tag:, version_no: 2, event_type: 'discard', name: 'secret tag',
|
TagVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
|
tag:, version_no: 2, event_type: 'discard', name: 'secret tag',
|
||||||
category: 'general', aliases: '', parent_tag_ids: '',
|
category: 'general', aliases: '', parent_tag_ids: '',
|
||||||
names: { ja: { name: 'secret tag', script_code: 'Jpan', auto_generated: false, aliases: [] } },
|
names: { ja: { name: 'secret tag', script_code: 'Jpan', auto_generated: false, aliases: [] } },
|
||||||
created_at: Time.utc(2024, 2))
|
created_at: Time.utc(2024, 2))
|
||||||
@@ -345,7 +361,9 @@ RSpec.describe 'Counts table API', type: :request do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
it 'includes private history in the public timeline without an exclusion count' do
|
it 'includes private history in the public timeline without an exclusion count' do
|
||||||
PostVersion.create!(post: record, version_no: 4, event_type: 'discard', url: record.url,
|
PostVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
|
post: record, version_no: 4, event_type: 'discard', url: record.url,
|
||||||
title: 'hidden title', tags: '', tags_json: [], parent_post_ids: '',
|
title: 'hidden title', tags: '', tags_json: [], parent_post_ids: '',
|
||||||
created_at: Time.utc(2024, 3))
|
created_at: Time.utc(2024, 3))
|
||||||
result = query(mode: 'summary', rows: 'created_at:month', cols: 'event_type')
|
result = query(mode: 'summary', rows: 'created_at:month', cols: 'event_type')
|
||||||
|
|||||||
@@ -314,6 +314,7 @@ RSpec.describe 'Materials API', type: :request do
|
|||||||
.and change(Tag, :count).by(1)
|
.and change(Tag, :count).by(1)
|
||||||
.and change(TagName, :count).by(1)
|
.and change(TagName, :count).by(1)
|
||||||
.and change(MaterialVersion, :count).by(1)
|
.and change(MaterialVersion, :count).by(1)
|
||||||
|
.and change { event_rows.length }.by(1)
|
||||||
|
|
||||||
expect(response).to have_http_status(:created)
|
expect(response).to have_http_status(:created)
|
||||||
|
|
||||||
@@ -325,6 +326,12 @@ RSpec.describe 'Materials API', type: :request do
|
|||||||
expect(material.file.attached?).to be(true)
|
expect(material.file.attached?).to be(true)
|
||||||
expect(material.version_no).to eq(1)
|
expect(material.version_no).to eq(1)
|
||||||
expect(material.material_versions.first.event_type).to eq('create')
|
expect(material.material_versions.first.event_type).to eq('create')
|
||||||
|
event = event_rows.last
|
||||||
|
expect(event).to include(
|
||||||
|
'event_type' => 'materials.create',
|
||||||
|
'created_by_user_id' => member_user.id)
|
||||||
|
expect(material.material_versions.first.event_id).to eq(event.fetch('id'))
|
||||||
|
expect(material.tag.tag_versions.pluck(:event_id)).to eq([event.fetch('id')])
|
||||||
expect(material.material_export_items.first.export_path).to eq('伊地知ニジカ/created.png')
|
expect(material.material_export_items.first.export_path).to eq('伊地知ニジカ/created.png')
|
||||||
expect(material.material_versions.first.export_paths_json).to eq(
|
expect(material.material_versions.first.export_paths_json).to eq(
|
||||||
'legacy_drive' => '伊地知ニジカ/created.png'
|
'legacy_drive' => '伊地知ニジカ/created.png'
|
||||||
@@ -509,6 +516,7 @@ RSpec.describe 'Materials API', type: :request do
|
|||||||
export_paths: { legacy_drive: '伊地知ニジカ/updated.jpg' }
|
export_paths: { legacy_drive: '伊地知ニジカ/updated.jpg' }
|
||||||
}
|
}
|
||||||
end.to change(MaterialVersion, :count).by(2)
|
end.to change(MaterialVersion, :count).by(2)
|
||||||
|
.and change { event_rows.length }.by(1)
|
||||||
|
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
|
|
||||||
@@ -517,6 +525,12 @@ RSpec.describe 'Materials API', type: :request do
|
|||||||
expect(material.tag.category).to eq('material')
|
expect(material.tag.category).to eq('material')
|
||||||
expect(material.url).to eq('https://example.com/updated-source')
|
expect(material.url).to eq('https://example.com/updated-source')
|
||||||
expect(material.updated_by_user).to eq(member_user)
|
expect(material.updated_by_user).to eq(member_user)
|
||||||
|
event = event_rows.last
|
||||||
|
expect(event).to include(
|
||||||
|
'event_type' => 'materials.update',
|
||||||
|
'created_by_user_id' => member_user.id)
|
||||||
|
expect(material.material_versions.pluck(:event_id).uniq).to eq([event.fetch('id')])
|
||||||
|
expect(material.tag.tag_versions.pluck(:event_id)).to eq([event.fetch('id')])
|
||||||
expect(material.file.attached?).to be(true)
|
expect(material.file.attached?).to be(true)
|
||||||
expect(material.file.blob.id).not_to eq(old_blob_id)
|
expect(material.file.blob.id).not_to eq(old_blob_id)
|
||||||
expect(ActiveStorage::Blob.where(id: old_blob_id).exists?).to be(true)
|
expect(ActiveStorage::Blob.where(id: old_blob_id).exists?).to be(true)
|
||||||
@@ -632,6 +646,7 @@ RSpec.describe 'Materials API', type: :request do
|
|||||||
MaterialVersionRecorder.record!(material:, event_type: :create,
|
MaterialVersionRecorder.record!(material:, event_type: :create,
|
||||||
created_by_user: member_user)
|
created_by_user: member_user)
|
||||||
TagVersioning.ensure_snapshot!(tag, created_by_user: member_user)
|
TagVersioning.ensure_snapshot!(tag, created_by_user: member_user)
|
||||||
|
events = event_rows
|
||||||
|
|
||||||
expect do
|
expect do
|
||||||
put "/materials/#{ material.id }", params: {
|
put "/materials/#{ material.id }", params: {
|
||||||
@@ -643,6 +658,7 @@ RSpec.describe 'Materials API', type: :request do
|
|||||||
|
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
expect(material.reload.version_no).to eq(1)
|
expect(material.reload.version_no).to eq(1)
|
||||||
|
expect(event_rows).to eq(events)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'records update version when only export_path changes' do
|
it 'records update version when only export_path changes' do
|
||||||
@@ -807,6 +823,12 @@ RSpec.describe 'Materials API', type: :request do
|
|||||||
|
|
||||||
it 'creates source suppression as member' do
|
it 'creates source suppression as member' do
|
||||||
sign_in_as(member_user)
|
sign_in_as(member_user)
|
||||||
|
materials = ['a', 'b'].map do |name|
|
||||||
|
Material.create!(
|
||||||
|
source_kind: 'google_drive_file', source_file_id: "blocked-#{ name }",
|
||||||
|
source_path: "伊地知ニジカ/危険素材/#{ name }.png",
|
||||||
|
url: "https://example.com/blocked-#{ name }", created_by_user: member_user)
|
||||||
|
end
|
||||||
|
|
||||||
expect do
|
expect do
|
||||||
post '/materials/suppressions', params: {
|
post '/materials/suppressions', params: {
|
||||||
@@ -815,6 +837,7 @@ RSpec.describe 'Materials API', type: :request do
|
|||||||
reason: 'copyright_high_risk'
|
reason: 'copyright_high_risk'
|
||||||
}
|
}
|
||||||
end.to change(MaterialSyncSuppression, :count).by(1)
|
end.to change(MaterialSyncSuppression, :count).by(1)
|
||||||
|
.and change { event_rows.length }.by(1)
|
||||||
|
|
||||||
expect(response).to have_http_status(:created)
|
expect(response).to have_http_status(:created)
|
||||||
expect(json).to include(
|
expect(json).to include(
|
||||||
@@ -822,6 +845,14 @@ RSpec.describe 'Materials API', type: :request do
|
|||||||
'drive_path' => '伊地知ニジカ/危険素材',
|
'drive_path' => '伊地知ニジカ/危険素材',
|
||||||
'normalized_source_key' => 'google_drive_path_prefix:伊地知ニジカ/危険素材'
|
'normalized_source_key' => 'google_drive_path_prefix:伊地知ニジカ/危険素材'
|
||||||
)
|
)
|
||||||
|
event = event_rows.last
|
||||||
|
expect(event).to include(
|
||||||
|
'event_type' => 'materials.block', 'created_by_user_id' => member_user.id)
|
||||||
|
expect(materials.map { _1.reload.discarded? }).to eq([true, true])
|
||||||
|
versions = MaterialVersion.where(material_id: materials.map(&:id))
|
||||||
|
expect(versions.where(event_type: 'discard').pluck(:material_id))
|
||||||
|
.to match_array(materials.map(&:id))
|
||||||
|
expect(versions.pluck(:event_id).uniq).to eq([event.fetch('id')])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -860,11 +891,19 @@ RSpec.describe 'Materials API', type: :request do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it 'discards the material and returns 204' do
|
it 'discards the material and returns 204' do
|
||||||
delete "/materials/#{ material.id }"
|
expect { delete "/materials/#{ material.id }" }
|
||||||
|
.to change { event_rows.length }.by(1)
|
||||||
|
|
||||||
expect(response).to have_http_status(:no_content)
|
expect(response).to have_http_status(:no_content)
|
||||||
expect(Material.find_by(id: material.id)).to be_nil
|
expect(Material.find_by(id: material.id)).to be_nil
|
||||||
expect(Material.with_discarded.find(material.id)).to be_discarded
|
expect(Material.with_discarded.find(material.id)).to be_discarded
|
||||||
|
event = event_rows.last
|
||||||
|
expect(event).to include(
|
||||||
|
'event_type' => 'materials.destroy',
|
||||||
|
'created_by_user_id' => member_user.id)
|
||||||
|
versions = MaterialVersion.where(material_id: material.id)
|
||||||
|
expect(versions.order(:version_no).last.event_type).to eq('discard')
|
||||||
|
expect(versions.pluck(:event_id).uniq).to eq([event.fetch('id')])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -177,6 +177,7 @@ RSpec.describe 'NicoTags', type: :request do
|
|||||||
}
|
}
|
||||||
}.to change(TagVersion, :count).by(2)
|
}.to change(TagVersion, :count).by(2)
|
||||||
.and change(NicoTagVersion, :count).by(1)
|
.and change(NicoTagVersion, :count).by(1)
|
||||||
|
.and change { event_rows.length }.by(1)
|
||||||
|
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
|
|
||||||
@@ -186,6 +187,10 @@ RSpec.describe 'NicoTags', type: :request do
|
|||||||
linked_versions = TagVersion.where(tag: [linked_a, linked_b]).order(:tag_id)
|
linked_versions = TagVersion.where(tag: [linked_a, linked_b]).order(:tag_id)
|
||||||
expect(linked_versions.map(&:event_type)).to eq(['create', 'create'])
|
expect(linked_versions.map(&:event_type)).to eq(['create', 'create'])
|
||||||
expect(linked_versions.map(&:created_by_user_id)).to all(eq(admin.id))
|
expect(linked_versions.map(&:created_by_user_id)).to all(eq(admin.id))
|
||||||
|
event = event_rows.last
|
||||||
|
expect(event).to include(
|
||||||
|
'event_type' => 'tags.update', 'created_by_user_id' => admin.id)
|
||||||
|
expect(linked_versions.pluck(:event_id).uniq).to eq([event.fetch('id')])
|
||||||
|
|
||||||
versions = nico_tag.reload.nico_tag_versions.order(:version_no)
|
versions = nico_tag.reload.nico_tag_versions.order(:version_no)
|
||||||
expect(versions.map(&:event_type)).to eq(['create', 'update'])
|
expect(versions.map(&:event_type)).to eq(['create', 'update'])
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
post:,
|
post:,
|
||||||
version_no: 1,
|
version_no: 1,
|
||||||
event_type: 'create',
|
event_type: 'create',
|
||||||
|
event_id: create_event!(
|
||||||
|
event_type: 'posts.create', created_by_user: post.uploaded_user).id,
|
||||||
title: post.title,
|
title: post.title,
|
||||||
url: post.url,
|
url: post.url,
|
||||||
thumbnail_base: post.thumbnail_base,
|
thumbnail_base: post.thumbnail_base,
|
||||||
@@ -1027,7 +1029,7 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
|
|
||||||
it 'dry-runs without persisting posts or new tags' do
|
it 'dry-runs without persisting posts or new tags' do
|
||||||
sign_in_as(member)
|
sign_in_as(member)
|
||||||
counts = [Post.count, Tag.count, TagName.count]
|
counts = [Post.count, Tag.count, TagName.count, event_rows.length]
|
||||||
|
|
||||||
post '/posts?dry=1', params: post_write_params(
|
post '/posts?dry=1', params: post_write_params(
|
||||||
title: 'dry-run post',
|
title: 'dry-run post',
|
||||||
@@ -1043,7 +1045,7 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
[{ 'name' => 'new_dry_run_tag',
|
[{ 'name' => 'new_dry_run_tag',
|
||||||
'category' => 'character',
|
'category' => 'character',
|
||||||
'section_literals' => [] }])
|
'section_literals' => [] }])
|
||||||
expect([Post.count, Tag.count, TagName.count]).to eq(counts)
|
expect([Post.count, Tag.count, TagName.count, event_rows.length]).to eq(counts)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'dry-runs an existing URL without validating its upload' do
|
it 'dry-runs an existing URL without validating its upload' do
|
||||||
@@ -2218,6 +2220,7 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
|
|
||||||
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!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
post:,
|
post:,
|
||||||
version_no:,
|
version_no:,
|
||||||
event_type:,
|
event_type:,
|
||||||
@@ -2230,8 +2233,7 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
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_by_user:
|
created_by_user:)
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
let!(:v1) do
|
let!(:v1) do
|
||||||
@@ -2559,6 +2561,7 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
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)
|
||||||
|
.and change { event_rows.length }.by(1)
|
||||||
|
|
||||||
expect(response).to have_http_status(:created)
|
expect(response).to have_http_status(:created)
|
||||||
|
|
||||||
@@ -2570,6 +2573,13 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
expect(version.url).to eq('https://example.com/versioned-post')
|
expect(version.url).to eq('https://example.com/versioned-post')
|
||||||
expect(version.created_by_user_id).to eq(member.id)
|
expect(version.created_by_user_id).to eq(member.id)
|
||||||
expect(version.tags).to eq(snapshot_tags(created_post))
|
expect(version.tags).to eq(snapshot_tags(created_post))
|
||||||
|
event = event_rows.last
|
||||||
|
expect(event).to include(
|
||||||
|
'event_type' => 'posts.create', 'created_by_user_id' => member.id)
|
||||||
|
expect(version.event_id).to eq(event.fetch('id'))
|
||||||
|
tag_versions = TagVersion.where(tag_id: created_post.tags.select(:id))
|
||||||
|
expect(tag_versions).not_to be_empty
|
||||||
|
expect(tag_versions.pluck(:event_id).uniq).to eq([event.fetch('id')])
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'creates next version on PUT /posts/:id when snapshot changes' do
|
it 'creates next version on PUT /posts/:id when snapshot changes' do
|
||||||
@@ -2585,6 +2595,7 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
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)
|
||||||
|
.and change { event_rows.length }.by(1)
|
||||||
|
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
|
|
||||||
@@ -2594,6 +2605,12 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
expect(version.title).to eq('updated title')
|
expect(version.title).to eq('updated title')
|
||||||
expect(version.created_by_user_id).to eq(member.id)
|
expect(version.created_by_user_id).to eq(member.id)
|
||||||
expect(version.tags).to eq(snapshot_tags(post_record.reload))
|
expect(version.tags).to eq(snapshot_tags(post_record.reload))
|
||||||
|
event = event_rows.last
|
||||||
|
expect(event).to include(
|
||||||
|
'event_type' => 'posts.update', 'created_by_user_id' => member.id)
|
||||||
|
expect(version.event_id).to eq(event.fetch('id'))
|
||||||
|
tag_version = TagVersion.find_by!(tag_id: tag_name2.reload.tag_id, version_no: 1)
|
||||||
|
expect(tag_version.event_id).to eq(event.fetch('id'))
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not create a new version on PUT /posts/:id when snapshot is unchanged' do
|
it 'does not create a new version on PUT /posts/:id when snapshot is unchanged' do
|
||||||
@@ -2602,6 +2619,7 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
PostTag.create!(post: post_record, tag: Tag.no_deerjikist)
|
PostTag.create!(post: post_record, tag: Tag.no_deerjikist)
|
||||||
base_version = create_post_version_for!(post_record.reload)
|
base_version = create_post_version_for!(post_record.reload)
|
||||||
|
|
||||||
|
events = event_rows
|
||||||
expect {
|
expect {
|
||||||
put "/posts/#{post_record.id}", params: post_write_params(
|
put "/posts/#{post_record.id}", params: post_write_params(
|
||||||
base_version_no: base_version.version_no,
|
base_version_no: base_version.version_no,
|
||||||
@@ -2615,10 +2633,12 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
expect(version.version_no).to eq(1)
|
expect(version.version_no).to eq(1)
|
||||||
expect(version.event_type).to eq('create')
|
expect(version.event_type).to eq('create')
|
||||||
expect(version.tags).to eq(snapshot_tags(post_record))
|
expect(version.tags).to eq(snapshot_tags(post_record))
|
||||||
|
expect(event_rows).to eq(events)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not create a version when POST /posts is invalid' do
|
it 'does not create a version when POST /posts is invalid' do
|
||||||
sign_in_as(member)
|
sign_in_as(member)
|
||||||
|
events = event_rows
|
||||||
|
|
||||||
expect do
|
expect do
|
||||||
post '/posts', params: post_write_params(
|
post '/posts', params: post_write_params(
|
||||||
@@ -2629,11 +2649,13 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
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)
|
||||||
|
expect(event_rows).to eq(events)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not create a version when PUT /posts/:id is invalid' do
|
it 'does not create a version when PUT /posts/:id is invalid' do
|
||||||
sign_in_as(member)
|
sign_in_as(member)
|
||||||
base_version = create_post_version_for!(post_record)
|
base_version = create_post_version_for!(post_record)
|
||||||
|
events = event_rows
|
||||||
|
|
||||||
expect do
|
expect do
|
||||||
put "/posts/#{post_record.id}", params: post_write_params(
|
put "/posts/#{post_record.id}", params: post_write_params(
|
||||||
@@ -2645,6 +2667,7 @@ RSpec.describe 'Posts API', type: :request do
|
|||||||
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)
|
||||||
|
expect(event_rows).to eq(events)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'rejects second-bearing original created timestamps on POST /posts' do
|
it 'rejects second-bearing original created timestamps on POST /posts' do
|
||||||
|
|||||||
@@ -119,7 +119,9 @@ RSpec.describe 'Sitemap entries API', type: :request do
|
|||||||
# Legacy empty bodies can exist even though the write service rejects them.
|
# Legacy empty bodies can exist even though the write service rejects them.
|
||||||
empty = TagDescription.create!(tag: empty_tag, language_code: 'ain',
|
empty = TagDescription.create!(tag: empty_tag, language_code: 'ain',
|
||||||
body: '', version_no: 1)
|
body: '', version_no: 1)
|
||||||
TagDescriptionVersion.create!(tag_id: empty_tag.id, language_code: 'ain',
|
TagDescriptionVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
|
tag_id: empty_tag.id, language_code: 'ain',
|
||||||
body: '', version_no: 1, event_type: 'create',
|
body: '', version_no: 1, event_type: 'create',
|
||||||
created_by_user_id: user.id, created_at: Time.current)
|
created_by_user_id: user.id, created_at: Time.current)
|
||||||
|
|
||||||
|
|||||||
@@ -53,8 +53,13 @@ RSpec.describe "TagChildren", type: :request do
|
|||||||
expect { do_request }
|
expect { do_request }
|
||||||
.to change { parent.reload.children.ids.include?(child.id) }
|
.to change { parent.reload.children.ids.include?(child.id) }
|
||||||
.from(false).to(true)
|
.from(false).to(true)
|
||||||
|
.and change { event_rows.length }.by(1)
|
||||||
|
|
||||||
expect(response).to have_http_status(:no_content)
|
expect(response).to have_http_status(:no_content)
|
||||||
|
event = event_rows.last
|
||||||
|
expect(event).to include(
|
||||||
|
'event_type' => 'tags.update', 'created_by_user_id' => admin.id)
|
||||||
|
expect(child.tag_versions.pluck(:event_id).uniq).to eq([event.fetch('id')])
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns 422 and does not create relation when the new link makes a cycle' do
|
it 'returns 422 and does not create relation when the new link makes a cycle' do
|
||||||
@@ -132,6 +137,7 @@ RSpec.describe "TagChildren", type: :request do
|
|||||||
expect {
|
expect {
|
||||||
do_request
|
do_request
|
||||||
}.to change(TagVersion, :count).by(2)
|
}.to change(TagVersion, :count).by(2)
|
||||||
|
.and change { event_rows.length }.by(1)
|
||||||
|
|
||||||
expect(response).to have_http_status(:no_content)
|
expect(response).to have_http_status(:no_content)
|
||||||
|
|
||||||
@@ -140,6 +146,10 @@ RSpec.describe "TagChildren", type: :request do
|
|||||||
expect(versions.first.parent_tag_ids.split).to include(parent.id.to_s)
|
expect(versions.first.parent_tag_ids.split).to include(parent.id.to_s)
|
||||||
expect(versions.second.parent_tag_ids).to eq('')
|
expect(versions.second.parent_tag_ids).to eq('')
|
||||||
expect(versions.second.created_by_user_id).to eq(admin.id)
|
expect(versions.second.created_by_user_id).to eq(admin.id)
|
||||||
|
event = event_rows.last
|
||||||
|
expect(event).to include(
|
||||||
|
'event_type' => 'tags.update', 'created_by_user_id' => admin.id)
|
||||||
|
expect(versions.pluck(:event_id).uniq).to eq([event.fetch('id')])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ RSpec.describe 'Tag description Wiki compatibility', type: :request do
|
|||||||
let!(:versions) do
|
let!(:versions) do
|
||||||
['A', 'A', 'new-version'].each_with_index.map do |body, index|
|
['A', 'A', 'new-version'].each_with_index.map do |body, index|
|
||||||
TagDescriptionVersion.create!(
|
TagDescriptionVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
tag_id: tag.id, language_code: 'ja', version_no: index + 1,
|
tag_id: tag.id, language_code: 'ja', version_no: index + 1,
|
||||||
event_type: index.zero? ? 'create' : 'update', body:,
|
event_type: index.zero? ? 'create' : 'update', body:,
|
||||||
created_by_user_id: user.id, created_at: Time.current)
|
created_by_user_id: user.id, created_at: Time.current)
|
||||||
@@ -73,6 +74,7 @@ RSpec.describe 'Tag description Wiki compatibility', type: :request do
|
|||||||
let!(:other_tag) { create(:tag, primary_name: 'other_description_contract') }
|
let!(:other_tag) { create(:tag, primary_name: 'other_description_contract') }
|
||||||
let!(:other_version) do
|
let!(:other_version) do
|
||||||
TagDescriptionVersion.create!(
|
TagDescriptionVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
tag_id: other_tag.id, language_code: 'ja', version_no: 1,
|
tag_id: other_tag.id, language_code: 'ja', version_no: 1,
|
||||||
event_type: 'create', body: 'other tag history',
|
event_type: 'create', body: 'other tag history',
|
||||||
created_by_user_id: user.id, created_at: Time.current)
|
created_by_user_id: user.id, created_at: Time.current)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ RSpec.describe 'Tag description native API', type: :request do
|
|||||||
def stored_state
|
def stored_state
|
||||||
[TagDescription, TagDescriptionVersion, TagName, TagVersion].map do |model|
|
[TagDescription, TagDescriptionVersion, TagName, TagVersion].map do |model|
|
||||||
model.order(:id).map(&:attributes)
|
model.order(:id).map(&:attributes)
|
||||||
end
|
end + [event_rows]
|
||||||
end
|
end
|
||||||
|
|
||||||
def fail_save model
|
def fail_save model
|
||||||
@@ -91,6 +91,8 @@ RSpec.describe 'Tag description native API', type: :request do
|
|||||||
let!(:version) do
|
let!(:version) do
|
||||||
TagDescriptionVersion.create!(
|
TagDescriptionVersion.create!(
|
||||||
tag_id: tag.id, language_code: 'ja', body: 'native version', version_no: 1,
|
tag_id: tag.id, language_code: 'ja', body: 'native version', version_no: 1,
|
||||||
|
event_id: create_event!(
|
||||||
|
event_type: 'tag_descriptions.create', created_by_user: author).id,
|
||||||
event_type: 'create', created_by_user_id: author.id, created_at: Time.current)
|
event_type: 'create', created_by_user_id: author.id, created_at: Time.current)
|
||||||
end
|
end
|
||||||
let(:name_endpoint) { "/tags/name/#{ tag_name.name }/description" }
|
let(:name_endpoint) { "/tags/name/#{ tag_name.name }/description" }
|
||||||
@@ -185,6 +187,7 @@ RSpec.describe 'Tag description native API', type: :request do
|
|||||||
expect { native_write(body: 'created natively') }
|
expect { native_write(body: 'created natively') }
|
||||||
.to change(TagDescription, :count).by(1)
|
.to change(TagDescription, :count).by(1)
|
||||||
.and change(TagDescriptionVersion, :count).by(1)
|
.and change(TagDescriptionVersion, :count).by(1)
|
||||||
|
.and change { event_rows.length }.by(1)
|
||||||
expect(response).to have_http_status(:created)
|
expect(response).to have_http_status(:created)
|
||||||
description = TagDescription.find_by!(tag_id: tag.id, language_code: 'ja')
|
description = TagDescription.find_by!(tag_id: tag.id, language_code: 'ja')
|
||||||
expect_current(description)
|
expect_current(description)
|
||||||
@@ -192,6 +195,10 @@ RSpec.describe 'Tag description native API', type: :request do
|
|||||||
expect(version).to have_attributes(
|
expect(version).to have_attributes(
|
||||||
body: 'created natively', version_no: 1, event_type: 'create',
|
body: 'created natively', version_no: 1, event_type: 'create',
|
||||||
created_by_user_id: editor.id)
|
created_by_user_id: editor.id)
|
||||||
|
event = event_rows.last
|
||||||
|
expect(event).to include(
|
||||||
|
'event_type' => 'tag_descriptions.create', 'created_by_user_id' => editor.id)
|
||||||
|
expect(version.event_id).to eq(event.fetch('id'))
|
||||||
get "/wiki/#{ description.id }"
|
get "/wiki/#{ description.id }"
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
expect(json).to include('body' => description.body, 'revision_id' => version.id)
|
expect(json).to include('body' => description.body, 'revision_id' => version.id)
|
||||||
@@ -223,6 +230,7 @@ RSpec.describe 'Tag description native API', type: :request do
|
|||||||
expect(description.version_no).to eq(1)
|
expect(description.version_no).to eq(1)
|
||||||
expect { native_write(body: 'after') }
|
expect { native_write(body: 'after') }
|
||||||
.to change(TagDescriptionVersion, :count).by(1)
|
.to change(TagDescriptionVersion, :count).by(1)
|
||||||
|
.and change { event_rows.length }.by(1)
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
expect(description.reload).to have_attributes(
|
expect(description.reload).to have_attributes(
|
||||||
body: 'after', version_no: 2,
|
body: 'after', version_no: 2,
|
||||||
@@ -234,6 +242,10 @@ RSpec.describe 'Tag description native API', type: :request do
|
|||||||
expect(version).to have_attributes(
|
expect(version).to have_attributes(
|
||||||
tag_id: tag.id, language_code: 'ja', version_no: 2, event_type: 'update',
|
tag_id: tag.id, language_code: 'ja', version_no: 2, event_type: 'update',
|
||||||
body: 'after', created_by_user_id: editor.id)
|
body: 'after', created_by_user_id: editor.id)
|
||||||
|
event = event_rows.last
|
||||||
|
expect(event).to include(
|
||||||
|
'event_type' => 'tag_descriptions.update', 'created_by_user_id' => editor.id)
|
||||||
|
expect(version.event_id).to eq(event.fetch('id'))
|
||||||
expect(revision.body).to eq(version.body)
|
expect(revision.body).to eq(version.body)
|
||||||
expect(description_versions(page).order(:version_no).last.body).to eq(version.body)
|
expect(description_versions(page).order(:version_no).last.body).to eq(version.body)
|
||||||
get "/wiki/#{ description.id }"
|
get "/wiki/#{ description.id }"
|
||||||
|
|||||||
@@ -121,10 +121,14 @@ RSpec.describe 'Tag group native API', type: :request do
|
|||||||
[:create, :update].each do |operation|
|
[:create, :update].each do |operation|
|
||||||
it "#{ operation }s structured data and keeps unplaced membership" do
|
it "#{ operation }s structured data and keeps unplaced membership" do
|
||||||
previous = group_version(group_id, 1)
|
previous = group_version(group_id, 1)
|
||||||
native_write(operation)
|
expect { native_write(operation) }.to change { event_rows.length }.by(1)
|
||||||
expect(response).to have_http_status(operation == :create ? :created : :ok)
|
expect(response).to have_http_status(operation == :create ? :created : :ok)
|
||||||
id = operation == :create ? json.fetch('id') : group_id
|
id = operation == :create ? json.fetch('id') : group_id
|
||||||
number = operation == :create ? 1 : 2
|
number = operation == :create ? 1 : 2
|
||||||
|
event = event_rows.last
|
||||||
|
event_type = operation == :create ? 'tag_groups.create' : 'tag_groups.update'
|
||||||
|
expect(event).to include('event_type' => event_type, 'created_by_user_id' => user.id)
|
||||||
|
expect(group_version(id, number).fetch('event_id')).to eq(event.fetch('id'))
|
||||||
expect_group_snapshot(
|
expect_group_snapshot(
|
||||||
id, number:, members: snapshot_members, placements:, description: '全体の説明')
|
id, number:, members: snapshot_members, placements:, description: '全体の説明')
|
||||||
expect(group_version(group_id, 1)).to eq(previous)
|
expect(group_version(group_id, 1)).to eq(previous)
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ RSpec.describe 'TagVersions API', type: :request do
|
|||||||
)
|
)
|
||||||
version =
|
version =
|
||||||
TagVersion.create!(
|
TagVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
tag: tag,
|
tag: tag,
|
||||||
version_no: version_no,
|
version_no: version_no,
|
||||||
event_type: event_type,
|
event_type: event_type,
|
||||||
|
|||||||
@@ -705,6 +705,7 @@ RSpec.describe 'Tags API', type: :request do
|
|||||||
expect {
|
expect {
|
||||||
patch "/tags/#{tag.id}", params: { name: 'new_tag_name', category: 'meme' }
|
patch "/tags/#{tag.id}", params: { name: 'new_tag_name', category: 'meme' }
|
||||||
}.to change(TagVersion, :count).by(2)
|
}.to change(TagVersion, :count).by(2)
|
||||||
|
.and change { event_rows.length }.by(1)
|
||||||
|
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
|
|
||||||
@@ -719,6 +720,10 @@ RSpec.describe 'Tags API', type: :request do
|
|||||||
expect(version_primary_name(versions.second, 'ja')).to eq('new_tag_name')
|
expect(version_primary_name(versions.second, 'ja')).to eq('new_tag_name')
|
||||||
expect(versions.second.category).to eq('meme')
|
expect(versions.second.category).to eq('meme')
|
||||||
expect(versions.second.created_by_user_id).to eq(member_user.id)
|
expect(versions.second.created_by_user_id).to eq(member_user.id)
|
||||||
|
event = event_rows.last
|
||||||
|
expect(event).to include(
|
||||||
|
'event_type' => 'tags.update', 'created_by_user_id' => member_user.id)
|
||||||
|
expect(versions.pluck(:event_id).uniq).to eq([event.fetch('id')])
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'updates deprecated state and records it in tag versions' do
|
it 'updates deprecated state and records it in tag versions' do
|
||||||
@@ -1275,6 +1280,7 @@ RSpec.describe 'Tags API', type: :request do
|
|||||||
deprecated: '0',
|
deprecated: '0',
|
||||||
}
|
}
|
||||||
}.to change(TagVersion, :count).by(2)
|
}.to change(TagVersion, :count).by(2)
|
||||||
|
.and change { event_rows.length }.by(1)
|
||||||
|
|
||||||
expect(response).to have_http_status(:ok)
|
expect(response).to have_http_status(:ok)
|
||||||
|
|
||||||
@@ -1290,6 +1296,10 @@ RSpec.describe 'Tags API', type: :request do
|
|||||||
expect(versions.second.category).to eq('meta')
|
expect(versions.second.category).to eq('meta')
|
||||||
expect(version_alias_names(versions.second, 'ja')).to include('spec_tag')
|
expect(version_alias_names(versions.second, 'ja')).to include('spec_tag')
|
||||||
expect(versions.second.created_by_user_id).to eq(member_user.id)
|
expect(versions.second.created_by_user_id).to eq(member_user.id)
|
||||||
|
event = event_rows.last
|
||||||
|
expect(event).to include(
|
||||||
|
'event_type' => 'tags.update', 'created_by_user_id' => member_user.id)
|
||||||
|
expect(versions.pluck(:event_id).uniq).to eq([event.fetch('id')])
|
||||||
end
|
end
|
||||||
|
|
||||||
it '同じ tag の旧 alias へ戻しても rename できる' do
|
it '同じ tag の旧 alias へ戻しても rename できる' do
|
||||||
|
|||||||
@@ -143,7 +143,9 @@ RSpec.describe 'Counts export contracts' do
|
|||||||
expect(CountsExportDownload.find(token, 'owner')).not_to be_nil
|
expect(CountsExportDownload.find(token, 'owner')).not_to be_nil
|
||||||
expect(CountsExportDownload.find(token, 'other')).to be_nil
|
expect(CountsExportDownload.find(token, 'other')).to be_nil
|
||||||
post = create(:post)
|
post = create(:post)
|
||||||
PostVersion.create!(post:, version_no: 1, event_type: 'discard', url: post.url,
|
PostVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
|
post:, version_no: 1, event_type: 'discard', url: post.url,
|
||||||
title: 'private', tags: '', tags_json: [], parent_post_ids: '', created_at: Time.utc(2024))
|
title: 'private', tags: '', tags_json: [], parent_post_ids: '', created_at: Time.utc(2024))
|
||||||
expect(CountsExportDownload.find(token, 'owner')).to be_nil
|
expect(CountsExportDownload.find(token, 'owner')).to be_nil
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ RSpec.describe 'Counts masked pivot contracts' do
|
|||||||
let!(:tags) do
|
let!(:tags) do
|
||||||
['A', 'B', 'C'].map do |name|
|
['A', 'B', 'C'].map do |name|
|
||||||
tag = create(:tag, primary_name: name)
|
tag = create(:tag, primary_name: name)
|
||||||
TagVersion.create!(tag:, version_no: 1, event_type: 'create', name:,
|
TagVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
|
tag:, version_no: 1, event_type: 'create', name:,
|
||||||
category: 'general', aliases: '', parent_tag_ids: '',
|
category: 'general', aliases: '', parent_tag_ids: '',
|
||||||
names: { ja: { name:, script_code: 'Jpan', auto_generated: false, aliases: [] } },
|
names: { ja: { name:, script_code: 'Jpan', auto_generated: false, aliases: [] } },
|
||||||
created_at: Time.utc(2023))
|
created_at: Time.utc(2023))
|
||||||
@@ -25,12 +27,16 @@ RSpec.describe 'Counts masked pivot contracts' do
|
|||||||
[[0, 1], [0], [1, 2]].each_with_index do |indexes, index|
|
[[0, 1], [0], [1, 2]].each_with_index do |indexes, index|
|
||||||
post = create(:post)
|
post = create(:post)
|
||||||
snapshot = indexes.map { { tag_id: tags[_1].id, version_no: 1, sections: [] } }
|
snapshot = indexes.map { { tag_id: tags[_1].id, version_no: 1, sections: [] } }
|
||||||
PostVersion.create!(post:, version_no: 1, event_type: 'create',
|
PostVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
|
post:, version_no: 1, event_type: 'create',
|
||||||
title: "hidden-title-#{ index }", url: post.url, tags: '', tags_json: snapshot,
|
title: "hidden-title-#{ index }", url: post.url, tags: '', tags_json: snapshot,
|
||||||
parent_post_ids: '', created_by_user: member, created_at: Time.utc(2024, 1))
|
parent_post_ids: '', created_by_user: member, created_at: Time.utc(2024, 1))
|
||||||
next if index == 2
|
next if index == 2
|
||||||
|
|
||||||
PostVersion.create!(post:, version_no: 2, event_type: 'discard',
|
PostVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
|
post:, version_no: 2, event_type: 'discard',
|
||||||
title: "private-discard-#{ index }", url: post.url, tags: '', tags_json: [],
|
title: "private-discard-#{ index }", url: post.url, tags: '', tags_json: [],
|
||||||
parent_post_ids: '', created_by_user: member, created_at: Time.utc(2024, 2))
|
parent_post_ids: '', created_by_user: member, created_at: Time.utc(2024, 2))
|
||||||
end
|
end
|
||||||
@@ -98,12 +104,16 @@ RSpec.describe 'Counts masked pivot contracts' do
|
|||||||
|
|
||||||
it 'uses changed-record membership for totals and hides changes to private predecessors' do
|
it 'uses changed-record membership for totals and hides changes to private predecessors' do
|
||||||
post = PostVersion.order(:id).last.post
|
post = PostVersion.order(:id).last.post
|
||||||
PostVersion.create!(post:, version_no: 2, event_type: 'update', title: 'changed-public-title',
|
PostVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
|
post:, version_no: 2, event_type: 'update', title: 'changed-public-title',
|
||||||
url: post.url, tags: '',
|
url: post.url, tags: '',
|
||||||
tags_json: [{ tag_id: tags.last.id, version_no: 1, sections: [] }],
|
tags_json: [{ tag_id: tags.last.id, version_no: 1, sections: [] }],
|
||||||
parent_post_ids: '', created_at: Time.utc(2024, 3))
|
parent_post_ids: '', created_at: Time.utc(2024, 3))
|
||||||
restored = PostVersion.order(:id).first.post
|
restored = PostVersion.order(:id).first.post
|
||||||
PostVersion.create!(post: restored, version_no: 3, event_type: 'restore',
|
PostVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
|
post: restored, version_no: 3, event_type: 'restore',
|
||||||
title: 'restored-public-title', url: restored.url, tags: '',
|
title: 'restored-public-title', url: restored.url, tags: '',
|
||||||
tags_json: [{ tag_id: tags.first.id, version_no: 1, sections: [] }], parent_post_ids: '',
|
tags_json: [{ tag_id: tags.first.id, version_no: 1, sections: [] }], parent_post_ids: '',
|
||||||
created_at: Time.utc(2024, 4))
|
created_at: Time.utc(2024, 4))
|
||||||
@@ -134,7 +144,9 @@ RSpec.describe 'Counts masked pivot contracts' do
|
|||||||
|
|
||||||
it 'keeps NULL as a public data value rather than treating it as a rollup' do
|
it 'keeps NULL as a public data value rather than treating it as a rollup' do
|
||||||
post = create(:post)
|
post = create(:post)
|
||||||
PostVersion.create!(post:, version_no: 1, event_type: 'create', title: 'public bot',
|
PostVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
|
post:, version_no: 1, event_type: 'create', title: 'public bot',
|
||||||
url: post.url, tags: '', tags_json: [], parent_post_ids: '', created_at: Time.utc(2024, 5))
|
url: post.url, tags: '', tags_json: [], parent_post_ids: '', created_at: Time.utc(2024, 5))
|
||||||
page = result(nil, 'rows' => 'user', 'cols' => 'event_type')
|
page = result(nil, 'rows' => 'user', 'cols' => 'event_type')
|
||||||
expect(page[:cells].select { _1[:values].first.nil? }.sum { _1[:count] }).to eq(1)
|
expect(page[:cells].select { _1[:values].first.nil? }.sum { _1[:count] }).to eq(1)
|
||||||
@@ -145,7 +157,9 @@ RSpec.describe 'Counts masked pivot contracts' do
|
|||||||
it 'sorts public numeric values numerically alongside a masked group' do
|
it 'sorts public numeric values numerically alongside a masked group' do
|
||||||
post = PostVersion.order(:id).last.post
|
post = PostVersion.order(:id).last.post
|
||||||
[2, 10].each do |number|
|
[2, 10].each do |number|
|
||||||
PostVersion.create!(post:, version_no: number, event_type: 'update',
|
PostVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
|
post:, version_no: number, event_type: 'update',
|
||||||
title: 'public numeric sort', url: post.url, tags: '', tags_json: [],
|
title: 'public numeric sort', url: post.url, tags: '', tags_json: [],
|
||||||
parent_post_ids: '', created_at: Time.utc(2024, 5))
|
parent_post_ids: '', created_at: Time.utc(2024, 5))
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ RSpec.describe MaterialSyncImporter do
|
|||||||
profile: 'legacy_drive',
|
profile: 'legacy_drive',
|
||||||
export_path: '素材/a.png',
|
export_path: '素材/a.png',
|
||||||
created_by_user: user)
|
created_by_user: user)
|
||||||
|
events = event_rows
|
||||||
|
|
||||||
result =
|
result =
|
||||||
described_class.import!(
|
described_class.import!(
|
||||||
@@ -73,6 +74,7 @@ RSpec.describe MaterialSyncImporter do
|
|||||||
expect(result.action).to eq(:unchanged)
|
expect(result.action).to eq(:unchanged)
|
||||||
expect(material.reload.tag).to eq(tag)
|
expect(material.reload.tag).to eq(tag)
|
||||||
expect(material.url).to eq('https://example.com/human')
|
expect(material.url).to eq('https://example.com/human')
|
||||||
|
expect(event_rows).to eq(events)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'suppresses a Google Drive candidate by relative path prefix' do
|
it 'suppresses a Google Drive candidate by relative path prefix' do
|
||||||
|
|||||||
@@ -14,6 +14,47 @@ RSpec.describe MaterialSyncRunner do
|
|||||||
let(:drive_client) { instance_double(GoogleDrive::ApiClient) }
|
let(:drive_client) { instance_double(GoogleDrive::ApiClient) }
|
||||||
|
|
||||||
describe '#sync!' do
|
describe '#sync!' do
|
||||||
|
it 'groups created and updated materials across sync sources into one event' do
|
||||||
|
existing = Material.create!(
|
||||||
|
source_kind: 'google_drive_file', source_file_id: 'existing-file',
|
||||||
|
source_path: 'old.png', url: 'https://example.com/existing',
|
||||||
|
created_by_user: user, updated_by_user: user)
|
||||||
|
source
|
||||||
|
MaterialSyncSource.create!(
|
||||||
|
name: 'Second Drive source', source_kind: 'google_drive_path',
|
||||||
|
source_file_id: 'folder-456', profile: 'legacy_drive',
|
||||||
|
created_by_user: user, updated_by_user: user)
|
||||||
|
allow(GoogleDrive::ApiClient).to receive(:new).and_return(drive_client)
|
||||||
|
allow(drive_client).to receive(:each_material_file_under_folder) do |folder_id, &block|
|
||||||
|
id = folder_id == 'folder-123' ? 'existing-file' : 'new-file'
|
||||||
|
block.call(id:, name: "#{ id }.png", mime_type: 'image/png',
|
||||||
|
relative_path: "素材/#{ id }.png", sha256_checksum: nil,
|
||||||
|
web_view_link: "https://drive.google.com/file/d/#{ id }/view")
|
||||||
|
end
|
||||||
|
allow(drive_client).to receive(:download_to_tempfile) do |id, **|
|
||||||
|
Tempfile.new(['sync-event', '.png']).tap do |file|
|
||||||
|
file.binmode
|
||||||
|
file.write(id)
|
||||||
|
file.rewind
|
||||||
|
end
|
||||||
|
end
|
||||||
|
allow(MaterialThumbnailGenerator).to receive(:generate!)
|
||||||
|
previous_ids = MaterialVersion.pluck(:id)
|
||||||
|
|
||||||
|
result = nil
|
||||||
|
expect { result = described_class.sync_enabled! }
|
||||||
|
.to change { event_rows.length }.by(1)
|
||||||
|
|
||||||
|
expect(result).to have_attributes(imported: 1, updated: 1, failed: 0)
|
||||||
|
event = event_rows.last
|
||||||
|
expect(event.fetch('event_type')).to eq('materials.sync')
|
||||||
|
created = Material.find_by!(source_file_id: 'new-file')
|
||||||
|
versions = MaterialVersion.where.not(id: previous_ids)
|
||||||
|
expect(versions.pluck(:material_id).uniq).to contain_exactly(existing.id, created.id)
|
||||||
|
expect(versions.pluck(:event_type)).to include('create', 'update')
|
||||||
|
expect(versions.pluck(:event_id).uniq).to eq([event.fetch('id')])
|
||||||
|
end
|
||||||
|
|
||||||
it 'imports google drive path candidates as they are yielded' do
|
it 'imports google drive path candidates as they are yielded' do
|
||||||
first_entry = { id: 'file-1',
|
first_entry = { id: 'file-1',
|
||||||
name: 'a.png',
|
name: 'a.png',
|
||||||
|
|||||||
@@ -88,3 +88,86 @@ RSpec.describe PostBulkCreator do
|
|||||||
expect(creator).to have_received(:create!).with(locale).once
|
expect(creator).to have_received(:create!).with(locale).once
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
RSpec.describe 'PostBulkCreator event grouping', type: :model do
|
||||||
|
self.use_transactional_tests = false
|
||||||
|
|
||||||
|
before do
|
||||||
|
expect(Rails.env.test?).to be(true)
|
||||||
|
expect(ApplicationRecord.connection.open_transactions).to eq(0)
|
||||||
|
@prefix = "bulk_event_#{ SecureRandom.hex(8) }"
|
||||||
|
@actor = create(:user, :member)
|
||||||
|
allow(PostCreatePreflight).to receive(:new) do |attributes:, **|
|
||||||
|
specs = [{ name: attributes.fetch(:title), category: :general }]
|
||||||
|
instance_double(PostCreatePreflight, run: attributes.merge(
|
||||||
|
existing_post_id: nil, snapshot_tag_specs: specs, post_tag_specs: specs,
|
||||||
|
normalised_parent_post_ids: [], tag_sections: { }, video_ms: nil))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
if @actor
|
||||||
|
posts = Post.where(uploaded_user: @actor)
|
||||||
|
PostTagSection.where(post_id: posts.select(:id)).delete_all
|
||||||
|
PostTag.where(post_id: posts.select(:id)).delete_all
|
||||||
|
PostVersion.where(post_id: posts.select(:id)).delete_all
|
||||||
|
posts.delete_all
|
||||||
|
tag_ids = TagName.where(name: ["#{ @prefix }_1", "#{ @prefix }_2"]).pluck(:tag_id)
|
||||||
|
TagVersion.where(tag_id: tag_ids).delete_all
|
||||||
|
TagName.where(tag_id: tag_ids).delete_all
|
||||||
|
Tag.where(id: tag_ids).delete_all
|
||||||
|
EventRecords::EventRow.where(created_by_user_id: @actor.id).delete_all
|
||||||
|
@actor.delete
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'shares one creation event between real workers and their tag histories' do
|
||||||
|
posts = [1, 2].map do |number|
|
||||||
|
{ title: "#{ @prefix }_#{ number }",
|
||||||
|
url: "https://example.com/#{ @prefix }/#{ number }" }
|
||||||
|
end
|
||||||
|
|
||||||
|
result = nil
|
||||||
|
expect {
|
||||||
|
result = PostBulkCreator.new(actor: @actor, posts:, thumbnails: { })
|
||||||
|
.run(Locale.nipponese)
|
||||||
|
}.to change { event_rows.length }.by(1)
|
||||||
|
|
||||||
|
expect(result.fetch(:results).map { _1.fetch(:status) }).to eq(['created', 'created'])
|
||||||
|
event = event_rows.last
|
||||||
|
expect(event).to include(
|
||||||
|
'event_type' => 'posts.create', 'created_by_user_id' => @actor.id)
|
||||||
|
versions = PostVersion.where(created_by_user_id: @actor.id)
|
||||||
|
expect(versions.count).to eq(2)
|
||||||
|
expect(versions.pluck(:event_id).uniq).to eq([event.fetch('id')])
|
||||||
|
tag_versions = TagVersion.where(created_by_user_id: @actor.id)
|
||||||
|
expect(tag_versions.count).to eq(2)
|
||||||
|
expect(tag_versions.pluck(:event_id).uniq).to eq([event.fetch('id')])
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not leave an event when every row fails validation' do
|
||||||
|
preflight = instance_double(PostCreatePreflight)
|
||||||
|
allow(PostCreatePreflight).to receive(:new).and_return(preflight)
|
||||||
|
allow(preflight).to receive(:run).and_raise(
|
||||||
|
PostCreatePreflight::ValidationFailed.new(fields: { url: ['invalid'] }))
|
||||||
|
events = event_rows
|
||||||
|
|
||||||
|
result = PostBulkCreator.new(
|
||||||
|
actor: @actor, posts: [{ title: 'invalid' }], thumbnails: { }).run(Locale.nipponese)
|
||||||
|
|
||||||
|
expect(result.fetch(:results).map { _1.fetch(:status) }).to eq(['failed'])
|
||||||
|
expect(event_rows).to eq(events)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not leave an event when every row is skipped' do
|
||||||
|
preflight = instance_double(PostCreatePreflight, run: { existing_post_id: 123 })
|
||||||
|
allow(PostCreatePreflight).to receive(:new).and_return(preflight)
|
||||||
|
events = event_rows
|
||||||
|
|
||||||
|
result = PostBulkCreator.new(
|
||||||
|
actor: @actor, posts: [{ title: 'existing' }], thumbnails: { }).run(Locale.nipponese)
|
||||||
|
|
||||||
|
expect(result.fetch(:results).map { _1.fetch(:status) }).to eq(['skipped'])
|
||||||
|
expect(event_rows).to eq(events)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ RSpec.describe 'Shared resource IDs across MySQL connections', type: :model do
|
|||||||
@tag = create(:tag, primary_name: "#{ @prefix }_owner")
|
@tag = create(:tag, primary_name: "#{ @prefix }_owner")
|
||||||
@group = TagGroup.create!(name: "#{ @prefix }_owner", version_no: 1)
|
@group = TagGroup.create!(name: "#{ @prefix }_owner", version_no: 1)
|
||||||
@tag_ids = Queue.new
|
@tag_ids = Queue.new
|
||||||
|
@event_ids = Queue.new
|
||||||
@threads = []
|
@threads = []
|
||||||
@release = Queue.new
|
@release = Queue.new
|
||||||
end
|
end
|
||||||
@@ -42,6 +43,9 @@ RSpec.describe 'Shared resource IDs across MySQL connections', type: :model do
|
|||||||
ids << @tag.id if @tag
|
ids << @tag.id if @tag
|
||||||
TagName.where(tag_id: ids).delete_all
|
TagName.where(tag_id: ids).delete_all
|
||||||
Tag.where(id: ids).delete_all
|
Tag.where(id: ids).delete_all
|
||||||
|
event_ids = []
|
||||||
|
event_ids << @event_ids.pop until @event_ids.empty? if @event_ids
|
||||||
|
EventRecords::EventRow.where(id: event_ids).delete_all
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -72,11 +76,17 @@ RSpec.describe 'Shared resource IDs across MySQL connections', type: :model do
|
|||||||
when :description
|
when :description
|
||||||
TagDescription.create!(tag: @tag, language_code: 'ja', body: '', version_no: 1)
|
TagDescription.create!(tag: @tag, language_code: 'ja', body: '', version_no: 1)
|
||||||
when :description_version
|
when :description_version
|
||||||
|
event = create_event!(event_type: 'unknown')
|
||||||
|
@event_ids << event.id
|
||||||
TagDescriptionVersion.create!(
|
TagDescriptionVersion.create!(
|
||||||
|
event_id: event.id,
|
||||||
tag_id: @tag.id, language_code: 'ja', body: 'body', version_no: 1,
|
tag_id: @tag.id, language_code: 'ja', body: 'body', version_no: 1,
|
||||||
event_type: 'create', created_at: Time.current)
|
event_type: 'create', created_at: Time.current)
|
||||||
when :group_version
|
when :group_version
|
||||||
|
event = create_event!(event_type: 'unknown')
|
||||||
|
@event_ids << event.id
|
||||||
TagGroupVersion.create!(
|
TagGroupVersion.create!(
|
||||||
|
event_id: event.id,
|
||||||
tag_group: @group, name: @group.name, version_no: 1, event_type: 'create',
|
tag_group: @group, name: @group.name, version_no: 1, event_type: 'create',
|
||||||
members: [], placements: [], created_at: Time.current)
|
members: [], placements: [], created_at: Time.current)
|
||||||
when :group
|
when :group
|
||||||
|
|||||||
@@ -15,10 +15,12 @@ RSpec.describe SharedResourceIdAllocator do
|
|||||||
TagGroup.new(id:, name: "group #{ number }", version_no: number)
|
TagGroup.new(id:, name: "group #{ number }", version_no: number)
|
||||||
when 'tag_description_versions'
|
when 'tag_description_versions'
|
||||||
TagDescriptionVersion.new(
|
TagDescriptionVersion.new(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
id:, tag_id: tag.id, language_code: 'ja', body: 'body', version_no: number,
|
id:, tag_id: tag.id, language_code: 'ja', body: 'body', version_no: number,
|
||||||
event_type: 'create', created_at: Time.current)
|
event_type: 'create', created_at: Time.current)
|
||||||
when 'tag_group_versions'
|
when 'tag_group_versions'
|
||||||
TagGroupVersion.new(
|
TagGroupVersion.new(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
id:, tag_group_id: group.id, name: group.name, version_no: number,
|
id:, tag_group_id: group.id, name: group.name, version_no: number,
|
||||||
event_type: 'create', members: [], placements: [], created_at: Time.current)
|
event_type: 'create', members: [], placements: [], created_at: Time.current)
|
||||||
end
|
end
|
||||||
@@ -82,6 +84,7 @@ RSpec.describe SharedResourceIdAllocator do
|
|||||||
it 'reserves a Tag ID referenced only by remaining description history' do
|
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
|
high = [Tag.maximum(:id).to_i, ExternalTag.maximum(:id).to_i].max + 1000
|
||||||
TagDescriptionVersion.create!(
|
TagDescriptionVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
tag_id: high, language_code: 'ja', version_no: 1, body: 'history',
|
tag_id: high, language_code: 'ja', version_no: 1, body: 'history',
|
||||||
event_type: 'create', created_at: Time.current)
|
event_type: 'create', created_at: Time.current)
|
||||||
expect(create(:tag).id).to be > high
|
expect(create(:tag).id).to be > high
|
||||||
|
|||||||
@@ -42,6 +42,41 @@ RSpec.describe Youtube::Sync do
|
|||||||
end
|
end
|
||||||
|
|
||||||
describe '#sync!' do
|
describe '#sync!' do
|
||||||
|
it 'groups created and updated post histories into one sync event' do
|
||||||
|
allow(PostVersionRecorder).to receive(:record!).and_call_original
|
||||||
|
allow(PostVersionRecorder).to receive(:ensure_snapshot!).and_call_original
|
||||||
|
existing = create(:post, url: 'https://youtu.be/video-1', title: 'old title')
|
||||||
|
allow(sync).to receive(:query_terms).and_return([])
|
||||||
|
allow(sync).to receive(:playlist_ids).and_return(['PL123'])
|
||||||
|
allow(client).to receive(:playlist_items).with(playlist_id: 'PL123', page_token: nil)
|
||||||
|
.and_return('items' => [
|
||||||
|
{ 'contentDetails' => { 'videoId' => 'video-1' } },
|
||||||
|
{ 'contentDetails' => { 'videoId' => 'video-2' } }])
|
||||||
|
allow(client).to receive(:videos).with(['video-1', 'video-2'])
|
||||||
|
.and_return('items' => [
|
||||||
|
youtube_video_item(id: 'video-1', title: 'updated', channel_id: 'UC_NONE'),
|
||||||
|
youtube_video_item(id: 'video-2', title: 'created', channel_id: 'UC_NONE')])
|
||||||
|
previous_post_ids = PostVersion.pluck(:id)
|
||||||
|
previous_tag_ids = TagVersion.pluck(:id)
|
||||||
|
|
||||||
|
expect { sync.sync! }.to change { event_rows.length }.by(1)
|
||||||
|
|
||||||
|
event = event_rows.last
|
||||||
|
expect(event).to include('event_type' => 'posts.sync', 'created_by_user_id' => nil)
|
||||||
|
versions = PostVersion.where.not(id: previous_post_ids)
|
||||||
|
created = Post.find_by!(url: 'https://www.youtube.com/watch?v=video-2')
|
||||||
|
expect(versions.pluck(:post_id).uniq).to contain_exactly(existing.id, created.id)
|
||||||
|
expect(versions.pluck(:event_type)).to include('create', 'update')
|
||||||
|
expect(versions.pluck(:event_id).uniq).to eq([event.fetch('id')])
|
||||||
|
tag_versions = TagVersion.where.not(id: previous_tag_ids)
|
||||||
|
expect(tag_versions).not_to be_empty
|
||||||
|
expect(tag_versions.pluck(:event_id).uniq).to eq([event.fetch('id')])
|
||||||
|
|
||||||
|
events = event_rows
|
||||||
|
expect { sync.sync! }.not_to change(PostVersion, :count)
|
||||||
|
expect(event_rows).to eq(events)
|
||||||
|
end
|
||||||
|
|
||||||
it 'returns without fetching video details when no video ids are discovered' do
|
it 'returns without fetching video details when no video ids are discovered' do
|
||||||
allow(sync).to receive(:query_terms).and_return([])
|
allow(sync).to receive(:query_terms).and_return([])
|
||||||
allow(sync).to receive(:playlist_ids).and_return([])
|
allow(sync).to receive(:playlist_ids).and_return([])
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
module EventRecords
|
||||||
|
# Read the database contract without requiring an application Event model.
|
||||||
|
class EventRow < ActiveRecord::Base
|
||||||
|
self.table_name = 'events'
|
||||||
|
end
|
||||||
|
|
||||||
|
def event_rows
|
||||||
|
EventRow.order(:id).map(&:attributes)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_event! event_type:, created_by_user: nil
|
||||||
|
EventRow.create!(event_type:, created_by_user_id: created_by_user&.id,
|
||||||
|
created_at: Time.current)
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -3,11 +3,13 @@ require 'cgi'
|
|||||||
# SQL fixtures use the migration contract without inventing application models.
|
# SQL fixtures use the migration contract without inventing application models.
|
||||||
# Include explicitly; these helpers must not affect unrelated examples.
|
# Include explicitly; these helpers must not affect unrelated examples.
|
||||||
module TagGroupRecords
|
module TagGroupRecords
|
||||||
|
include EventRecords
|
||||||
|
|
||||||
GROUP_TABLES = [
|
GROUP_TABLES = [
|
||||||
'tag_groups', 'tag_group_tags', 'tag_group_groups',
|
'tag_groups', 'tag_group_tags', 'tag_group_groups',
|
||||||
'tag_group_placements', 'tag_group_versions'].freeze
|
'tag_group_placements', 'tag_group_versions'].freeze
|
||||||
WRITE_TABLES = GROUP_TABLES + [
|
WRITE_TABLES = GROUP_TABLES + [
|
||||||
'tag_names', 'tag_description_versions',
|
'events', 'tag_names', 'tag_description_versions',
|
||||||
'tag_descriptions']
|
'tag_descriptions']
|
||||||
|
|
||||||
def group_connection = ApplicationRecord.connection
|
def group_connection = ApplicationRecord.connection
|
||||||
@@ -114,6 +116,7 @@ module TagGroupRecords
|
|||||||
group_id = insert_group_row('tag_groups', attributes)
|
group_id = insert_group_row('tag_groups', attributes)
|
||||||
insert_group_row('tag_group_versions', {
|
insert_group_row('tag_group_versions', {
|
||||||
tag_group_id: group_id, version_no: 1, event_type: 'create',
|
tag_group_id: group_id, version_no: 1, event_type: 'create',
|
||||||
|
event_id: create_event!(event_type: 'tag_groups.create', created_by_user: user).id,
|
||||||
name:, description:, members: members.to_json, placements: placements.to_json,
|
name:, description:, members: members.to_json, placements: placements.to_json,
|
||||||
created_by_user_id: user.id, created_at: now })
|
created_by_user_id: user.id, created_at: now })
|
||||||
members.each do |member|
|
members.each do |member|
|
||||||
|
|||||||
@@ -174,6 +174,7 @@ RSpec.describe 'nico:sync' do
|
|||||||
|
|
||||||
def create_post_version_for!(post, version_no: 1, event_type: 'create', created_by_user: nil)
|
def create_post_version_for!(post, version_no: 1, event_type: 'create', created_by_user: nil)
|
||||||
PostVersion.create!(
|
PostVersion.create!(
|
||||||
|
event_id: create_event!(event_type: 'unknown').id,
|
||||||
post: post,
|
post: post,
|
||||||
version_no: version_no,
|
version_no: version_no,
|
||||||
event_type: event_type,
|
event_type: event_type,
|
||||||
@@ -186,8 +187,7 @@ RSpec.describe 'nico:sync' do
|
|||||||
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,
|
||||||
created_by_user: created_by_user
|
created_by_user: created_by_user)
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it '新規 post 作成時に version 1 を作る' do
|
it '新規 post 作成時に version 1 を作る' do
|
||||||
@@ -209,6 +209,7 @@ RSpec.describe 'nico:sync' do
|
|||||||
expect {
|
expect {
|
||||||
run_rake_task('nico:sync')
|
run_rake_task('nico:sync')
|
||||||
}.to change(PostVersion, :count).by(1)
|
}.to change(PostVersion, :count).by(1)
|
||||||
|
.and change { event_rows.length }.by(1)
|
||||||
|
|
||||||
post = Post.find_by!(url: 'https://www.nicovideo.jp/watch/sm9')
|
post = Post.find_by!(url: 'https://www.nicovideo.jp/watch/sm9')
|
||||||
version = post.post_versions.order(:version_no).last
|
version = post.post_versions.order(:version_no).last
|
||||||
@@ -217,6 +218,32 @@ RSpec.describe 'nico:sync' do
|
|||||||
expect(version.event_type).to eq('create')
|
expect(version.event_type).to eq('create')
|
||||||
expect(version.created_by_user).to be_nil
|
expect(version.created_by_user).to be_nil
|
||||||
expect(version.tags).to eq(snapshot_tags(post.reload))
|
expect(version.tags).to eq(snapshot_tags(post.reload))
|
||||||
|
event = event_rows.last
|
||||||
|
expect(event).to include('event_type' => 'posts.sync', 'created_by_user_id' => nil)
|
||||||
|
expect(version.event_id).to eq(event.fetch('id'))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'groups created and updated post histories into one sync event' do
|
||||||
|
existing = create(:post, title: 'old', url: 'https://www.nicovideo.jp/watch/sm9')
|
||||||
|
stub_python([
|
||||||
|
{ 'code' => 'sm9', 'title' => 'updated', 'tags' => [] },
|
||||||
|
{ 'code' => 'sm10', 'title' => 'created', 'tags' => [] }])
|
||||||
|
allow(URI).to receive(:open).and_return(StringIO.new('<html></html>'))
|
||||||
|
previous_post_ids = PostVersion.pluck(:id)
|
||||||
|
previous_tag_ids = TagVersion.pluck(:id)
|
||||||
|
|
||||||
|
expect { run_rake_task('nico:sync') }.to change { event_rows.length }.by(1)
|
||||||
|
|
||||||
|
event = event_rows.last
|
||||||
|
expect(event).to include('event_type' => 'posts.sync', 'created_by_user_id' => nil)
|
||||||
|
created = Post.find_by!(url: 'https://www.nicovideo.jp/watch/sm10')
|
||||||
|
versions = PostVersion.where.not(id: previous_post_ids)
|
||||||
|
expect(versions.pluck(:post_id).uniq).to contain_exactly(existing.id, created.id)
|
||||||
|
expect(versions.pluck(:event_type)).to include('create', 'update')
|
||||||
|
expect(versions.pluck(:event_id).uniq).to eq([event.fetch('id')])
|
||||||
|
tag_versions = TagVersion.where.not(id: previous_tag_ids)
|
||||||
|
expect(tag_versions).not_to be_empty
|
||||||
|
expect(tag_versions.pluck(:event_id).uniq).to eq([event.fetch('id')])
|
||||||
end
|
end
|
||||||
|
|
||||||
it '既存 post の内容または tags が変わったとき update version を作る' do
|
it '既存 post の内容または tags が変わったとき update version を作る' do
|
||||||
@@ -282,6 +309,7 @@ RSpec.describe 'nico:sync' do
|
|||||||
}])
|
}])
|
||||||
|
|
||||||
allow(URI).to receive(:open).and_return(StringIO.new('<html></html>'))
|
allow(URI).to receive(:open).and_return(StringIO.new('<html></html>'))
|
||||||
|
events = event_rows
|
||||||
|
|
||||||
expect {
|
expect {
|
||||||
run_rake_task('nico:sync')
|
run_rake_task('nico:sync')
|
||||||
@@ -291,6 +319,7 @@ RSpec.describe 'nico:sync' do
|
|||||||
expect(version.version_no).to eq(1)
|
expect(version.version_no).to eq(1)
|
||||||
expect(version.event_type).to eq('create')
|
expect(version.event_type).to eq('create')
|
||||||
expect(version.tags).to eq(snapshot_tags(post.reload))
|
expect(version.tags).to eq(snapshot_tags(post.reload))
|
||||||
|
expect(event_rows).to eq(events)
|
||||||
end
|
end
|
||||||
|
|
||||||
it '新規 nico tag に nico tag version を作る' do
|
it '新規 nico tag に nico tag version を作る' do
|
||||||
|
|||||||
新しいイシューから参照
ユーザーをブロックする