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
314 lines
9.2 KiB
JavaScript
314 lines
9.2 KiB
JavaScript
/**
|
|
* Unit tests for the pure decision logic behind the one-command iOS Simulator
|
|
* e2e orchestrator (`ios-e2e.mjs`, wired as `test:e2e:ios`). Deterministic, no
|
|
* simulator required: they pin the flag→step-plan mapping, the loud/vacuous
|
|
* guards, booted-udid selection over `simctl` JSON, app-id extraction, and the
|
|
* exact argv each real leg is spawned with. Runs in the packages/app vitest
|
|
* suite (`bun run --cwd packages/app test`), i.e. the root test:client lane.
|
|
*/
|
|
import { describe, expect, it } from "vitest";
|
|
import {
|
|
assertNonVacuousPlan,
|
|
buildAuthSmokeCommand,
|
|
buildCloudProvisioningCommand,
|
|
buildIosSimBuildCommand,
|
|
buildLocalChatSmokeCommand,
|
|
classifyStepExit,
|
|
DEFAULT_IOS_SIMULATOR,
|
|
extractAppId,
|
|
IOS_E2E_STEP_IDS,
|
|
IOS_E2E_VERIFICATION_STEP_IDS,
|
|
isAppInstalled,
|
|
parseIosE2eArgs,
|
|
planIosE2eSteps,
|
|
resolveTargetDevice,
|
|
selectBootedUdid,
|
|
} from "./ios-e2e-lib.mjs";
|
|
|
|
const UDID = "A1B2C3D4-1111-2222-3333-444455556666";
|
|
|
|
describe("parseIosE2eArgs", () => {
|
|
it("defaults to a full run with no flags", () => {
|
|
const f = parseIosE2eArgs(["node", "ios-e2e.mjs"]);
|
|
expect(f).toEqual({
|
|
device: undefined,
|
|
appPath: undefined,
|
|
output: undefined,
|
|
skipBuild: false,
|
|
skipAuth: false,
|
|
skipLocalChat: false,
|
|
cloud: false,
|
|
noWait: false,
|
|
});
|
|
});
|
|
|
|
it("parses every boolean flag", () => {
|
|
const f = parseIosE2eArgs([
|
|
"--skip-build",
|
|
"--skip-auth",
|
|
"--skip-local-chat",
|
|
"--cloud",
|
|
"--no-wait",
|
|
]);
|
|
expect(f.skipBuild).toBe(true);
|
|
expect(f.skipAuth).toBe(true);
|
|
expect(f.skipLocalChat).toBe(true);
|
|
expect(f.cloud).toBe(true);
|
|
expect(f.noWait).toBe(true);
|
|
});
|
|
|
|
it("captures --device, --app-path, and --output values", () => {
|
|
const f = parseIosE2eArgs([
|
|
"--device",
|
|
"iPhone 15",
|
|
"--app-path",
|
|
"/tmp/App.app",
|
|
"--output",
|
|
"/tmp/evidence",
|
|
]);
|
|
expect(f.device).toBe("iPhone 15");
|
|
expect(f.appPath).toBe("/tmp/App.app");
|
|
expect(f.output).toBe("/tmp/evidence");
|
|
});
|
|
|
|
it("does not read past the end of argv for a trailing value flag", () => {
|
|
const f = parseIosE2eArgs(["--device"]);
|
|
expect(f.device).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe("planIosE2eSteps", () => {
|
|
const full = {
|
|
skipBuild: false,
|
|
skipAuth: false,
|
|
skipLocalChat: false,
|
|
cloud: false,
|
|
noWait: false,
|
|
};
|
|
|
|
it("runs build → auth → local-chat by default (no cloud)", () => {
|
|
const ids = planIosE2eSteps(full).map((s) => s.id);
|
|
expect(ids).toEqual(["build", "auth", "local-chat"]);
|
|
});
|
|
|
|
it("appends cloud last only when requested", () => {
|
|
const ids = planIosE2eSteps({ ...full, cloud: true }).map((s) => s.id);
|
|
expect(ids).toEqual(["build", "auth", "local-chat", "cloud"]);
|
|
});
|
|
|
|
it("preserves the fixed run order under any flag subset", () => {
|
|
const ids = planIosE2eSteps({ ...full, cloud: true }).map((s) => s.id);
|
|
const canonicalRank = (id) => IOS_E2E_STEP_IDS.indexOf(id);
|
|
const ranks = ids.map(canonicalRank);
|
|
expect(ranks).toEqual([...ranks].sort((a, b) => a - b));
|
|
});
|
|
|
|
it.each([
|
|
["skipBuild", "build"],
|
|
["skipAuth", "auth"],
|
|
["skipLocalChat", "local-chat"],
|
|
])("%s removes exactly the %s step", (flag, removedId) => {
|
|
const ids = planIosE2eSteps({ ...full, [flag]: true }).map((s) => s.id);
|
|
expect(ids).not.toContain(removedId);
|
|
// Everything else that would have run still runs.
|
|
for (const id of ["build", "auth", "local-chat"]) {
|
|
if (id !== removedId) expect(ids).toContain(id);
|
|
}
|
|
});
|
|
|
|
it("marks only auth/local-chat as simulator-app verification legs", () => {
|
|
const steps = planIosE2eSteps({ ...full, cloud: true });
|
|
const verifying = steps.filter((s) => s.verification).map((s) => s.id);
|
|
expect(verifying).toEqual(IOS_E2E_VERIFICATION_STEP_IDS);
|
|
expect(steps.find((s) => s.id === "build").verification).toBe(false);
|
|
expect(steps.find((s) => s.id === "cloud").verification).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("assertNonVacuousPlan", () => {
|
|
it("returns the verification legs for a real plan", () => {
|
|
const steps = planIosE2eSteps({
|
|
skipBuild: false,
|
|
skipAuth: false,
|
|
skipLocalChat: false,
|
|
cloud: false,
|
|
});
|
|
const verifying = assertNonVacuousPlan(steps);
|
|
expect(verifying.map((s) => s.id)).toEqual(["auth", "local-chat"]);
|
|
});
|
|
|
|
it("throws when every verification leg is skipped (build-only is not proof)", () => {
|
|
const steps = planIosE2eSteps({
|
|
skipBuild: false,
|
|
skipAuth: true,
|
|
skipLocalChat: true,
|
|
cloud: false,
|
|
});
|
|
expect(steps.map((s) => s.id)).toEqual(["build"]);
|
|
expect(() => assertNonVacuousPlan(steps)).toThrow(/refusing to run/i);
|
|
});
|
|
|
|
it("throws on a fully empty plan", () => {
|
|
expect(() => assertNonVacuousPlan([])).toThrow(/refusing to run/i);
|
|
});
|
|
|
|
it("throws when only the cloud leg survives (cloud is optional, not app proof)", () => {
|
|
const steps = planIosE2eSteps({
|
|
skipBuild: true,
|
|
skipAuth: true,
|
|
skipLocalChat: true,
|
|
cloud: true,
|
|
});
|
|
expect(steps.map((s) => s.id)).toEqual(["cloud"]);
|
|
expect(() => assertNonVacuousPlan(steps)).toThrow(
|
|
/cloud alone is not enough/i,
|
|
);
|
|
});
|
|
|
|
it("throws when build and cloud survive without auth or local chat", () => {
|
|
const steps = planIosE2eSteps({
|
|
skipBuild: false,
|
|
skipAuth: true,
|
|
skipLocalChat: true,
|
|
cloud: true,
|
|
});
|
|
expect(steps.map((s) => s.id)).toEqual(["build", "cloud"]);
|
|
expect(() => assertNonVacuousPlan(steps)).toThrow(/auth \/ local-chat/i);
|
|
});
|
|
});
|
|
|
|
describe("selectBootedUdid", () => {
|
|
it("returns the udid of the first Booted device", () => {
|
|
const json = {
|
|
devices: {
|
|
"com.apple.CoreSimulator.SimRuntime.iOS-18-2": [
|
|
{ udid: "shutdown-one", state: "Shutdown" },
|
|
{ udid: UDID, state: "Booted" },
|
|
],
|
|
},
|
|
};
|
|
expect(selectBootedUdid(json)).toBe(UDID);
|
|
});
|
|
|
|
it("returns null when nothing is booted", () => {
|
|
const json = {
|
|
devices: {
|
|
rt: [{ udid: "x", state: "Shutdown" }],
|
|
},
|
|
};
|
|
expect(selectBootedUdid(json)).toBeNull();
|
|
});
|
|
|
|
it.each([
|
|
[null],
|
|
[undefined],
|
|
[{}],
|
|
[{ devices: null }],
|
|
[{ devices: { rt: "nope" } }],
|
|
])("tolerates malformed simctl json %#", (json) => {
|
|
expect(selectBootedUdid(json)).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("resolveTargetDevice", () => {
|
|
it("passes through an explicit device", () => {
|
|
expect(resolveTargetDevice("iPhone 15 Pro")).toBe("iPhone 15 Pro");
|
|
});
|
|
it("defaults to the pinned dev simulator", () => {
|
|
expect(resolveTargetDevice(undefined)).toBe(DEFAULT_IOS_SIMULATOR);
|
|
});
|
|
});
|
|
|
|
describe("extractAppId", () => {
|
|
it("reads the appId from app.config.ts source (double quotes)", () => {
|
|
expect(extractAppId('export default { appId: "ai.elizaos.app" }')).toBe(
|
|
"ai.elizaos.app",
|
|
);
|
|
});
|
|
it("reads single-quoted appId", () => {
|
|
expect(extractAppId("{ appId: 'com.example.custom' }")).toBe(
|
|
"com.example.custom",
|
|
);
|
|
});
|
|
it("falls back to the known default when absent", () => {
|
|
expect(extractAppId("export default {}")).toBe("ai.elizaos.app");
|
|
});
|
|
});
|
|
|
|
describe("leg command builders", () => {
|
|
it("builds the sim build command", () => {
|
|
expect(buildIosSimBuildCommand()).toEqual({
|
|
cmd: "bun",
|
|
args: ["run", "build:ios:local:sim"],
|
|
});
|
|
});
|
|
|
|
it("targets the booted udid for the auth leg", () => {
|
|
expect(buildAuthSmokeCommand(UDID)).toEqual({
|
|
cmd: "node",
|
|
args: [
|
|
"../../packages/app-core/scripts/mobile-auth-simulator-smoke.mjs",
|
|
"--platform",
|
|
"ios",
|
|
"--device",
|
|
UDID,
|
|
],
|
|
});
|
|
});
|
|
|
|
it("refuses to build an auth command without a udid", () => {
|
|
expect(() => buildAuthSmokeCommand(undefined)).toThrow(/udid/i);
|
|
});
|
|
|
|
it("keeps the flags that make the chat leg real (no host fallback, full-bun engine)", () => {
|
|
const { args } = buildLocalChatSmokeCommand();
|
|
expect(args).toContain("--require-installed");
|
|
expect(args).toContain("--ios-select-local");
|
|
expect(args).toContain("--ios-full-bun-smoke");
|
|
expect(args.slice(0, 3)).toEqual([
|
|
"scripts/mobile-local-chat-smoke.mjs",
|
|
"--platform",
|
|
"ios",
|
|
]);
|
|
});
|
|
|
|
it("builds the cloud provisioning command", () => {
|
|
expect(buildCloudProvisioningCommand()).toEqual({
|
|
cmd: "node",
|
|
args: ["scripts/cloud-provisioning-e2e.mjs"],
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("classifyStepExit", () => {
|
|
it("treats exit 0 as ok", () => {
|
|
expect(classifyStepExit(0)).toEqual({ ok: true });
|
|
});
|
|
it("treats a non-zero exit as a hard failure", () => {
|
|
expect(classifyStepExit(1)).toEqual({
|
|
ok: false,
|
|
reason: "exited with code 1",
|
|
});
|
|
});
|
|
it("treats a signal kill (null status) as a hard failure", () => {
|
|
expect(classifyStepExit(null)).toEqual({
|
|
ok: false,
|
|
reason: "terminated by signal",
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("isAppInstalled", () => {
|
|
it("accepts a non-empty container path", () => {
|
|
expect(isAppInstalled("/Users/x/App.app")).toBe(true);
|
|
});
|
|
it.each([
|
|
["", false],
|
|
[" ", false],
|
|
[null, false],
|
|
[undefined, false],
|
|
])("rejects %p", (container, expected) => {
|
|
expect(isAppInstalled(container)).toBe(expected);
|
|
});
|
|
});
|