'localhost', 'aud' => 'localhost', 'iat' => time(), 'nbf' => time(), 'exp' => time() + 3600, 'uid' => 1, 'email' => 'admin@admin.com' ]; $this->token = JWT::encode($payload, $key, 'HS256'); } protected function callProtected($method, $path, $params = []) { return $this->withHeaders(['Cookie' => 'token=' . $this->token]) ->call($method, $path, $params); } public function testIndexReturnsSuccess() { $result = $this->callProtected('get', 'api/tests'); $result->assertStatus(200); $json = $result->getJSON(); $data = json_decode($json, true); $this->assertEquals('success', $data['status']); $this->assertIsArray($data['data']); } public function testShowReturnsDataIfFound() { // First get an ID $indexResult = $this->callProtected('get', 'api/tests'); $indexData = json_decode($indexResult->getJSON(), true); if (empty($indexData['data'])) { $this->markTestSkipped('No test definitions found in database to test show.'); } $id = $indexData['data'][0]['TestSiteID']; $result = $this->callProtected('get', "api/tests/$id"); $result->assertStatus(200); $json = $result->getJSON(); $data = json_decode($json, true); $this->assertEquals('success', $data['status']); $this->assertIsArray($data['data']); $this->assertEquals($id, $data['data']['TestSiteID']); } public function testCreateTestWithThreshold() { $testData = [ 'TestSiteCode' => 'TH' . substr(time(), -4), 'TestSiteName' => 'Threshold Test ' . time(), 'TestType' => 'TEST', 'SiteID' => 1, 'details' => [ 'RefType' => 'THOLD', 'ResultType' => 'NMRIC' ], 'refnum' => [ [ 'NumRefType' => 'THOLD', 'RangeType' => 'VALUE', 'Sex' => '1', 'AgeStart' => 0, 'AgeEnd' => 100, 'LowSign' => '>', 'Low' => 5.5, 'Interpretation' => 'High' ] ] ]; $result = $this->withHeaders(['Cookie' => 'token=' . $this->token]) ->withBody(json_encode($testData)) ->call('post', 'api/tests'); $result->assertStatus(201); $json = $result->getJSON(); $data = json_decode($json, true); $this->assertEquals('created', $data['status']); $id = $data['data']['TestSiteId']; // Verify retrieval $showResult = $this->callProtected('get', "api/tests/$id"); $showData = json_decode($showResult->getJSON(), true); $this->assertArrayHasKey('refnum', $showData['data']); $this->assertCount(1, $showData['data']['refnum']); $this->assertEquals(5.5, $showData['data']['refnum'][0]['Low']); $this->assertEquals('High', $showData['data']['refnum'][0]['Interpretation']); } /** * Test valid TestType and ResultType combinations * @dataProvider validTestTypeResultTypeProvider */ public function testValidTestTypeResultTypeCombinations($testType, $resultType, $refType, $shouldSucceed) { $testData = [ 'TestSiteCode' => 'TT' . substr(time(), -4) . rand(10, 99), 'TestSiteName' => 'Type Test ' . time(), 'TestType' => $testType, 'SiteID' => 1, 'details' => [ 'ResultType' => $resultType, 'RefType' => $refType ] ]; // Add reference data if needed if ($refType === 'RANGE' || $refType === 'THOLD') { $testData['refnum'] = [ [ 'NumRefType' => $refType, 'RangeType' => 'VALUE', 'Sex' => '1', 'AgeStart' => 0, 'AgeEnd' => 100, 'LowSign' => '>', 'Low' => 5.5, 'Interpretation' => 'Normal' ] ]; } elseif ($refType === 'VSET' || $refType === 'TEXT') { $testData['reftxt'] = [ [ 'TxtRefType' => $refType, 'Sex' => '1', 'AgeStart' => 0, 'AgeEnd' => 100, 'RefTxt' => 'Normal range text', 'Flag' => 'N' ] ]; } $result = $this->withHeaders(['Cookie' => 'token=' . $this->token]) ->withBody(json_encode($testData)) ->call('post', 'api/tests'); if ($shouldSucceed) { $result->assertStatus(201); $data = json_decode($result->getJSON(), true); $this->assertEquals('created', $data['status']); } else { // Invalid combinations should fail validation or return error $this->assertGreaterThanOrEqual(400, $result->getStatusCode()); } } public function validTestTypeResultTypeProvider() { return [ // TEST type - can have NMRIC, RANGE, TEXT, VSET 'TEST with NMRIC' => ['TEST', 'NMRIC', 'RANGE', true], 'TEST with RANGE' => ['TEST', 'RANGE', 'RANGE', true], 'TEST with TEXT' => ['TEST', 'TEXT', 'TEXT', true], 'TEST with VSET' => ['TEST', 'VSET', 'VSET', true], 'TEST with THOLD' => ['TEST', 'NMRIC', 'THOLD', true], // PARAM type - can have NMRIC, RANGE, TEXT, VSET 'PARAM with NMRIC' => ['PARAM', 'NMRIC', 'RANGE', true], 'PARAM with RANGE' => ['PARAM', 'RANGE', 'RANGE', true], 'PARAM with TEXT' => ['PARAM', 'TEXT', 'TEXT', true], 'PARAM with VSET' => ['PARAM', 'VSET', 'VSET', true], // CALC type - only NMRIC 'CALC with NMRIC' => ['CALC', 'NMRIC', 'RANGE', true], // GROUP type - only NORES 'GROUP with NORES' => ['GROUP', 'NORES', 'NOREF', true], // TITLE type - only NORES 'TITLE with NORES' => ['TITLE', 'NORES', 'NOREF', true], ]; } /** * Test ResultType to RefType mapping * @dataProvider resultTypeToRefTypeProvider */ public function testResultTypeToRefTypeMapping($resultType, $refType, $expectedRefTable) { $testData = [ 'TestSiteCode' => 'RT' . substr(time(), -4) . rand(10, 99), 'TestSiteName' => 'RefType Test ' . time(), 'TestType' => 'TEST', 'SiteID' => 1, 'details' => [ 'ResultType' => $resultType, 'RefType' => $refType ] ]; // Add appropriate reference data if ($expectedRefTable === 'refnum') { $testData['refnum'] = [ [ 'NumRefType' => $refType, 'RangeType' => 'VALUE', 'Sex' => '1', 'AgeStart' => 18, 'AgeEnd' => 99, 'LowSign' => 'GE', 'Low' => 10, 'HighSign' => 'LE', 'High' => 20, 'Interpretation' => 'Normal' ] ]; } elseif ($expectedRefTable === 'reftxt') { $testData['reftxt'] = [ [ 'TxtRefType' => $refType, 'Sex' => '1', 'AgeStart' => 18, 'AgeEnd' => 99, 'RefTxt' => 'Reference text', 'Flag' => 'N' ] ]; } $result = $this->withHeaders(['Cookie' => 'token=' . $this->token]) ->withBody(json_encode($testData)) ->call('post', 'api/tests'); $result->assertStatus(201); $data = json_decode($result->getJSON(), true); $id = $data['data']['TestSiteId']; // Verify the reference data was stored in correct table $showResult = $this->callProtected('get', "api/tests/$id"); $showData = json_decode($showResult->getJSON(), true); if ($expectedRefTable === 'refnum') { $this->assertArrayHasKey('refnum', $showData['data']); $this->assertNotEmpty($showData['data']['refnum']); } elseif ($expectedRefTable === 'reftxt') { $this->assertArrayHasKey('reftxt', $showData['data']); $this->assertNotEmpty($showData['data']['reftxt']); } } public function resultTypeToRefTypeProvider() { return [ // NMRIC with RANGE → refnum table 'NMRIC with RANGE uses refnum' => ['NMRIC', 'RANGE', 'refnum'], // NMRIC with THOLD → refnum table 'NMRIC with THOLD uses refnum' => ['NMRIC', 'THOLD', 'refnum'], // RANGE with RANGE → refnum table 'RANGE with RANGE uses refnum' => ['RANGE', 'RANGE', 'refnum'], // RANGE with THOLD → refnum table 'RANGE with THOLD uses refnum' => ['RANGE', 'THOLD', 'refnum'], // VSET with VSET → reftxt table 'VSET with VSET uses reftxt' => ['VSET', 'VSET', 'reftxt'], // TEXT with TEXT → reftxt table 'TEXT with TEXT uses reftxt' => ['TEXT', 'TEXT', 'reftxt'], ]; } /** * Test CALC type always has NMRIC result type */ public function testCalcTypeAlwaysHasNmricResultType() { $testData = [ 'TestSiteCode' => 'CALC' . substr(time(), -4), 'TestSiteName' => 'Calc Test ' . time(), 'TestType' => 'CALC', 'SiteID' => 1, 'details' => [ 'DisciplineID' => 1, 'DepartmentID' => 1, 'FormulaCode' => 'WEIGHT/(HEIGHT/100)^2', 'Unit1' => 'kg/m2', 'Decimal' => 1 ], 'members' => [] ]; $result = $this->withHeaders(['Cookie' => 'token=' . $this->token]) ->withBody(json_encode($testData)) ->call('post', 'api/tests'); $result->assertStatus(201); $data = json_decode($result->getJSON(), true); $id = $data['data']['TestSiteId']; // Verify CALC test was created $showResult = $this->callProtected('get', "api/tests/$id"); $showData = json_decode($showResult->getJSON(), true); $this->assertEquals('CALC', $showData['data']['TestType']); $this->assertArrayHasKey('testdefcal', $showData['data']); } /** * Test GROUP type has no result (NORES) */ public function testGroupTypeHasNoResult() { // First create member tests $member1Data = [ 'TestSiteCode' => 'M1' . substr(time(), -4), 'TestSiteName' => 'Member 1 ' . time(), 'TestType' => 'TEST', 'SiteID' => 1, 'details' => [ 'ResultType' => 'NMRIC', 'RefType' => 'RANGE' ], 'refnum' => [ [ 'NumRefType' => 'RANGE', 'RangeType' => 'VALUE', 'Sex' => '1', 'AgeStart' => 0, 'AgeEnd' => 100, 'LowSign' => '>', 'Low' => 5.5, 'Interpretation' => 'Normal' ] ] ]; $result1 = $this->withHeaders(['Cookie' => 'token=' . $this->token]) ->withBody(json_encode($member1Data)) ->call('post', 'api/tests'); $data1 = json_decode($result1->getJSON(), true); $member1Id = $data1['data']['TestSiteId']; $member2Data = [ 'TestSiteCode' => 'M2' . substr(time(), -4), 'TestSiteName' => 'Member 2 ' . time(), 'TestType' => 'TEST', 'SiteID' => 1, 'details' => [ 'ResultType' => 'NMRIC', 'RefType' => 'RANGE' ], 'refnum' => [ [ 'NumRefType' => 'RANGE', 'RangeType' => 'VALUE', 'Sex' => '1', 'AgeStart' => 0, 'AgeEnd' => 100, 'LowSign' => '>', 'Low' => 5.5, 'Interpretation' => 'Normal' ] ] ]; $result2 = $this->withHeaders(['Cookie' => 'token=' . $this->token]) ->withBody(json_encode($member2Data)) ->call('post', 'api/tests'); $data2 = json_decode($result2->getJSON(), true); $member2Id = $data2['data']['TestSiteId']; // Create group test $groupData = [ 'TestSiteCode' => 'GRP' . substr(time(), -4), 'TestSiteName' => 'Group Test ' . time(), 'TestType' => 'GROUP', 'SiteID' => 1, 'details' => [ 'ResultType' => 'NORES', 'RefType' => 'NOREF' ], 'members' => [$member1Id, $member2Id] ]; $result = $this->withHeaders(['Cookie' => 'token=' . $this->token]) ->withBody(json_encode($groupData)) ->call('post', 'api/tests'); $result->assertStatus(201); $data = json_decode($result->getJSON(), true); $id = $data['data']['TestSiteId']; // Verify GROUP test was created with members $showResult = $this->callProtected('get', "api/tests/$id"); $showData = json_decode($showResult->getJSON(), true); $this->assertEquals('GROUP', $showData['data']['TestType']); $this->assertArrayHasKey('testdefgrp', $showData['data']); } }