mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
fa03d9b913
* refactor: consolidate coercion helpers * fix: remove duplicate coercion imports * fix: preserve serialized coercion guard * chore: ratchet coercion helper carve-outs * fix(test): keep gauntlet subprocess startup lean * fix: preserve imported session timestamp semantics * fix: preserve catalog timestamp string semantics * chore: align plugin SDK surface ratchet * fix: preserve trajectory and SDK string contracts * fix(test): preserve QA record assertion semantics * fix: complete standalone record guard rename * refactor(cron): use canonical string coercion * fix(acpx): preserve Pi timestamp parsing * test(channels): adapt custody test harnesses * test(telegram): classify media harness as test support * test(acpx): split timestamp contract coverage * test(channels): support generated custody contracts * chore: ban the full coercion helper name set Extends the declaration guard to all eleven consolidated helper names and renames the cron schedule-identity readNumber wrapper to readScheduleInteger so the banned generic name cannot regrow. * fix(scripts): repair release-validation guard drift and lint cause Restores the renamed isJsonRecord guard in assertTrustedWorkflowHarness after main added isRecord call sites in parallel, and attaches the caught YAML error as the thrown error cause (preserve-caught-error was red on main). * fix: preserve Claude timestamp string semantics * fix: preserve persisted timestamp string semantics * fix: preserve date-first timestamp contracts * fix(openai): harden delegation failure formatting * chore: close coercion helper guard gaps * test(openai): model non-error delegation rejection * chore: refresh plugin SDK API contract * fix(tasks): use canonical string field reader * fix(ai): use canonical provider error field coercion * fix(browser): migrate native bootstrap coercion * docs(plugin-sdk): clarify text record export compatibility * fix(gateway): normalize approval execution identity * test(outbound): isolate message action poll harness
145 lines
6.1 KiB
TypeScript
145 lines
6.1 KiB
TypeScript
// K8s manifest tests cover the deployable Kubernetes bundle shape.
|
|
import { readFileSync } from "node:fs";
|
|
import { describe, expect, it } from "vitest";
|
|
import { parse } from "yaml";
|
|
|
|
type Manifest = Record<string, unknown>;
|
|
|
|
function readManifest(name: string): Manifest {
|
|
const parsed = parse(readFileSync(`scripts/k8s/manifests/${name}`, "utf8")) as unknown;
|
|
expect(parsed).toBeTypeOf("object");
|
|
expect(parsed).not.toBeNull();
|
|
expect(Array.isArray(parsed)).toBe(false);
|
|
return parsed as Manifest;
|
|
}
|
|
|
|
function assertRecord(value: unknown, label: string): Record<string, unknown> {
|
|
expect(value, label).toBeTypeOf("object");
|
|
expect(value, label).not.toBeNull();
|
|
expect(Array.isArray(value), label).toBe(false);
|
|
return value as Record<string, unknown>;
|
|
}
|
|
|
|
function asRecords(value: unknown, label: string): Record<string, unknown>[] {
|
|
expect(Array.isArray(value), label).toBe(true);
|
|
return value as Record<string, unknown>[];
|
|
}
|
|
|
|
function asStrings(value: unknown, label: string): string[] {
|
|
expect(Array.isArray(value), label).toBe(true);
|
|
for (const entry of value as unknown[]) {
|
|
expect(entry, label).toBeTypeOf("string");
|
|
}
|
|
return value as string[];
|
|
}
|
|
|
|
function findNamed(records: Record<string, unknown>[], name: string): Record<string, unknown> {
|
|
const record = records.find((entry) => entry.name === name);
|
|
expect(record, name).toBeDefined();
|
|
return record as Record<string, unknown>;
|
|
}
|
|
|
|
describe("k8s manifests", () => {
|
|
it("keeps kustomization resources aligned with shipped manifests", () => {
|
|
const kustomization = readManifest("kustomization.yaml");
|
|
|
|
expect(kustomization).toMatchObject({
|
|
apiVersion: "kustomize.config.k8s.io/v1beta1",
|
|
kind: "Kustomization",
|
|
});
|
|
expect(asStrings(kustomization.resources, "kustomization resources").toSorted()).toEqual([
|
|
"configmap.yaml",
|
|
"deployment.yaml",
|
|
"pvc.yaml",
|
|
"service.yaml",
|
|
]);
|
|
});
|
|
|
|
it("keeps gateway service selectors and ports aligned with deployment labels", () => {
|
|
const deployment = readManifest("deployment.yaml");
|
|
const service = readManifest("service.yaml");
|
|
const deploymentSpec = assertRecord(deployment.spec, "deployment spec");
|
|
const selector = assertRecord(deploymentSpec.selector, "deployment selector");
|
|
const matchLabels = assertRecord(selector.matchLabels, "deployment match labels");
|
|
const template = assertRecord(deploymentSpec.template, "deployment template");
|
|
const templateMetadata = assertRecord(template.metadata, "deployment template metadata");
|
|
const templateLabels = assertRecord(templateMetadata.labels, "deployment template labels");
|
|
const serviceSpec = assertRecord(service.spec, "service spec");
|
|
const serviceSelector = assertRecord(serviceSpec.selector, "service selector");
|
|
const ports = asRecords(serviceSpec.ports, "service ports");
|
|
|
|
expect(deployment).toMatchObject({
|
|
apiVersion: "apps/v1",
|
|
kind: "Deployment",
|
|
metadata: { name: "openclaw" },
|
|
});
|
|
expect(matchLabels).toEqual({ app: "openclaw" });
|
|
expect(templateLabels).toMatchObject(matchLabels);
|
|
expect(serviceSelector).toEqual(matchLabels);
|
|
expect(ports).toContainEqual({
|
|
name: "gateway",
|
|
port: 18789,
|
|
protocol: "TCP",
|
|
targetPort: 18789,
|
|
});
|
|
});
|
|
|
|
it("keeps deployment mounts, secrets, and security posture deployable", () => {
|
|
const deployment = readManifest("deployment.yaml");
|
|
const spec = assertRecord(deployment.spec, "deployment spec");
|
|
const template = assertRecord(spec.template, "deployment template");
|
|
const podSpec = assertRecord(template.spec, "pod spec");
|
|
const containers = asRecords(podSpec.containers, "containers");
|
|
const gateway = findNamed(containers, "gateway");
|
|
const env = asRecords(gateway.env, "gateway env");
|
|
const volumes = asRecords(podSpec.volumes, "pod volumes");
|
|
const securityContext = assertRecord(gateway.securityContext, "gateway security context");
|
|
|
|
expect(gateway.command).toEqual(["node", "/app/dist/index.js", "gateway", "run"]);
|
|
expect(findNamed(env, "HOME")).toMatchObject({ value: "/home/node" });
|
|
expect(findNamed(env, "OPENCLAW_CONFIG_DIR")).toMatchObject({ value: "/home/node/.openclaw" });
|
|
expect(findNamed(env, "OPENCLAW_GATEWAY_TOKEN")).toMatchObject({
|
|
valueFrom: { secretKeyRef: { key: "OPENCLAW_GATEWAY_TOKEN", name: "openclaw-secrets" } },
|
|
});
|
|
expect(findNamed(volumes, "openclaw-home")).toMatchObject({
|
|
persistentVolumeClaim: { claimName: "openclaw-home-pvc" },
|
|
});
|
|
expect(findNamed(volumes, "config")).toMatchObject({ configMap: { name: "openclaw-config" } });
|
|
expect(securityContext).toMatchObject({
|
|
allowPrivilegeEscalation: false,
|
|
readOnlyRootFilesystem: true,
|
|
runAsNonRoot: true,
|
|
});
|
|
});
|
|
|
|
it("keeps config and persistence manifests aligned with the gateway", () => {
|
|
const configMap = readManifest("configmap.yaml");
|
|
const pvc = readManifest("pvc.yaml");
|
|
const data = assertRecord(configMap.data, "configmap data");
|
|
const config = JSON.parse(String(data["openclaw.json"])) as Record<string, unknown>;
|
|
const gateway = assertRecord(config.gateway, "openclaw config gateway");
|
|
const auth = assertRecord(gateway.auth, "openclaw config auth");
|
|
const agents = assertRecord(config.agents, "openclaw config agents");
|
|
const defaults = assertRecord(agents.defaults, "openclaw config agent defaults");
|
|
const pvcSpec = assertRecord(pvc.spec, "pvc spec");
|
|
const resources = assertRecord(pvcSpec.resources, "pvc resources");
|
|
const requests = assertRecord(resources.requests, "pvc resource requests");
|
|
|
|
expect(configMap).toMatchObject({
|
|
apiVersion: "v1",
|
|
kind: "ConfigMap",
|
|
metadata: { name: "openclaw-config" },
|
|
});
|
|
expect(gateway).toMatchObject({ mode: "local", port: 18789 });
|
|
expect(auth).toMatchObject({ mode: "token" });
|
|
expect(defaults).toMatchObject({ workspace: "~/.openclaw/workspace" });
|
|
expect(data["AGENTS.md"]).toContain("OpenClaw Assistant");
|
|
expect(pvc).toMatchObject({
|
|
apiVersion: "v1",
|
|
kind: "PersistentVolumeClaim",
|
|
metadata: { name: "openclaw-home-pvc" },
|
|
});
|
|
expect(requests).toMatchObject({ storage: "10Gi" });
|
|
});
|
|
});
|