feat(client): persist digital governance events
This commit is contained in:
@@ -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 = ?")
|
||||
) {
|
||||
|
||||
Reference in New Issue
Block a user