mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-27 14:07:31 +00:00
5385cb0956
Main E2E 38020587365 exhausted twenty shared-readiness/login failures over 47 minutes, then probation hit the job limit before stable report uploads. Admit the same authenticated inventory and pool/disk history once before either tier, without retries or ignored failures, and upload completed stable failures before observational work. Share the existing readiness predicates and cover every identity/history requirement, actual spec cleanup and the workflow fail-fast command. Preserve phase thresholds, test tiers, pins and the stable verdict. This does not diagnose the hosted runtime failure. Change-source: pulse-maintainer
515 lines
24 KiB
JavaScript
515 lines
24 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs/promises';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
import test from 'node:test';
|
|
import { execFile } from 'node:child_process';
|
|
import { promisify } from 'node:util';
|
|
import { runInNewContext } from 'node:vm';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
import {
|
|
buildManagedLocalBackendEnv,
|
|
buildManagedLocalBackendState,
|
|
shouldBuildManagedLocalBackendFrontend,
|
|
shouldBuildManagedLocalBackendBinary,
|
|
} from './managed-local-backend.mjs';
|
|
|
|
const scriptsDir = path.dirname(fileURLToPath(import.meta.url));
|
|
const integrationRoot = path.resolve(scriptsDir, '..');
|
|
|
|
test('integration setup pins the governed Node.js major', async () => {
|
|
const [quickStart, setupScript] = await Promise.all([
|
|
fs.readFile(path.join(integrationRoot, 'QUICK_START.md'), 'utf8'),
|
|
fs.readFile(path.join(scriptsDir, 'setup.sh'), 'utf8'),
|
|
]);
|
|
|
|
assert.match(quickStart, /- Node\.js 24 and npm/);
|
|
assert.match(setupScript, /if \[ "\$NODE_VERSION" -ne 24 \]; then/);
|
|
assert.match(setupScript, /Node\.js major version 24 is required/);
|
|
assert.doesNotMatch(setupScript, /Node\.js 24\+/);
|
|
});
|
|
|
|
test('buildManagedLocalBackendState uses deterministic defaults', () => {
|
|
const state = buildManagedLocalBackendState({});
|
|
assert.equal(state.repoRoot, path.resolve(integrationRoot, '../..'));
|
|
assert.equal(state.backendVariant, 'core');
|
|
assert.equal(state.baseURL, 'http://127.0.0.1:8765');
|
|
assert.equal(state.metricsPort, '0');
|
|
assert.equal(state.billingStatePath, path.join(state.rootDir, 'data', 'billing.json'));
|
|
assert.equal(state.binaryPath, path.join(state.repoRoot, 'pulse'));
|
|
assert.equal(state.binaryBuildCwd, state.repoRoot);
|
|
assert.deepEqual(state.binaryBuildArgs, ['build', '-o', '__OUTPUT__', './cmd/pulse']);
|
|
assert.equal(
|
|
state.embeddedFrontendDistPath,
|
|
path.join(state.repoRoot, 'internal', 'api', 'frontend-modern', 'dist'),
|
|
);
|
|
});
|
|
|
|
test('buildManagedLocalBackendState scopes the working directory by run id', () => {
|
|
const state = buildManagedLocalBackendState({
|
|
PULSE_E2E_RUN_ID: 'parallel run/42',
|
|
});
|
|
|
|
assert.equal(
|
|
state.rootDir.endsWith(path.join('tmp', 'integration-local-backend', 'parallel-run-42')),
|
|
true,
|
|
);
|
|
});
|
|
|
|
test('buildManagedLocalBackendState supports enterprise variant with sibling repo defaults', () => {
|
|
const state = buildManagedLocalBackendState({
|
|
PULSE_E2E_LOCAL_BACKEND_VARIANT: 'enterprise',
|
|
});
|
|
|
|
assert.equal(state.backendVariant, 'enterprise');
|
|
assert.equal(
|
|
state.binaryPath,
|
|
path.join(state.repoRoot, 'tmp', 'integration-local-backend', 'bin', 'pulse-enterprise'),
|
|
);
|
|
assert.equal(
|
|
state.binaryBuildCwd,
|
|
path.join(path.dirname(state.repoRoot), 'pulse-enterprise'),
|
|
);
|
|
assert.deepEqual(state.binaryBuildArgs, ['build', '-buildvcs=false', '-o', '__OUTPUT__', './cmd/pulse-enterprise']);
|
|
assert.ok(state.binarySourceRoots.some((root) => root.endsWith(path.join('pulse-enterprise', 'internal'))));
|
|
assert.ok(state.binarySourceRoots.includes(path.join(state.repoRoot, 'internal')));
|
|
});
|
|
|
|
test('buildManagedLocalBackendEnv seeds auth, bootstrap token, and billing path', () => {
|
|
const state = buildManagedLocalBackendState({
|
|
PULSE_E2E_LOCAL_BACKEND_PORT: '9001',
|
|
PULSE_MULTI_TENANT_ENABLED: 'true',
|
|
});
|
|
const env = buildManagedLocalBackendEnv(state, {
|
|
PULSE_MULTI_TENANT_ENABLED: 'true',
|
|
});
|
|
|
|
assert.equal(env.PORT, '9001');
|
|
assert.equal(env.FRONTEND_PORT, '9001');
|
|
assert.equal(env.PULSE_DEV, 'true');
|
|
assert.equal(env.PULSE_DATA_DIR, state.dataDir);
|
|
assert.equal(env.PULSE_E2E_BILLING_STATE_PATH, state.billingStatePath);
|
|
assert.match(env.PULSE_AUDIT_SIGNING_KEY, /^[0-9a-f]{64}$/);
|
|
assert.equal(env.PULSE_E2E_BOOTSTRAP_TOKEN.length > 0, true);
|
|
assert.equal(env.PULSE_E2E_PRIMARY_API_TOKEN.length > 0, true);
|
|
assert.equal(env.PULSE_METRICS_PORT, '0');
|
|
assert.equal('ALLOW_ADMIN_BYPASS' in env, false);
|
|
assert.equal(env.PULSE_MULTI_TENANT_ENABLED, 'true');
|
|
assert.match(env.ALLOWED_ORIGINS, /5173/);
|
|
});
|
|
|
|
test('buildManagedLocalBackendEnv preserves explicit audit signing key', () => {
|
|
const state = buildManagedLocalBackendState({
|
|
PULSE_E2E_LOCAL_BACKEND_PORT: '9002',
|
|
});
|
|
const env = buildManagedLocalBackendEnv(state, {
|
|
PULSE_AUDIT_SIGNING_KEY: 'explicit-audit-key',
|
|
});
|
|
|
|
assert.equal(env.PULSE_AUDIT_SIGNING_KEY, 'explicit-audit-key');
|
|
});
|
|
|
|
test('buildManagedLocalBackendEnv accepts deterministic e2e audit signing key', () => {
|
|
const state = buildManagedLocalBackendState({
|
|
PULSE_E2E_LOCAL_BACKEND_PORT: '9002',
|
|
});
|
|
const env = buildManagedLocalBackendEnv(state, {
|
|
PULSE_E2E_AUDIT_SIGNING_KEY: 'e2e-audit-key',
|
|
});
|
|
|
|
assert.equal(env.PULSE_AUDIT_SIGNING_KEY, 'e2e-audit-key');
|
|
});
|
|
|
|
test('buildManagedLocalBackendEnv seeds deterministic auth in hosted mode', () => {
|
|
const state = buildManagedLocalBackendState({
|
|
PULSE_E2E_LOCAL_BACKEND_PORT: '9002',
|
|
PULSE_HOSTED_MODE: 'true',
|
|
});
|
|
const env = buildManagedLocalBackendEnv(state, {
|
|
PULSE_HOSTED_MODE: 'true',
|
|
});
|
|
|
|
assert.equal(env.PULSE_AUTH_USER, 'admin');
|
|
assert.equal(env.PULSE_AUTH_PASS, 'adminadminadmin');
|
|
});
|
|
|
|
test('buildManagedLocalBackendEnv preserves explicit hosted auth overrides', () => {
|
|
const state = buildManagedLocalBackendState({
|
|
PULSE_E2E_LOCAL_BACKEND_PORT: '9003',
|
|
PULSE_HOSTED_MODE: 'true',
|
|
});
|
|
const env = buildManagedLocalBackendEnv(state, {
|
|
PULSE_HOSTED_MODE: 'true',
|
|
PULSE_AUTH_USER: 'hosted-admin',
|
|
PULSE_AUTH_PASS: 'custom-password-123',
|
|
});
|
|
|
|
assert.equal(env.PULSE_AUTH_USER, 'hosted-admin');
|
|
assert.equal(env.PULSE_AUTH_PASS, 'custom-password-123');
|
|
});
|
|
|
|
test('buildManagedLocalBackendEnv seeds deterministic auth in demo mode', () => {
|
|
const state = buildManagedLocalBackendState({
|
|
PULSE_E2E_LOCAL_BACKEND_PORT: '9004',
|
|
DEMO_MODE: 'true',
|
|
});
|
|
const env = buildManagedLocalBackendEnv(state, {
|
|
DEMO_MODE: 'true',
|
|
});
|
|
|
|
assert.equal(env.PULSE_AUTH_USER, 'admin');
|
|
assert.equal(env.PULSE_AUTH_PASS, 'adminadminadmin');
|
|
});
|
|
|
|
test('multi-tenant release auth reuses storage state and classifies login rate limits', async () => {
|
|
const helpers = await fs.readFile(path.join(integrationRoot, 'tests', 'helpers.ts'), 'utf8');
|
|
const multiTenantSpec = await fs.readFile(
|
|
path.join(integrationRoot, 'tests', '03-multi-tenant.spec.ts'),
|
|
'utf8',
|
|
);
|
|
|
|
assert.match(multiTenantSpec, /createAuthenticatedStorageState/);
|
|
assert.match(multiTenantSpec, /storageState:\s*async\s*\(\{\s*authStorageStatePath\s*\},\s*use\)/);
|
|
assert.match(multiTenantSpec, /authStorageStatePath:\s*\[/);
|
|
assert.match(multiTenantSpec, /fs\.mkdtempSync\(path\.join\(authRoot, 'multi-tenant-'\)\)/);
|
|
assert.match(multiTenantSpec, /finally\s*\{\s*fs\.rmSync\(authDir, \{ recursive: true, force: true \}\)/);
|
|
assert.match(multiTenantSpec, /\{\s*scope:\s*'worker'\s*\}/);
|
|
|
|
assert.match(helpers, /new URL\(response\.url\(\)\)\.pathname === "\/api\/login"/);
|
|
assert.match(helpers, /response\.request\(\)\.method\(\)\.toUpperCase\(\) === "POST"/);
|
|
assert.match(helpers, /loginResponse\?\.status\(\) === 429/);
|
|
assert.match(helpers, /return "error:Too many requests"/);
|
|
assert.match(helpers, /\/too many\/i\.test\(lastOutcome\) \? 15_000/);
|
|
});
|
|
|
|
test('sharing diagnostic uses cookie-session auth without granting cross-org token access', async () => {
|
|
const spec = await fs.readFile(path.join(integrationRoot, 'tests', '03-multi-tenant.spec.ts'), 'utf8');
|
|
const sharing = spec.split("test('Scenario 6:")[1].split("test('Scenario 7:")[0];
|
|
assert.match(sharing, /await ensureSessionAuthenticated\(page\)/);
|
|
assert.doesNotMatch(sharing, /await ensureAuthenticated\(page\)/);
|
|
assert.match(sharing, /sessionStorage\.getItem\('pulse_auth'\)\)\)\.toBeNull\(\)/);
|
|
assert.match(sharing, /cookie\.name === 'pulse_session'/);
|
|
assert.match(sharing, /cookie\.name === '__Host-pulse_session'/);
|
|
assert.match(sharing, /getByRole\('button', \{ name: 'Accept' \}\)\)\.toBeVisible\(\)/);
|
|
});
|
|
|
|
test('shared browser readiness waits for the bounded mock history contract', async () => {
|
|
const helpers = await fs.readFile(path.join(integrationRoot, 'tests', 'helpers.ts'), 'utf8');
|
|
const compose = await fs.readFile(path.join(integrationRoot, 'docker-compose.test.yml'), 'utf8');
|
|
|
|
assert.match(compose, /PULSE_MOCK_TRENDS_SEED_DURATION=\$\{PULSE_MOCK_TRENDS_SEED_DURATION:-168h\}/);
|
|
assert.match(helpers, /"\/api\/storage-charts\?range=10080"/);
|
|
assert.match(helpers, /defaultMockInventoryReady\(await response\.json\(\)\)/);
|
|
assert.match(helpers, /defaultMockHistoryReady\(await response\.json\(\)\)/);
|
|
assert.match(helpers, /default-mock-readiness\.mjs/);
|
|
assert.match(helpers, /default mock history should cover the seven-day Core E2E chart window/);
|
|
});
|
|
|
|
test('multi-tenant release org switching waits for selected org UI state', async () => {
|
|
const helpers = await fs.readFile(path.join(integrationRoot, 'tests', 'helpers.ts'), 'utf8');
|
|
|
|
assert.match(helpers, /export async function switchOrg\(page: Page, orgId: string\)/);
|
|
assert.match(helpers, /window\.sessionStorage\.setItem\("pulse_org_id", id\)/);
|
|
assert.match(helpers, /window\.localStorage\.setItem\("pulse_org_id", id\)/);
|
|
assert.match(helpers, /document\.cookie = `pulse_org_id=\$\{encodeURIComponent\(id\)\}; Path=\/; SameSite=Lax`/);
|
|
assert.match(helpers, /await page\.reload\(\{ waitUntil: "domcontentloaded" \}\)/);
|
|
assert.match(helpers, /await waitForAppShell\(page\)/);
|
|
assert.match(helpers, /getByRole\("combobox", \{ name: "Organization" \}\)/);
|
|
assert.match(helpers, /toHaveValue\(\s*orgId,\s*\{\s*timeout: 20_000\s*\},\s*\)/);
|
|
});
|
|
|
|
test('shouldBuildManagedLocalBackendBinary returns true when binary is missing', async () => {
|
|
const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'pulse-managed-backend-'));
|
|
const state = {
|
|
repoRoot,
|
|
binaryPath: path.join(repoRoot, 'pulse'),
|
|
embeddedFrontendDistPath: path.join(repoRoot, 'internal', 'api', 'frontend-modern', 'dist'),
|
|
};
|
|
|
|
await fs.mkdir(path.join(repoRoot, 'cmd', 'pulse'), { recursive: true });
|
|
await fs.writeFile(path.join(repoRoot, 'cmd', 'pulse', 'main.go'), 'package main\n');
|
|
await fs.writeFile(path.join(repoRoot, 'go.mod'), 'module example.com/pulse\n');
|
|
|
|
await assert.doesNotReject(async () => {
|
|
assert.equal(await shouldBuildManagedLocalBackendBinary(state), true);
|
|
});
|
|
});
|
|
|
|
test('shouldBuildManagedLocalBackendBinary detects stale source inputs', async () => {
|
|
const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'pulse-managed-backend-'));
|
|
const binaryPath = path.join(repoRoot, 'pulse');
|
|
const mainPath = path.join(repoRoot, 'cmd', 'pulse', 'main.go');
|
|
const internalPath = path.join(repoRoot, 'internal', 'api', 'router.go');
|
|
const embeddedIndexPath = path.join(repoRoot, 'internal', 'api', 'frontend-modern', 'dist', 'index.html');
|
|
await fs.mkdir(path.dirname(mainPath), { recursive: true });
|
|
await fs.mkdir(path.dirname(internalPath), { recursive: true });
|
|
await fs.mkdir(path.dirname(embeddedIndexPath), { recursive: true });
|
|
await fs.writeFile(path.join(repoRoot, 'go.mod'), 'module example.com/pulse\n');
|
|
await fs.writeFile(mainPath, 'package main\n');
|
|
await fs.writeFile(internalPath, 'package api\n');
|
|
await fs.writeFile(embeddedIndexPath, '<!doctype html>');
|
|
await fs.writeFile(binaryPath, 'binary');
|
|
|
|
const older = new Date('2026-03-12T09:00:00Z');
|
|
const newer = new Date('2026-03-12T10:00:00Z');
|
|
await fs.utimes(binaryPath, older, older);
|
|
await fs.utimes(mainPath, older, older);
|
|
await fs.utimes(internalPath, newer, newer);
|
|
await fs.utimes(embeddedIndexPath, older, older);
|
|
|
|
assert.equal(
|
|
await shouldBuildManagedLocalBackendBinary({
|
|
repoRoot,
|
|
binaryPath,
|
|
embeddedFrontendDistPath: path.join(repoRoot, 'internal', 'api', 'frontend-modern', 'dist'),
|
|
}),
|
|
true,
|
|
);
|
|
});
|
|
|
|
test('shouldBuildManagedLocalBackendBinary skips rebuild when binary is fresh', async () => {
|
|
const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'pulse-managed-backend-'));
|
|
const binaryPath = path.join(repoRoot, 'pulse');
|
|
const mainPath = path.join(repoRoot, 'cmd', 'pulse', 'main.go');
|
|
const goModPath = path.join(repoRoot, 'go.mod');
|
|
const embeddedIndexPath = path.join(repoRoot, 'internal', 'api', 'frontend-modern', 'dist', 'index.html');
|
|
await fs.mkdir(path.dirname(mainPath), { recursive: true });
|
|
await fs.mkdir(path.dirname(embeddedIndexPath), { recursive: true });
|
|
await fs.writeFile(goModPath, 'module example.com/pulse\n');
|
|
await fs.writeFile(mainPath, 'package main\n');
|
|
await fs.writeFile(embeddedIndexPath, '<!doctype html>');
|
|
await fs.writeFile(binaryPath, 'binary');
|
|
|
|
const older = new Date('2026-03-12T09:00:00Z');
|
|
const newer = new Date('2026-03-12T10:00:00Z');
|
|
await fs.utimes(mainPath, older, older);
|
|
await fs.utimes(goModPath, older, older);
|
|
await fs.utimes(embeddedIndexPath, older, older);
|
|
await fs.utimes(binaryPath, newer, newer);
|
|
|
|
assert.equal(
|
|
await shouldBuildManagedLocalBackendBinary({
|
|
repoRoot,
|
|
binaryPath,
|
|
embeddedFrontendDistPath: path.join(repoRoot, 'internal', 'api', 'frontend-modern', 'dist'),
|
|
}),
|
|
false,
|
|
);
|
|
});
|
|
|
|
// A dependency-only update must not accidentally exercise a cached old SDK.
|
|
for (const manifest of ['go.mod', 'go.sum']) {
|
|
test(`shouldBuildManagedLocalBackendBinary rebuilds after ${manifest}-only dependency update`, async (t) => {
|
|
const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'pulse-managed-moby-'));
|
|
t.after(() => fs.rm(repoRoot, { recursive: true, force: true }));
|
|
const binaryPath = path.join(repoRoot, 'pulse');
|
|
const embeddedFrontendDistPath = path.join(repoRoot, 'internal', 'api', 'frontend-modern', 'dist');
|
|
await fs.mkdir(embeddedFrontendDistPath, { recursive: true });
|
|
const inputs = {
|
|
'go.mod': 'module example.com/pulse\nrequire github.com/moby/moby/client v0.6.0\n',
|
|
'go.sum': 'fixture dependency checksum\n',
|
|
};
|
|
const older = new Date('2026-03-12T09:00:00Z');
|
|
const built = new Date('2026-03-12T10:00:00Z');
|
|
const updated = new Date('2026-03-12T11:00:00Z');
|
|
for (const [name, content] of Object.entries(inputs)) {
|
|
await fs.writeFile(path.join(repoRoot, name), content);
|
|
await fs.utimes(path.join(repoRoot, name), older, older);
|
|
}
|
|
await fs.writeFile(binaryPath, 'fixture binary');
|
|
await fs.utimes(binaryPath, built, built);
|
|
const state = { repoRoot, binaryPath, embeddedFrontendDistPath };
|
|
assert.equal(await shouldBuildManagedLocalBackendBinary(state), false);
|
|
await fs.utimes(path.join(repoRoot, manifest), updated, updated);
|
|
assert.equal(await shouldBuildManagedLocalBackendBinary(state), true);
|
|
await fs.utimes(binaryPath, updated, updated);
|
|
assert.equal(await shouldBuildManagedLocalBackendBinary(state), false);
|
|
});
|
|
}
|
|
|
|
test('shouldBuildManagedLocalBackendBinary rebuilds enterprise variant when sibling enterprise source is newer', async () => {
|
|
const workspaceRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'pulse-managed-backend-enterprise-'));
|
|
const pulseRepoRoot = path.join(workspaceRoot, 'pulse');
|
|
const enterpriseRepoRoot = path.join(workspaceRoot, 'pulse-enterprise');
|
|
const binaryPath = path.join(pulseRepoRoot, 'tmp', 'integration-local-backend', 'bin', 'pulse-enterprise');
|
|
const enterpriseMainPath = path.join(enterpriseRepoRoot, 'cmd', 'pulse-enterprise', 'main.go');
|
|
const coreMainPath = path.join(pulseRepoRoot, 'cmd', 'pulse', 'main.go');
|
|
const embeddedIndexPath = path.join(pulseRepoRoot, 'internal', 'api', 'frontend-modern', 'dist', 'index.html');
|
|
|
|
await fs.mkdir(path.dirname(enterpriseMainPath), { recursive: true });
|
|
await fs.mkdir(path.dirname(coreMainPath), { recursive: true });
|
|
await fs.mkdir(path.dirname(embeddedIndexPath), { recursive: true });
|
|
await fs.mkdir(path.dirname(binaryPath), { recursive: true });
|
|
await fs.writeFile(path.join(pulseRepoRoot, 'go.mod'), 'module example.com/pulse\n');
|
|
await fs.writeFile(path.join(enterpriseRepoRoot, 'go.mod'), 'module example.com/pulse-enterprise\n');
|
|
await fs.writeFile(enterpriseMainPath, 'package main\n');
|
|
await fs.writeFile(coreMainPath, 'package main\n');
|
|
await fs.writeFile(embeddedIndexPath, '<!doctype html>');
|
|
await fs.writeFile(binaryPath, 'binary');
|
|
|
|
const older = new Date('2026-03-12T09:00:00Z');
|
|
const newer = new Date('2026-03-12T10:00:00Z');
|
|
await fs.utimes(binaryPath, older, older);
|
|
await fs.utimes(coreMainPath, older, older);
|
|
await fs.utimes(embeddedIndexPath, older, older);
|
|
await fs.utimes(enterpriseMainPath, newer, newer);
|
|
|
|
assert.equal(
|
|
await shouldBuildManagedLocalBackendBinary({
|
|
repoRoot: pulseRepoRoot,
|
|
binaryPath,
|
|
embeddedFrontendDistPath: path.join(pulseRepoRoot, 'internal', 'api', 'frontend-modern', 'dist'),
|
|
binarySourceRoots: [
|
|
path.join(enterpriseRepoRoot, 'cmd'),
|
|
path.join(enterpriseRepoRoot, 'internal'),
|
|
path.join(enterpriseRepoRoot, 'test'),
|
|
path.join(pulseRepoRoot, 'cmd'),
|
|
path.join(pulseRepoRoot, 'internal'),
|
|
path.join(pulseRepoRoot, 'pkg'),
|
|
],
|
|
binaryManifestPaths: [
|
|
path.join(enterpriseRepoRoot, 'go.mod'),
|
|
path.join(enterpriseRepoRoot, 'go.sum'),
|
|
path.join(pulseRepoRoot, 'go.mod'),
|
|
path.join(pulseRepoRoot, 'go.sum'),
|
|
],
|
|
}),
|
|
true,
|
|
);
|
|
});
|
|
|
|
test('shouldBuildManagedLocalBackendFrontend rebuilds when frontend sources are newer', async () => {
|
|
const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'pulse-managed-backend-'));
|
|
const frontendRoot = path.join(repoRoot, 'frontend-modern');
|
|
const embeddedFrontendDistPath = path.join(repoRoot, 'internal', 'api', 'frontend-modern', 'dist');
|
|
const sourcePath = path.join(frontendRoot, 'src', 'App.tsx');
|
|
const buildOutputPath = path.join(embeddedFrontendDistPath, 'index.html');
|
|
|
|
await fs.mkdir(path.dirname(sourcePath), { recursive: true });
|
|
await fs.mkdir(path.dirname(buildOutputPath), { recursive: true });
|
|
await fs.writeFile(path.join(frontendRoot, 'package.json'), '{"name":"pulse-modern"}');
|
|
await fs.writeFile(path.join(frontendRoot, 'index.html'), '<!doctype html>');
|
|
await fs.writeFile(sourcePath, 'export const App = () => null;\n');
|
|
await fs.writeFile(buildOutputPath, '<!doctype html>');
|
|
|
|
const older = new Date('2026-03-12T09:00:00Z');
|
|
const newer = new Date('2026-03-12T10:00:00Z');
|
|
await fs.utimes(buildOutputPath, older, older);
|
|
await fs.utimes(path.join(frontendRoot, 'package.json'), older, older);
|
|
await fs.utimes(path.join(frontendRoot, 'index.html'), older, older);
|
|
await fs.utimes(sourcePath, newer, newer);
|
|
|
|
assert.equal(
|
|
await shouldBuildManagedLocalBackendFrontend({ frontendRoot, embeddedFrontendDistPath }),
|
|
true,
|
|
);
|
|
});
|
|
|
|
test('shouldBuildManagedLocalBackendFrontend skips rebuild when embedded frontend is fresh', async () => {
|
|
const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'pulse-managed-backend-'));
|
|
const frontendRoot = path.join(repoRoot, 'frontend-modern');
|
|
const embeddedFrontendDistPath = path.join(repoRoot, 'internal', 'api', 'frontend-modern', 'dist');
|
|
const sourcePath = path.join(frontendRoot, 'src', 'App.tsx');
|
|
const buildOutputPath = path.join(embeddedFrontendDistPath, 'index.html');
|
|
|
|
await fs.mkdir(path.dirname(sourcePath), { recursive: true });
|
|
await fs.mkdir(path.dirname(buildOutputPath), { recursive: true });
|
|
await fs.writeFile(path.join(frontendRoot, 'package.json'), '{"name":"pulse-modern"}');
|
|
await fs.writeFile(path.join(frontendRoot, 'index.html'), '<!doctype html>');
|
|
await fs.writeFile(sourcePath, 'export const App = () => null;\n');
|
|
await fs.writeFile(buildOutputPath, '<!doctype html>');
|
|
|
|
const older = new Date('2026-03-12T09:00:00Z');
|
|
const newer = new Date('2026-03-12T10:00:00Z');
|
|
await fs.utimes(sourcePath, older, older);
|
|
await fs.utimes(path.join(frontendRoot, 'package.json'), older, older);
|
|
await fs.utimes(path.join(frontendRoot, 'index.html'), older, older);
|
|
await fs.utimes(buildOutputPath, newer, newer);
|
|
|
|
assert.equal(
|
|
await shouldBuildManagedLocalBackendFrontend({ frontendRoot, embeddedFrontendDistPath }),
|
|
false,
|
|
);
|
|
});
|
|
|
|
// Diagnostic orchestration must not impersonate a stable runtime gate.
|
|
import { spawnSync } from 'node:child_process';
|
|
|
|
function list(config, tier = '') {
|
|
return spawnSync(process.execPath, [
|
|
'node_modules/@playwright/test/cli.js', 'test',
|
|
'--config', config, 'tests/03-multi-tenant.spec.ts', '--list',
|
|
], { cwd: integrationRoot, env: { ...process.env, PULSE_E2E_TIER: tier }, encoding: 'utf8' });
|
|
}
|
|
test('explicit diagnostic discovers only the seven desktop multi-tenant scenarios', () => {
|
|
const result = list('playwright.multi-tenant-diagnostic.config.ts');
|
|
assert.equal(result.status, 0, result.stdout + result.stderr);
|
|
assert.match(result.stdout, /Total: 7 tests in 1 file/);
|
|
assert.doesNotMatch(result.stdout, /mobile-chrome|mobile-safari/);
|
|
});
|
|
for (const tier of ['stable', 'probation']) {
|
|
test(`diagnostic refuses ${tier} gate identity`, () => {
|
|
const result = list('playwright.multi-tenant-diagnostic.config.ts', tier);
|
|
assert.notEqual(result.status, 0);
|
|
assert.match(result.stderr, /diagnostics cannot run as an E2E tier/);
|
|
});
|
|
}
|
|
test('normal stable configuration retains multi-tenant quarantine', () => {
|
|
const result = list('playwright.config.ts', 'stable');
|
|
assert.notEqual(result.status, 0);
|
|
assert.match(result.stdout, /Total: 0 tests/);
|
|
});
|
|
|
|
|
|
test('owned cookie state selects the supervisor path without changing direct-run fallback', async () => {
|
|
const helpers = await fs.readFile(path.join(integrationRoot, 'tests', 'helpers.ts'), 'utf8');
|
|
const declaration = helpers.match(/const sharedCookieStatePath = [\s\S]*?;\n/);
|
|
assert.ok(declaration, 'cookie path resolver must remain inspectable');
|
|
const source = declaration[0].replace('(): string', '()') + '\nsharedCookieStatePath();';
|
|
const resolve = value => runInNewContext(source, {
|
|
path, helpersDir: path.join(integrationRoot, 'tests'),
|
|
process: { env: { PULSE_E2E_COOKIE_STATE_PATH: value } },
|
|
});
|
|
assert.equal(resolve(' /invocation-owned/cookies.json '), '/invocation-owned/cookies.json');
|
|
for (const value of [undefined, '', ' ']) {
|
|
assert.equal(resolve(value), path.join(integrationRoot, 'tmp/playwright-auth/shared-cookie-session.json'));
|
|
}
|
|
});
|
|
|
|
test('owned runner orchestration proves reaping, signal cleanup and concurrent isolation', { timeout: 30000 }, async () => {
|
|
const run = promisify(execFile);
|
|
// Registry-recognised orchestration proof executes the behavioural fixtures;
|
|
// neither a source-string match nor a fixture result is browser acceptance.
|
|
await run('python3', [path.join(scriptsDir, 'owned-run.test.py')], { timeout: 20000 });
|
|
await run(process.execPath, ['--test', path.join(scriptsDir, 'run-tests-interruption.test.mjs')], { timeout: 15000 });
|
|
});
|
|
|
|
test('managed offline provisioning preserves ephemeral trust and strict Community validation', async (t) => {
|
|
const { startOfflineLicenseIssuer } = await import('./offline-license-issuer.mjs');
|
|
const issuer = await startOfflineLicenseIssuer();
|
|
t.after(() => issuer.close());
|
|
const env = {
|
|
PULSE_E2E_RUN_ID: 'offline-provisioning-contract',
|
|
PULSE_LICENSE_PUBLIC_KEY: issuer.publicKey,
|
|
PULSE_LICENSE_SERVER_URL: issuer.url,
|
|
PULSE_LICENSE_DEV_MODE: 'false',
|
|
PULSE_MOCK_MODE: 'false',
|
|
PULSE_MULTI_TENANT_ENABLED: 'true',
|
|
PULSE_E2E_OFFLINE_ACTIVATION_KEY: issuer.activationKey,
|
|
};
|
|
const state = buildManagedLocalBackendState(env);
|
|
const backend = buildManagedLocalBackendEnv(state, env);
|
|
assert.equal(state.backendVariant, 'core');
|
|
assert.deepEqual(state.binaryBuildArgs, ['build', '-o', '__OUTPUT__', './cmd/pulse']);
|
|
for (const key of Object.keys(env).filter(key => key !== 'PULSE_E2E_RUN_ID')) {
|
|
assert.equal(backend[key], env[key], `${key} must reach the actual managed backend unchanged`);
|
|
}
|
|
assert.equal(new URL(backend.PULSE_LICENSE_SERVER_URL).hostname, '127.0.0.1');
|
|
assert.equal(Buffer.from(backend.PULSE_LICENSE_PUBLIC_KEY, 'base64').length, 32);
|
|
assert.equal(backend.PULSE_DATA_DIR, state.dataDir);
|
|
// Development orchestration remains independent of license validation bypass.
|
|
assert.equal(backend.PULSE_DEV, 'true');
|
|
assert.equal(backend.PULSE_LICENSE_DEV_MODE, 'false');
|
|
});
|