212 lines
6.4 KiB
TypeScript
212 lines
6.4 KiB
TypeScript
import { expect, test, type Page } from '@playwright/test';
|
|
|
|
import { policyAuthorSession } from '../../src/app/testing';
|
|
|
|
const mockConfig = {
|
|
authority: {
|
|
issuer: 'https://authority.local',
|
|
clientId: 'stella-ops-ui',
|
|
authorizeEndpoint: 'https://authority.local/connect/authorize',
|
|
tokenEndpoint: 'https://authority.local/connect/token',
|
|
logoutEndpoint: 'https://authority.local/connect/logout',
|
|
redirectUri: 'http://127.0.0.1:4400/auth/callback',
|
|
postLogoutRedirectUri: 'http://127.0.0.1:4400/',
|
|
scope: 'openid profile email ui.read doctor:read',
|
|
audience: 'https://doctor.local',
|
|
},
|
|
apiBaseUrls: {
|
|
authority: 'https://authority.local',
|
|
doctor: 'https://doctor.local',
|
|
gateway: 'https://gateway.local',
|
|
},
|
|
quickstartMode: true,
|
|
setup: 'complete',
|
|
};
|
|
|
|
const oidcConfig = {
|
|
issuer: mockConfig.authority.issuer,
|
|
authorization_endpoint: mockConfig.authority.authorizeEndpoint,
|
|
token_endpoint: mockConfig.authority.tokenEndpoint,
|
|
jwks_uri: 'https://authority.local/.well-known/jwks.json',
|
|
response_types_supported: ['code'],
|
|
subject_types_supported: ['public'],
|
|
id_token_signing_alg_values_supported: ['RS256'],
|
|
};
|
|
|
|
const doctorSession = {
|
|
...policyAuthorSession,
|
|
scopes: [
|
|
...new Set([
|
|
...policyAuthorSession.scopes,
|
|
'ui.read',
|
|
'admin',
|
|
'ui.admin',
|
|
'orch:read',
|
|
'orch:operate',
|
|
'health:read',
|
|
'doctor:read',
|
|
]),
|
|
],
|
|
};
|
|
|
|
const mockPlugins = {
|
|
plugins: [
|
|
{
|
|
pluginId: 'integration.registry',
|
|
displayName: 'Registry Integration',
|
|
category: 'integration',
|
|
version: '1.0.0',
|
|
checkCount: 3,
|
|
},
|
|
],
|
|
total: 1,
|
|
};
|
|
|
|
const mockChecks = {
|
|
checks: [
|
|
{
|
|
checkId: 'integration.registry.v2-endpoint',
|
|
name: 'V2 Endpoint Check',
|
|
description: 'Verify OCI registry V2 API endpoint accessibility',
|
|
pluginId: 'integration.registry',
|
|
category: 'integration',
|
|
defaultSeverity: 'fail',
|
|
tags: ['registry', 'oci', 'connectivity'],
|
|
estimatedDurationMs: 5000,
|
|
},
|
|
{
|
|
checkId: 'integration.registry.auth-config',
|
|
name: 'Authentication Config',
|
|
description: 'Validate registry authentication configuration',
|
|
pluginId: 'integration.registry',
|
|
category: 'integration',
|
|
defaultSeverity: 'fail',
|
|
tags: ['registry', 'oci', 'auth'],
|
|
estimatedDurationMs: 3000,
|
|
},
|
|
{
|
|
checkId: 'integration.registry.referrers-api',
|
|
name: 'Referrers API Support',
|
|
description: 'Detect OCI 1.1 Referrers API support',
|
|
pluginId: 'integration.registry',
|
|
category: 'integration',
|
|
defaultSeverity: 'warn',
|
|
tags: ['registry', 'oci', 'referrers'],
|
|
estimatedDurationMs: 4000,
|
|
},
|
|
],
|
|
total: 3,
|
|
};
|
|
|
|
async function setupDoctorPage(page: Page): Promise<void> {
|
|
await page.addInitScript((stubSession) => {
|
|
(window as any).__stellaopsTestSession = stubSession;
|
|
}, doctorSession);
|
|
|
|
await page.route('**/config.json', (route) =>
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify(mockConfig),
|
|
}),
|
|
);
|
|
await page.route('**/platform/envsettings.json', (route) =>
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify(mockConfig),
|
|
}),
|
|
);
|
|
await page.route('https://authority.local/**', (route) => {
|
|
const url = route.request().url();
|
|
if (url.includes('/.well-known/openid-configuration')) {
|
|
return route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify(oidcConfig),
|
|
});
|
|
}
|
|
if (url.includes('/.well-known/jwks.json')) {
|
|
return route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({ keys: [] }),
|
|
});
|
|
}
|
|
return route.abort();
|
|
});
|
|
|
|
await page.route('**/doctor/api/v1/doctor/plugins**', (route) =>
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify(mockPlugins),
|
|
}),
|
|
);
|
|
await page.route('**/doctor/api/v1/doctor/checks**', (route) =>
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify(mockChecks),
|
|
}),
|
|
);
|
|
await page.route('**/doctor/api/v1/doctor/run', (route) =>
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({ runId: 'dr-mock-001' }),
|
|
}),
|
|
);
|
|
await page.route('**/doctor/api/v1/doctor/run/**', (route) =>
|
|
route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
runId: 'dr-mock-001',
|
|
status: 'completed',
|
|
startedAt: '2026-02-21T10:00:00Z',
|
|
completedAt: '2026-02-21T10:00:10Z',
|
|
durationMs: 10000,
|
|
summary: { passed: 2, info: 0, warnings: 1, failed: 0, skipped: 0, total: 3 },
|
|
overallSeverity: 'warn',
|
|
results: [],
|
|
}),
|
|
}),
|
|
);
|
|
}
|
|
|
|
async function openDoctor(page: Page): Promise<void> {
|
|
await page.goto('/ops/operations/doctor', { waitUntil: 'domcontentloaded' });
|
|
await page.waitForLoadState('networkidle', { timeout: 5000 }).catch(() => null);
|
|
}
|
|
|
|
test.describe('Doctor dashboard registry surface', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await setupDoctorPage(page);
|
|
});
|
|
|
|
test('loads Doctor diagnostics page with run controls', async ({ page }) => {
|
|
await openDoctor(page);
|
|
await expect(page.getByRole('heading', { name: 'Doctor Diagnostics' })).toBeVisible({
|
|
timeout: 15000,
|
|
});
|
|
await expect(page.getByRole('button', { name: /Quick Check/i })).toBeVisible();
|
|
await expect(page.getByRole('button', { name: /Normal Check/i })).toBeVisible();
|
|
await expect(page.getByRole('button', { name: /Full Check/i })).toBeVisible();
|
|
});
|
|
|
|
test('renders registry plugin and checks in doctor packs', async ({ page }) => {
|
|
await openDoctor(page);
|
|
await expect(page.getByRole('heading', { name: /Doctor Packs/i })).toBeVisible({ timeout: 15000 });
|
|
await expect(page.getByText(/Registry Integration/i)).toBeVisible();
|
|
await expect(page.getByText(/integration\.registry\.v2-endpoint/i)).toBeVisible();
|
|
});
|
|
|
|
test('filter controls and initial empty-state are visible', async ({ page }) => {
|
|
await openDoctor(page);
|
|
await expect(page.locator('#category-filter')).toBeVisible();
|
|
await expect(page.locator('#search-filter')).toBeVisible();
|
|
await expect(page.getByText(/No Diagnostics Run Yet/i)).toBeVisible();
|
|
});
|
|
});
|