Files
wehub-resource-sync 426e9eeabd
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
ci / test (push) Has been cancelled
ci / lint-and-format (push) Has been cancelled
ci / build (push) Has been cancelled
ci / dev-startup (push) Has been cancelled
gitleaks / gitleaks (push) Has been cancelled
Markdown Links / Relative Markdown Links (push) Has been cancelled
Quality (Extended) / Homepage Build (PR smoke) (push) Has been cancelled
Quality (Extended) / Comment-only diff guard (push) Has been cancelled
Quality (Extended) / Format + Type Safety Ratchet (push) Has been cancelled
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Has been cancelled
Quality (Extended) / Develop Gate (lint) (push) Has been cancelled
Chat shell gestures / Chat shell gesture + parity e2e (push) Has been cancelled
Cloud Gateway Discord / Test (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Has been cancelled
Build Agent Image / build-and-push (push) Has been cancelled
Dev Smoke / bun run dev onboarding chat (push) Has been cancelled
Dev Smoke / Vite HMR dependency-level smoke (push) Has been cancelled
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Has been cancelled
Publish @elizaos/example-code / check_npm (push) Has been cancelled
Publish @elizaos/example-code / publish_npm (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / verify_version (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / publish_npm (push) Has been cancelled
Sandbox Live Smoke / Sandbox live smoke (push) Has been cancelled
Snap Build & Test / Build Snap (amd64) (push) Has been cancelled
Snap Build & Test / Build Snap (arm64) (push) Has been cancelled
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Has been cancelled
Cloud Gateway Webhook / Test (push) Has been cancelled
Cloud Tests / lint-and-types (push) Has been cancelled
Cloud Tests / unit-tests (push) Has been cancelled
Cloud Tests / integration-tests (push) Has been cancelled
Cloud Tests / e2e-tests (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Apps Worker (Product 2) / Determine environment (push) Has been cancelled
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Has been cancelled
Deploy Eliza Provisioning Worker / Determine environment (push) Has been cancelled
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Has been cancelled
Dev Smoke / Classify changed paths (push) Has been cancelled
supply-chain / sbom (push) Has been cancelled
supply-chain / vulnerability-scan (push) Has been cancelled
Build, Push & Deploy to Phala Cloud / build-and-push (push) Has been cancelled
Test Packaging / Validate Packaging Configs (push) Has been cancelled
Test Packaging / Build & Test PyPI Package (push) Has been cancelled
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Has been cancelled
Test Packaging / Pack & Test JS Tarballs (push) Has been cancelled
UI Fixture E2E / ui-fixture-e2e (push) Has been cancelled
UI Fixture E2E / fixture-e2e (push) Has been cancelled
UI Story Gate / story-gate (push) Has been cancelled
vault-ci / test (macos-latest) (push) Has been cancelled
vault-ci / test (ubuntu-latest) (push) Has been cancelled
vault-ci / test (windows-latest) (push) Has been cancelled
vault-ci / app-core wiring tests (push) Has been cancelled
verify-patches / verify patches/CHECKSUMS.sha256 (push) Has been cancelled
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Has been cancelled
Voice Benchmark Smoke / voice bench smoke summary (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Has been cancelled
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:43:05 +08:00

236 lines
6.5 KiB
TypeScript

/**
* Deterministic coverage for docker-node onboarding helpers. The tests exercise
* argument parsing, container selection, and host-key pin preservation without
* opening SSH connections or touching a real control-plane database.
*/
import { describe, expect, it } from "bun:test";
import {
buildOnboardSshConfig,
capacityForOnboardUpsert,
hostKeyFingerprintForOnboardUpsert,
parseArgs,
parseDockerPs,
selectZombieAgentContainers,
} from "./onboard-docker-node";
describe("parseDockerPs", () => {
it("parses name/state pairs and drops blank + stderr lines", () => {
const out = [
"agent-abc\texited",
"",
"cloud-container-xyz\trunning",
"[stderr] some warning",
" unrelated-svc\tcreated ",
].join("\n");
expect(parseDockerPs(out)).toEqual([
{ name: "agent-abc", state: "exited" },
{ name: "cloud-container-xyz", state: "running" },
{ name: "unrelated-svc", state: "created" },
]);
});
it("returns empty for empty output", () => {
expect(parseDockerPs("")).toEqual([]);
});
});
describe("selectZombieAgentContainers", () => {
it("selects exited/created/dead agent containers (both naming schemes)", () => {
const rows = [
{ name: "agent-1", state: "exited" },
{ name: "cloud-container-2", state: "created" },
{ name: "agent-3", state: "dead" },
];
expect(selectZombieAgentContainers(rows)).toEqual([
"agent-1",
"cloud-container-2",
"agent-3",
]);
});
it("never selects a running/restarting/paused agent container (active sandbox safe)", () => {
const rows = [
{ name: "agent-live", state: "running" },
{ name: "cloud-container-live", state: "restarting" },
{ name: "agent-paused", state: "paused" },
];
expect(selectZombieAgentContainers(rows)).toEqual([]);
});
it("ignores non-agent containers even when exited", () => {
const rows = [
{ name: "caddy", state: "exited" },
{ name: "postgres", state: "dead" },
{ name: "my-agent-helper", state: "exited" }, // does not START with a prefix
];
expect(selectZombieAgentContainers(rows)).toEqual([]);
});
});
describe("parseArgs", () => {
const emptyEnv = {} as NodeJS.ProcessEnv;
it("parses flags with defaults", () => {
const args = parseArgs(
["--host", "1.2.3.4", "--node-id", "robot-1", "--key", "/k/id"],
emptyEnv,
);
expect(args).toMatchObject({
host: "1.2.3.4",
nodeId: "robot-1",
keyPath: "/k/id",
sshPort: 22,
sshUser: "root",
capacity: 8,
dryRun: false,
});
});
it("honors --dry-run and explicit overrides", () => {
const args = parseArgs(
[
"--host",
"h",
"--node-id",
"n",
"--ssh-port",
"2222",
"--ssh-user",
"ops",
"--capacity",
"4",
"--dry-run",
],
emptyEnv,
);
expect(args).toMatchObject({
sshPort: 2222,
sshUser: "ops",
capacity: 4,
dryRun: true,
});
});
it("falls back to env vars when flags are absent", () => {
const env = {
ONBOARD_NODE_HOST: "5.6.7.8",
ONBOARD_NODE_ID: "env-node",
ONBOARD_NODE_CAPACITY: "16",
} as NodeJS.ProcessEnv;
const args = parseArgs([], env);
expect(args).toMatchObject({
host: "5.6.7.8",
nodeId: "env-node",
capacity: 16,
});
});
it("throws when host or node-id is missing", () => {
expect(() => parseArgs(["--host", "h"], emptyEnv)).toThrow("node-id");
expect(() => parseArgs(["--node-id", "n"], emptyEnv)).toThrow("host");
});
it("rejects an out-of-range capacity and ssh-port", () => {
expect(() =>
parseArgs(
["--host", "h", "--node-id", "n", "--capacity", "99"],
emptyEnv,
),
).toThrow("capacity");
expect(() =>
parseArgs(["--host", "h", "--node-id", "n", "--ssh-port", "0"], emptyEnv),
).toThrow("ssh-port");
});
it("throws when a flag is missing its value", () => {
expect(() => parseArgs(["--host", "--node-id", "n"], emptyEnv)).toThrow(
"requires a value",
);
});
});
describe("host-key pinning helpers", () => {
const args = {
host: "203.0.113.10",
nodeId: "robot-1",
keyPath: "/ssh/key",
sshPort: 2222,
sshUser: "root",
capacity: 8,
dryRun: false,
};
it("passes an existing docker node pin into the SSH verifier before re-onboard", () => {
const onHostKeyDiscovered = async () => {};
const config = buildOnboardSshConfig(
args,
{ host_key_fingerprint: "pinned-fingerprint", capacity: 8 },
onHostKeyDiscovered,
);
expect(config).toEqual({
hostname: "203.0.113.10",
port: 2222,
username: "root",
privateKeyPath: "/ssh/key",
hostKeyFingerprint: "pinned-fingerprint",
onHostKeyDiscovered,
});
});
it("uses TOFU only when the existing docker node is unpinned or absent", () => {
const onHostKeyDiscovered = async () => {};
expect(
buildOnboardSshConfig(
args,
{ host_key_fingerprint: null, capacity: 8 },
onHostKeyDiscovered,
).hostKeyFingerprint,
).toBeUndefined();
expect(
buildOnboardSshConfig(args, null, onHostKeyDiscovered).hostKeyFingerprint,
).toBeUndefined();
});
it("never overwrites an established pin with a re-onboard capture", () => {
expect(
hostKeyFingerprintForOnboardUpsert(
{ host_key_fingerprint: "pinned-fingerprint", capacity: 8 },
"attacker-fingerprint",
),
).toBe("pinned-fingerprint");
});
it("persists the captured fingerprint for first onboard or still-unpinned nodes", () => {
expect(hostKeyFingerprintForOnboardUpsert(null, "first-pin")).toBe(
"first-pin",
);
expect(
hostKeyFingerprintForOnboardUpsert(
{ host_key_fingerprint: null, capacity: 8 },
"first-pin",
),
).toBe("first-pin");
expect(hostKeyFingerprintForOnboardUpsert(null, undefined)).toBeNull();
});
});
describe("capacityForOnboardUpsert", () => {
it("preserves an existing node's operator-tuned capacity across re-onboard", () => {
// Robot the operator bumped to 24 via a direct DB write; the --capacity
// flag still carries the small-box default and must not win.
expect(
capacityForOnboardUpsert(
{ host_key_fingerprint: "pinned", capacity: 24 },
8,
),
).toBe(24);
});
it("seeds a brand-new row from the --capacity flag", () => {
expect(capacityForOnboardUpsert(null, 8)).toBe(8);
expect(capacityForOnboardUpsert(null, 4)).toBe(4);
});
});