From f33cd6cc7b727d9a15687650df152518e68b7fd7 Mon Sep 17 00:00:00 2001 From: mikael-zakaria Date: Tue, 14 Oct 2025 15:50:22 +0700 Subject: [PATCH] Update Patient Unit Testing --- app/Controllers/Patient.php | 15 +- app/Models/PatientModel.php | 45 +- phpunit.xml.dist | 14 +- tests/feature/Patients/PatientCreateTest.php | 417 +++++++++++++------ tests/feature/Patients/PatientDeleteTest.php | 144 +++---- tests/feature/Patients/PatientIndexTest.php | 20 +- tests/feature/Patients/PatientShowTest.php | 15 +- tests/feature/Patients/PatientUpdateTest.php | 215 ++++++---- 8 files changed, 551 insertions(+), 334 deletions(-) diff --git a/app/Controllers/Patient.php b/app/Controllers/Patient.php index 0b20c4b..b6f4727 100644 --- a/app/Controllers/Patient.php +++ b/app/Controllers/Patient.php @@ -16,11 +16,13 @@ class Patient extends Controller { $this->db = \Config\Database::connect(); $this->model = new PatientModel(); $this->rules = [ - 'PatientID' => 'required|max_length[50]', - 'AlternatePID' => 'permit_empty|max_length[50]', - 'NameFirst' => 'required|min_length[1]|max_length[255]', - 'EmailAddress1' => 'required', - 'Gender' => 'required' + 'PatientID' => 'required|max_length[50]', + 'AlternatePID' => 'permit_empty|max_length[50]', + 'NameFirst' => 'required|min_length[1]|max_length[255]', + 'EmailAddress1' => 'required', + 'Gender' => 'required', + 'Birthdate' => 'required', + "PatIdt.Identifier" => 'max_length[255]' ]; } @@ -79,8 +81,7 @@ class Patient extends Controller { return $this->failServerError('Something went wrong: ' . $e->getMessage()); } } - - // Unit Testing Pass : \clqms-be\tests\feature\Patients\PatientDeleteTest.php + public function delete() { try { $input = $this->request->getJSON(true); diff --git a/app/Models/PatientModel.php b/app/Models/PatientModel.php index 6679310..6cdbc12 100644 --- a/app/Models/PatientModel.php +++ b/app/Models/PatientModel.php @@ -113,15 +113,15 @@ class PatientModel extends BaseUtcModel { public function createPatient($input) { $db = \Config\Database::connect(); - $patidt = $input['PatIdt'] ?? []; - $patatt = $input['PatAtt'] ?? []; + $patidt = $input['PatIdt'] ?? []; + $patatt = $input['PatAtt'] ?? []; $patcom['Comment'] = $input['PatCom'] ?? null; $input['LinkTo'] = empty($input['LinkTo']) ? null : $input['LinkTo']; $input['Birthdate'] = $this->isValidDateTime($input['Birthdate']); $input['DeathDateTime'] = $this->isValidDateTime($input['DeathDateTime']); - + $db->transBegin(); + try { - $db->transStart(); $this->insert($input); $newInternalPID = $this->getInsertID(); @@ -147,14 +147,15 @@ class PatientModel extends BaseUtcModel { $this->checkDbError($db, 'Insert patatt'); } - $db->transComplete(); - if ($db->transStatus() === false) { - throw new \Exception("Failed to sync patient relations"); - } + $db->transCommit(); + // $db->transComplete(); + // if ($db->transStatus() === false) { + // throw new \Exception("Failed to sync patient relations"); + // } return $newInternalPID; } catch (\Exception $e) { - // $db->transRollback(); + $db->transRollback(); throw $e; } } @@ -168,8 +169,9 @@ class PatientModel extends BaseUtcModel { $input['Birthdate'] = $this->isValidDateTime($input['Birthdate']); $input['DeathDateTime'] = $this->isValidDateTime($input['DeathDateTime']); + $db->transBegin(); + try { - $db->transStart(); $InternalPID = $input['InternalPID']; $this->where('InternalPID',$InternalPID)->set($input)->update(); $this->checkDbError($db, 'Update patient'); @@ -269,23 +271,20 @@ class PatientModel extends BaseUtcModel { $this->checkDbError($db, 'Update/Delete patatt'); } - $db->transComplete(); - - if ($db->transStatus() === false) { - throw new \Exception('Failed to sync patient relations'); - } + $db->transCommit(); + // if ($db->transStatus() === false) { + // throw new \Exception('Failed to sync patient relations'); + // } return $InternalPID; + } catch (\Exception $e) { - // $db->transRollback(); + $db->transRollback(); throw $e; } } private function transformPatientData(array $patient): array { $patient["Age"] = $this->calculateAgeFromBirthdate($patient["Birthdate"], $patient["DeathDateTime"]); - // $patient["Birthdate"] = $this->formatedDate($patient["Birthdate"]); - // $patient["CreateDate"] = $this->formatedDate($patient["CreateDate"]); - // $patient["DelDate"] = $this->formatedDate($patient["DelDate"]); $patient["DeathDateTime"] = $this->formattedDate($patient["DeathDateTime"]); $patient["CreateDate"] = $this->formattedDate($patient["CreateDate"]); $patient["BirthdateConversion"] = $this->formatedDateForDisplay($patient["Birthdate"]); @@ -320,7 +319,7 @@ class PatientModel extends BaseUtcModel { ->getRowArray() ?: null; } - // Conversion to (Years Months Days) + // Conversion to (Years Months Days) - For Age private function calculateAgeFromBirthdate($birthdate, $deathdatetime) { $dob = new \DateTime($birthdate); @@ -350,9 +349,7 @@ class PatientModel extends BaseUtcModel { // Conversion Time to Format Y-m-d\TH:i:s\Z private function formattedDate(?string $dateString): ?string { try { - if (empty($dateString)) { - return null; - } + if (empty($dateString)) {return null;} $dt = new \DateTime($dateString, new \DateTimeZone("UTC")); return $dt->format('Y-m-d\TH:i:s\Z'); // ISO 8601 UTC @@ -361,7 +358,7 @@ class PatientModel extends BaseUtcModel { } } - // Conversion Time to Format j M Y + // Conversion Time to Format j M Y - For BirthdateConversion private function formatedDateForDisplay($dateString) { $date = \DateTime::createFromFormat('Y-m-d H:i', $dateString); diff --git a/phpunit.xml.dist b/phpunit.xml.dist index f7d5608..0541374 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -42,11 +42,11 @@ - + - + @@ -55,11 +55,11 @@ - - - - - + + + + + diff --git a/tests/feature/Patients/PatientCreateTest.php b/tests/feature/Patients/PatientCreateTest.php index cc415bf..d9a6252 100644 --- a/tests/feature/Patients/PatientCreateTest.php +++ b/tests/feature/Patients/PatientCreateTest.php @@ -4,12 +4,14 @@ namespace Tests\Feature\Patients; use CodeIgniter\Test\FeatureTestTrait; use CodeIgniter\Test\CIUnitTestCase; +use Faker\Factory; class PatientCreateTest extends CIUnitTestCase { use FeatureTestTrait; protected $endpoint = 'api/patient'; + // 400 - Passed public function testCreatePatientValidationFail() { // error 400 yg diharapkan $payload = ['Name' => 'Ngawur']; @@ -43,117 +45,310 @@ class PatientCreateTest extends CIUnitTestCase } - // Wajib Diganti ya payloadnya kalau mau dijalankan + // 201 - Passed public function testCreatePatientSuccess() { - $payload = [ - "PatientID"=> "SMAJ6", //Wajib Ganti - "AlternatePID"=> "P0234", - "Prefix"=> "Mr.", - "NameFirst"=> "Budi", - "NameMiddle"=> "Santoso", - "NameMaiden"=> "Kiki", - "NameLast"=> "Wijaya", - "Suffix"=> "S.kom", - "NameAlias"=> "Bud", - "Gender"=> "1", - 'EmailAddress1' => 'kaka@gmail.a1com', //Wajib Ganti - 'Identity' => [ - "IdentifierType" => "KTP", - "Identifier" => "317409050590100" //Wajib Ganti - ] - ]; - // $result = $this->call('post', 'api/patient', $payload); - $result = $this->withBodyFormat('json') - ->call('post', 'api/patient', $payload); + $faker = Factory::create('id_ID'); + + for ($i = 0; $i < 7; $i++) { - $result->assertStatus(201); - $result->assertJSONFragment([ - 'status' => 'success', - ]); + $payload = [ + "PatientID" => "DUM" . $faker->numberBetween(1, 1000). $faker->numberBetween(1, 1000).$faker->numberBetween(1, 1000), + "AlternatePID" => "DMY" . $faker->numberBetween(1, 1000). $faker->numberBetween(1, 1000).$faker->numberBetween(1, 1000), + "Prefix" => $faker->title, + "NameFirst" => $faker->firstName, + "NameMiddle" => $faker->firstName, + "NameMaiden" => $faker->firstName, + "NameLast" => $faker->lastName, + "Suffix" => "S.Kom", + "NameAlias" => $faker->userName, + "Gender" => $faker->numberBetween(5, 6), + "PlaceOfBirth" => $faker->city, + "Birthdate" => $faker->date('Y-m-d'), + "ZIP" => $faker->postcode, + "Street_1" => $faker->streetAddress, + "Street_2" => "RT " . $faker->numberBetween(1, 10) . " RW " . $faker->numberBetween(1, 10), + "Street_3" => "Blok " . $faker->numberBetween(1, 20), + "City" => $faker->city, + "Province" => $faker->state, + "EmailAddress1" => "A" . $faker->numberBetween(1, 1000). $faker->numberBetween(1, 1000).$faker->numberBetween(1, 1000).'@gmail.com', + "EmailAddress2" => "B" . $faker->numberBetween(1, 1000). $faker->numberBetween(1, 1000).$faker->numberBetween(1, 1000).'@gmail.com', + "Phone" => $faker->phoneNumber, + "MobilePhone" => $faker->phoneNumber, + "Race" => (string) $faker->numberBetween(175, 205), + "Country" => (string) $faker->numberBetween(221, 469), + "MaritalStatus" => (string) $faker->numberBetween(8, 15), + "Religion" => (string) $faker->numberBetween(206, 212), + "Ethnic" => (string) $faker->numberBetween(213, 220), + "Citizenship" => "WNI", + "DeathIndicator" => (string) $faker->numberBetween(16, 17), + "LinkTo" => (string) $faker->numberBetween(2, 3), + "Custodian" => $i-1, + "PatIdt" => [ + "IdentifierType" => "KTP", + "Identifier" => $faker->nik() ?? $faker->numerify('################') + ], + "PatAtt" => [ + [ "Address" => "/api/upload/" . $faker->word . ".jpg" ] + ], + "PatCom" => $faker->sentence, + ]; + if($payload['DeathIndicator'] == '16') { + $payload['DeathDateTime'] = $faker->date('Y-m-d H:i:s'); + } else { + $payload['DeathDateTime'] = null; + } + + $result = $this->withBodyFormat('json')->call('post', 'api/patient', $payload); + $result->assertStatus(201); + $result->assertJSONFragment(['status' => 'success']); + + } } - // public function testCreatePatientValidationError() { - // $payload = [ - // "NameFirst" => "Jane" // PatientID kosong - // ]; + // Error - Passed + public function testCreatePatientValidationError() { + $payload = [ + "NameFirst" => "Jane" // PatientID kosong + ]; - // $result = $this->withBodyFormat('json')->post($this->endpoint, $payload); + $result = $this->withBodyFormat('json')->post($this->endpoint, $payload); + // dd($result); + $result->assertArrayHasKey("errors", $result); + } - // $result->assertStatus(422); - // $result->assertJSONFragment(["field" => "patient"]); - // } + // Error - Passed + public function testCreatePatidtValidationError() { - // public function testCreatePatidtValidationError() { - // $payload = [ - // "PatientID" => "PX002", - // "NameFirst" => "Jane", - // "Identity" => [ - // "IdentifierType" => null, - // "Identifier" => null - // ] - // ]; + $faker = Factory::create('id_ID'); - // $result = $this->withBodyFormat('json')->post($this->endpoint, $payload); + $payload = [ + "PatientID" => "DUMQ" . $faker->numberBetween(1, 1000). $faker->numberBetween(1, 1000).$faker->numberBetween(1, 1000), + "AlternatePID" => "DMYQ" . $faker->numberBetween(1, 1000). $faker->numberBetween(1, 1000).$faker->numberBetween(1, 1000), + "Prefix" => $faker->title, + "NameFirst" => $faker->firstName, + "NameMiddle" => $faker->firstName, + "NameMaiden" => $faker->firstName, + "NameLast" => $faker->lastName, + "Suffix" => "S.Kom", + "NameAlias" => $faker->userName, + "Gender" => $faker->numberBetween(5, 6), + "PlaceOfBirth" => $faker->city, + "Birthdate" => $faker->date('Y-m-d'), + "ZIP" => $faker->postcode, + "Street_1" => $faker->streetAddress, + "Street_2" => "RT " . $faker->numberBetween(1, 10) . " RW " . $faker->numberBetween(1, 10), + "Street_3" => "Blok " . $faker->numberBetween(1, 20), + "City" => $faker->city, + "Province" => $faker->state, + "EmailAddress1" => "AAQ" . (string)$faker->numberBetween(1, 1110).'@gmail.com', + "EmailAddress2" => "BAQ" . (string)$faker->numberBetween(1, 1110).'@gmail.com', + "Phone" => $faker->phoneNumber, + "MobilePhone" => $faker->phoneNumber, + "Race" => (string) $faker->numberBetween(175, 205), + "Country" => (string) $faker->numberBetween(221, 469), + "MaritalStatus" => (string) $faker->numberBetween(8, 15), + "Religion" => (string) $faker->numberBetween(206, 212), + "Ethnic" => (string) $faker->numberBetween(213, 220), + "Citizenship" => "WNI", + "DeathIndicator" => (string) $faker->numberBetween(16, 17), + "LinkTo" => (string) $faker->numberBetween(2, 3), + "Custodian" => 1, + "PatIdt" => [ + "IdentifierType" => "KTP", + "Identifier" => "numberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetweennumberBetween" + // "Identifier" => $faker->nik() ?? $faker->numerify('################') + ], + "PatAtt" => [ + [ "Address" => "/api/upload/" . $faker->word . ".jpg" ] + ], + "PatCom" => $faker->sentence, + ]; - // $result->assertStatus(422); - // $result->assertJSONFragment(["field" => "patidt"]); - // } + if($payload['DeathIndicator'] == '16') { + $payload['DeathDateTime'] = $faker->date('Y-m-d H:i:s'); + } else { + $payload['DeathDateTime'] = null; + } - // public function testCreateWithoutAttachments() { - // $payload = [ - // "PatientID" => "PX003", - // "NameFirst" => "NoAttach", - // "Identity" => [ - // "IdentifierType" => "KTP", - // "Identifier" => "99999" - // ] - // ]; + $result = $this->withBodyFormat('json')->post($this->endpoint, $payload); - // $result = $this->withBodyFormat('json')->post($this->endpoint, $payload); + $result->assertArrayHasKey("errors", $result); + } - // $result->assertStatus(201); - // } + // 201 - Passed + public function testCreateWithoutAttachments() { + $faker = Factory::create('id_ID'); - // public function testCreateWithoutComment() { - // $payload = [ - // "PatientID" => "PX004", - // "NameFirst" => "NoComment", - // "Identity" => [ - // "IdentifierType" => "SIM", - // "Identifier" => "A12345" - // ], - // "Attachments" => [ - // ["Address" => "Jl. Melati No.2"] - // ] - // ]; + $payload = [ + "PatientID" => "DUAU" . $faker->numberBetween(1, 1000).$faker->numberBetween(1, 1000), + "AlternatePID" => "DMAU" . $faker->numberBetween(5, 1000).$faker->numberBetween(1, 1000), + "Prefix" => $faker->title, + "NameFirst" => $faker->firstName, + "NameMiddle" => $faker->firstName, + "NameMaiden" => $faker->firstName, + "NameLast" => $faker->lastName, + "Suffix" => "S.Kom", + "NameAlias" => $faker->userName, + "Gender" => $faker->numberBetween(5, 6), + "PlaceOfBirth" => $faker->city, + "Birthdate" => $faker->date('Y-m-d'), + "ZIP" => $faker->postcode, + "Street_1" => $faker->streetAddress, + "Street_2" => "RT " . $faker->numberBetween(1, 10) . " RW " . $faker->numberBetween(1, 10), + "Street_3" => "Blok " . $faker->numberBetween(1, 20), + "City" => $faker->city, + "Province" => $faker->state, + "EmailAddress1" => "AiA" . $faker->numberBetween(1, 1110).$faker->numberBetween(1, 1110).'@gmail.com', + "EmailAddress2" => "BiA" . $faker->numberBetween(1, 1110).$faker->numberBetween(1, 1110).'@gmail.com', + "Phone" => $faker->phoneNumber, + "MobilePhone" => $faker->phoneNumber, + "Race" => (string) $faker->numberBetween(175, 205), + "Country" => (string) $faker->numberBetween(221, 469), + "MaritalStatus" => (string) $faker->numberBetween(8, 15), + "Religion" => (string) $faker->numberBetween(206, 212), + "Ethnic" => (string) $faker->numberBetween(213, 220), + "Citizenship" => "WNI", + "DeathIndicator" => (string) $faker->numberBetween(16, 17), + "LinkTo" => (string) $faker->numberBetween(2, 3), + "Custodian" => 1, + "PatIdt" => [ + "IdentifierType" => "KTP", + "Identifier" => $faker->nik() ?? $faker->numerify('################') + ], + "PatAtt" => [], + "PatCom" => $faker->sentence, + ]; - // $result = $this->withBodyFormat('json')->post($this->endpoint, $payload); + if($payload['DeathIndicator'] == '16') { + $payload['DeathDateTime'] = $faker->date('Y-m-d H:i:s'); + } else { + $payload['DeathDateTime'] = null; + } - // $result->assertStatus(201); - // } + $result = $this->withBodyFormat('json')->post($this->endpoint, $payload); - // public function testCreateDatabaseError() { - // // Insert patient pertama - // $payload = [ - // "InternalPID" => 999, // Force ID - // "PatientID" => "PX005", - // "NameFirst" => "First", - // "Identity" => [ - // "IdentifierType" => "KTP", - // "Identifier" => "DBERR1" - // ] - // ]; - // $this->withBodyFormat('json')->post($this->endpoint, $payload); + $result->assertStatus(201); + } - // // Insert kedua dengan InternalPID sama → trigger error - // $payload["NameFirst"] = "Second"; - // $payload["Identity"]["Identifier"] = "DBERR2"; + // 201 - Passed + public function testCreateWithoutPatComments() { + $faker = Factory::create('id_ID'); - // $result = $this->withBodyFormat('json')->post($this->endpoint, $payload); + $payload = [ + "PatientID" => "DUALU" . $faker->numberBetween(1, 1000).$faker->numberBetween(1, 1000), + "AlternatePID" => "DMALU" . $faker->numberBetween(5, 1000).$faker->numberBetween(1, 1000), + "Prefix" => $faker->title, + "NameFirst" => $faker->firstName, + "NameMiddle" => $faker->firstName, + "NameMaiden" => $faker->firstName, + "NameLast" => $faker->lastName, + "Suffix" => "S.Kom", + "NameAlias" => $faker->userName, + "Gender" => $faker->numberBetween(5, 6), + "PlaceOfBirth" => $faker->city, + "Birthdate" => $faker->date('Y-m-d'), + "ZIP" => $faker->postcode, + "Street_1" => $faker->streetAddress, + "Street_2" => "RT " . $faker->numberBetween(1, 10) . " RW " . $faker->numberBetween(1, 10), + "Street_3" => "Blok " . $faker->numberBetween(1, 20), + "City" => $faker->city, + "Province" => $faker->state, + "EmailAddress1" => "AiAe" . $faker->numberBetween(1, 1110).$faker->numberBetween(1, 1110).'@gmail.com', + "EmailAddress2" => "BiAe" . $faker->numberBetween(1, 1110).$faker->numberBetween(1, 1110).'@gmail.com', + "Phone" => $faker->phoneNumber, + "MobilePhone" => $faker->phoneNumber, + "Race" => (string) $faker->numberBetween(175, 205), + "Country" => (string) $faker->numberBetween(221, 469), + "MaritalStatus" => (string) $faker->numberBetween(8, 15), + "Religion" => (string) $faker->numberBetween(206, 212), + "Ethnic" => (string) $faker->numberBetween(213, 220), + "Citizenship" => "WNI", + "DeathIndicator" => (string) $faker->numberBetween(16, 17), + "LinkTo" => (string) $faker->numberBetween(2, 3), + "Custodian" => 1, + "PatIdt" => [ + "IdentifierType" => "KTP", + "Identifier" => $faker->nik() ?? $faker->numerify('################') + ], + "PatAtt" => [ + [ "Address" => "/api/upload/" . $faker->word . ".jpg" ], + [ "Address" => "/api/upload/" . $faker->word . ".jpg" ], + [ "Address" => "/api/upload/" . $faker->word . ".jpg" ] + ], + "PatCom" => null, + ]; - // $result->assertStatus(500); - // } + if($payload['DeathIndicator'] == '16') { + $payload['DeathDateTime'] = $faker->date('Y-m-d H:i:s'); + } else { + $payload['DeathDateTime'] = null; + } + + $result = $this->withBodyFormat('json')->post($this->endpoint, $payload); + + $result->assertStatus(201); + } + + // 500 - Passed + public function testCreateDatabaseError() { + $faker = Factory::create('id_ID'); + + // Insert patient pertama + $payload = [ + "PatientID" => "DaUALU" . $faker->numberBetween(1, 1000).$faker->numberBetween(1, 1000), + "AlternatePID" => "DaMALU" . $faker->numberBetween(5, 1000).$faker->numberBetween(1, 1000), + "Prefix" => $faker->title, + "NameFirst" => $faker->firstName, + "NameMiddle" => $faker->firstName, + "NameMaiden" => $faker->firstName, + "NameLast" => $faker->lastName, + "Suffix" => "S.Kom", + "NameAlias" => $faker->userName, + "Gender" => $faker->numberBetween(5, 6), + "PlaceOfBirth" => $faker->city, + "Birthdate" => $faker->date('Y-m-d'), + "ZIP" => $faker->postcode, + "Street_1" => $faker->streetAddress, + "Street_2" => "RT " . $faker->numberBetween(1, 10) . " RW " . $faker->numberBetween(1, 10), + "Street_3" => "Blok " . $faker->numberBetween(1, 20), + "City" => $faker->city, + "Province" => $faker->state, + "EmailAddress1" => "AiaAe" . $faker->numberBetween(1, 1110).$faker->numberBetween(1, 1110).'@gmail.com', + "EmailAddress2" => "BiaAe" . $faker->numberBetween(1, 1110).$faker->numberBetween(1, 1110).'@gmail.com', + "Phone" => $faker->phoneNumber, + "MobilePhone" => $faker->phoneNumber, + "Race" => (string) $faker->numberBetween(175, 205), + "Country" => (string) $faker->numberBetween(221, 469), + "MaritalStatus" => (string) $faker->numberBetween(8, 15), + "Religion" => (string) $faker->numberBetween(206, 212), + "Ethnic" => (string) $faker->numberBetween(213, 220), + "Citizenship" => "WNI", + "DeathIndicator" => (string) $faker->numberBetween(16, 17), + "LinkTo" => (string) $faker->numberBetween(2, 3), + "Custodian" => 1, + "PatIdt" => [ + "IdentifierType" => "KTP", + "Identifier" => $faker->nik() ?? $faker->numerify('################') + ], + "PatAtt" => [ + [ "Address" => "/api/upload/" . $faker->word . ".jpg" ], + [ "Address" => "/api/upload/" . $faker->word . ".jpg" ], + [ "Address" => "/api/upload/" . $faker->word . ".jpg" ] + ], + "PatCom" => [], // Ini Salah + ]; + + if($payload['DeathIndicator'] == '16') { + $payload['DeathDateTime'] = $faker->date('Y-m-d H:i:s'); + } else { + $payload['DeathDateTime'] = null; + } + + $result = $this->withBodyFormat('json')->post($this->endpoint, $payload); + + $result->assertStatus(500); + } // public function testCreateDuplicateIdentifier() { // $payload = [ @@ -178,44 +373,4 @@ class PatientCreateTest extends CIUnitTestCase // $result->assertStatus(422); // } - - // public function testCreateWithLinkTo() { - // $payload = [ - // "PatientID" => "PX008", - // "NameFirst" => "LinkTo", - // "Identity" => [ - // "IdentifierType" => "KTP", - // "Identifier" => "LINK123" - // ], - // "LinkTo" => [ - // ["InternalPID" => 1], - // ["InternalPID" => 2] - // ] - // ]; - - // $result = $this->withBodyFormat('json')->post($this->endpoint, $payload); - - // $result->assertStatus(201); - - // $data = json_decode($result->getBody(), true); - // $this->assertIsInt($data["data"]); - // } - - // public function testCreateWithNumericAndNullFields() { - // $payload = [ - // "PatientID" => "PX009", - // "NameFirst" => "Numeric", - // "Gender" => null, - // "RaceID" => "3", - // "DeathIndicator" => 0, - // "Identity" => [ - // "IdentifierType" => "SIM", - // "Identifier" => "NUM123" - // ] - // ]; - - // $result = $this->withBodyFormat('json')->post($this->endpoint, $payload); - - // $result->assertStatus(201); - // } } diff --git a/tests/feature/Patients/PatientDeleteTest.php b/tests/feature/Patients/PatientDeleteTest.php index b9bc567..f5b497c 100644 --- a/tests/feature/Patients/PatientDeleteTest.php +++ b/tests/feature/Patients/PatientDeleteTest.php @@ -10,87 +10,87 @@ class PatientDeleteTest extends CIUnitTestCase use FeatureTestTrait; protected $endpoint = 'api/patient'; - // 500 error catch - public function testDeleteWithoutInternalPID() { - $result = $this->withBodyFormat('json') - ->delete($this->endpoint, []); + // // 500 error catch + // public function testDeleteWithoutInternalPID() { + // $result = $this->withBodyFormat('json') + // ->delete($this->endpoint, []); - $result->assertStatus(500); - $result->assertJSONFragment([ - 'status' => 500, - 'messages' => [ - 'error' => 'Internal server error: Undefined array key "InternalPID"' - ] - ]); - } + // $result->assertStatus(500); + // $result->assertJSONFragment([ + // 'status' => 500, + // 'messages' => [ + // 'error' => 'Internal server error: Undefined array key "InternalPID"' + // ] + // ]); + // } - // 400 - public function testDeleteWitWrongInternalPID() { - $result = $this->withBodyFormat('json') - ->delete($this->endpoint, [ - // Pilih salah satu - // 'InternalPID' => [], - // 'InternalPID' => 0, - // 'InternalPID' => '0', - // 'InternalPID' => '', - 'InternalPID' => null, - ]); + // // 400 + // public function testDeleteWitWrongInternalPID() { + // $result = $this->withBodyFormat('json') + // ->delete($this->endpoint, [ + // // Pilih salah satu + // // 'InternalPID' => [], + // // 'InternalPID' => 0, + // // 'InternalPID' => '0', + // // 'InternalPID' => '', + // 'InternalPID' => null, + // ]); - $result->assertStatus(400); - $result->assertJSONFragment([ - 'status' => 'error', - 'message' => 'Patient ID must be a valid integer.' - ]); - } + // $result->assertStatus(400); + // $result->assertJSONFragment([ + // 'status' => 'error', + // 'message' => 'Patient ID must be a valid integer.' + // ]); + // } - // 404 not found - public function testDeleteNotFound() { - $testId = 9999; + // // 404 not found + // public function testDeleteNotFound() { + // $testId = 9999; - $result = $this->withBodyFormat('json') - ->delete($this->endpoint, [ - 'InternalPID' => $testId - ]); + // $result = $this->withBodyFormat('json') + // ->delete($this->endpoint, [ + // 'InternalPID' => $testId + // ]); - $result->assertStatus(404); - $result->assertJSONFragment([ - 'status' => 404, - 'error' => 404, - 'messages' => [ - 'error' => 'Patient ID with '.$testId.' not found.' - ] - ]); - } + // $result->assertStatus(404); + // $result->assertJSONFragment([ + // 'status' => 404, + // 'error' => 404, + // 'messages' => [ + // 'error' => 'Patient ID with '.$testId.' not found.' + // ] + // ]); + // } - // 200 ok - public function testDeleteSuccess() { + // // 200 ok + // public function testDeleteSuccess() { - $testId = 3; - // Anggap patient 1 2 3 ada di seed DB - $result = $this->withBodyFormat('json') - ->delete($this->endpoint, [ - 'InternalPID' => $testId - ]); + // $testId = 3; + // // Anggap patient 1 2 3 ada di seed DB + // $result = $this->withBodyFormat('json') + // ->delete($this->endpoint, [ + // 'InternalPID' => $testId + // ]); - $result->assertStatus(200); - $result->assertJSONFragment([ - 'status' => 'success', - 'message' => 'Patient ID with '.$testId.' deleted successfully.' - ]); - } + // $result->assertStatus(200); + // $result->assertJSONFragment([ + // 'status' => 'success', + // 'message' => 'Patient ID with '.$testId.' deleted successfully.' + // ]); + // } - // 400 - SQL Inject - public function testDeleteServerError() { - // Simulasi: kirim input aneh yang trigger exception - $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.' - ]); - } + // // 400 - SQL Inject + // public function testDeleteServerError() { + // // Simulasi: kirim input aneh yang trigger exception + // $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.' + // ]); + // } } diff --git a/tests/feature/Patients/PatientIndexTest.php b/tests/feature/Patients/PatientIndexTest.php index 43f55da..fc3f65a 100644 --- a/tests/feature/Patients/PatientIndexTest.php +++ b/tests/feature/Patients/PatientIndexTest.php @@ -12,7 +12,7 @@ class PatientIndexTest extends CIUnitTestCase protected $endpoint = 'api/patient'; /** - * Case 1: tanpa parameter, harus 200 dan status success + * Case 1: tanpa parameter, harus 200 dan status success - Passed */ public function testIndexWithoutParams() { $result = $this->call('get', $this->endpoint); @@ -24,7 +24,7 @@ class PatientIndexTest extends CIUnitTestCase } /** - * Case 2: parameter Name yang tidak ada → return kosong [] + * Case 2: parameter Name yang tidak ada → return kosong [] - Passed */ public function testIndexWithWrongNameParam() { $result = $this->call('get', $this->endpoint, [ @@ -39,7 +39,7 @@ class PatientIndexTest extends CIUnitTestCase } /** - * Case 3: parameter Name benar → return array tidak kosong + * Case 3: parameter Name benar → return array tidak kosong - Passed */ public function testIndexWithCorrectNameParam() { // Sesuaikan dengan data di database test Anda @@ -58,7 +58,7 @@ class PatientIndexTest extends CIUnitTestCase } /** - * Case 4: parameter InternalPID → return data sesuai ID + * Case 4: parameter InternalPID → return data sesuai ID - Passed */ public function testIndexWithInternalPID() { $result = $this->call('get', $this->endpoint, [ @@ -77,7 +77,7 @@ class PatientIndexTest extends CIUnitTestCase } /** - * Case 5: parameter PatientID → return data sesuai PatientID + * Case 5: parameter PatientID → return data sesuai PatientID - Passed */ public function testIndexWithPatientID() { $result = $this->call('get', $this->endpoint, [ @@ -96,7 +96,7 @@ class PatientIndexTest extends CIUnitTestCase } /** - * Case 6: parameter Birthdate → return data sesuai tanggal + * Case 6: parameter Birthdate → return data sesuai tanggal - Passed */ public function testIndexWithBirthdate() { $result = $this->call('get', $this->endpoint, [ @@ -115,7 +115,7 @@ class PatientIndexTest extends CIUnitTestCase } /** - * Case 7: Simulasi server error (optional, jika bisa trigger) + * Case 7: Simulasi server error (optional, jika bisa trigger) - Passed */ public function testIndexServerErrorSimulation() { // Misalnya panggil dengan param aneh untuk trigger exception @@ -123,12 +123,6 @@ class PatientIndexTest extends CIUnitTestCase 'InternalPID' => "' OR 1=2 --" ]); - // dd([ - // 'status' => $result->getStatusCode(), - // 'body' => $result->getBody(), - // 'json' => $result->getJSON(), - // ]); - // Boleh assert 200 (jika sanitasi bagus) atau 500 (kalau exception) $this->assertContains($result->getStatusCode(), [200, 500, null]); } diff --git a/tests/feature/Patients/PatientShowTest.php b/tests/feature/Patients/PatientShowTest.php index bb22e05..e8126de 100644 --- a/tests/feature/Patients/PatientShowTest.php +++ b/tests/feature/Patients/PatientShowTest.php @@ -11,28 +11,29 @@ class PatientShowTest extends CIUnitTestCase protected $endpoint = 'api/patient'; - // 200 ok not found + // 200 ok not found - Passed public function testShowNotFound() { $result = $this->get($this->endpoint . '/90909090'); $result->assertStatus(200); $result->assertJSONFragment([ 'status' => 'success', - "message" => "Patient with ID 90909090 not found.", - 'data' => [] + "message" => "data not found." ]); } - // 200 ok + // 200 ok found - Passed public function testShowSingleRow() { // Pastikan DB test punya seed patient InternalPID=10 tanpa id/addr $result = $this->get($this->endpoint . '/1'); - $result->assertStatus(200); $data = $result->getJSON(true); $data = json_decode($data, true); - $this->assertEquals('success', $data['status']); - // $this->assertNull($data['data']['Attachments']); + $result->assertStatus(200); + $result->assertArrayHasKey('data', $data); + $result->assertIsArray($data['data']); + + // $this->assertEquals('success', $data['status']); } } diff --git a/tests/feature/Patients/PatientUpdateTest.php b/tests/feature/Patients/PatientUpdateTest.php index b128557..28b243b 100644 --- a/tests/feature/Patients/PatientUpdateTest.php +++ b/tests/feature/Patients/PatientUpdateTest.php @@ -4,86 +4,155 @@ namespace Tests\Feature\Patients; use CodeIgniter\Test\FeatureTestTrait; use CodeIgniter\Test\CIUnitTestCase; +use Faker\Factory; class PatientUpdateTest extends CIUnitTestCase { use FeatureTestTrait; + protected $endpoint = 'api/patient'; + + public function testUpdatePatientSuccess() + { + $faker = Factory::create('id_ID'); - // Belon + $payload = [ + "InternalPID" => 1, + "PatientID" => "UPDT" . $faker->numberBetween(100, 999), + "NameFirst" => $faker->firstName, + "NameLast" => $faker->lastName, + "Gender" => (string) $faker->numberBetween(5, 6), + "Birthdate" => $faker->date('Y-m-d'), + "DeathDateTime" => null, + "EmailAddress1" => "asasa@gmail.com", + "PlaceOfBirth" => $faker->city, + "Race" => (string) $faker->numberBetween(175, 205), + "Religion" => (string) $faker->numberBetween(206, 212), + "Country" => (string) $faker->numberBetween(221, 469), + "MaritalStatus" => (string) $faker->numberBetween(8, 15), + "Citizenship" => "WNI", + "LinkTo" => null, + "PatIdt" => [ + "IdentifierType" => "KTP", + "Identifier" => $faker->nik(), + ], + "PatCom" => $faker->sentence(5), + "PatAtt" => [ + [ "Address" => "/api/upload/" . $faker->word . ".jpg" ], + [ "Address" => "/api/upload/" . $faker->word . ".jpg" ] + ] + ]; -// public function testUpdateFail() -// { -// $payload = []; -// $result = $this->withBodyFormat('json') -// ->call('patch', 'api/patient', $payload); -// $result->assertStatus(400); + $result = $this->withBodyFormat('json')->patch($this->endpoint, $payload); -// $payload = [ -// "InternalPID"=> 100, -// "PatientID"=> "SMAJ50", //Wajib Ganti -// "AlternatePID"=> "P0234", -// "Prefix"=> "Mr.", -// "NameFirst"=> "Budi", -// "NameMiddle"=> "Santoso", -// "NameMaiden"=> "Kiki", -// "NameLast"=> "Wijaya", -// "Suffix"=> "S.kom", -// "NameAlias"=> "Bud", -// "Gender"=> "1", -// 'EmailAddress1' => 'kaka@gmail.a1com', //Wajib Ganti -// 'Identity' => [ -// "IdentifierType" => "KTP", -// "Identifier" => "317409050590100" //Wajib Ganti -// ] -// ]; -// $result = $this->withBodyFormat('json') -// ->call('patch', 'api/patient', $payload); -// $result->assertStatus(404); + $result->assertStatus(201); + } -// $payload = [ -// "PatientID"=> "SMAJ50", //Wajib Ganti -// "AlternatePID"=> "P0234", -// "Prefix"=> "Mr.", -// "NameFirst"=> "Budi", -// "NameMiddle"=> "Santoso", -// "NameMaiden"=> "Kiki", -// "NameLast"=> "Wijaya", -// "Suffix"=> "S.kom", -// "NameAlias"=> "Bud", -// "Gender"=> "1", -// 'EmailAddress1' => 'kaka@gmail.a1com', //Wajib Ganti -// 'Identity' => [ -// "IdentifierType" => "KTP", -// "Identifier" => "317409050590100" //Wajib Ganti -// ] -// ]; -// $result = $this->withBodyFormat('json') -// ->call('patch', 'api/patient', $payload); -// $result->assertStatus(500); -// } + // /** + // * ❌ Test gagal karena InternalPID kosong (validasi) + // */ + public function testUpdatePatientValidationError() + { + $faker = Factory::create('id_ID'); + + $payload = [ + "NameFirst" => $faker->firstName, + "Gender" => (string) $faker->numberBetween(5, 6), + "Birthdate" => $faker->date('Y-m-d'), + // Tidak kirim InternalPID + ]; + + $result = $this->withBodyFormat('json')->patch($this->endpoint, $payload); + $json = $result->getJSON(); + $data = json_decode($json, true); + + $result->assertStatus(400); + $this->assertArrayHasKey('errors', $data); + } + + // /** + // * ✅ Test update dengan address baru & hapus address lama + // */ + public function testUpdatePatientWithAddressChange() + { + $faker = Factory::create('id_ID'); + + $payload = [ + "InternalPID" => 1, + "PatientID" => "UPDT" . $faker->numberBetween(100, 999), + "NameFirst" => $faker->firstName, + "NameLast" => $faker->lastName, + "Gender" => (string) $faker->numberBetween(5, 6), + "Birthdate" => $faker->date('Y-m-d'), + "DeathDateTime" => null, + "EmailAddress1" => "asasa@gmail.com", + "PlaceOfBirth" => $faker->city, + "Race" => (string) $faker->numberBetween(175, 205), + "Religion" => (string) $faker->numberBetween(206, 212), + "Country" => (string) $faker->numberBetween(221, 469), + "MaritalStatus" => (string) $faker->numberBetween(8, 15), + "Citizenship" => "WNI", + "LinkTo" => null, + "PatIdt" => [ + "IdentifierType" => "KTP", + "Identifier" => $faker->nik(), + ], + "PatCom" => $faker->sentence(5), + "PatAtt" => [ + [ "Address" => "/api/upload/" . $faker->word . ".jpg" ], + [ "Address" => "/api/upload/" . $faker->word . ".jpg" ], + [ "Address" => "/api/upload/" . $faker->word . ".jpg" ], + [ "Address" => "/api/upload/" . $faker->word . ".jpg" ] + ] + ]; + + $result = $this->withBodyFormat('json')->patch($this->endpoint, $payload); + + $result->assertStatus(201); + + $json = $result->getJSON(); + $data = json_decode($json, true); + + $this->assertEquals('success', $data['status']); + } + + // /** + // * ⚠️ Test update kosong → semua pattat address dihapus (soft delete) + // */ + public function testUpdatePatientEmptyAddress() + { + $faker = Factory::create('id_ID'); + + $payload = [ + "InternalPID" => 1, + "PatientID" => "UPDT" . $faker->numberBetween(100, 999), + "NameFirst" => $faker->firstName, + "NameLast" => $faker->lastName, + "Gender" => (string) $faker->numberBetween(5, 6), + "Birthdate" => $faker->date('Y-m-d'), + "DeathDateTime" => null, + "EmailAddress1" => "asasa@gmail.com", + "PlaceOfBirth" => $faker->city, + "Race" => (string) $faker->numberBetween(175, 205), + "Religion" => (string) $faker->numberBetween(206, 212), + "Country" => (string) $faker->numberBetween(221, 469), + "MaritalStatus" => (string) $faker->numberBetween(8, 15), + "Citizenship" => "WNI", + "LinkTo" => null, + "PatIdt" => [ + "IdentifierType" => "KTP", + "Identifier" => $faker->nik(), + ], + "PatCom" => $faker->sentence(5), + "PatAtt" => [] + ]; + + $result = $this->withBodyFormat('json')->patch($this->endpoint, $payload); + + $json = $result->getJSON(); + $data = json_decode($json, true); + + $result->assertStatus(201); + $this->assertEquals('success', $data['status']); + } -// public function testUpdateSuccess() -// { -// $payload = [ -// "InternalPID"=> 1, //Wajib Ganti -// "PatientID"=> "SMAJ50", -// "AlternatePID"=> "asasasa", -// "Prefix"=> "Mr.", -// "NameFirst"=> "Budi", -// "NameMiddle"=> "Santoso", -// "NameMaiden"=> "Kiki", -// "NameLast"=> "Wijaya", -// "Suffix"=> "S.kom", -// "NameAlias"=> "Bud", -// "Gender"=> "1", -// 'EmailAddress1' => 'kaka@gmail.a1com', -// 'Identity' => [ -// "IdentifierType" => "KTP", -// "Identifier" => "317409050590100" -// ] -// ]; -// $result = $this->withBodyFormat('json') -// ->call('patch', 'api/patient', $payload); -// $result->assertStatus(200); -// } }