Files
openclaw/src/plugin-sdk/delivery-queue-runtime.test.ts
T
Peter Steinberger ad6bc6d3ae fix(delivery): keep failed queue rows payload-free (#123642)
* refactor(delivery): collapse failed-row lifecycle

Replace the unshipped failure-operations platform with payload-free terminal receipts owned by existing queue boundaries. Keep bounded/permanent idempotency only for reusable or crash-ambiguous producers, move physical expiry to queue maintenance, and preserve migration and media-cleanup safety.\n\nTogether with #123410, production code is net negative by 11 lines; tests, docs, and generated protocol mirrors are accounted separately.

* fix(delivery): break state DB import cycle

* fix(delivery): classify SQLite boundary uses

* test(gateway): mark retained health fixture
2026-08-14 06:22:51 -07:00

173 lines
5.6 KiB
TypeScript

/**
* Tests delivery queue runtime ordering and retry behavior.
*/
import { expectDefined } from "@openclaw/normalization-core";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import {
getActiveGatewayRootWorkCount,
resetGatewayWorkAdmission,
tryBeginGatewaySuspendAdmission,
} from "../process/gateway-work-admission.js";
import { runWithGatewayRootWorkAdmissionForTest } from "../process/gateway-work-admission.test-helpers.js";
const mocks = vi.hoisted(() => ({
coreDrainPendingDeliveries: vi.fn(async () => {}),
deliverOutboundPayloads: vi.fn(async () => []),
deliverRuntimeModuleLoads: 0,
}));
vi.mock("../infra/outbound/delivery-queue-recovery.js", () => ({
drainPendingDeliveriesCore: mocks.coreDrainPendingDeliveries,
}));
vi.mock("../infra/outbound/deliver-runtime.js", () => {
mocks.deliverRuntimeModuleLoads += 1;
return {
deliverOutboundPayloads: mocks.deliverOutboundPayloads,
deliverOutboundPayloadsInternal: mocks.deliverOutboundPayloads,
};
});
type DeliveryQueueRuntimeModule = typeof import("./delivery-queue-runtime.js");
let drainPendingDeliveries: DeliveryQueueRuntimeModule["drainPendingDeliveries"];
const log = {
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
};
beforeAll(async () => {
({ drainPendingDeliveries } = await import("./delivery-queue-runtime.js"));
});
beforeEach(() => {
resetGatewayWorkAdmission();
mocks.coreDrainPendingDeliveries.mockReset().mockResolvedValue(undefined);
mocks.deliverOutboundPayloads.mockReset().mockResolvedValue([]);
log.info.mockClear();
log.warn.mockClear();
log.error.mockClear();
});
afterEach(resetGatewayWorkAdmission);
describe("plugin-sdk delivery queue drainPendingDeliveries", () => {
it("defers lazy runtime resolution and core draining while suspension is prepared", async () => {
const suspension = tryBeginGatewaySuspendAdmission(() => {});
expect(suspension?.commit()).toBe(true);
const pending = drainPendingDeliveries({
drainKey: "demo:test",
logLabel: "Demo reconnect drain",
cfg: {},
log,
selectEntry: () => ({ match: false }),
});
await Promise.resolve();
expect(mocks.deliverRuntimeModuleLoads).toBe(0);
expect(mocks.coreDrainPendingDeliveries).not.toHaveBeenCalled();
expect(suspension?.release()).toBe(true);
await pending;
expect(mocks.deliverRuntimeModuleLoads).toBe(1);
expect(mocks.coreDrainPendingDeliveries).toHaveBeenCalledOnce();
expect(getActiveGatewayRootWorkCount()).toBe(0);
});
it("counts a reconnect drain independently from its admitted parent", async () => {
let finishDrain = () => {};
const drainStarted = new Promise<void>((resolveStarted) => {
mocks.coreDrainPendingDeliveries.mockImplementationOnce(
() =>
new Promise<void>((resolveDrain) => {
finishDrain = resolveDrain;
resolveStarted();
}),
);
});
const deliver = vi.fn(async () => []);
await runWithGatewayRootWorkAdmissionForTest(async () => {
expect(getActiveGatewayRootWorkCount()).toBe(1);
const pending = drainPendingDeliveries({
drainKey: "demo:test",
logLabel: "Demo reconnect drain",
cfg: {},
log,
deliver,
selectEntry: () => ({ match: false }),
});
await drainStarted;
expect(getActiveGatewayRootWorkCount()).toBe(2);
finishDrain();
await pending;
expect(getActiveGatewayRootWorkCount()).toBe(1);
});
expect(getActiveGatewayRootWorkCount()).toBe(0);
});
it("releases its independent root when the core drain rejects", async () => {
mocks.coreDrainPendingDeliveries.mockImplementationOnce(async () => {
expect(getActiveGatewayRootWorkCount()).toBe(1);
throw new Error("drain failed");
});
await expect(
drainPendingDeliveries({
drainKey: "demo:test",
logLabel: "Demo reconnect drain",
cfg: {},
log,
deliver: vi.fn(async () => []),
selectEntry: () => ({ match: false }),
}),
).rejects.toThrow("drain failed");
expect(getActiveGatewayRootWorkCount()).toBe(0);
});
it("injects the lazy outbound deliver runtime when no deliver fn is provided", async () => {
await drainPendingDeliveries({
drainKey: "demo:test",
logLabel: "Demo reconnect drain",
cfg: {},
log,
selectEntry: () => ({ match: false }),
});
expect(mocks.coreDrainPendingDeliveries).toHaveBeenCalledTimes(1);
const [{ deliver: lazyDeliver }] = expectDefined(
(mocks.coreDrainPendingDeliveries.mock.calls as unknown as Array<[{ deliver?: unknown }]>)[0],
"(mocks.coreDrainPendingDeliveries.mock.calls as unknown as Array<[{ deliver?: unknown }]>)[0] test invariant",
);
expect(lazyDeliver).toBe(mocks.deliverOutboundPayloads);
});
it("preserves an explicit deliver fn without loading the lazy runtime", async () => {
const deliver = vi.fn(async () => []);
await drainPendingDeliveries({
drainKey: "demo:test",
logLabel: "Demo reconnect drain",
cfg: {},
log,
deliver,
selectEntry: () => ({ match: false }),
});
expect(mocks.coreDrainPendingDeliveries).toHaveBeenCalledTimes(1);
const [{ deliver: explicitDeliver }] = expectDefined(
(mocks.coreDrainPendingDeliveries.mock.calls as unknown as Array<[{ deliver?: unknown }]>)[0],
"(mocks.coreDrainPendingDeliveries.mock.calls as unknown as Array<[{ deliver?: unknown }]>)[0] test invariant",
);
expect(explicitDeliver).toBe(deliver);
expect(mocks.deliverOutboundPayloads).not.toHaveBeenCalled();
});
});