db = Mockery::mock(\wpdb::class); $this->db->prefix = 'wp_'; $this->repo = new GroupAccessRepository($this->db); } public function testInsertReturnsId(): void { Functions\expect('current_time')->with('mysql')->andReturn('2026-06-02 09:00:00'); $this->db->shouldReceive('insert') ->once() ->with( 'wp_us_group_access', Mockery::on(static function (array $d): bool { return $d['offering_id'] === 8 && $d['student_id'] === 5 && $d['status'] === GroupAccess::STATUS_INVITED; }), ['%d', '%d', '%s', '%d', '%s', '%d', '%s'] ); $this->db->insert_id = 3; self::assertSame(3, $this->repo->insert(new GroupAccess(offeringId: 8, studentId: 5, invitedBy: 2))); } public function testHasGrantTrueWhenCountPositive(): void { $this->db->shouldReceive('prepare') ->once() ->with( Mockery::pattern('/offering_id = %d AND student_id = %d AND status IN/'), 'wp_us_group_access', 8, 5, GroupAccess::STATUS_INVITED, GroupAccess::STATUS_ENROLLED ) ->andReturn('SELECT ...'); $this->db->shouldReceive('get_var')->andReturn('1'); self::assertTrue($this->repo->hasGrant(8, 5)); } public function testHasGrantFalseWhenZero(): void { $this->db->shouldReceive('prepare')->andReturn('SELECT ...'); $this->db->shouldReceive('get_var')->andReturn('0'); self::assertFalse($this->repo->hasGrant(8, 5)); } public function testFindGrantedOfferingIdsReturnsInts(): void { $this->db->shouldReceive('prepare') ->once() ->with( Mockery::pattern('/DISTINCT offering_id.*student_id = %d/s'), 'wp_us_group_access', 5, GroupAccess::STATUS_INVITED, GroupAccess::STATUS_ENROLLED ) ->andReturn('SELECT ...'); $this->db->shouldReceive('get_col')->andReturn(['8', '9']); self::assertSame([8, 9], $this->repo->findGrantedOfferingIds(5)); } public function testFindByOfferingMapsRows(): void { $this->db->shouldReceive('prepare')->andReturn('SELECT ...'); $this->db->shouldReceive('get_results')->andReturn([ (object) [ 'id' => '1', 'offering_id' => '8', 'student_id' => '5', 'email' => '', 'invite_id' => null, 'status' => GroupAccess::STATUS_INVITED, 'invited_by' => '2', ], ]); $grants = $this->repo->findByOffering(8); self::assertCount(1, $grants); self::assertSame(5, $grants[0]->studentId); } public function testLinkStudentByEmailUpdatesNullStudentRows(): void { $this->db->shouldReceive('prepare') ->once() ->with( Mockery::pattern('/UPDATE %i SET student_id = %d WHERE email = %s AND student_id IS NULL/'), 'wp_us_group_access', 5, 'a@b.test' ) ->andReturn('UPDATE ...'); $this->db->shouldReceive('query')->once()->with('UPDATE ...')->andReturn(1); self::assertTrue($this->repo->linkStudentByEmail('a@b.test', 5)); } public function testLinkStudentByEmailIgnoresEmptyEmail(): void { $this->db->shouldReceive('prepare')->never(); self::assertFalse($this->repo->linkStudentByEmail('', 5)); } public function testMarkEnrolledUpdatesStatus(): void { $this->db->shouldReceive('update') ->once() ->with( 'wp_us_group_access', ['status' => GroupAccess::STATUS_ENROLLED], ['offering_id' => 8, 'student_id' => 5], ['%s'], ['%d', '%d'] ) ->andReturn(1); self::assertTrue($this->repo->markEnrolled(8, 5)); } public function testRevokeUpdatesStatus(): void { $this->db->shouldReceive('update') ->once() ->with('wp_us_group_access', ['status' => GroupAccess::STATUS_REVOKED], ['id' => 3], ['%s'], ['%d']) ->andReturn(1); self::assertTrue($this->repo->revoke(3)); } }