| @@ -15,7 +15,7 @@ RSpec.describe 'Posts API', type: :request do | |||||
| end | end | ||||
| let!(:tag_name) { TagName.create!(name: 'spec_tag') } | let!(:tag_name) { TagName.create!(name: 'spec_tag') } | ||||
| let!(:tag) { Tag.create!(tag_name: tag_name, category: 'general') } | |||||
| let!(:tag) { Tag.create!(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| | ||||
| @@ -27,9 +27,9 @@ RSpec.describe 'Posts API', type: :request do | |||||
| let!(:user) { create_member_user! } | let!(:user) { create_member_user! } | ||||
| let!(:tag_name) { TagName.create!(name: "spec_tag") } | let!(:tag_name) { TagName.create!(name: "spec_tag") } | ||||
| let!(:tag) { Tag.create!(tag_name:, category: "general") } | |||||
| let!(:tag) { Tag.create!(tag_name:, category: :general) } | |||||
| let!(:tag_name2) { TagName.create!(name: 'unko') } | let!(:tag_name2) { TagName.create!(name: 'unko') } | ||||
| let!(:tag2) { Tag.create!(tag_name: tag_name2, category: 'deerjikist') } | |||||
| let!(:tag2) { Tag.create!(tag_name: tag_name2, category: :deerjikist) } | |||||
| let!(:alias_tag_name) { TagName.create!(name: 'manko', canonical: tag_name) } | let!(:alias_tag_name) { TagName.create!(name: 'manko', canonical: tag_name) } | ||||
| let!(:hit_post) do | let!(:hit_post) do | ||||
| @@ -114,6 +114,123 @@ RSpec.describe 'Posts API', type: :request do | |||||
| expect(json.fetch('count')).to eq(0) | expect(json.fetch('count')).to eq(0) | ||||
| end | end | ||||
| end | end | ||||
| context 'when tags contain not:' do | |||||
| let!(:foo_tag_name) { TagName.create!(name: 'not_spec_foo') } | |||||
| let!(:foo_tag) { Tag.create!(tag_name: foo_tag_name, category: :general) } | |||||
| let!(:bar_tag_name) { TagName.create!(name: 'not_spec_bar') } | |||||
| let!(:bar_tag) { Tag.create!(tag_name: bar_tag_name, category: :general) } | |||||
| let!(:baz_tag_name) { TagName.create!(name: 'not_spec_baz') } | |||||
| let!(:baz_tag) { Tag.create!(tag_name: baz_tag_name, category: :general) } | |||||
| let!(:foo_alias_tag_name) do | |||||
| TagName.create!(name: 'not_spec_foo_alias', canonical: foo_tag_name) | |||||
| end | |||||
| let!(:foo_only_post) do | |||||
| Post.create!(uploaded_user: user, title: 'foo only', | |||||
| url: 'https://example.com/not-spec-foo').tap do |p| | |||||
| PostTag.create!(post: p, tag: foo_tag) | |||||
| end | |||||
| end | |||||
| let!(:bar_only_post) do | |||||
| Post.create!(uploaded_user: user, title: 'bar only', | |||||
| url: 'https://example.com/not-spec-bar').tap do |p| | |||||
| PostTag.create!(post: p, tag: bar_tag) | |||||
| end | |||||
| end | |||||
| let!(:baz_only_post) do | |||||
| Post.create!(uploaded_user: user, title: 'baz only', | |||||
| url: 'https://example.com/not-spec-baz').tap do |p| | |||||
| PostTag.create!(post: p, tag: baz_tag) | |||||
| end | |||||
| end | |||||
| let!(:foo_bar_post) do | |||||
| Post.create!(uploaded_user: user, title: 'foo bar', | |||||
| url: 'https://example.com/not-spec-foo-bar').tap do |p| | |||||
| PostTag.create!(post: p, tag: foo_tag) | |||||
| PostTag.create!(post: p, tag: bar_tag) | |||||
| end | |||||
| end | |||||
| let!(:foo_baz_post) do | |||||
| Post.create!(uploaded_user: user, title: 'foo baz', | |||||
| url: 'https://example.com/not-spec-foo-baz').tap do |p| | |||||
| PostTag.create!(post: p, tag: foo_tag) | |||||
| PostTag.create!(post: p, tag: baz_tag) | |||||
| end | |||||
| end | |||||
| let(:controlled_ids) do | |||||
| [foo_only_post.id, bar_only_post.id, baz_only_post.id, | |||||
| foo_bar_post.id, foo_baz_post.id] | |||||
| end | |||||
| it 'supports not search' do | |||||
| get '/posts', params: { tags: 'not:not_spec_foo' } | |||||
| expect(response).to have_http_status(:ok) | |||||
| expect(json.fetch('posts').map { |p| p['id'] } & controlled_ids).to match_array( | |||||
| [bar_only_post.id, baz_only_post.id] | |||||
| ) | |||||
| end | |||||
| it 'supports alias in not search' do | |||||
| get '/posts', params: { tags: 'not:not_spec_foo_alias' } | |||||
| expect(response).to have_http_status(:ok) | |||||
| expect(json.fetch('posts').map { |p| p['id'] } & controlled_ids).to match_array( | |||||
| [bar_only_post.id, baz_only_post.id] | |||||
| ) | |||||
| end | |||||
| it 'treats multiple not terms as AND when match is omitted' do | |||||
| get '/posts', params: { tags: 'not:not_spec_foo not:not_spec_bar' } | |||||
| expect(response).to have_http_status(:ok) | |||||
| expect(json.fetch('posts').map { |p| p['id'] } & controlled_ids).to match_array( | |||||
| [baz_only_post.id] | |||||
| ) | |||||
| end | |||||
| it 'treats multiple not terms as OR when match=any' do | |||||
| get '/posts', params: { tags: 'not:not_spec_foo not:not_spec_bar', match: 'any' } | |||||
| expect(response).to have_http_status(:ok) | |||||
| expect(json.fetch('posts').map { |p| p['id'] } & controlled_ids).to match_array( | |||||
| [foo_only_post.id, bar_only_post.id, baz_only_post.id, foo_baz_post.id] | |||||
| ) | |||||
| end | |||||
| it 'supports mixed positive and negative search with AND' do | |||||
| get '/posts', params: { tags: 'not_spec_foo not:not_spec_bar' } | |||||
| expect(response).to have_http_status(:ok) | |||||
| expect(json.fetch('posts').map { |p| p['id'] } & controlled_ids).to match_array( | |||||
| [foo_only_post.id, foo_baz_post.id] | |||||
| ) | |||||
| end | |||||
| it 'supports mixed positive and negative search with OR when match=any' do | |||||
| get '/posts', params: { tags: 'not_spec_foo not:not_spec_bar', match: 'any' } | |||||
| expect(response).to have_http_status(:ok) | |||||
| expect(json.fetch('posts').map { |p| p['id'] } & controlled_ids).to match_array( | |||||
| [foo_only_post.id, baz_only_post.id, foo_bar_post.id, foo_baz_post.id] | |||||
| ) | |||||
| end | |||||
| end | |||||
| end | end | ||||
| describe 'GET /posts/:id' do | describe 'GET /posts/:id' do | ||||
| @@ -210,7 +327,7 @@ RSpec.describe 'Posts API', type: :request do | |||||
| context "when nico tag already exists in tags" do | context "when nico tag already exists in tags" do | ||||
| before do | before do | ||||
| Tag.find_or_create_by!(tag_name: TagName.find_or_create_by!(name: 'nico:nico_tag'), | Tag.find_or_create_by!(tag_name: TagName.find_or_create_by!(name: 'nico:nico_tag'), | ||||
| category: 'nico') | |||||
| category: :nico) | |||||
| end | end | ||||
| it 'return 400' do | it 'return 400' do | ||||
| @@ -276,7 +393,7 @@ RSpec.describe 'Posts API', type: :request do | |||||
| # 追加で別タグも作って、更新時に入れ替わることを見る | # 追加で別タグも作って、更新時に入れ替わることを見る | ||||
| tn2 = TagName.create!(name: 'spec_tag_2') | tn2 = TagName.create!(name: 'spec_tag_2') | ||||
| Tag.create!(tag_name: tn2, category: 'general') | |||||
| Tag.create!(tag_name: tn2, category: :general) | |||||
| put "/posts/#{post_record.id}", params: { | put "/posts/#{post_record.id}", params: { | ||||
| title: 'updated title', | title: 'updated title', | ||||
| @@ -295,7 +412,7 @@ RSpec.describe 'Posts API', type: :request do | |||||
| context "when nico tag already exists in tags" do | context "when nico tag already exists in tags" do | ||||
| before do | before do | ||||
| Tag.find_or_create_by!(tag_name: TagName.find_or_create_by!(name: 'nico:nico_tag'), | Tag.find_or_create_by!(tag_name: TagName.find_or_create_by!(name: 'nico:nico_tag'), | ||||
| category: 'nico') | |||||
| category: :nico) | |||||
| end | end | ||||
| it 'return 400' do | it 'return 400' do | ||||
| @@ -332,7 +449,7 @@ RSpec.describe 'Posts API', type: :request do | |||||
| it 'returns add/remove events (history) for a post' do | it 'returns add/remove events (history) for a post' do | ||||
| # add | # add | ||||
| tn2 = TagName.create!(name: 'spec_tag2') | tn2 = TagName.create!(name: 'spec_tag2') | ||||
| tag2 = Tag.create!(tag_name: tn2, category: 'general') | |||||
| tag2 = Tag.create!(tag_name: tn2, category: :general) | |||||
| pt = PostTag.create!(post: post_record, tag: tag2, created_user: member) | pt = PostTag.create!(post: post_record, tag: tag2, created_user: member) | ||||
| # remove (discard) | # remove (discard) | ||||