吸收数字员工PlanStep管理端强租约
This commit is contained in:
@@ -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({
|
||||
|
||||
Reference in New Issue
Block a user