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
236 lines
6.5 KiB
TypeScript
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);
|
|
});
|
|
});
|