require 'rails_helper' RSpec.describe 'Collection Viewing API', type: :request do let(:user) { create(:user, collection_privacy: 'everyone') } let(:private_user) { create(:user, collection_privacy: 'private_collection') } let(:crew_only_user) { create(:user, collection_privacy: 'crew_only') } let(:viewer) { create(:user) } let(:access_token) do Doorkeeper::AccessToken.create!(resource_owner_id: viewer.id, expires_in: 30.days, scopes: 'public') end let(:headers) do { 'Authorization' => "Bearer #{access_token.token}", 'Content-Type' => 'application/json' } end # Create some collection items for testing before do # User's collection create(:collection_character, user: user) create(:collection_weapon, user: user) create(:collection_summon, user: user) create(:collection_job_accessory, user: user) # Private user's collection create(:collection_character, user: private_user) create(:collection_weapon, user: private_user) end describe 'GET /api/v1/users/:user_id/collection' do context 'viewing public collection' do it 'returns the complete collection' do get "/api/v1/users/#{user.id}/collection", headers: headers expect(response).to have_http_status(:ok) json = JSON.parse(response.body) expect(json).to have_key('characters') expect(json).to have_key('weapons') expect(json).to have_key('summons') expect(json).to have_key('job_accessories') expect(json['characters'].length).to eq(1) expect(json['weapons'].length).to eq(1) expect(json['summons'].length).to eq(1) expect(json['job_accessories'].length).to eq(1) end it 'returns only characters when type=characters' do get "/api/v1/users/#{user.id}/collection", params: { type: 'characters' }, headers: headers expect(response).to have_http_status(:ok) json = JSON.parse(response.body) expect(json).to have_key('characters') expect(json).not_to have_key('weapons') expect(json).not_to have_key('summons') expect(json['characters'].length).to eq(1) end it 'returns only weapons when type=weapons' do get "/api/v1/users/#{user.id}/collection", params: { type: 'weapons' }, headers: headers expect(response).to have_http_status(:ok) json = JSON.parse(response.body) expect(json).to have_key('weapons') expect(json).not_to have_key('characters') expect(json).not_to have_key('summons') expect(json['weapons'].length).to eq(1) end it 'returns only summons when type=summons' do get "/api/v1/users/#{user.id}/collection", params: { type: 'summons' }, headers: headers expect(response).to have_http_status(:ok) json = JSON.parse(response.body) expect(json).to have_key('summons') expect(json).not_to have_key('characters') expect(json).not_to have_key('weapons') expect(json['summons'].length).to eq(1) end it 'returns only job accessories when type=job_accessories' do get "/api/v1/users/#{user.id}/collection", params: { type: 'job_accessories' }, headers: headers expect(response).to have_http_status(:ok) json = JSON.parse(response.body) expect(json).to have_key('job_accessories') expect(json).not_to have_key('characters') expect(json).not_to have_key('weapons') expect(json['job_accessories'].length).to eq(1) end it 'works without authentication for public collections' do get "/api/v1/users/#{user.id}/collection" expect(response).to have_http_status(:ok) json = JSON.parse(response.body) expect(json).to have_key('characters') end end context 'viewing private collection' do it 'returns forbidden for non-owner' do get "/api/v1/users/#{private_user.id}/collection", headers: headers expect(response).to have_http_status(:forbidden) json = JSON.parse(response.body) expect(json['error']).to include('do not have permission') end it 'allows owner to view their own private collection' do owner_token = Doorkeeper::AccessToken.create!( resource_owner_id: private_user.id, expires_in: 30.days, scopes: 'public' ) owner_headers = { 'Authorization' => "Bearer #{owner_token.token}", 'Content-Type' => 'application/json' } get "/api/v1/users/#{private_user.id}/collection", headers: owner_headers expect(response).to have_http_status(:ok) json = JSON.parse(response.body) expect(json).to have_key('characters') expect(json).to have_key('weapons') end end context 'viewing crew-only collection' do it 'returns forbidden for non-crew members' do get "/api/v1/users/#{crew_only_user.id}/collection", headers: headers expect(response).to have_http_status(:forbidden) json = JSON.parse(response.body) expect(json['error']).to include('do not have permission') end it 'allows owner to view their own crew-only collection' do owner_token = Doorkeeper::AccessToken.create!( resource_owner_id: crew_only_user.id, expires_in: 30.days, scopes: 'public' ) owner_headers = { 'Authorization' => "Bearer #{owner_token.token}", 'Content-Type' => 'application/json' } get "/api/v1/users/#{crew_only_user.id}/collection", headers: owner_headers expect(response).to have_http_status(:ok) json = JSON.parse(response.body) expect(json).to have_key('characters') end end context 'error handling' do it 'returns not found for non-existent user' do get "/api/v1/users/#{SecureRandom.uuid}/collection", headers: headers expect(response).to have_http_status(:not_found) json = JSON.parse(response.body) expect(json['error']).to include('User not found') end end end end