Support test lifecycle updates in order patch
- add Tests delta handling to order patch endpoint\n- create/edit/soft-delete patres rows in transaction\n- update OpenAPI request schema for Tests payload\n- add feature coverage for create/edit/delete lifecycle
This commit is contained in:
parent
e869ec4ac4
commit
6b4ffe017c
@ -220,22 +220,63 @@ class OrderTestController extends Controller {
|
|||||||
return $this->failValidationErrors(['OrderID' => 'OrderID in URL does not match body']);
|
return $this->failValidationErrors(['OrderID' => 'OrderID in URL does not match body']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$transactionStarted = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$input['OrderID'] = $OrderID;
|
|
||||||
$order = $this->model->getOrder($OrderID);
|
$order = $this->model->getOrder($OrderID);
|
||||||
if (!$order) {
|
if (!$order) {
|
||||||
return $this->failNotFound('Order not found');
|
return $this->failNotFound('Order not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
$updateData = [];
|
$updateData = [];
|
||||||
if (isset($input['Priority'])) $updateData['Priority'] = $input['Priority'];
|
if (array_key_exists('Priority', $input)) {
|
||||||
if (isset($input['OrderStatus'])) $updateData['OrderStatus'] = $input['OrderStatus'];
|
$updateData['Priority'] = $input['Priority'];
|
||||||
if (isset($input['OrderingProvider'])) $updateData['OrderingProvider'] = $input['OrderingProvider'];
|
}
|
||||||
if (isset($input['DepartmentID'])) $updateData['DepartmentID'] = $input['DepartmentID'];
|
if (array_key_exists('OrderStatus', $input)) {
|
||||||
if (isset($input['WorkstationID'])) $updateData['WorkstationID'] = $input['WorkstationID'];
|
$updateData['OrderStatus'] = $input['OrderStatus'];
|
||||||
|
}
|
||||||
|
if (array_key_exists('OrderingProvider', $input)) {
|
||||||
|
$updateData['OrderingProvider'] = $input['OrderingProvider'];
|
||||||
|
}
|
||||||
|
if (array_key_exists('DepartmentID', $input)) {
|
||||||
|
$updateData['DepartmentID'] = $input['DepartmentID'];
|
||||||
|
}
|
||||||
|
if (array_key_exists('WorkstationID', $input)) {
|
||||||
|
$updateData['WorkstationID'] = $input['WorkstationID'];
|
||||||
|
}
|
||||||
|
|
||||||
if (!empty($updateData)) {
|
$testsDelta = null;
|
||||||
$this->model->update($order['InternalOID'], $updateData);
|
if (array_key_exists('Tests', $input)) {
|
||||||
|
$testsDelta = $this->normalizeTestsDelta($input['Tests']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($updateData) && $testsDelta === null) {
|
||||||
|
return $this->failValidationErrors(['error' => 'Update payload is required']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$hasUpdates = !empty($updateData) || $testsDelta !== null;
|
||||||
|
if ($hasUpdates) {
|
||||||
|
$this->db->transStart();
|
||||||
|
$transactionStarted = true;
|
||||||
|
|
||||||
|
if (!empty($updateData)) {
|
||||||
|
$updated = $this->model->update($order['InternalOID'], $updateData);
|
||||||
|
if (!$updated) {
|
||||||
|
throw new \RuntimeException('Failed to update order');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($testsDelta !== null) {
|
||||||
|
$this->model->applyTestDelta((int) $order['InternalOID'], $testsDelta, $order);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->db->transComplete();
|
||||||
|
|
||||||
|
if ($this->db->transStatus() === false) {
|
||||||
|
throw new \RuntimeException('Transaction failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
$transactionStarted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$updatedOrder = $this->model->getOrder($OrderID);
|
$updatedOrder = $this->model->getOrder($OrderID);
|
||||||
@ -247,11 +288,51 @@ class OrderTestController extends Controller {
|
|||||||
'message' => 'Order updated successfully',
|
'message' => 'Order updated successfully',
|
||||||
'data' => $updatedOrder
|
'data' => $updatedOrder
|
||||||
], 200);
|
], 200);
|
||||||
|
} catch (\InvalidArgumentException $e) {
|
||||||
|
if ($transactionStarted) {
|
||||||
|
$this->db->transRollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->failValidationErrors(['Tests' => $e->getMessage()]);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
if ($transactionStarted) {
|
||||||
|
$this->db->transRollback();
|
||||||
|
}
|
||||||
|
|
||||||
return $this->failServerError('Something went wrong: ' . $e->getMessage());
|
return $this->failServerError('Something went wrong: ' . $e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function normalizeTestsDelta($tests): ?array {
|
||||||
|
if (!is_array($tests)) {
|
||||||
|
throw new \InvalidArgumentException('Tests must be an object');
|
||||||
|
}
|
||||||
|
|
||||||
|
$delta = [
|
||||||
|
'created' => [],
|
||||||
|
'edited' => [],
|
||||||
|
'deleted' => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach (array_keys($delta) as $key) {
|
||||||
|
if (!array_key_exists($key, $tests)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_array($tests[$key])) {
|
||||||
|
throw new \InvalidArgumentException(ucfirst($key) . ' tests must be an array');
|
||||||
|
}
|
||||||
|
|
||||||
|
$delta[$key] = $tests[$key];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($delta['created']) && empty($delta['edited']) && empty($delta['deleted'])) {
|
||||||
|
throw new \InvalidArgumentException('Tests delta is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $delta;
|
||||||
|
}
|
||||||
|
|
||||||
public function delete() {
|
public function delete() {
|
||||||
$input = $this->request->getJSON(true);
|
$input = $this->request->getJSON(true);
|
||||||
$orderID = $input['OrderID'] ?? null;
|
$orderID = $input['OrderID'] ?? null;
|
||||||
|
|||||||
@ -231,6 +231,168 @@ class OrderTestModel extends BaseModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function applyTestDelta(int $orderOID, array $testsDelta, ?array $order = null): array {
|
||||||
|
$order ??= $this->find($orderOID);
|
||||||
|
if (!$order) {
|
||||||
|
throw new \InvalidArgumentException('Order not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
$summary = [
|
||||||
|
'created' => [],
|
||||||
|
'edited' => [],
|
||||||
|
'deleted' => [],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach (($testsDelta['created'] ?? []) as $item) {
|
||||||
|
if (!is_array($item)) {
|
||||||
|
throw new \InvalidArgumentException('Invalid created test payload');
|
||||||
|
}
|
||||||
|
|
||||||
|
$summary['created'][] = $this->createTestRow($order, $item);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (($testsDelta['edited'] ?? []) as $item) {
|
||||||
|
if (!is_array($item)) {
|
||||||
|
throw new \InvalidArgumentException('Invalid edited test payload');
|
||||||
|
}
|
||||||
|
|
||||||
|
$summary['edited'][] = $this->editTestRow($orderOID, $item);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (($testsDelta['deleted'] ?? []) as $item) {
|
||||||
|
$testSiteID = is_array($item) ? (int) ($item['TestSiteID'] ?? 0) : (int) $item;
|
||||||
|
if ($testSiteID <= 0) {
|
||||||
|
throw new \InvalidArgumentException('TestSiteID is required for deleted tests');
|
||||||
|
}
|
||||||
|
|
||||||
|
$summary['deleted'][] = $this->deleteTestRow($orderOID, $testSiteID);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $summary;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createTestRow(array $order, array $item): array {
|
||||||
|
$testSiteID = (int) ($item['TestSiteID'] ?? 0);
|
||||||
|
if ($testSiteID <= 0) {
|
||||||
|
throw new \InvalidArgumentException('TestSiteID is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
$testSite = $this->db->table('testdefsite')
|
||||||
|
->select('TestSiteID, TestSiteCode')
|
||||||
|
->where('TestSiteID', $testSiteID)
|
||||||
|
->where('EndDate', null)
|
||||||
|
->get()
|
||||||
|
->getRowArray();
|
||||||
|
|
||||||
|
if (!$testSite) {
|
||||||
|
throw new \InvalidArgumentException('Test site not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
$payload = [
|
||||||
|
'OrderID' => (int) $order['InternalOID'],
|
||||||
|
'TestSiteID' => $testSiteID,
|
||||||
|
'TestSiteCode' => $testSite['TestSiteCode'],
|
||||||
|
'SID' => (string) ($order['OrderID'] ?? ''),
|
||||||
|
'SampleID' => (string) ($order['OrderID'] ?? ''),
|
||||||
|
'ResultDateTime' => $item['ResultDateTime'] ?? ($order['TrnDate'] ?? date('Y-m-d H:i:s')),
|
||||||
|
'CreateDate' => date('Y-m-d H:i:s'),
|
||||||
|
];
|
||||||
|
|
||||||
|
$payload['Result'] = $item['Result'] ?? $item['ResultValue'] ?? null;
|
||||||
|
|
||||||
|
$extraFields = array_intersect_key($item, array_flip([
|
||||||
|
'InternalSID',
|
||||||
|
'AspCnt',
|
||||||
|
'ResultCode',
|
||||||
|
'SampleType',
|
||||||
|
'WorkstationID',
|
||||||
|
'EquipmentID',
|
||||||
|
'RefNumID',
|
||||||
|
'RefTxtID',
|
||||||
|
]));
|
||||||
|
|
||||||
|
$payload = array_merge($payload, $extraFields);
|
||||||
|
|
||||||
|
if (!$this->db->table('patres')->insert($payload)) {
|
||||||
|
throw new \RuntimeException('Failed to create test row');
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'ResultID' => (int) $this->db->insertID(),
|
||||||
|
'TestSiteID' => $testSiteID,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function editTestRow(int $orderOID, array $item): array {
|
||||||
|
$testSiteID = (int) ($item['TestSiteID'] ?? 0);
|
||||||
|
if ($testSiteID <= 0) {
|
||||||
|
throw new \InvalidArgumentException('TestSiteID is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
$row = $this->getLatestActiveTestRow($orderOID, $testSiteID);
|
||||||
|
if (!$row) {
|
||||||
|
throw new \InvalidArgumentException('Test row not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
$updateData = array_intersect_key($item, array_flip([
|
||||||
|
'InternalSID',
|
||||||
|
'AspCnt',
|
||||||
|
'Result',
|
||||||
|
'ResultCode',
|
||||||
|
'SampleType',
|
||||||
|
'ResultDateTime',
|
||||||
|
'WorkstationID',
|
||||||
|
'EquipmentID',
|
||||||
|
'RefNumID',
|
||||||
|
'RefTxtID',
|
||||||
|
]));
|
||||||
|
|
||||||
|
if (empty($updateData)) {
|
||||||
|
throw new \InvalidArgumentException('Edit payload is empty');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->db->table('patres')
|
||||||
|
->where('ResultID', $row['ResultID'])
|
||||||
|
->update($updateData)) {
|
||||||
|
throw new \RuntimeException('Failed to update test row');
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'ResultID' => (int) $row['ResultID'],
|
||||||
|
'TestSiteID' => $testSiteID,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function deleteTestRow(int $orderOID, int $testSiteID): array {
|
||||||
|
$row = $this->getLatestActiveTestRow($orderOID, $testSiteID);
|
||||||
|
if (!$row) {
|
||||||
|
throw new \InvalidArgumentException('Test row not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->db->table('patres')
|
||||||
|
->where('ResultID', $row['ResultID'])
|
||||||
|
->update(['DelDate' => date('Y-m-d H:i:s')])) {
|
||||||
|
throw new \RuntimeException('Failed to delete test row');
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'ResultID' => (int) $row['ResultID'],
|
||||||
|
'TestSiteID' => $testSiteID,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getLatestActiveTestRow(int $orderOID, int $testSiteID): ?array {
|
||||||
|
$row = $this->db->table('patres')
|
||||||
|
->where('OrderID', $orderOID)
|
||||||
|
->where('TestSiteID', $testSiteID)
|
||||||
|
->where('DelDate', null)
|
||||||
|
->orderBy('ResultID', 'DESC')
|
||||||
|
->get()
|
||||||
|
->getRowArray();
|
||||||
|
|
||||||
|
return $row ?: null;
|
||||||
|
}
|
||||||
|
|
||||||
private function getContainerRequirement($testSiteID, $testMapDetailModel, $containerDefModel): array {
|
private function getContainerRequirement($testSiteID, $testMapDetailModel, $containerDefModel): array {
|
||||||
// Try to find container requirement from test mapping
|
// Try to find container requirement from test mapping
|
||||||
$containerDef = $this->db->table('testmapdetail tmd')
|
$containerDef = $this->db->table('testmapdetail tmd')
|
||||||
|
|||||||
@ -1502,6 +1502,44 @@ paths:
|
|||||||
type: integer
|
type: integer
|
||||||
WorkstationID:
|
WorkstationID:
|
||||||
type: integer
|
type: integer
|
||||||
|
Tests:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
created:
|
||||||
|
type: array
|
||||||
|
description: New tests to create for this order
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
TestSiteID:
|
||||||
|
type: integer
|
||||||
|
Result:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
ResultDateTime:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
nullable: true
|
||||||
|
edited:
|
||||||
|
type: array
|
||||||
|
description: Existing tests to edit by OrderID + TestSiteID
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
TestSiteID:
|
||||||
|
type: integer
|
||||||
|
Result:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
ResultDateTime:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
nullable: true
|
||||||
|
deleted:
|
||||||
|
type: array
|
||||||
|
description: TestSiteID values to soft delete by latest active row
|
||||||
|
items:
|
||||||
|
type: integer
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Order updated
|
description: Order updated
|
||||||
|
|||||||
@ -252,6 +252,44 @@
|
|||||||
type: integer
|
type: integer
|
||||||
WorkstationID:
|
WorkstationID:
|
||||||
type: integer
|
type: integer
|
||||||
|
Tests:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
created:
|
||||||
|
type: array
|
||||||
|
description: New tests to create for this order
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
TestSiteID:
|
||||||
|
type: integer
|
||||||
|
Result:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
ResultDateTime:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
nullable: true
|
||||||
|
edited:
|
||||||
|
type: array
|
||||||
|
description: Existing tests to edit by OrderID + TestSiteID
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
TestSiteID:
|
||||||
|
type: integer
|
||||||
|
Result:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
ResultDateTime:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
nullable: true
|
||||||
|
deleted:
|
||||||
|
type: array
|
||||||
|
description: TestSiteID values to soft delete by latest active row
|
||||||
|
items:
|
||||||
|
type: integer
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Order updated
|
description: Order updated
|
||||||
|
|||||||
@ -1,121 +1,222 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Tests\Feature\OrderTest;
|
declare(strict_types=1);
|
||||||
|
|
||||||
use CodeIgniter\Test\FeatureTestTrait;
|
namespace Tests\Feature\OrderTest;
|
||||||
use CodeIgniter\Test\CIUnitTestCase;
|
|
||||||
use Firebase\JWT\JWT;
|
use App\Models\OrderTest\OrderTestModel;
|
||||||
|
use App\Models\PatResultModel;
|
||||||
class OrderTestPatchTest extends CIUnitTestCase
|
use App\Models\Patient\PatientModel;
|
||||||
{
|
use App\Models\Test\TestDefSiteModel;
|
||||||
use FeatureTestTrait;
|
use CodeIgniter\Test\CIUnitTestCase;
|
||||||
|
use CodeIgniter\Test\FeatureTestTrait;
|
||||||
protected string $token;
|
use Firebase\JWT\JWT;
|
||||||
protected string $endpoint = 'api/ordertest';
|
|
||||||
|
class OrderTestPatchTest extends CIUnitTestCase
|
||||||
protected function setUp(): void
|
{
|
||||||
{
|
use FeatureTestTrait;
|
||||||
parent::setUp();
|
|
||||||
$key = getenv('JWT_SECRET') ?: 'my-secret-key';
|
protected string $token;
|
||||||
$payload = [
|
protected string $endpoint = 'api/ordertest';
|
||||||
'iss' => 'localhost',
|
|
||||||
'aud' => 'localhost',
|
protected function setUp(): void
|
||||||
'iat' => time(),
|
{
|
||||||
'nbf' => time(),
|
parent::setUp();
|
||||||
'exp' => time() + 3600,
|
|
||||||
'uid' => 1,
|
$key = getenv('JWT_SECRET') ?: 'my-secret-key';
|
||||||
'email' => 'admin@admin.com',
|
$payload = [
|
||||||
];
|
'iss' => 'localhost',
|
||||||
$this->token = JWT::encode($payload, $key, 'HS256');
|
'aud' => 'localhost',
|
||||||
}
|
'iat' => time(),
|
||||||
|
'nbf' => time(),
|
||||||
private function authHeaders(): array
|
'exp' => time() + 3600,
|
||||||
{
|
'uid' => 1,
|
||||||
return ['Cookie' => 'token=' . $this->token];
|
'email' => 'admin@admin.com',
|
||||||
}
|
];
|
||||||
|
$this->token = JWT::encode($payload, $key, 'HS256');
|
||||||
private function createOrderTest(array $data = []): array
|
}
|
||||||
{
|
|
||||||
$payload = array_merge([
|
private function authHeaders(): array
|
||||||
'OrderCode' => 'ORD_' . uniqid(),
|
{
|
||||||
'OrderName' => 'Test Order ' . uniqid(),
|
return ['Cookie' => 'token=' . $this->token];
|
||||||
], $data);
|
}
|
||||||
|
|
||||||
$response = $this->withHeaders($this->authHeaders())
|
private function findResultByOrderAndSite(int $internalOID, int $testSiteID): ?array
|
||||||
->withBodyFormat('json')
|
{
|
||||||
->call('post', $this->endpoint, $payload);
|
$resultModel = new PatResultModel();
|
||||||
|
|
||||||
$response->assertStatus(201);
|
return $resultModel->where('OrderID', $internalOID)
|
||||||
$decoded = json_decode($response->getJSON(), true);
|
->where('TestSiteID', $testSiteID)
|
||||||
return $decoded['data'];
|
->where('DelDate', null)
|
||||||
}
|
->orderBy('ResultID', 'DESC')
|
||||||
|
->first();
|
||||||
public function testPartialUpdateOrderTestSuccess()
|
}
|
||||||
{
|
|
||||||
$order = $this->createOrderTest();
|
private function findRowBySite(array $rows, int $testSiteID): ?array
|
||||||
$id = $order['OrderID'];
|
{
|
||||||
|
foreach ($rows as $row) {
|
||||||
$patch = $this->withHeaders($this->authHeaders())
|
if ((int) ($row['TestSiteID'] ?? 0) === $testSiteID) {
|
||||||
->withBodyFormat('json')
|
return $row;
|
||||||
->call('patch', "{$this->endpoint}/{$id}", ['OrderName' => 'Updated Order']);
|
}
|
||||||
|
}
|
||||||
$patch->assertStatus(200);
|
|
||||||
$patchData = json_decode($patch->getJSON(), true);
|
return null;
|
||||||
$this->assertEquals('success', $patchData['status']);
|
}
|
||||||
|
|
||||||
$show = $this->withHeaders($this->authHeaders())->call('get', "{$this->endpoint}/{$id}");
|
private function createOrderWithTest(): array
|
||||||
$show->assertStatus(200);
|
{
|
||||||
$showData = json_decode($show->getJSON(), true)['data'];
|
$patientModel = new PatientModel();
|
||||||
|
$patient = $patientModel->where('DelDate', null)->first();
|
||||||
$this->assertEquals('Updated Order', $showData['OrderName']);
|
$this->assertNotEmpty($patient, 'No active patient found for order patch test.');
|
||||||
$this->assertEquals($order['OrderCode'], $showData['OrderCode']);
|
|
||||||
}
|
$testModel = new TestDefSiteModel();
|
||||||
|
$tests = $testModel->where('EndDate', null)
|
||||||
public function testPartialUpdateOrderTestNotFound()
|
->where('TestType', 'TEST')
|
||||||
{
|
->findAll(2);
|
||||||
$patch = $this->withHeaders($this->authHeaders())
|
|
||||||
->withBodyFormat('json')
|
if (count($tests) < 2) {
|
||||||
->call('patch', "{$this->endpoint}/999999", ['OrderName' => 'Updated']);
|
$tests = $testModel->where('EndDate', null)->findAll(2);
|
||||||
|
}
|
||||||
$patch->assertStatus(404);
|
|
||||||
}
|
$this->assertGreaterThanOrEqual(2, count($tests), 'Need at least 2 test definitions for lifecycle patch test.');
|
||||||
|
|
||||||
public function testPartialUpdateOrderTestInvalidId()
|
$response = $this->withHeaders($this->authHeaders())
|
||||||
{
|
->withBodyFormat('json')
|
||||||
$patch = $this->withHeaders($this->authHeaders())
|
->call('post', $this->endpoint, [
|
||||||
->withBodyFormat('json')
|
'InternalPID' => (int) $patient['InternalPID'],
|
||||||
->call('patch', "{$this->endpoint}/invalid", ['OrderName' => 'Updated']);
|
'SiteID' => (int) ($tests[0]['SiteID'] ?? 1),
|
||||||
|
'Tests' => [
|
||||||
$patch->assertStatus(400);
|
[
|
||||||
}
|
'TestSiteID' => (int) $tests[0]['TestSiteID'],
|
||||||
|
],
|
||||||
public function testPartialUpdateOrderTestEmptyPayload()
|
],
|
||||||
{
|
]);
|
||||||
$order = $this->createOrderTest();
|
|
||||||
$id = $order['OrderID'];
|
$response->assertStatus(201);
|
||||||
|
$decoded = json_decode($response->getJSON(), true);
|
||||||
$patch = $this->withHeaders($this->authHeaders())
|
$this->assertSame('success', $decoded['status'] ?? null);
|
||||||
->withBodyFormat('json')
|
|
||||||
->call('patch', "{$this->endpoint}/{$id}", []);
|
return [
|
||||||
|
'order' => $decoded['data'],
|
||||||
$patch->assertStatus(400);
|
'baseTest' => $tests[0],
|
||||||
}
|
'extraTest' => $tests[1],
|
||||||
|
];
|
||||||
public function testPartialUpdateOrderTestSingleField()
|
}
|
||||||
{
|
|
||||||
$order = $this->createOrderTest();
|
public function testPatchTestsLifecycleCreatedEditedDeleted(): void
|
||||||
$id = $order['OrderID'];
|
{
|
||||||
|
$fixture = $this->createOrderWithTest();
|
||||||
$patch = $this->withHeaders($this->authHeaders())
|
$order = $fixture['order'];
|
||||||
->withBodyFormat('json')
|
$baseTest = $fixture['baseTest'];
|
||||||
->call('patch', "{$this->endpoint}/{$id}", ['OrderCode' => 'NEW_' . uniqid()]);
|
$extraTest = $fixture['extraTest'];
|
||||||
|
$orderID = $order['OrderID'];
|
||||||
$patch->assertStatus(200);
|
$internalOID = (int) $order['InternalOID'];
|
||||||
$showData = json_decode($this->withHeaders($this->authHeaders())
|
$baseSiteID = (int) $baseTest['TestSiteID'];
|
||||||
->call('get', "{$this->endpoint}/{$id}")
|
$extraSiteID = (int) $extraTest['TestSiteID'];
|
||||||
->getJSON(), true)['data'];
|
|
||||||
|
$createResponse = $this->withHeaders($this->authHeaders())
|
||||||
$this->assertNotEquals($order['OrderCode'], $showData['OrderCode']);
|
->withBodyFormat('json')
|
||||||
$this->assertEquals($order['OrderName'], $showData['OrderName']);
|
->call('patch', "{$this->endpoint}/{$orderID}", [
|
||||||
}
|
'Tests' => [
|
||||||
}
|
'created' => [
|
||||||
|
[
|
||||||
|
'TestSiteID' => $extraSiteID,
|
||||||
|
'Result' => 'CREATED',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$createResponse->assertStatus(200);
|
||||||
|
$createJson = json_decode($createResponse->getJSON(), true);
|
||||||
|
$this->assertSame('success', $createJson['status'] ?? null);
|
||||||
|
|
||||||
|
$createdRow = $this->findRowBySite($createJson['data']['Tests'] ?? [], $extraSiteID);
|
||||||
|
$this->assertNotNull($createdRow, 'Created test not returned by patch response.');
|
||||||
|
$this->assertSame('CREATED', $createdRow['Result'] ?? null);
|
||||||
|
$createdResultID = (int) ($createdRow['ResultID'] ?? 0);
|
||||||
|
$this->assertGreaterThan(0, $createdResultID, 'Created test missing ResultID.');
|
||||||
|
|
||||||
|
$createdDbRow = $this->findResultByOrderAndSite($internalOID, $extraSiteID);
|
||||||
|
$this->assertNotNull($createdDbRow, 'Created test not found in DB.');
|
||||||
|
$this->assertSame('CREATED', $createdDbRow['Result'] ?? null);
|
||||||
|
|
||||||
|
$editResponse = $this->withHeaders($this->authHeaders())
|
||||||
|
->withBodyFormat('json')
|
||||||
|
->call('patch', "{$this->endpoint}/{$orderID}", [
|
||||||
|
'Tests' => [
|
||||||
|
'edited' => [
|
||||||
|
[
|
||||||
|
'TestSiteID' => $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' => [
|
||||||
|
$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($createdResultID);
|
||||||
|
$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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user