feat(client): persist digital governance events

This commit is contained in:
baiyanyun
2026-06-07 18:59:59 +08:00
parent 5e76828afe
commit e37024bdbe
14 changed files with 925 additions and 158 deletions

View File

@@ -2,6 +2,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
const mockState = vi.hoisted(() => ({
db: null as TestDb | null,
ensureSchemaCalls: 0,
settings: new Map<string, unknown>(),
fetch: vi.fn(),
}));
@@ -20,7 +21,10 @@ vi.mock("../system/deviceId", () => ({
}));
vi.mock("../../db", () => ({
ensureDigitalEmployeeSchema: () => true,
ensureDigitalEmployeeSchema: () => {
mockState.ensureSchemaCalls += 1;
return true;
},
getDb: () => mockState.db,
readSetting: (key: string) => mockState.settings.get(key) ?? null,
}));
@@ -31,6 +35,7 @@ describe("digital employee sync service", () => {
vi.useFakeTimers();
vi.setSystemTime(new Date("2026-06-07T08:00:00.000Z"));
mockState.fetch = vi.fn();
mockState.ensureSchemaCalls = 0;
vi.stubGlobal("fetch", mockState.fetch);
mockState.settings = new Map<string, unknown>([
["step1_config", { serverHost: "https://manage.example.com" }],
@@ -62,6 +67,7 @@ describe("digital employee sync service", () => {
status: "pending",
attempts: 0,
});
expect(mockState.ensureSchemaCalls).toBeGreaterThan(0);
});
it("marks entity-only acknowledgements as synced", async () => {
@@ -103,6 +109,7 @@ describe("digital employee sync service", () => {
error: null,
}),
]);
expect(mockState.ensureSchemaCalls).toBeGreaterThan(0);
});
it("keeps unacknowledged rows retryable after a partial backend ack", async () => {
@@ -146,6 +153,11 @@ describe("digital employee sync service", () => {
});
expect(status.recentFailures[0]).toMatchObject({
id: "plan:upsert:plan-2",
entitySummary: {
title: "同步测试计划 plan-2",
status: "pending",
summary: "用于验证同步失败诊断的业务摘要",
},
dueForRetry: false,
managementRecordUrl:
"https://manage.example.com/system/content/content-digital-employee-sync?device_id=device-001&entity_type=plan&entity_id=plan-2&outbox_id=plan%3Aupsert%3Aplan-2",
@@ -216,6 +228,10 @@ function insertPlan(id: string): void {
function insertEntity(entityType: string, id: string): void {
entityMap(entityType)?.set(id, {
id,
title: `同步测试计划 ${id}`,
status: "pending",
objective: "用于验证同步失败诊断的业务摘要",
payload: "{}",
remote_id: null,
sync_status: "pending",
sync_error: null,
@@ -292,6 +308,10 @@ interface TestOutboxRow {
interface TestSyncEntityRow {
id: string;
title?: string;
status?: string;
objective?: string | null;
payload?: string;
remote_id: string | null;
sync_status: string;
sync_error: string | null;
@@ -402,6 +422,11 @@ class TestDb {
}
private get(sql: string, args: unknown[]): unknown {
if (sql.startsWith("SELECT title, status, objective, payload FROM digital_plans")) {
const [id] = args as [string];
return this.plans.get(id);
}
if (
sql.includes("COUNT(*) AS count FROM digital_sync_outbox WHERE status = ?")
) {