instructorId); self::assertSame(Offering::KIND_PRIVATE_LESSON, $offering->kind); self::assertSame('30-min Piano', $offering->title); self::assertSame(35.00, $offering->price); self::assertSame(30, $offering->durationMinutes); self::assertSame(42, $offering->id); } public function testDefaults(): void { $offering = new Offering(1, Offering::KIND_GROUP_CLASS, 'Choir'); self::assertSame(0.0, $offering->price); self::assertSame('CAD', $offering->currency); self::assertSame(Offering::BILLING_ONE_TIME, $offering->billingMode); self::assertNull($offering->durationMinutes); self::assertFalse($offering->allowWeekly); self::assertNull($offering->capacity); self::assertTrue($offering->isActive); self::assertNull($offering->id); } public function testFromRowMapsCorrectlyAndCastsNullables(): void { $row = (object) [ 'id' => '7', 'instructor_id' => '3', 'kind' => Offering::KIND_GROUP_CLASS, 'title' => 'Year Choir', 'description' => 'Weekly choir', 'duration_minutes' => null, 'price' => '120.00', 'currency' => 'CAD', 'billing_mode' => Offering::BILLING_FULL_TERM, 'allow_weekly' => '0', 'capacity' => '20', 'term_start' => '2026-09-01', 'term_end' => '2027-06-30', 'schedule_note' => 'Tuesdays 4:00pm', 'etransfer_email' => null, 'is_active' => '1', ]; $offering = Offering::fromRow($row); self::assertSame(7, $offering->id); self::assertSame(3, $offering->instructorId); self::assertNull($offering->durationMinutes); self::assertSame(120.00, $offering->price); self::assertSame(20, $offering->capacity); self::assertSame(Offering::BILLING_FULL_TERM, $offering->billingMode); self::assertTrue($offering->isActive); } public function testToArrayContainsExpectedKeys(): void { $offering = new Offering(1, Offering::KIND_PRIVATE_LESSON, 'Lesson', id: 10); $arr = $offering->toArray(); foreach (['id', 'instructor_id', 'kind', 'title', 'price', 'billing_mode', 'is_active'] as $key) { self::assertArrayHasKey($key, $arr); } } public function testToArrayIncludesEtransferEmailByDefault(): void { $offering = new Offering(1, Offering::KIND_PRIVATE_LESSON, 'Lesson', etransferEmail: 'studio@example.com', id: 10); self::assertArrayHasKey('etransfer_email', $offering->toArray()); self::assertSame('studio@example.com', $offering->toArray()['etransfer_email']); } public function testToArrayOmitsEtransferEmailWhenExcluded(): void { $offering = new Offering(1, Offering::KIND_PRIVATE_LESSON, 'Lesson', etransferEmail: 'studio@example.com', id: 10); self::assertArrayNotHasKey('etransfer_email', $offering->toArray(includeEtransferEmail: false)); } public function testValidKindAndBillingConstants(): void { self::assertContains(Offering::KIND_PRIVATE_LESSON, Offering::VALID_KINDS); self::assertContains(Offering::KIND_GROUP_CLASS, Offering::VALID_KINDS); self::assertContains(Offering::BILLING_ONE_TIME, Offering::VALID_BILLING_MODES); self::assertContains(Offering::BILLING_FULL_TERM, Offering::VALID_BILLING_MODES); } }