画面上でのテストは完了. 移行にす早さが求められるので,準備ができたらマージする. Reviewed-on: #430 Co-authored-by: miteruzo <miteruzo@naver.com>
このコミットはプルリクエスト #430 でマージされました。
このコミットが含まれているのは:
@@ -0,0 +1,84 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Shared Tag and ExternalTag IDs', type: :model do
|
||||
include TagGroupRecords
|
||||
let(:user) { create_member_user! }
|
||||
|
||||
def create_resource resource
|
||||
if resource == :tag
|
||||
Tag.find_or_create_by_tag_name!(Locale.nipponese, 'shared_id_new', category: :general)
|
||||
else
|
||||
ExternalTag.create!(platform: :nico, name: 'shared_id_new')
|
||||
end
|
||||
end
|
||||
|
||||
[:tag, :external_tag].each do |resource|
|
||||
it "starts #{ resource } at 1 when both tables are empty" do
|
||||
expect(Tag.count).to eq(0)
|
||||
expect(ExternalTag.count).to eq(0)
|
||||
expect(create_resource(resource).id).to eq(1)
|
||||
end
|
||||
|
||||
it "allocates #{ resource } above the other table's maximum" do
|
||||
create(:tag, id: resource == :tag ? 1 : 20)
|
||||
create(:external_tag, id: resource == :external_tag ? 1 : 20)
|
||||
|
||||
expect(create_resource(resource).id).to eq(21)
|
||||
end
|
||||
|
||||
it "uses the current maximum after deleting earlier #{ resource } records" do
|
||||
tag = create(:tag, id: 100)
|
||||
TagName.where(tag_id: tag.id).update_all(tag_id: nil)
|
||||
tag.destroy!
|
||||
create(:external_tag, id: 200).destroy!
|
||||
expect(Tag.unscoped.count).to eq(0)
|
||||
expect(ExternalTag.unscoped.count).to eq(0)
|
||||
expect(create_resource(resource).id).to eq(1)
|
||||
end
|
||||
|
||||
it "preserves legacy overlaps and gaps when creating #{ resource }" do
|
||||
[1, 2, 5].each { create(:tag, id: _1) }
|
||||
[1, 3, 8].each { create(:external_tag, id: _1) }
|
||||
tags = Tag.order(:id).map(&:attributes)
|
||||
external_tags = ExternalTag.order(:id).map(&:attributes)
|
||||
|
||||
record = create_resource(resource)
|
||||
|
||||
aggregate_failures do
|
||||
expect(record.id).to eq(9)
|
||||
expect(Tag.where(id: [1, 2, 5]).order(:id).map(&:attributes)).to eq(tags)
|
||||
expect(ExternalTag.where(id: [1, 3, 8]).order(:id).map(&:attributes)).to eq(external_tags)
|
||||
expect(Tag.exists?(id: 3)).to be(false)
|
||||
expect(ExternalTag.exists?(id: 2)).to be(false)
|
||||
end
|
||||
end
|
||||
|
||||
it "ignores the description and group ID space when creating #{ resource }" do
|
||||
tag = create(:tag, id: 1)
|
||||
create(:external_tag, id: 2)
|
||||
TagDescription.create!(id: 100, tag:, language_code: 'ja', body: '', version_no: 1)
|
||||
seed_group(name: 'separate_space', id: 200)
|
||||
|
||||
expect(create_resource(resource).id).to eq(3)
|
||||
end
|
||||
end
|
||||
|
||||
it 'preserves overlapping IDs during updates without rewriting the other resource' do
|
||||
tag = create(:tag, id: 5)
|
||||
external = create(:external_tag, id: 5)
|
||||
tag.update!(category: :meta)
|
||||
external.update!(name: 'updated_external')
|
||||
|
||||
expect(tag.reload.id).to eq(5)
|
||||
expect(external.reload.id).to eq(5)
|
||||
expect(tag.category).to eq('meta')
|
||||
expect(external.name).to eq('updated_external')
|
||||
expect(Tag.count).to eq(1)
|
||||
expect(ExternalTag.count).to eq(1)
|
||||
end
|
||||
|
||||
it 'allocates distinct IDs for successive creations across both tables' do
|
||||
records = [:tag, :external_tag].map { create_resource(_1) }
|
||||
expect(records.map(&:id)).to eq([1, 2])
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,50 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe TagRepr do
|
||||
let(:tag) { create(:tag) }
|
||||
|
||||
def description_for record
|
||||
TagDescription.create!(tag: record, language_code: 'ja', body: '', version_no: 1)
|
||||
end
|
||||
|
||||
it 'keeps has_wiki boolean for the selected language in base and inline' do
|
||||
description_for(tag)
|
||||
[:base, :inline].each do |method|
|
||||
expect(described_class.public_send(method, tag).as_json.fetch('has_wiki')).to be(true)
|
||||
expect(described_class.public_send(method, tag, language_code: 'en')
|
||||
.as_json.fetch('has_wiki')).to be(false)
|
||||
end
|
||||
end
|
||||
|
||||
it 'uses the same language for a tag and its parents' do
|
||||
parent = create(:tag)
|
||||
description_for(tag)
|
||||
description_for(parent)
|
||||
TagImplication.create!(tag:, parent_tag: parent)
|
||||
['ja', 'en'].each do |language_code|
|
||||
row = described_class.base(tag, language_code:).as_json
|
||||
expected = language_code == 'ja'
|
||||
expect(row.fetch('has_wiki')).to be(expected)
|
||||
expect(row.fetch('parents').sole.fetch('has_wiki')).to be(expected)
|
||||
end
|
||||
end
|
||||
|
||||
it 'preloads existence checks for many tags and their parents without Wiki reads' do
|
||||
parent = create(:tag)
|
||||
description_for(parent)
|
||||
records = Array.new(3) do
|
||||
create(:tag).tap { TagImplication.create!(tag: _1, parent_tag: parent) }
|
||||
end
|
||||
description_for(records.first)
|
||||
queries = []
|
||||
subscriber = ->(*args) { queries << args.last.fetch(:sql) }
|
||||
rows = nil
|
||||
ActiveSupport::Notifications.subscribed(subscriber, 'sql.active_record') do
|
||||
rows = described_class.many(records).as_json
|
||||
end
|
||||
expect(rows.map { _1.fetch('has_wiki') }).to eq([true, false, false])
|
||||
expect(rows.map { _1.fetch('parents').sole.fetch('has_wiki') }).to eq([true] * 3)
|
||||
expect(queries.grep(/\bSELECT\b.*\btag_descriptions\b/im).size).to be <= 2
|
||||
expect(queries.grep(/\bwiki_(pages|revisions|versions)\b/i)).to be_empty
|
||||
end
|
||||
end
|
||||
@@ -1,6 +1,47 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Tag, type: :model do
|
||||
describe '#has_description?' do
|
||||
let(:tag) { create(:tag) }
|
||||
|
||||
it 'checks the requested language and treats an empty description as present' do
|
||||
TagDescription.create!(tag:, language_code: 'ja', body: '', version_no: 1)
|
||||
expect(tag.has_description?(language_code: 'ja')).to be(true)
|
||||
expect(tag.has_description?(language_code: 'en')).to be(false)
|
||||
end
|
||||
|
||||
it 'returns false without a description' do
|
||||
expect(tag.has_description?(language_code: 'ja')).to be(false)
|
||||
end
|
||||
|
||||
it 'does not use a Wiki page as evidence of a description' do
|
||||
user = create_member_user!
|
||||
Wiki::Commit.create_legacy_content!(tag_name: tag.tag_name('ja'), body: 'legacy',
|
||||
created_by_user: user)
|
||||
expect(tag.has_description?(language_code: 'ja')).to be(false)
|
||||
end
|
||||
|
||||
it 'reuses preloaded descriptions without reading Wiki or querying again' do
|
||||
TagDescription.create!(tag:, language_code: 'ja', body: '', version_no: 1)
|
||||
loaded = Tag.includes(:tag_descriptions).find(tag.id)
|
||||
queries = []
|
||||
subscriber = ->(*args) { queries << args.last.fetch(:sql) }
|
||||
ActiveSupport::Notifications.subscribed(subscriber, 'sql.active_record') do
|
||||
expect(loaded.has_description?(language_code: 'ja')).to be(true)
|
||||
expect(loaded.has_description?(language_code: 'en')).to be(false)
|
||||
end
|
||||
expect(queries).to be_empty
|
||||
end
|
||||
|
||||
it 'keeps language selection generic without enabling another Language record' do
|
||||
tag.association(:tag_descriptions).target = [
|
||||
TagDescription.new(tag:, language_code: 'en', body: '', version_no: 1)]
|
||||
tag.association(:tag_descriptions).loaded!
|
||||
expect(tag.has_description?(language_code: 'en')).to be(true)
|
||||
expect(tag.has_description?(language_code: 'ja')).to be(false)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'external tag separation' do
|
||||
['nico:reserved', 'NiCo:reserved'].each do |name|
|
||||
it "rejects the reserved prefix #{ name } for internal tags" do
|
||||
|
||||
新しいイシューから参照
ユーザーをブロックする