'localhost', 'aud' => 'localhost', 'iat' => time(), 'nbf' => time(), 'exp' => time() + 3600, 'uid' => 1, 'email' => 'admin@admin.com', ]; $this->token = JWT::encode($payload, $key, 'HS256'); } private function authHeaders(): array { return ['Cookie' => 'token=' . $this->token]; } private function findResultByOrderAndSite(int $internalOID, int $testSiteID): ?array { $resultModel = new PatResultModel(); return $resultModel->where('OrderID', $internalOID) ->where('TestSiteID', $testSiteID) ->where('DelDate', null) ->orderBy('ResultID', 'DESC') ->first(); } private function findRowBySite(array $rows, int $testSiteID): ?array { foreach ($rows as $row) { if ((int) ($row['TestSiteID'] ?? 0) === $testSiteID) { return $row; } } return null; } private function createOrderWithTest(): array { $patientModel = new PatientModel(); $patient = $patientModel->where('DelDate', null)->first(); $this->assertNotEmpty($patient, 'No active patient found for order patch test.'); $testModel = new TestDefSiteModel(); $tests = $testModel->where('EndDate', null) ->where('TestType', 'TEST') ->findAll(2); if (count($tests) < 2) { $tests = $testModel->where('EndDate', null)->findAll(2); } $this->assertGreaterThanOrEqual(2, count($tests), 'Need at least 2 test definitions for lifecycle patch test.'); $response = $this->withHeaders($this->authHeaders()) ->withBodyFormat('json') ->call('post', $this->endpoint, [ 'InternalPID' => (int) $patient['InternalPID'], 'SiteID' => (int) ($tests[0]['SiteID'] ?? 1), 'Tests' => [ [ 'TestSiteID' => (int) $tests[0]['TestSiteID'], ], ], ]); $response->assertStatus(201); $decoded = json_decode($response->getJSON(), true); $this->assertSame('success', $decoded['status'] ?? null); return [ 'order' => $decoded['data'], 'baseTest' => $tests[0], 'extraTest' => $tests[1], ]; } public function testPatchTestsLifecycleAddedEditedDeleted(): void { $fixture = $this->createOrderWithTest(); $order = $fixture['order']; $baseTest = $fixture['baseTest']; $extraTest = $fixture['extraTest']; $orderID = $order['OrderID']; $internalOID = (int) $order['InternalOID']; $baseSiteID = (int) $baseTest['TestSiteID']; $extraSiteID = (int) $extraTest['TestSiteID']; $addResponse = $this->withHeaders($this->authHeaders()) ->withBodyFormat('json') ->call('patch', "{$this->endpoint}/{$orderID}", [ 'Tests' => [ 'added' => [ [ 'TestSiteID' => (string) $extraSiteID, 'Result' => 'ADDED', ], ], ], ]); $addResponse->assertStatus(200); $addJson = json_decode($addResponse->getJSON(), true); $this->assertSame('success', $addJson['status'] ?? null); $addedRow = $this->findRowBySite($addJson['data']['Tests'] ?? [], $extraSiteID); $this->assertNotNull($addedRow, 'Added test not returned by patch response.'); $this->assertSame('ADDED', $addedRow['Result'] ?? null); $addedResultID = (int) ($addedRow['ResultID'] ?? 0); $this->assertGreaterThan(0, $addedResultID, 'Added test missing ResultID.'); $addedDbRow = $this->findResultByOrderAndSite($internalOID, $extraSiteID); $this->assertNotNull($addedDbRow, 'Added test not found in DB.'); $this->assertSame('ADDED', $addedDbRow['Result'] ?? null); $editResponse = $this->withHeaders($this->authHeaders()) ->withBodyFormat('json') ->call('patch', "{$this->endpoint}/{$orderID}", [ 'Tests' => [ 'edited' => [ [ 'TestSiteID' => (string) $baseSiteID, 'Result' => 'EDITED', ], ], ], ]); $editResponse->assertStatus(200); $editJson = json_decode($editResponse->getJSON(), true); $this->assertSame('success', $editJson['status'] ?? null); $editedRow = $this->findRowBySite($editJson['data']['Tests'] ?? [], $baseSiteID); $this->assertNotNull($editedRow, 'Edited test not returned by patch response.'); $this->assertSame('EDITED', $editedRow['Result'] ?? null); $deleteResponse = $this->withHeaders($this->authHeaders()) ->withBodyFormat('json') ->call('patch', "{$this->endpoint}/{$orderID}", [ 'Tests' => [ 'deleted' => [ [ 'TestSiteID' => (string) $extraSiteID, ], ], ], ]); $deleteResponse->assertStatus(200); $deleteJson = json_decode($deleteResponse->getJSON(), true); $this->assertSame('success', $deleteJson['status'] ?? null); $this->assertNull($this->findRowBySite($deleteJson['data']['Tests'] ?? [], $extraSiteID), 'Deleted test still returned by patch response.'); $deletedDbRow = (new PatResultModel())->find($addedResultID); $this->assertNotNull($deletedDbRow, 'Deleted test row missing from DB.'); $this->assertNotNull($deletedDbRow['DelDate'] ?? null, 'Deleted test row not soft deleted.'); } public function testPatchTestsNotFound(): void { $response = $this->withHeaders($this->authHeaders()) ->withBodyFormat('json') ->call('patch', "{$this->endpoint}/999999999", [ 'Tests' => [ 'deleted' => [1], ], ]); $response->assertStatus(404); } public function testPatchTestsInvalidId(): void { $response = $this->withHeaders($this->authHeaders()) ->withBodyFormat('json') ->call('patch', "{$this->endpoint}/invalid", [ 'Tests' => [ 'deleted' => [1], ], ]); $response->assertStatus(404); } public function testPatchTestsEmptyPayload(): void { $fixture = $this->createOrderWithTest(); $orderID = $fixture['order']['OrderID']; $response = $this->withHeaders($this->authHeaders()) ->withBodyFormat('json') ->call('patch', "{$this->endpoint}/{$orderID}", []); $response->assertStatus(400); } }