| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||