249 lines
7.7 KiB
JavaScript
249 lines
7.7 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import {
|
|
commitsMatch,
|
|
evaluateArtifactRuntimeReadiness,
|
|
normalizeCommit
|
|
} from "./src/artifact-runtime-readiness-guard.mjs";
|
|
|
|
const targetSha = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
|
|
const staleSha = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
|
|
const digest = "sha256:1111111111111111111111111111111111111111111111111111111111111111";
|
|
|
|
function artifactReport(sourceCommitId = targetSha, overrides = {}) {
|
|
return {
|
|
commitId: sourceCommitId.slice(0, 7),
|
|
artifactPublish: {
|
|
status: "published",
|
|
sourceCommitId,
|
|
serviceCount: 2,
|
|
requiredServiceCount: 2,
|
|
publishedCount: 2,
|
|
registryPrefix: "127.0.0.1:5000/hwlab",
|
|
services: [
|
|
{
|
|
serviceId: "hwlab-cloud-api",
|
|
artifactRequired: true,
|
|
status: "published",
|
|
imageTag: sourceCommitId.slice(0, 7),
|
|
digest
|
|
},
|
|
{
|
|
serviceId: "hwlab-cloud-web",
|
|
artifactRequired: true,
|
|
status: "published",
|
|
imageTag: sourceCommitId.slice(0, 7),
|
|
digest,
|
|
runtimeKind: "cloud-web",
|
|
distFreshness: { status: "pass" }
|
|
}
|
|
],
|
|
...overrides
|
|
}
|
|
};
|
|
}
|
|
|
|
function artifactCatalog(commitId = targetSha, {
|
|
ciPublished = true,
|
|
registryVerified = true,
|
|
serviceDigest = digest,
|
|
artifactState = "published"
|
|
} = {}) {
|
|
return {
|
|
artifactState,
|
|
commitId,
|
|
publish: {
|
|
ciPublished,
|
|
registryVerified
|
|
},
|
|
services: [
|
|
{
|
|
serviceId: "hwlab-cloud-api",
|
|
commitId,
|
|
artifactRequired: true,
|
|
publishState: serviceDigest === "not_published" ? "skeleton-only" : "published",
|
|
imageTag: commitId.slice(0, 7),
|
|
digest: serviceDigest
|
|
},
|
|
{
|
|
serviceId: "hwlab-cloud-web",
|
|
commitId,
|
|
artifactRequired: true,
|
|
publishState: serviceDigest === "not_published" ? "skeleton-only" : "published",
|
|
imageTag: commitId.slice(0, 7),
|
|
digest: serviceDigest
|
|
}
|
|
]
|
|
};
|
|
}
|
|
|
|
function desiredStatePlan(commitId = targetSha, state = "already_promoted", status = "pass") {
|
|
return {
|
|
status,
|
|
summary: {
|
|
desiredCommitId: commitId,
|
|
desiredImageTag: commitId.slice(0, 7),
|
|
diagnostics: status === "blocked" ? 1 : 0,
|
|
blockers: status === "blocked" ? 1 : 0
|
|
},
|
|
target: {
|
|
convergence: {
|
|
state,
|
|
comparableFields: 8,
|
|
matchingTargetFields: state === "already_promoted" ? 8 : 0,
|
|
pendingTargetFields: state === "already_promoted" ? 0 : 8
|
|
}
|
|
},
|
|
services: [
|
|
{
|
|
serviceId: "hwlab-cloud-web",
|
|
catalog: { commitId }
|
|
}
|
|
]
|
|
};
|
|
}
|
|
|
|
function liveRuntime(commitId = targetSha, { cloudWebCommitId = commitId, apiCommitId = commitId } = {}) {
|
|
return {
|
|
mode: "live-read-only-http",
|
|
api: {
|
|
status: "observed",
|
|
source: "health-live",
|
|
evidenceSource: "live-http",
|
|
endpoint: "http://74.48.78.17:16667/health/live",
|
|
serviceId: "hwlab-cloud-api",
|
|
environment: "dev",
|
|
healthStatus: "ok",
|
|
commitId: apiCommitId,
|
|
shortCommitId: apiCommitId.slice(0, 7),
|
|
commitSource: "runtime-env",
|
|
imageTag: apiCommitId.slice(0, 7),
|
|
observedAt: "2026-05-22T00:00:00.000Z"
|
|
},
|
|
cloudWeb: {
|
|
status: "observed",
|
|
source: "health-live",
|
|
evidenceSource: "live-http",
|
|
endpoint: "http://74.48.78.17:16666/health/live",
|
|
serviceId: "hwlab-cloud-web",
|
|
environment: "dev",
|
|
healthStatus: "ok",
|
|
artifactKind: "cloud-web",
|
|
commitId: cloudWebCommitId,
|
|
shortCommitId: cloudWebCommitId.slice(0, 7),
|
|
commitSource: "runtime-env",
|
|
imageTag: cloudWebCommitId.slice(0, 7),
|
|
observedAt: "2026-05-22T00:00:00.000Z"
|
|
}
|
|
};
|
|
}
|
|
|
|
function readiness(overrides = {}) {
|
|
return evaluateArtifactRuntimeReadiness({
|
|
artifactReport: artifactReport(overrides.artifactSource ?? targetSha, overrides.artifactReportOverrides),
|
|
artifactCatalog: artifactCatalog(overrides.catalogCommit ?? targetSha, overrides.catalogOptions ?? {}),
|
|
desiredStatePlan: overrides.desiredPlan ?? desiredStatePlan(overrides.desiredCommit ?? targetSha, overrides.desiredState ?? "already_promoted", overrides.desiredStatus ?? "pass"),
|
|
runtimeReport: null,
|
|
liveRuntime: overrides.live ?? liveRuntime(targetSha),
|
|
targetRef: "origin/main",
|
|
targetCommitId: overrides.targetCommit ?? targetSha
|
|
});
|
|
}
|
|
|
|
test("commit comparison accepts full and short lowercase SHA forms only", () => {
|
|
assert.equal(normalizeCommit("ABCDEF1"), "abcdef1");
|
|
assert.equal(normalizeCommit("not-a-sha"), "unknown");
|
|
assert.equal(commitsMatch(targetSha, targetSha.slice(0, 7)), true);
|
|
assert.equal(commitsMatch(targetSha, "unknown"), false);
|
|
});
|
|
|
|
test("readiness passes only when artifact, desired-state, API runtime, and Cloud Web runtime match target", () => {
|
|
const result = readiness();
|
|
assert.equal(result.ready, true);
|
|
assert.equal(result.canInferFromSourceLocalStatic, false);
|
|
assert.deepEqual(result.blockers, []);
|
|
});
|
|
|
|
test("stale local artifact report cannot imply latest-main readiness", () => {
|
|
const result = readiness({ targetCommit: staleSha });
|
|
assert.equal(result.ready, false);
|
|
assert.equal(result.canInferFromSourceLocalStatic, false);
|
|
assert.deepEqual(
|
|
result.blockers.map((item) => item.id),
|
|
[
|
|
"artifact-report-target-match",
|
|
"artifact-catalog-target-match",
|
|
"desired-state-target-match",
|
|
"api-runtime-target-match",
|
|
"cloud-web-runtime-target-match"
|
|
]
|
|
);
|
|
});
|
|
|
|
test("desired-state promotion pending blocks before apply", () => {
|
|
const result = readiness({
|
|
desiredCommit: staleSha,
|
|
desiredState: "promotion_pending",
|
|
desiredStatus: "planned"
|
|
});
|
|
assert.equal(result.ready, false);
|
|
assert.equal(result.desiredState.targetConvergence.state, "promotion_pending");
|
|
assert.ok(result.blockers.some((item) => item.id === "desired-state-target-match"));
|
|
});
|
|
|
|
test("unpublished catalog digests keep readiness blocked", () => {
|
|
const result = readiness({
|
|
catalogOptions: {
|
|
artifactState: "contract-skeleton",
|
|
ciPublished: false,
|
|
registryVerified: false,
|
|
serviceDigest: "not_published"
|
|
}
|
|
});
|
|
assert.equal(result.ready, false);
|
|
assert.equal(result.catalog.ciPublished, false);
|
|
assert.equal(result.catalog.registryVerified, false);
|
|
assert.deepEqual(
|
|
result.blockers.map((item) => item.id),
|
|
["artifact-catalog-published"]
|
|
);
|
|
});
|
|
|
|
test("Cloud Web served runtime identity must match artifact and target", () => {
|
|
const result = readiness({
|
|
live: liveRuntime(targetSha, { cloudWebCommitId: staleSha })
|
|
});
|
|
assert.equal(result.ready, false);
|
|
assert.ok(result.blockers.some((item) => item.id === "cloud-web-runtime-target-match"));
|
|
assert.ok(result.blockers.some((item) => item.id === "cloud-web-runtime-artifact-match"));
|
|
assert.equal(result.runtime.cloudWeb.effective.shortCommitId, staleSha.slice(0, 7));
|
|
});
|
|
|
|
test("Cloud Web publish report must retain build freshness evidence", () => {
|
|
const result = readiness({
|
|
artifactReportOverrides: {
|
|
services: [
|
|
{
|
|
serviceId: "hwlab-cloud-api",
|
|
artifactRequired: true,
|
|
status: "published",
|
|
imageTag: targetSha.slice(0, 7),
|
|
digest
|
|
},
|
|
{
|
|
serviceId: "hwlab-cloud-web",
|
|
artifactRequired: true,
|
|
status: "published",
|
|
imageTag: targetSha.slice(0, 7),
|
|
digest,
|
|
runtimeKind: "cloud-web"
|
|
}
|
|
]
|
|
}
|
|
});
|
|
assert.equal(result.ready, false);
|
|
assert.equal(result.artifact.cloudWeb.distFreshnessStatus, "missing");
|
|
assert.ok(result.blockers.some((item) => item.id === "cloud-web-published-build-freshness"));
|
|
});
|