user = User::create([ 'public_id' => 'test-user-assess-001', 'name' => 'Assessment Test User', 'email' => 'assess@example.com', ]); $this->organization = Organization::create([ 'owner_ref' => $this->user->public_id, 'name' => 'Assess Clinic', 'slug' => 'assess-clinic', 'timezone' => 'UTC', 'settings' => ['onboarded' => true], ]); Member::create([ 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'user_ref' => $this->user->public_id, 'role' => 'doctor', ]); $this->branch = Branch::create([ 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'name' => 'Main', 'is_active' => true, ]); $this->patient = Patient::create([ 'uuid' => (string) Str::uuid(), 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_number' => 'LC-2026-ASSESS-001', 'first_name' => 'Ama', 'last_name' => 'Mensah', ]); } public function test_template_auto_generates_uuid_and_has_no_owner_ref(): void { $template = AssessmentTemplate::create([ 'code' => 'universal_intake', 'name' => 'Universal Intake', 'category' => AssessmentTemplate::CATEGORY_UNIVERSAL, 'version' => 1, 'is_current' => true, 'is_active' => true, 'meta' => ['capture_roles' => ['nurse', 'doctor']], ]); $this->assertNotEmpty($template->uuid); $this->assertTrue(Str::isUuid($template->uuid)); $this->assertNull($template->organization_id); $this->assertFalse(isset($template->getAttributes()['owner_ref'])); $this->assertSame(['capture_roles' => ['nurse', 'doctor']], $template->meta); $this->assertSame('uuid', $template->getRouteKeyName()); } public function test_system_template_code_version_is_unique(): void { AssessmentTemplate::create([ 'code' => 'nihss', 'name' => 'NIHSS v1', 'category' => AssessmentTemplate::CATEGORY_DISEASE, 'version' => 1, 'is_current' => true, ]); $this->expectException(QueryException::class); AssessmentTemplate::create([ 'code' => 'nihss', 'name' => 'NIHSS v1 duplicate', 'category' => AssessmentTemplate::CATEGORY_DISEASE, 'version' => 1, 'is_current' => false, ]); } public function test_same_code_different_versions_allowed(): void { AssessmentTemplate::create([ 'code' => 'mrs', 'name' => 'mRS v1', 'category' => AssessmentTemplate::CATEGORY_DISEASE, 'version' => 1, 'is_current' => false, ]); $v2 = AssessmentTemplate::create([ 'code' => 'mrs', 'name' => 'mRS v2', 'category' => AssessmentTemplate::CATEGORY_DISEASE, 'version' => 2, 'is_current' => true, ]); $this->assertSame(2, AssessmentTemplate::where('code', 'mrs')->count()); $this->assertTrue($v2->is_current); $this->assertSame($v2->id, AssessmentTemplate::currentSystemByCode('mrs')?->id); } public function test_question_unique_per_template_and_ordered_relation(): void { $template = AssessmentTemplate::create([ 'code' => 'fixture_scale', 'name' => 'Fixture', 'category' => AssessmentTemplate::CATEGORY_SCREENING, 'version' => 1, 'is_current' => true, ]); AssessmentQuestion::create([ 'template_id' => $template->id, 'code' => 'item_b', 'label' => 'Item B', 'answer_type' => AssessmentQuestion::TYPE_SCORE_ITEM, 'sort_order' => 2, 'is_required' => true, ]); AssessmentQuestion::create([ 'template_id' => $template->id, 'code' => 'item_a', 'label' => 'Item A', 'answer_type' => AssessmentQuestion::TYPE_SCORE_ITEM, 'sort_order' => 1, 'is_required' => true, ]); $codes = $template->questions()->pluck('code')->all(); $this->assertSame(['item_a', 'item_b'], $codes); $this->expectException(QueryException::class); AssessmentQuestion::create([ 'template_id' => $template->id, 'code' => 'item_a', 'label' => 'Duplicate', 'answer_type' => AssessmentQuestion::TYPE_TEXT, 'sort_order' => 3, ]); } public function test_assessment_instance_relations_and_owned_scope(): void { $template = AssessmentTemplate::create([ 'code' => 'universal_intake', 'name' => 'Universal Intake', 'category' => AssessmentTemplate::CATEGORY_UNIVERSAL, 'version' => 1, 'is_current' => true, ]); $question = AssessmentQuestion::create([ 'template_id' => $template->id, 'code' => 'chief_complaint', 'label' => 'Chief complaint', 'answer_type' => AssessmentQuestion::TYPE_TEXT, 'sort_order' => 1, 'is_required' => true, ]); $visit = Visit::create([ 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_id' => $this->patient->id, 'status' => Visit::STATUS_OPEN, 'checked_in_at' => now(), ]); $consultation = Consultation::create([ 'owner_ref' => $this->user->public_id, 'visit_id' => $visit->id, 'patient_id' => $this->patient->id, 'status' => Consultation::STATUS_DRAFT, 'started_at' => now(), ]); $assessment = Assessment::create([ 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_id' => $this->patient->id, 'template_id' => $template->id, 'consultation_id' => $consultation->id, 'visit_id' => $visit->id, 'status' => Assessment::STATUS_DRAFT, 'started_by' => $this->user->public_id, ]); $this->assertNotEmpty($assessment->uuid); $this->assertTrue(Str::isUuid($assessment->uuid)); $this->assertTrue($assessment->isDraft()); $this->assertFalse($assessment->isCompleted()); AssessmentAnswer::create([ 'owner_ref' => $this->user->public_id, 'assessment_id' => $assessment->id, 'question_id' => $question->id, 'value_text' => 'Headache for 2 days', ]); $this->assertSame(1, $assessment->answers()->count()); $this->assertSame('Headache for 2 days', $assessment->answers->first()->authoritativeValue()); $this->assertTrue($this->patient->assessments->contains($assessment)); $this->assertTrue($consultation->assessments->contains($assessment)); $this->assertTrue($visit->assessments->contains($assessment)); $this->assertSame($template->id, $assessment->template->id); $this->assertSame(1, Assessment::query()->owned($this->user->public_id)->count()); $this->assertSame(0, Assessment::query()->owned('other-owner')->count()); } public function test_answer_unique_per_assessment_and_question(): void { $template = AssessmentTemplate::create([ 'code' => 'fixture', 'name' => 'Fixture', 'category' => AssessmentTemplate::CATEGORY_UNIVERSAL, 'version' => 1, 'is_current' => true, ]); $question = AssessmentQuestion::create([ 'template_id' => $template->id, 'code' => 'pain_score', 'label' => 'Pain', 'answer_type' => AssessmentQuestion::TYPE_NUMBER, 'sort_order' => 1, ]); $assessment = Assessment::create([ 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_id' => $this->patient->id, 'template_id' => $template->id, 'status' => Assessment::STATUS_DRAFT, ]); AssessmentAnswer::create([ 'owner_ref' => $this->user->public_id, 'assessment_id' => $assessment->id, 'question_id' => $question->id, 'value_number' => 5, ]); $this->expectException(QueryException::class); AssessmentAnswer::create([ 'owner_ref' => $this->user->public_id, 'assessment_id' => $assessment->id, 'question_id' => $question->id, 'value_number' => 7, ]); } public function test_deleting_assessment_cascades_answers_but_not_template(): void { $template = AssessmentTemplate::create([ 'code' => 'cascade_fixture', 'name' => 'Cascade', 'category' => AssessmentTemplate::CATEGORY_UNIVERSAL, 'version' => 1, 'is_current' => true, ]); $question = AssessmentQuestion::create([ 'template_id' => $template->id, 'code' => 'q1', 'label' => 'Q1', 'answer_type' => AssessmentQuestion::TYPE_BOOLEAN, 'sort_order' => 1, ]); $assessment = Assessment::create([ 'owner_ref' => $this->user->public_id, 'organization_id' => $this->organization->id, 'patient_id' => $this->patient->id, 'template_id' => $template->id, 'status' => Assessment::STATUS_DRAFT, ]); AssessmentAnswer::create([ 'owner_ref' => $this->user->public_id, 'assessment_id' => $assessment->id, 'question_id' => $question->id, 'value_boolean' => true, ]); $assessmentId = $assessment->id; $assessment->forceDelete(); $this->assertDatabaseMissing('care_assessments', ['id' => $assessmentId]); $this->assertDatabaseMissing('care_assessment_answers', ['assessment_id' => $assessmentId]); $this->assertDatabaseHas('care_assessment_templates', ['id' => $template->id]); $this->assertDatabaseHas('care_assessment_questions', ['id' => $question->id]); } }