import { expect, test, type Page, type Route } from '@playwright/test'; import type { StubAuthSession } from '../../src/app/testing/auth-fixtures'; const operatorSession: StubAuthSession = { subjectId: 'ops-e2e-user', tenant: 'tenant-default', scopes: [ 'admin', 'ui.read', 'ui.admin', 'orch:read', 'orch:operate', 'orch:quota', 'orch:backfill', 'health:read', 'scheduler:read', 'scheduler:trigger', 'policy:read', 'release:read', ], }; const mockConfig = { authority: { issuer: '/authority', clientId: 'stella-ops-ui', authorizeEndpoint: '/authority/connect/authorize', tokenEndpoint: '/authority/connect/token', logoutEndpoint: '/authority/connect/logout', redirectUri: 'https://127.0.0.1:4400/auth/callback', postLogoutRedirectUri: 'https://127.0.0.1:4400/', scope: 'openid profile email ui.read', audience: '/gateway', dpopAlgorithms: ['ES256'], refreshLeewaySeconds: 60, }, apiBaseUrls: { authority: '/authority', scanner: '/scanner', policy: '/policy', concelier: '/concelier', attestor: '/attestor', gateway: '/gateway', }, quickstartMode: true, setup: 'complete', }; const jobList = { jobs: [ { tenantId: 'tenant-default', projectId: 'proj-a', jobId: 'job-001', runId: 'run-001', jobType: 'scan', status: 'failed', priority: 10, attempt: 2, maxAttempts: 3, correlationId: 'corr-001', workerId: 'worker-1', taskRunnerId: 'runner-a', createdAt: '2026-03-08T08:00:00Z', scheduledAt: '2026-03-08T08:00:00Z', leasedAt: '2026-03-08T08:01:00Z', completedAt: '2026-03-08T08:05:00Z', notBefore: '2026-03-08T08:00:00Z', reason: 'Dead-letter candidate', replayOf: null, createdBy: 'operator', }, ], nextCursor: null, }; const jobDetail = { ...jobList.jobs[0], payloadDigest: 'sha256:payload-001', payload: JSON.stringify({ artifact: 'sha256:artifact-001', action: 'scan' }), idempotencyKey: 'idem-001', leaseId: 'lease-001', leaseUntil: '2026-03-08T08:06:00Z', }; const quotaList = { items: [ { quotaId: 'quota-001', tenantId: 'tenant-default', jobType: 'scan', maxActive: 4, maxPerHour: 100, burstCapacity: 100, refillRate: 5, currentTokens: 40, currentActive: 1, currentHourCount: 10, paused: false, pauseReason: null, quotaTicket: null, createdAt: '2026-03-01T00:00:00Z', updatedAt: '2026-03-08T07:30:00Z', updatedBy: 'ops@example.com', }, ], count: 1, continuationToken: null, }; const quotaSummary = { totalQuotas: 1, pausedQuotas: 0, averageTokenUtilization: 0.6, averageConcurrencyUtilization: 0.25, }; const jobSummary = { totalJobs: 12, pendingJobs: 2, scheduledJobs: 3, leasedJobs: 1, succeededJobs: 5, failedJobs: 1, canceledJobs: 1, timedOutJobs: 0, }; const deadLetterEntries = [ { id: 'dlq-001', jobId: 'job-001', jobType: 'scan', tenantId: 'tenant-default', tenantName: 'Default Tenant', state: 'failed', errorCode: 'DLQ_TIMEOUT', errorMessage: 'Scanner timed out', retryCount: 2, maxRetries: 3, age: 7200, createdAt: '2026-03-08T06:00:00Z', }, ]; const schedulerRuns = [ { id: 'run-001', scheduleId: 'sch-001', scheduleName: 'Nightly Sync', status: 'running', triggeredAt: '2026-03-08T07:00:00Z', startedAt: '2026-03-08T07:01:00Z', completedAt: null, durationMs: null, triggeredBy: 'schedule', progress: 48, itemsProcessed: 48, itemsTotal: 100, retryCount: 0, output: { metrics: { scanned: 48, failed: 1 } }, }, ]; async function fulfillJson(route: Route, body: unknown, status = 200): Promise { await route.fulfill({ status, contentType: 'application/json', body: JSON.stringify(body), }); } async function setupHarness(page: Page): Promise { await page.addInitScript((session) => { (window as { __stellaopsTestSession?: unknown }).__stellaopsTestSession = session; }, operatorSession); await page.route('**/platform/envsettings.json', (route) => fulfillJson(route, mockConfig)); await page.route('**/platform/i18n/*.json', (route) => fulfillJson(route, {})); await page.route('**/config.json', (route) => fulfillJson(route, mockConfig)); await page.route('**/.well-known/openid-configuration', (route) => fulfillJson(route, { issuer: 'https://127.0.0.1:4400/authority', authorization_endpoint: 'https://127.0.0.1:4400/authority/connect/authorize', token_endpoint: 'https://127.0.0.1:4400/authority/connect/token', jwks_uri: 'https://127.0.0.1:4400/authority/.well-known/jwks.json', response_types_supported: ['code'], subject_types_supported: ['public'], id_token_signing_alg_values_supported: ['RS256'], }), ); await page.route('**/authority/.well-known/jwks.json', (route) => fulfillJson(route, { keys: [] })); await page.route('**/console/branding**', (route) => fulfillJson(route, { tenantId: operatorSession.tenant, appName: 'Stella Ops', logoUrl: null, cssVariables: {}, }), ); await page.route('**/console/profile**', (route) => fulfillJson(route, { subjectId: operatorSession.subjectId, username: 'ops-e2e', displayName: 'Ops E2E', tenant: operatorSession.tenant, roles: ['ops-operator'], scopes: operatorSession.scopes, }), ); await page.route('**/console/token/introspect**', (route) => fulfillJson(route, { active: true, tenant: operatorSession.tenant, subject: operatorSession.subjectId, scopes: operatorSession.scopes, }), ); await page.route('**/authority/console/tenants**', (route) => fulfillJson(route, { tenants: [ { tenantId: operatorSession.tenant, displayName: 'Default Tenant', isDefault: true, isActive: true, }, ], }), ); // Register the generic API fallback before exact mocks so the exact handlers win. await page.route('**/api/**', (route) => fulfillJson(route, {})); await page.route('**/api/v2/context/regions**', (route) => fulfillJson(route, [{ regionId: 'eu-west', displayName: 'EU West', sortOrder: 1, enabled: true }]), ); await page.route('**/api/v2/context/environments**', (route) => fulfillJson(route, [ { environmentId: 'prod', regionId: 'eu-west', environmentType: 'prod', displayName: 'Prod', sortOrder: 1, enabled: true, }, ]), ); await page.route('**/api/v2/context/preferences**', (route) => fulfillJson(route, { tenantId: operatorSession.tenant, actorId: operatorSession.subjectId, regions: ['eu-west'], environments: ['prod'], timeWindow: '24h', stage: 'all', updatedAt: '2026-03-08T07:00:00Z', updatedBy: operatorSession.subjectId, }), ); await page.route('**/doctor/api/v1/doctor/trends**', (route) => fulfillJson(route, [])); await page.route('**/api/v1/approvals**', (route) => fulfillJson(route, [])); await page.route('**/api/v1/telemetry/ttfs', (route) => fulfillJson(route, { accepted: true }, 202)); await page.route(/\/gateway\/jobengine\/jobs\/summary(?:\?.*)?$/, (route) => fulfillJson(route, jobSummary)); await page.route(/\/gateway\/jobengine\/jobs(?:\?.*)?$/, (route) => fulfillJson(route, jobList)); await page.route(/\/gateway\/jobengine\/jobs\/job-001\/detail(?:\?.*)?$/, (route) => fulfillJson(route, jobDetail)); await page.route(/\/gateway\/jobengine\/dag\/job\/job-001\/parents(?:\?.*)?$/, (route) => fulfillJson(route, { edges: [] }), ); await page.route(/\/gateway\/jobengine\/dag\/job\/job-001\/children(?:\?.*)?$/, (route) => fulfillJson(route, { edges: [ { edgeId: 'edge-001', runId: 'run-001', parentJobId: 'job-001', childJobId: 'job-002', edgeType: 'dependency', createdAt: '2026-03-08T08:02:00Z', }, ], }), ); await page.route(/\/gateway\/jobengine\/quotas\/summary(?:\?.*)?$/, (route) => fulfillJson(route, quotaSummary)); await page.route(/\/gateway\/jobengine\/quotas(?:\?.*)?$/, (route) => fulfillJson(route, quotaList)); await page.route(/\/gateway\/jobengine\/quotas\/quota-001\/pause(?:\?.*)?$/, (route) => fulfillJson(route, { ...quotaList.items[0], paused: true, pauseReason: 'Paused from execution operations UI' }), ); await page.route(/\/gateway\/jobengine\/deadletter\/stats(?:\?.*)?$/, (route) => fulfillJson(route, { totalEntries: 1, pendingEntries: 0, replayingEntries: 0, replayedEntries: 0, resolvedEntries: 0, exhaustedEntries: 1, expiredEntries: 0, retryableEntries: 1, topErrorCodes: { DLQ_TIMEOUT: 1 }, }), ); await page.route(/\/api\/v1\/jobengine\/deadletter\/stats(?:\?.*)?$/, (route) => fulfillJson(route, { totalEntries: 1, pendingEntries: 0, replayingEntries: 0, replayedEntries: 0, resolvedEntries: 0, exhaustedEntries: 1, expiredEntries: 0, retryableEntries: 1, topErrorCodes: { DLQ_TIMEOUT: 1 }, }), ); await page.route(/\/api\/v1\/jobengine\/deadletter\/summary(?:\?.*)?$/, (route) => fulfillJson(route, { summaries: [{ errorCode: 'DLQ_TIMEOUT', entryCount: 1 }] }), ); await page.route(/\/api\/v1\/jobengine\/deadletter\/resolve\/batch(?:\?.*)?$/, (route) => fulfillJson(route, { resolvedCount: 1 }), ); await page.route(/\/api\/v1\/jobengine\/deadletter\/dlq-001\/replay(?:\?.*)?$/, (route) => fulfillJson(route, { success: true, newJobId: 'job-replay-001' }), ); await page.route(/\/api\/v1\/jobengine\/deadletter(?:\?.*)?$/, (route) => fulfillJson(route, { items: deadLetterEntries, total: deadLetterEntries.length }), ); await page.route('**/scheduler/api/v1/scheduler/runs**', (route) => fulfillJson(route, schedulerRuns), ); } test.beforeEach(async ({ page }) => { await setupHarness(page); }); test('execution operations cutover keeps canonical jobengine, dead-letter, scheduler, and scanner flows usable', async ({ page, }) => { await page.goto('/ops/operations/jobengine/jobs', { waitUntil: 'networkidle' }); await expect(page.getByRole('heading', { name: 'JobEngine Jobs' })).toBeVisible(); await page.locator('.job-card').first().getByRole('button', { name: 'Expand', exact: true }).click(); await page.getByRole('link', { name: 'View Details' }).click(); await expect(page).toHaveURL(/\/ops\/operations\/jobengine\/jobs\/job-001$/); await expect(page.getByText('Execution Metadata')).toBeVisible(); await page.goto('/ops/operations/dead-letter/queue', { waitUntil: 'networkidle' }); await expect(page.getByRole('heading', { name: 'Dead-Letter Queue' })).toBeVisible(); await page.locator('tbody input[type="checkbox"]').first().check(); await page.getByRole('button', { name: 'Replay Selected' }).click(); await expect(page.getByText('Queued replay for 1 selected entry.')).toBeVisible(); await page.goto('/ops/operations/scheduler/runs', { waitUntil: 'networkidle' }); await expect(page.getByRole('heading', { name: 'Scheduler Runs' })).toBeVisible(); await page.getByText('Nightly Sync').click(); await page.getByRole('link', { name: 'Live Stream' }).click(); await expect(page).toHaveURL(/\/ops\/operations\/scheduler\/runs\/run-001\/stream$/); await expect(page.getByRole('heading', { name: 'Scheduler Run Stream' })).toBeVisible(); await page.goto('/ops/scanner-ops/offline-kits', { waitUntil: 'networkidle' }); await expect(page.getByRole('heading', { name: 'Scanner Operations' })).toBeVisible(); await page.getByRole('button', { name: 'Verify All' }).click(); await expect(page.getByText('Verified 2 kits locally.')).toBeVisible(); });