Complete overhaul of the valueset system to use human-readable names
instead of numeric IDs for improved maintainability and API consistency.
- PatientController: Renamed 'Gender' field to 'Sex' in validation rules
- ValuesetController: Changed API endpoints from ID-based (/:num) to name-based (/:any)
- TestsController: Refactored to use ValueSet library instead of direct valueset queries
- Added ValueSet library (app/Libraries/ValueSet.php) with static lookup methods:
- getOptions() - returns dropdown format [{value, label}]
- getLabel(, ) - returns label for a value
- transformLabels(, ) - batch transform records
- get() and getRaw() for Lookups compatibility
- Added ValueSetApiController for public valueset API endpoints
- Added ValueSet refresh endpoint (POST /api/valueset/refresh)
- Added DemoOrderController for testing order creation without auth
- 2026-01-12-000001: Convert valueset references from VID to VValue
- 2026-01-12-000002: Rename patient.Gender column to Sex
- OrderTestController: Now uses OrderTestModel with proper model pattern
- TestsController: Uses ValueSet library for all lookup operations
- ValueSetController: Simplified to use name-based lookups
- Updated all organization (account/site/workstation) dialogs and index views
- Updated specimen container dialogs and index views
- Updated tests_index.php with ValueSet integration
- Updated patient dialog form and index views
- Removed .factory/config.json and CLAUDE.md (replaced by AGENTS.md)
- Consolidated lookups in Lookups.php (removed inline valueset constants)
- Updated all test files to match new field names
- 32 modified files, 17 new files, 2 deleted files
- Net: +661 insertions, -1443 deletions (significant cleanup)
250 lines
8.2 KiB
PHP
250 lines
8.2 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Patients;
|
|
|
|
use CodeIgniter\Test\FeatureTestTrait;
|
|
use CodeIgniter\Test\CIUnitTestCase;
|
|
use Faker\Factory;
|
|
|
|
/**
|
|
* PatientDeleteTest
|
|
*
|
|
* Test cases for Patient DELETE endpoint
|
|
* Endpoint: DELETE api/patient
|
|
*
|
|
* Test Cases:
|
|
* 1. testDeleteWithoutInternalPID - 500 error when InternalPID not provided
|
|
* 2. testDeleteWithNullInternalPID - 400 error when InternalPID is null
|
|
* 3. testDeleteWithZeroInternalPID - 400 error when InternalPID is 0
|
|
* 4. testDeleteWithEmptyStringInternalPID - 400 error when InternalPID is empty string
|
|
* 5. testDeleteWithArrayInternalPID - 400 error when InternalPID is array
|
|
* 6. testDeleteNotFound - 404 error when patient not found
|
|
* 7. testDeleteSQLInjectionAttempt - 400 error when SQL injection attempted
|
|
* 8. testDeleteSuccess - 200 success when valid delete (commented - requires DB setup)
|
|
*/
|
|
class PatientDeleteTest extends CIUnitTestCase
|
|
{
|
|
use FeatureTestTrait;
|
|
|
|
protected $endpoint = 'api/patient';
|
|
|
|
/**
|
|
* Test Case 1: Delete without InternalPID key
|
|
* Expected: 500 Internal Server Error (Undefined array key)
|
|
*/
|
|
public function testDeleteWithoutInternalPID()
|
|
{
|
|
$result = $this->withBodyFormat('json')
|
|
->delete($this->endpoint, []);
|
|
|
|
$result->assertStatus(500);
|
|
|
|
$json = $result->getJSON();
|
|
$data = json_decode($json, true);
|
|
|
|
$this->assertArrayHasKey('messages', $data);
|
|
}
|
|
|
|
/**
|
|
* Test Case 2: Delete with null InternalPID
|
|
* Expected: 400 Bad Request - Patient ID must be a valid integer
|
|
*/
|
|
public function testDeleteWithNullInternalPID()
|
|
{
|
|
$result = $this->withBodyFormat('json')
|
|
->delete($this->endpoint, [
|
|
'InternalPID' => null,
|
|
]);
|
|
|
|
$result->assertStatus(400);
|
|
$result->assertJSONFragment([
|
|
'status' => 'error',
|
|
'message' => 'Patient ID must be a valid integer.'
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Test Case 3: Delete with zero InternalPID
|
|
* Expected: 400 Bad Request - Patient ID must be a valid integer
|
|
*/
|
|
public function testDeleteWithZeroInternalPID()
|
|
{
|
|
$result = $this->withBodyFormat('json')
|
|
->delete($this->endpoint, [
|
|
'InternalPID' => 0,
|
|
]);
|
|
|
|
$result->assertStatus(400);
|
|
$result->assertJSONFragment([
|
|
'status' => 'error',
|
|
'message' => 'Patient ID must be a valid integer.'
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Test Case 4: Delete with empty string InternalPID
|
|
* Expected: 400 Bad Request - Patient ID must be a valid integer
|
|
*/
|
|
public function testDeleteWithEmptyStringInternalPID()
|
|
{
|
|
$result = $this->withBodyFormat('json')
|
|
->delete($this->endpoint, [
|
|
'InternalPID' => '',
|
|
]);
|
|
|
|
$result->assertStatus(400);
|
|
$result->assertJSONFragment([
|
|
'status' => 'error',
|
|
'message' => 'Patient ID must be a valid integer.'
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Test Case 5: Delete with array InternalPID
|
|
* Expected: 400 Bad Request - Patient ID must be a valid integer
|
|
*/
|
|
public function testDeleteWithArrayInternalPID()
|
|
{
|
|
$result = $this->withBodyFormat('json')
|
|
->delete($this->endpoint, [
|
|
'InternalPID' => [],
|
|
]);
|
|
|
|
$result->assertStatus(400);
|
|
$result->assertJSONFragment([
|
|
'status' => 'error',
|
|
'message' => 'Patient ID must be a valid integer.'
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Test Case 6: Delete non-existent patient
|
|
* Expected: 404 Not Found (or 500 if database not configured)
|
|
*/
|
|
public function testDeleteNotFound()
|
|
{
|
|
$testId = 9999999;
|
|
|
|
$result = $this->withBodyFormat('json')
|
|
->delete($this->endpoint, [
|
|
'InternalPID' => $testId
|
|
]);
|
|
|
|
// May return 404 (not found), 500 (database error), or null (connection issue)
|
|
$statusCode = $result->getStatusCode();
|
|
$this->assertTrue(
|
|
in_array($statusCode, [404, 500]) || $statusCode === null,
|
|
"Expected status code 404, 500, or null but got: " . var_export($statusCode, true)
|
|
);
|
|
|
|
$json = $result->getJSON();
|
|
$data = json_decode($json, true);
|
|
|
|
$this->assertArrayHasKey('messages', $data);
|
|
}
|
|
|
|
/**
|
|
* Test Case 7: Delete with SQL injection attempt
|
|
* Expected: 400 Bad Request - should be sanitized
|
|
*/
|
|
public function testDeleteSQLInjectionAttempt()
|
|
{
|
|
$result = $this->withBodyFormat('json')
|
|
->delete($this->endpoint, [
|
|
'InternalPID' => "' OR 1=1 --"
|
|
]);
|
|
|
|
$result->assertStatus(400);
|
|
$result->assertJSONFragment([
|
|
'status' => 'error',
|
|
'message' => 'Patient ID must be a valid integer.'
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Test Case 8: Delete with negative number
|
|
* Expected: 400 Bad Request - should reject negative numbers
|
|
*/
|
|
public function testDeleteWithNegativeInternalPID()
|
|
{
|
|
$result = $this->withBodyFormat('json')
|
|
->delete($this->endpoint, [
|
|
'InternalPID' => -1,
|
|
]);
|
|
|
|
$result->assertStatus(400);
|
|
$result->assertJSONFragment([
|
|
'status' => 'error',
|
|
'message' => 'Patient ID must be a valid integer.'
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Test Case 9: Delete with string number
|
|
* Expected: Depends on implementation - may accept "123" as valid
|
|
*/
|
|
public function testDeleteWithStringNumber()
|
|
{
|
|
$testId = "9999998";
|
|
|
|
$result = $this->withBodyFormat('json')
|
|
->delete($this->endpoint, [
|
|
'InternalPID' => $testId
|
|
]);
|
|
|
|
// Should either be 404 (not found), 200 (deleted), 500 (db error), or null (connection issue)
|
|
$statusCode = $result->getStatusCode();
|
|
$this->assertTrue(
|
|
in_array($statusCode, [200, 404, 500]) || $statusCode === null,
|
|
"Expected status code 200, 404, 500, or null but got: " . var_export($statusCode, true)
|
|
);
|
|
}
|
|
|
|
// =====================================================
|
|
// SUCCESS TEST CASES
|
|
// NOTE: Uncomment these when you have proper DB seeding
|
|
// =====================================================
|
|
|
|
// /**
|
|
// * Test Case: Delete success
|
|
// * Expected: 200 OK - Patient deleted successfully
|
|
// *
|
|
// * IMPORTANT: This test modifies database state.
|
|
// * Make sure you have proper seeding or use DatabaseTestTrait
|
|
// */
|
|
// public function testDeleteSuccess()
|
|
// {
|
|
// // First create a patient to delete
|
|
// $faker = Factory::create('id_ID');
|
|
//
|
|
// $createPayload = [
|
|
// "PatientID" => "DELTEST" . $faker->numberBetween(10000, 99999),
|
|
// "AlternatePID" => "DELALT" . $faker->numberBetween(10000, 99999),
|
|
// "Prefix" => "Mr.",
|
|
// "NameFirst" => "ToBeDeleted",
|
|
// "NameLast" => "Patient",
|
|
// "Sex" => "5",
|
|
// "Birthdate" => "1990-01-01",
|
|
// ];
|
|
//
|
|
// // Create the patient
|
|
// $createResult = $this->withBodyFormat('json')->post($this->endpoint, $createPayload);
|
|
// $createResult->assertStatus(201);
|
|
//
|
|
// // Get the created patient ID from response or database
|
|
// // Then delete it
|
|
// $testId = 3; // Replace with actual ID
|
|
//
|
|
// $result = $this->withBodyFormat('json')
|
|
// ->delete($this->endpoint, [
|
|
// 'InternalPID' => $testId
|
|
// ]);
|
|
//
|
|
// $result->assertStatus(200);
|
|
// $result->assertJSONFragment([
|
|
// 'status' => 'success',
|
|
// 'message' => 'Patient ID with '.$testId.' deleted successfully.'
|
|
// ]);
|
|
// }
|
|
}
|