You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
docuseal/spec/models/cohort_spec.rb

299 lines
8.9 KiB

# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Cohort, type: :model do
describe 'concerns' do
it 'includes SoftDeletable' do
expect(Cohort.ancestors).to include(SoftDeletable)
end
it 'includes AASM' do
expect(Cohort.ancestors).to include(AASM)
end
end
describe 'associations' do
it { should belong_to(:institution) }
it { should belong_to(:template) }
it { should have_many(:cohort_enrollments).dependent(:destroy) }
it { should have_many(:submissions).through(:cohort_enrollments) }
end
describe 'validations' do
it { should validate_presence_of(:name) }
it { should validate_presence_of(:program_type) }
it { should validate_presence_of(:sponsor_email) }
it { should validate_inclusion_of(:program_type).in_array(%w[learnership internship candidacy]) }
it { should validate_inclusion_of(:status).in_array(%w[draft active completed]) }
it 'validates sponsor email format' do
cohort = build(:cohort, sponsor_email: 'invalid-email')
expect(cohort).not_to be_valid
expect(cohort.errors[:sponsor_email]).to be_present
end
it 'accepts valid email format' do
cohort = build(:cohort, sponsor_email: 'sponsor@example.com')
expect(cohort).to be_valid
end
end
describe 'strip_attributes' do
it 'strips whitespace from name' do
cohort = create(:cohort, name: ' Test Cohort ')
expect(cohort.name).to eq('Test Cohort')
end
it 'strips whitespace from program_type' do
cohort = create(:cohort, program_type: ' learnership ')
expect(cohort.program_type).to eq('learnership')
end
it 'strips whitespace from sponsor_email' do
cohort = create(:cohort, sponsor_email: ' sponsor@example.com ')
expect(cohort.sponsor_email).to eq('sponsor@example.com')
end
end
describe 'scopes' do
let!(:draft_cohort) { create(:cohort, status: 'draft') }
let!(:active_cohort) { create(:cohort, status: 'active') }
let!(:completed_cohort) { create(:cohort, status: 'completed') }
let!(:deleted_cohort) { create(:cohort, deleted_at: Time.current) }
describe '.draft' do
it 'returns only draft cohorts' do
expect(Cohort.draft).to include(draft_cohort)
expect(Cohort.draft).not_to include(active_cohort, completed_cohort)
end
end
describe '.active_status' do
it 'returns only active cohorts' do
expect(Cohort.active_status).to include(active_cohort)
expect(Cohort.active_status).not_to include(draft_cohort, completed_cohort)
end
end
describe '.completed' do
it 'returns only completed cohorts' do
expect(Cohort.completed).to include(completed_cohort)
expect(Cohort.completed).not_to include(draft_cohort, active_cohort)
end
end
describe '.ready_for_sponsor' do
let!(:ready_cohort) do
create(:cohort, status: 'active', students_completed_at: Time.current)
end
it 'returns active cohorts with students_completed_at set' do
expect(Cohort.ready_for_sponsor).to include(ready_cohort)
expect(Cohort.ready_for_sponsor).not_to include(active_cohort)
end
end
describe '.active (from SoftDeletable)' do
it 'excludes soft-deleted cohorts' do
expect(Cohort.active).not_to include(deleted_cohort)
end
end
end
describe 'state machine' do
let(:cohort) { create(:cohort, status: 'draft') }
describe 'initial state' do
it 'starts in draft state' do
expect(cohort.draft?).to be true
end
end
describe '#activate event' do
it 'transitions from draft to active' do
expect { cohort.activate! }
.to change(cohort, :status).from('draft').to('active')
end
it 'sets tp_signed_at timestamp' do
expect { cohort.activate! }
.to change(cohort, :tp_signed_at).from(nil)
end
it 'cannot transition from completed to active' do
cohort.update!(status: 'completed')
expect { cohort.activate! }.to raise_error(AASM::InvalidTransition)
end
end
describe '#complete event' do
before { cohort.update!(status: 'active') }
it 'transitions from active to completed' do
expect { cohort.complete! }
.to change(cohort, :status).from('active').to('completed')
end
it 'sets finalized_at timestamp' do
expect { cohort.complete! }
.to change(cohort, :finalized_at).from(nil)
end
it 'cannot transition from draft to completed' do
cohort.update!(status: 'draft')
expect { cohort.complete! }.to raise_error(AASM::InvalidTransition)
end
end
describe 'state predicates' do
it 'provides draft? predicate' do
cohort.update!(status: 'draft')
expect(cohort.draft?).to be true
expect(cohort.active?).to be false
expect(cohort.completed?).to be false
end
it 'provides active? predicate' do
cohort.update!(status: 'active')
expect(cohort.active?).to be true
expect(cohort.draft?).to be false
expect(cohort.completed?).to be false
end
it 'provides completed? predicate' do
cohort.update!(status: 'completed')
expect(cohort.completed?).to be true
expect(cohort.draft?).to be false
expect(cohort.active?).to be false
end
end
end
describe '#all_students_completed?' do
let(:cohort) { create(:cohort) }
context 'when no student enrollments exist' do
it 'returns false' do
expect(cohort.all_students_completed?).to be false
end
end
context 'when all student enrollments are completed' do
before do
create(:cohort_enrollment, cohort: cohort, role: 'student', status: 'complete')
create(:cohort_enrollment, cohort: cohort, role: 'student', status: 'complete')
end
it 'returns true' do
expect(cohort.all_students_completed?).to be true
end
end
context 'when some student enrollments are not completed' do
before do
create(:cohort_enrollment, cohort: cohort, role: 'student', status: 'complete')
create(:cohort_enrollment, cohort: cohort, role: 'student', status: 'waiting')
end
it 'returns false' do
expect(cohort.all_students_completed?).to be false
end
end
context 'when sponsor enrollment exists' do
before do
create(:cohort_enrollment, cohort: cohort, role: 'student', status: 'complete')
create(:cohort_enrollment, cohort: cohort, role: 'sponsor', status: 'waiting')
end
it 'only checks student enrollments' do
expect(cohort.all_students_completed?).to be true
end
end
end
describe '#sponsor_access_ready?' do
let(:cohort) { create(:cohort, status: 'active', tp_signed_at: Time.current) }
context 'when all conditions are met' do
before do
create(:cohort_enrollment, cohort: cohort, role: 'student', status: 'complete')
end
it 'returns true' do
expect(cohort.sponsor_access_ready?).to be true
end
end
context 'when cohort is not active' do
before do
cohort.update!(status: 'draft')
create(:cohort_enrollment, cohort: cohort, role: 'student', status: 'complete')
end
it 'returns false' do
expect(cohort.sponsor_access_ready?).to be false
end
end
context 'when tp_signed_at is not set' do
before do
cohort.update!(tp_signed_at: nil)
create(:cohort_enrollment, cohort: cohort, role: 'student', status: 'complete')
end
it 'returns false' do
expect(cohort.sponsor_access_ready?).to be false
end
end
context 'when not all students are completed' do
before do
create(:cohort_enrollment, cohort: cohort, role: 'student', status: 'waiting')
end
it 'returns false' do
expect(cohort.sponsor_access_ready?).to be false
end
end
end
describe '#tp_can_sign?' do
it 'returns true when cohort is in draft state' do
cohort = create(:cohort, status: 'draft')
expect(cohort.tp_can_sign?).to be true
end
it 'returns false when cohort is active' do
cohort = create(:cohort, status: 'active')
expect(cohort.tp_can_sign?).to be false
end
it 'returns false when cohort is completed' do
cohort = create(:cohort, status: 'completed')
expect(cohort.tp_can_sign?).to be false
end
end
describe 'soft delete functionality' do
let(:cohort) { create(:cohort) }
it 'soft deletes the record' do
expect { cohort.soft_delete }
.to change { cohort.reload.deleted_at }.from(nil)
end
it 'excludes soft-deleted records from default scope' do
cohort.soft_delete
expect(Cohort.all).not_to include(cohort)
end
it 'restores soft-deleted records' do
cohort.soft_delete
expect { cohort.restore }
.to change { cohort.reload.deleted_at }.to(nil)
end
end
end