吸收数字员工通知跨端同步
This commit is contained in:
@@ -446,6 +446,157 @@ describe("digital employee sync service", () => {
|
||||
]));
|
||||
});
|
||||
|
||||
it("pulls remote notification updates into local notification overlays", async () => {
|
||||
mockState.settings.set("digital_employee_ui_state_v1", {
|
||||
notificationStateById: {
|
||||
"task:existing": { status: "read", read_at: "2026-06-07T07:30:00.000Z" },
|
||||
},
|
||||
});
|
||||
mockState.fetch.mockResolvedValue(jsonResponse({
|
||||
success: true,
|
||||
data: {
|
||||
notifications: [
|
||||
{
|
||||
notification_id: "task:need-input:1",
|
||||
status: "read",
|
||||
read_at: "2026-06-07T08:01:00.000Z",
|
||||
remote_updated_at: "2026-06-07T08:02:00.000Z",
|
||||
source: "management-inbox",
|
||||
},
|
||||
{
|
||||
notificationId: "task:reply:1",
|
||||
status: "resolved",
|
||||
reply: "已补充客户编号",
|
||||
repliedAt: "2026-06-07T08:03:00.000Z",
|
||||
},
|
||||
],
|
||||
},
|
||||
}));
|
||||
|
||||
const { pullDigitalEmployeeNotificationUpdates } = await import("./syncService");
|
||||
const { readDigitalEmployeeUiState } = await import("./stateService");
|
||||
const result = await pullDigitalEmployeeNotificationUpdates({ force: true });
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: true,
|
||||
endpoint: "https://manage.example.com/api/digital-employee/notifications/updates",
|
||||
applied: 2,
|
||||
ignored: 0,
|
||||
pulled_at: "2026-06-07T08:00:00.000Z",
|
||||
});
|
||||
expect(mockState.fetch).toHaveBeenCalledWith(
|
||||
"https://manage.example.com/api/digital-employee/notifications/updates",
|
||||
expect.objectContaining({ method: "GET" }),
|
||||
);
|
||||
expect(readDigitalEmployeeUiState().notificationStateById).toMatchObject({
|
||||
"task:existing": { status: "read", read_at: "2026-06-07T07:30:00.000Z" },
|
||||
"task:need-input:1": {
|
||||
status: "read",
|
||||
read_at: "2026-06-07T08:01:00.000Z",
|
||||
remote_updated_at: "2026-06-07T08:02:00.000Z",
|
||||
source: "management-inbox",
|
||||
},
|
||||
"task:reply:1": {
|
||||
status: "resolved",
|
||||
reply: "已补充客户编号",
|
||||
replied_at: "2026-06-07T08:03:00.000Z",
|
||||
source: "management-api",
|
||||
},
|
||||
});
|
||||
expect(Array.from(mockState.db?.events.values() ?? [])).toEqual(expect.arrayContaining([
|
||||
expect.objectContaining({ kind: "digital_workday_notification_updates_pulled" }),
|
||||
]));
|
||||
});
|
||||
|
||||
it("keeps local notification overlays when remote notification update pull fails", async () => {
|
||||
mockState.settings.set("digital_employee_ui_state_v1", {
|
||||
notificationStateById: {
|
||||
"task:existing": { status: "read", read_at: "2026-06-07T07:30:00.000Z" },
|
||||
},
|
||||
});
|
||||
mockState.fetch.mockResolvedValue({
|
||||
ok: false,
|
||||
status: 503,
|
||||
text: vi.fn().mockResolvedValue(JSON.stringify({ message: "notification service unavailable" })),
|
||||
} as unknown as Response);
|
||||
|
||||
const { pullDigitalEmployeeNotificationUpdates } = await import("./syncService");
|
||||
const { readDigitalEmployeeUiState } = await import("./stateService");
|
||||
const result = await pullDigitalEmployeeNotificationUpdates({ force: true });
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
endpoint: "https://manage.example.com/api/digital-employee/notifications/updates",
|
||||
applied: 0,
|
||||
ignored: 0,
|
||||
error: "notification service unavailable",
|
||||
});
|
||||
expect(readDigitalEmployeeUiState().notificationStateById).toMatchObject({
|
||||
"task:existing": { status: "read", read_at: "2026-06-07T07:30:00.000Z" },
|
||||
});
|
||||
expect(Array.from(mockState.db?.events.values() ?? [])).toEqual(expect.arrayContaining([
|
||||
expect.objectContaining({ kind: "digital_workday_notification_updates_pull_failed" }),
|
||||
]));
|
||||
});
|
||||
|
||||
it("submits notification actions to management and records submission events", async () => {
|
||||
mockState.fetch.mockResolvedValue(jsonResponse({ success: true, data: { accepted: true } }));
|
||||
|
||||
const { submitDigitalEmployeeNotificationAction } = await import("./syncService");
|
||||
const result = await submitDigitalEmployeeNotificationAction({
|
||||
notification_id: "task:need-input:1",
|
||||
action: "reply",
|
||||
status: "read",
|
||||
reply: "已补充客户编号",
|
||||
replied_at: "2026-06-07T08:03:00.000Z",
|
||||
});
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: true,
|
||||
endpoint: "https://manage.example.com/api/digital-employee/notifications/actions",
|
||||
submitted_at: "2026-06-07T08:00:00.000Z",
|
||||
});
|
||||
expect(mockState.fetch).toHaveBeenCalledWith(
|
||||
"https://manage.example.com/api/digital-employee/notifications/actions",
|
||||
expect.objectContaining({
|
||||
method: "POST",
|
||||
body: expect.stringContaining("task:need-input:1"),
|
||||
}),
|
||||
);
|
||||
expect(lastSyncRequestBody()).toMatchObject({
|
||||
notification_id: "task:need-input:1",
|
||||
action: "reply",
|
||||
status: "read",
|
||||
reply: "已补充客户编号",
|
||||
submitted_at: "2026-06-07T08:00:00.000Z",
|
||||
device_id: "device-001",
|
||||
});
|
||||
expect(Array.from(mockState.db?.events.values() ?? [])).toEqual(expect.arrayContaining([
|
||||
expect.objectContaining({ kind: "digital_workday_notification_action_submitted" }),
|
||||
]));
|
||||
});
|
||||
|
||||
it("records notification action submit failures without throwing", async () => {
|
||||
mockState.fetch.mockResolvedValue({
|
||||
ok: false,
|
||||
status: 500,
|
||||
text: vi.fn().mockResolvedValue(JSON.stringify({ message: "notification action rejected" })),
|
||||
} as unknown as Response);
|
||||
|
||||
const { submitDigitalEmployeeNotificationAction } = await import("./syncService");
|
||||
const result = await submitDigitalEmployeeNotificationAction({ notification_id: "task:need-input:1", action: "read" });
|
||||
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
endpoint: "https://manage.example.com/api/digital-employee/notifications/actions",
|
||||
submitted_at: "2026-06-07T08:00:00.000Z",
|
||||
error: "notification action rejected",
|
||||
});
|
||||
expect(Array.from(mockState.db?.events.values() ?? [])).toEqual(expect.arrayContaining([
|
||||
expect.objectContaining({ kind: "digital_workday_notification_action_submit_failed" }),
|
||||
]));
|
||||
});
|
||||
|
||||
it.each([
|
||||
["skillPolicies"],
|
||||
["policies"],
|
||||
|
||||
Reference in New Issue
Block a user