db = Mockery::mock(\wpdb::class); $this->db->prefix = 'wp_'; $this->repo = new GuardianRepository($this->db); $this->db->shouldReceive('prepare')->andReturnUsing( static fn (string $sql, ...$args): string => $sql . '|' . implode(',', $args) )->byDefault(); } public function testInsertStoresTheLinkAndReturnsId(): void { Functions\expect('current_time')->with('mysql')->andReturn('2026-07-29 09:00:00'); $this->db->shouldReceive('get_row')->once()->andReturn(null); $this->db->shouldReceive('insert') ->once() ->with( 'wp_us_guardians', [ 'guardian_id' => 5, 'student_id' => 42, 'relationship' => 'Parent', 'created_at' => '2026-07-29 09:00:00', ], ['%d', '%d', '%s', '%s'] ); $this->db->insert_id = 7; self::assertSame(7, $this->repo->insert(new GuardianLink(5, 42, 'Parent'))); } /** * v1 is one guardian per child. The check lives in the repository so every * caller — signup, the family screen, admin — gets it without repeating it. */ public function testInsertRefusesAChildThatAlreadyHasAGuardian(): void { $this->db->shouldReceive('get_row')->once()->andReturn((object) [ 'id' => 1, 'guardian_id' => 9, 'student_id' => 42, 'relationship' => '', 'created_at' => '2026-07-01 09:00:00', ]); $this->db->shouldNotReceive('insert'); self::assertSame(0, $this->repo->insert(new GuardianLink(5, 42))); } public function testFindByStudentReturnsNullWhenTheyBookForThemselves(): void { $this->db->shouldReceive('get_row')->once()->andReturn(null); self::assertNull($this->repo->findByStudent(42)); } public function testFindByGuardianMapsEveryRow(): void { $this->db->shouldReceive('get_results')->once()->andReturn([ (object) ['id' => 1, 'guardian_id' => 5, 'student_id' => 42, 'relationship' => '', 'created_at' => '2026-07-01 09:00:00'], (object) ['id' => 2, 'guardian_id' => 5, 'student_id' => 43, 'relationship' => '', 'created_at' => '2026-07-02 09:00:00'], ]); $links = $this->repo->findByGuardian(5); self::assertCount(2, $links); self::assertSame([42, 43], array_map(static fn (GuardianLink $l): int => $l->studentId, $links)); } public function testFindByGuardianReturnsAnEmptyListWhenTheQueryReturnsNull(): void { $this->db->shouldReceive('get_results')->once()->andReturn(null); self::assertSame([], $this->repo->findByGuardian(5)); } public function testIsGuardianOfIsTrueOnlyForALinkedPair(): void { $this->db->shouldReceive('get_var')->once()->andReturn('1'); self::assertTrue($this->repo->isGuardianOf(5, 42)); $this->db->shouldReceive('get_var')->once()->andReturn(null); self::assertFalse($this->repo->isGuardianOf(5, 99)); } public function testDeleteReportsWhetherARowWasRemoved(): void { $this->db->shouldReceive('delete') ->once() ->with('wp_us_guardians', ['guardian_id' => 5, 'student_id' => 42], ['%d', '%d']) ->andReturn(1); self::assertTrue($this->repo->delete(5, 42)); $this->db->shouldReceive('delete')->once()->andReturn(0); self::assertFalse($this->repo->delete(5, 99)); } public function testCountChildrenReturnsAnInt(): void { $this->db->shouldReceive('get_var')->once()->andReturn('2'); self::assertSame(2, $this->repo->countChildren(5)); } }