db = Mockery::mock(\wpdb::class); $this->db->prefix = 'wp_'; $this->repo = new AvailabilityRepository($this->db); } public function testInsertCallsWpdbInsertAndReturnsId(): void { Functions\expect('current_time')->with('mysql')->andReturn('2026-04-01 12:00:00'); $this->db->shouldReceive('insert') ->once() ->with( 'wp_us_availability', Mockery::on(static function (array $data): bool { return $data['instructor_id'] === 5 && $data['start_dt'] === '2026-04-01 09:00:00' && $data['is_booked'] === 0; }), ['%d', '%s', '%s', '%d', '%s'] ); $this->db->insert_id = 42; $slot = new AvailabilitySlot(5, '2026-04-01 09:00:00', '2026-04-01 10:00:00'); $result = $this->repo->insert($slot); self::assertSame(42, $result); } public function testFindByIdReturnsNullWhenNotFound(): void { $this->db->shouldReceive('prepare') ->once() ->andReturn('SELECT * FROM wp_us_availability WHERE id = 99'); $this->db->shouldReceive('get_row') ->once() ->andReturn(null); $result = $this->repo->findById(99); self::assertNull($result); } public function testFindByIdReturnsSlotWhenFound(): void { $row = (object) [ 'id' => '10', 'instructor_id' => '5', 'start_dt' => '2026-04-01 09:00:00', 'end_dt' => '2026-04-01 10:00:00', 'is_booked' => '0', ]; $this->db->shouldReceive('prepare')->andReturn('SELECT ...'); $this->db->shouldReceive('get_row')->andReturn($row); $slot = $this->repo->findById(10); self::assertInstanceOf(AvailabilitySlot::class, $slot); self::assertSame(10, $slot->id); self::assertSame(5, $slot->instructorId); } public function testMarkBookedUpdatesRecord(): void { $this->db->shouldReceive('update') ->once() ->with('wp_us_availability', ['is_booked' => 1], ['id' => 7], ['%d'], ['%d']) ->andReturn(1); $result = $this->repo->markBooked(7); self::assertTrue($result); } public function testDeleteReturnsFalseWhenRowNotDeleted(): void { $this->db->shouldReceive('delete') ->once() ->with('wp_us_availability', ['id' => 1, 'is_booked' => 0], ['%d', '%d']) ->andReturn(0); self::assertFalse($this->repo->delete(1)); } public function testFindAvailableWithNoFiltersUsesNoParams(): void { $this->db->shouldReceive('get_results') ->once() ->with(Mockery::pattern('/WHERE is_booked = 0/')) ->andReturn([]); $result = $this->repo->findAvailable(); self::assertSame([], $result); } public function testFindAvailableWithInstructorFilterPreparesQuery(): void { $this->db->shouldReceive('prepare') ->once() ->with(Mockery::pattern('/instructor_id = %d/'), Mockery::any()) ->andReturn('SELECT ...'); $this->db->shouldReceive('get_results')->andReturn([]); $this->repo->findAvailable(instructorId: 3); } public function testFindByInstructorReturnsSlots(): void { $row = (object) [ 'id' => '5', 'instructor_id' => '3', 'start_dt' => '2026-04-01 09:00:00', 'end_dt' => '2026-04-01 10:00:00', 'is_booked' => '0', ]; $this->db->shouldReceive('prepare')->andReturn('SELECT ...'); $this->db->shouldReceive('get_results')->andReturn([$row]); $slots = $this->repo->findByInstructor(3); self::assertCount(1, $slots); self::assertInstanceOf(AvailabilitySlot::class, $slots[0]); } }