このコミットが含まれているのは:
@@ -120,17 +120,61 @@ RSpec.describe 'Counts table API', type: :request do
|
||||
expect(versions(query(empty.except('conditions.0.values')))).to eq([1, 2, 3])
|
||||
end
|
||||
|
||||
it 'canonicalises unreferenced tags while preserving partial stable references' do
|
||||
reference = { tag_refs: '', 'tag_refs.0.name' => 'alpha',
|
||||
'tag_refs.0.tag_id' => tag.id.to_s }
|
||||
it 'resolves current tag names for AND, OR and negative searches' do
|
||||
expect(versions(query(tags: 'alpha'))).to eq([1, 2])
|
||||
expect(versions(query(reference.merge(tags: 'alpha nico:beta')))).to eq([1])
|
||||
expect(versions(query(reference.merge(tags: 'nico:beta alpha')))).to eq([1])
|
||||
expect(versions(query(reference.merge(tags: 'alpha not:nico:beta')))).to eq([2])
|
||||
expect(versions(query(reference.merge(tags: 'alpha nico:beta', match: 'any')))).to eq([1, 2])
|
||||
expect(versions(query(tags: 'alpha nico:beta'))).to eq([1])
|
||||
expect(versions(query(tags: 'nico:beta alpha'))).to eq([1])
|
||||
expect(versions(query(tags: 'alpha not:nico:beta'))).to eq([2])
|
||||
expect(versions(query(tags: 'alpha nico:beta', match: 'any'))).to eq([1, 2])
|
||||
expect(versions(query(tags: 'not:alpha'))).to eq([3])
|
||||
get '/counts/query', params: base.merge(reference).merge(tags: 'different')
|
||||
expect(response).to have_http_status(:bad_request)
|
||||
end
|
||||
|
||||
context 'when current tag names differ from recorded snapshot names' do
|
||||
before do
|
||||
tag.tag_names.find_by!(language_code: 'ja', primary_flg: true)
|
||||
.update!(name: 'renamed-alpha')
|
||||
create(:tag_name, :alias, tag:, name: 'alpha-alias')
|
||||
TagVersion.create!(tag:, version_no: 2, event_type: 'update', name: 'renamed-alpha',
|
||||
category: 'general', aliases: 'alpha-alias', parent_tag_ids: '',
|
||||
names: { ja: { name: 'renamed-alpha', script_code: 'Jpan', auto_generated: false,
|
||||
aliases: [{ name: 'alpha-alias', script_code: 'Jpan' }] } },
|
||||
created_at: Time.utc(2024, 2, 1))
|
||||
PostVersion.create!(post: record, version_no: 4, event_type: 'update',
|
||||
url: record.url, title: 'after rename', tags: '', parent_post_ids: '',
|
||||
tags_json: [{ tag_id: tag.id, version_no: 2, name: 'renamed-alpha',
|
||||
category: 'general', sections: [] }],
|
||||
created_by_user: actor, created_at: Time.utc(2024, 2, 2))
|
||||
end
|
||||
|
||||
it 'resolves current names and aliases to stable IDs in both definitions' do
|
||||
{ 'history' => [1, 2, 4], 'latest' => [4] }.each do |definition, expected|
|
||||
['renamed-alpha', 'alpha-alias'].each do |name|
|
||||
expect(versions(query(tags: name, definition:))).to eq(expected)
|
||||
end
|
||||
end
|
||||
result = query(tags: 'renamed-alpha')
|
||||
expect(result['versions'].first['tags'].first['name']).to eq('alpha')
|
||||
expect(versions(query(tags: 'renamed-alpha', definition: 'latest',
|
||||
at: '2024-01-31T16:00:00Z', current: '0'))).to eq([2])
|
||||
expect(versions(query(tags: 'not:alpha-alias'))).to eq([3])
|
||||
expect(versions(query(tags: 'renamed-alpha nico:beta'))).to eq([1])
|
||||
expect(versions(query(tags: 'renamed-alpha nico:beta', match: 'any'))).to eq([1, 2, 4])
|
||||
end
|
||||
|
||||
it 'does not match a historical name reassigned to another tag ID' do
|
||||
create(:tag, primary_name: 'alpha')
|
||||
expect(versions(query(tags: 'alpha'))).to eq([])
|
||||
expect(versions(query(tags: 'unregistered-name'))).to eq([])
|
||||
expect(versions(query(tags: 'not:unregistered-name'))).to eq([1, 2, 3, 4])
|
||||
expect(versions(query(tags: 'renamed-alpha'))).to eq([1, 2, 4])
|
||||
end
|
||||
|
||||
it 'resolves external names from current registrations too' do
|
||||
external.update!(name: 'renamed-beta')
|
||||
expect(versions(query(tags: 'nico:renamed-beta'))).to eq([1])
|
||||
expect(versions(query(tags: 'nico:beta'))).to eq([])
|
||||
expect(versions(query(tags: 'not:nico:renamed-beta'))).to eq([2, 3, 4])
|
||||
end
|
||||
end
|
||||
|
||||
it 'rejects malformed structured conditions and axes without accepting arbitrary SQL' do
|
||||
@@ -149,7 +193,8 @@ RSpec.describe 'Counts table API', type: :request do
|
||||
end
|
||||
|
||||
it 'rejects duplicate URL keys and JSON query payloads at the HTTP boundary' do
|
||||
['resource=posts&limit=20&limit=50', 'resource=posts&headers=%5B%22Id.%22%5D',
|
||||
['resource=posts&tag_refs=', 'resource=posts&tag_refs.0.tag_id=1',
|
||||
'resource=posts&limit=20&limit=50', 'resource=posts&headers=%5B%22Id.%22%5D',
|
||||
'resource=posts&limit%5B%5D=20'].each do |query_string|
|
||||
get "/counts/query?#{ query_string }"
|
||||
expect(response).to have_http_status(:bad_request)
|
||||
|
||||
@@ -11,43 +11,49 @@ RSpec.describe 'Post filter identity across locales', type: :request do
|
||||
let!(:other_post) { create(:post) }
|
||||
|
||||
before do
|
||||
# Legacy data can predate the current prohibition on internal nico: names.
|
||||
primary_tag_name_for(tag, 'ja').update_columns(name: 'nico:shared')
|
||||
primary_tag_name_for(tag, 'ain').update!(name: 'シロシ', auto_generated: false)
|
||||
create(:tag_name, :alias, tag:, name: 'union_alias', language_code: 'ja')
|
||||
PostTag.create!(post: internal_post, tag:)
|
||||
PostExternalTag.create!(post: external_post, external_tag: external)
|
||||
end
|
||||
|
||||
['nico:shared', 'union_alias', 'not:nico:shared'].each do |literal|
|
||||
literals = ['legacy_union_source', 'union_alias', 'not:legacy_union_source', 'nico:shared']
|
||||
literals.each do |literal|
|
||||
it "preserves the complete match set for #{ literal }" do
|
||||
get '/posts', params: { locale: 'ja', tags: literal }
|
||||
expect(response).to have_http_status(:ok)
|
||||
original_ids = json.fetch('posts').map { _1.fetch('id') }
|
||||
expected = literal.start_with?('not:') ? [other_post.id] :
|
||||
[internal_post.id, external_post.id]
|
||||
expected = if literal.start_with?('not:')
|
||||
[external_post.id, other_post.id]
|
||||
elsif literal == 'nico:shared'
|
||||
[external_post.id]
|
||||
else
|
||||
[internal_post.id]
|
||||
end
|
||||
expect(original_ids).to match_array(expected)
|
||||
|
||||
get '/posts/tag_filters', params: {
|
||||
locale: 'ja', target_locale: 'ain-Kana', tags: literal }
|
||||
expect(response).to have_http_status(:ok)
|
||||
references = json.fetch('references')
|
||||
expect(references.first).to include('tag_id' => tag.id, 'external_tag_id' => external.id)
|
||||
|
||||
reference_params = { tag_refs: '' }
|
||||
references.each_with_index do |reference, index|
|
||||
['name', 'tag_id', 'external_tag_id', 'negative', 'missing'].each do |field|
|
||||
value = reference[field]
|
||||
next if value.nil? || (field == 'missing' && value == false)
|
||||
|
||||
reference_params["tag_refs.#{ index }.#{ field }"] =
|
||||
[true, false].include?(value) ? (value ? '1' : '0') : value.to_s
|
||||
end
|
||||
end
|
||||
get '/posts', params: { locale: 'ain-Kana',
|
||||
tags: references.map { _1.fetch('name') }.join(' ') }.merge(reference_params)
|
||||
translated = json.fetch('tags')
|
||||
expect(translated).to eq(literal == 'nico:shared' ? literal :
|
||||
(literal.start_with?('not:') ? 'not:シロシ' : 'シロシ'))
|
||||
get '/posts', params: { locale: 'ain-Kana', tags: translated }
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(json.fetch('posts').map { _1.fetch('id') }).to match_array(original_ids)
|
||||
end
|
||||
end
|
||||
|
||||
it 'rejects a locale conversion that cannot preserve a legacy union' do
|
||||
primary_tag_name_for(tag, 'ja').update_columns(name: 'nico:shared')
|
||||
get '/posts/tag_filters', params: {
|
||||
locale: 'ja', target_locale: 'ain-Kana', tags: 'nico:shared' }
|
||||
expect(response).to have_http_status(:unprocessable_entity)
|
||||
end
|
||||
|
||||
it 'rejects removed reference parameters instead of overriding the name' do
|
||||
get '/posts', params: { locale: 'ja', tags: 'legacy_union_source',
|
||||
tag_refs: '', 'tag_refs.0.tag_id' => external.id.to_s }
|
||||
expect(response).to have_http_status(:bad_request)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -133,7 +133,6 @@ RSpec.describe 'Counts masked pivot contracts' do
|
||||
it 'rejects private predicates and supplied measure coordinates before SQL labels escape' do
|
||||
probes = [{ 'resource_id' => PostVersion.first.post_id.to_s },
|
||||
{ 'tags' => 'A' },
|
||||
{ 'tag_refs' => '', 'tag_refs.0.name' => 'A', 'tag_refs.0.tag_id' => tags.first.id.to_s },
|
||||
{ 'conditions' => '', 'conditions.0.field' => 'title', 'conditions.0.operator' => 'eq',
|
||||
'conditions.0.value' => 'hidden-title-0' },
|
||||
{ 'drill' => '', 'drill.0.field' => 'tags', 'drill.0.value_type' => 'string',
|
||||
|
||||
新しいイシューから参照
ユーザーをブロックする