Files
pikasTech-HWLAB/scripts/g14-ci-plan.test.mjs
T

151 lines
6.6 KiB
JavaScript

import assert from "node:assert/strict";
import { execFile } from "node:child_process";
import { mkdir, mkdtemp, writeFile } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import test from "node:test";
import { promisify } from "node:util";
import {
classifyGlobalChange,
componentModelsForServices,
createG14CiPlan,
matchingPaths
} from "./src/g14-ci-plan-lib.mjs";
const execFileAsync = promisify(execFile);
test("G14 CI planner maps bridge changes to cloud-api only", async () => {
const repo = await createFixtureRepo();
await writeFile(path.join(repo, "cmd/hwlab-deepseek-responses-bridge/main.mjs"), "console.log('bridge v2');\n");
await git(repo, ["add", "."]);
await git(repo, ["commit", "-m", "change bridge"]);
const plan = await createG14CiPlan({ repoRoot: repo, baseRef: "HEAD~1", targetRef: "HEAD" });
assert.deepEqual(plan.affectedServices, ["hwlab-cloud-api"]);
assert.equal(plan.reusedServices.includes("hwlab-cloud-web"), true);
const cloudApi = plan.services.find((service) => service.serviceId === "hwlab-cloud-api");
assert.equal(cloudApi.affected, true);
assert.deepEqual(cloudApi.reason, ["component-path-changed"]);
});
test("G14 CI planner treats docs-only changes as no image build", async () => {
const repo = await createFixtureRepo();
await mkdir(path.join(repo, "docs/reference"), { recursive: true });
await writeFile(path.join(repo, "docs/reference/g14-gitops-cicd.md"), "docs v2\n");
await git(repo, ["add", "."]);
await git(repo, ["commit", "-m", "change docs"]);
const plan = await createG14CiPlan({ repoRoot: repo, baseRef: "HEAD~1", targetRef: "HEAD" });
assert.deepEqual(plan.affectedServices, []);
assert.equal(plan.imageBuildRequired, false);
assert.equal(plan.changedPathSummary.docsOnly, true);
});
test("planner rebuilds when catalog digest is missing instead of blocking reuse", async () => {
const repo = await createFixtureRepo({ catalog: "missing-digest" });
await mkdir(path.join(repo, "docs/reference"), { recursive: true });
await writeFile(path.join(repo, "docs/reference/g14-gitops-cicd.md"), "docs v2\n");
await git(repo, ["add", "."]);
await git(repo, ["commit", "-m", "change docs"]);
const plan = await createG14CiPlan({ repoRoot: repo, baseRef: "HEAD~1", targetRef: "HEAD" });
assert.deepEqual(plan.affectedServices, ["hwlab-cloud-api", "hwlab-cloud-web"]);
assert.equal(plan.imageBuildRequired, true);
assert.equal(plan.services[0].reason.includes("catalog-digest-missing"), true);
});
test("component model uses built-in service paths", () => {
const models = componentModelsForServices(["hwlab-cloud-api"]);
assert.deepEqual(models[0].componentPaths, [
"cmd/hwlab-cloud-api/",
"cmd/hwlab-codex-api-responses-forwarder/",
"cmd/hwlab-deepseek-responses-bridge/",
"internal/audit/",
"internal/cloud/",
"internal/db/",
"skills/",
"tools/"
]);
assert.deepEqual(matchingPaths(["cmd/hwlab-cloud-api/main.ts"], models[0].componentPaths), ["cmd/hwlab-cloud-api/main.ts"]);
});
test("planner remains compatible with deploy.k3s.serviceMappings shape", async () => {
const repo = await createFixtureRepo({ deployServices: false, k3sServiceMappings: true });
await writeFile(path.join(repo, "cmd/hwlab-deepseek-responses-bridge/main.mjs"), "console.log('bridge v2');\n");
await git(repo, ["add", "."]);
await git(repo, ["commit", "-m", "change bridge"]);
const plan = await createG14CiPlan({ repoRoot: repo, baseRef: "HEAD~1", targetRef: "HEAD" });
assert.equal(plan.compatibility.currentRenderCompatible, true);
assert.equal(plan.compatibility.serviceIdSource, "deploy.k3s.serviceMappings");
assert.deepEqual(plan.affectedServices, ["hwlab-cloud-api"]);
assert.equal(plan.inputs.serviceCount, 2);
});
test("global change classifier distinguishes gitops-only and test-only", () => {
assert.equal(classifyGlobalChange(["deploy/gitops/g14/runtime-dev/workloads.yaml"]).gitopsOnly, true);
assert.equal(classifyGlobalChange(["cmd/hwlab-cloud-api/main.test.mjs"]).testOnly, true);
});
async function createFixtureRepo(options = {}) {
const deployServices = options.deployServices !== false;
const k3sServiceMappings = options.k3sServiceMappings === true;
const repo = await mkdtemp(path.join(os.tmpdir(), "hwlab-g14-ci-plan-"));
await mkdir(path.join(repo, "cmd/hwlab-cloud-api"), { recursive: true });
await mkdir(path.join(repo, "cmd/hwlab-deepseek-responses-bridge"), { recursive: true });
await mkdir(path.join(repo, "web/hwlab-cloud-web"), { recursive: true });
await mkdir(path.join(repo, "deploy"), { recursive: true });
await writeFile(path.join(repo, "deploy/deploy.json"), JSON.stringify(createDeployFixture({ deployServices, k3sServiceMappings }), null, 2));
const catalogMode = options.catalog ?? "ready";
if (catalogMode !== false) {
await writeFile(path.join(repo, "deploy/artifact-catalog.dev.json"), JSON.stringify(createCatalogFixture(catalogMode), null, 2));
}
await writeFile(path.join(repo, "package.json"), JSON.stringify({ type: "module" }, null, 2));
await writeFile(path.join(repo, "package-lock.json"), JSON.stringify({ lockfileVersion: 3 }, null, 2));
await writeFile(path.join(repo, "cmd/hwlab-cloud-api/main.ts"), "console.log('api');\n");
await writeFile(path.join(repo, "cmd/hwlab-deepseek-responses-bridge/main.mjs"), "console.log('bridge');\n");
await writeFile(path.join(repo, "cmd/hwlab-deepseek-responses-bridge/main.test.mjs"), "console.log('test');\n");
await writeFile(path.join(repo, "web/hwlab-cloud-web/index.html"), "<html></html>\n");
await git(repo, ["init"]);
await git(repo, ["add", "."]);
await git(repo, ["commit", "-m", "initial"]);
return repo;
}
function createDeployFixture({ deployServices, k3sServiceMappings }) {
const deploy = {};
if (deployServices) {
deploy.services = [
{ serviceId: "hwlab-cloud-api" },
{ serviceId: "hwlab-cloud-web" }
];
}
if (k3sServiceMappings) {
deploy.k3s = {
serviceMappings: [
{ serviceId: "hwlab-cloud-api", name: "hwlab-cloud-api" },
{ serviceId: "hwlab-cloud-web", name: "hwlab-cloud-web" }
]
};
}
return deploy;
}
function createCatalogFixture(mode) {
const digest = mode === "ready" ? `sha256:${"1".repeat(64)}` : "not_published";
return {
services: ["hwlab-cloud-api", "hwlab-cloud-web"].map((serviceId) => ({
serviceId,
commitId: "abc1234",
image: `127.0.0.1:5000/hwlab/${serviceId}:abc1234`,
imageTag: "abc1234",
digest
}))
};
}
async function git(cwd, args) {
return execFileAsync("git", ["-c", "user.name=HWLAB Test", "-c", "user.email=hwlab-test@example.invalid", ...args], { cwd });
}