withoutMiddleware(EnsurePlatformSession::class); $this->user = User::create([ 'public_id' => 'scoring-user', 'name' => 'Scoring User', 'email' => 'scoring@example.com', ]); $this->organization = Organization::create([ 'owner_ref' => $this->user->public_id, 'name' => 'Score Clinic', 'slug' => 'score-clinic', 'timezone' => 'UTC', 'settings' => [ 'onboarded' => true, 'rollout' => [CareFeatures::ASSESSMENTS_ENGINE => true], ], ]); $this->member = 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-SCORE-001', 'first_name' => 'Ama', 'last_name' => 'Score', ]); } public function test_sum_items_materializes_total_and_max(): void { $template = AssessmentTemplate::create([ 'code' => 'nihss_fixture', 'name' => 'NIHSS fixture', 'category' => AssessmentTemplate::CATEGORY_DISEASE, 'version' => 1, 'is_current' => true, 'scoring_strategy' => 'sum_items', 'meta' => ['capture_roles' => ['doctor']], ]); AssessmentQuestion::create([ 'template_id' => $template->id, 'code' => 'loc', 'label' => 'LOC', 'answer_type' => AssessmentQuestion::TYPE_SCORE_ITEM, 'options' => [ 'choices' => [ ['code' => '0', 'label' => 'Alert', 'score' => 0], ['code' => '2', 'label' => 'Obtunded', 'score' => 2], ], ], 'is_required' => true, 'sort_order' => 1, 'score_key' => 'loc', ]); AssessmentQuestion::create([ 'template_id' => $template->id, 'code' => 'motor', 'label' => 'Motor', 'answer_type' => AssessmentQuestion::TYPE_SCORE_ITEM, 'options' => [ 'max' => 4, 'choices' => [ ['code' => '0', 'label' => 'None', 'score' => 0], ['code' => '3', 'label' => 'Severe', 'score' => 3], ], ], 'is_required' => true, 'sort_order' => 2, 'score_key' => 'motor', ]); $this->actingAs($this->user) ->post(route('care.assessments.store', $this->patient), ['template_code' => 'nihss_fixture']) ->assertRedirect(); $assessment = Assessment::first(); $this->actingAs($this->user) ->put(route('care.assessments.update', $assessment), [ 'answers' => ['loc' => '2', 'motor' => '3'], ]) ->assertRedirect(); $this->actingAs($this->user) ->post(route('care.assessments.complete', $assessment)) ->assertRedirect(); $score = AssessmentScore::where('assessment_id', $assessment->id)->first(); $this->assertNotNull($score); $this->assertEquals(5, (float) $score->total_score); $this->assertEquals(6, (float) $score->max_score); // 2 + 4 $this->assertSame('nihss_fixture', $score->template_code); $this->assertEquals(2, $score->subscores['loc']); $this->assertEquals(3, $score->subscores['motor']); $this->actingAs($this->user) ->get(route('care.assessments.show', $assessment)) ->assertOk() ->assertSee('Score') ->assertSee('5'); } public function test_single_value_strategy_for_mrs_style(): void { $template = AssessmentTemplate::create([ 'code' => 'mrs_fixture', 'name' => 'mRS fixture', 'category' => AssessmentTemplate::CATEGORY_DISEASE, 'version' => 1, 'is_current' => true, 'scoring_strategy' => 'single_value', 'meta' => ['capture_roles' => ['doctor']], ]); AssessmentQuestion::create([ 'template_id' => $template->id, 'code' => 'mrs_score', 'label' => 'mRS', 'answer_type' => AssessmentQuestion::TYPE_SCORE_ITEM, 'options' => [ 'min' => 0, 'max' => 6, 'choices' => [ ['code' => '0', 'label' => 'None', 'score' => 0], ['code' => '3', 'label' => 'Moderate', 'score' => 3], ['code' => '6', 'label' => 'Dead', 'score' => 6], ], ], 'is_required' => true, 'sort_order' => 1, 'score_key' => 'total', ]); $this->actingAs($this->user) ->post(route('care.assessments.store', $this->patient), ['template_code' => 'mrs_fixture']) ->assertRedirect(); $assessment = Assessment::first(); $this->actingAs($this->user) ->from(route('care.assessments.show', $assessment)) ->put(route('care.assessments.update', $assessment), [ 'answers' => ['mrs_score' => '3'], ]); $this->actingAs($this->user) ->post(route('care.assessments.complete', $assessment)) ->assertRedirect(); $preview = app(ScoringService::class)->preview($assessment->fresh(['template.questions', 'answers.question'])); $this->assertEquals(3, $preview['total']); $this->assertEquals(6, $preview['max']); $score = AssessmentScore::first(); $this->assertEquals(3, (float) $score->total_score); $this->assertEquals(6, (float) $score->max_score); } public function test_null_scoring_strategy_skips_score_row(): void { $template = AssessmentTemplate::create([ 'code' => 'no_score', 'name' => 'No score', 'category' => AssessmentTemplate::CATEGORY_UNIVERSAL, 'version' => 1, 'is_current' => true, 'scoring_strategy' => null, 'meta' => ['capture_roles' => ['doctor', 'nurse']], ]); AssessmentQuestion::create([ 'template_id' => $template->id, 'code' => 'note', 'label' => 'Note', 'answer_type' => AssessmentQuestion::TYPE_TEXT, 'is_required' => true, 'sort_order' => 1, ]); $this->actingAs($this->user) ->post(route('care.assessments.store', $this->patient), ['template_code' => 'no_score']) ->assertRedirect(); $assessment = Assessment::first(); $this->actingAs($this->user) ->put(route('care.assessments.update', $assessment), [ 'answers' => ['note' => 'hello'], ]); $this->actingAs($this->user) ->post(route('care.assessments.complete', $assessment)) ->assertRedirect(); $this->assertSame(0, AssessmentScore::count()); $this->assertTrue($assessment->fresh()->isCompleted()); } public function test_complete_fails_when_score_items_missing(): void { $template = AssessmentTemplate::create([ 'code' => 'need_scores', 'name' => 'Need scores', 'category' => AssessmentTemplate::CATEGORY_DISEASE, 'version' => 1, 'is_current' => true, 'scoring_strategy' => 'sum_items', 'meta' => ['capture_roles' => ['doctor']], ]); AssessmentQuestion::create([ 'template_id' => $template->id, 'code' => 'item_a', 'label' => 'A', 'answer_type' => AssessmentQuestion::TYPE_SCORE_ITEM, 'options' => ['choices' => [['code' => '1', 'label' => '1', 'score' => 1]]], 'is_required' => false, 'sort_order' => 1, ]); $this->actingAs($this->user) ->post(route('care.assessments.store', $this->patient), ['template_code' => 'need_scores']) ->assertRedirect(); $assessment = Assessment::first(); $this->actingAs($this->user) ->from(route('care.assessments.show', $assessment)) ->post(route('care.assessments.complete', $assessment)) ->assertSessionHasErrors('answers.item_a'); $this->assertSame(Assessment::STATUS_DRAFT, $assessment->fresh()->status); $this->assertSame(0, AssessmentScore::count()); } }