このコミットが含まれているのは:
2026-09-27 15:07:24 +09:00
コミット c912bebbee
40個のファイルの変更、473行の追加、377行の削除
+10 -6
ファイルの表示
@@ -7,6 +7,8 @@ class PostsController < ApplicationController
end end
def index def index
locale = resolve_locale!
url = params[:url].presence url = params[:url].presence
title = params[:title].presence title = params[:title].presence
original_created_from = params[:original_created_from].presence original_created_from = params[:original_created_from].presence
@@ -46,7 +48,7 @@ class PostsController < ApplicationController
'COALESCE(pt_max.max_updated_at, posts.updated_at))' 'COALESCE(pt_max.max_updated_at, posts.updated_at))'
q = q =
filtered_posts filtered_posts(locale)
.joins("LEFT JOIN (#{ pt_max_sql }) pt_max ON pt_max.post_id = posts.id") .joins("LEFT JOIN (#{ pt_max_sql }) pt_max ON pt_max.post_id = posts.id")
.reselect('posts.*', Arel.sql("#{ updated_at_all_sql } AS updated_at_all")) .reselect('posts.*', Arel.sql("#{ updated_at_all_sql } AS updated_at_all"))
.preload(:external_tags, :uploaded_user, :parents, :children, .preload(:external_tags, :uploaded_user, :parents, :children,
@@ -102,8 +104,10 @@ class PostsController < ApplicationController
end end
def random def random
locale = resolve_locale!
post = post =
filtered_posts filtered_posts(locale)
.preload(:uploaded_user, :parents, :children, .preload(:uploaded_user, :parents, :children,
post_tags: [:sections, { tag: [:deerjikists, :materials, post_tags: [:sections, { tag: [:deerjikists, :materials,
{ tag_name: :wiki_page }] }]) { tag_name: :wiki_page }] }])
@@ -394,19 +398,19 @@ class PostsController < ApplicationController
private private
def filtered_posts def filtered_posts locale
tag_names = params[:tags].to_s.split tag_names = params[:tags].to_s.split
match_type = params[:match] match_type = params[:match]
if tag_names.present? if tag_names.present?
filter_posts_by_tags(tag_names, match_type) filter_posts_by_tags(locale, tag_names, match_type)
else else
Post.all Post.all
end end
end end
def filter_posts_by_tags tag_names, match_type def filter_posts_by_tags locale, tag_names, match_type
literals = tag_names.map do |raw_name| literals = tag_names.map do |raw_name|
{ name: TagName.canonicalise(raw_name.sub(/\Anot:/i, '')).first, { name: TagName.canonicalise(locale, raw_name.sub(/\Anot:/i, '')).first,
negative: raw_name.downcase.start_with?('not:') } negative: raw_name.downcase.start_with?('not:') }
end end
+16 -14
ファイルの表示
@@ -430,7 +430,7 @@ class TagsController < ApplicationController
else else
tag.update!(category:, deprecated_at: deprecated ? Time.current : nil) tag.update!(category:, deprecated_at: deprecated ? Time.current : nil)
end end
rename_tag_name!(tag, name) if name_changed rename_tag_name!(locale, tag, name) if name_changed
alias_names << old_name if name_changed alias_names << old_name if name_changed
alias_names.delete(name) alias_names.delete(name)
@@ -701,13 +701,13 @@ class TagsController < ApplicationController
false false
end end
def rename_tag_name! tag, name def rename_tag_name! locale, tag, name
return if name == tag.name return if name == tag.name(locale.language_code)
current_tag_name = tag.tag_name current_tag_name = tag.tag_name(locale.language_code)
target_tag_name = TagName.find_by(name:) target_tag_name = TagName.find_by(language_code: locale.language_code, name:)
if target_tag_name.nil? unless target_tag_name
current_tag_name.update!(name:) current_tag_name.update!(name:)
return return
end end
@@ -722,19 +722,21 @@ class TagsController < ApplicationController
TagVersioning.ensure_snapshot!(old_owner_tag, created_by_user: current_user) TagVersioning.ensure_snapshot!(old_owner_tag, created_by_user: current_user)
end end
promoted_tag_name.update!(canonical: nil) promoted_tag_name.update!(canonical_id: nil, primary_flg: true)
TagName.where(canonical_id: current_tag_name.id) TagName.where(tag:,
language_code: current_tag_name.language.language_code,
primary_flg: false)
.where.not(id: promoted_tag_name.id) .where.not(id: promoted_tag_name.id)
.find_each do |alias_tag_name| .find_each do |alias_tag_name|
alias_tag_name.update!(canonical: promoted_tag_name) alias_tag_name.update!(canonical_id: promoted_tag_name, primary_flg: false)
end end
current_tag_name.wiki_page&.update!(tag_name: promoted_tag_name) current_tag_name.wiki_page&.update!(tag_name_id: promoted_tag_name.id)
tag.update!(tag_name: promoted_tag_name) tag.update!(tag_name_id: promoted_tag_name.id)
current_tag_name.association(:wiki_page).reset current_tag_name.association(:wiki_page).reset
current_tag_name.association(:tag).reset current_tag_name.association(:tag).reset
current_tag_name.reload.update!(canonical: promoted_tag_name) current_tag_name.reload.update!(canonical_id: promoted_tag_name, primary_flg: false)
return unless old_owner_tag && old_owner_tag != tag return unless old_owner_tag && old_owner_tag != tag
@@ -766,12 +768,12 @@ class TagsController < ApplicationController
current_aliases.each do |alias_tag_name| current_aliases.each do |alias_tag_name|
next if alias_names.include?(alias_tag_name.name) next if alias_names.include?(alias_tag_name.name)
alias_tag_name.update!(canonical: nil) alias_tag_name.update!(canonical_id: nil)
end end
alias_names.each do |alias_name| alias_names.each do |alias_name|
alias_tag_name = TagName.find_or_create_by!(name: alias_name) alias_tag_name = TagName.find_or_create_by!(name: alias_name)
alias_tag_name.update!(canonical: tag.tag_name) alias_tag_name.update!(canonical_id: tag.tag_name.id)
end end
affected_tags.compact.uniq.each do |affected_tag| affected_tags.compact.uniq.each do |affected_tag|
+3 -6
ファイルの表示
@@ -51,12 +51,10 @@ class Tag < ApplicationRecord
has_many :tag_versions has_many :tag_versions
has_many :tag_names has_many :tag_names
belongs_to :tag_name
delegate :wiki_page, to: :tag_name delegate :wiki_page, to: :tag_name
attribute :version_no, :integer, default: 1 attribute :version_no, :integer, default: 1
delegate :name, to: :tag_name, allow_nil: true
validates :tag_name, presence: true validates :tag_name, presence: true
enum :category, deerjikist: 'deerjikist', enum :category, deerjikist: 'deerjikist',
@@ -84,9 +82,8 @@ class Tag < ApplicationRecord
'mtr:' => :material, 'mtr:' => :material,
'meta:' => :meta }.freeze 'meta:' => :meta }.freeze
def name= val def tag_name(language_code) = tag_names.find_by(language_code:, primary_flg: true)
(self.tag_name ||= build_tag_name).name = val def name(language_code) = tag_name(language_code)&.name
end
def deprecated? = deprecated_at? def deprecated? = deprecated_at?
@@ -276,7 +273,7 @@ class Tag < ApplicationRecord
end end
TagVersioning.record!(source_tag, event_type: :discard, created_by_user:) TagVersioning.record!(source_tag, event_type: :discard, created_by_user:)
source_tag.tag_names.update_all(tag_id: source_tag.id, primary_key: false, source_tag.tag_names.update_all(tag_id: target_tag.id, primary_flg: false,
updated_at: Time.current) updated_at: Time.current)
source_tag.destroy! source_tag.destroy!
+3 -3
ファイルの表示
@@ -13,8 +13,8 @@ class TagName < ApplicationRecord
def primary? = primary_flg def primary? = primary_flg
def canonical = TagName.find_by(language:, tag:, primary_flg: true) def canonical = TagName.find_by(language_code:, tag_id:, primary_flg: true)
def aliases = TagName.where(language:, tag:, primary_flg: false) def aliases = TagName.where(language_code:, tag_id:, primary_flg: false)
def self.canonicalise locale, names def self.canonicalise locale, names
names = Array(names).map { |n| n.to_s.strip }.reject(&:blank?) names = Array(names).map { |n| n.to_s.strip }.reject(&:blank?)
@@ -23,7 +23,7 @@ class TagName < ApplicationRecord
tns = TagName.where(language_code: locale.language_code, name: names).index_by(&:name) tns = TagName.where(language_code: locale.language_code, name: names).index_by(&:name)
names.map { |name| names.map { |name|
if tns[name].primary_flg if !(tns[name]) || tns[name].primary?
name name
else else
TagName.find_by(language_code: locale.language_code, TagName.find_by(language_code: locale.language_code,
+7 -8
ファイルの表示
@@ -10,7 +10,7 @@ class PostCreatePlan
def build! locale def build! locale
direct_tag_specs, tag_sections = parse_direct_tag_specs(locale) direct_tag_specs, tag_sections = parse_direct_tag_specs(locale)
default_tag_specs = build_default_tag_specs(direct_tag_specs) default_tag_specs = build_default_tag_specs(locale, direct_tag_specs)
snapshot_tag_specs = merge_tag_specs(direct_tag_specs + default_tag_specs) snapshot_tag_specs = merge_tag_specs(direct_tag_specs + default_tag_specs)
preload_existing_tags_by_name!(snapshot_tag_specs.map { _1[:name] }) preload_existing_tags_by_name!(snapshot_tag_specs.map { _1[:name] })
validate_new_tag_specs!(snapshot_tag_specs) validate_new_tag_specs!(snapshot_tag_specs)
@@ -263,12 +263,11 @@ class PostCreatePlan
end end
def resolved_tag_name locale, name def resolved_tag_name locale, name
tag_name = TagName.find_by!(language_code: locale.language_code, name:) tag_name = TagName.find_by(language_code: locale.language_code, name:)
unless tag_name.primary_flg return name if !(tag_name) || tag_name.primary?
tag_name = TagName.find_by!(language_code: locale.language_code,
tag_id: tag_name.tag_id, TagName.find_by!(language_code: locale.language_code,
primary_flg: true) tag_id: tag_name.tag_id,
end primary_flg: true).name
tag_name.name
end end
end end
+3 -3
ファイルの表示
@@ -74,13 +74,13 @@ class PostCreator
normalised_parent_post_ids: @attributes[:normalised_parent_post_ids] || [], normalised_parent_post_ids: @attributes[:normalised_parent_post_ids] || [],
video_ms: @attributes[:video_ms] } video_ms: @attributes[:video_ms] }
else else
build_materialised_plan build_materialised_plan(locale)
end end
end end
end end
def build_materialised_plan def build_materialised_plan locale
plan = PostCreatePlan.new(attributes: @attributes).build! plan = PostCreatePlan.new(attributes: @attributes).build!(locale)
snapshot_tags = materialise_tags(plan[:snapshot_tag_specs] || []) snapshot_tags = materialise_tags(plan[:snapshot_tag_specs] || [])
post_tags = materialise_tags(plan[:post_tag_specs] || []) post_tags = materialise_tags(plan[:post_tag_specs] || [])
{ {
+15 -6
ファイルの表示
@@ -1,20 +1,29 @@
FactoryBot.define do FactoryBot.define do
factory :tag do factory :tag do
transient do transient do
name { nil } primary_name { nil }
primary_tag_name do
attributes = primary_name.nil? ? { } : { name: primary_name }
build(:tag_name, **attributes)
end
end end
category { :general } category { :general }
post_count { 0 } post_count { 0 }
association :tag_name
after(:build) do |tag, evaluator| after(:build) do |tag, evaluator|
tag.name = evaluator.name if evaluator.name.present? evaluator.primary_tag_name.primary_flg = true
tag.tag_names.target << evaluator.primary_tag_name
end end
after(:create) do |tag| before(:create) do |tag, evaluator|
tag.tag_name.update!(tag:) if tag.tag_name.tag_id != tag.id evaluator.primary_tag_name.save!
# Compatibility write only; ownership is established through tag_id below.
tag.tag_name_id = evaluator.primary_tag_name.id
end
after(:create) do |tag, evaluator|
evaluator.primary_tag_name.update!(tag:, primary_flg: true)
end end
end end
end end
+9 -7
ファイルの表示
@@ -13,17 +13,19 @@ RSpec.describe Locale, type: :model do
end end
it 'generates a primary name for every existing tag using the name generator' do it 'generates a primary name for every existing tag using the name generator' do
first = create(:tag, name: 'first_existing_tag') first = create(:tag, primary_name: 'first_existing_tag')
second = create(:tag, name: 'second_existing_tag') second = create(:tag, primary_name: 'second_existing_tag')
allow(TagName).to receive(:generate_name).and_call_original allow(TagName).to receive(:generate_name).and_call_original
allow(TagName).to receive(:generate_name) allow(TagName).to receive(:generate_name)
.with(kind_of(described_class), first, first.name) .with(kind_of(described_class), first, primary_tag_name_for(first, 'ja').name)
.and_return('name_from_generator') .and_return('name_from_generator')
locale = create_french_locale! locale = create_french_locale!
expect(TagName).to have_received(:generate_name).with(locale, first, first.name) expect(TagName).to have_received(:generate_name)
expect(TagName).to have_received(:generate_name).with(locale, second, second.name) .with(locale, first, primary_tag_name_for(first, 'ja').name)
expect(TagName).to have_received(:generate_name)
.with(locale, second, primary_tag_name_for(second, 'ja').name)
expect(first.tag_names.find_by!(language_code: 'fr', primary_flg: true)) expect(first.tag_names.find_by!(language_code: 'fr', primary_flg: true))
.to have_attributes(name: 'name_from_generator', tag_id: first.id, .to have_attributes(name: 'name_from_generator', tag_id: first.id,
language_code: 'fr', script_code: 'Latn', language_code: 'fr', script_code: 'Latn',
@@ -37,7 +39,7 @@ RSpec.describe Locale, type: :model do
it 'does not duplicate an existing primary name in the new language' do it 'does not duplicate an existing primary name in the new language' do
prepare_french_reference! prepare_french_reference!
tag = create(:tag, name: 'already_named') tag = create(:tag, primary_name: 'already_named')
existing = create(:tag_name, name: 'nom_existant', tag:, existing = create(:tag_name, name: 'nom_existant', tag:,
language_code: 'fr', script_code: 'Latn') language_code: 'fr', script_code: 'Latn')
@@ -51,7 +53,7 @@ RSpec.describe Locale, type: :model do
it 'creates a primary name when the language has only an alias' do it 'creates a primary name when the language has only an alias' do
prepare_french_reference! prepare_french_reference!
tag = create(:tag, name: 'alias_only_tag') tag = create(:tag, primary_name: 'alias_only_tag')
alias_name = create(:tag_name, :alias, name: 'alias_fr', alias_name = create(:tag_name, :alias, name: 'alias_fr',
tag:, tag:,
language_code: 'fr', script_code: 'Latn') language_code: 'fr', script_code: 'Latn')
+1 -1
ファイルの表示
@@ -2,7 +2,7 @@ require 'rails_helper'
RSpec.describe MaterialExportItem, type: :model do RSpec.describe MaterialExportItem, type: :model do
let(:user) { create(:user, :member) } let(:user) { create(:user, :member) }
let(:tag) { create(:tag, name: 'export_item', category: :material) } let(:tag) { create(:tag, primary_name: 'export_item', category: :material) }
let(:material) do let(:material) do
Material.create!(tag:, url: 'https://example.com/material', Material.create!(tag:, url: 'https://example.com/material',
created_by_user: user, updated_by_user: user) created_by_user: user, updated_by_user: user)
+1 -1
ファイルの表示
@@ -18,7 +18,7 @@ RSpec.describe Post, type: :model do
expect(post.snapshot_tags_json).to eq([ expect(post.snapshot_tags_json).to eq([
{ 'tag_id' => tag.id, { 'tag_id' => tag.id,
'version_no' => tag.version_no, 'version_no' => tag.version_no,
'name' => tag.name, 'name' => primary_tag_name_for(tag, 'ja').name,
'category' => tag.category, 'category' => tag.category,
'sections' => [{ 'begin_ms' => 2000, 'end_ms' => nil }] }, 'sections' => [{ 'begin_ms' => 2000, 'end_ms' => nil }] },
{ 'external_tag_id' => external.id }]) { 'external_tag_id' => external.id }])
+1 -1
ファイルの表示
@@ -2,7 +2,7 @@ require 'rails_helper'
RSpec.describe PostVersion, type: :model do RSpec.describe PostVersion, type: :model do
let!(:tag_name) { create(:tag_name, name: 'post_version_spec_tag') } let!(:tag_name) { create(:tag_name, name: 'post_version_spec_tag') }
let!(:tag) { create(:tag, tag_name: tag_name, category: :general) } let!(:tag) { create(:tag, primary_tag_name: tag_name, category: :general) }
let!(:post_record) do let!(:post_record) do
Post.create!(title: 'spec post', url: 'https://example.com/post-version-spec').tap do |post| Post.create!(title: 'spec post', url: 'https://example.com/post-version-spec').tap do |post|
+5 -5
ファイルの表示
@@ -2,8 +2,8 @@ require 'rails_helper'
RSpec.describe TagImplication, type: :model do RSpec.describe TagImplication, type: :model do
it 'rejects a parent tag that would create a cycle' do it 'rejects a parent tag that would create a cycle' do
child = create(:tag, name: 'tag_implication_cycle_child') child = create(:tag, primary_name: 'tag_implication_cycle_child')
parent = create(:tag, name: 'tag_implication_cycle_parent') parent = create(:tag, primary_name: 'tag_implication_cycle_parent')
described_class.create!(tag: child, parent_tag: parent) described_class.create!(tag: child, parent_tag: parent)
@@ -17,9 +17,9 @@ RSpec.describe TagImplication, type: :model do
end end
it 'terminates even when existing data already contains a cycle' do it 'terminates even when existing data already contains a cycle' do
child = create(:tag, name: 'tag_implication_existing_cycle_child') child = create(:tag, primary_name: 'tag_implication_existing_cycle_child')
parent = create(:tag, name: 'tag_implication_existing_cycle_parent') parent = create(:tag, primary_name: 'tag_implication_existing_cycle_parent')
ancestor = create(:tag, name: 'tag_implication_existing_cycle_ancestor') ancestor = create(:tag, primary_name: 'tag_implication_existing_cycle_ancestor')
described_class.create!(tag: parent, parent_tag: ancestor) described_class.create!(tag: parent, parent_tag: ancestor)
described_class.insert_all!( described_class.insert_all!(
+15 -10
ファイルの表示
@@ -50,17 +50,17 @@ RSpec.describe TagNameSanitisationRule, type: :model do
end end
it 'renames without merging tags or deleting either language identity' do it 'renames without merging tags or deleting either language identity' do
japanese = create(:tag, name: 'foobar') japanese = create(:tag, primary_name: 'foobar')
english_name = create(:tag_name, name: 'temporary', english_name = create(:tag_name, name: 'temporary',
language_code: 'en', script_code: 'Latn') language_code: 'en', script_code: 'Latn')
english = create(:tag, tag_name: english_name) english = create(:tag, primary_tag_name: english_name)
english_name.update_columns(name: 'foo_bar') english_name.update_columns(name: 'foo_bar')
described_class.apply! described_class.apply!
expect(english_name.reload).to have_attributes( expect(english_name.reload).to have_attributes(
name: 'foobar', language_code: 'en', tag_id: english.id) name: 'foobar', language_code: 'en', tag_id: english.id)
expect(japanese.reload.tag_name).to have_attributes( expect(primary_tag_name_for(japanese.reload, 'ja')).to have_attributes(
name: 'foobar', language_code: 'ja', tag_id: japanese.id) name: 'foobar', language_code: 'ja', tag_id: japanese.id)
expect(english.reload.tag_name_id).to eq(english_name.id) expect(english.reload.tag_name_id).to eq(english_name.id)
end end
@@ -96,15 +96,19 @@ RSpec.describe TagNameSanitisationRule, type: :model do
context 'when the source tag_name has a tag and the existing one has no tag' do context 'when the source tag_name has a tag and the existing one has no tag' do
let!(:existing) { create(:tag_name, name: 'foobar') } let!(:existing) { create(:tag_name, name: 'foobar') }
let!(:source_tag) { create(:tag, name: 'tmp', category: :general) } let!(:source_tag) { create(:tag, primary_name: 'tmp', category: :general) }
let!(:source_tag_name_id) { source_tag.tag_name_id } let!(:source_tag_name_id) { primary_tag_name_for(source_tag, 'ja').id }
before do before do
source_tag.tag_name.update_columns(name: 'foo_bar', updated_at: Time.current) primary_tag_name_for(source_tag, 'ja').update_columns(
name: 'foo_bar', updated_at: Time.current)
end end
it 'moves the tag to the existing tag_name' do it 'moves the tag to the existing tag_name' do
described_class.apply! described_class.apply!
expect(existing.reload).to have_attributes(
tag_id: source_tag.id, language_code: 'ja', primary_flg: true)
expect(primary_tag_name_for(source_tag, 'ja')).to eq(existing)
expect(source_tag.reload.tag_name_id).to eq(existing.id) expect(source_tag.reload.tag_name_id).to eq(existing.id)
expect(TagName.unscoped.exists?(source_tag_name_id)).to be(false) expect(TagName.unscoped.exists?(source_tag_name_id)).to be(false)
end end
@@ -112,6 +116,7 @@ RSpec.describe TagNameSanitisationRule, type: :model do
context 'when the sanitised name is an alias of an existing tag' do context 'when the sanitised name is an alias of an existing tag' do
let!(:existing_tag) { create(:tag) } let!(:existing_tag) { create(:tag) }
let!(:existing_primary) { primary_tag_name_for(existing_tag, 'ja') }
let!(:alias_name) do let!(:alias_name) do
create(:tag_name, :alias, name: 'foobar', tag: existing_tag) create(:tag_name, :alias, name: 'foobar', tag: existing_tag)
end end
@@ -129,17 +134,17 @@ RSpec.describe TagNameSanitisationRule, type: :model do
tag_id: existing_tag.id, language_code: 'ja', primary_flg: false) tag_id: existing_tag.id, language_code: 'ja', primary_flg: false)
expect(TagName.find_by!(tag_id: existing_tag.id, expect(TagName.find_by!(tag_id: existing_tag.id,
language_code: 'ja', primary_flg: true)) language_code: 'ja', primary_flg: true))
.to eq(existing_tag.tag_name) .to eq(existing_primary)
expect(Tag.find(existing_tag.id)).to eq(existing_tag) expect(Tag.find(existing_tag.id)).to eq(existing_tag)
end end
end end
context 'when both source and existing tag_names have tags' do context 'when both source and existing tag_names have tags' do
let!(:existing_tn) { create(:tag_name, name: 'foobar') } let!(:existing_tn) { create(:tag_name, name: 'foobar') }
let!(:existing_tag) { create(:tag, tag_name: existing_tn, category: :general) } let!(:existing_tag) { create(:tag, primary_tag_name: existing_tn, category: :general) }
let!(:source_tn) { create(:tag_name, name: 'tmp') } let!(:source_tn) { create(:tag_name, name: 'tmp') }
let!(:source_tag) { create(:tag, tag_name: source_tn, category: :general) } let!(:source_tag) { create(:tag, primary_tag_name: source_tn, category: :general) }
let!(:source_tag_name_id) { source_tn.id } let!(:source_tag_name_id) { source_tn.id }
before do before do
@@ -155,7 +160,7 @@ RSpec.describe TagNameSanitisationRule, type: :model do
expect(Tag.unscoped.exists?(source_tag.id)).to be(false) expect(Tag.unscoped.exists?(source_tag.id)).to be(false)
expect(TagName.unscoped.exists?(source_tag_name_id)).to be(false) expect(TagName.unscoped.exists?(source_tag_name_id)).to be(false)
expect(post.reload.tags).to contain_exactly(existing_tag) expect(post.reload.tags).to contain_exactly(existing_tag)
expect(existing_tag.reload.name).to eq('foobar') expect(primary_tag_name_for(existing_tag.reload, 'ja').name).to eq('foobar')
end end
end end
end end
+26 -9
ファイルの表示
@@ -28,16 +28,33 @@ RSpec.describe TagName, type: :model do
end end
it 'persists both sides of the representative tag factory association' do it 'persists both sides of the representative tag factory association' do
tag = create(:tag) name = create(:tag_name)
tag = create(:tag, primary_tag_name: name)
expect(tag.reload.tag_name.reload.tag_id).to eq(tag.id) expect(name.reload).to have_attributes(
expect(tag.tag_name.tag).to eq(tag) tag_id: tag.id, language_code: 'ja', script_code: 'Jpan', primary_flg: true)
expect(name.tag).to eq(tag)
expect(described_class.find_by!(tag_id: tag.id, language_code: 'ja', primary_flg: true)) expect(described_class.find_by!(tag_id: tag.id, language_code: 'ja', primary_flg: true))
.to eq(tag.tag_name) .to eq(name)
expect(tag.reload.tag_name_id).to eq(name.id)
end
it 'builds a primary name without persisting the tag or its name' do
tag = nil
expect {
tag = build(:tag, primary_name: 'built_primary')
}.to change(Tag, :count).by(0).and change(described_class, :count).by(0)
expect(tag).to be_new_record
expect(tag.tag_names.target).to contain_exactly(have_attributes(
name: 'built_primary', language_code: 'ja', script_code: 'Jpan',
primary_flg: true))
end end
it 'persists an alias owned by the same tag as its primary name' do it 'persists an alias owned by the same tag as its primary name' do
tag = create(:tag) primary_name = create(:tag_name)
tag = create(:tag, primary_tag_name: primary_name)
alias_name = create(:tag_name, :alias, name: 'valid_alias', tag:) alias_name = create(:tag_name, :alias, name: 'valid_alias', tag:)
expect(alias_name.reload).to have_attributes( expect(alias_name.reload).to have_attributes(
@@ -46,12 +63,12 @@ RSpec.describe TagName, type: :model do
primary = described_class.find_by!(tag_id: alias_name.tag_id, primary = described_class.find_by!(tag_id: alias_name.tag_id,
language_code: alias_name.language_code, language_code: alias_name.language_code,
primary_flg: true) primary_flg: true)
expect(primary).to eq(tag.tag_name) expect(primary).to eq(primary_name)
end end
describe '.canonicalise' do describe '.canonicalise' do
it 'resolves the primary name of the alias language on the same tag' do it 'resolves the primary name of the alias language on the same tag' do
tag = create(:tag, name: '日本語正本') tag = create(:tag, primary_name: '日本語正本')
english_primary = create(:tag_name, tag:, name: 'english_primary', english_primary = create(:tag_name, tag:, name: 'english_primary',
language_code: 'en', script_code: 'Latn') language_code: 'en', script_code: 'Latn')
english_alias = create(:tag_name, :alias, tag:, name: 'english_alias', english_alias = create(:tag_name, :alias, tag:, name: 'english_alias',
@@ -69,10 +86,10 @@ RSpec.describe TagName, type: :model do
end end
it 'resolves only aliases in the requested language' do it 'resolves only aliases in the requested language' do
japanese = create(:tag, name: 'japanese_canonical') japanese = create(:tag, primary_name: 'japanese_canonical')
english_name = create(:tag_name, name: 'english_canonical', english_name = create(:tag_name, name: 'english_canonical',
language_code: 'en', script_code: 'Latn') language_code: 'en', script_code: 'Latn')
english = create(:tag, tag_name: english_name) english = create(:tag, primary_tag_name: english_name)
japanese_alias = create(:tag_name, :alias, name: 'shared_alias', japanese_alias = create(:tag_name, :alias, name: 'shared_alias',
tag: japanese) tag: japanese)
english_alias = create(:tag_name, :alias, name: 'shared_alias', english_alias = create(:tag_name, :alias, name: 'shared_alias',
+54 -40
ファイルの表示
@@ -4,7 +4,7 @@ RSpec.describe Tag, type: :model do
describe 'external tag separation' do describe 'external tag separation' do
['nico:reserved', 'NiCo:reserved'].each do |name| ['nico:reserved', 'NiCo:reserved'].each do |name|
it "rejects the reserved prefix #{ name } for internal tags" do it "rejects the reserved prefix #{ name } for internal tags" do
tag = build(:tag, name:) tag = build(:tag, primary_name: name)
expect(tag).to be_invalid expect(tag).to be_invalid
expect(tag.errors[:name]).to be_present expect(tag.errors[:name]).to be_present
@@ -32,10 +32,10 @@ RSpec.describe Tag, type: :model do
{ code: 'en', language_code: 'en', script_code: 'Latn', { code: 'en', language_code: 'en', script_code: 'Latn',
name: 'English', created_at: Time.current }]) unless Locale.exists?(code: 'en') name: 'English', created_at: Time.current }]) unless Locale.exists?(code: 'en')
english_locale = Locale.find('en') english_locale = Locale.find('en')
japanese = create(:tag, name: 'japanese_canonical') japanese = create(:tag, primary_name: 'japanese_canonical')
english_name = create(:tag_name, name: 'english_canonical', english_name = create(:tag_name, name: 'english_canonical',
language_code: 'en', script_code: 'Latn') language_code: 'en', script_code: 'Latn')
english = create(:tag, tag_name: english_name) english = create(:tag, primary_tag_name: english_name)
japanese_alias = create(:tag_name, :alias, name: 'shared_alias', japanese_alias = create(:tag_name, :alias, name: 'shared_alias',
tag: japanese) tag: japanese)
english_alias = create(:tag_name, :alias, name: 'shared_alias', english_alias = create(:tag_name, :alias, name: 'shared_alias',
@@ -59,16 +59,16 @@ RSpec.describe Tag, type: :model do
it 'rejects deprecated tags when deny_deprecated is enabled' do it 'rejects deprecated tags when deny_deprecated is enabled' do
tag_name = create(:tag_name, name: 'normalise deprecated tag') tag_name = create(:tag_name, name: 'normalise deprecated tag')
deprecated_tag = create(:tag, deprecated_tag = create(:tag,
tag_name:, primary_tag_name: tag_name,
category: :general, category: :general,
deprecated_at: 1.day.from_now) deprecated_at: 1.day.from_now)
expect { expect {
described_class.normalise_tags!(Locale.nipponese, described_class.normalise_tags!(Locale.nipponese,
[deprecated_tag.name], [primary_tag_name_for(deprecated_tag, 'ja').name],
deny_deprecated: true) deny_deprecated: true)
}.to raise_error(Tag::DeprecatedTagNormalisationError) { |error| }.to raise_error(Tag::DeprecatedTagNormalisationError) { |error|
expect(error.tag_names).to eq([deprecated_tag.name]) expect(error.tag_names).to eq([primary_tag_name_for(deprecated_tag, 'ja').name])
} }
end end
@@ -85,7 +85,9 @@ RSpec.describe Tag, type: :model do
['伊地知ニジカ[1:00-]'], ['伊地知ニジカ[1:00-]'],
with_sections: true) with_sections: true)
tag = result.fetch(:tags).find { _1.name == '伊地知ニジカ' } tag = result.fetch(:tags).find do |candidate|
primary_tag_name_for(candidate, 'ja').name == '伊地知ニジカ'
end
expect(result.fetch(:sections).fetch(tag.id)).to eq([[60_000, nil]]) expect(result.fetch(:sections).fetch(tag.id)).to eq([[60_000, nil]])
end end
@@ -94,7 +96,9 @@ RSpec.describe Tag, type: :model do
['伊地知ニジカ[-1:00]'], ['伊地知ニジカ[-1:00]'],
with_sections: true) with_sections: true)
tag = result.fetch(:tags).find { _1.name == '伊地知ニジカ' } tag = result.fetch(:tags).find do |candidate|
primary_tag_name_for(candidate, 'ja').name == '伊地知ニジカ'
end
expect(result.fetch(:sections).fetch(tag.id)).to eq([[0, 60_000]]) expect(result.fetch(:sections).fetch(tag.id)).to eq([[0, 60_000]])
end end
@@ -103,7 +107,9 @@ RSpec.describe Tag, type: :model do
['伊地知ニジカ[-]'], ['伊地知ニジカ[-]'],
with_sections: true) with_sections: true)
tag = result.fetch(:tags).find { _1.name == '伊地知ニジカ' } tag = result.fetch(:tags).find do |candidate|
primary_tag_name_for(candidate, 'ja').name == '伊地知ニジカ'
end
expect(result.fetch(:sections)[tag.id]).to be_nil expect(result.fetch(:sections)[tag.id]).to be_nil
end end
@@ -112,7 +118,9 @@ RSpec.describe Tag, type: :model do
['伊地知ニジカ[0:00-]'], ['伊地知ニジカ[0:00-]'],
with_sections: true) with_sections: true)
tag = result.fetch(:tags).find { _1.name == '伊地知ニジカ' } tag = result.fetch(:tags).find do |candidate|
primary_tag_name_for(candidate, 'ja').name == '伊地知ニジカ'
end
expect(result.fetch(:sections)[tag.id]).to be_nil expect(result.fetch(:sections)[tag.id]).to be_nil
end end
@@ -121,7 +129,9 @@ RSpec.describe Tag, type: :model do
['伊地知ニジカ[1:00-1:00]'], ['伊地知ニジカ[1:00-1:00]'],
with_sections: true) with_sections: true)
tag = result.fetch(:tags).find { _1.name == '伊地知ニジカ' } tag = result.fetch(:tags).find do |candidate|
primary_tag_name_for(candidate, 'ja').name == '伊地知ニジカ'
end
expect(result.fetch(:sections).fetch(tag.id)).to eq([[60_000, 60_001]]) expect(result.fetch(:sections).fetch(tag.id)).to eq([[60_000, 60_001]])
end end
@@ -130,7 +140,9 @@ RSpec.describe Tag, type: :model do
['伊地知ニジカ[2:00-1:00]'], ['伊地知ニジカ[2:00-1:00]'],
with_sections: true) with_sections: true)
tag = result.fetch(:tags).find { _1.name == '伊地知ニジカ' } tag = result.fetch(:tags).find do |candidate|
primary_tag_name_for(candidate, 'ja').name == '伊地知ニジカ'
end
expect(result.fetch(:sections).fetch(tag.id)).to eq([[60_000, 120_000]]) expect(result.fetch(:sections).fetch(tag.id)).to eq([[60_000, 120_000]])
end end
@@ -139,7 +151,9 @@ RSpec.describe Tag, type: :model do
['伊地知ニジカ[1:00-][2:00-3:00]'], ['伊地知ニジカ[1:00-][2:00-3:00]'],
with_sections: true) with_sections: true)
tag = result.fetch(:tags).find { _1.name == '伊地知ニジカ' } tag = result.fetch(:tags).find do |candidate|
primary_tag_name_for(candidate, 'ja').name == '伊地知ニジカ'
end
expect(result.fetch(:sections).fetch(tag.id)).to eq([[60_000, nil]]) expect(result.fetch(:sections).fetch(tag.id)).to eq([[60_000, nil]])
end end
@@ -148,25 +162,23 @@ RSpec.describe Tag, type: :model do
['伊地知ニジカ[1:00-3:00][3:00-]'], ['伊地知ニジカ[1:00-3:00][3:00-]'],
with_sections: true) with_sections: true)
tag = result.fetch(:tags).find { _1.name == '伊地知ニジカ' } tag = result.fetch(:tags).find do |candidate|
primary_tag_name_for(candidate, 'ja').name == '伊地知ニジカ'
end
expect(result.fetch(:sections).fetch(tag.id)).to eq([[60_000, nil]]) expect(result.fetch(:sections).fetch(tag.id)).to eq([[60_000, nil]])
end end
end end
describe '.expand_parent_tags' do describe '.expand_parent_tags' do
it 'expands through multiple deprecated parents to an active ancestor' do it 'expands through multiple deprecated parents to an active ancestor' do
child = create(:tag, name: 'expand_child') child = create(:tag, primary_name: 'expand_child')
deprecated_parent = create( deprecated_parent = create(:tag,
:tag, primary_name: 'expand_deprecated_parent',
name: 'expand_deprecated_parent', deprecated_at: Time.current)
deprecated_at: Time.current deprecated_grandparent = create(:tag,
) primary_name: 'expand_deprecated_grandparent',
deprecated_grandparent = create( deprecated_at: Time.current)
:tag, active_ancestor = create(:tag, primary_name: 'expand_active_ancestor')
name: 'expand_deprecated_grandparent',
deprecated_at: Time.current
)
active_ancestor = create(:tag, name: 'expand_active_ancestor')
TagImplication.create!(tag: child, parent_tag: deprecated_parent) TagImplication.create!(tag: child, parent_tag: deprecated_parent)
TagImplication.create!(tag: deprecated_parent, parent_tag: deprecated_grandparent) TagImplication.create!(tag: deprecated_parent, parent_tag: deprecated_grandparent)
TagImplication.create!(tag: deprecated_grandparent, parent_tag: active_ancestor) TagImplication.create!(tag: deprecated_grandparent, parent_tag: active_ancestor)
@@ -183,8 +195,8 @@ RSpec.describe Tag, type: :model do
end end
it 'terminates when implications contain a cycle' do it 'terminates when implications contain a cycle' do
first = create(:tag, name: 'expand_cycle_first') first = create(:tag, primary_name: 'expand_cycle_first')
second = create(:tag, name: 'expand_cycle_second') second = create(:tag, primary_name: 'expand_cycle_second')
TagImplication.create!(tag: first, parent_tag: second) TagImplication.create!(tag: first, parent_tag: second)
now = Time.current now = Time.current
TagImplication.insert_all!( TagImplication.insert_all!(
@@ -210,7 +222,7 @@ RSpec.describe Tag, type: :model do
tag = described_class.find_or_create_by_tag_name!( tag = described_class.find_or_create_by_tag_name!(
locale, 'english_name', category: :character) locale, 'english_name', category: :character)
expect(tag.tag_name.reload).to have_attributes( expect(primary_tag_name_for(tag, 'en')).to have_attributes(
language_code: 'en', script_code: 'Latn', language_code: 'en', script_code: 'Latn',
primary_flg: true, tag_id: tag.id) primary_flg: true, tag_id: tag.id)
expect(tag.category).to eq('character') expect(tag.category).to eq('character')
@@ -223,8 +235,10 @@ RSpec.describe Tag, type: :model do
locale, 'same_name', category: :character) locale, 'same_name', category: :character)
expect(english).not_to eq(japanese) expect(english).not_to eq(japanese)
expect(english.tag_name.language_code).to eq('en') expect(primary_tag_name_for(english, 'en')).to have_attributes(
expect(japanese.tag_name.language_code).to eq('ja') name: 'same_name', tag_id: english.id, language_code: 'en', primary_flg: true)
expect(primary_tag_name_for(japanese, 'ja')).to have_attributes(
name: 'same_name', tag_id: japanese.id, language_code: 'ja', primary_flg: true)
expect(described_class.find_or_create_by_tag_name!( expect(described_class.find_or_create_by_tag_name!(
locale, 'same_name', category: :general)).to eq(english) locale, 'same_name', category: :general)).to eq(english)
expect(described_class.find_or_create_by_tag_name!( expect(described_class.find_or_create_by_tag_name!(
@@ -232,7 +246,7 @@ RSpec.describe Tag, type: :model do
end end
it 'preserves the V1 representative when looking up another primary language' do it 'preserves the V1 representative when looking up another primary language' do
tag = create(:tag, name: '日本語代表名') tag = create(:tag, primary_name: '日本語代表名')
representative_id = tag.tag_name_id representative_id = tag.tag_name_id
english = create(:tag_name, name: 'english_primary', tag:, english = create(:tag_name, name: 'english_primary', tag:,
language_code: 'en', script_code: 'Latn') language_code: 'en', script_code: 'Latn')
@@ -251,7 +265,7 @@ RSpec.describe Tag, type: :model do
with_tagme: false, with_no_deerjikist: false) with_tagme: false, with_no_deerjikist: false)
expect(tags.length).to eq(1) expect(tags.length).to eq(1)
expect(tags.first.tag_name).to have_attributes( expect(primary_tag_name_for(tags.first, 'en')).to have_attributes(
name: 'normalised_english', language_code: 'en', script_code: 'Latn', name: 'normalised_english', language_code: 'en', script_code: 'Latn',
primary_flg: true, tag_id: tags.first.id) primary_flg: true, tag_id: tags.first.id)
end end
@@ -265,7 +279,7 @@ RSpec.describe Tag, type: :model do
' lookup_new ', category: :character) ' lookup_new ', category: :character)
}.to change(Tag, :count).by(1).and change(TagName, :count).by(1) }.to change(Tag, :count).by(1).and change(TagName, :count).by(1)
expect(tag.name).to eq('lookup_new') expect(primary_tag_name_for(tag, 'ja').name).to eq('lookup_new')
expect(tag.category).to eq('character') expect(tag.category).to eq('character')
end end
@@ -290,7 +304,7 @@ RSpec.describe Tag, type: :model do
it 'reuses the owning tag through another alias' do it 'reuses the owning tag through another alias' do
tag = create(:tag) tag = create(:tag)
primary = tag.tag_name primary = primary_tag_name_for(tag, 'ja')
alias_name = create(:tag_name, :alias, name: 'lookup_alias', tag:) alias_name = create(:tag_name, :alias, name: 'lookup_alias', tag:)
other_alias = create(:tag_name, :alias, name: 'lookup_other_alias', tag:) other_alias = create(:tag_name, :alias, name: 'lookup_other_alias', tag:)
@@ -304,14 +318,14 @@ RSpec.describe Tag, type: :model do
expect(found).to eq(tag) expect(found).to eq(tag)
}.to change(Tag, :count).by(0).and change(TagName, :count).by(0) }.to change(Tag, :count).by(0).and change(TagName, :count).by(0)
expect(tag.reload.tag_name).to eq(primary) expect(primary_tag_name_for(tag.reload, 'ja')).to eq(primary)
end end
end end
describe '.merge_tags!' do describe '.merge_tags!' do
let!(:target_tag) { create(:tag, category: :general) } let!(:target_tag) { create(:tag, category: :general) }
let!(:source_tag) { create(:tag, category: :general) } let!(:source_tag) { create(:tag, category: :general) }
let!(:source_tag_name) { source_tag.tag_name } let!(:source_tag_name) { primary_tag_name_for(source_tag, 'ja') }
let!(:post_record) do let!(:post_record) do
Post.create!(url: 'https://example.com/posts/1', title: 'test post') Post.create!(url: 'https://example.com/posts/1', title: 'test post')
@@ -363,7 +377,7 @@ RSpec.describe Tag, type: :model do
it 'keeps source history and records the new target alias after deleting the source' do it 'keeps source history and records the new target alias after deleting the source' do
user = create_member_user! user = create_member_user!
source_name = source_tag.name source_name = primary_tag_name_for(source_tag, 'ja').name
source_alias = create(:tag_name, :alias, name: 'merge_alias', tag: source_tag) source_alias = create(:tag_name, :alias, name: 'merge_alias', tag: source_tag)
TagVersioning.ensure_snapshot!(source_tag, created_by_user: user) TagVersioning.ensure_snapshot!(source_tag, created_by_user: user)
original_version = source_tag.tag_versions.first original_version = source_tag.tag_versions.first
@@ -380,7 +394,7 @@ RSpec.describe Tag, type: :model do
target_versions = target_tag.tag_versions.order(:version_no) target_versions = target_tag.tag_versions.order(:version_no)
expect(target_versions.pluck(:event_type)).to eq(['create', 'update']) expect(target_versions.pluck(:event_type)).to eq(['create', 'update'])
expect(target_versions.last.aliases.split).to eq([source_name]) expect(target_versions.last.aliases.split).to contain_exactly('merge_alias', source_name)
end end
it 'deletes source relationships while preserving unrelated relationships' do it 'deletes source relationships while preserving unrelated relationships' do
@@ -486,7 +500,7 @@ RSpec.describe Tag, type: :model do
it 'rolls back earlier deletions, links, and history when a later source has a wiki' do it 'rolls back earlier deletions, links, and history when a later source has a wiki' do
earlier_source = create(:tag) earlier_source = create(:tag)
earlier_name = earlier_source.tag_name earlier_name = primary_tag_name_for(earlier_source, 'ja')
source_section = create(:post_tag_section, post: post_record, source_section = create(:post_tag_section, post: post_record,
tag: source_tag, tag: source_tag,
begin_ms: 1000, end_ms: 2000) begin_ms: 1000, end_ms: 2000)
+3 -3
ファイルの表示
@@ -1,7 +1,7 @@
require 'rails_helper' require 'rails_helper'
RSpec.describe VersionRecord, type: :model do RSpec.describe VersionRecord, type: :model do
let!(:tag) { create(:tag, name: 'version_record_tag') } let!(:tag) { create(:tag, primary_name: 'version_record_tag') }
let!(:nico_tag) { create(:external_tag, name: 'version_record_tag') } let!(:nico_tag) { create(:external_tag, name: 'version_record_tag') }
it 'makes TagVersion read only after create' do it 'makes TagVersion read only after create' do
@@ -9,7 +9,7 @@ RSpec.describe VersionRecord, type: :model do
tag: tag, tag: tag,
version_no: 1, version_no: 1,
event_type: 'create', event_type: 'create',
name: tag.name, name: primary_tag_name_for(tag, 'ja').name,
category: tag.category, category: tag.category,
aliases: '', aliases: '',
parent_tag_ids: '', parent_tag_ids: '',
@@ -27,7 +27,7 @@ RSpec.describe VersionRecord, type: :model do
tag: tag, tag: tag,
version_no: 1, version_no: 1,
event_type: 'create', event_type: 'create',
name: tag.name, name: primary_tag_name_for(tag, 'ja').name,
category: tag.category, category: tag.category,
aliases: '', aliases: '',
parent_tag_ids: '', parent_tag_ids: '',
+1 -1
ファイルの表示
@@ -30,7 +30,7 @@ RSpec.describe 'Deerjikists API', type: :request do
expect(json['tag']).to be_a(Hash) expect(json['tag']).to be_a(Hash)
expect(json['tag']['id']).to eq(tag1.id) expect(json['tag']['id']).to eq(tag1.id)
expect(json['tag']['name']).to eq(tag1.name) expect(json['tag']['name']).to eq(primary_tag_name_for(tag1, 'ja').name)
end end
end end
+1 -1
ファイルの表示
@@ -15,7 +15,7 @@ RSpec.describe 'error responses', type: :request do
it 'returns a stable field-error payload for unprocessable requests' do it 'returns a stable field-error payload for unprocessable requests' do
member = create(:user, :member) member = create(:user, :member)
tag = create(:tag, :general, name: 'error_response_tag') tag = create(:tag, category: :general, primary_name: 'error_response_tag')
sign_in_as(member) sign_in_as(member)
patch "/tags/#{ tag.id }", params: { category: 'nico' } patch "/tags/#{ tag.id }", params: { category: 'nico' }
+4 -4
ファイルの表示
@@ -898,22 +898,22 @@ RSpec.describe 'Gekanator learning API', type: :request do
describe 'GET /gekanator/questions' do describe 'GET /gekanator/questions' do
it 'omits questions for deprecated tags' do it 'omits questions for deprecated tags' do
active_tag = create(:tag, name: 'active_question_tag', category: :general) active_tag = create(:tag, primary_name: 'active_question_tag', category: :general)
deprecated_tag = create(:tag, deprecated_tag = create(:tag,
name: 'deprecated_question_tag', primary_name: 'deprecated_question_tag',
category: :general, category: :general,
deprecated_at: Time.current) deprecated_at: Time.current)
[active_tag, deprecated_tag].each do |question_tag| [active_tag, deprecated_tag].each do |question_tag|
GekanatorQuestion.create!( GekanatorQuestion.create!(
text: "#{ question_tag.name }?", text: "#{ primary_tag_name_for(question_tag, 'ja').name }?",
kind: 'tag', kind: 'tag',
source: 'admin_curated', source: 'admin_curated',
status: 'accepted', status: 'accepted',
priority_weight: 1.0, priority_weight: 1.0,
condition: { condition: {
type: 'tag', type: 'tag',
key: "#{ question_tag.category }:#{ question_tag.name }" key: "#{ question_tag.category }:#{ primary_tag_name_for(question_tag, 'ja').name }"
}, },
created_by: admin created_by: admin
) )
+2 -2
ファイルの表示
@@ -4,9 +4,9 @@ require 'rails_helper'
RSpec.describe 'Gekanator posts API', type: :request do RSpec.describe 'Gekanator posts API', type: :request do
describe 'GET /gekanator/posts' do describe 'GET /gekanator/posts' do
it 'omits deprecated tags and returns the stored similarity cosine' do it 'omits deprecated tags and returns the stored similarity cosine' do
active_tag = create(:tag, name: 'active tag', category: :general) active_tag = create(:tag, primary_name: 'active tag', category: :general)
deprecated_tag = create(:tag, deprecated_tag = create(:tag,
name: 'deprecated tag', primary_name: 'deprecated tag',
category: :general, category: :general,
deprecated_at: Time.current) deprecated_at: Time.current)
post_record = Post.create!(title: 'source', url: 'https://example.com/source') post_record = Post.create!(title: 'source', url: 'https://example.com/source')
+15 -12
ファイルの表示
@@ -96,7 +96,7 @@ RSpec.describe 'Locale propagation on write paths', type: :request do
tag = create(:tag) tag = create(:tag)
put "/tags/#{ tag.id }", params: locale_params.merge( put "/tags/#{ tag.id }", params: locale_params.merge(
name: tag.name, category: tag.category, deprecated: false, name: primary_tag_name_for(tag, 'ja').name, category: tag.category, deprecated: false,
aliases: '', parent_tags: 'locale_parent') aliases: '', parent_tags: 'locale_parent')
expect(response).to have_http_status(:ok), response.body expect(response).to have_http_status(:ok), response.body
@@ -110,11 +110,11 @@ RSpec.describe 'Locale propagation on write paths', type: :request do
let!(:english_name) do let!(:english_name) do
create(:tag_name, name: 'shared_name', language_code: 'en', script_code: 'Latn') create(:tag_name, name: 'shared_name', language_code: 'en', script_code: 'Latn')
end end
let!(:english_tag) { create(:tag, tag_name: english_name, category: :material) } let!(:english_tag) { create(:tag, primary_tag_name: english_name, category: :material) }
let!(:japanese_tag) do let!(:japanese_tag) do
create(:tag, name: 'temporary_japanese', category: :general).tap do |tag| create(:tag, primary_name: 'temporary_japanese', category: :general).tap do |tag|
# Isolate lookup from the separately tested uniqueness validation. # Isolate lookup from the separately tested uniqueness validation.
tag.tag_name.update_columns(name: 'shared_name') primary_tag_name_for(tag, 'ja').update_columns(name: 'shared_name')
end end
end end
@@ -157,7 +157,8 @@ RSpec.describe 'Locale propagation on write paths', type: :request do
child = create(:tag) child = create(:tag)
put "/tags/#{ child.id }", params: { put "/tags/#{ child.id }", params: {
locale: 'en', name: child.name, category: child.category, deprecated: false, locale: 'en', name: primary_tag_name_for(child, 'ja').name,
category: child.category, deprecated: false,
aliases: '', parent_tags: 'shared_name' } aliases: '', parent_tags: 'shared_name' }
expect(response).to have_http_status(:ok), response.body expect(response).to have_http_status(:ok), response.body
@@ -180,23 +181,24 @@ RSpec.describe 'Locale propagation on write paths', type: :request do
let!(:english_name) do let!(:english_name) do
create(:tag_name, name: 'english_original', language_code: 'en', script_code: 'Latn') create(:tag_name, name: 'english_original', language_code: 'en', script_code: 'Latn')
end end
let!(:tag) { create(:tag, tag_name: english_name) } let!(:tag) { create(:tag, primary_tag_name: english_name) }
it 'allows a rename to a name already used in Japanese' do it 'allows a rename to a name already used in Japanese' do
japanese = create(:tag, name: 'rename_target') japanese = create(:tag, primary_name: 'rename_target')
patch "/tags/#{ tag.id }", params: { locale: 'en', name: 'rename_target' } patch "/tags/#{ tag.id }", params: { locale: 'en', name: 'rename_target' }
expect(response).to have_http_status(:ok), response.body expect(response).to have_http_status(:ok), response.body
expect(tag.reload.tag_name).to have_attributes( expect(primary_tag_name_for(tag.reload, 'en')).to have_attributes(
name: 'rename_target', language_code: 'en', tag_id: tag.id) name: 'rename_target', language_code: 'en', tag_id: tag.id)
expect(japanese.reload.tag_name).to have_attributes( expect(primary_tag_name_for(japanese.reload, 'ja')).to have_attributes(
name: 'rename_target', language_code: 'ja', tag_id: japanese.id) name: 'rename_target', language_code: 'ja', tag_id: japanese.id)
end end
it 'creates aliases in the requested language' do it 'creates aliases in the requested language' do
put "/tags/#{ tag.id }", params: { put "/tags/#{ tag.id }", params: {
locale: 'en', name: tag.name, category: tag.category, deprecated: false, locale: 'en', name: english_name.name,
category: tag.category, deprecated: false,
aliases: 'english_alias', parent_tags: '' } aliases: 'english_alias', parent_tags: '' }
expect(response).to have_http_status(:ok), response.body expect(response).to have_http_status(:ok), response.body
@@ -210,7 +212,7 @@ RSpec.describe 'Locale propagation on write paths', type: :request do
end end
it 'does not take an alias from another language' do it 'does not take an alias from another language' do
japanese = create(:tag, name: 'japanese_owner') japanese = create(:tag, primary_name: 'japanese_owner')
japanese_alias = create(:tag_name, :alias, name: 'shared_alias', japanese_alias = create(:tag_name, :alias, name: 'shared_alias',
tag: japanese) tag: japanese)
expect(japanese_alias.reload).to have_attributes( expect(japanese_alias.reload).to have_attributes(
@@ -218,7 +220,8 @@ RSpec.describe 'Locale propagation on write paths', type: :request do
primary_flg: false, language_code: 'ja') primary_flg: false, language_code: 'ja')
put "/tags/#{ tag.id }", params: { put "/tags/#{ tag.id }", params: {
locale: 'en', name: tag.name, category: tag.category, deprecated: false, locale: 'en', name: english_name.name,
category: tag.category, deprecated: false,
aliases: 'shared_alias', parent_tags: '' } aliases: 'shared_alias', parent_tags: '' }
expect(response).to have_http_status(:ok), response.body expect(response).to have_http_status(:ok), response.body
+24 -22
ファイルの表示
@@ -26,10 +26,10 @@ RSpec.describe 'Materials API', type: :request do
describe 'GET /materials' do describe 'GET /materials' do
let!(:tag_a) do let!(:tag_a) do
create(:tag, name: 'material_index_a', category: :material) create(:tag, primary_name: 'material_index_a', category: :material)
end end
let!(:tag_b) do let!(:tag_b) do
create(:tag, name: 'material_index_b', category: :material) create(:tag, primary_name: 'material_index_b', category: :material)
end end
let!(:material_a) do let!(:material_a) do
@@ -116,20 +116,20 @@ RSpec.describe 'Materials API', type: :request do
it 'filters by descendant tags and returns stable parent tag groups' do it 'filters by descendant tags and returns stable parent tag groups' do
root = root =
create(:tag, name: 'material_scope_root', create(:tag, primary_name: 'material_scope_root',
category: :material) category: :material)
child_b = child_b =
create(:tag, name: 'material_scope_b', create(:tag, primary_name: 'material_scope_b',
category: :material) category: :material)
child_a = child_a =
create(:tag, name: 'material_scope_a', create(:tag, primary_name: 'material_scope_a',
category: :material) category: :material)
deprecated = deprecated =
create(:tag, name: 'material_scope_old', create(:tag, primary_name: 'material_scope_old',
category: :material, category: :material,
deprecated_at: Time.current) deprecated_at: Time.current)
grandchild = grandchild =
create(:tag, name: 'material_scope_grandchild', create(:tag, primary_name: 'material_scope_grandchild',
category: :material) category: :material)
root_material = root_material =
build_material(tag: root, user: member_user, build_material(tag: root, user: member_user,
@@ -179,7 +179,7 @@ RSpec.describe 'Materials API', type: :request do
describe 'GET /materials/:id' do describe 'GET /materials/:id' do
let!(:tag) do let!(:tag) do
create(:tag, name: 'material_show', category: :material) create(:tag, primary_name: 'material_show', category: :material)
end end
let!(:material) do let!(:material) do
build_material(tag:, user: member_user, file: dummy_upload(filename: 'show.png')) build_material(tag:, user: member_user, file: dummy_upload(filename: 'show.png'))
@@ -269,7 +269,7 @@ RSpec.describe 'Materials API', type: :request do
expect(response).to have_http_status(:created) expect(response).to have_http_status(:created)
material = Material.order(:id).last material = Material.order(:id).last
expect(material.tag.name).to eq('material_create_new') expect(primary_tag_name_for(material.tag, 'ja').name).to eq('material_create_new')
expect(material.tag.category).to eq('material') expect(material.tag.category).to eq('material')
expect(material.created_by_user).to eq(member_user) expect(material.created_by_user).to eq(member_user)
expect(material.updated_by_user).to eq(member_user) expect(material.updated_by_user).to eq(member_user)
@@ -297,7 +297,8 @@ RSpec.describe 'Materials API', type: :request do
expect(response).to have_http_status(:created) expect(response).to have_http_status(:created)
tag = Tag.joins(:tag_name).find_by!(tag_names: { name: 'material_create_versioned_tag' }) tag = TagName.find_by!(name: 'material_create_versioned_tag',
language_code: 'ja', primary_flg: true).tag
version = tag.tag_versions.order(:version_no).last version = tag.tag_versions.order(:version_no).last
expect(version.event_type).to eq('create') expect(version.event_type).to eq('create')
@@ -324,7 +325,7 @@ RSpec.describe 'Materials API', type: :request do
it 'returns 422 when the existing tag is not material/character' do it 'returns 422 when the existing tag is not material/character' do
general_tag_name = create(:tag_name, name: 'material_create_general_tag') general_tag_name = create(:tag_name, name: 'material_create_general_tag')
create(:tag, tag_name: general_tag_name, category: :general) create(:tag, primary_tag_name: general_tag_name, category: :general)
post '/materials', params: { post '/materials', params: {
tag: 'material_create_general_tag', tag: 'material_create_general_tag',
@@ -345,7 +346,7 @@ RSpec.describe 'Materials API', type: :request do
expect(response).to have_http_status(:created) expect(response).to have_http_status(:created)
material = Material.order(:id).last material = Material.order(:id).last
expect(material.tag.name).to eq('material_create_url_only') expect(primary_tag_name_for(material.tag, 'ja').name).to eq('material_create_url_only')
expect(material.url).to eq('https://example.com/material-source') expect(material.url).to eq('https://example.com/material-source')
expect(material.file.attached?).to be(false) expect(material.file.attached?).to be(false)
end end
@@ -384,7 +385,7 @@ RSpec.describe 'Materials API', type: :request do
describe 'PUT /materials/:id' do describe 'PUT /materials/:id' do
let!(:tag) do let!(:tag) do
create(:tag, name: 'material_update_old', category: :material) create(:tag, primary_name: 'material_update_old', category: :material)
end end
let!(:material) do let!(:material) do
build_material(tag:, user: member_user, file: dummy_upload(filename: 'old.png')) build_material(tag:, user: member_user, file: dummy_upload(filename: 'old.png'))
@@ -463,7 +464,7 @@ RSpec.describe 'Materials API', type: :request do
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
material.reload material.reload
expect(material.tag.name).to eq('material_update_new') expect(primary_tag_name_for(material.tag, 'ja').name).to eq('material_update_new')
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)
@@ -497,7 +498,8 @@ RSpec.describe 'Materials API', type: :request do
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
tag = Tag.joins(:tag_name).find_by!(tag_names: { name: 'material_update_versioned_tag' }) tag = TagName.find_by!(name: 'material_update_versioned_tag',
language_code: 'ja', primary_flg: true).tag
version = tag.tag_versions.order(:version_no).last version = tag.tag_versions.order(:version_no).last
expect(version.event_type).to eq('create') expect(version.event_type).to eq('create')
@@ -508,7 +510,7 @@ RSpec.describe 'Materials API', type: :request do
it 'backfills a create tag_version for an existing material tag without history' do it 'backfills a create tag_version for an existing material tag without history' do
existing_tag = existing_tag =
create(:tag, name: 'material_update_existing_no_history', create(:tag, primary_name: 'material_update_existing_no_history',
category: :material) category: :material)
expect(existing_tag.tag_versions).to be_empty expect(existing_tag.tag_versions).to be_empty
@@ -531,7 +533,7 @@ RSpec.describe 'Materials API', type: :request do
it 'backfills a create tag_version for an existing character tag without history' do it 'backfills a create tag_version for an existing character tag without history' do
existing_tag = existing_tag =
create(:tag, name: 'material_update_character_no_history', create(:tag, primary_name: 'material_update_character_no_history',
category: :character) category: :character)
expect(existing_tag.tag_versions).to be_empty expect(existing_tag.tag_versions).to be_empty
@@ -563,7 +565,7 @@ RSpec.describe 'Materials API', type: :request do
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
material.reload material.reload
expect(material.tag.name).to eq('material_update_remove_file') expect(primary_tag_name_for(material.tag, 'ja').name).to eq('material_update_remove_file')
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)
expect(material.file.attached?).to be(false) expect(material.file.attached?).to be(false)
@@ -651,8 +653,8 @@ RSpec.describe 'Materials API', type: :request do
end end
describe 'GET /materials/download.zip' do describe 'GET /materials/download.zip' do
let!(:tag_a) { create(:tag, name: 'zip_a', category: :material) } let!(:tag_a) { create(:tag, primary_name: 'zip_a', category: :material) }
let!(:tag_b) { create(:tag, name: 'zip_b', category: :material) } let!(:tag_b) { create(:tag, primary_name: 'zip_b', category: :material) }
let!(:material_a) do let!(:material_a) do
build_material(tag: tag_a, user: member_user, build_material(tag: tag_a, user: member_user,
file: dummy_upload(filename: 'a.png', body: 'zip-a')) file: dummy_upload(filename: 'a.png', body: 'zip-a'))
@@ -701,7 +703,7 @@ RSpec.describe 'Materials API', type: :request do
describe 'GET /materials/versions' do describe 'GET /materials/versions' do
let!(:tag) do let!(:tag) do
create(:tag, name: 'material_history', category: :material) create(:tag, primary_name: 'material_history', category: :material)
end end
let!(:material) do let!(:material) do
build_material(tag:, user: member_user, file: dummy_upload(filename: 'history.png')) build_material(tag:, user: member_user, file: dummy_upload(filename: 'history.png'))
@@ -776,7 +778,7 @@ RSpec.describe 'Materials API', type: :request do
describe 'DELETE /materials/:id' do describe 'DELETE /materials/:id' do
let!(:tag) do let!(:tag) do
create(:tag, name: 'material_destroy', category: :material) create(:tag, primary_name: 'material_destroy', category: :material)
end end
let!(:material) do let!(:material) do
build_material(tag:, user: member_user, file: dummy_upload(filename: 'destroy.png')) build_material(tag:, user: member_user, file: dummy_upload(filename: 'destroy.png'))
+7 -7
ファイルの表示
@@ -44,10 +44,10 @@ RSpec.describe 'NicoTags', type: :request do
unlinked.update!(name: 'search_unlinked') unlinked.update!(name: 'search_unlinked')
other = create(:external_tag) other = create(:external_tag)
other.update!(name: 'other') other.update!(name: 'other')
destination = create(:tag, :general) destination = create(:tag, category: :general)
destination.tag_name.update!(name: 'destination_search') primary_tag_name_for(destination, 'ja').update!(name: 'destination_search')
NicoTagRelation.create!(nico_tag: linked, tag: destination) NicoTagRelation.create!(nico_tag: linked, tag: destination)
NicoTagRelation.create!(nico_tag: other, tag: create(:tag, :general)) NicoTagRelation.create!(nico_tag: other, tag: create(:tag, category: :general))
get '/tags/nico', params: { get '/tags/nico', params: {
name: 'search_', name: 'search_',
@@ -151,7 +151,7 @@ RSpec.describe 'NicoTags', type: :request do
it '404 when only an internal tag exists for the target id' do it '404 when only an internal tag exists for the target id' do
sign_in_as(member) sign_in_as(member)
non_nico = create(:tag, :general) non_nico = create(:tag, category: :general)
expect(ExternalTag.exists?(non_nico.id)).to be(false) expect(ExternalTag.exists?(non_nico.id)).to be(false)
patch "/tags/nico/#{non_nico.id}", params: { tags: 'a b' } patch "/tags/nico/#{non_nico.id}", params: { tags: 'a b' }
expect(response).to have_http_status(:not_found) expect(response).to have_http_status(:not_found)
@@ -163,17 +163,17 @@ RSpec.describe 'NicoTags', type: :request do
nico_tag = create(:external_tag, name: 'nico_tags_spec_source') nico_tag = create(:external_tag, name: 'nico_tags_spec_source')
linked_a_name = create(:tag_name, name: 'nico_linked_a') linked_a_name = create(:tag_name, name: 'nico_linked_a')
linked_a = create(:tag, tag_name: linked_a_name, category: :general) linked_a = create(:tag, primary_tag_name: linked_a_name, category: :general)
linked_b_name = create(:tag_name, name: 'nico_linked_b') linked_b_name = create(:tag_name, name: 'nico_linked_b')
linked_b = create(:tag, tag_name: linked_b_name, category: :general) linked_b = create(:tag, primary_tag_name: linked_b_name, category: :general)
NicoTagVersionRecorder.record!(external_tag: nico_tag, NicoTagVersionRecorder.record!(external_tag: nico_tag,
event_type: :create, created_by_user: admin) event_type: :create, created_by_user: admin)
expect { expect {
patch "/tags/nico/#{nico_tag.id}", params: { patch "/tags/nico/#{nico_tag.id}", params: {
tags: " #{linked_a.name}\n#{linked_b.name} " tags: " #{ linked_a_name.name }\n#{ linked_b_name.name } "
} }
}.to change(TagVersion, :count).by(2) }.to change(TagVersion, :count).by(2)
.and change(NicoTagVersion, :count).by(1) .and change(NicoTagVersion, :count).by(1)
+65 -46
ファイルの表示
@@ -114,7 +114,7 @@ RSpec.describe 'Posts API', type: :request do
end end
let!(:tag_name) { create(:tag_name, name: 'spec_tag') } let!(:tag_name) { create(:tag_name, name: 'spec_tag') }
let!(:tag) { create(:tag, tag_name: tag_name, category: :general) } let!(:tag) { create(:tag, primary_tag_name: tag_name, category: :general) }
let!(:post_record) do let!(:post_record) do
Post.create!(title: 'spec post', url: 'https://example.com/spec').tap do |p| Post.create!(title: 'spec post', url: 'https://example.com/spec').tap do |p|
@@ -126,9 +126,9 @@ RSpec.describe 'Posts API', type: :request do
let!(:user) { create_member_user! } let!(:user) { create_member_user! }
let!(:tag_name) { create(:tag_name, name: 'spec_tag') } let!(:tag_name) { create(:tag_name, name: 'spec_tag') }
let!(:tag) { create(:tag, tag_name:, category: :general) } let!(:tag) { create(:tag, primary_tag_name: tag_name, category: :general) }
let!(:tag_name2) { create(:tag_name, name: 'unko') } let!(:tag_name2) { create(:tag_name, name: 'unko') }
let!(:tag2) { create(:tag, tag_name: tag_name2, category: :deerjikist) } let!(:tag2) { create(:tag, primary_tag_name: tag_name2, category: :deerjikist) }
let!(:alias_tag_name) { create(:tag_name, :alias, name: 'manko', tag:) } let!(:alias_tag_name) { create(:tag_name, :alias, name: 'manko', tag:) }
let!(:hit_post) do let!(:hit_post) do
@@ -267,7 +267,7 @@ RSpec.describe 'Posts API', type: :request do
end end
it 'keeps internal name searches independent of colliding external ids' do it 'keeps internal name searches independent of colliding external ids' do
get '/posts', params: { tags: tag.name } get '/posts', params: { tags: primary_tag_name_for(tag, 'ja').name }
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
expect(json.fetch('count')).to eq(3) expect(json.fetch('count')).to eq(3)
@@ -286,7 +286,7 @@ RSpec.describe 'Posts API', type: :request do
[nil, 'all'].each do |match| [nil, 'all'].each do |match|
it "intersects internal and external matches with match=#{ match || 'omitted' }" do it "intersects internal and external matches with match=#{ match || 'omitted' }" do
params = { tags: "#{ tag.name } nico:search_external" } params = { tags: "#{ primary_tag_name_for(tag, 'ja').name } nico:search_external" }
params[:match] = match if match params[:match] = match if match
get '/posts', params: params get '/posts', params: params
@@ -307,7 +307,8 @@ RSpec.describe 'Posts API', type: :request do
end end
it 'excludes external matches from an internal tag search' do it 'excludes external matches from an internal tag search' do
get '/posts', params: { tags: "#{ tag.name } not:nico:search_external" } get '/posts', params: {
tags: "#{ primary_tag_name_for(tag, 'ja').name } not:nico:search_external" }
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
expect(json.fetch('count')).to eq(2) expect(json.fetch('count')).to eq(2)
@@ -316,7 +317,9 @@ RSpec.describe 'Posts API', type: :request do
end end
it 'unions an external match with a negated internal match' do it 'unions an external match with a negated internal match' do
get '/posts', params: { tags: "nico:search_external not:#{ tag.name }", match: 'any' } get '/posts', params: {
tags: "nico:search_external not:#{ primary_tag_name_for(tag, 'ja').name }",
match: 'any' }
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
expect(json.fetch('count')).to eq(2) expect(json.fetch('count')).to eq(2)
@@ -333,7 +336,9 @@ RSpec.describe 'Posts API', type: :request do
end end
it 'applies the same mixed name search to the existing random endpoint' do it 'applies the same mixed name search to the existing random endpoint' do
get '/posts/random', params: { tags: "#{ tag.name } nico:search_external", match: 'all' } get '/posts/random', params: {
tags: "#{ primary_tag_name_for(tag, 'ja').name } nico:search_external",
match: 'all' }
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
expect(json.fetch('id')).to eq(both_post.id) expect(json.fetch('id')).to eq(both_post.id)
@@ -342,13 +347,13 @@ RSpec.describe 'Posts API', type: :request do
context 'when tags contain not:' do context 'when tags contain not:' do
let!(:foo_tag_name) { create(:tag_name, name: 'not_spec_foo') } let!(:foo_tag_name) { create(:tag_name, name: 'not_spec_foo') }
let!(:foo_tag) { create(:tag, tag_name: foo_tag_name, category: :general) } let!(:foo_tag) { create(:tag, primary_tag_name: foo_tag_name, category: :general) }
let!(:bar_tag_name) { create(:tag_name, name: 'not_spec_bar') } let!(:bar_tag_name) { create(:tag_name, name: 'not_spec_bar') }
let!(:bar_tag) { create(:tag, tag_name: bar_tag_name, category: :general) } let!(:bar_tag) { create(:tag, primary_tag_name: bar_tag_name, category: :general) }
let!(:baz_tag_name) { create(:tag_name, name: 'not_spec_baz') } let!(:baz_tag_name) { create(:tag_name, name: 'not_spec_baz') }
let!(:baz_tag) { create(:tag, tag_name: baz_tag_name, category: :general) } let!(:baz_tag) { create(:tag, primary_tag_name: baz_tag_name, category: :general) }
let!(:foo_alias_tag_name) do let!(:foo_alias_tag_name) do
create(:tag_name, :alias, name: 'not_spec_foo_alias', tag: foo_tag) create(:tag_name, :alias, name: 'not_spec_foo_alias', tag: foo_tag)
@@ -739,7 +744,9 @@ RSpec.describe 'Posts API', type: :request do
.find { _1['name'] == 'nico:post_index_external' } .find { _1['name'] == 'nico:post_index_external' }
expect(post_json.fetch('tags')).to include( expect(post_json.fetch('tags')).to include(
a_hash_including('id' => tag.id, 'name' => tag.name, 'category' => 'general')) a_hash_including('id' => tag.id,
'name' => primary_tag_name_for(tag, 'ja').name,
'category' => 'general'))
expect(external_json).not_to be_nil expect(external_json).not_to be_nil
expect_external_tag_json(external_json, external_tag) expect_external_tag_json(external_json, external_tag)
end end
@@ -773,7 +780,7 @@ RSpec.describe 'Posts API', type: :request do
it 'omits deprecated tags' do it 'omits deprecated tags' do
deprecated_tag = create(:tag, deprecated_tag = create(:tag,
name: 'deprecated_post_tag', primary_name: 'deprecated_post_tag',
category: :general, category: :general,
deprecated_at: Time.current) deprecated_at: Time.current)
PostTag.create!(post: post_record, tag: deprecated_tag) PostTag.create!(post: post_record, tag: deprecated_tag)
@@ -853,7 +860,7 @@ RSpec.describe 'Posts API', type: :request do
tags = tags =
15.times.map do |i| 15.times.map do |i|
tag_name = create(:tag_name, name: "show_query_tag_#{ i }") tag_name = create(:tag_name, name: "show_query_tag_#{ i }")
tag = create(:tag, tag_name:, category: :general) tag = create(:tag, primary_tag_name: tag_name, category: :general)
create(:tag_name, :alias, name: "show_query_alias_#{ i }", tag:) create(:tag_name, :alias, name: "show_query_alias_#{ i }", tag:)
PostTag.create!(post: post_record, tag:) PostTag.create!(post: post_record, tag:)
tag tag
@@ -909,7 +916,9 @@ RSpec.describe 'Posts API', type: :request do
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
expect(json.fetch('tags')).to include( expect(json.fetch('tags')).to include(
a_hash_including('id' => tag.id, 'name' => tag.name, 'category' => 'general')) a_hash_including('id' => tag.id,
'name' => primary_tag_name_for(tag, 'ja').name,
'category' => 'general'))
external_json = external_json =
json json
.fetch('tags') .fetch('tags')
@@ -1157,7 +1166,7 @@ RSpec.describe 'Posts API', type: :request do
it 'rejects a deprecated tag specified directly' do it 'rejects a deprecated tag specified directly' do
create(:tag, create(:tag,
name: 'deprecated_direct_tag', primary_name: 'deprecated_direct_tag',
category: :general, category: :general,
deprecated_at: Time.current) deprecated_at: Time.current)
sign_in_as(member) sign_in_as(member)
@@ -1176,16 +1185,16 @@ RSpec.describe 'Posts API', type: :request do
end end
it 'expands through multiple deprecated parent tags and saves active ancestors' do it 'expands through multiple deprecated parent tags and saves active ancestors' do
child = create(:tag, name: 'active_child', category: :general) child = create(:tag, primary_name: 'active_child', category: :general)
deprecated_parent = create(:tag, deprecated_parent = create(:tag,
name: 'deprecated_parent', primary_name: 'deprecated_parent',
category: :general, category: :general,
deprecated_at: Time.current) deprecated_at: Time.current)
deprecated_grandparent = create(:tag, deprecated_grandparent = create(:tag,
name: 'deprecated_grandparent', primary_name: 'deprecated_grandparent',
category: :general, category: :general,
deprecated_at: Time.current) deprecated_at: Time.current)
active_grandparent = create(:tag, name: 'active_grandparent', category: :general) active_grandparent = create(:tag, primary_name: 'active_grandparent', category: :general)
TagImplication.create!(tag: child, parent_tag: deprecated_parent) TagImplication.create!(tag: child, parent_tag: deprecated_parent)
TagImplication.create!(tag: deprecated_parent, parent_tag: deprecated_grandparent) TagImplication.create!(tag: deprecated_parent, parent_tag: deprecated_grandparent)
TagImplication.create!(tag: deprecated_grandparent, parent_tag: active_grandparent) TagImplication.create!(tag: deprecated_grandparent, parent_tag: active_grandparent)
@@ -1199,7 +1208,8 @@ RSpec.describe 'Posts API', type: :request do
) )
expect(response).to have_http_status(:created) expect(response).to have_http_status(:created)
saved_names = Post.find(json.fetch('id')).tags.map(&:name) saved_names = TagName.where(tag_id: Post.find(json.fetch('id')).tags.select(:id),
language_code: 'ja', primary_flg: true).pluck(:name)
expect(saved_names).to include('active_child', 'active_grandparent') expect(saved_names).to include('active_child', 'active_grandparent')
expect(saved_names).not_to include('deprecated_parent', 'deprecated_grandparent') expect(saved_names).not_to include('deprecated_parent', 'deprecated_grandparent')
end end
@@ -1302,7 +1312,8 @@ RSpec.describe 'Posts API', type: :request do
expect(response).to have_http_status(:created) expect(response).to have_http_status(:created)
created_post = Post.find(json.fetch('id')) created_post = Post.find(json.fetch('id'))
tag = Tag.joins(:tag_name).find_by!(tag_names: { name: '伊地知ニジカ' }) tag = TagName.find_by!(name: '伊地知ニジカ',
language_code: 'ja', primary_flg: true).tag
section = PostTagSection.find_by!(post: created_post, tag:) section = PostTagSection.find_by!(post: created_post, tag:)
expect(section.begin_ms).to eq(60_000) expect(section.begin_ms).to eq(60_000)
@@ -1322,7 +1333,8 @@ RSpec.describe 'Posts API', type: :request do
expect(response).to have_http_status(:created) expect(response).to have_http_status(:created)
created_post = Post.find(json.fetch('id')) created_post = Post.find(json.fetch('id'))
tag = Tag.joins(:tag_name).find_by!(tag_names: { name: '伊地知ニジカ' }) tag = TagName.find_by!(name: '伊地知ニジカ',
language_code: 'ja', primary_flg: true).tag
expect(PostTagSection.find_by(post: created_post, tag:)).to be_nil expect(PostTagSection.find_by(post: created_post, tag:)).to be_nil
end end
@@ -1339,7 +1351,8 @@ RSpec.describe 'Posts API', type: :request do
expect(response).to have_http_status(:created) expect(response).to have_http_status(:created)
created_post = Post.find(json.fetch('id')) created_post = Post.find(json.fetch('id'))
tag = Tag.joins(:tag_name).find_by!(tag_names: { name: '伊地知ニジカ' }) tag = TagName.find_by!(name: '伊地知ニジカ',
language_code: 'ja', primary_flg: true).tag
expect(PostTagSection.find_by(post: created_post, tag:)).to be_nil expect(PostTagSection.find_by(post: created_post, tag:)).to be_nil
end end
@@ -1656,7 +1669,7 @@ RSpec.describe 'Posts API', type: :request do
begin_ms: 1000, end_ms: 2000) begin_ms: 1000, end_ms: 2000)
tn2 = create(:tag_name, name: 'spec_tag_2') tn2 = create(:tag_name, name: 'spec_tag_2')
replacement_tag = create(:tag, tag_name: tn2, category: :general) replacement_tag = create(:tag, primary_tag_name: tn2, category: :general)
put "/posts/#{post_record.id}", params: post_update_params( put "/posts/#{post_record.id}", params: post_update_params(
post_record, post_record,
@@ -1705,7 +1718,7 @@ RSpec.describe 'Posts API', type: :request do
it 'rejects a deprecated tag specified directly' do it 'rejects a deprecated tag specified directly' do
create(:tag, create(:tag,
name: 'deprecated_update_tag', primary_name: 'deprecated_update_tag',
category: :general, category: :general,
deprecated_at: Time.current) deprecated_at: Time.current)
sign_in_as(member) sign_in_as(member)
@@ -1962,7 +1975,7 @@ RSpec.describe 'Posts API', type: :request do
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,
title: 'updated by me', title: 'updated by me',
tags: "spec_tag #{Tag.no_deerjikist.name}") tags: "spec_tag #{primary_tag_name_for(Tag.no_deerjikist, 'ja').name}")
expect(response).to have_http_status(:conflict) expect(response).to have_http_status(:conflict)
@@ -1994,7 +2007,8 @@ RSpec.describe 'Posts API', type: :request do
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,
title: post_record.title, title: post_record.title,
tags: "spec_tag #{Tag.no_deerjikist.name} incoming_added_tag") tags: ['spec_tag', primary_tag_name_for(Tag.no_deerjikist, 'ja').name,
'incoming_added_tag'].join(' '))
expect(response).to have_http_status(:conflict) expect(response).to have_http_status(:conflict)
@@ -2024,15 +2038,17 @@ RSpec.describe 'Posts API', type: :request do
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,
title: post_record.title, title: post_record.title,
tags: "spec_tag #{Tag.no_deerjikist.name} incoming_merge_tag", tags: ['spec_tag', primary_tag_name_for(Tag.no_deerjikist, 'ja').name,
'incoming_merge_tag'].join(' '),
merge: '1') merge: '1')
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
names = post_record.reload.tags.map(&:name) names = TagName.where(tag_id: post_record.reload.tags.select(:id),
language_code: 'ja', primary_flg: true).pluck(:name)
expect(names).to include('spec_tag') expect(names).to include('spec_tag')
expect(names).to include(Tag.no_deerjikist.name) expect(names).to include(primary_tag_name_for(Tag.no_deerjikist, 'ja').name)
expect(names).to include('current_merge_tag') expect(names).to include('current_merge_tag')
expect(names).to include('incoming_merge_tag') expect(names).to include('incoming_merge_tag')
end end
@@ -2055,14 +2071,15 @@ RSpec.describe 'Posts API', type: :request do
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,
title: post_record.title, title: post_record.title,
tags: "spec_tag #{ Tag.no_deerjikist.name }") tags: "spec_tag #{ primary_tag_name_for(Tag.no_deerjikist, 'ja').name }")
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
names = post_record.reload.tags.map(&:name) names = TagName.where(tag_id: post_record.reload.tags.select(:id),
language_code: 'ja', primary_flg: true).pluck(:name)
expect(names).to include('spec_tag') expect(names).to include('spec_tag')
expect(names).to include(Tag.no_deerjikist.name) expect(names).to include(primary_tag_name_for(Tag.no_deerjikist, 'ja').name)
expect(post_record.external_tags).to contain_exactly(nico_tag) expect(post_record.external_tags).to contain_exactly(nico_tag)
end end
@@ -2077,14 +2094,15 @@ RSpec.describe 'Posts API', type: :request do
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,
title: 'updated title', title: 'updated title',
tags: "spec_tag #{ Tag.no_deerjikist.name }") tags: "spec_tag #{ primary_tag_name_for(Tag.no_deerjikist, 'ja').name }")
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
names = post_record.reload.tags.map(&:name) names = TagName.where(tag_id: post_record.reload.tags.select(:id),
language_code: 'ja', primary_flg: true).pluck(:name)
expect(names).to include('spec_tag') expect(names).to include('spec_tag')
expect(names).to include(Tag.no_deerjikist.name) expect(names).to include(primary_tag_name_for(Tag.no_deerjikist, 'ja').name)
expect(post_record.external_tags).to contain_exactly(nico_tag) expect(post_record.external_tags).to contain_exactly(nico_tag)
external_json = external_json =
@@ -2112,16 +2130,17 @@ RSpec.describe 'Posts API', type: :request do
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,
title: post_record.title, title: post_record.title,
tags: "spec_tag #{ Tag.no_deerjikist.name }") tags: "spec_tag #{ primary_tag_name_for(Tag.no_deerjikist, 'ja').name }")
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
names = post_record.reload.tags.map(&:name) names = TagName.where(tag_id: post_record.reload.tags.select(:id),
language_code: 'ja', primary_flg: true).pluck(:name)
expect(post_record.external_tags).to contain_exactly(nico_tag) expect(post_record.external_tags).to contain_exactly(nico_tag)
expect(names).to include('spec_tag') expect(names).to include('spec_tag')
expect(names).to include(Tag.no_deerjikist.name) expect(names).to include(primary_tag_name_for(Tag.no_deerjikist, 'ja').name)
expect(names).not_to include(linked_tag.name) expect(names).not_to include(primary_tag_name_for(linked_tag, 'ja').name)
end end
it 'force-updates stale posts without base_version_no' do it 'force-updates stale posts without base_version_no' do
@@ -2137,7 +2156,7 @@ RSpec.describe 'Posts API', type: :request do
put "/posts/#{post_record.id}", params: post_write_params( put "/posts/#{post_record.id}", params: post_write_params(
title: 'forced title', title: 'forced title',
tags: "spec_tag #{Tag.no_deerjikist.name}", tags: "spec_tag #{primary_tag_name_for(Tag.no_deerjikist, 'ja').name}",
force: '1') force: '1')
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
@@ -2191,7 +2210,7 @@ RSpec.describe 'Posts API', type: :request do
let(:oc_before) { Time.zone.local(2020, 1, 1, 0, 0, 0) } let(:oc_before) { Time.zone.local(2020, 1, 1, 0, 0, 0) }
let!(:tag_name2) { create(:tag_name, name: 'spec_tag_2') } let!(:tag_name2) { create(:tag_name, name: 'spec_tag_2') }
let!(:tag2) { create(:tag, tag_name: tag_name2, category: :general) } let!(:tag2) { create(:tag, primary_tag_name: tag_name2, category: :general) }
def snapshot_tags(post) def snapshot_tags(post)
post.snapshot_tag_names.join(' ') post.snapshot_tag_names.join(' ')
@@ -2416,7 +2435,7 @@ RSpec.describe 'Posts API', type: :request do
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
expect(json.fetch('count')).to eq(3) expect(json.fetch('count')).to eq(3)
expect(json.fetch('versions').first.fetch('tags')).to include( expect(json.fetch('versions').first.fetch('tags')).to include(
'name' => tag2.name, 'type' => 'context') 'name' => primary_tag_name_for(tag2, 'ja').name, 'type' => 'context')
end end
it 'filters versions by tag when the current snapshot includes the tag' do it 'filters versions by tag when the current snapshot includes the tag' do
@@ -2550,7 +2569,7 @@ RSpec.describe 'Posts API', type: :request do
base_version = create_post_version_for!(post_record) base_version = create_post_version_for!(post_record)
tag_name2 = create(:tag_name, name: 'spec_tag_2') tag_name2 = create(:tag_name, name: 'spec_tag_2')
create(:tag, tag_name: tag_name2, category: :general) create(:tag, primary_tag_name: tag_name2, category: :general)
expect do expect do
put "/posts/#{post_record.id}", params: post_write_params( put "/posts/#{post_record.id}", params: post_write_params(
@@ -2715,7 +2734,7 @@ RSpec.describe 'Posts API', type: :request do
base_version = create_post_version_for!(post_record.reload) base_version = create_post_version_for!(post_record.reload)
tag_name2 = create(:tag_name, name: 'spec_tag_2') tag_name2 = create(:tag_name, name: 'spec_tag_2')
tag2 = create(:tag, tag_name: tag_name2, category: :general) tag2 = create(:tag, primary_tag_name: tag_name2, category: :general)
expect { expect {
put "/posts/#{post_record.id}", params: post_write_params( put "/posts/#{post_record.id}", params: post_write_params(
+8 -8
ファイルの表示
@@ -3,13 +3,13 @@ require 'rails_helper'
RSpec.describe 'TagVersions API', type: :request do RSpec.describe 'TagVersions API', type: :request do
let(:member) { create(:user, :member, name: 'version member') } let(:member) { create(:user, :member, name: 'version member') }
let!(:tag) { create(:tag, name: 'tag_versions_target', category: :general) } let!(:tag) { create(:tag, primary_name: 'tag_versions_target', category: :general) }
let!(:other_tag) { create(:tag, name: 'tag_versions_other', category: :general) } let!(:other_tag) { create(:tag, primary_name: 'tag_versions_other', category: :general) }
let!(:parent_shared) { create(:tag, name: 'parent_shared', category: :general) } let!(:parent_shared) { create(:tag, primary_name: 'parent_shared', category: :general) }
let!(:parent_old) { create(:tag, name: 'parent_old', category: :general) } let!(:parent_old) { create(:tag, primary_name: 'parent_old', category: :general) }
let!(:parent_new) { create(:tag, name: 'parent_new', category: :general) } let!(:parent_new) { create(:tag, primary_name: 'parent_new', category: :general) }
let!(:other_parent) { create(:tag, name: 'other_parent', category: :general) } let!(:other_parent) { create(:tag, primary_name: 'other_parent', category: :general) }
let(:t_v1) { Time.zone.local(2020, 1, 1, 12, 0, 0) } let(:t_v1) { Time.zone.local(2020, 1, 1, 12, 0, 0) }
let(:t_v2) { Time.zone.local(2020, 1, 2, 12, 0, 0) } let(:t_v2) { Time.zone.local(2020, 1, 2, 12, 0, 0) }
@@ -211,7 +211,7 @@ RSpec.describe 'TagVersions API', type: :request do
end end
it 'returns empty when the specified tag has no versions' do it 'returns empty when the specified tag has no versions' do
fresh_tag = create(:tag, name: 'no_versions_tag', category: :general) fresh_tag = create(:tag, primary_name: 'no_versions_tag', category: :general)
get '/tags/versions', params: { id: fresh_tag.id } get '/tags/versions', params: { id: fresh_tag.id }
@@ -233,7 +233,7 @@ RSpec.describe 'TagVersions API', type: :request do
it 'does not create tag versions by wiki updates when tag has no versions yet' do it 'does not create tag versions by wiki updates when tag has no versions yet' do
wiki_tag_name = create(:tag_name, name: 'tag_versions_from_wiki') wiki_tag_name = create(:tag_name, name: 'tag_versions_from_wiki')
wiki_tag = create(:tag, tag_name: wiki_tag_name, category: :general) wiki_tag = create(:tag, primary_tag_name: wiki_tag_name, category: :general)
wiki_page = wiki_page =
Wiki::Commit.create_content!( Wiki::Commit.create_content!(
+6 -6
ファイルの表示
@@ -9,12 +9,12 @@ RSpec.describe 'Tag and wiki history integrity', type: :request do
def create_tag! name:, category: :general def create_tag! name:, category: :general
tag_name = create(:tag_name, name:) tag_name = create(:tag_name, name:)
create(:tag, tag_name:, category:) create(:tag, primary_tag_name: tag_name, category:)
end end
def create_wiki_for_tag! tag:, body: 'wiki body', user: member_user def create_wiki_for_tag! tag:, body: 'wiki body', user: member_user
Wiki::Commit.create_content!( Wiki::Commit.create_content!(
tag_name: tag.tag_name, tag_name: primary_tag_name_for(tag, 'ja'),
body:, body:,
created_by_user: user, created_by_user: user,
message: 'init') message: 'init')
@@ -43,7 +43,7 @@ RSpec.describe 'Tag and wiki history integrity', type: :request do
wiki_page.reload wiki_page.reload
version = wiki_page.wiki_versions.order(:version_no).last version = wiki_page.wiki_versions.order(:version_no).last
expect(tag.name).to eq('patch_tag_wiki_after') expect(primary_tag_name_for(tag, 'ja').name).to eq('patch_tag_wiki_after')
expect(wiki_page.title).to eq('patch_tag_wiki_after') expect(wiki_page.title).to eq('patch_tag_wiki_after')
expect(version).to have_attributes( expect(version).to have_attributes(
@@ -72,7 +72,7 @@ RSpec.describe 'Tag and wiki history integrity', type: :request do
tag.reload tag.reload
wiki_page.reload wiki_page.reload
expect(tag.name).to eq('patch_tag_category_only') expect(primary_tag_name_for(tag, 'ja').name).to eq('patch_tag_category_only')
expect(tag.category).to eq('meme') expect(tag.category).to eq('meme')
expect(wiki_page.wiki_versions.count).to eq(before_wiki_versions) expect(wiki_page.wiki_versions.count).to eq(before_wiki_versions)
end end
@@ -101,7 +101,7 @@ RSpec.describe 'Tag and wiki history integrity', type: :request do
wiki_page.reload wiki_page.reload
version = wiki_page.wiki_versions.order(:version_no).last version = wiki_page.wiki_versions.order(:version_no).last
expect(tag.name).to eq('put_tag_wiki_after') expect(primary_tag_name_for(tag, 'ja').name).to eq('put_tag_wiki_after')
expect(wiki_page.title).to eq('put_tag_wiki_after') expect(wiki_page.title).to eq('put_tag_wiki_after')
expect(version).to have_attributes( expect(version).to have_attributes(
@@ -134,7 +134,7 @@ RSpec.describe 'Tag and wiki history integrity', type: :request do
tag.reload tag.reload
wiki_page.reload wiki_page.reload
expect(tag.name).to eq('put_tag_category_only') expect(primary_tag_name_for(tag, 'ja').name).to eq('put_tag_category_only')
expect(tag.category).to eq('meme') expect(tag.category).to eq('meme')
expect(wiki_page.wiki_versions.count).to eq(before_wiki_versions) expect(wiki_page.wiki_versions.count).to eq(before_wiki_versions)
end end
+2 -2
ファイルの表示
@@ -12,7 +12,7 @@ RSpec.describe 'Tags deerjikists API', type: :request do
let(:guest) { create(:user, role: :guest) } let(:guest) { create(:user, role: :guest) }
before do before do
tag.tag_name.update!(name: 'deerjika') primary_tag_name_for(tag, 'ja').update!(name: 'deerjika')
end end
describe 'GET /tags/:id/deerjikists' do describe 'GET /tags/:id/deerjikists' do
@@ -344,7 +344,7 @@ RSpec.describe 'Tags deerjikists API', type: :request do
end end
before do before do
other_tag.tag_name.update!(name: 'existing-deerjikist') primary_tag_name_for(other_tag, 'ja').update!(name: 'existing-deerjikist')
end end
it 'returns an indexed 422 error and rolls back the complete replacement' do it 'returns an indexed 422 error and rolls back the complete replacement' do
+112 -107
ファイルの表示
@@ -3,12 +3,12 @@ require 'rails_helper'
RSpec.describe 'Tags API', type: :request do RSpec.describe 'Tags API', type: :request do
let!(:tn) { create(:tag_name, name: 'spec_tag') } let!(:tn) { create(:tag_name, name: 'spec_tag') }
let!(:tag) { create(:tag, tag_name: tn, category: :general) } let!(:tag) { create(:tag, primary_tag_name: tn, category: :general) }
let!(:alias_tn) { create(:tag_name, :alias, name: 'unko', tag:) } let!(:alias_tn) { create(:tag_name, :alias, name: 'unko', tag:) }
let!(:post) { Post.create!(url: 'https://example.com/unkounkounko') } let!(:post) { Post.create!(url: 'https://example.com/unkounkounko') }
let!(:post_tag) { PostTag.create!(post:, tag:) } let!(:post_tag) { PostTag.create!(post:, tag:) }
let!(:tn2) { create(:tag_name, name: 'unknown') } let!(:tn2) { create(:tag_name, name: 'unknown') }
let!(:tag2) { create(:tag, tag_name: tn2, category: :general) } let!(:tag2) { create(:tag, primary_tag_name: tn2, category: :general) }
def response_tags def response_tags
json.fetch('tags') json.fetch('tags')
@@ -38,7 +38,9 @@ RSpec.describe 'Tags API', type: :request do
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
expect(json.fetch('count')).to eq(2) expect(json.fetch('count')).to eq(2)
expect(response_tags).to contain_exactly( expect(response_tags).to contain_exactly(
a_hash_including('id' => tag.id, 'name' => tag.name, 'category' => 'general'), a_hash_including('id' => tag.id,
'name' => primary_tag_name_for(tag, 'ja').name,
'category' => 'general'),
a_hash_including( a_hash_including(
'id' => external.id, 'id' => external.id,
'name' => 'nico:spec_external', 'name' => 'nico:spec_external',
@@ -91,7 +93,7 @@ RSpec.describe 'Tags API', type: :request do
end end
it 'filters tags by category' do it 'filters tags by category' do
meme = create(:tag, name: 'meme_only', category: :meme) meme = create(:tag, primary_name: 'meme_only', category: :meme)
get '/tags', params: { category: 'meme' } get '/tags', params: { category: 'meme' }
@@ -102,28 +104,28 @@ RSpec.describe 'Tags API', type: :request do
it 'filters tags by deprecated state' do it 'filters tags by deprecated state' do
deprecated_tag = create(:tag, deprecated_tag = create(:tag,
name: 'deprecated_filter', primary_name: 'deprecated_filter',
category: :general, category: :general,
deprecated_at: 1.day.from_now) deprecated_at: 1.day.from_now)
active_tag = create(:tag, name: 'active_filter', category: :general) active_tag = create(:tag, primary_name: 'active_filter', category: :general)
get '/tags', params: { name: '_filter', deprecated: '1' } get '/tags', params: { name: '_filter', deprecated: '1' }
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
expect(response_names).to include(deprecated_tag.name) expect(response_names).to include(primary_tag_name_for(deprecated_tag, 'ja').name)
expect(response_names).not_to include(active_tag.name) expect(response_names).not_to include(primary_tag_name_for(active_tag, 'ja').name)
get '/tags', params: { name: '_filter', deprecated: '0' } get '/tags', params: { name: '_filter', deprecated: '0' }
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
expect(response_names).to include(active_tag.name) expect(response_names).to include(primary_tag_name_for(active_tag, 'ja').name)
expect(response_names).not_to include(deprecated_tag.name) expect(response_names).not_to include(primary_tag_name_for(deprecated_tag, 'ja').name)
end end
it 'filters tags by post_count range' do it 'filters tags by post_count range' do
low = create(:tag, name: 'pc_low', category: :general) low = create(:tag, primary_name: 'pc_low', category: :general)
mid = create(:tag, name: 'pc_mid', category: :general) mid = create(:tag, primary_name: 'pc_mid', category: :general)
high = create(:tag, name: 'pc_high', category: :general) high = create(:tag, primary_name: 'pc_high', category: :general)
low.update_columns(post_count: 1) low.update_columns(post_count: 1)
mid.update_columns(post_count: 3) mid.update_columns(post_count: 3)
@@ -141,8 +143,8 @@ RSpec.describe 'Tags API', type: :request do
end end
it 'filters tags by created_at range' do it 'filters tags by created_at range' do
old_tag = create(:tag, name: 'created_old', category: :general) old_tag = create(:tag, primary_name: 'created_old', category: :general)
new_tag = create(:tag, name: 'created_new', category: :general) new_tag = create(:tag, primary_name: 'created_new', category: :general)
old_time = Time.zone.local(2024, 1, 1, 0, 0, 0) old_time = Time.zone.local(2024, 1, 1, 0, 0, 0)
new_time = Time.zone.local(2024, 2, 1, 0, 0, 0) new_time = Time.zone.local(2024, 2, 1, 0, 0, 0)
@@ -161,8 +163,8 @@ RSpec.describe 'Tags API', type: :request do
end end
it 'filters tags by updated_at range' do it 'filters tags by updated_at range' do
old_tag = create(:tag, name: 'updated_old', category: :general) old_tag = create(:tag, primary_name: 'updated_old', category: :general)
new_tag = create(:tag, name: 'updated_new', category: :general) new_tag = create(:tag, primary_name: 'updated_new', category: :general)
old_time = Time.zone.local(2024, 3, 1, 0, 0, 0) old_time = Time.zone.local(2024, 3, 1, 0, 0, 0)
new_time = Time.zone.local(2024, 4, 1, 0, 0, 0) new_time = Time.zone.local(2024, 4, 1, 0, 0, 0)
@@ -181,12 +183,12 @@ RSpec.describe 'Tags API', type: :request do
end end
it 'orders tags by custom category order' do it 'orders tags by custom category order' do
create(:tag, name: 'cat_deerjikist', category: :deerjikist) create(:tag, primary_name: 'cat_deerjikist', category: :deerjikist)
create(:tag, name: 'cat_meme', category: :meme) create(:tag, primary_name: 'cat_meme', category: :meme)
create(:tag, name: 'cat_character', category: :character) create(:tag, primary_name: 'cat_character', category: :character)
create(:tag, name: 'cat_general', category: :general) create(:tag, primary_name: 'cat_general', category: :general)
create(:tag, name: 'cat_material', category: :material) create(:tag, primary_name: 'cat_material', category: :material)
create(:tag, name: 'cat_meta', category: :meta) create(:tag, primary_name: 'cat_meta', category: :meta)
create(:external_tag, name: 'cat_nico') create(:external_tag, name: 'cat_nico')
get '/tags', params: { name: 'cat_', order: 'category:asc', limit: 20 } get '/tags', params: { name: 'cat_', order: 'category:asc', limit: 20 }
@@ -201,19 +203,19 @@ RSpec.describe 'Tags API', type: :request do
context 'with mixed legacy pagination' do context 'with mixed legacy pagination' do
let!(:first_tag) do let!(:first_tag) do
create(:tag, create(:tag,
tag_name: create(:tag_name, name: 'a_mixed_page'), primary_tag_name: create(:tag_name, name: 'a_mixed_page'),
category: :meme) category: :meme)
end end
let!(:middle_tag) do let!(:middle_tag) do
create(:tag, create(:tag,
tag_name: create(:tag_name, name: 'm_mixed_page'), primary_tag_name: create(:tag_name, name: 'm_mixed_page'),
category: :meta) category: :meta)
end end
let!(:last_tag) do let!(:last_tag) do
create(:tag, create(:tag,
tag_name: create(:tag_name, name: 'z_mixed_page'), primary_tag_name: create(:tag_name, name: 'z_mixed_page'),
category: :general) category: :general)
end end
@@ -264,7 +266,7 @@ RSpec.describe 'Tags API', type: :request do
it 'paginates and keeps total count' do it 'paginates and keeps total count' do
%w[pag_a pag_b pag_c].each do |name| %w[pag_a pag_b pag_c].each do |name|
create(:tag, tag_name: create(:tag_name, name:), category: :general) create(:tag, primary_tag_name: create(:tag_name, name:), category: :general)
end end
get '/tags', params: { name: 'pag_', order: 'name:asc', page: 2, limit: 2 } get '/tags', params: { name: 'pag_', order: 'name:asc', page: 2, limit: 2 }
@@ -275,8 +277,8 @@ RSpec.describe 'Tags API', type: :request do
end end
it 'falls back to default ordering when order is invalid' do it 'falls back to default ordering when order is invalid' do
low = create(:tag, name: 'fallback_low', category: :general) low = create(:tag, primary_name: 'fallback_low', category: :general)
high = create(:tag, name: 'fallback_high', category: :general) high = create(:tag, primary_name: 'fallback_high', category: :general)
low.update_columns(post_count: 1) low.update_columns(post_count: 1)
high.update_columns(post_count: 9) high.update_columns(post_count: 9)
@@ -289,7 +291,7 @@ RSpec.describe 'Tags API', type: :request do
it 'normalises invalid page and limit' do it 'normalises invalid page and limit' do
%w[norm_a norm_b].each do |name| %w[norm_a norm_b].each do |name|
create(:tag, tag_name: create(:tag_name, name:), category: :general) create(:tag, primary_tag_name: create(:tag_name, name:), category: :general)
end end
get '/tags', params: { name: 'norm_', order: 'name:asc', page: 0, limit: 0 } get '/tags', params: { name: 'norm_', order: 'name:asc', page: 0, limit: 0 }
@@ -302,7 +304,7 @@ RSpec.describe 'Tags API', type: :request do
it 'returns aliases and parent tags' do it 'returns aliases and parent tags' do
parent_tag = create(:tag, parent_tag = create(:tag,
tag_name: create(:tag_name, name: 'index_parent_tag'), primary_tag_name: create(:tag_name, name: 'index_parent_tag'),
category: :meme) category: :meme)
TagImplication.create!(tag:, parent_tag:) TagImplication.create!(tag:, parent_tag:)
@@ -348,7 +350,7 @@ RSpec.describe 'Tags API', type: :request do
it 'returns aliases and parent tags' do it 'returns aliases and parent tags' do
parent_tag = create(:tag, parent_tag = create(:tag,
tag_name: create(:tag_name, name: 'show_parent_tag'), primary_tag_name: create(:tag_name, name: 'show_parent_tag'),
category: :character) category: :character)
TagImplication.create!(tag:, parent_tag:) TagImplication.create!(tag:, parent_tag:)
@@ -395,7 +397,7 @@ RSpec.describe 'Tags API', type: :request do
it 'prefers Tag when Tag and ExternalTag have the same id' do it 'prefers Tag when Tag and ExternalTag have the same id' do
internal = create( internal = create(
:tag, :tag,
tag_name: create(:tag_name, name: 'internal_collision')) primary_tag_name: create(:tag_name, name: 'internal_collision'))
create( create(
:external_tag, :external_tag,
id: internal.id, id: internal.id,
@@ -413,7 +415,7 @@ RSpec.describe 'Tags API', type: :request do
describe 'GET /tags/autocomplete' do describe 'GET /tags/autocomplete' do
it 'combines internal and external matches without conflating equal ids' do it 'combines internal and external matches without conflating equal ids' do
internal = create(:tag, category: :general, name: 'mixed_internal', post_count: 2) internal = create(:tag, category: :general, primary_name: 'mixed_internal', post_count: 2)
external = create(:external_tag, id: internal.id, external = create(:external_tag, id: internal.id,
name: 'mixed_external', post_count: 3) name: 'mixed_external', post_count: 3)
@@ -452,7 +454,7 @@ RSpec.describe 'Tags API', type: :request do
it 'limits the combined results to 20 and sorts ties by displayed name' do it 'limits the combined results to 20 and sorts ties by displayed name' do
11.times do |i| 11.times do |i|
name = "combined_#{ i.to_s.rjust(2, '0') }" name = "combined_#{ i.to_s.rjust(2, '0') }"
create(:tag, category: :general, name:, post_count: 1) create(:tag, category: :general, primary_name: name, post_count: 1)
create(:external_tag, name:, post_count: 1) create(:external_tag, name:, post_count: 1)
end end
@@ -464,8 +466,8 @@ RSpec.describe 'Tags API', type: :request do
end end
it 'excludes external tags but preserves internal alias matches when nico is false' do it 'excludes external tags but preserves internal alias matches when nico is false' do
internal = create(:tag, category: :general, name: 'switch_internal', post_count: 1) internal = create(:tag, category: :general, primary_name: 'switch_internal', post_count: 1)
alias_target = create(:tag, category: :general, name: 'alias_target', post_count: 1) alias_target = create(:tag, category: :general, primary_name: 'alias_target', post_count: 1)
create(:tag_name, :alias, name: 'switch_alias', tag: alias_target) create(:tag_name, :alias, name: 'switch_alias', tag: alias_target)
create(:external_tag, name: 'switch_external', post_count: 1) create(:external_tag, name: 'switch_external', post_count: 1)
@@ -473,27 +475,30 @@ RSpec.describe 'Tags API', type: :request do
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
expect(json).to contain_exactly( expect(json).to contain_exactly(
a_hash_including('id' => internal.id, 'name' => internal.name), a_hash_including('id' => internal.id,
a_hash_including('id' => alias_target.id, 'name' => alias_target.name, 'name' => primary_tag_name_for(internal, 'ja').name),
a_hash_including('id' => alias_target.id,
'name' => primary_tag_name_for(alias_target, 'ja').name,
'matched_alias' => 'switch_alias')) 'matched_alias' => 'switch_alias'))
end end
['%', '_'].each do |wildcard| ['%', '_'].each do |wildcard|
it "treats #{ wildcard } literally for primary, alias, and external names" do it "treats #{ wildcard } literally for primary, alias, and external names" do
literal = "literal#{ wildcard }match" literal = "literal#{ wildcard }match"
create(:tag, category: :general, name: literal, post_count: 1) create(:tag, category: :general, primary_name: literal, post_count: 1)
alias_target = create( alias_target = create(
:tag, category: :general, name: 'literal_alias_target', post_count: 1) :tag, category: :general, primary_name: 'literal_alias_target', post_count: 1)
create(:tag_name, :alias, name: "#{ literal }_alias", create(:tag_name, :alias, name: "#{ literal }_alias",
tag: alias_target) tag: alias_target)
create(:external_tag, name: literal, post_count: 1) create(:external_tag, name: literal, post_count: 1)
create(:tag, category: :general, name: 'literalXmatch', post_count: 2) create(:tag, category: :general, primary_name: 'literalXmatch', post_count: 2)
create(:external_tag, name: 'literalXmatch', post_count: 2) create(:external_tag, name: 'literalXmatch', post_count: 2)
get '/tags/autocomplete', params: { q: "literal#{ wildcard }" } get '/tags/autocomplete', params: { q: "literal#{ wildcard }" }
expect(json.map { |row| row.fetch('name') }) expect(json.map { |row| row.fetch('name') })
.to contain_exactly(literal, alias_target.name, "nico:#{ literal }") .to contain_exactly(
literal, primary_tag_name_for(alias_target, 'ja').name, "nico:#{ literal }")
end end
end end
@@ -523,7 +528,7 @@ RSpec.describe 'Tags API', type: :request do
it 'omits deprecated tags' do it 'omits deprecated tags' do
deprecated_tag = create(:tag, deprecated_tag = create(:tag,
name: 'spec_deprecated', primary_name: 'spec_deprecated',
category: :general, category: :general,
deprecated_at: Time.current) deprecated_at: Time.current)
deprecated_tag.update_columns(post_count: 1) deprecated_tag.update_columns(post_count: 1)
@@ -619,7 +624,7 @@ RSpec.describe 'Tags API', type: :request do
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
tag.reload tag.reload
expect(tag.name).to eq('new') expect(primary_tag_name_for(tag, 'ja').name).to eq('new')
expect(tag.category).to eq('general') expect(tag.category).to eq('general')
body = JSON.parse(response.body) body = JSON.parse(response.body)
@@ -634,7 +639,7 @@ RSpec.describe 'Tags API', type: :request do
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
tag.reload tag.reload
expect(tag.name).to eq('spec_tag') expect(primary_tag_name_for(tag, 'ja').name).to eq('spec_tag')
expect(tag.category).to eq('meme') expect(tag.category).to eq('meme')
end end
@@ -644,7 +649,7 @@ RSpec.describe 'Tags API', type: :request do
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
tag.reload tag.reload
expect(tag.name).to eq('spec_tag') expect(primary_tag_name_for(tag, 'ja').name).to eq('spec_tag')
expect(tag.category).to eq('general') expect(tag.category).to eq('general')
end end
@@ -654,7 +659,7 @@ RSpec.describe 'Tags API', type: :request do
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
tag.reload tag.reload
expect(tag.name).to eq('n') expect(primary_tag_name_for(tag, 'ja').name).to eq('n')
expect(tag.category).to eq('meta') expect(tag.category).to eq('meta')
end end
@@ -667,7 +672,7 @@ RSpec.describe 'Tags API', type: :request do
patch "/tags/#{tag.id}", params: { name: 'new', category: 'nico' } patch "/tags/#{tag.id}", params: { name: 'new', category: 'nico' }
expect(response).to have_http_status(:unprocessable_entity) expect(response).to have_http_status(:unprocessable_entity)
expect(tag.reload.name).to eq('spec_tag') expect(primary_tag_name_for(tag.reload, 'ja').name).to eq('spec_tag')
expect(tag.category).to eq('general') expect(tag.category).to eq('general')
end end
@@ -717,7 +722,7 @@ RSpec.describe 'Tags API', type: :request do
it 'PATCH で tag の name を変更すると対応する wiki version を作成する' do it 'PATCH で tag の name を変更すると対応する wiki version を作成する' do
wiki_page = wiki_page =
Wiki::Commit.create_content!( Wiki::Commit.create_content!(
tag_name: tag.tag_name, tag_name: primary_tag_name_for(tag, 'ja'),
body: 'wiki body before', body: 'wiki body before',
created_by_user: member_user, created_by_user: member_user,
message: 'init') message: 'init')
@@ -745,7 +750,7 @@ RSpec.describe 'Tags API', type: :request do
it 'tag の category だけを変更しても wiki version は作成しない' do it 'tag の category だけを変更しても wiki version は作成しない' do
wiki_page = wiki_page =
Wiki::Commit.create_content!( Wiki::Commit.create_content!(
tag_name: tag.tag_name, tag_name: primary_tag_name_for(tag, 'ja'),
body: 'wiki body before', body: 'wiki body before',
created_by_user: member_user, created_by_user: member_user,
message: 'init') message: 'init')
@@ -781,8 +786,8 @@ RSpec.describe 'Tags API', type: :request do
tag.reload tag.reload
expect(tag.name).to eq('spec_tag') expect(primary_tag_name_for(tag, 'ja').name).to eq('spec_tag')
expect(tag.tag_name).to have_attributes( expect(primary_tag_name_for(tag, 'ja')).to have_attributes(
tag_id: tag.id, language_code: 'ja', primary_flg: true) tag_id: tag.id, language_code: 'ja', primary_flg: true)
expect(TagName.find_by!(language_code: 'ja', name: 'patch_roundtrip_target')) expect(TagName.find_by!(language_code: 'ja', name: 'patch_roundtrip_target'))
.to have_attributes(tag_id: tag.id, primary_flg: false) .to have_attributes(tag_id: tag.id, primary_flg: false)
@@ -791,7 +796,7 @@ RSpec.describe 'Tags API', type: :request do
it '別 tag の正規名には変更できない' do it '別 tag の正規名には変更できない' do
wiki_page = wiki_page =
Wiki::Commit.create_content!( Wiki::Commit.create_content!(
tag_name: tag.tag_name, tag_name: primary_tag_name_for(tag, 'ja'),
body: 'patch collision wiki', body: 'patch collision wiki',
created_by_user: member_user, created_by_user: member_user,
message: 'init') message: 'init')
@@ -803,10 +808,10 @@ RSpec.describe 'Tags API', type: :request do
'name' => ['その名前は既に使はれてゐます.'] 'name' => ['その名前は既に使はれてゐます.']
) )
expect(tag.reload.name).to eq('spec_tag') expect(primary_tag_name_for(tag.reload, 'ja').name).to eq('spec_tag')
expect(TagName.where(tag_id: tag.id, language_code: 'ja', primary_flg: false).pluck(:name)) expect(TagName.where(tag_id: tag.id, language_code: 'ja', primary_flg: false).pluck(:name))
.to contain_exactly('unko') .to contain_exactly('unko')
expect(wiki_page.reload.tag_name).to eq(tag.tag_name) expect(wiki_page.reload.tag_name).to eq(primary_tag_name_for(tag, 'ja'))
end end
it 'system tag の name は変更できない' do it 'system tag の name は変更できない' do
@@ -815,34 +820,34 @@ RSpec.describe 'Tags API', type: :request do
patch "/tags/#{ system_tag.id }", params: { name: 'patch_system_tag_renamed' } patch "/tags/#{ system_tag.id }", params: { name: 'patch_system_tag_renamed' }
expect(response).to have_http_status(:unprocessable_entity) expect(response).to have_http_status(:unprocessable_entity)
expect(system_tag.reload.name).to eq('bot操作') expect(primary_tag_name_for(system_tag.reload, 'ja').name).to eq('bot操作')
end end
end end
end end
describe 'GET /tags/with-depth' do describe 'GET /tags/with-depth' do
let!(:root_meme) do let!(:root_meme) do
create(:tag, name: 'depth_a_root_meme', category: :meme) create(:tag, primary_name: 'depth_a_root_meme', category: :meme)
end end
let!(:root_material) do let!(:root_material) do
create(:tag, name: 'depth_b_root_material', category: :material) create(:tag, primary_name: 'depth_b_root_material', category: :material)
end end
let!(:hidden_general_root) do let!(:hidden_general_root) do
create(:tag, name: 'depth_hidden_general_root', category: :general) create(:tag, primary_name: 'depth_hidden_general_root', category: :general)
end end
let!(:child_character) do let!(:child_character) do
create(:tag, name: 'depth_child_character', category: :character) create(:tag, primary_name: 'depth_child_character', category: :character)
end end
let!(:grandchild_material) do let!(:grandchild_material) do
create(:tag, name: 'depth_grandchild_material', category: :material) create(:tag, primary_name: 'depth_grandchild_material', category: :material)
end end
let!(:child_general) do let!(:child_general) do
create(:tag, name: 'depth_child_general', category: :general) create(:tag, primary_name: 'depth_child_general', category: :general)
end end
before do before do
@@ -886,11 +891,11 @@ RSpec.describe 'Tags API', type: :request do
it 'passes through deprecated tags when finding children' do it 'passes through deprecated tags when finding children' do
deprecated_middle = create(:tag, deprecated_middle = create(:tag,
name: 'depth_deprecated_middle', primary_name: 'depth_deprecated_middle',
category: :character, category: :character,
deprecated_at: Time.current) deprecated_at: Time.current)
visible_descendant = create(:tag, visible_descendant = create(:tag,
name: 'depth_visible_descendant', primary_name: 'depth_visible_descendant',
category: :material) category: :material)
TagImplication.create!(parent_tag: root_material, tag: deprecated_middle) TagImplication.create!(parent_tag: root_material, tag: deprecated_middle)
TagImplication.create!(parent_tag: deprecated_middle, tag: visible_descendant) TagImplication.create!(parent_tag: deprecated_middle, tag: visible_descendant)
@@ -904,18 +909,18 @@ RSpec.describe 'Tags API', type: :request do
it 'passes through multiple deprecated tags for roots and has_children' do it 'passes through multiple deprecated tags for roots and has_children' do
active_child = create(:tag, active_child = create(:tag,
name: 'depth_active_child_below_deprecated', primary_name: 'depth_active_child_below_deprecated',
category: :character) category: :character)
deprecated_parent = create(:tag, deprecated_parent = create(:tag,
name: 'depth_deprecated_parent', primary_name: 'depth_deprecated_parent',
category: :character, category: :character,
deprecated_at: Time.current) deprecated_at: Time.current)
deprecated_grandparent = create(:tag, deprecated_grandparent = create(:tag,
name: 'depth_deprecated_grandparent', primary_name: 'depth_deprecated_grandparent',
category: :material, category: :material,
deprecated_at: Time.current) deprecated_at: Time.current)
active_ancestor = create(:tag, active_ancestor = create(:tag,
name: 'depth_active_ancestor', primary_name: 'depth_active_ancestor',
category: :meme) category: :meme)
TagImplication.create!(tag: active_child, parent_tag: deprecated_parent) TagImplication.create!(tag: active_child, parent_tag: deprecated_parent)
TagImplication.create!(tag: deprecated_parent, parent_tag: deprecated_grandparent) TagImplication.create!(tag: deprecated_parent, parent_tag: deprecated_grandparent)
@@ -942,10 +947,10 @@ RSpec.describe 'Tags API', type: :request do
it 'treats an active tag with only deprecated ancestors as a root' do it 'treats an active tag with only deprecated ancestors as a root' do
active_child = create(:tag, active_child = create(:tag,
name: 'depth_root_below_deprecated', primary_name: 'depth_root_below_deprecated',
category: :character) category: :character)
deprecated_parent = create(:tag, deprecated_parent = create(:tag,
name: 'depth_root_deprecated_parent', primary_name: 'depth_root_deprecated_parent',
category: :material, category: :material,
deprecated_at: Time.current) deprecated_at: Time.current)
TagImplication.create!(tag: active_child, parent_tag: deprecated_parent) TagImplication.create!(tag: active_child, parent_tag: deprecated_parent)
@@ -962,11 +967,11 @@ RSpec.describe 'Tags API', type: :request do
it 'terminates when deprecated implications contain a cycle' do it 'terminates when deprecated implications contain a cycle' do
first = create(:tag, first = create(:tag,
name: 'depth_cycle_first', primary_name: 'depth_cycle_first',
category: :character, category: :character,
deprecated_at: Time.current) deprecated_at: Time.current)
second = create(:tag, second = create(:tag,
name: 'depth_cycle_second', primary_name: 'depth_cycle_second',
category: :material, category: :material,
deprecated_at: Time.current) deprecated_at: Time.current)
TagImplication.create!(tag: first, parent_tag: root_material) TagImplication.create!(tag: first, parent_tag: root_material)
@@ -994,19 +999,19 @@ RSpec.describe 'Tags API', type: :request do
let!(:material_user) { create_member_user! } let!(:material_user) { create_member_user! }
let!(:root_tag) do let!(:root_tag) do
create(:tag, name: 'materials_root', category: :material) create(:tag, primary_name: 'materials_root', category: :material)
end end
let!(:child_a_tag) do let!(:child_a_tag) do
create(:tag, name: 'materials_child_a', category: :material) create(:tag, primary_name: 'materials_child_a', category: :material)
end end
let!(:child_b_tag) do let!(:child_b_tag) do
create(:tag, name: 'materials_child_b', category: :character) create(:tag, primary_name: 'materials_child_b', category: :character)
end end
let!(:grandchild_tag) do let!(:grandchild_tag) do
create(:tag, name: 'materials_grandchild', category: :material) create(:tag, primary_name: 'materials_grandchild', category: :material)
end end
let!(:root_material) do let!(:root_material) do
@@ -1028,7 +1033,7 @@ RSpec.describe 'Tags API', type: :request do
end end
it 'returns a tag tree with nested materials sorted by child name' do it 'returns a tag tree with nested materials sorted by child name' do
get "/tags/name/#{ CGI.escape(root_tag.name) }/materials" get "/tags/name/#{ CGI.escape(primary_tag_name_for(root_tag, 'ja').name) }/materials"
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
@@ -1119,7 +1124,7 @@ RSpec.describe 'Tags API', type: :request do
} }
expect(response).to have_http_status(:unprocessable_entity) expect(response).to have_http_status(:unprocessable_entity)
expect(tag.reload.name).to eq('spec_tag') expect(primary_tag_name_for(tag.reload, 'ja').name).to eq('spec_tag')
end end
it 'category が空なら 422 を返す' do it 'category が空なら 422 を返す' do
@@ -1131,7 +1136,7 @@ RSpec.describe 'Tags API', type: :request do
} }
expect(response).to have_http_status(:unprocessable_entity) expect(response).to have_http_status(:unprocessable_entity)
expect(tag.reload.name).to eq('spec_tag') expect(primary_tag_name_for(tag.reload, 'ja').name).to eq('spec_tag')
expect(tag.category).to eq('general') expect(tag.category).to eq('general')
end end
@@ -1151,10 +1156,10 @@ RSpec.describe 'Tags API', type: :request do
it 'name, category, aliases, parent tags をまとめて更新できる' do it 'name, category, aliases, parent tags をまとめて更新できる' do
old_parent = create(:tag, old_parent = create(:tag,
tag_name: create(:tag_name, name: 'put_old_parent'), primary_tag_name: create(:tag_name, name: 'put_old_parent'),
category: :general) category: :general)
kept_parent = create(:tag, kept_parent = create(:tag,
tag_name: create(:tag_name, name: 'put_kept_parent'), primary_tag_name: create(:tag_name, name: 'put_kept_parent'),
category: :general) category: :general)
TagImplication.create!(tag:, parent_tag: old_parent) TagImplication.create!(tag:, parent_tag: old_parent)
TagImplication.create!(tag:, parent_tag: kept_parent) TagImplication.create!(tag:, parent_tag: kept_parent)
@@ -1171,7 +1176,7 @@ RSpec.describe 'Tags API', type: :request do
tag.reload tag.reload
expect(tag.name).to eq('put_renamed_tag') expect(primary_tag_name_for(tag, 'ja').name).to eq('put_renamed_tag')
expect(tag.category).to eq('meme') expect(tag.category).to eq('meme')
expect(TagName.find_by!(language_code: 'ja', name: 'put_alias_a')) expect(TagName.find_by!(language_code: 'ja', name: 'put_alias_a'))
@@ -1179,17 +1184,16 @@ RSpec.describe 'Tags API', type: :request do
expect(TagName.find_by!(language_code: 'ja', name: 'put_alias_b')) expect(TagName.find_by!(language_code: 'ja', name: 'put_alias_b'))
.to have_attributes(tag_id: tag.id, primary_flg: false) .to have_attributes(tag_id: tag.id, primary_flg: false)
old_name_alias = TagName.find_by(name: 'spec_tag') old_name_alias = TagName.find_by(language_code: 'ja', name: 'spec_tag')
expect(old_name_alias).to be_present expect(old_name_alias).to be_present
expect(old_name_alias).to have_attributes( expect(old_name_alias).to have_attributes(
tag_id: tag.id, language_code: 'ja', primary_flg: false) tag_id: tag.id, language_code: 'ja', primary_flg: false)
expect(alias_tn.reload.tag_id).to be_nil expect(alias_tn.reload.tag_id).to be_nil
expect(tag.parents.map(&:name)).to contain_exactly( expect(TagName.where(tag_id: tag.parents.select(:id),
'put_kept_parent', language_code: 'ja', primary_flg: true).pluck(:name))
'put_new_parent' .to contain_exactly('put_kept_parent', 'put_new_parent')
)
expect(TagImplication.where(tag:, parent_tag: old_parent)).not_to exist expect(TagImplication.where(tag:, parent_tag: old_parent)).not_to exist
@@ -1290,7 +1294,7 @@ RSpec.describe 'Tags API', type: :request do
tag.reload tag.reload
expect(tag.name).to eq('spec_tag') expect(primary_tag_name_for(tag, 'ja').name).to eq('spec_tag')
expect(TagName.find_by!(language_code: 'ja', name: 'put_roundtrip_b')) expect(TagName.find_by!(language_code: 'ja', name: 'put_roundtrip_b'))
.to have_attributes(tag_id: tag.id, primary_flg: false) .to have_attributes(tag_id: tag.id, primary_flg: false)
expect(TagName.where(tag_id: tag.id, language_code: 'ja', primary_flg: false).pluck(:name)) expect(TagName.where(tag_id: tag.id, language_code: 'ja', primary_flg: false).pluck(:name))
@@ -1309,10 +1313,10 @@ RSpec.describe 'Tags API', type: :request do
it 'parent tag の snapshot も作成する' do it 'parent tag の snapshot も作成する' do
old_parent = create(:tag, old_parent = create(:tag,
tag_name: create(:tag_name, name: 'put_snapshot_old_parent'), primary_tag_name: create(:tag_name, name: 'put_snapshot_old_parent'),
category: :general) category: :general)
new_parent = create(:tag, new_parent = create(:tag,
tag_name: create(:tag_name, name: 'put_snapshot_new_parent'), primary_tag_name: create(:tag_name, name: 'put_snapshot_new_parent'),
category: :general) category: :general)
TagImplication.create!(tag:, parent_tag: old_parent) TagImplication.create!(tag:, parent_tag: old_parent)
@@ -1320,7 +1324,7 @@ RSpec.describe 'Tags API', type: :request do
name: 'spec_tag', name: 'spec_tag',
category: 'general', category: 'general',
aliases: 'unko', aliases: 'unko',
parent_tags: new_parent.name, parent_tags: primary_tag_name_for(new_parent, 'ja').name,
deprecated: '0', deprecated: '0',
} }
@@ -1343,13 +1347,13 @@ RSpec.describe 'Tags API', type: :request do
expect(response).to have_http_status(:unprocessable_entity) expect(response).to have_http_status(:unprocessable_entity)
expect(tag.reload.name).to eq('spec_tag') expect(primary_tag_name_for(tag.reload, 'ja').name).to eq('spec_tag')
expect(tag.category).to eq('general') expect(tag.category).to eq('general')
end end
it 'system tag の name は変更できない' do it 'system tag の name は変更できない' do
system_tag = Tag.tagme system_tag = Tag.tagme
old_name = system_tag.name old_name = primary_tag_name_for(system_tag, 'ja').name
old_category = system_tag.category old_category = system_tag.category
expect { expect {
@@ -1364,14 +1368,14 @@ RSpec.describe 'Tags API', type: :request do
expect(response).to have_http_status(:unprocessable_entity) expect(response).to have_http_status(:unprocessable_entity)
expect(system_tag.reload.name).to eq(old_name) expect(primary_tag_name_for(system_tag.reload, 'ja').name).to eq(old_name)
expect(system_tag.category).to eq(old_category) expect(system_tag.category).to eq(old_category)
end end
it 'wiki を持つ tag を更新すると wiki version も作成する' do it 'wiki を持つ tag を更新すると wiki version も作成する' do
wiki_page = wiki_page =
Wiki::Commit.create_content!( Wiki::Commit.create_content!(
tag_name: tag.tag_name, tag_name: primary_tag_name_for(tag, 'ja'),
body: 'wiki body before', body: 'wiki body before',
created_by_user: member_user, created_by_user: member_user,
message: 'init') message: 'init')
@@ -1410,7 +1414,7 @@ RSpec.describe 'Tags API', type: :request do
it 'wiki を持つ tag を旧 alias へ戻しても wiki を新しい正本名へ移す' do it 'wiki を持つ tag を旧 alias へ戻しても wiki を新しい正本名へ移す' do
wiki_page = wiki_page =
Wiki::Commit.create_content!( Wiki::Commit.create_content!(
tag_name: tag.tag_name, tag_name: primary_tag_name_for(tag, 'ja'),
body: 'wiki body before', body: 'wiki body before',
created_by_user: member_user, created_by_user: member_user,
message: 'init') message: 'init')
@@ -1439,8 +1443,9 @@ RSpec.describe 'Tags API', type: :request do
tag.reload tag.reload
expect(wiki_page.reload.tag_name).to eq(tag.tag_name) expect(wiki_page.reload.tag_name).to eq(primary_tag_name_for(tag, 'ja'))
expect(TagName.find_by!(name: 'put_wiki_roundtrip_b').wiki_page).to be_nil expect(TagName.find_by!(language_code: 'ja', name: 'put_wiki_roundtrip_b').wiki_page)
.to be_nil
expect(TagName.find_by!(language_code: 'ja', name: 'put_wiki_roundtrip_b')) expect(TagName.find_by!(language_code: 'ja', name: 'put_wiki_roundtrip_b'))
.to have_attributes(tag_id: tag.id, primary_flg: false) .to have_attributes(tag_id: tag.id, primary_flg: false)
@@ -1452,7 +1457,7 @@ RSpec.describe 'Tags API', type: :request do
it '別名を他 tag から奪った場合、奪はれた側の tag version も作成する' do it '別名を他 tag から奪った場合、奪はれた側の tag version も作成する' do
old_owner = create(:tag, old_owner = create(:tag,
tag_name: create(:tag_name, name: 'put_alias_old_owner'), primary_tag_name: create(:tag_name, name: 'put_alias_old_owner'),
category: :general) category: :general)
stolen_alias = create(:tag_name, :alias, stolen_alias = create(:tag_name, :alias,
name: 'put_stolen_alias', name: 'put_stolen_alias',
@@ -1493,14 +1498,14 @@ RSpec.describe 'Tags API', type: :request do
it '別 tag の alias 名を rename で奪へる' do it '別 tag の alias 名を rename で奪へる' do
old_owner = create(:tag, old_owner = create(:tag,
tag_name: create(:tag_name, name: 'put_alias_collision_owner'), primary_tag_name: create(:tag_name, name: 'put_alias_collision_owner'),
category: :general) category: :general)
stolen_alias = create(:tag_name, :alias, stolen_alias = create(:tag_name, :alias,
name: 'put_alias_collision_name', name: 'put_alias_collision_name',
tag: old_owner) tag: old_owner)
wiki_page = wiki_page =
Wiki::Commit.create_content!( Wiki::Commit.create_content!(
tag_name: tag.tag_name, tag_name: primary_tag_name_for(tag, 'ja'),
body: 'put collision wiki', body: 'put collision wiki',
created_by_user: member_user, created_by_user: member_user,
message: 'init') message: 'init')
@@ -1519,7 +1524,7 @@ RSpec.describe 'Tags API', type: :request do
old_owner.reload old_owner.reload
stolen_alias.reload stolen_alias.reload
expect(tag.name).to eq('put_alias_collision_name') expect(primary_tag_name_for(tag, 'ja').name).to eq('put_alias_collision_name')
expect(stolen_alias).to have_attributes( expect(stolen_alias).to have_attributes(
tag_id: tag.id, language_code: 'ja', primary_flg: true) tag_id: tag.id, language_code: 'ja', primary_flg: true)
expect(TagName.find_by!(language_code: 'ja', name: 'spec_tag')) expect(TagName.find_by!(language_code: 'ja', name: 'spec_tag'))
@@ -1532,12 +1537,12 @@ RSpec.describe 'Tags API', type: :request do
expect(old_owner_versions.last.event_type).to eq('update') expect(old_owner_versions.last.event_type).to eq('update')
expect(old_owner_versions.last.aliases.split).not_to include('put_alias_collision_name') expect(old_owner_versions.last.aliases.split).not_to include('put_alias_collision_name')
expect(wiki_page.reload.tag_name).to eq(tag.tag_name) expect(wiki_page.reload.tag_name).to eq(primary_tag_name_for(tag, 'ja'))
end end
it 'parent_tags に指定すると循環する tag は 422 にする' do it 'parent_tags に指定すると循環する tag は 422 にする' do
child = create(:tag, child = create(:tag,
tag_name: create(:tag_name, name: 'put_cycle_child'), primary_tag_name: create(:tag_name, name: 'put_cycle_child'),
category: :general) category: :general)
TagImplication.create!(tag: child, parent_tag: tag) TagImplication.create!(tag: child, parent_tag: tag)
@@ -1546,7 +1551,7 @@ RSpec.describe 'Tags API', type: :request do
name: 'spec_tag', name: 'spec_tag',
category: 'general', category: 'general',
aliases: 'unko', aliases: 'unko',
parent_tags: child.name, parent_tags: primary_tag_name_for(child, 'ja').name,
deprecated: '0', deprecated: '0',
} }
@@ -1558,7 +1563,7 @@ RSpec.describe 'Tags API', type: :request do
it 'tag の name を変更すると対応する wiki version を作成する' do it 'tag の name を変更すると対応する wiki version を作成する' do
wiki_page = wiki_page =
Wiki::Commit.create_content!( Wiki::Commit.create_content!(
tag_name: tag.tag_name, tag_name: primary_tag_name_for(tag, 'ja'),
body: 'wiki body before', body: 'wiki body before',
created_by_user: member_user, created_by_user: member_user,
message: 'init') message: 'init')
+2 -2
ファイルの表示
@@ -396,7 +396,7 @@ RSpec.describe 'Theatres API', type: :request do
end end
it 'finalizes skip when votes reach majority and stores voters and tag snapshots' do it 'finalizes skip when votes reach majority and stores voters and tag snapshots' do
tag = create(:tag, name: 'skip-target') tag = create(:tag, primary_name: 'skip-target')
PostTag.create!(post: niconico_post, tag:) PostTag.create!(post: niconico_post, tag:)
TheatreSkipVote.create!(theatre:, post: niconico_post, user: member) TheatreSkipVote.create!(theatre:, post: niconico_post, user: member)
@@ -503,7 +503,7 @@ RSpec.describe 'Theatres API', type: :request do
end end
it 'returns tag penalties and candidate weights for the current watchers' do it 'returns tag penalties and candidate weights for the current watchers' do
tag = create(:tag, name: 'heavy-tag') tag = create(:tag, primary_name: 'heavy-tag')
PostTag.create!(post: second_niconico_post, tag:) PostTag.create!(post: second_niconico_post, tag:)
event = TheatreSkipEvent.create!( event = TheatreSkipEvent.create!(
theatre:, theatre:,
+5 -4
ファイルの表示
@@ -20,7 +20,7 @@ RSpec.describe 'Wiki API', type: :request do
end end
let!(:tag) do let!(:tag) do
create(:tag, create(:tag,
tag_name: tn, primary_tag_name: tn,
category: :general, category: :general,
deprecated_at: Time.zone.local(2026, 6, 1)) deprecated_at: Time.zone.local(2026, 6, 1))
end end
@@ -275,7 +275,7 @@ RSpec.describe 'Wiki API', type: :request do
it 'wiki body だけを変更しても tag version は作成しない' do it 'wiki body だけを変更しても tag version は作成しない' do
linked_tag_name = create(:tag_name, name: 'wiki_body_only_tag') linked_tag_name = create(:tag_name, name: 'wiki_body_only_tag')
linked_tag = create(:tag, tag_name: linked_tag_name, category: :general) linked_tag = create(:tag, primary_tag_name: linked_tag_name, category: :general)
TagVersionRecorder.record!( TagVersionRecorder.record!(
tag: linked_tag, tag: linked_tag,
@@ -535,7 +535,7 @@ RSpec.describe 'Wiki API', type: :request do
it 'wiki title を変更すると対応する tag の version を作成する' do it 'wiki title を変更すると対応する tag の version を作成する' do
linked_tag_name = create(:tag_name, name: 'wiki_linked_tag_for_version') linked_tag_name = create(:tag_name, name: 'wiki_linked_tag_for_version')
linked_tag = create(:tag, tag_name: linked_tag_name, category: :general) linked_tag = create(:tag, primary_tag_name: linked_tag_name, category: :general)
linked_page = linked_page =
Wiki::Commit.create_content!( Wiki::Commit.create_content!(
@@ -563,7 +563,8 @@ RSpec.describe 'Wiki API', type: :request do
expect(response).to have_http_status(:ok) expect(response).to have_http_status(:ok)
linked_tag.reload linked_tag.reload
expect(linked_tag.name).to eq('wiki_linked_tag_for_version_renamed') expect(primary_tag_name_for(linked_tag, 'ja').name)
.to eq('wiki_linked_tag_for_version_renamed')
versions = linked_tag.tag_versions.order(:version_no) versions = linked_tag.tag_versions.order(:version_no)
+1 -1
ファイルの表示
@@ -2,7 +2,7 @@ require 'rails_helper'
RSpec.describe MaterialSyncImporter do RSpec.describe MaterialSyncImporter do
let(:user) { create(:user, :member) } let(:user) { create(:user, :member) }
let(:tag) { create(:tag, name: 'sync_tag', category: :material) } let(:tag) { create(:tag, primary_name: 'sync_tag', category: :material) }
def tempfile_for body def tempfile_for body
Tempfile.new(['material-sync-importer', '.png']).tap do |file| Tempfile.new(['material-sync-importer', '.png']).tap do |file|
+1 -1
ファイルの表示
@@ -65,7 +65,7 @@ RSpec.describe NicoTagVersionRecorder do
end end
it 'records sorted linked internal names and later link removal' do it 'records sorted linked internal names and later link removal' do
tags = ['z_link', 'a_link'].map { |name| create(:tag, name:, category: :general) } tags = ['z_link', 'a_link'].map { |name| create(:tag, primary_name: name, category: :general) }
tags.each { |tag| NicoTagRelation.create!(nico_tag: external_tag, tag:) } tags.each { |tag| NicoTagRelation.create!(nico_tag: external_tag, tag:) }
expect(record(:create).linked_tags).to eq('a_link z_link') expect(record(:create).linked_tags).to eq('a_link z_link')
+15 -11
ファイルの表示
@@ -2,7 +2,7 @@ require 'rails_helper'
RSpec.describe PostCreatePlan do RSpec.describe PostCreatePlan do
def create_tag! name, category def create_tag! name, category
create(:tag, name:, category:) create(:tag, primary_name: name, category:)
end end
before do before do
@@ -16,12 +16,15 @@ RSpec.describe PostCreatePlan do
it 'resolves the matching language identity and its parents' do it 'resolves the matching language identity and its parents' do
english_name = create(:tag_name, name: 'shared_name', english_name = create(:tag_name, name: 'shared_name',
language_code: 'en', script_code: 'Latn') language_code: 'en', script_code: 'Latn')
english = create(:tag, tag_name: english_name, category: :character) english = create(:tag, primary_tag_name: english_name, category: :character)
japanese = create(:tag, name: 'temporary_japanese', category: :general) japanese = create(:tag, primary_name: 'temporary_japanese', category: :general)
# The DB permits this identity; validation has its own contract spec. # The DB permits this identity; validation has its own contract spec.
japanese.tag_name.update_columns(name: 'shared_name') primary_tag_name_for(japanese, 'ja').update_columns(name: 'shared_name')
english_parent = create(:tag, name: 'english_parent', category: :material) english_parent_name = create(:tag_name, name: 'english_parent',
japanese_parent = create(:tag, name: 'japanese_parent', category: :general) language_code: 'en', script_code: 'Latn')
english_parent = create(:tag, primary_tag_name: english_parent_name,
category: :material)
japanese_parent = create(:tag, primary_name: 'japanese_parent', category: :general)
TagImplication.create!(tag: english, parent_tag: english_parent) TagImplication.create!(tag: english, parent_tag: english_parent)
TagImplication.create!(tag: japanese, parent_tag: japanese_parent) TagImplication.create!(tag: japanese, parent_tag: japanese_parent)
@@ -30,15 +33,16 @@ RSpec.describe PostCreatePlan do
expect(plan[:direct_tag_specs]).to eq( expect(plan[:direct_tag_specs]).to eq(
[{ name: 'shared_name', category: :character }]) [{ name: 'shared_name', category: :character }])
expect(plan[:post_tag_specs]).to include( expect(plan[:post_tag_specs]).to include(
{ name: english_parent.name, category: :material }) { name: primary_tag_name_for(english_parent, 'en').name, category: :material })
expect(plan[:post_tag_specs].pluck(:name)).not_to include(japanese_parent.name) expect(plan[:post_tag_specs].pluck(:name))
.not_to include(primary_tag_name_for(japanese_parent, 'ja').name)
end end
it 'resolves aliases within the requested language' do it 'resolves aliases within the requested language' do
japanese = create(:tag, name: 'japanese_canonical') japanese = create(:tag, primary_name: 'japanese_canonical')
english_name = create(:tag_name, name: 'english_canonical', english_name = create(:tag_name, name: 'english_canonical',
language_code: 'en', script_code: 'Latn') language_code: 'en', script_code: 'Latn')
english = create(:tag, tag_name: english_name, category: :character) english = create(:tag, primary_tag_name: english_name, category: :character)
japanese_alias = create(:tag_name, :alias, name: 'shared_alias', japanese_alias = create(:tag_name, :alias, name: 'shared_alias',
tag: japanese) tag: japanese)
english_alias = create(:tag_name, :alias, name: 'shared_alias', english_alias = create(:tag_name, :alias, name: 'shared_alias',
@@ -55,7 +59,7 @@ RSpec.describe PostCreatePlan do
plan = described_class.new(attributes: { tags: 'shared_alias' }).build!(locale) plan = described_class.new(attributes: { tags: 'shared_alias' }).build!(locale)
expect(plan[:direct_tag_specs]).to eq( expect(plan[:direct_tag_specs]).to eq(
[{ name: english.name, category: :character }]) [{ name: english_name.name, category: :character }])
end end
end end
+1 -1
ファイルの表示
@@ -64,7 +64,7 @@ RSpec.describe PostImportPreviewer do
end end
it 'applies metadata to automatic fields and recognises metadata tags' do it 'applies metadata to automatic fields and recognises metadata tags' do
create(:tag, name: 'known-tag', category: :general) create(:tag, primary_name: 'known-tag', category: :general)
allow(PostMetadataFetcher).to receive(:fetch).and_return( allow(PostMetadataFetcher).to receive(:fetch).and_return(
title: 'metadata title', title: 'metadata title',
thumbnail_base: 'https://example.com/thumb.jpg', thumbnail_base: 'https://example.com/thumb.jpg',
+1 -1
ファイルの表示
@@ -77,7 +77,7 @@ RSpec.describe Wiki::Commit do
it 'does not record tag_version on body-only wiki update' do it 'does not record tag_version on body-only wiki update' do
tag_name = create(:tag_name, name: 'commit_integrity_linked_tag') tag_name = create(:tag_name, name: 'commit_integrity_linked_tag')
tag = create(:tag, tag_name:, category: :general) tag = create(:tag, primary_tag_name: tag_name, category: :general)
page = page =
described_class.create_content!( described_class.create_content!(
+2 -2
ファイルの表示
@@ -81,7 +81,7 @@ RSpec.describe Wiki::Commit do
it 'does not record tag version when corresponding tag has no versions' do it 'does not record tag version when corresponding tag has no versions' do
tag_name = create(:tag_name, name: 'commit_linked_tag_without_versions') tag_name = create(:tag_name, name: 'commit_linked_tag_without_versions')
tag = create(:tag, tag_name:, category: :general) tag = create(:tag, primary_tag_name: tag_name, category: :general)
page = page =
described_class.create_content!( described_class.create_content!(
@@ -108,7 +108,7 @@ RSpec.describe Wiki::Commit do
it 'does not record tag version when corresponding tag has no versions' do it 'does not record tag version when corresponding tag has no versions' do
tag_name = create(:tag_name, name: 'commit_linked_tag_without_versions') tag_name = create(:tag_name, name: 'commit_linked_tag_without_versions')
tag = create(:tag, tag_name:, category: :general) tag = create(:tag, primary_tag_name: tag_name, category: :general)
page = page =
described_class.create_content!( described_class.create_content!(
+4
ファイルの表示
@@ -1,4 +1,8 @@
module TestRecords module TestRecords
def primary_tag_name_for(tag, language_code)
TagName.find_by!(tag_id: tag.id, language_code:, primary_flg: true)
end
def create_member_user! def create_member_user!
User.create!(name: 'spec user', User.create!(name: 'spec user',
inheritance_code: SecureRandom.hex(16), inheritance_code: SecureRandom.hex(16),
+14 -5
ファイルの表示
@@ -64,7 +64,8 @@ RSpec.describe 'nico:sync' do
run_rake_task('nico:sync') run_rake_task('nico:sync')
post.reload post.reload
active_tag_names = post.tags.joins(:tag_name).pluck('tag_names.name') active_tag_names = TagName.where(tag_id: post.tags.select(:id),
language_code: 'ja', primary_flg: true).pluck(:name)
expect(active_tag_names).to include('spec_kept') expect(active_tag_names).to include('spec_kept')
expect(post.external_tags).to contain_exactly(nico) expect(post.external_tags).to contain_exactly(nico)
@@ -434,7 +435,9 @@ RSpec.describe 'nico:sync' do
external = post.external_tags.sole external = post.external_tags.sole
expect(external.name).to eq('raw tag[]') expect(external.name).to eq('raw tag[]')
expect(post.tags.map(&:name)).not_to include('bot操作') expect(TagName.where(tag_id: post.tags.select(:id),
language_code: 'ja', primary_flg: true).pluck(:name))
.not_to include('bot操作')
expect(post.post_versions.order(:version_no).last.tags_json) expect(post.post_versions.order(:version_no).last.tags_json)
.to include('external_tag_id' => external.id) .to include('external_tag_id' => external.id)
@@ -446,7 +449,9 @@ RSpec.describe 'nico:sync' do
run_nico_sync_with_tags!([]) run_nico_sync_with_tags!([])
}.to change(PostVersion, :count).by(1) }.to change(PostVersion, :count).by(1)
expect(post.reload.external_tags).to be_empty expect(post.reload.external_tags).to be_empty
expect(post.tags.map(&:name)).not_to include('bot操作') expect(TagName.where(tag_id: post.tags.select(:id),
language_code: 'ja', primary_flg: true).pluck(:name))
.not_to include('bot操作')
end end
it 'サニタイズ後の既存外部タグを再利用し、その連携タグを記載する' do it 'サニタイズ後の既存外部タグを再利用し、その連携タグを記載する' do
@@ -563,7 +568,9 @@ RSpec.describe 'nico:sync' do
name: 'AAA') name: 'AAA')
expect(ExternalTag.exists?(platform: :nico, name: 'AAA?')).to be(false) expect(ExternalTag.exists?(platform: :nico, name: 'AAA?')).to be(false)
expect(post.tags.map(&:name)).not_to include('bot操作') expect(TagName.where(tag_id: post.tags.select(:id),
language_code: 'ja', primary_flg: true).pluck(:name))
.not_to include('bot操作')
expect(post.post_versions.order(:version_no).last.tags_json) expect(post.post_versions.order(:version_no).last.tags_json)
.to include('external_tag_id' => external.id) .to include('external_tag_id' => external.id)
@@ -577,7 +584,9 @@ RSpec.describe 'nico:sync' do
}.to change(PostVersion, :count).by(1) }.to change(PostVersion, :count).by(1)
expect(post.reload.external_tags).to be_empty expect(post.reload.external_tags).to be_empty
expect(post.tags.map(&:name)).not_to include('bot操作') expect(TagName.where(tag_id: post.tags.select(:id),
language_code: 'ja', primary_flg: true).pluck(:name))
.not_to include('bot操作')
end end
it 'nico: prefix を含む従来の規則で外部タグ名をサニタイズする' do it 'nico: prefix を含む従来の規則で外部タグ名をサニタイズする' do
+4 -4
ファイルの表示
@@ -6,10 +6,10 @@ RSpec.describe 'post_similarity:calc' do
it 'calculates similarities from active tags only' do it 'calculates similarities from active tags only' do
# 必要最低限のデータ # 必要最低限のデータ
t1 = create(:tag, name: 't1') t1 = create(:tag, primary_name: 't1')
t2 = create(:tag, name: 't2') t2 = create(:tag, primary_name: 't2')
t3 = create(:tag, name: 't3') t3 = create(:tag, primary_name: 't3')
deprecated_tag = create(:tag, name: 'deprecated', deprecated_at: Time.current) deprecated_tag = create(:tag, primary_name: 'deprecated', deprecated_at: Time.current)
p1 = Post.create!(url: "https://example.com/1") p1 = Post.create!(url: "https://example.com/1")
p2 = Post.create!(url: "https://example.com/2") p2 = Post.create!(url: "https://example.com/2")
+4 -4
ファイルの表示
@@ -6,10 +6,10 @@ RSpec.describe 'tag_similarity:calc' do
it 'calculates similarities for active tags only' do it 'calculates similarities for active tags only' do
# 必要最低限のデータ # 必要最低限のデータ
t1 = create(:tag, name: 't1') t1 = create(:tag, primary_name: 't1')
t2 = create(:tag, name: 't2') t2 = create(:tag, primary_name: 't2')
t3 = create(:tag, name: 't3') t3 = create(:tag, primary_name: 't3')
deprecated_tag = create(:tag, name: 'deprecated', deprecated_at: Time.current) deprecated_tag = create(:tag, primary_name: 'deprecated', deprecated_at: Time.current)
p1 = Post.create!(url: "https://example.com/1") p1 = Post.create!(url: "https://example.com/1")
p2 = Post.create!(url: "https://example.com/2") p2 = Post.create!(url: "https://example.com/2")