payload; } public function getPost($index = null, $filter = null, $flags = null) { if ($index === null) { return $this->payload; } return $this->payload[$index] ?? null; } } class TestableAuthController extends AuthController { public function __construct() { // Skip the real constructor to avoid bootstrapping live models } public function setRequest(RequestInterface $request): self { $this->request = $request; return $this; } public function setResponse(ResponseInterface $response): self { $this->response = $response; return $this; } public function setUserModel(UserModel $model): self { $this->userModel = $model; return $this; } public function setIpAttemptModel(IpAttemptModel $model): self { $this->ipAttemptModel = $model; return $this; } public function setLoginActivityModel(LoginActivityModel $model): self { $this->loginActivityModel = $model; return $this; } protected function getUserRoleNames(int $userId): array { return ['admin']; } } class AuthControllerTest extends CIUnitTestCase { private TestableAuthController $controller; private UserModel $userModel; private IpAttemptModel $ipAttemptModel; private LoginActivityModel $loginActivityModel; protected function setUp(): void { parent::setUp(); helper('security'); putenv('JWT_SECRET=test-secret-for-auth-controller'); $_ENV['JWT_SECRET'] = 'test-secret-for-auth-controller'; $_SERVER['JWT_SECRET'] = 'test-secret-for-auth-controller'; $this->controller = new TestableAuthController(); $this->controller->setResponse(service('response')); $this->userModel = $this->getMockBuilder(UserModel::class) ->disableOriginalConstructor() ->addMethods(['where']) ->onlyMethods(['first', 'update']) ->getMock(); $this->ipAttemptModel = $this->getMockBuilder(IpAttemptModel::class) ->disableOriginalConstructor() ->addMethods(['where']) ->onlyMethods(['first']) ->getMock(); $this->loginActivityModel = $this->getMockBuilder(LoginActivityModel::class) ->disableOriginalConstructor() ->onlyMethods(['insert']) ->getMock(); $this->controller->setUserModel($this->userModel); $this->controller->setIpAttemptModel($this->ipAttemptModel); $this->controller->setLoginActivityModel($this->loginActivityModel); } public function testApiLoginReturnsTokenAndRoles() { $payload = [ 'email' => 'admin@example.com', 'password' => '12345678', ]; $user = [ 'id' => 16, 'email' => $payload['email'], 'firstname' => 'Admin', 'lastname' => 'Panel', 'password' => pbkdf2_hash($payload['password']), 'is_suspended' => false, 'user_type' => 'admin', ]; $this->ipAttemptModel->method('where')->willReturnSelf(); $this->ipAttemptModel->method('first')->willReturn(null); $this->userModel->method('where')->willReturnSelf(); $this->userModel->method('first')->willReturn($user); $this->userModel->method('update')->willReturn(true); $this->loginActivityModel->method('insert')->willReturn(true); $this->controller->setRequest(new FakeAuthJsonRequest($payload)); $response = $this->controller->apiLogin(); $body = json_decode((string) $response->getBody(), true); $this->assertIsArray($body); $this->assertTrue($body['status'], json_encode($body)); $this->assertArrayHasKey('token', $body); $this->assertArrayHasKey('user', $body); $this->assertSame($user['id'], $body['user']['id']); $this->assertArrayHasKey('roles', $body['user']); $this->assertTrue($body['user']['roles']['admin'] ?? false); $this->assertIsString($body['token']); } }