token = $this->generateTestToken(); } /** * Cleanup after test */ protected function tearDown(): void { parent::tearDown(); } /** * Generate JWT token for testing */ protected function generateTestToken(): string { $key = getenv('JWT_SECRET') ?: 'my-secret-key'; $payload = [ 'iss' => 'localhost', 'aud' => 'localhost', 'iat' => time(), 'nbf' => time(), 'exp' => time() + 3600, 'uid' => 1, 'email' => 'admin@admin.com' ]; return JWT::encode($payload, $key, 'HS256'); } /** * Make authenticated GET request */ protected function get(string $path, array $options = []) { $this->withHeaders(['Authorization' => 'Bearer ' . $this->token]); return $this->call('get', $path, $options); } /** * Make authenticated POST request */ protected function post(string $path, array $options = []) { $this->withHeaders(['Authorization' => 'Bearer ' . $this->token]); return $this->call('post', $path, $options); } /** * Make authenticated PUT request */ protected function put(string $path, array $options = []) { $this->withHeaders(['Authorization' => 'Bearer ' . $this->token]); return $this->call('put', $path, $options); } /** * Make authenticated DELETE request */ protected function delete(string $path, array $options = []) { $this->withHeaders(['Authorization' => 'Bearer ' . $this->token]); return $this->call('delete', $path, $options); } /** * Create a TEST type test definition */ protected function createTestData(): array { return [ 'SiteID' => 1, 'TestSiteCode' => $this->testSiteCode, 'TestSiteName' => 'Test Definition ' . time(), 'TestType' => self::TEST_TYPE_TEST, 'Description' => 'Test description', 'SeqScr' => 10, 'SeqRpt' => 10, 'IndentLeft' => 0, 'VisibleScr' => 1, 'VisibleRpt' => 1, 'CountStat' => 1, 'details' => [ 'DisciplineID' => 1, 'DepartmentID' => 1, 'ResultType' => 1, // Numeric 'RefType' => 1, // NMRC 'Unit1' => 'mg/dL', 'Decimal' => 2, 'Method' => 'Test Method', 'ExpectedTAT' => 60 ], 'testmap' => [ [ 'HostType' => 'HIS', 'HostID' => 'TEST001', 'HostTestCode' => 'TEST001', 'HostTestName' => 'Test (HIS)' ] ] ]; } /** * Create a PARAM type test definition */ protected function createParamData(): array { return [ 'SiteID' => 1, 'TestSiteCode' => 'PARM' . substr(time(), -4), 'TestSiteName' => 'Parameter Test ' . time(), 'TestType' => self::TEST_TYPE_PARAM, 'Description' => 'Parameter test description', 'SeqScr' => 5, 'SeqRpt' => 5, 'VisibleScr' => 1, 'VisibleRpt' => 1, 'CountStat' => 1, 'details' => [ 'DisciplineID' => 1, 'DepartmentID' => 1, 'ResultType' => 1, 'RefType' => 1, 'Unit1' => 'unit', 'Decimal' => 1, 'Method' => 'Parameter Method' ] ]; } /** * Create a GROUP type test definition with members */ protected function createGroupData(array $memberIds = []): array { return [ 'SiteID' => 1, 'TestSiteCode' => 'GRUP' . substr(time(), -4), 'TestSiteName' => 'Group Test ' . time(), 'TestType' => self::TEST_TYPE_GROUP, 'Description' => 'Group test description', 'SeqScr' => 100, 'SeqRpt' => 100, 'VisibleScr' => 1, 'VisibleRpt' => 1, 'CountStat' => 1, 'Members' => $memberIds ?: [1, 2], 'testmap' => [ [ 'HostType' => 'LIS', 'HostID' => 'LIS001', 'HostTestCode' => 'PANEL', 'HostTestName' => 'Test Panel (LIS)' ] ] ]; } /** * Create a CALC type test definition */ protected function createCalcData(): array { return [ 'SiteID' => 1, 'TestSiteCode' => 'CALC' . substr(time(), -4), 'TestSiteName' => 'Calculated Test ' . time(), 'TestType' => self::TEST_TYPE_CALC, 'Description' => 'Calculated test description', 'SeqScr' => 50, 'SeqRpt' => 50, 'VisibleScr' => 1, 'VisibleRpt' => 1, 'CountStat' => 1, 'details' => [ 'DisciplineID' => 1, 'DepartmentID' => 1, 'FormulaInput' => '["TEST1", "TEST2"]', 'FormulaCode' => 'TEST1 + TEST2', 'FormulaLang' => 'SQL', 'RefType' => 1, 'Unit1' => 'mg/dL', 'Decimal' => 0, 'Method' => 'Calculation Method' ], 'testmap' => [ [ 'HostType' => 'LIS', 'HostID' => 'LIS001', 'HostTestCode' => 'CALCR', 'HostTestName' => 'Calculated Result (LIS)' ] ] ]; } /** * Assert API response has success status */ protected function assertSuccessResponse($response, string $message = 'Response should be successful'): void { $body = json_decode($response->response()->getBody(), true); $this->assertArrayHasKey('status', $body, $message); $this->assertEquals('success', $body['status'], $message); } /** * Assert API response has error status */ protected function assertErrorResponse($response, string $message = 'Response should be an error'): void { $body = json_decode($response->response()->getBody(), true); $this->assertArrayHasKey('status', $body, $message); $this->assertNotEquals('success', $body['status'], $message); } /** * Assert response has data key */ protected function assertHasData($response, string $message = 'Response should have data'): void { $body = json_decode($response->response()->getBody(), true); $this->assertArrayHasKey('data', $body, $message); } /** * Get test type name from VID */ protected function getTestTypeName(int $vid): string { return match ($vid) { self::TEST_TYPE_TEST => 'TEST', self::TEST_TYPE_PARAM => 'PARAM', self::TEST_TYPE_CALC => 'CALC', self::TEST_TYPE_GROUP => 'GROUP', self::TEST_TYPE_TITLE => 'TITLE', default => 'UNKNOWN' }; } /** * Skip test if database not available */ protected function requireDatabase(): void { $db = \Config\Database::connect(); try { $db->connect(); } catch (\Exception $e) { $this->markTestSkipped('Database not available: ' . $e->getMessage()); } } /** * Skip test if required seeded data not found */ protected function requireSeededData(): void { $db = \Config\Database::connect(); $count = $db->table('valueset') ->where('VSetID', self::VALUESET_TEST_TYPE) ->countAllResults(); if ($count === 0) { $this->markTestSkipped('Test type valuesets not seeded'); } } }