Files
ladill-frontdesk/tests/Feature/FrontdeskPhase5Test.php
T
isaaccladandCursor 9e2d79936c
Deploy Ladill Frontdesk / deploy (push) Failing after 35s
Test / test (push) Failing after 2m45s
Initial Ladill Frontdesk release with deploy pipeline.
Visitor management app with SSO, kiosk, badges, reports, and Gitea CI deploy to frontdesk.ladill.com.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-27 20:37:15 +00:00

220 lines
6.9 KiB
PHP

<?php
namespace Tests\Feature;
use App\Http\Middleware\EnsurePlatformSession;
use App\Models\AuditLog;
use App\Models\Branch;
use App\Models\Member;
use App\Models\Organization;
use App\Models\User;
use App\Models\Visit;
use App\Models\Visitor;
use App\Models\WatchlistEntry;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Carbon;
use Tests\TestCase;
class FrontdeskPhase5Test extends TestCase
{
use RefreshDatabase;
protected User $user;
protected Organization $organization;
protected function setUp(): void
{
parent::setUp();
$this->withoutMiddleware(EnsurePlatformSession::class);
$this->user = User::create([
'public_id' => 'phase5-user-001',
'name' => 'Phase5 User',
'email' => 'phase5@example.com',
]);
$this->organization = Organization::create([
'owner_ref' => $this->user->public_id,
'name' => 'Phase5 Org',
'slug' => 'phase5-org',
'settings' => ['onboarded' => true, 'badge_expiry_hours' => 8],
]);
Member::create([
'owner_ref' => $this->user->public_id,
'organization_id' => $this->organization->id,
'user_ref' => $this->user->public_id,
'role' => 'org_admin',
]);
Branch::create([
'owner_ref' => $this->user->public_id,
'organization_id' => $this->organization->id,
'name' => 'HQ',
'is_active' => true,
]);
}
public function test_blacklisted_check_in_records_audit_log(): void
{
$visitor = Visitor::create([
'owner_ref' => $this->user->public_id,
'organization_id' => $this->organization->id,
'full_name' => 'Blocked Person',
'email' => 'blocked@example.com',
'watchlist_status' => Visitor::WATCHLIST_BLACKLISTED,
]);
$this->actingAs($this->user)
->post(route('frontdesk.visits.store'), [
'visitor_id' => $visitor->id,
'full_name' => 'Blocked Person',
'email' => 'blocked@example.com',
'visitor_type' => 'visitor',
'policies_accepted' => '1',
])
->assertForbidden();
$this->assertDatabaseHas('frontdesk_audit_logs', [
'action' => 'watchlist.blocked_attempt',
]);
}
public function test_flagged_visitor_check_in_queues_for_approval(): void
{
$visitor = Visitor::create([
'owner_ref' => $this->user->public_id,
'organization_id' => $this->organization->id,
'full_name' => 'Flagged Person',
'watchlist_status' => Visitor::WATCHLIST_REQUIRES_APPROVAL,
]);
$this->actingAs($this->user)
->post(route('frontdesk.visitors.check-in.store', $visitor), [
'visitor_type' => 'visitor',
'policies_accepted' => '1',
])
->assertRedirect();
$visit = Visit::first();
$this->assertSame(Visit::STATUS_WAITING, $visit->status);
$this->assertTrue($visit->awaitingApproval());
$this->assertDatabaseHas('frontdesk_audit_logs', [
'action' => 'watchlist.flagged_checkin',
]);
}
public function test_watchlist_admin_can_create_entry(): void
{
$this->actingAs($this->user)
->post(route('frontdesk.watchlist.store'), [
'full_name' => 'Known Person',
'status' => Visitor::WATCHLIST_BLACKLISTED,
'reason' => 'Previous incident',
])
->assertRedirect(route('frontdesk.watchlist.index'));
$this->assertDatabaseHas('frontdesk_watchlist_entries', [
'full_name' => 'Known Person',
'status' => Visitor::WATCHLIST_BLACKLISTED,
]);
}
public function test_audit_log_page_loads_and_exports_csv(): void
{
AuditLog::record(
$this->user->public_id,
'visit.checked_in',
$this->organization->id,
$this->user->public_id,
Visit::class,
1,
['visitor' => 'Test'],
);
$this->actingAs($this->user)
->get(route('frontdesk.audit.index'))
->assertOk()
->assertSee('Audit log');
$this->actingAs($this->user)
->get(route('frontdesk.audit.export'))
->assertOk()
->assertHeader('content-type', 'text/csv; charset=UTF-8');
}
public function test_security_can_verify_badge_code(): void
{
$visitor = Visitor::create([
'owner_ref' => $this->user->public_id,
'organization_id' => $this->organization->id,
'full_name' => 'Verify Me',
]);
$visit = Visit::create([
'owner_ref' => $this->user->public_id,
'organization_id' => $this->organization->id,
'visitor_id' => $visitor->id,
'visitor_type' => 'visitor',
'status' => Visit::STATUS_CHECKED_IN,
'badge_code' => 'ABC12345',
'checked_in_at' => now(),
'badge_expires_at' => now()->addHours(4),
]);
$this->actingAs($this->user)
->post(route('frontdesk.security.verify.lookup'), ['lookup' => 'ABC12345'])
->assertOk()
->assertSee('Verify Me')
->assertSee('Valid');
}
public function test_soft_deleted_visitor_can_be_restored(): void
{
$visitor = Visitor::create([
'owner_ref' => $this->user->public_id,
'organization_id' => $this->organization->id,
'full_name' => 'Archived Person',
]);
$visitor->delete();
$this->actingAs($this->user)
->post(route('frontdesk.compliance.visitors.restore', $visitor->id))
->assertRedirect();
$this->assertNull($visitor->fresh()->deleted_at);
$this->assertDatabaseHas('frontdesk_audit_logs', ['action' => 'visitor.restored']);
}
public function test_expired_badge_command_logs_alert(): void
{
Carbon::setTestNow('2026-06-27 18:00:00');
$visitor = Visitor::create([
'owner_ref' => $this->user->public_id,
'organization_id' => $this->organization->id,
'full_name' => 'Expired Badge Guest',
]);
Visit::create([
'owner_ref' => $this->user->public_id,
'organization_id' => $this->organization->id,
'visitor_id' => $visitor->id,
'visitor_type' => 'visitor',
'status' => Visit::STATUS_CHECKED_IN,
'checked_in_at' => now()->subHours(10),
'badge_expires_at' => now()->subHour(),
'badge_code' => 'EXP12345',
]);
$this->artisan('frontdesk:mark-expired-badges')->assertSuccessful();
$this->assertDatabaseHas('frontdesk_audit_logs', ['action' => 'badge.expired']);
Carbon::setTestNow();
}
}