acceptances = Mockery::mock(AcceptanceRepository::class); $this->policies = Mockery::mock(PolicyRepository::class); $this->policyVersions = Mockery::mock(PolicyVersionRepository::class); $this->answers = Mockery::mock(AnswerRepository::class); $this->questions = Mockery::mock(QuestionRepository::class); $this->payments = Mockery::mock(PaymentRepository::class); $this->history = new StudentHistory( $this->acceptances, $this->policies, $this->policyVersions, $this->answers, $this->questions, $this->payments ); } public function testPolicyAcceptancesResolvePolicyTitleAndVersion(): void { $this->acceptances->shouldReceive('findByStudent')->once()->with(5)->andReturn([ new PolicyAcceptance(9, 5, PolicyAcceptance::REG_ACCOUNT, 5, null, '2026-06-02 09:00:00', 1), ]); $this->policyVersions->shouldReceive('findById')->with(9) ->andReturn(new PolicyVersion(2, 3, null, PolicyVersion::STATUS_PUBLISHED, id: 9)); $this->policies->shouldReceive('findById')->with(2) ->andReturn(new Policy('Waiver', 'waiver', 9, Policy::SCOPE_SIGNUP, 2)); $rows = $this->history->policyAcceptances(5); self::assertSame( [ [ 'policy' => 'Waiver', 'version' => 'v3', 'context' => 'Account signup', 'accepted_at' => '2026-06-02 09:00:00', ], ], $rows ); } public function testPolicyAcceptancesFallBackWhenVersionIsGone(): void { $this->acceptances->shouldReceive('findByStudent')->once()->with(5)->andReturn([ new PolicyAcceptance(9, 5, PolicyAcceptance::REG_LESSON, 12), ]); $this->policyVersions->shouldReceive('findById')->with(9)->andReturn(null); $rows = $this->history->policyAcceptances(5); self::assertSame('#9', $rows[0]['policy']); self::assertSame('—', $rows[0]['version']); self::assertSame('Lesson #12', $rows[0]['context']); self::assertSame('', $rows[0]['accepted_at']); } public function testIntakeAnswersResolveQuestionLabels(): void { $this->answers->shouldReceive('findByStudent')->once()->with(5)->andReturn([ new Answer(4, Answer::REG_ENROLLMENT, 3, 5, 'Beginner', 1), ]); $this->questions->shouldReceive('findById')->with(4) ->andReturn(new Question(1, 'Experience level', id: 4)); $rows = $this->history->intakeAnswers(5); self::assertSame( [ [ 'question' => 'Experience level', 'answer' => 'Beginner', 'context' => 'Enrolment #3', ], ], $rows ); } public function testIntakeAnswersFallBackWhenQuestionIsGone(): void { $this->answers->shouldReceive('findByStudent')->once()->with(5)->andReturn([ new Answer(4, Answer::REG_LESSON, 12, 5, null, 1), ]); $this->questions->shouldReceive('findById')->with(4)->andReturn(null); $rows = $this->history->intakeAnswers(5); self::assertSame('#4', $rows[0]['question']); self::assertSame('—', $rows[0]['answer']); } public function testPaymentsBuildDisplayRows(): void { $this->payments->shouldReceive('findByStudent')->once()->with(5)->andReturn([ new Payment( 5, 3, Payment::REG_LESSON, 12, 100.00, 'CAD', Payment::METHOD_CARD, Payment::STATUS_PAID, taxRate: 13.0, taxAmount: 13.00, receiptNumber: 'USC-7', createdAt: '2026-06-08 09:00:00', id: 50 ), ]); $rows = $this->history->payments(5); self::assertSame( [ [ 'created_at' => '2026-06-08 09:00:00', 'context' => 'Lesson #12', 'method' => Payment::METHOD_CARD, 'status' => Payment::STATUS_PAID, 'amount' => 100.00, 'tax_amount' => 13.00, 'total' => 113.00, 'currency' => 'CAD', 'receipt' => 'USC-7', ], ], $rows ); } public function testPaymentsFallBackWhenUnpaidAndUndated(): void { $this->payments->shouldReceive('findByStudent')->once()->with(5)->andReturn([ new Payment(5, 3, Payment::REG_ENROLLMENT, 3, 40.00, id: 51), ]); $rows = $this->history->payments(5); self::assertSame('', $rows[0]['created_at']); self::assertSame('Enrolment #3', $rows[0]['context']); self::assertSame('—', $rows[0]['receipt']); } }