mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 02:25:56 +00:00
Isolate Core E2E runtime state
Contract-Neutral: Core E2E scheduling and mock-readiness harness change; no deployment, installability, or runtime contract delta
This commit is contained in:
@@ -34,13 +34,13 @@ permissions:
|
||||
|
||||
jobs:
|
||||
e2e:
|
||||
name: Playwright Core E2E (shard ${{ matrix.shard }}/4)
|
||||
name: Playwright Core E2E (shard ${{ matrix.shard }}/8)
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 60
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
shard: [1, 2, 3, 4]
|
||||
shard: [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@@ -83,8 +83,9 @@ jobs:
|
||||
PULSE_E2E_SKIP_DOCKER: "true"
|
||||
PULSE_E2E_SKIP_PLAYWRIGHT_INSTALL: "true"
|
||||
PULSE_E2E_PERF: "1"
|
||||
PULSE_E2E_REQUIRE_DEFAULT_MOCK_READY: "true"
|
||||
PULSE_MULTI_TENANT_ENABLED: "true"
|
||||
run: npm test -- --shard=${{ matrix.shard }}/4
|
||||
run: npm test -- --shard=${{ matrix.shard }}/8
|
||||
|
||||
- name: Collect container logs
|
||||
if: always()
|
||||
|
||||
@@ -257,7 +257,9 @@ test.describe('Platform pages shell', () => {
|
||||
{ path: '/docker/images', testId: 'docker-page' },
|
||||
{ path: '/docker/storage', testId: 'docker-page' },
|
||||
{ path: '/docker/networks', testId: 'docker-page' },
|
||||
{ path: '/docker/swarm', testId: 'docker-page' },
|
||||
// The default mock inventory intentionally has no Swarm resources.
|
||||
// Empty inventory tabs omit search because there is nothing actionable
|
||||
// to filter; populated Swarm table toolbars have component coverage.
|
||||
{ path: '/kubernetes/nodes', testId: 'kubernetes-page' },
|
||||
{ path: '/kubernetes/workloads', testId: 'kubernetes-page' },
|
||||
{ path: '/kubernetes/services', testId: 'kubernetes-page' },
|
||||
|
||||
@@ -1112,9 +1112,14 @@ async function storageStateHasLiveSession(
|
||||
baseURL: preferredBrowserBaseURL(),
|
||||
storageState: statePath,
|
||||
});
|
||||
const page = await context.newPage();
|
||||
try {
|
||||
const res = await context.request.get("/api/state");
|
||||
return res.status() === 200;
|
||||
if (res.status() !== 200) {
|
||||
return false;
|
||||
}
|
||||
await waitForDefaultMockRuntimeReady(page);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
} finally {
|
||||
@@ -1122,6 +1127,83 @@ async function storageStateHasLiveSession(
|
||||
}
|
||||
}
|
||||
|
||||
type E2ERuntimeStateResource = {
|
||||
name?: string;
|
||||
sources?: string[];
|
||||
type?: string;
|
||||
};
|
||||
|
||||
type E2ERuntimeState = {
|
||||
connectedInfrastructure?: Array<{ name?: string }>;
|
||||
resources?: E2ERuntimeStateResource[];
|
||||
};
|
||||
|
||||
const requiresDefaultMockRuntimeReadiness = (): boolean =>
|
||||
["1", "true", "yes", "on"].includes(
|
||||
String(process.env.PULSE_E2E_REQUIRE_DEFAULT_MOCK_READY || "")
|
||||
.trim()
|
||||
.toLowerCase(),
|
||||
);
|
||||
|
||||
const resourceHasSource = (
|
||||
resource: E2ERuntimeStateResource,
|
||||
source: string,
|
||||
): boolean => resource.sources?.includes(source) === true;
|
||||
|
||||
async function waitForDefaultMockRuntimeReady(page: Page): Promise<void> {
|
||||
if (!requiresDefaultMockRuntimeReadiness()) {
|
||||
return;
|
||||
}
|
||||
|
||||
await expect
|
||||
.poll(
|
||||
async () => {
|
||||
const response = await page.request.get("/api/state").catch(() => null);
|
||||
if (!response?.ok()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const state = (await response.json()) as E2ERuntimeState;
|
||||
const resources = Array.isArray(state.resources) ? state.resources : [];
|
||||
const infrastructure = Array.isArray(state.connectedInfrastructure)
|
||||
? state.connectedInfrastructure
|
||||
: [];
|
||||
|
||||
return (
|
||||
resources.some(
|
||||
(resource) =>
|
||||
resource.name === "nvme-primary" &&
|
||||
resource.type === "storage" &&
|
||||
resourceHasSource(resource, "vmware"),
|
||||
) &&
|
||||
resources.some(
|
||||
(resource) =>
|
||||
resource.type === "k8s-cluster" &&
|
||||
resourceHasSource(resource, "kubernetes"),
|
||||
) &&
|
||||
resources.some(
|
||||
(resource) =>
|
||||
resource.name === "tank" &&
|
||||
resourceHasSource(resource, "truenas"),
|
||||
) &&
|
||||
resources.some((resource) => resource.type === "docker-host") &&
|
||||
resources.some((resource) => resource.type === "pbs") &&
|
||||
resources.some((resource) => resource.type === "pmg") &&
|
||||
infrastructure.some(
|
||||
(entry) => entry.name === "esxi-01.lab.local",
|
||||
)
|
||||
);
|
||||
},
|
||||
{
|
||||
message:
|
||||
"default mock inventory should be complete before browser fixtures start",
|
||||
timeout: 120_000,
|
||||
intervals: [1_000, 2_000, 5_000],
|
||||
},
|
||||
)
|
||||
.toBe(true);
|
||||
}
|
||||
|
||||
export async function createAuthenticatedStorageState(
|
||||
browser: Browser,
|
||||
storageStatePath: string,
|
||||
@@ -1144,6 +1226,7 @@ export async function createAuthenticatedStorageState(
|
||||
const page = await context.newPage();
|
||||
try {
|
||||
await ensureSessionAuthenticated(page);
|
||||
await waitForDefaultMockRuntimeReady(page);
|
||||
await context.storageState({ path: storageStatePath });
|
||||
} finally {
|
||||
await context.close();
|
||||
|
||||
Reference in New Issue
Block a user