吸收数字员工PlanStep本地软租约
This commit is contained in:
@@ -7,6 +7,8 @@ const mockState = vi.hoisted(() => ({
|
||||
finishedRuns: [] as Array<Record<string, unknown>>,
|
||||
dispatchEvents: [] as Array<Record<string, unknown>>,
|
||||
governanceCommands: [] as Array<Record<string, unknown>>,
|
||||
acquiredLeases: [] as Array<Record<string, unknown>>,
|
||||
releasedLeases: [] as Array<Record<string, unknown>>,
|
||||
settings: {
|
||||
enabled: true,
|
||||
catch_up_on_startup: true,
|
||||
@@ -54,6 +56,25 @@ vi.mock("./stateService", () => ({
|
||||
mockState.dispatchEvents.push(input);
|
||||
return { eventId: `dispatch-${input.status}`, kind: `plan_step_dispatch_${input.status}`, payload: input };
|
||||
},
|
||||
acquireDigitalEmployeePlanStepLease: (input: Record<string, unknown>) => {
|
||||
mockState.acquiredLeases.push(input);
|
||||
const candidate = mockState.readyCandidates.find((item) => item.stepId === input.stepId);
|
||||
if (candidate?.leaseActive || candidate?.skipReason === "lease_active") {
|
||||
return {
|
||||
ok: false,
|
||||
reason: "lease_active",
|
||||
lease: { lease_id: `lease-${input.stepId}`, state: "active", owner_device_id: "device-remote", acquired_at: "2026-06-07T00:59:00.000Z", expires_at: "2026-06-07T01:04:00.000Z" },
|
||||
};
|
||||
}
|
||||
return {
|
||||
ok: true,
|
||||
lease: { lease_id: `lease-${input.stepId}`, state: "active", owner_device_id: "device-001", acquired_at: input.occurredAt, expires_at: "2026-06-07T01:05:10.000Z" },
|
||||
};
|
||||
},
|
||||
releaseDigitalEmployeePlanStepLease: (input: Record<string, unknown>) => {
|
||||
mockState.releasedLeases.push(input);
|
||||
return { ok: true, lease: { lease_id: input.leaseId, state: "released", owner_device_id: "device-001", acquired_at: "2026-06-07T01:00:10.000Z", expires_at: "2026-06-07T01:05:10.000Z", released_at: input.occurredAt, release_reason: input.reason } };
|
||||
},
|
||||
recordDigitalEmployeeGovernanceCommand: (input: Record<string, unknown>) => {
|
||||
mockState.governanceCommands.push(input);
|
||||
return {
|
||||
@@ -72,6 +93,8 @@ describe("digital employee scheduler service", () => {
|
||||
mockState.finishedRuns = [];
|
||||
mockState.dispatchEvents = [];
|
||||
mockState.governanceCommands = [];
|
||||
mockState.acquiredLeases = [];
|
||||
mockState.releasedLeases = [];
|
||||
mockState.dispatchPolicy = {
|
||||
enabled: true,
|
||||
max_per_sweep: 3,
|
||||
@@ -156,6 +179,8 @@ describe("digital employee scheduler service", () => {
|
||||
expect.objectContaining({ method: "POST" }),
|
||||
);
|
||||
expect(mockState.dispatchEvents.map((event) => event.status)).toEqual(["started", "completed"]);
|
||||
expect(mockState.acquiredLeases).toEqual([expect.objectContaining({ stepId: "step-ready", triggerSource: "auto" })]);
|
||||
expect(mockState.releasedLeases).toEqual([expect.objectContaining({ stepId: "step-ready", leaseId: "lease-step-ready", reason: "completed" })]);
|
||||
expect(mockState.governanceCommands[0]).toMatchObject({
|
||||
commandKind: "start_run",
|
||||
planId: "plan-1",
|
||||
@@ -179,9 +204,24 @@ describe("digital employee scheduler service", () => {
|
||||
expect(result.readyStepDispatch).toMatchObject({ candidates: 1, dispatched: 0, failed: 1 });
|
||||
expect(result.readyStepDispatch.errors[0]).toMatchObject({ stepId: "step-ready", error: "ComputerServer 502: bad gateway" });
|
||||
expect(mockState.dispatchEvents.map((event) => event.status)).toEqual(["started", "failed"]);
|
||||
expect(mockState.releasedLeases).toEqual([expect.objectContaining({ stepId: "step-ready", leaseId: "lease-step-ready", reason: "failed" })]);
|
||||
expect(mockState.governanceCommands).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("skips ready plan steps with active leases", async () => {
|
||||
mockState.schedules = [];
|
||||
mockState.readyCandidates = [readyStepCandidate({ dispatchable: false, skipReason: "lease_active", leaseActive: true })];
|
||||
const { runDigitalEmployeeSchedulerCheck } = await import("./schedulerService");
|
||||
|
||||
const result = await runDigitalEmployeeSchedulerCheck(new Date("2026-06-07T01:00:10.000Z"));
|
||||
|
||||
expect(result.readyStepDispatch).toMatchObject({ candidates: 1, dispatched: 0, skipped: 1, failed: 0 });
|
||||
expect(fetch).not.toHaveBeenCalled();
|
||||
expect(mockState.acquiredLeases).toEqual([expect.objectContaining({ stepId: "step-ready", triggerSource: "auto" })]);
|
||||
expect(mockState.releasedLeases).toHaveLength(0);
|
||||
expect(mockState.dispatchEvents).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("honors disabled ready step dispatch policy", async () => {
|
||||
mockState.readyCandidates = [readyStepCandidate()];
|
||||
mockState.dispatchPolicy = { ...mockState.dispatchPolicy, enabled: false };
|
||||
|
||||
Reference in New Issue
Block a user