- Rename all controllers from X.php to XController.php format - Add new RefTxtModel for text-based reference ranges - Rename group_dialog.php to grp_dialog.php and remove title_dialog.php - Add comprehensive test suite for v2/master/TestDef module - Update Routes.php to reflect controller renames - Remove obsolete data files (clqms_v2.sql, lab.dbml)
326 lines
8.0 KiB
PHP
326 lines
8.0 KiB
PHP
<?php
|
|
|
|
namespace Tests\Support\v2;
|
|
|
|
use CodeIgniter\Test\CIUnitTestCase;
|
|
use CodeIgniter\Test\FeatureTestTrait;
|
|
use Firebase\JWT\JWT;
|
|
|
|
/**
|
|
* Base test case for v2 Master Data tests
|
|
*
|
|
* Provides common setup, authentication, and helper methods
|
|
* for all v2 master test feature and unit tests.
|
|
*/
|
|
abstract class MasterTestCase extends CIUnitTestCase
|
|
{
|
|
use FeatureTestTrait;
|
|
|
|
/**
|
|
* JWT token for authentication
|
|
*/
|
|
protected ?string $token = null;
|
|
|
|
/**
|
|
* Test site ID
|
|
*/
|
|
protected int $testSiteId = 1;
|
|
|
|
/**
|
|
* Test site code
|
|
*/
|
|
protected string $testSiteCode = 'TEST01';
|
|
|
|
/**
|
|
* Valueset IDs for test types
|
|
*/
|
|
public const VALUESET_TEST_TYPE = 27; // VSetID for Test Types
|
|
public const VALUESET_RESULT_TYPE = 43; // VSetID for Result Types
|
|
public const VALUESET_REF_TYPE = 44; // VSetID for Reference Types
|
|
public const VALUESET_ENTITY_TYPE = 39; // VSetID for Entity Types
|
|
|
|
/**
|
|
* Test Type VIDs
|
|
*/
|
|
public const TEST_TYPE_TEST = 1; // VID for TEST
|
|
public const TEST_TYPE_PARAM = 2; // VID for PARAM
|
|
public const TEST_TYPE_CALC = 3; // VID for CALC
|
|
public const TEST_TYPE_GROUP = 4; // VID for GROUP
|
|
public const TEST_TYPE_TITLE = 5; // VID for TITLE
|
|
|
|
/**
|
|
* Setup test environment
|
|
*/
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
$this->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');
|
|
}
|
|
}
|
|
}
|