Files
elizaos--eliza/plugins/plugin-personal-assistant/test/lifeops-feature-flags.integration.test.ts
T
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

167 lines
5.9 KiB
TypeScript

/**
* Integration coverage for the LifeOps feature-flag service against a real
* runtime: verifies base defaults, cloud-linked-on overrides, and key
* validation, wrapping the runtime with a fake CLOUD_AUTH service to exercise
* the signed-in path.
*/
import type { IAgentRuntime, Service } from "@elizaos/core";
import { afterEach, describe, expect, it } from "vitest";
import { createFeatureFlagService } from "../src/lifeops/feature-flags.ts";
import {
ALL_FEATURE_KEYS,
BASE_FEATURE_DEFAULTS,
CLOUD_LINKED_DEFAULT_ON,
isLifeOpsFeatureKey,
resolveFeatureDefaults,
} from "../src/lifeops/feature-flags.types.ts";
import { executeRawSql } from "../src/lifeops/sql.ts";
import { createLifeOpsTestRuntime } from "./helpers/runtime.ts";
interface MockCloudAuth {
isAuthenticated(): boolean;
}
/**
* Wrap a real lifeops runtime with a fake CLOUD_AUTH service so the
* feature-flag service believes the user is signed in. Other services
* are passed through unchanged so SQL still works.
*/
function withMockCloudAuth(
runtime: IAgentRuntime,
authenticated: boolean,
): IAgentRuntime {
const mockAuth = {
isAuthenticated: () => authenticated,
} as Service & MockCloudAuth;
const original = runtime.getService.bind(runtime);
return new Proxy(runtime, {
get(target, prop, receiver) {
if (prop === "getService") {
return <T extends Service>(serviceType: string): T | null => {
if (serviceType === "CLOUD_AUTH") {
return mockAuth as T;
}
return original<T>(serviceType);
};
}
return Reflect.get(target, prop, receiver);
},
});
}
describe("LifeOps feature flag schema integration", () => {
let runtimeResult: Awaited<
ReturnType<typeof createLifeOpsTestRuntime>
> | null = null;
afterEach(async () => {
if (runtimeResult) {
await runtimeResult.cleanup();
runtimeResult = null;
}
});
it("reads compile-time defaults from a fresh runtime and persists overrides via the plugin schema", async () => {
runtimeResult = await createLifeOpsTestRuntime();
const service = createFeatureFlagService(runtimeResult.runtime);
const states = await service.list();
expect(states).toHaveLength(ALL_FEATURE_KEYS.length);
for (const state of states) {
expect(isLifeOpsFeatureKey(state.featureKey)).toBe(true);
if (!isLifeOpsFeatureKey(state.featureKey)) continue;
expect(state.enabled).toBe(
BASE_FEATURE_DEFAULTS[state.featureKey].enabled,
);
expect(state.source).toBe("default");
}
const enabled = await service.enable(
"notifications.push",
"local",
runtimeResult.runtime.agentId,
{ channel: "ntfy" },
);
expect(enabled.enabled).toBe(true);
expect(enabled.source).toBe("local");
expect(enabled.enabledBy).toBe(runtimeResult.runtime.agentId);
expect(enabled.metadata).toEqual({ channel: "ntfy" });
const roundTrip = await service.get("notifications.push");
expect(roundTrip.enabled).toBe(true);
expect(roundTrip.source).toBe("local");
expect(roundTrip.enabledBy).toBe(runtimeResult.runtime.agentId);
expect(roundTrip.metadata).toEqual({ channel: "ntfy" });
});
it("rejects unknown persisted feature keys instead of skipping them", async () => {
runtimeResult = await createLifeOpsTestRuntime();
await executeRawSql(
runtimeResult.runtime,
`INSERT INTO lifeops_features (
feature_key, enabled, source, enabled_at, enabled_by, metadata, created_at, updated_at
) VALUES (
'made_up.feature', TRUE, 'local', now(), NULL, '{}'::jsonb, now(), now()
)`,
);
const service = createFeatureFlagService(runtimeResult.runtime);
await expect(service.list()).rejects.toThrow(
/Unknown feature flag 'made_up\.feature'/,
);
});
});
describe("resolveFeatureDefaults Cloud-link policy", () => {
it("returns the conservative baseline when cloudLinked is false", () => {
const defaults = resolveFeatureDefaults({ cloudLinked: false });
for (const key of ALL_FEATURE_KEYS) {
expect(defaults[key].enabled).toBe(BASE_FEATURE_DEFAULTS[key].enabled);
}
expect(defaults["travel.book_flight"].enabled).toBe(false);
expect(defaults["travel.book_hotel"].enabled).toBe(false);
expect(defaults["cloud.duffel"].enabled).toBe(false);
});
it("flips travel + cloud.duffel ON when cloudLinked is true", () => {
const defaults = resolveFeatureDefaults({ cloudLinked: true });
for (const key of CLOUD_LINKED_DEFAULT_ON) {
expect(defaults[key].enabled).toBe(true);
}
// Non-promoted keys keep their baseline.
expect(defaults["notifications.push"].enabled).toBe(false);
expect(defaults["browser.automation"].enabled).toBe(false);
});
});
describe("FeatureFlagService cloud-aware defaults", () => {
let runtimeResult: Awaited<
ReturnType<typeof createLifeOpsTestRuntime>
> | null = null;
afterEach(async () => {
if (runtimeResult) {
await runtimeResult.cleanup();
runtimeResult = null;
}
});
it("isEnabled('travel.book_flight') returns true with no DB row when CLOUD_AUTH reports authenticated", async () => {
runtimeResult = await createLifeOpsTestRuntime();
const cloudLinkedRuntime = withMockCloudAuth(runtimeResult.runtime, true);
const service = createFeatureFlagService(cloudLinkedRuntime);
expect(await service.isEnabled("travel.book_flight")).toBe(true);
expect(await service.isEnabled("travel.book_hotel")).toBe(true);
expect(await service.isEnabled("cloud.duffel")).toBe(true);
// Non-Cloud-default keys still respect the baseline.
expect(await service.isEnabled("notifications.push")).toBe(false);
});
it("isEnabled('travel.book_flight') stays false when CLOUD_AUTH is missing", async () => {
runtimeResult = await createLifeOpsTestRuntime();
const service = createFeatureFlagService(runtimeResult.runtime);
expect(await service.isEnabled("travel.book_flight")).toBe(false);
});
});