bootApp(); } public function testAdminSeesConfigAndJobs(): void { $_SESSION['_user'] = (new UserContext(1, 'Admin', 'admin', null, UserContext::SOURCE_LOCAL))->toArray(); $_SESSION['_last_active'] = time(); $_SESSION['_authenticated_at'] = time(); // First call: getConfig $this->enqueueApiResponse(200, [ 'sections' => [ 'app' => ['APP_ENV' => 'development', 'LOG_LEVEL' => 'Info'], 'database' => ['DB_DRIVER' => 'sqlite'], 'auth' => ['INTERNAL_JOB_TOKEN' => '***', 'UI_SERVICE_TOKEN' => 'irdb_svc...'], 'reputation' => ['SCORE_REPORT_HARD_CUTOFF_DAYS' => 365], 'jobs' => ['JOB_AUDIT_RETENTION_DAYS' => 180], 'geoip' => ['GEOIP_PROVIDER' => 'dbip', 'GEOIP_COUNTRY_DB' => '/data/geoip/country.mmdb', 'GEOIP_ASN_DB' => '/data/geoip/asn.mmdb', 'MAXMIND_LICENSE_KEY' => '', 'IPINFO_TOKEN' => ''], ], ]); // Second call: getJobsStatus $this->enqueueApiResponse(200, [ 'now' => '2026-04-29T10:00:00Z', 'jobs' => [ 'recompute-scores' => [ 'name' => 'recompute-scores', 'default_interval_seconds' => 300, 'max_runtime_seconds' => 240, 'overdue' => false, 'lock' => null, 'last_run' => [ 'id' => 1, 'status' => 'success', 'items_processed' => 0, 'triggered_by' => 'schedule', 'started_at' => '2026-04-29T09:55:00Z', 'finished_at' => '2026-04-29T09:55:01Z', 'error_message' => null, ], ], ], ]); // Third call: getAppSettings (audit toggles) $this->enqueueApiResponse(200, [ 'audit_report_received_enabled' => true, 'audit_blocklist_request_enabled' => true, ]); $resp = $this->request('GET', '/app/settings'); self::assertSame(200, $resp->getStatusCode()); $body = (string) $resp->getBody(); self::assertStringContainsString('Configuration', $body); self::assertStringContainsString('Jobs', $body); self::assertStringContainsString('GeoIP', $body); self::assertStringContainsString('recompute-scores', $body); self::assertStringContainsString('Run now', $body); self::assertStringContainsString('dbip', $body); } public function testViewerRedirectsToNoAccess(): void { $_SESSION['_user'] = (new UserContext(2, 'Viewer', 'viewer', null, UserContext::SOURCE_LOCAL))->toArray(); $_SESSION['_last_active'] = time(); $_SESSION['_authenticated_at'] = time(); $resp = $this->request('GET', '/app/settings'); self::assertSame(303, $resp->getStatusCode()); self::assertSame('/no-access', $resp->getHeaderLine('Location')); } public function testAnonymousRedirectsToLogin(): void { $_SESSION = []; $resp = $this->request('GET', '/app/settings'); self::assertSame(302, $resp->getStatusCode()); self::assertSame('/login', $resp->getHeaderLine('Location')); } }