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

172 lines
6.1 KiB
TypeScript

/**
* Global pause integration tests.
*
* Layer 1 — `GlobalPauseStore` lifecycle: pause sets the window, `current()`
* reflects active state inside the window, `clear()` returns to inactive.
*
* Layer 2 — the PRODUCTION fire path: `processDueScheduledTasks` against a
* real PGlite-backed runtime (real `LifeOpsRepository` store, real state log,
* the SAME `GlobalPauseStore` the runner consults). With the pause window
* active, a due `respectsGlobalPause: true` task must be SKIPPED by the
* runner (terminal `skipped`, `global_pause` decision log, `skipped`
* state-log row) while a due `respectsGlobalPause: false` emergency task
* must FIRE. An earlier version of this test re-implemented the runner's
* pre-fire decision as a local helper and asserted the helper — it passed
* regardless of what the production runner did. This version drives the
* real tick, so inverting or dropping the pause check in the runner turns
* it red.
*/
import { afterEach, describe, expect, it } from "vitest";
import { createGlobalPauseStore } from "../src/lifeops/global-pause/store.ts";
import { LifeOpsRepository } from "../src/lifeops/repository.js";
import type { ScheduledTask } from "../src/lifeops/scheduled-task/index.js";
import { processDueScheduledTasks } from "../src/lifeops/scheduled-task/scheduler.js";
import { createMinimalRuntimeStub } from "./first-run-helpers.ts";
import {
createLifeOpsTestRuntime,
type RealTestRuntimeResult,
} from "./helpers/runtime.ts";
interface PauseTaskSeed {
taskId: string;
promptInstructions: string;
trigger: ScheduledTask["trigger"];
priority: ScheduledTask["priority"];
respectsGlobalPause: boolean;
}
async function seedScheduledTask(
runtime: RealTestRuntimeResult["runtime"],
seed: PauseTaskSeed,
): Promise<ScheduledTask> {
const repo = new LifeOpsRepository(runtime);
const task: ScheduledTask = {
taskId: seed.taskId,
kind: "reminder",
promptInstructions: seed.promptInstructions,
trigger: seed.trigger,
priority: seed.priority,
respectsGlobalPause: seed.respectsGlobalPause,
source: "user_chat",
createdBy: runtime.agentId,
ownerVisible: true,
state: { status: "scheduled", followupCount: 0 },
};
await repo.upsertScheduledTask(runtime.agentId, task);
return task;
}
describe("global pause integration", () => {
let runtimeResult: RealTestRuntimeResult | null = null;
afterEach(async () => {
if (runtimeResult) {
await runtimeResult.cleanup();
runtimeResult = null;
}
});
it("pause + current + clear lifecycle works", async () => {
const runtime = createMinimalRuntimeStub();
const store = createGlobalPauseStore(runtime);
expect((await store.current()).active).toBe(false);
const startIso = new Date(Date.now() - 60_000).toISOString();
const endIso = new Date(Date.now() + 86_400_000).toISOString();
await store.set({ startIso, endIso, reason: "vacation" });
const active = await store.current();
expect(active.active).toBe(true);
expect(active.reason).toBe("vacation");
expect(active.startIso).toBe(startIso);
expect(active.endIso).toBe(endIso);
// After endIso, no longer active.
const afterEnd = await store.current(new Date(Date.parse(endIso) + 1));
expect(afterEnd.active).toBe(false);
await store.clear();
expect((await store.current()).active).toBe(false);
});
it("real tick under pause: respectsGlobalPause task skips, emergency task fires", async () => {
runtimeResult = await createLifeOpsTestRuntime();
const { runtime } = runtimeResult;
const repo = new LifeOpsRepository(runtime);
// Two due one-shot tasks: a routine reminder that respects the pause and
// an emergency that must cut through it.
const fireAt = "2026-05-09T12:00:00.000Z";
const respecting = await seedScheduledTask(runtime, {
taskId: "st_pause_respecting",
promptInstructions: "Routine reminder that honors vacation mode.",
trigger: { kind: "once", atIso: fireAt },
priority: "medium",
respectsGlobalPause: true,
});
const emergency = await seedScheduledTask(runtime, {
taskId: "st_pause_emergency",
promptInstructions: "Take the heart medication now.",
trigger: { kind: "once", atIso: fireAt },
priority: "high",
respectsGlobalPause: false,
});
// Engage the pause via the SAME store the production runner consults.
const pause = createGlobalPauseStore(runtime);
await pause.set({
startIso: "2026-05-09T11:00:00.000Z",
endIso: "2026-05-09T20:00:00.000Z",
reason: "vacation",
});
const tickAt = new Date("2026-05-09T12:01:00.000Z");
expect((await pause.current(tickAt)).active).toBe(true);
const result = await processDueScheduledTasks({
runtime,
agentId: runtime.agentId,
now: tickAt,
limit: 10,
});
expect(result.errors).toEqual([]);
// The pause-respecting task was visited and skipped by the REAL runner:
// terminal `skipped`, pause reason recorded on the decision log.
const skipped = await repo.getScheduledTask(
runtime.agentId,
respecting.taskId,
);
expect(skipped?.state.status).toBe("skipped");
expect(skipped?.state.lastDecisionLog).toContain("global_pause");
// The emergency task fired straight through the active pause window.
const fired = await repo.getScheduledTask(
runtime.agentId,
emergency.taskId,
);
expect(fired?.state.status).toBe("fired");
expect(fired?.state.firedAt).toBeDefined();
// State-log rows written by the real runner, not inferred by the test.
const respectingTransitions = (
await repo.listScheduledTaskLog({
agentId: runtime.agentId,
taskId: respecting.taskId,
})
).map((entry) => entry.transition);
expect(respectingTransitions).toContain("skipped");
expect(respectingTransitions).not.toContain("fired");
const emergencyTransitions = (
await repo.listScheduledTaskLog({
agentId: runtime.agentId,
taskId: emergency.taskId,
})
).map((entry) => entry.transition);
expect(emergencyTransitions).toContain("fired");
expect(emergencyTransitions).not.toContain("skipped");
});
});