hensei-api/spec/models/crew_spec.rb
Justin Edmund 872b6fdb59 add crew specs and fix error handling
- add transactional fixtures to rails_helper for test isolation
- restructure crew errors to CrewErrors module for Zeitwerk
- add rescue_from for CrewErrors::CrewError in api_controller
- add model specs for Crew and CrewMembership (34 examples)
- add controller specs for crews and memberships (28 examples)
- add crew-related specs to User model (22 examples)
- add factories for crews and crew_memberships
2025-12-03 22:51:34 -08:00

134 lines
4 KiB
Ruby

require 'rails_helper'
RSpec.describe Crew, type: :model do
describe 'associations' do
it { should have_many(:crew_memberships).dependent(:destroy) }
it { should have_many(:users).through(:crew_memberships) }
it { should have_many(:active_memberships) }
it { should have_many(:active_members).through(:active_memberships) }
end
describe 'validations' do
it { should validate_presence_of(:name) }
it { should validate_length_of(:name).is_at_most(100) }
it { should validate_length_of(:gamertag).is_at_most(50) }
context 'granblue_crew_id uniqueness' do
it 'validates uniqueness' do
crew1 = create(:crew, granblue_crew_id: 'ABC123')
crew2 = build(:crew, granblue_crew_id: 'ABC123')
expect(crew2).not_to be_valid
expect(crew2.errors[:granblue_crew_id]).to include('has already been taken')
end
it 'allows nil values' do
create(:crew, granblue_crew_id: nil)
crew2 = build(:crew, granblue_crew_id: nil)
expect(crew2).to be_valid
end
end
end
describe '#captain' do
let(:crew) { create(:crew) }
let(:captain_user) { create(:user) }
let(:member_user) { create(:user) }
before do
create(:crew_membership, :captain, crew: crew, user: captain_user)
create(:crew_membership, crew: crew, user: member_user)
end
it 'returns the captain user' do
expect(crew.captain).to eq(captain_user)
end
it 'returns nil if no captain' do
crew.crew_memberships.find_by(role: :captain).update!(role: :member)
expect(crew.captain).to be_nil
end
it 'does not return retired captains' do
crew.crew_memberships.find_by(role: :captain).retire!
expect(crew.captain).to be_nil
end
end
describe '#vice_captains' do
let(:crew) { create(:crew) }
let(:captain_user) { create(:user) }
let(:vc1) { create(:user) }
let(:vc2) { create(:user) }
let(:member_user) { create(:user) }
before do
create(:crew_membership, :captain, crew: crew, user: captain_user)
create(:crew_membership, :vice_captain, crew: crew, user: vc1)
create(:crew_membership, :vice_captain, crew: crew, user: vc2)
create(:crew_membership, crew: crew, user: member_user)
end
it 'returns all vice captains' do
expect(crew.vice_captains).to contain_exactly(vc1, vc2)
end
it 'does not include retired vice captains' do
crew.crew_memberships.find_by(user: vc1).retire!
expect(crew.vice_captains).to contain_exactly(vc2)
end
end
describe '#officers' do
let(:crew) { create(:crew) }
let(:captain_user) { create(:user) }
let(:vc1) { create(:user) }
let(:member_user) { create(:user) }
before do
create(:crew_membership, :captain, crew: crew, user: captain_user)
create(:crew_membership, :vice_captain, crew: crew, user: vc1)
create(:crew_membership, crew: crew, user: member_user)
end
it 'returns captain and vice captains' do
expect(crew.officers).to contain_exactly(captain_user, vc1)
end
it 'does not include regular members' do
expect(crew.officers).not_to include(member_user)
end
end
describe '#member_count' do
let(:crew) { create(:crew) }
before do
create(:crew_membership, :captain, crew: crew)
create(:crew_membership, crew: crew)
create(:crew_membership, :retired, crew: crew)
end
it 'returns count of active members only' do
expect(crew.member_count).to eq(2)
end
end
describe 'active_members scope' do
let(:crew) { create(:crew) }
let(:active_user) { create(:user) }
let(:retired_user) { create(:user) }
before do
create(:crew_membership, crew: crew, user: active_user)
create(:crew_membership, :retired, crew: crew, user: retired_user)
end
it 'returns only active members' do
expect(crew.active_members).to contain_exactly(active_user)
end
it 'does not include retired members' do
expect(crew.active_members).not_to include(retired_user)
end
end
end