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

278 lines
9.9 KiB
TypeScript

/**
* External-API contract test for the plugin-owned Google -> LifeOps parser.
*
* `lifeOpsCalendarEventFromGoogle` / `lifeOpsCalendarAttendeeFromGoogle` /
* `lifeOpsCalendarSummaryFromGoogle` (src/internal/google-delegates.ts) are the
* genuine external-API boundary for the calendar view's data: they map the
* `GoogleCalendarEvent` / `GoogleCalendarListEntry` shapes produced by
* `@elizaos/plugin-google`'s `mapEvent` / list mapper (over the googleapis
* `calendar_v3` JSON) into the `LifeOpsCalendarEvent` / `LifeOpsCalendarSummary`
* the CalendarSection feed renders.
*
* The fixtures below are hand-built to match the EXACT shape `mapEvent` emits
* (verified against plugins/plugin-google/src/calendar.ts `mapEvent`, ~205-243,
* and `mapCalendarListEntry`, ~186-203):
* - timed event: start/end are ISO `new Date(dateTime).toISOString()` strings,
* `isAllDay: false`, `timeZone` populated, `htmlLink`, `meetLink` (from
* `hangoutLink` or `conferenceData.entryPoints[0].uri`), `attendees` mapped
* to `{ email, name? }`, `organizer` `{ email, name?, self }`, and a
* `metadata` object with `iCalUID`/`recurringEventId`/`createdAt`/`updatedAt`.
* - all-day event: `start`/`end` are `new Date("YYYY-MM-DDT00:00:00.000Z")`
* ISO strings, `isAllDay: true`.
* - minimal event: only `id`/`calendarId` (every other field undefined), to
* exercise the parser's `(untitled)` / `confirmed` / `false` / `null`
* defaults.
*
* This validates the parser against the real provider shape entirely within
* plugin-calendar (no @elizaos/agent / connector graph needed).
*/
import type {
GoogleCalendarEvent,
GoogleCalendarListEntry,
} from "@elizaos/plugin-google";
import type { LifeOpsConnectorGrant } from "@elizaos/shared";
import { describe, expect, it } from "vitest";
import {
lifeOpsCalendarEventFromGoogle,
lifeOpsCalendarSummaryFromGoogle,
} from "../src/internal/google-delegates.js";
const AGENT_ID = "agent-7";
const ownerGrant: LifeOpsConnectorGrant = {
id: "connector-account:acct-123",
agentId: AGENT_ID,
provider: "google",
connectorAccountId: "acct-123",
side: "owner",
identity: { email: "owner@example.com" },
identityEmail: "owner@example.com",
grantedScopes: ["https://www.googleapis.com/auth/calendar.events"],
capabilities: ["google.calendar.read", "google.calendar.write"],
tokenRef: null,
mode: "local",
executionTarget: "local",
sourceOfTruth: "connector_account",
preferredByAgent: true,
cloudConnectionId: null,
metadata: {},
lastRefreshAt: "2026-06-16T00:00:00.000Z",
createdAt: "2026-06-01T00:00:00.000Z",
updatedAt: "2026-06-16T00:00:00.000Z",
};
// Shape produced by plugin-google `mapEvent` for a normal timed event.
const timedGoogleEvent: GoogleCalendarEvent = {
id: "evt_abc123",
calendarId: "owner@example.com",
title: "Design sync",
status: "confirmed",
start: new Date("2026-06-17T15:00:00.000Z").toISOString(),
end: new Date("2026-06-17T16:00:00.000Z").toISOString(),
isAllDay: false,
timeZone: "America/New_York",
htmlLink: "https://www.google.com/calendar/event?eid=evt_abc123",
meetLink: "https://meet.google.com/abc-defg-hij",
attendees: [
{ email: "owner@example.com", name: "Owner Person" },
{ email: "guest@elsewhere.com", name: "Guest" },
],
location: "Room 4B",
description: "Weekly design review",
organizer: { email: "owner@example.com", name: "Owner Person", self: true },
metadata: {
iCalUID: "evt_abc123@google.com",
recurringEventId: null,
createdAt: "2026-06-10T09:00:00.000Z",
updatedAt: "2026-06-15T12:00:00.000Z",
},
};
// Shape produced by `mapEvent` for an all-day event (date-only start/end).
const allDayGoogleEvent: GoogleCalendarEvent = {
id: "evt_holiday",
calendarId: "owner@example.com",
title: "Company holiday",
status: "confirmed",
start: new Date("2026-07-04T00:00:00.000Z").toISOString(),
end: new Date("2026-07-05T00:00:00.000Z").toISOString(),
isAllDay: true,
timeZone: "America/New_York",
htmlLink: "https://www.google.com/calendar/event?eid=evt_holiday",
attendees: [],
metadata: {
iCalUID: "evt_holiday@google.com",
recurringEventId: null,
createdAt: null,
updatedAt: null,
},
};
// Minimal shape — `mapEvent` only guarantees id + calendarId; everything else
// can be undefined (e.g. a cancelled or sparse event row).
const minimalGoogleEvent: GoogleCalendarEvent = {
id: "evt_min",
calendarId: "primary",
};
describe("lifeOpsCalendarEventFromGoogle (Google -> LifeOps contract)", () => {
it("maps a timed event to a contract-valid LifeOpsCalendarEvent", () => {
const result = lifeOpsCalendarEventFromGoogle({
event: timedGoogleEvent,
grant: ownerGrant,
agentId: AGENT_ID,
syncedAt: "2026-06-16T08:00:00.000Z",
});
expect(result.id).toBe(
"agent-7:google:owner:calendar:owner@example.com:evt_abc123",
);
expect(result.externalId).toBe("evt_abc123");
expect(result.agentId).toBe(AGENT_ID);
expect(result.provider).toBe("google");
expect(result.side).toBe("owner");
expect(result.calendarId).toBe("owner@example.com");
expect(result.title).toBe("Design sync");
expect(result.description).toBe("Weekly design review");
expect(result.location).toBe("Room 4B");
expect(result.status).toBe("confirmed");
expect(result.startAt).toBe("2026-06-17T15:00:00.000Z");
expect(result.endAt).toBe("2026-06-17T16:00:00.000Z");
expect(result.isAllDay).toBe(false);
expect(result.timezone).toBe("America/New_York");
expect(result.htmlLink).toBe(
"https://www.google.com/calendar/event?eid=evt_abc123",
);
// conferenceLink is sourced from the Google `meetLink` field.
expect(result.conferenceLink).toBe("https://meet.google.com/abc-defg-hij");
expect(result.organizer).toEqual({
email: "owner@example.com",
name: "Owner Person",
self: true,
});
// attendees mapped to the LifeOps attendee shape (response/self/organizer
// default false/null because Google's summary projection omits them).
expect(result.attendees).toEqual([
{
email: "owner@example.com",
displayName: "Owner Person",
responseStatus: null,
self: false,
organizer: false,
optional: false,
},
{
email: "guest@elsewhere.com",
displayName: "Guest",
responseStatus: null,
self: false,
organizer: false,
optional: false,
},
]);
// metadata is merged with googlePlugin:true plus the Google metadata block.
expect(result.metadata.googlePlugin).toBe(true);
expect(result.metadata.iCalUID).toBe("evt_abc123@google.com");
expect(result.metadata.updatedAt).toBe("2026-06-15T12:00:00.000Z");
expect(result.syncedAt).toBe("2026-06-16T08:00:00.000Z");
expect(result.updatedAt).toBe("2026-06-16T08:00:00.000Z");
expect(result.connectorAccountId).toBe("acct-123");
expect(result.grantId).toBe("connector-account:acct-123");
expect(result.accountEmail).toBe("owner@example.com");
});
it("maps an all-day event preserving date-only bounds and isAllDay", () => {
const result = lifeOpsCalendarEventFromGoogle({
event: allDayGoogleEvent,
grant: ownerGrant,
agentId: AGENT_ID,
syncedAt: "2026-06-16T08:00:00.000Z",
});
expect(result.isAllDay).toBe(true);
expect(result.startAt).toBe("2026-07-04T00:00:00.000Z");
expect(result.endAt).toBe("2026-07-05T00:00:00.000Z");
expect(result.title).toBe("Company holiday");
// No conference / no attendees for this event.
expect(result.conferenceLink).toBeNull();
expect(result.attendees).toEqual([]);
});
it("applies (untitled)/confirmed/false/null defaults for a minimal event", () => {
const result = lifeOpsCalendarEventFromGoogle({
event: minimalGoogleEvent,
grant: ownerGrant,
agentId: AGENT_ID,
syncedAt: "2026-06-16T08:00:00.000Z",
});
expect(result.title).toBe("(untitled)");
expect(result.description).toBe("");
expect(result.location).toBe("");
expect(result.status).toBe("confirmed");
expect(result.isAllDay).toBe(false);
expect(result.timezone).toBeNull();
expect(result.htmlLink).toBeNull();
expect(result.conferenceLink).toBeNull();
expect(result.organizer).toBeNull();
expect(result.attendees).toEqual([]);
// start falls back to syncedAt, end falls back to start when absent.
expect(result.startAt).toBe("2026-06-16T08:00:00.000Z");
expect(result.endAt).toBe("2026-06-16T08:00:00.000Z");
expect(result.id).toBe("agent-7:google:owner:calendar:primary:evt_min");
});
});
describe("lifeOpsCalendarSummaryFromGoogle (calendar list contract)", () => {
const listEntry: GoogleCalendarListEntry = {
calendarId: "owner@example.com",
summary: "Owner Person",
description: "Primary calendar",
primary: true,
accessRole: "owner",
backgroundColor: "#0b8043",
foregroundColor: "#ffffff",
timeZone: "America/New_York",
selected: true,
};
it("maps a calendar list entry to a LifeOpsCalendarSummary with feed defaults", () => {
const summary = lifeOpsCalendarSummaryFromGoogle({
entry: listEntry,
grant: ownerGrant,
});
expect(summary).toEqual({
provider: "google",
side: "owner",
grantId: "connector-account:acct-123",
accountEmail: "owner@example.com",
calendarId: "owner@example.com",
summary: "Owner Person",
description: "Primary calendar",
primary: true,
accessRole: "owner",
backgroundColor: "#0b8043",
foregroundColor: "#ffffff",
timeZone: "America/New_York",
selected: true,
// includeInFeed defaults to true (opt-out, never opt-in).
includeInFeed: true,
});
});
it("honors an explicit includeInFeed=false override", () => {
const summary = lifeOpsCalendarSummaryFromGoogle({
entry: listEntry,
grant: ownerGrant,
includeInFeed: false,
});
expect(summary.includeInFeed).toBe(false);
});
});