- 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
137 lines
4.1 KiB
Ruby
137 lines
4.1 KiB
Ruby
require 'rails_helper'
|
|
|
|
RSpec.describe CrewMembership, type: :model do
|
|
describe 'associations' do
|
|
it { should belong_to(:crew) }
|
|
it { should belong_to(:user) }
|
|
end
|
|
|
|
describe 'validations' do
|
|
it 'validates uniqueness of user_id scoped to crew_id' do
|
|
crew = create(:crew)
|
|
user = create(:user)
|
|
create(:crew_membership, crew: crew, user: user)
|
|
|
|
duplicate = build(:crew_membership, crew: crew, user: user)
|
|
expect(duplicate).not_to be_valid
|
|
expect(duplicate.errors[:user_id]).to include('has already been taken')
|
|
end
|
|
end
|
|
|
|
describe 'role enum' do
|
|
it { should define_enum_for(:role).with_values(member: 0, vice_captain: 1, captain: 2) }
|
|
end
|
|
|
|
describe 'scopes' do
|
|
let(:crew) { create(:crew) }
|
|
|
|
describe '.active' do
|
|
it 'returns only non-retired memberships' do
|
|
active = create(:crew_membership, crew: crew)
|
|
retired = create(:crew_membership, :retired, crew: crew)
|
|
|
|
expect(CrewMembership.active).to include(active)
|
|
expect(CrewMembership.active).not_to include(retired)
|
|
end
|
|
end
|
|
|
|
describe '.retired' do
|
|
it 'returns only retired memberships' do
|
|
active = create(:crew_membership, crew: crew)
|
|
retired = create(:crew_membership, :retired, crew: crew)
|
|
|
|
expect(CrewMembership.retired).to include(retired)
|
|
expect(CrewMembership.retired).not_to include(active)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'one active crew per user validation' do
|
|
let(:crew1) { create(:crew) }
|
|
let(:crew2) { create(:crew) }
|
|
let(:user) { create(:user) }
|
|
|
|
it 'prevents user from joining multiple active crews' do
|
|
create(:crew_membership, crew: crew1, user: user)
|
|
membership2 = build(:crew_membership, crew: crew2, user: user)
|
|
|
|
expect(membership2).not_to be_valid
|
|
expect(membership2.errors[:user]).to include('can only be in one active crew')
|
|
end
|
|
|
|
it 'allows user to join new crew after retiring from old one' do
|
|
membership1 = create(:crew_membership, crew: crew1, user: user)
|
|
membership1.retire!
|
|
|
|
membership2 = build(:crew_membership, crew: crew2, user: user)
|
|
expect(membership2).to be_valid
|
|
end
|
|
end
|
|
|
|
describe 'captain limit validation' do
|
|
let(:crew) { create(:crew) }
|
|
let(:captain_user) { create(:user) }
|
|
|
|
before do
|
|
create(:crew_membership, :captain, crew: crew, user: captain_user)
|
|
end
|
|
|
|
it 'prevents multiple captains' do
|
|
new_captain = build(:crew_membership, :captain, crew: crew)
|
|
|
|
expect(new_captain).not_to be_valid
|
|
expect(new_captain.errors[:role]).to include('crew can only have one captain')
|
|
end
|
|
|
|
it 'allows captain after previous captain retires' do
|
|
crew.crew_memberships.find_by(role: :captain).retire!
|
|
new_captain = build(:crew_membership, :captain, crew: crew)
|
|
|
|
expect(new_captain).to be_valid
|
|
end
|
|
end
|
|
|
|
describe 'vice captain limit validation' do
|
|
let(:crew) { create(:crew) }
|
|
|
|
before do
|
|
create(:crew_membership, :captain, crew: crew)
|
|
3.times { create(:crew_membership, :vice_captain, crew: crew) }
|
|
end
|
|
|
|
it 'prevents more than 3 vice captains' do
|
|
fourth_vc = build(:crew_membership, :vice_captain, crew: crew)
|
|
|
|
expect(fourth_vc).not_to be_valid
|
|
expect(fourth_vc.errors[:role]).to include('crew can only have up to 3 vice captains')
|
|
end
|
|
|
|
it 'allows new vice captain after one retires' do
|
|
crew.crew_memberships.where(role: :vice_captain).first.retire!
|
|
new_vc = build(:crew_membership, :vice_captain, crew: crew)
|
|
|
|
expect(new_vc).to be_valid
|
|
end
|
|
end
|
|
|
|
describe '#retire!' do
|
|
let(:crew) { create(:crew) }
|
|
let(:user) { create(:user) }
|
|
let(:membership) { create(:crew_membership, :vice_captain, crew: crew, user: user) }
|
|
|
|
it 'sets retired to true' do
|
|
membership.retire!
|
|
expect(membership.retired).to be true
|
|
end
|
|
|
|
it 'sets retired_at timestamp' do
|
|
membership.retire!
|
|
expect(membership.retired_at).to be_within(1.second).of(Time.current)
|
|
end
|
|
|
|
it 'demotes to member role' do
|
|
membership.retire!
|
|
expect(membership.role).to eq('member')
|
|
end
|
|
end
|
|
end
|