diff --git a/spec/requests/api/v1/party_shares_spec.rb b/spec/requests/api/v1/party_shares_spec.rb new file mode 100644 index 0000000..64ba752 --- /dev/null +++ b/spec/requests/api/v1/party_shares_spec.rb @@ -0,0 +1,139 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Api::V1::PartyShares', type: :request do + let(:user) { create(:user) } + let(:access_token) do + Doorkeeper::AccessToken.create!(resource_owner_id: user.id, expires_in: 30.days, scopes: 'public') + end + let(:auth_headers) { { 'Authorization' => "Bearer #{access_token.token}" } } + + let(:crew) { create(:crew) } + let(:party) { create(:party, user: user) } + + before do + create(:crew_membership, crew: crew, user: user) + end + + describe 'GET /api/v1/parties/:party_id/shares' do + context 'as party owner' do + it 'returns list of shares' do + share = create(:party_share, party: party, shareable: crew, shared_by: user) + + get "/api/v1/parties/#{party.id}/shares", headers: auth_headers + + expect(response).to have_http_status(:ok) + json = JSON.parse(response.body) + expect(json['shares'].length).to eq(1) + expect(json['shares'][0]['id']).to eq(share.id) + end + + it 'returns empty array when no shares' do + get "/api/v1/parties/#{party.id}/shares", headers: auth_headers + + expect(response).to have_http_status(:ok) + json = JSON.parse(response.body) + expect(json['shares']).to eq([]) + end + end + + context 'as non-owner' do + let(:other_user) { create(:user) } + let(:other_token) do + Doorkeeper::AccessToken.create!(resource_owner_id: other_user.id, expires_in: 30.days, scopes: 'public') + end + let(:other_headers) { { 'Authorization' => "Bearer #{other_token.token}" } } + + it 'returns unauthorized' do + get "/api/v1/parties/#{party.id}/shares", headers: other_headers + + expect(response).to have_http_status(:unauthorized) + end + end + + context 'without authentication' do + it 'returns unauthorized' do + get "/api/v1/parties/#{party.id}/shares" + + expect(response).to have_http_status(:unauthorized) + end + end + end + + describe 'POST /api/v1/parties/:party_id/shares' do + context 'as party owner in a crew' do + it 'shares the party with their crew' do + post "/api/v1/parties/#{party.id}/shares", headers: auth_headers + + expect(response).to have_http_status(:created) + json = JSON.parse(response.body) + expect(json['share']['shareable_type']).to eq('crew') + expect(json['share']['shareable']['id']).to eq(crew.id) + end + + it 'returns error when already shared' do + create(:party_share, party: party, shareable: crew, shared_by: user) + + post "/api/v1/parties/#{party.id}/shares", headers: auth_headers + + expect(response).to have_http_status(:unprocessable_entity) + end + end + + context 'as party owner not in a crew' do + before do + user.active_crew_membership.retire! + end + + it 'returns not_in_crew error' do + post "/api/v1/parties/#{party.id}/shares", headers: auth_headers + + expect(response).to have_http_status(:unprocessable_entity) + json = JSON.parse(response.body) + expect(json['code']).to eq('not_in_crew') + end + end + + context 'as non-owner' do + let(:other_user) { create(:user) } + let(:other_token) do + Doorkeeper::AccessToken.create!(resource_owner_id: other_user.id, expires_in: 30.days, scopes: 'public') + end + let(:other_headers) { { 'Authorization' => "Bearer #{other_token.token}" } } + + it 'returns unauthorized' do + post "/api/v1/parties/#{party.id}/shares", headers: other_headers + + expect(response).to have_http_status(:unauthorized) + end + end + end + + describe 'DELETE /api/v1/parties/:party_id/shares/:id' do + let!(:share) { create(:party_share, party: party, shareable: crew, shared_by: user) } + + context 'as party owner' do + it 'removes the share' do + delete "/api/v1/parties/#{party.id}/shares/#{share.id}", headers: auth_headers + + expect(response).to have_http_status(:no_content) + expect(PartyShare.exists?(share.id)).to be false + end + end + + context 'as non-owner' do + let(:other_user) { create(:user) } + let(:other_token) do + Doorkeeper::AccessToken.create!(resource_owner_id: other_user.id, expires_in: 30.days, scopes: 'public') + end + let(:other_headers) { { 'Authorization' => "Bearer #{other_token.token}" } } + + it 'returns unauthorized' do + delete "/api/v1/parties/#{party.id}/shares/#{share.id}", headers: other_headers + + expect(response).to have_http_status(:unauthorized) + end + end + end +end