Files
elizaos--eliza/plugins/plugin-elizacloud/__tests__/cloud-coding-container-routes.test.ts
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

265 lines
8.4 KiB
TypeScript

import type http from "node:http";
import { CLOUD_CONTAINER_SERVICE_TYPE } from "@elizaos/shared";
import { describe, expect, it } from "vitest";
import { handleCloudCodingContainerRoute } from "../src/routes/cloud-coding-container-routes";
import type {
PromoteVfsToCloudContainerRequest,
RequestCodingAgentContainerRequest,
SyncCloudCodingContainerRequest,
} from "../src/types/cloud";
function requestWithBody(body: unknown): http.IncomingMessage {
return {
body,
headers: {},
method: "POST",
url: "/",
} as http.IncomingMessage & { body: unknown };
}
function responseSink(): http.ServerResponse & { jsonBody: () => unknown } {
let body = "";
const sink = {
headersSent: false,
statusCode: 200,
setHeader: () => {},
end: (chunk?: unknown) => {
body = typeof chunk === "string" ? chunk : String(chunk ?? "");
sink.headersSent = true;
return {} as http.ServerResponse;
},
jsonBody: () => JSON.parse(body),
};
return sink as http.ServerResponse & { jsonBody: () => unknown };
}
describe("cloud coding-container routes", () => {
it("forwards VFS promotion requests to the cloud container service", async () => {
let captured: PromoteVfsToCloudContainerRequest | null = null;
const service = {
promoteVfsToCloudContainer: async (request: PromoteVfsToCloudContainerRequest) => {
captured = request;
return {
success: true,
data: {
promotionId: "promo-1",
status: "accepted" as const,
source: request.source,
workspacePath: "/workspace",
createdAt: "2026-05-11T00:00:00.000Z",
},
};
},
requestCodingAgentContainer: async () => {
throw new Error("unexpected");
},
syncCodingContainerChanges: async () => {
throw new Error("unexpected");
},
};
const requestedServiceTypes: string[] = [];
const runtime = {
getService: (serviceType: string) => {
requestedServiceTypes.push(serviceType);
return serviceType === CLOUD_CONTAINER_SERVICE_TYPE ? service : null;
},
};
const request: PromoteVfsToCloudContainerRequest = {
preferredAgent: "codex",
source: {
sourceKind: "project",
projectId: "vfs-project-1",
revision: "rev-1",
files: [{ path: "src/index.ts", contents: "export {};", encoding: "utf-8" }],
},
};
const response = responseSink();
const handled = await handleCloudCodingContainerRoute(
requestWithBody(request),
response,
"/api/cloud/coding-containers/promotions",
"POST",
{ runtime: runtime as never }
);
expect(handled).toBe(true);
expect(response.statusCode).toBe(200);
expect(requestedServiceTypes).toEqual([CLOUD_CONTAINER_SERVICE_TYPE]);
expect(captured).toEqual(request);
expect(response.jsonBody()).toMatchObject({
success: true,
data: { promotionId: "promo-1", workspacePath: "/workspace" },
});
});
it("forwards coding-agent container requests with Claude/Codex/OpenCode agent ids", async () => {
let captured: RequestCodingAgentContainerRequest | null = null;
const service = {
promoteVfsToCloudContainer: async () => {
throw new Error("unexpected");
},
requestCodingAgentContainer: async (request: RequestCodingAgentContainerRequest) => {
captured = request;
return {
success: true,
data: {
containerId: "cc-1",
status: "requested" as const,
agent: request.agent,
promotionId: request.promotionId,
workspacePath: "/workspace",
createdAt: "2026-05-11T00:00:00.000Z",
},
};
},
syncCodingContainerChanges: async () => {
throw new Error("unexpected");
},
};
const runtime = { getService: () => service };
const request: RequestCodingAgentContainerRequest = {
agent: "claude",
promotionId: "promo-1",
prompt: "Fix the failing test",
};
const response = responseSink();
await handleCloudCodingContainerRoute(
requestWithBody(request),
response,
"/api/cloud/coding-containers",
"POST",
{ runtime: runtime as never }
);
expect(captured).toEqual(request);
expect(response.jsonBody()).toMatchObject({
success: true,
data: { containerId: "cc-1", agent: "claude", promotionId: "promo-1" },
});
});
it("forwards sync requests with decoded container ids", async () => {
let capturedContainerId: string | null = null;
let capturedRequest: SyncCloudCodingContainerRequest | null = null;
const service = {
promoteVfsToCloudContainer: async () => {
throw new Error("unexpected");
},
requestCodingAgentContainer: async () => {
throw new Error("unexpected");
},
syncCodingContainerChanges: async (
containerId: string,
request: SyncCloudCodingContainerRequest
) => {
capturedContainerId = containerId;
capturedRequest = request;
return {
success: true,
data: {
syncId: "sync-1",
containerId,
status: "ready" as const,
direction: request.direction ?? "pull",
target: request.target,
changedFiles: request.changedFiles ?? [],
deletedFiles: request.deletedFiles ?? [],
patches: request.patches ?? [],
createdAt: "2026-05-11T00:00:00.000Z",
},
};
},
};
const runtime = { getService: () => service };
const request: SyncCloudCodingContainerRequest = {
direction: "pull",
target: { sourceKind: "workspace", workspaceId: "workspace-1", baseRevision: "rev-1" },
patches: [{ path: "src/index.ts", format: "unified-diff", patch: "@@ test" }],
};
const response = responseSink();
await handleCloudCodingContainerRoute(
requestWithBody(request),
response,
"/api/cloud/coding-containers/container%2Fone/sync",
"POST",
{ runtime: runtime as never }
);
expect(capturedContainerId).toBe("container/one");
expect(capturedRequest).toEqual(request);
expect(response.jsonBody()).toMatchObject({
success: true,
data: { syncId: "sync-1", containerId: "container/one" },
});
});
it("rejects malformed request shapes before hitting the service", async () => {
const response = responseSink();
const runtime = {
getService: () => ({
promoteVfsToCloudContainer: async () => {
throw new Error("should not be called");
},
requestCodingAgentContainer: async () => {
throw new Error("should not be called");
},
syncCodingContainerChanges: async () => {
throw new Error("should not be called");
},
}),
};
await handleCloudCodingContainerRoute(
requestWithBody({ agent: "gemini", promotionId: "promo-1" }),
response,
"/api/cloud/coding-containers",
"POST",
{ runtime: runtime as never }
);
expect(response.statusCode).toBe(400);
expect(response.jsonBody()).toEqual({
error: 'Invalid option: expected one of "claude"|"codex"|"opencode"|"elizaos"',
});
});
it("does not discover cloud container services through legacy spelling guesses", async () => {
const legacyOnlyRuntime = {
getService: (serviceType: string) =>
serviceType === "cloud-container"
? {
promoteVfsToCloudContainer: async () => {
throw new Error("should not be called");
},
requestCodingAgentContainer: async () => {
throw new Error("should not be called");
},
syncCodingContainerChanges: async () => {
throw new Error("should not be called");
},
}
: null,
};
const response = responseSink();
const handled = await handleCloudCodingContainerRoute(
requestWithBody({
source: { sourceKind: "project", projectId: "vfs-project-1" },
}),
response,
"/api/cloud/coding-containers/promotions",
"POST",
{ runtime: legacyOnlyRuntime as never }
);
expect(handled).toBe(true);
expect(response.statusCode).toBe(503);
expect(response.jsonBody()).toEqual({
error: "Cloud container service is not available",
});
});
});