Deploy Ladill Care / deploy (push) Successful in 1m54s
Link DemoWorld doctor staff to practitioners, clear unresolved routing, distribute staff across branches, and restore scoped consultation/pharmacy ticket sync so demos are not left Unassigned. Co-authored-by: Cursor <cursoragent@cursor.com>
357 lines
15 KiB
PHP
357 lines
15 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\Appointment;
|
|
use App\Models\Assessment;
|
|
use App\Models\Bill;
|
|
use App\Models\Branch;
|
|
use App\Models\Drug;
|
|
use App\Models\InvestigationRequest;
|
|
use App\Models\InvestigationType;
|
|
use App\Models\Organization;
|
|
use App\Models\Patient;
|
|
use App\Models\Prescription;
|
|
use App\Models\User;
|
|
use App\Services\Care\DemoTenantSeeder;
|
|
use App\Services\Care\PlanService;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Artisan;
|
|
use Tests\TestCase;
|
|
|
|
class DemoSeedCommandTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
public function test_command_is_registered(): void
|
|
{
|
|
$this->assertTrue(collect(Artisan::all())->has('demo:seed'));
|
|
}
|
|
|
|
public function test_free_plan_respects_branch_cap_and_skips_paid_modules(): void
|
|
{
|
|
$user = User::create([
|
|
'public_id' => 'demo-free-public-id',
|
|
'name' => 'Ladill Demo (Free)',
|
|
'email' => 'demo-free@ladill.com',
|
|
]);
|
|
|
|
$this->artisan('demo:seed', [
|
|
'identity' => 'demo-free@ladill.com',
|
|
'--plan' => 'free',
|
|
])->assertSuccessful();
|
|
|
|
$org = Organization::query()->where('owner_ref', $user->public_id)->first();
|
|
$this->assertNotNull($org);
|
|
$this->assertSame('free', data_get($org->settings, 'plan'));
|
|
$this->assertTrue((bool) data_get($org->settings, 'onboarded'));
|
|
$this->assertSame(1, (int) data_get($org->settings, 'billed_branches'));
|
|
$this->assertNotEmpty(data_get($org->settings, 'plan_expires_at'));
|
|
$this->assertTrue(\Carbon\Carbon::parse(data_get($org->settings, 'plan_expires_at'))->isFuture());
|
|
|
|
$this->assertSame(1, Branch::query()->where('owner_ref', $user->public_id)->where('is_active', true)->count());
|
|
$this->assertSame(1, app(PlanService::class)->maxBranches($org));
|
|
$this->assertGreaterThanOrEqual(12, Patient::query()->where('owner_ref', $user->public_id)->count());
|
|
$this->assertGreaterThanOrEqual(20, Appointment::query()->where('owner_ref', $user->public_id)->count());
|
|
|
|
$this->assertSame(0, InvestigationType::query()->where('owner_ref', $user->public_id)->count());
|
|
$this->assertSame(0, InvestigationRequest::query()->where('owner_ref', $user->public_id)->count());
|
|
$this->assertSame(0, Drug::query()->where('owner_ref', $user->public_id)->count());
|
|
$this->assertSame(0, Prescription::query()->where('owner_ref', $user->public_id)->count());
|
|
$this->assertSame(0, Bill::query()->where('owner_ref', $user->public_id)->count());
|
|
}
|
|
|
|
public function test_pro_plan_seeds_multi_branch_and_paid_modules(): void
|
|
{
|
|
$user = User::create([
|
|
'public_id' => 'demo-pro-public-id',
|
|
'name' => 'Ladill Demo (Pro)',
|
|
'email' => 'demo-pro@ladill.com',
|
|
]);
|
|
|
|
$this->artisan('demo:seed', [
|
|
'identity' => 'demo-pro@ladill.com',
|
|
'--plan' => 'pro',
|
|
])->assertSuccessful();
|
|
|
|
$org = Organization::query()->where('owner_ref', $user->public_id)->firstOrFail();
|
|
$this->assertSame('pro', app(PlanService::class)->planKey($org));
|
|
$this->assertSame(3, Branch::query()->where('owner_ref', $user->public_id)->count());
|
|
$this->assertTrue(app(PlanService::class)->canUseProModules($org));
|
|
|
|
$this->assertGreaterThan(0, InvestigationType::query()->where('owner_ref', $user->public_id)->count());
|
|
$this->assertGreaterThan(0, InvestigationRequest::query()->where('owner_ref', $user->public_id)->count());
|
|
$this->assertGreaterThan(0, Drug::query()->where('owner_ref', $user->public_id)->count());
|
|
$this->assertGreaterThan(0, Prescription::query()->where('owner_ref', $user->public_id)->count());
|
|
$this->assertGreaterThan(0, Bill::query()->where('owner_ref', $user->public_id)->count());
|
|
|
|
$catalog = array_keys(config('care.specialty_modules', []));
|
|
$this->assertNotEmpty($catalog);
|
|
foreach ($catalog as $key) {
|
|
$this->assertTrue(
|
|
(bool) data_get($org->settings, "specialty_modules.{$key}"),
|
|
"Expected specialty module [{$key}] enabled for pro demo"
|
|
);
|
|
$type = config("care.specialty_modules.{$key}.department_type");
|
|
$this->assertGreaterThan(
|
|
0,
|
|
\App\Models\Department::query()
|
|
->where('owner_ref', $user->public_id)
|
|
->where('type', $type)
|
|
->where('is_active', true)
|
|
->count(),
|
|
"Expected active departments for [{$key}]"
|
|
);
|
|
$this->assertGreaterThan(
|
|
0,
|
|
Appointment::query()
|
|
->where('owner_ref', $user->public_id)
|
|
->whereIn('status', [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN])
|
|
->whereHas('department', fn ($q) => $q->where('type', $type))
|
|
->count(),
|
|
"Expected waiting specialty appointments for [{$key}]"
|
|
);
|
|
}
|
|
}
|
|
|
|
public function test_free_demo_does_not_enable_specialty_modules(): void
|
|
{
|
|
$user = User::create([
|
|
'public_id' => 'demo-free-specialty-id',
|
|
'name' => 'Ladill Demo (Free)',
|
|
'email' => 'demo-free@ladill.com',
|
|
]);
|
|
|
|
$this->artisan('demo:seed', [
|
|
'identity' => 'demo-free@ladill.com',
|
|
'--plan' => 'free',
|
|
])->assertSuccessful();
|
|
|
|
$org = Organization::query()->where('owner_ref', $user->public_id)->firstOrFail();
|
|
foreach (array_keys(config('care.specialty_modules', [])) as $key) {
|
|
$this->assertFalse((bool) data_get($org->settings, "specialty_modules.{$key}"));
|
|
}
|
|
}
|
|
|
|
public function test_enterprise_seeds_assessments_and_more_branches(): void
|
|
{
|
|
$user = User::create([
|
|
'public_id' => 'demo-enterprise-public-id',
|
|
'name' => 'Accra Healthcare Group',
|
|
'email' => 'demo-enterprise@ladill.com',
|
|
]);
|
|
|
|
$this->artisan('demo:seed', [
|
|
'identity' => $user->public_id,
|
|
'--plan' => 'enterprise',
|
|
])->assertSuccessful();
|
|
|
|
$org = Organization::query()->where('owner_ref', $user->public_id)->firstOrFail();
|
|
$this->assertSame('enterprise', app(PlanService::class)->planKey($org));
|
|
$this->assertSame(6, Branch::query()->where('owner_ref', $user->public_id)->count());
|
|
$this->assertGreaterThan(0, Assessment::query()->where('owner_ref', $user->public_id)->count());
|
|
$this->assertGreaterThan(0, Bill::query()->where('owner_ref', $user->public_id)->count());
|
|
}
|
|
|
|
public function test_idempotent_reseed_without_reset_does_not_duplicate(): void
|
|
{
|
|
$user = User::create([
|
|
'public_id' => 'demo-idempotent-id',
|
|
'name' => 'Idempotent Demo',
|
|
'email' => 'demo-idempotent@ladill.com',
|
|
]);
|
|
|
|
$this->artisan('demo:seed', [
|
|
'identity' => 'demo-idempotent@ladill.com',
|
|
'--plan' => 'free',
|
|
])->assertSuccessful();
|
|
|
|
$patients = Patient::query()->where('owner_ref', $user->public_id)->count();
|
|
$appointments = Appointment::query()->where('owner_ref', $user->public_id)->count();
|
|
$orgs = Organization::query()->where('owner_ref', $user->public_id)->count();
|
|
|
|
$this->artisan('demo:seed', [
|
|
'identity' => 'demo-idempotent@ladill.com',
|
|
'--plan' => 'free',
|
|
])->assertSuccessful();
|
|
|
|
$this->assertSame($patients, Patient::query()->where('owner_ref', $user->public_id)->count());
|
|
$this->assertSame($appointments, Appointment::query()->where('owner_ref', $user->public_id)->count());
|
|
$this->assertSame($orgs, Organization::query()->where('owner_ref', $user->public_id)->count());
|
|
$this->assertSame(1, Branch::query()->where('owner_ref', $user->public_id)->count());
|
|
|
|
$organization = Organization::query()->where('owner_ref', $user->public_id)->first();
|
|
$this->assertTrue((bool) data_get($organization?->settings, 'demo'));
|
|
$this->assertFalse((bool) data_get($organization?->settings, 'rollout.workflow_engine'));
|
|
$this->assertFalse((bool) data_get($organization?->settings, 'rollout.financial_gates'));
|
|
}
|
|
|
|
public function test_reset_wipes_only_tenant_scoped_data_then_reseeds(): void
|
|
{
|
|
$keep = User::create([
|
|
'public_id' => 'other-owner',
|
|
'name' => 'Other',
|
|
'email' => 'other@ladill.com',
|
|
]);
|
|
Organization::create([
|
|
'owner_ref' => $keep->public_id,
|
|
'name' => 'Keep Me',
|
|
'slug' => 'keep-me',
|
|
'settings' => ['onboarded' => true, 'plan' => 'free'],
|
|
]);
|
|
Branch::create([
|
|
'owner_ref' => $keep->public_id,
|
|
'organization_id' => Organization::query()->where('owner_ref', $keep->public_id)->value('id'),
|
|
'name' => 'Keep Branch',
|
|
'is_active' => true,
|
|
]);
|
|
|
|
$user = User::create([
|
|
'public_id' => 'demo-reset-id',
|
|
'name' => 'Reset Demo',
|
|
'email' => 'demo-reset@ladill.com',
|
|
]);
|
|
|
|
$this->artisan('demo:seed', [
|
|
'identity' => 'demo-reset@ladill.com',
|
|
'--plan' => 'pro',
|
|
])->assertSuccessful();
|
|
|
|
$patientCount = Patient::query()->where('owner_ref', $user->public_id)->count();
|
|
$this->assertGreaterThan(0, $patientCount);
|
|
$this->assertGreaterThan(0, Bill::query()->where('owner_ref', $user->public_id)->count());
|
|
|
|
$this->artisan('demo:seed', [
|
|
'identity' => 'demo-reset@ladill.com',
|
|
'--plan' => 'free',
|
|
'--reset' => true,
|
|
])->assertSuccessful();
|
|
|
|
$this->assertSame(1, Branch::query()->where('owner_ref', $user->public_id)->count());
|
|
$this->assertSame(0, Bill::query()->where('owner_ref', $user->public_id)->count());
|
|
$this->assertSame(0, Drug::query()->where('owner_ref', $user->public_id)->count());
|
|
$this->assertSame('free', data_get(
|
|
Organization::query()->where('owner_ref', $user->public_id)->value('settings'),
|
|
'plan'
|
|
));
|
|
|
|
// Other tenant untouched.
|
|
$this->assertSame(1, Organization::query()->where('owner_ref', $keep->public_id)->count());
|
|
$this->assertSame(1, Branch::query()->where('owner_ref', $keep->public_id)->count());
|
|
}
|
|
|
|
public function test_reset_keeps_organization_onboarded_mid_wipe(): void
|
|
{
|
|
$user = User::create([
|
|
'public_id' => 'demo-mid-reset-id',
|
|
'name' => 'Mid Reset Demo',
|
|
'email' => 'demo-mid-reset@ladill.com',
|
|
]);
|
|
|
|
$this->artisan('demo:seed', [
|
|
'identity' => 'demo-mid-reset@ladill.com',
|
|
'--plan' => 'free',
|
|
])->assertSuccessful();
|
|
|
|
$orgId = (int) Organization::query()->where('owner_ref', $user->public_id)->value('id');
|
|
$this->assertGreaterThan(0, $orgId);
|
|
|
|
app(DemoTenantSeeder::class)->resetTenant($user->public_id);
|
|
|
|
$org = Organization::query()->find($orgId);
|
|
$this->assertNotNull($org);
|
|
$this->assertTrue((bool) data_get($org->settings, 'onboarded'));
|
|
$this->assertTrue(app(\App\Services\Care\OrganizationResolver::class)->isOnboarded($user));
|
|
$this->assertSame(0, Branch::query()->where('owner_ref', $user->public_id)->count());
|
|
}
|
|
|
|
public function test_creates_local_mirror_when_public_id_and_email_provided(): void
|
|
{
|
|
$this->assertDatabaseMissing('users', ['email' => 'new-demo@ladill.com']);
|
|
|
|
$this->artisan('demo:seed', [
|
|
'--public-id' => 'brand-new-public-id',
|
|
'--email' => 'new-demo@ladill.com',
|
|
'--name' => 'New Demo',
|
|
'--plan' => 'free',
|
|
])->assertSuccessful();
|
|
|
|
$user = User::query()->where('email', 'new-demo@ladill.com')->first();
|
|
$this->assertNotNull($user);
|
|
$this->assertSame('brand-new-public-id', $user->public_id);
|
|
$this->assertSame(1, Organization::query()->where('owner_ref', $user->public_id)->count());
|
|
}
|
|
|
|
public function test_fails_when_mirror_missing_and_insufficient_identity(): void
|
|
{
|
|
$this->artisan('demo:seed', [
|
|
'identity' => 'missing@ladill.com',
|
|
'--plan' => 'free',
|
|
])->assertFailed();
|
|
}
|
|
|
|
public function test_volumes_helper_marks_free_without_paid_modules(): void
|
|
{
|
|
$volumes = app(DemoTenantSeeder::class)->volumes('free');
|
|
$this->assertFalse($volumes['paid_modules']);
|
|
$this->assertSame(1, $volumes['branches']);
|
|
$this->assertSame(0, $volumes['assessments']);
|
|
|
|
$pro = app(DemoTenantSeeder::class)->volumes('pro');
|
|
$this->assertTrue($pro['paid_modules']);
|
|
$this->assertSame(3, $pro['branches']);
|
|
}
|
|
|
|
public function test_demo_world_continuity_and_staff_roles(): void
|
|
{
|
|
$user = User::create([
|
|
'public_id' => 'demo-world-care-id',
|
|
'name' => 'Ladill Demo (Pro)',
|
|
'email' => 'demo-pro@ladill.com',
|
|
]);
|
|
|
|
$this->artisan('demo:seed', [
|
|
'identity' => 'demo-pro@ladill.com',
|
|
'--plan' => 'pro',
|
|
])->assertSuccessful();
|
|
|
|
$org = Organization::query()->where('owner_ref', $user->public_id)->firstOrFail();
|
|
$this->assertSame(\App\Support\DemoWorld::business('pro')['name'], $org->name);
|
|
|
|
$ama = \App\Support\DemoWorld::personByKey('ama-mensah');
|
|
$this->assertNotNull($ama);
|
|
$patient = Patient::query()
|
|
->where('owner_ref', $user->public_id)
|
|
->where('phone', $ama['phone'])
|
|
->first();
|
|
$this->assertNotNull($patient);
|
|
$this->assertSame($ama['email'], $patient->email);
|
|
$this->assertSame($ama['national_id'], $patient->national_id);
|
|
|
|
$this->assertTrue(
|
|
\App\Models\Member::query()
|
|
->where('owner_ref', $user->public_id)
|
|
->where('user_ref', 'demo-pro-doctor@ladill.com')
|
|
->where('role', 'doctor')
|
|
->exists()
|
|
);
|
|
$this->assertTrue(
|
|
\App\Models\Member::query()
|
|
->where('owner_ref', $user->public_id)
|
|
->where('user_ref', 'demo-pro-receptionist@ladill.com')
|
|
->where('role', 'receptionist')
|
|
->exists()
|
|
);
|
|
|
|
$waiters = Appointment::query()
|
|
->where('owner_ref', $user->public_id)
|
|
->whereIn('status', [Appointment::STATUS_WAITING, Appointment::STATUS_CHECKED_IN])
|
|
->get();
|
|
$this->assertNotEmpty($waiters);
|
|
foreach ($waiters as $waiter) {
|
|
$this->assertNotNull($waiter->practitioner_id, 'Demo waiters must all have a doctor assigned');
|
|
$this->assertNotSame('unresolved', $waiter->queue_routing_status);
|
|
}
|
|
}
|
|
}
|