Files
ladill-care/tests/Feature/CareNurseOrgAccessTest.php
T
isaaccladandCursor 938f351ede
Deploy Ladill Care / deploy (push) Successful in 41s
Expand specialty RBAC with dedicated specialist roles and support access.
Add cardiologist/ENT/derm/ophthalmology/ortho/podiatry/fertility roles and
manage/refer/view matrices so GPs, nurses, lab, pharmacy, and reception get
realistic module access beyond single-app specialists.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-20 09:21:26 +00:00

386 lines
13 KiB
PHP

<?php
namespace Tests\Feature;
use App\Http\Middleware\EnsurePlatformSession;
use App\Models\Appointment;
use App\Models\Branch;
use App\Models\Department;
use App\Models\Member;
use App\Models\Organization;
use App\Models\Patient;
use App\Models\User;
use App\Models\Visit;
use App\Services\Care\OrganizationResolver;
use App\Services\Care\SpecialtyModuleService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class CareNurseOrgAccessTest extends TestCase
{
use RefreshDatabase;
public function test_nurse_with_email_user_ref_membership_is_not_sent_to_onboarding(): void
{
$this->withoutMiddleware(EnsurePlatformSession::class);
$owner = User::create([
'public_id' => 'owner-pid',
'name' => 'Owner',
'email' => 'owner@example.com',
]);
$nurse = User::create([
'public_id' => 'nurse-pid',
'name' => 'Nurse',
'email' => 'demo-pro-nurse@ladill.com',
]);
$organization = Organization::create([
'owner_ref' => $owner->public_id,
'name' => 'Clinic',
'slug' => 'clinic',
'settings' => [
'onboarded' => true,
'plan' => 'pro',
'plan_expires_at' => now()->addMonth()->toIso8601String(),
],
]);
Member::create([
'owner_ref' => $owner->public_id,
'organization_id' => $organization->id,
'user_ref' => $owner->public_id,
'role' => 'hospital_admin',
]);
// Demo seeder style: staff member keyed by email until SSO remaps.
Member::create([
'owner_ref' => $owner->public_id,
'organization_id' => $organization->id,
'user_ref' => strtolower($nurse->email),
'role' => 'nurse',
]);
$resolver = app(OrganizationResolver::class);
$this->assertTrue($resolver->isOnboarded($nurse));
$this->assertFalse($resolver->canCompleteOnboarding($nurse));
$this->assertSame($organization->id, $resolver->resolveForUser($nurse)?->id);
// Remap sticks so later requests resolve by public_id.
$this->assertDatabaseHas('care_members', [
'organization_id' => $organization->id,
'user_ref' => $nurse->public_id,
'role' => 'nurse',
]);
$this->actingAs($nurse)
->get(route('care.dashboard'))
->assertOk();
$this->actingAs($nurse)
->get(route('care.onboarding.show'))
->assertRedirect(route('care.dashboard'));
}
public function test_nurse_who_wrongly_owns_empty_org_still_uses_employer_membership(): void
{
$this->withoutMiddleware(EnsurePlatformSession::class);
$owner = User::create([
'public_id' => 'employer-owner',
'name' => 'Owner',
'email' => 'employer@example.com',
]);
$nurse = User::create([
'public_id' => 'orphan-nurse',
'name' => 'Nurse',
'email' => 'orphan-nurse@example.com',
]);
$employer = Organization::create([
'owner_ref' => $owner->public_id,
'name' => 'Employer Clinic',
'slug' => 'employer-clinic',
'settings' => [
'onboarded' => true,
'plan' => 'pro',
'plan_expires_at' => now()->addMonth()->toIso8601String(),
'specialty_modules' => ['emergency' => true],
],
]);
// Orphan org from completing owner onboarding while membership was unresolved.
Organization::create([
'owner_ref' => $nurse->public_id,
'name' => 'Nurse Own Clinic',
'slug' => 'nurse-own-clinic',
'settings' => ['onboarded' => true],
]);
Member::create([
'owner_ref' => $owner->public_id,
'organization_id' => $employer->id,
'user_ref' => strtolower($nurse->email),
'role' => 'ed_nurse',
]);
$branch = Branch::create([
'owner_ref' => $owner->public_id,
'organization_id' => $employer->id,
'name' => 'Main',
'is_active' => true,
]);
app(SpecialtyModuleService::class)->activate($employer, $owner->public_id, 'emergency');
$dept = Department::query()->where('type', 'emergency')->where('branch_id', $branch->id)->firstOrFail();
$patient = Patient::create([
'owner_ref' => $owner->public_id,
'organization_id' => $employer->id,
'branch_id' => $branch->id,
'patient_number' => 'P-ORPHAN',
'first_name' => 'Ama',
'last_name' => 'Patient',
'gender' => 'female',
'date_of_birth' => '1990-01-01',
]);
$visit = Visit::create([
'owner_ref' => $owner->public_id,
'organization_id' => $employer->id,
'branch_id' => $branch->id,
'patient_id' => $patient->id,
'status' => Visit::STATUS_IN_PROGRESS,
'checked_in_at' => now(),
]);
Appointment::create([
'owner_ref' => $owner->public_id,
'organization_id' => $employer->id,
'branch_id' => $branch->id,
'patient_id' => $patient->id,
'department_id' => $dept->id,
'visit_id' => $visit->id,
'type' => Appointment::TYPE_WALK_IN,
'status' => Appointment::STATUS_IN_CONSULTATION,
'scheduled_at' => now(),
'checked_in_at' => now(),
'started_at' => now(),
]);
$this->assertSame($employer->id, app(OrganizationResolver::class)->resolveForUser($nurse)?->id);
$this->actingAs($nurse)
->get(route('care.specialty.workspace', [
'module' => 'emergency',
'visit' => $visit,
'tab' => 'documents',
]))
->assertOk()
->assertSee('Documents')
->assertSee('Upload document');
}
public function test_nurse_can_open_emergency_documents_tab(): void
{
$this->withoutMiddleware(EnsurePlatformSession::class);
$owner = User::create([
'public_id' => 'er-docs-owner',
'name' => 'Owner',
'email' => 'er-docs-owner@example.com',
]);
$nurse = User::create([
'public_id' => 'er-docs-nurse',
'name' => 'ED Nurse',
'email' => 'er-docs-nurse@example.com',
]);
$organization = Organization::create([
'owner_ref' => $owner->public_id,
'name' => 'ER Clinic',
'slug' => 'er-docs-clinic',
'settings' => [
'onboarded' => true,
'plan' => 'pro',
'plan_expires_at' => now()->addMonth()->toIso8601String(),
'specialty_modules' => ['emergency' => true],
],
]);
Member::create([
'owner_ref' => $owner->public_id,
'organization_id' => $organization->id,
'user_ref' => $owner->public_id,
'role' => 'hospital_admin',
]);
Member::create([
'owner_ref' => $owner->public_id,
'organization_id' => $organization->id,
'user_ref' => $nurse->public_id,
'role' => 'ed_nurse',
]);
$branch = Branch::create([
'owner_ref' => $owner->public_id,
'organization_id' => $organization->id,
'name' => 'Main',
'is_active' => true,
]);
app(SpecialtyModuleService::class)->activate($organization, $owner->public_id, 'emergency');
$dept = Department::query()->where('type', 'emergency')->where('branch_id', $branch->id)->firstOrFail();
$patient = Patient::create([
'owner_ref' => $owner->public_id,
'organization_id' => $organization->id,
'branch_id' => $branch->id,
'patient_number' => 'P-DOCS',
'first_name' => 'Ama',
'last_name' => 'Docs',
'gender' => 'female',
'date_of_birth' => '1990-01-01',
]);
$visit = Visit::create([
'owner_ref' => $owner->public_id,
'organization_id' => $organization->id,
'branch_id' => $branch->id,
'patient_id' => $patient->id,
'status' => Visit::STATUS_IN_PROGRESS,
'checked_in_at' => now(),
]);
Appointment::create([
'owner_ref' => $owner->public_id,
'organization_id' => $organization->id,
'branch_id' => $branch->id,
'patient_id' => $patient->id,
'department_id' => $dept->id,
'visit_id' => $visit->id,
'type' => Appointment::TYPE_WALK_IN,
'status' => Appointment::STATUS_IN_CONSULTATION,
'scheduled_at' => now(),
'checked_in_at' => now(),
'started_at' => now(),
]);
$this->actingAs($nurse)
->get(route('care.specialty.workspace', [
'module' => 'emergency',
'visit' => $visit,
'tab' => 'documents',
]))
->assertOk()
->assertSee('Documents')
->assertSee('Upload document');
}
public function test_view_only_staff_sees_documents_without_upload(): void
{
$this->withoutMiddleware(EnsurePlatformSession::class);
$owner = User::create([
'public_id' => 'view-docs-owner',
'name' => 'Owner',
'email' => 'view-docs-owner@example.com',
]);
$lab = User::create([
'public_id' => 'view-docs-lab',
'name' => 'Lab',
'email' => 'view-docs-lab@example.com',
]);
$organization = Organization::create([
'owner_ref' => $owner->public_id,
'name' => 'Rad Clinic',
'slug' => 'rad-docs-clinic',
'settings' => [
'onboarded' => true,
'plan' => 'pro',
'plan_expires_at' => now()->addMonth()->toIso8601String(),
],
]);
Member::create([
'owner_ref' => $owner->public_id,
'organization_id' => $organization->id,
'user_ref' => $owner->public_id,
'role' => 'hospital_admin',
]);
Member::create([
'owner_ref' => $owner->public_id,
'organization_id' => $organization->id,
'user_ref' => $lab->public_id,
'role' => 'lab_technician',
]);
$branch = Branch::create([
'owner_ref' => $owner->public_id,
'organization_id' => $organization->id,
'name' => 'Main',
'is_active' => true,
]);
$modules = app(SpecialtyModuleService::class);
$modules->activate($organization, $owner->public_id, 'radiology');
$dept = Department::query()->where('type', 'radiology')->where('branch_id', $branch->id)->firstOrFail();
$patient = Patient::create([
'owner_ref' => $owner->public_id,
'organization_id' => $organization->id,
'branch_id' => $branch->id,
'patient_number' => 'P-VIEW-DOCS',
'first_name' => 'Kofi',
'last_name' => 'View',
'gender' => 'male',
'date_of_birth' => '1985-01-01',
]);
$visit = Visit::create([
'owner_ref' => $owner->public_id,
'organization_id' => $organization->id,
'branch_id' => $branch->id,
'patient_id' => $patient->id,
'status' => Visit::STATUS_IN_PROGRESS,
'checked_in_at' => now(),
]);
Appointment::create([
'owner_ref' => $owner->public_id,
'organization_id' => $organization->id,
'branch_id' => $branch->id,
'patient_id' => $patient->id,
'department_id' => $dept->id,
'visit_id' => $visit->id,
'type' => Appointment::TYPE_WALK_IN,
'status' => Appointment::STATUS_IN_CONSULTATION,
'scheduled_at' => now(),
'checked_in_at' => now(),
'started_at' => now(),
]);
$labMember = Member::query()->where('user_ref', $lab->public_id)->firstOrFail();
// Lab manages pathology/blood bank; radiology is view-only.
$this->assertSame('view', $modules->memberAccessLevel($organization, $labMember, 'radiology'));
$this->actingAs($lab)
->get(route('care.specialty.workspace', [
'module' => 'radiology',
'visit' => $visit,
'tab' => 'documents',
]))
->assertOk()
->assertSee('Documents')
->assertDontSee('Upload document');
}
}