From df385d976895e8860759e26e43c0a338ebe30662 Mon Sep 17 00:00:00 2001 From: miteruzo Date: Wed, 4 Mar 2026 22:53:14 +0900 Subject: [PATCH] #247 --- .../app/controllers/deerjikists_controller.rb | 32 +++- backend/app/controllers/tags_controller.rb | 2 +- backend/config/routes.rb | 8 +- backend/spec/factories/tags.rb | 4 +- backend/spec/requests/deerjikists_spec.rb | 181 ++++++++++++++++++ .../spec/requests/tags_deerjikists_spec.rb | 102 ++++++++++ 6 files changed, 322 insertions(+), 7 deletions(-) create mode 100644 backend/spec/requests/deerjikists_spec.rb create mode 100644 backend/spec/requests/tags_deerjikists_spec.rb diff --git a/backend/app/controllers/deerjikists_controller.rb b/backend/app/controllers/deerjikists_controller.rb index 795edec..b04eaf6 100644 --- a/backend/app/controllers/deerjikists_controller.rb +++ b/backend/app/controllers/deerjikists_controller.rb @@ -6,7 +6,7 @@ class DeerjikistsController < ApplicationController deerjikist = Deerjikist .joins(:tag) - .includes(:tag, tag: :tag_name) + .includes(tag: :tag_name) .find_by(platform:, code:) if deerjikist render json: DeerjikistRepr.base(deerjikist) @@ -14,4 +14,34 @@ class DeerjikistsController < ApplicationController head :not_found end end + + def update + return head :unauthorized unless current_user + return head :forbidden unless current_user.member? + + platform = params[:platform].to_s.strip + code = params[:code].to_s.strip + tag_id = params[:tag_id].to_i + return head :bad_request if platform.blank? || code.blank? || tag_id <= 0 + + deerjikist = Deerjikist.find_or_initialize_by(platform:, code:).tap do |d| + d.tag_id = tag_id + d.save! + end + + render json: DeerjikistRepr.base(deerjikist) + end + + def destroy + return head :unauthorized unless current_user + return head :forbidden unless current_user.member? + + platform = params[:platform].to_s.strip + code = params[:code].to_s.strip + return head :bad_request if platform.blank? || code.blank? + + Deerjikist.find([platform, code]).destroy! + + head :no_content + end end diff --git a/backend/app/controllers/tags_controller.rb b/backend/app/controllers/tags_controller.rb index b68c47e..186b6b9 100644 --- a/backend/app/controllers/tags_controller.rb +++ b/backend/app/controllers/tags_controller.rb @@ -90,7 +90,7 @@ class TagsController < ApplicationController tag = Tag.joins(:tag_name) .includes(:tag_name, tag_name: :wiki_page) .find_by(id: params[:id]) - return head :bad_request unless tag + return head :not_found unless tag render json: DeerjikistRepr.many(tag.deerjikists) end diff --git a/backend/config/routes.rb b/backend/config/routes.rb index f7052d2..ed7b4ac 100644 --- a/backend/config/routes.rb +++ b/backend/config/routes.rb @@ -65,9 +65,11 @@ Rails.application.routes.draw do resources :deerjikists, only: [] do collection do - get ':platform/:code', action: :show - put ':platfrom/:code', action: :update - delete ':platform/:code', action: :destroy + scope ':platform/:code' do + get '', action: :show + put '', action: :update + delete '', action: :destroy + end end end end diff --git a/backend/spec/factories/tags.rb b/backend/spec/factories/tags.rb index 5d9530e..e2d1120 100644 --- a/backend/spec/factories/tags.rb +++ b/backend/spec/factories/tags.rb @@ -1,11 +1,11 @@ FactoryBot.define do factory :tag do - category { 'general' } + category { :general } post_count { 0 } association :tag_name trait :nico do - category { 'nico' } + category { :nico } tag_name { association(:tag_name, name: "nico:#{ SecureRandom.hex(4) }") } end end diff --git a/backend/spec/requests/deerjikists_spec.rb b/backend/spec/requests/deerjikists_spec.rb new file mode 100644 index 0000000..136fc2d --- /dev/null +++ b/backend/spec/requests/deerjikists_spec.rb @@ -0,0 +1,181 @@ +require 'rails_helper' + +RSpec.describe 'Deerjikists API', type: :request do + let(:platform) { 'nico' } + let(:code) { 'deerjika-bot' } + + let!(:tag1) { create(:tag, category: :deerjikist) } + let!(:tag2) { create(:tag, category: :deerjikist) } + + let(:member) { create(:user, :member) } + let(:guest) { create(:user, role: :guest) } + + describe 'GET /deerjikists/:platform/:code' do + subject(:do_request) do + get "/deerjikists/#{ platform }/#{ code }" + end + + context 'when deerjikist exists' do + before do + Deerjikist.create!(platform:, code:, tag: tag1) + end + + it 'returns 200 and deerjikist json' do + do_request + expect(response).to have_http_status(:ok) + + expect(json).to be_a(Hash) + expect(json['platform']).to eq(platform) + expect(json['code']).to eq(code) + + expect(json['tag']).to be_a(Hash) + expect(json['tag']['id']).to eq(tag1.id) + expect(json['tag']['name']).to eq(tag1.name) + end + end + + context 'when deerjikist does not exist' do + it 'returns 404' do + do_request + expect(response).to have_http_status(:not_found) + end + end + + context 'when platform or code become blank after strip' do + it 'returns 400' do + get '/deerjikists/%20/%20' + expect(response).to have_http_status(:bad_request) + end + end + end + + describe 'PUT /deerjikists/:platform/:code' do + subject(:do_request) do + put "/deerjikists/#{ platform }/#{ code }", params: payload + end + + let(:payload) { { tag_id: tag1.id } } + + context 'when not legged in' do + it 'returns 401' do + sign_out + do_request + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when logged in but not member' do + it 'returns 403' do + sign_in_as guest + do_request + expect(response).to have_http_status(:forbidden) + end + end + + context 'when member' do + before do + sign_in_as member + end + + context 'when params invalid' do + it 'returns 400 when tag_id is missing or invalid' do + put "/deerjikists/#{ platform }/#{ code }", params: { tag_id: 0 } + expect(response).to have_http_status(:bad_request) + end + + it 'returns 400 when platform or code blank after strip' do + put '/deerjikists/%20/%20', params: { tag_id: tag1.id } + expect(response).to have_http_status(:bad_request) + end + end + + context 'when creating new deerjikist' do + it 'creates and returns 200 with json' do + expect { do_request }.to change { Deerjikist.count }.by(1) + expect(response).to have_http_status(:ok) + + d = Deerjikist.find_by(platform:, code:) + expect(d).to be_present + expect(d.tag_id).to eq(tag1.id) + + expect(json['platform']).to eq(platform) + expect(json['code']).to eq(code) + end + end + + context 'when updating existing deerjikist' do + before do + Deerjikist.create!(platform:, code:, tag: tag1) + end + + let(:payload) { { tag_id: tag2.id } } + + it 'updates tag_id and returns 200' do + expect { do_request }.not_to change { Deerjikist.count } + expect(response).to have_http_status(:ok) + + d = Deerjikist.find_by(platform:, code:) + expect(d.tag_id).to eq(tag2.id) + + expect(json['platform']).to eq(platform) + expect(json['code']).to eq(code) + end + end + end + end + + describe 'DELETE /deerjikists/:platform/:code' do + subject(:do_request) do + delete "/deerjikists/#{ platform }/#{ code }" + end + + context 'when not logged in' do + it 'returns 401' do + sign_out + do_request + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when logged in but not member' do + it 'returns 403' do + sign_in_as guest + do_request + expect(response).to have_http_status(:forbidden) + end + end + + context 'when member' do + before do + sign_in_as member + end + + it 'returns 400 when platform/code blank after strip' do + delete '/deerjikists/%20/%20' + expect(response).to have_http_status(:bad_request) + end + + context 'when deerjikist exists' do + before do + Deerjikist.create!(platform: platform, code: code, tag: tag1) + end + + it 'destroys and returns 204' do + expect { + do_request + }.to change { Deerjikist.exists?(platform: platform, code: code) } + .from(true).to(false) + + expect(response).to have_http_status(:no_content) + end + end + + context 'when deerjikist does not exist' do + it 'returns 404' do + do_request + expect(response).to have_http_status(:not_found) + end + end + end + end +end diff --git a/backend/spec/requests/tags_deerjikists_spec.rb b/backend/spec/requests/tags_deerjikists_spec.rb new file mode 100644 index 0000000..4825f36 --- /dev/null +++ b/backend/spec/requests/tags_deerjikists_spec.rb @@ -0,0 +1,102 @@ +require 'rails_helper' + +RSpec.describe 'Tags deerjikists API', type: :request do + let(:platform1) { 'nico' } + let(:code1) { 'deerjika-bot' } + let(:platform2) { 'youtube' } + let(:code2) { 'deerjika-bot.bsky.social' } + + let!(:tag) { create(:tag, category: :deerjikist) } + + before do + # show_by_name / deerjikists_by_name 用に名前を固定 + tag.tag_name.update!(name: 'deerjika') + end + + describe 'GET /tags/:id/deerjikists' do + subject(:do_request) do + get "/tags/#{ tag_id }/deerjikists" + end + + let(:tag_id) { tag.id } + + context 'when tag exists and has no deerjikists' do + it 'returns 200 and empty array' do + do_request + expect(response).to have_http_status(:ok) + expect(json).to eq([]) + end + end + + context 'when tag exists and has deerjikists' do + before do + Deerjikist.create!(platform: platform1, code: code1, tag: tag) + Deerjikist.create!(platform: platform2, code: code2, tag: tag) + end + + it 'returns 200 and deerjikists array' do + do_request + expect(response).to have_http_status(:ok) + + expect(json).to be_a(Array) + expect(json.size).to eq(2) + + expect(json.map { |h| [h['platform'], h['code']] }).to contain_exactly( + [platform1, code1], + [platform2, code2], + ) + end + end + + context 'when tag does not exist' do + let(:tag_id) { 9_999_999 } + + it 'returns 404' do + do_request + expect(response).to have_http_status(:not_found) + end + end + end + + describe 'GET /tags/name/:name/deerjikists' do + subject(:do_request) do + get "/tags/name/#{ name }/deerjikists" + end + + let(:name) { 'deerjika' } + + context 'when name is blank after strip' do + let(:name) { '%20' } + + it 'returns 400' do + do_request + expect(response).to have_http_status(:bad_request) + end + end + + context 'when tag does not exist for name' do + let(:name) { 'no-such-tag' } + + it 'returns 404' do + do_request + expect(response).to have_http_status(:not_found) + end + end + + context 'when tag exists and has deerjikists' do + before do + Deerjikist.create!(platform: platform1, code: code1, tag: tag) + end + + it 'returns 200 and deerjikists array' do + do_request + expect(response).to have_http_status(:ok) + + expect(json).to be_a(Array) + expect(json.size).to eq(1) + expect(json[0]['platform']).to eq(platform1) + expect(json[0]['code']).to eq(code1) + end + end + end +end