吸收数字员工PlanStep管理端强租约

This commit is contained in:
baiyanyun
2026-06-10 23:59:54 +08:00
parent c400367d22
commit 751ce45619
12 changed files with 737 additions and 37 deletions

View File

@@ -137,6 +137,93 @@ describe("digital employee sync service", () => {
});
});
it("acquires a remote plan step dispatch lease through the management endpoint", async () => {
mockState.fetch.mockResolvedValue(jsonResponse({
ok: true,
lease: {
lease_id: "remote-lease-1",
owner_device_id: "device-001",
acquired_at: "2026-06-07T08:00:00.000Z",
expires_at: "2026-06-07T08:05:00.000Z",
},
}));
const { acquireDigitalEmployeePlanStepRemoteLease } = await import("./syncService");
const result = await acquireDigitalEmployeePlanStepRemoteLease({
planId: "plan-1",
stepId: "step-1",
taskId: "task-1",
triggerSource: "auto",
ttlMs: 300_000,
occurredAt: "2026-06-07T08:00:00.000Z",
});
expect(result).toMatchObject({
ok: true,
endpoint: "https://manage.example.com/api/digital-employee/leases/plan-step-dispatch/acquire",
lease: {
lease_id: "remote-lease-1",
remote_lease_id: "remote-lease-1",
owner_device_id: "device-001",
},
});
expect(mockState.fetch).toHaveBeenCalledWith(
"https://manage.example.com/api/digital-employee/leases/plan-step-dispatch/acquire",
expect.objectContaining({
method: "POST",
headers: expect.objectContaining({
Authorization: "Bearer login-token-001",
"X-Qiming-Client-Key": "client-key-001",
"X-Qiming-Device-Id": "device-001",
}),
body: expect.stringContaining('"step_id":"step-1"'),
}),
);
});
it("returns a remote lease rejection without falling back", async () => {
mockState.fetch.mockResolvedValue(jsonResponse({ ok: false, reason: "locked_by_other_device" }));
const { acquireDigitalEmployeePlanStepRemoteLease } = await import("./syncService");
const result = await acquireDigitalEmployeePlanStepRemoteLease({ stepId: "step-1", occurredAt: "2026-06-07T08:00:00.000Z" });
expect(result).toMatchObject({ ok: false, rejected: true, reason: "locked_by_other_device" });
});
it("falls back when remote plan step dispatch lease acquire fails", async () => {
mockState.fetch.mockResolvedValue({
ok: false,
status: 503,
text: vi.fn().mockResolvedValue(JSON.stringify({ message: "lease service unavailable" })),
} as unknown as Response);
const { acquireDigitalEmployeePlanStepRemoteLease } = await import("./syncService");
const result = await acquireDigitalEmployeePlanStepRemoteLease({ stepId: "step-1", occurredAt: "2026-06-07T08:00:00.000Z" });
expect(result).toMatchObject({ ok: false, fallback: true, error: "lease service unavailable" });
});
it("reports remote release failures without throwing", async () => {
mockState.fetch.mockResolvedValue({
ok: false,
status: 500,
text: vi.fn().mockResolvedValue(JSON.stringify({ message: "release failed" })),
} as unknown as Response);
const { releaseDigitalEmployeePlanStepRemoteLease } = await import("./syncService");
const result = await releaseDigitalEmployeePlanStepRemoteLease({
planId: "plan-1",
stepId: "step-1",
taskId: "task-1",
leaseId: "remote-lease-1",
remoteLeaseId: "remote-lease-1",
reason: "completed",
occurredAt: "2026-06-07T08:01:00.000Z",
});
expect(result).toMatchObject({ ok: false, endpoint: "https://manage.example.com/api/digital-employee/leases/plan-step-dispatch/release", error: "release failed" });
});
it("marks entity-only acknowledgements as synced", async () => {
insertPlan("plan-1");
insertOutbox("plan:upsert:plan-1", "plan", "plan-1");
@@ -659,6 +746,9 @@ describe("digital employee sync service", () => {
lease_state: "released",
lease_owner_device_id: "device-001",
lease_expires_at: "2026-06-07T08:04:00.000Z",
lease_source: "remote",
remote_lease_id: "remote-lease-step-1",
remote_release_error: "release failed",
reason: "completed",
lease: {
lease_id: "lease-step-1",
@@ -668,6 +758,9 @@ describe("digital employee sync service", () => {
expires_at: "2026-06-07T08:04:00.000Z",
released_at: "2026-06-07T08:01:00.000Z",
release_reason: "completed",
source: "remote",
remote_lease_id: "remote-lease-step-1",
remote_release_error: "release failed",
},
});
insertEventEntity("event-plan-step-ready", "plan_step_ready", {
@@ -937,6 +1030,10 @@ describe("digital employee sync service", () => {
lease_expires_at: "2026-06-07T08:04:00.000Z",
lease_released_at: "2026-06-07T08:01:00.000Z",
lease_release_reason: "completed",
lease_source: "remote",
remote_lease_id: "remote-lease-step-1",
remote_release_error: "release failed",
remote_lease_rejected: false,
reason: "completed",
});
expect(businessView("event-plan-step-ready")).toMatchObject({