PSA/ee/appliance/operator/tests/status.test.mjs
Hermes 284313f908
Some checks are pending
Bidi Control Character Guard / bidi-control-guard (push) Waiting to run
Circular Dependency Check / Check for new circular dependencies (push) Waiting to run
Citus Migration Smoke / Combined migrations on single-node Citus (push) Waiting to run
E2E Fresh Install Tests / fresh-install-e2e (push) Waiting to run
ext-v2 guardrails / Run ext-v2 guard and ESLint (push) Waiting to run
Integration Tests / Check for relevant changes (push) Waiting to run
Integration Tests / ${{ (github.event_name == 'schedule' || github.event.inputs.suite == 'full') && 'Full integration suite' || 'Tier-1 integration subset' }} (push) Blocked by required conditions
Mobile checks / Mobile lint + typecheck (push) Waiting to run
Mobile checks / Mobile unit tests (push) Waiting to run
Mobile checks / Mobile dependency audit (report) (push) Waiting to run
Mobile checks / Mobile reproducibility checks (push) Waiting to run
Secrets guard (env backups) / Ensure no tracked env backup files (push) Waiting to run
Temporal Readiness / fast-readiness (push) Waiting to run
Temporal Readiness / docker-parity (push) Waiting to run
TypeScript Type Check / Nx affected typecheck (push) Waiting to run
Unit Tests / Skipped-test budget (push) Waiting to run
Unit Tests / Nx affected unit tests (push) Waiting to run
Unit Tests / Server unit coverage (informational) (push) Waiting to run
Validate Tenant Management Schema / Check for relevant changes (push) Waiting to run
Validate Tenant Management Schema / Validate Tenant Management Schema (push) Blocked by required conditions
EE Workflows Build Guard / ee-workflows-build-guard (push) Waiting to run
Initial import of AlgaPSA codebase from PSA server
Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz

Source: /opt/alga-psa on psa.joliet.tech
2026-06-22 16:12:17 -05:00

687 lines
27 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { collectStatus } from '../lib/status.mjs';
class MockCaptureRunner {
constructor(responses) {
this.responses = responses;
}
async runCapture(command, args) {
const key = `${command} ${args.join(' ')}`;
const response = this.responses[key];
if (!response) {
return { ok: false, code: 1, output: 'missing mock response' };
}
if (response.ok) {
return { ok: true, code: 0, output: response.output ?? '' };
}
return { ok: false, code: response.code ?? 1, output: response.output ?? '' };
}
}
function buildEnv() {
return {
runtime: { assetRoot: '/tmp' },
site: { siteId: 'appliance-single-node', configDir: '/tmp/config' },
paths: { kubeconfig: '/tmp/kubeconfig', talosconfig: '/tmp/talosconfig' },
nodeIp: '10.0.0.2',
appUrl: 'https://psa.example.com',
};
}
function readyCondition(status = 'True', message = '') {
return [{ type: 'Ready', status, message }];
}
function healthyResponses() {
const responses = {
'talosctl --talosconfig /tmp/talosconfig -n 10.0.0.2 -e 10.0.0.2 health --wait-timeout 20s': { ok: true, output: 'ok' },
'kubectl --kubeconfig /tmp/kubeconfig get --raw=/readyz': { ok: true, output: 'ok' },
'kubectl --kubeconfig /tmp/kubeconfig get nodes -o json': {
ok: true,
output: JSON.stringify({
items: [{ metadata: { name: 'node-1' }, status: { conditions: readyCondition('True') } }],
}),
},
'kubectl --kubeconfig /tmp/kubeconfig -n flux-system get gitrepositories.source.toolkit.fluxcd.io -o json': {
ok: true,
output: JSON.stringify({
items: [
{
metadata: { name: 'alga-appliance' },
status: { conditions: readyCondition('True'), artifact: { revision: 'sha1:1234abcd' } },
},
],
}),
},
'kubectl --kubeconfig /tmp/kubeconfig -n flux-system get kustomizations.kustomize.toolkit.fluxcd.io -o json': {
ok: true,
output: JSON.stringify({
items: [
{ metadata: { name: 'alga-platform' }, status: { conditions: readyCondition('True') } },
{ metadata: { name: 'alga-core' }, status: { conditions: readyCondition('True') } },
{ metadata: { name: 'alga-background' }, status: { conditions: readyCondition('True') } },
],
}),
},
'kubectl --kubeconfig /tmp/kubeconfig -n alga-system get helmreleases.helm.toolkit.fluxcd.io -o json': {
ok: true,
output: JSON.stringify({ items: [{ metadata: { name: 'alga-core' }, status: { conditions: readyCondition('True') } }] }),
},
'kubectl --kubeconfig /tmp/kubeconfig -n alga-system get configmap/appliance-release-selection -o json': {
ok: true,
output: JSON.stringify({ data: { releaseVersion: '1.0.0', selectedChannel: 'stable', appVersion: '1.0.0', manifestDigest: 'sha256:release' } }),
},
'kubectl --kubeconfig /tmp/kubeconfig -n alga-system get configmap/appliance-values-alga-core -o json': {
ok: true,
output: JSON.stringify({ data: { 'alga-core.single-node.yaml': 'appUrl: https://psa.example.com' } }),
},
'kubectl --kubeconfig /tmp/kubeconfig -n msp get jobs.batch -o json': {
ok: true,
output: JSON.stringify({ items: [] }),
},
'kubectl --kubeconfig /tmp/kubeconfig get events --sort-by=.metadata.creationTimestamp -A -o json': {
ok: true,
output: JSON.stringify({
items: [
{
metadata: { namespace: 'msp', creationTimestamp: '2026-04-30T00:00:00Z' },
reason: 'Pulled',
type: 'Normal',
message: 'Successfully pulled image',
involvedObject: { kind: 'Pod', name: 'alga-core-sebastian-abc' },
},
],
}),
},
'curl -ksS -I --max-time 10 https://psa.example.com': {
ok: true,
output: 'HTTP/2 302\nlocation: https://psa.example.com/msp/dashboard\n',
},
};
const workloadResources = [
'deployment/alga-core-sebastian',
'statefulset/db',
'statefulset/redis',
'deployment/pgbouncer',
'deployment/temporal',
'deployment/workflow-worker',
'deployment/email-service',
'deployment/temporal-worker',
];
for (const resource of workloadResources) {
responses[`kubectl --kubeconfig /tmp/kubeconfig -n msp get ${resource} -o json`] = {
ok: true,
output: JSON.stringify({ spec: { replicas: 1 }, status: { readyReplicas: 1 } }),
};
}
return responses;
}
test('T001: canonical status shape includes release, urls, rollup, tiers, blockers, components, and events', async () => {
const status = await collectStatus(buildEnv(), {
runner: new MockCaptureRunner(healthyResponses()),
});
assert.equal(status.canonical.siteId, 'appliance-single-node');
assert.equal(status.canonical.release.selectedReleaseVersion, '1.0.0');
assert.equal(status.canonical.release.appVersion, '1.0.0');
assert.equal(status.canonical.release.gitRevision, 'sha1:1234abcd');
assert.equal(status.canonical.urls.loginUrl, 'https://psa.example.com');
assert.equal(status.canonical.urls.statusUrl, 'http://10.0.0.2:8080');
assert.equal(status.canonical.rollup.state, 'fully_healthy');
assert.equal(status.canonical.tiers.platform.ready, true);
assert.equal(status.canonical.tiers.login.ready, true);
assert.equal(status.canonical.topBlockers.length, 0);
assert.equal(status.canonical.components.length, 8);
assert.equal(status.canonical.recentEvents.length, 1);
assert.equal(status.canonical.loginProbe.reachable, true);
});
test('T002: login-ready with background failures rolls up as ready_with_background_issues', async () => {
const responses = healthyResponses();
responses['kubectl --kubeconfig /tmp/kubeconfig -n msp get deployment/workflow-worker -o json'] = {
ok: true,
output: JSON.stringify({ spec: { replicas: 1 }, status: { readyReplicas: 0 } }),
};
responses['kubectl --kubeconfig /tmp/kubeconfig -n flux-system get kustomizations.kustomize.toolkit.fluxcd.io -o json'] = {
ok: true,
output: JSON.stringify({
items: [
{ metadata: { name: 'alga-platform' }, status: { conditions: readyCondition('True') } },
{ metadata: { name: 'alga-core' }, status: { conditions: readyCondition('True') } },
{ metadata: { name: 'alga-background' }, status: { conditions: readyCondition('False', 'dependency not ready') } },
],
}),
};
const status = await collectStatus(buildEnv(), {
runner: new MockCaptureRunner(responses),
});
assert.equal(status.canonical.tiers.login.ready, true);
assert.equal(status.canonical.tiers.background.ready, false);
assert.equal(status.canonical.rollup.state, 'ready_with_background_issues');
assert.notEqual(status.canonical.rollup.state, 'failed_action_required');
});
test('T003: core blocker keeps LOGIN_READY false and rollup failed_action_required', async () => {
const responses = healthyResponses();
responses['kubectl --kubeconfig /tmp/kubeconfig -n msp get statefulset/db -o json'] = {
ok: true,
output: JSON.stringify({ spec: { replicas: 1 }, status: { readyReplicas: 0 } }),
};
const status = await collectStatus(buildEnv(), {
runner: new MockCaptureRunner(responses),
});
assert.equal(status.canonical.tiers.core.ready, false);
assert.equal(status.canonical.tiers.login.ready, false);
assert.equal(status.canonical.rollup.state, 'failed_action_required');
});
test('T004: status dashboard model includes talos, cluster, flux, workloads, release, and config paths', async () => {
const status = await collectStatus(buildEnv(), {
runner: new MockCaptureRunner(healthyResponses()),
});
assert.equal(status.connectivityMode, 'app-healthy');
assert.equal(status.topBlocker.layer, 'none');
assert.equal(status.host.status, 'healthy');
assert.equal(status.cluster.apiReachable, true);
assert.equal(status.flux.status, 'healthy');
assert.equal(status.workloads.components.length, 8);
assert.equal(status.release.selectedReleaseVersion, '1.0.0');
assert.equal(status.release.appUrl, 'https://psa.example.com');
assert.equal(status.configPaths.kubeconfig, '/tmp/kubeconfig');
});
test('T005: talos-only state reports Kubernetes availability as blocker', async () => {
const responses = healthyResponses();
responses['kubectl --kubeconfig /tmp/kubeconfig get --raw=/readyz'] = { ok: false, output: 'connection refused' };
const status = await collectStatus(buildEnv(), {
runner: new MockCaptureRunner(responses),
});
assert.equal(status.connectivityMode, 'talos-only');
assert.equal(status.topBlocker.layer, 'Kubernetes availability');
});
test('T005: cluster-down with talos unreachable reports talos blocker', async () => {
const responses = healthyResponses();
responses['talosctl --talosconfig /tmp/talosconfig -n 10.0.0.2 -e 10.0.0.2 health --wait-timeout 20s'] = {
ok: false,
output: 'dial tcp timeout',
};
responses['kubectl --kubeconfig /tmp/kubeconfig get --raw=/readyz'] = { ok: false, output: 'connection refused' };
const status = await collectStatus(buildEnv(), {
runner: new MockCaptureRunner(responses),
});
assert.equal(status.connectivityMode, 'degraded');
assert.equal(status.topBlocker.layer, 'Talos host reachability');
});
test('T005: flux-degraded state reports Flux blocker', async () => {
const responses = healthyResponses();
responses['kubectl --kubeconfig /tmp/kubeconfig -n flux-system get kustomizations.kustomize.toolkit.fluxcd.io -o json'] = {
ok: true,
output: JSON.stringify({
items: [{ metadata: { name: 'alga-appliance' }, status: { conditions: readyCondition('False', 'reconcile failed') } }],
}),
};
const status = await collectStatus(buildEnv(), {
runner: new MockCaptureRunner(responses),
});
assert.equal(status.topBlocker.layer, 'Flux source/reconcile failure');
});
test('T005: workload-unhealthy state reports workload blocker', async () => {
const responses = healthyResponses();
responses['kubectl --kubeconfig /tmp/kubeconfig -n msp get deployment/temporal-worker -o json'] = {
ok: true,
output: JSON.stringify({ spec: { replicas: 1 }, status: { readyReplicas: 0 } }),
};
const status = await collectStatus(buildEnv(), {
runner: new MockCaptureRunner(responses),
});
assert.equal(status.workloads.status, 'unhealthy');
assert.equal(status.topBlocker.layer, 'workload readiness failure');
});
test('T004: DNS resolver failures are classified with explicit DNS remediation guidance', async () => {
const responses = healthyResponses();
responses['kubectl --kubeconfig /tmp/kubeconfig get events --sort-by=.metadata.creationTimestamp -A -o json'] = {
ok: true,
output: JSON.stringify({
items: [
{
metadata: { namespace: 'flux-system', creationTimestamp: '2026-04-30T00:00:00Z' },
reason: 'Failed',
type: 'Warning',
message: 'lookup factory.talos.dev on 192.168.64.1:53: connection refused',
involvedObject: { kind: 'Pod', name: 'source-controller-abc' },
},
],
}),
};
const status = await collectStatus(buildEnv(), {
runner: new MockCaptureRunner(responses),
});
assert.equal(status.topBlocker.layer, 'Platform DNS resolution');
assert.match(status.topBlocker.reason, /DNS resolver failure detected/i);
assert.match(status.topBlocker.nextAction, /Configure explicit DNS servers/i);
});
test('T005: Postgres subPath failure is classified as a core storage blocker', async () => {
const responses = healthyResponses();
responses['kubectl --kubeconfig /tmp/kubeconfig -n msp get statefulset/db -o json'] = {
ok: true,
output: JSON.stringify({ spec: { replicas: 1 }, status: { readyReplicas: 0 } }),
};
responses['kubectl --kubeconfig /tmp/kubeconfig get events --sort-by=.metadata.creationTimestamp -A -o json'] = {
ok: true,
output: JSON.stringify({
items: [
{
metadata: { namespace: 'msp', creationTimestamp: '2026-04-30T00:00:00Z' },
reason: 'Failed',
type: 'Warning',
message: 'failed to create subPath directory for volumeMount "db-data"',
involvedObject: { kind: 'Pod', name: 'db-0' },
},
],
}),
};
const status = await collectStatus(buildEnv(), {
runner: new MockCaptureRunner(responses),
});
assert.equal(status.topBlocker.layer, 'Core Postgres storage initialization');
assert.match(status.topBlocker.reason, /PVC\/subPath initialization failed/i);
assert.match(status.topBlocker.nextAction, /repair or recreate the Postgres PVC subPath/i);
});
test('T006: workflow-worker missing image tag is classified as background-only blocker', async () => {
const responses = healthyResponses();
responses['kubectl --kubeconfig /tmp/kubeconfig -n msp get deployment/workflow-worker -o json'] = {
ok: true,
output: JSON.stringify({ spec: { replicas: 1 }, status: { readyReplicas: 0 } }),
};
responses['kubectl --kubeconfig /tmp/kubeconfig get events --sort-by=.metadata.creationTimestamp -A -o json'] = {
ok: true,
output: JSON.stringify({
items: [
{
metadata: { namespace: 'msp', creationTimestamp: '2026-04-30T00:00:00Z' },
reason: 'Failed',
type: 'Warning',
message: 'Failed to pull image "ghcr.io/nine-minds/workflow-worker:61e4a00e": not found',
involvedObject: { kind: 'Pod', name: 'workflow-worker-6c5f8f7d9b-abcde' },
},
],
}),
};
const status = await collectStatus(buildEnv(), {
runner: new MockCaptureRunner(responses),
});
assert.equal(status.topBlocker.layer, 'Image tag availability');
assert.equal(status.topBlocker.component, 'workflow-worker');
assert.equal(status.topBlocker.loginBlocking, false);
assert.match(status.topBlocker.reason, /workflow-worker:61e4a00e/i);
});
test('T007: alga-core missing image tag is classified as login-blocking blocker', async () => {
const responses = healthyResponses();
responses['kubectl --kubeconfig /tmp/kubeconfig -n msp get deployment/alga-core-sebastian -o json'] = {
ok: true,
output: JSON.stringify({ spec: { replicas: 1 }, status: { readyReplicas: 0 } }),
};
responses['kubectl --kubeconfig /tmp/kubeconfig get events --sort-by=.metadata.creationTimestamp -A -o json'] = {
ok: true,
output: JSON.stringify({
items: [
{
metadata: { namespace: 'msp', creationTimestamp: '2026-04-30T00:00:00Z' },
reason: 'Failed',
type: 'Warning',
message: 'ImagePullBackOff: Failed to pull image "ghcr.io/nine-minds/alga-psa-ee:deadbeef": not found',
involvedObject: { kind: 'Pod', name: 'alga-core-sebastian-5bd8b8d9f8-xxyyy' },
},
],
}),
};
const status = await collectStatus(buildEnv(), {
runner: new MockCaptureRunner(responses),
});
assert.equal(status.topBlocker.layer, 'Image tag availability');
assert.equal(status.topBlocker.component, 'alga-core');
assert.equal(status.topBlocker.loginBlocking, true);
assert.match(status.topBlocker.reason, /alga-psa-ee:deadbeef/i);
});
test('T008: image pull context canceled is classified as retryable interruption', async () => {
const responses = healthyResponses();
responses['kubectl --kubeconfig /tmp/kubeconfig -n msp get deployment/email-service -o json'] = {
ok: true,
output: JSON.stringify({ spec: { replicas: 1 }, status: { readyReplicas: 0 } }),
};
responses['kubectl --kubeconfig /tmp/kubeconfig get events --sort-by=.metadata.creationTimestamp -A -o json'] = {
ok: true,
output: JSON.stringify({
items: [
{
metadata: { namespace: 'msp', creationTimestamp: '2026-04-30T00:00:00Z' },
reason: 'Failed',
type: 'Warning',
message: 'Failed to pull image "ghcr.io/nine-minds/email-service:61e4a00e": context canceled',
involvedObject: { kind: 'Pod', name: 'email-service-7cc4f7f9d8-abcde' },
},
],
}),
};
const status = await collectStatus(buildEnv(), {
runner: new MockCaptureRunner(responses),
});
assert.equal(status.topBlocker.layer, 'Image pull interruption');
assert.equal(status.topBlocker.component, 'email-service');
assert.match(status.topBlocker.reason, /retryable/i);
assert.match(status.topBlocker.nextAction, /automatic retry/i);
});
test('image tag not found takes precedence over older interrupted pull events', async () => {
const responses = healthyResponses();
responses['kubectl --kubeconfig /tmp/kubeconfig -n msp get deployment/workflow-worker -o json'] = {
ok: true,
output: JSON.stringify({ spec: { replicas: 1 }, status: { readyReplicas: 0 } }),
};
responses['kubectl --kubeconfig /tmp/kubeconfig get events --sort-by=.metadata.creationTimestamp -A -o json'] = {
ok: true,
output: JSON.stringify({
items: [
{
metadata: { namespace: 'msp', creationTimestamp: '2026-04-30T00:00:00Z' },
reason: 'Failed',
type: 'Warning',
message: 'Failed to pull image "ghcr.io/nine-minds/workflow-worker:61e4a00e": context canceled',
involvedObject: { kind: 'Pod', name: 'workflow-worker-6c5f8f7d9b-abcde' },
},
{
metadata: { namespace: 'msp', creationTimestamp: '2026-04-30T00:01:00Z' },
reason: 'Failed',
type: 'Warning',
message: 'Failed to pull image "ghcr.io/nine-minds/workflow-worker:61e4a00e": not found',
involvedObject: { kind: 'Pod', name: 'workflow-worker-6c5f8f7d9b-abcde' },
},
],
}),
};
const status = await collectStatus(buildEnv(), {
runner: new MockCaptureRunner(responses),
});
assert.equal(status.topBlocker.layer, 'Image tag availability');
assert.equal(status.topBlocker.component, 'workflow-worker');
assert.equal(status.topBlocker.loginBlocking, false);
});
test('recent events keep the latest entries instead of stale oldest entries', async () => {
const responses = healthyResponses();
responses['kubectl --kubeconfig /tmp/kubeconfig get events --sort-by=.metadata.creationTimestamp -A -o json'] = {
ok: true,
output: JSON.stringify({
items: Array.from({ length: 25 }, (_, index) => ({
metadata: { namespace: 'msp', creationTimestamp: `2026-04-30T00:${String(index).padStart(2, '0')}:00Z` },
reason: 'Pulled',
type: 'Normal',
message: `event-${index}`,
involvedObject: { kind: 'Pod', name: `pod-${index}` },
})),
}),
};
const status = await collectStatus(buildEnv(), {
runner: new MockCaptureRunner(responses),
});
assert.equal(status.canonical.recentEvents.length, 20);
assert.equal(status.canonical.recentEvents[0].message, 'event-5');
assert.equal(status.canonical.recentEvents[19].message, 'event-24');
});
test('Talos client auth failure does not block LOGIN_READY when Kubernetes and core are healthy', async () => {
const responses = healthyResponses();
responses['talosctl --talosconfig /tmp/talosconfig -n 10.0.0.2 -e 10.0.0.2 health --wait-timeout 20s'] = {
ok: false,
output: 'x509: certificate signed by unknown authority',
};
const status = await collectStatus(buildEnv(), {
runner: new MockCaptureRunner(responses),
});
assert.equal(status.host.status, 'unreachable');
assert.equal(status.canonical.tiers.platform.ready, true);
assert.equal(status.canonical.tiers.login.ready, true);
assert.equal(status.canonical.rollup.state, 'fully_healthy');
});
test('active core image pull reports installing state with size estimate instead of action required', async () => {
const responses = healthyResponses();
responses['kubectl --kubeconfig /tmp/kubeconfig -n msp get deployment/alga-core-sebastian -o json'] = {
ok: true,
output: JSON.stringify({ spec: { replicas: 1 }, status: { readyReplicas: 0 } }),
};
responses['kubectl --kubeconfig /tmp/kubeconfig get events --sort-by=.metadata.creationTimestamp -A -o json'] = {
ok: true,
output: JSON.stringify({
items: [
{
metadata: { namespace: 'msp', creationTimestamp: '2026-04-30T00:00:00Z' },
reason: 'Pulling',
type: 'Normal',
message: 'Pulling image "ghcr.io/nine-minds/alga-psa-ee:94446747"',
involvedObject: { kind: 'Pod', name: 'alga-core-sebastian-5bd8b8d9f8-xxyyy' },
},
],
}),
};
const status = await collectStatus(buildEnv(), {
runner: new MockCaptureRunner(responses),
});
assert.equal(status.canonical.rollup.state, 'installing');
assert.match(status.canonical.rollup.message, /Pulling image ghcr\.io\/nine-minds\/alga-psa-ee:94446747/i);
assert.equal(status.canonical.activeOperations.length, 1);
assert.equal(status.canonical.activeOperations[0].component, 'alga-core');
assert.equal(status.canonical.activeOperations[0].estimatedSizeHuman, '~1.8 GB');
assert.equal(status.canonical.activeOperations[0].progressAvailable, false);
});
test('T009: helm timeout with db subPath failure reports db storage blocker as top cause', async () => {
const responses = healthyResponses();
responses['kubectl --kubeconfig /tmp/kubeconfig -n alga-system get helmreleases.helm.toolkit.fluxcd.io -o json'] = {
ok: true,
output: JSON.stringify({
items: [{ metadata: { name: 'alga-core' }, status: { conditions: readyCondition('False', 'context deadline exceeded') } }],
}),
};
responses['kubectl --kubeconfig /tmp/kubeconfig -n msp get statefulset/db -o json'] = {
ok: true,
output: JSON.stringify({ spec: { replicas: 1 }, status: { readyReplicas: 0 } }),
};
responses['kubectl --kubeconfig /tmp/kubeconfig get events --sort-by=.metadata.creationTimestamp -A -o json'] = {
ok: true,
output: JSON.stringify({
items: [
{
metadata: { namespace: 'msp', creationTimestamp: '2026-04-30T00:00:00Z' },
reason: 'Failed',
type: 'Warning',
message: 'failed to create subPath directory for volumeMount "db-data"',
involvedObject: { kind: 'Pod', name: 'db-0' },
},
],
}),
};
const status = await collectStatus(buildEnv(), {
runner: new MockCaptureRunner(responses),
});
assert.equal(status.flux.helmStatus, 'unhealthy');
assert.equal(status.topBlocker.layer, 'Core Postgres storage initialization');
assert.match(status.topBlocker.reason, /subPath/i);
});
test('T010: bootstrap job completion plus seeded users query sets BOOTSTRAP_READY true', async () => {
const responses = healthyResponses();
responses['kubectl --kubeconfig /tmp/kubeconfig -n alga-system get helmreleases.helm.toolkit.fluxcd.io -o json'] = {
ok: true,
output: JSON.stringify({
items: [{ metadata: { name: 'alga-core' }, status: { conditions: readyCondition('False', 'still reconciling') } }],
}),
};
responses['kubectl --kubeconfig /tmp/kubeconfig -n msp get jobs.batch -o json'] = {
ok: true,
output: JSON.stringify({
items: [
{
metadata: { name: 'alga-core-bootstrap' },
status: { conditions: [{ type: 'Complete', status: 'True' }], succeeded: 1 },
},
],
}),
};
responses[
"kubectl --kubeconfig /tmp/kubeconfig -n msp exec db-0 -- sh -c PGPASSWORD=$POSTGRES_PASSWORD psql -U postgres -d server -tAc 'select count(*) from users;'"
] = {
ok: true,
output: '7\n',
};
const status = await collectStatus(buildEnv(), {
runner: new MockCaptureRunner(responses),
});
assert.equal(status.bootstrap.job.completed, true);
assert.equal(status.bootstrap.seed.usersCount, 7);
assert.equal(status.canonical.tiers.bootstrap.ready, true);
});
test('bootstrap job log error is promoted into canonical blocker and rollup', async () => {
const responses = healthyResponses();
responses['kubectl --kubeconfig /tmp/kubeconfig -n msp get jobs.batch -o json'] = {
ok: true,
output: JSON.stringify({
items: [
{
metadata: { name: 'alga-core-sebastian-bootstrap-r7' },
status: { conditions: [{ type: 'Failed', status: 'True' }], failed: 1 },
},
],
}),
};
responses['kubectl --kubeconfig /tmp/kubeconfig -n msp get pods -l job-name=alga-core-sebastian-bootstrap-r7 -o json'] = {
ok: true,
output: JSON.stringify({
items: [
{
metadata: { name: 'alga-core-sebastian-bootstrap-r7-abc', creationTimestamp: '2026-04-30T00:01:00Z' },
spec: { containers: [{ name: 'bootstrap' }] },
status: { phase: 'Failed' },
},
],
}),
};
responses['kubectl --kubeconfig /tmp/kubeconfig -n msp logs alga-core-sebastian-bootstrap-r7-abc -c bootstrap --tail=160 --timestamps'] = {
ok: true,
output: [
'2026-04-30T00:01:00Z Running migrations',
'2026-04-30T00:01:01Z ERROR: Configured seed directory does not exist: /app/ee/server/seeds/onboarding',
].join('\n'),
};
const status = await collectStatus(buildEnv(), {
runner: new MockCaptureRunner(responses),
});
assert.equal(status.topBlocker.layer, 'Bootstrap job failure');
assert.match(status.topBlocker.reason, /Configured seed directory does not exist/);
assert.match(status.topBlocker.nextAction, /seed directory/);
assert.equal(status.canonical.topBlockers[0].layer, 'Bootstrap job failure');
assert.match(status.canonical.rollup.message, /Configured seed directory does not exist/);
assert.equal(status.canonical.bootstrap.logs.available, true);
assert.equal(status.canonical.bootstrap.logs.detectedErrors.length, 1);
});
test('T011: Temporal schema compatibility errors are classified with autosetup guidance', async () => {
const responses = healthyResponses();
responses['kubectl --kubeconfig /tmp/kubeconfig -n msp get deployment/temporal -o json'] = {
ok: true,
output: JSON.stringify({ spec: { replicas: 1 }, status: { readyReplicas: 0 } }),
};
responses['kubectl --kubeconfig /tmp/kubeconfig get events --sort-by=.metadata.creationTimestamp -A -o json'] = {
ok: true,
output: JSON.stringify({
items: [
{
metadata: { namespace: 'msp', creationTimestamp: '2026-04-30T00:00:00Z' },
reason: 'BackOff',
type: 'Warning',
message: 'sql schema version compatibility check failed',
involvedObject: { kind: 'Pod', name: 'temporal-abcde' },
},
],
}),
};
const status = await collectStatus(buildEnv(), {
runner: new MockCaptureRunner(responses),
});
assert.equal(status.topBlocker.layer, 'Temporal schema initialization');
assert.match(status.topBlocker.reason, /schema not initialized/i);
assert.match(status.topBlocker.nextAction, /autosetup/i);
});
test('T012: Temporal UI service-link collision errors are classified with disable-service-links guidance', async () => {
const responses = healthyResponses();
responses['kubectl --kubeconfig /tmp/kubeconfig -n msp get deployment/temporal -o json'] = {
ok: true,
output: JSON.stringify({ spec: { replicas: 1 }, status: { readyReplicas: 0 } }),
};
responses['kubectl --kubeconfig /tmp/kubeconfig get events --sort-by=.metadata.creationTimestamp -A -o json'] = {
ok: true,
output: JSON.stringify({
items: [
{
metadata: { namespace: 'msp', creationTimestamp: '2026-04-30T00:00:00Z' },
reason: 'BackOff',
type: 'Warning',
message: 'cannot unmarshal !!str tcp://10.96.0.1:443 into int',
involvedObject: { kind: 'Pod', name: 'temporal-ui-abcde' },
},
],
}),
};
const status = await collectStatus(buildEnv(), {
runner: new MockCaptureRunner(responses),
});
assert.equal(status.topBlocker.layer, 'Kubernetes service-link environment collision');
assert.match(status.topBlocker.reason, /service-link environment collision/i);
assert.match(status.topBlocker.nextAction, /Disable service links/i);
});