From 2e0f710adce1c269bd012d401e79da6a3522f1eb Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Sun, 4 Jan 2026 22:54:01 -0800 Subject: [PATCH] add party share factory and model specs --- spec/factories/party_shares.rb | 17 +++++ spec/models/party_share_spec.rb | 121 ++++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 spec/factories/party_shares.rb create mode 100644 spec/models/party_share_spec.rb diff --git a/spec/factories/party_shares.rb b/spec/factories/party_shares.rb new file mode 100644 index 0000000..1c506ea --- /dev/null +++ b/spec/factories/party_shares.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :party_share do + party + association :shareable, factory: :crew + association :shared_by, factory: :user + + # Ensure the shared_by user owns the party and is in the crew + after(:build) do |party_share| + party_share.party.user = party_share.shared_by + unless party_share.shareable.crew_memberships.exists?(user: party_share.shared_by, retired: false) + create(:crew_membership, crew: party_share.shareable, user: party_share.shared_by) + end + end + end +end diff --git a/spec/models/party_share_spec.rb b/spec/models/party_share_spec.rb new file mode 100644 index 0000000..79f4de8 --- /dev/null +++ b/spec/models/party_share_spec.rb @@ -0,0 +1,121 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe PartyShare, type: :model do + describe 'associations' do + it { should belong_to(:party) } + it { should belong_to(:shareable) } + it { should belong_to(:shared_by).class_name('User') } + end + + describe 'validations' do + let(:crew) { create(:crew) } + let(:user) { create(:user) } + let(:party) { create(:party, user: user) } + + before do + create(:crew_membership, crew: crew, user: user) + end + + it 'validates uniqueness of party scoped to shareable' do + create(:party_share, party: party, shareable: crew, shared_by: user) + + duplicate = build(:party_share, party: party, shareable: crew, shared_by: user) + expect(duplicate).not_to be_valid + expect(duplicate.errors[:party_id]).to include('has already been shared with this group') + end + + it 'allows same party to be shared with different crews' do + crew2 = create(:crew) + create(:crew_membership, crew: crew2, user: user) + + share1 = create(:party_share, party: party, shareable: crew, shared_by: user) + share2 = build(:party_share, party: party, shareable: crew2, shared_by: user) + + expect(share2).to be_valid + end + end + + describe 'owner validation' do + let(:crew) { create(:crew) } + let(:owner) { create(:user) } + let(:other_user) { create(:user) } + let(:party) { create(:party, user: owner) } + + before do + create(:crew_membership, crew: crew, user: owner) + create(:crew_membership, crew: crew, user: other_user) + end + + it 'allows owner to share their party' do + share = build(:party_share, party: party, shareable: crew, shared_by: owner) + expect(share).to be_valid + end + + it 'prevents non-owner from sharing the party' do + share = build(:party_share, party: party, shareable: crew, shared_by: other_user) + expect(share).not_to be_valid + expect(share.errors[:shared_by]).to include('must be the party owner') + end + end + + describe 'crew membership validation' do + let(:crew) { create(:crew) } + let(:user) { create(:user) } + let(:party) { create(:party, user: user) } + + it 'allows sharing to a crew the user belongs to' do + create(:crew_membership, crew: crew, user: user) + share = build(:party_share, party: party, shareable: crew, shared_by: user) + expect(share).to be_valid + end + + it 'prevents sharing to a crew the user does not belong to' do + share = build(:party_share, party: party, shareable: crew, shared_by: user) + expect(share).not_to be_valid + expect(share.errors[:shareable]).to include('you must be a member of this crew') + end + + it 'prevents sharing if user has retired from crew' do + membership = create(:crew_membership, crew: crew, user: user) + membership.retire! + + share = build(:party_share, party: party, shareable: crew, shared_by: user) + expect(share).not_to be_valid + end + end + + describe 'scopes' do + let(:crew1) { create(:crew) } + let(:crew2) { create(:crew) } + let(:user) { create(:user) } + let(:party1) { create(:party, user: user) } + let(:party2) { create(:party, user: user) } + + before do + create(:crew_membership, crew: crew1, user: user) + create(:crew_membership, crew: crew2, user: user) + end + + describe '.for_crew' do + it 'returns shares for a specific crew' do + share1 = create(:party_share, party: party1, shareable: crew1, shared_by: user) + share2 = create(:party_share, party: party2, shareable: crew2, shared_by: user) + + expect(PartyShare.for_crew(crew1)).to include(share1) + expect(PartyShare.for_crew(crew1)).not_to include(share2) + end + end + + describe '.for_party' do + it 'returns shares for a specific party' do + share1 = create(:party_share, party: party1, shareable: crew1, shared_by: user) + share2 = create(:party_share, party: party2, shareable: crew1, shared_by: user) + + expect(PartyShare.for_party(party1)).to include(share1) + expect(PartyShare.for_party(party1)).not_to include(share2) + end + end + end +end