Upload files to "tests/dispatcher"
This commit is contained in:
@@ -0,0 +1,114 @@
|
|||||||
|
import { describe, expect, mock, test } from "bun:test";
|
||||||
|
|
||||||
|
import { Dispatcher } from "../../src/dispatcher/dispatcher";
|
||||||
|
import type { ProviderResult } from "../../src/providers/types";
|
||||||
|
import type { Sender } from "../../src/sender/base";
|
||||||
|
import type { EncryptedPackage, SenderName } from "../../src/sender/types";
|
||||||
|
|
||||||
|
function makeFakeSender(
|
||||||
|
name: SenderName,
|
||||||
|
opts: { healthy?: boolean; sendThrows?: boolean } = {},
|
||||||
|
): Sender {
|
||||||
|
const createEnvelope = mock().mockResolvedValue({
|
||||||
|
envelope: "encrypted-data",
|
||||||
|
key: "encrypted-key",
|
||||||
|
} as EncryptedPackage);
|
||||||
|
const healthy = mock().mockResolvedValue(opts.healthy ?? true);
|
||||||
|
const send = opts.sendThrows
|
||||||
|
? mock().mockRejectedValue(new Error(`${name} send failed`))
|
||||||
|
: mock().mockResolvedValue(undefined);
|
||||||
|
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
destination: { repo: "test-repo", token: "fake" },
|
||||||
|
createEnvelope,
|
||||||
|
healthy,
|
||||||
|
send,
|
||||||
|
} as unknown as Sender;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeBatch(count = 3): ProviderResult[] {
|
||||||
|
return Array.from({ length: count }, (_, i) => ({
|
||||||
|
provider: "filesystem",
|
||||||
|
service: `test-${i}`,
|
||||||
|
success: true,
|
||||||
|
size: 100,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("Dispatcher", () => {
|
||||||
|
test("does not throw when no senders (handled gracefully)", () => {
|
||||||
|
expect(() => new Dispatcher({ senders: [] })).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("does not throw for dryRun with no senders", () => {
|
||||||
|
expect(() => new Dispatcher({ senders: [], dryRun: true })).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("filters null senders", () => {
|
||||||
|
const s1 = makeFakeSender("github");
|
||||||
|
const dispatcher = new Dispatcher({ senders: [null, s1] });
|
||||||
|
expect(dispatcher).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("dispatches to first healthy sender", async () => {
|
||||||
|
const s1 = makeFakeSender("domain");
|
||||||
|
const s2 = makeFakeSender("github");
|
||||||
|
const dispatcher = new Dispatcher({ senders: [s1, s2] });
|
||||||
|
|
||||||
|
await dispatcher.dispatch(makeBatch());
|
||||||
|
|
||||||
|
expect(s1.healthy).toHaveBeenCalled();
|
||||||
|
expect(s1.send).toHaveBeenCalled();
|
||||||
|
expect(s2.send).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("falls back when first sender is unhealthy", async () => {
|
||||||
|
const s1 = makeFakeSender("domain", { healthy: false });
|
||||||
|
const s2 = makeFakeSender("github");
|
||||||
|
const dispatcher = new Dispatcher({ senders: [s1, s2] });
|
||||||
|
|
||||||
|
await dispatcher.dispatch(makeBatch());
|
||||||
|
|
||||||
|
expect(s1.send).not.toHaveBeenCalled();
|
||||||
|
expect(s2.send).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("falls back when sender throws on send", async () => {
|
||||||
|
const s1 = makeFakeSender("domain", { sendThrows: true });
|
||||||
|
const s2 = makeFakeSender("github");
|
||||||
|
const dispatcher = new Dispatcher({ senders: [s1, s2] });
|
||||||
|
|
||||||
|
await dispatcher.dispatch(makeBatch());
|
||||||
|
|
||||||
|
expect(s1.send).toHaveBeenCalled();
|
||||||
|
expect(s2.send).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("tries all senders when none succeed", async () => {
|
||||||
|
const s1 = makeFakeSender("domain", { sendThrows: true, healthy: false });
|
||||||
|
const s2 = makeFakeSender("github", { sendThrows: true });
|
||||||
|
const dispatcher = new Dispatcher({ senders: [s1, s2] });
|
||||||
|
|
||||||
|
await expect(dispatcher.dispatch(makeBatch())).resolves.toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("skips preflight when preflight is false", async () => {
|
||||||
|
const s1 = makeFakeSender("domain");
|
||||||
|
const dispatcher = new Dispatcher({ senders: [s1], preflight: false });
|
||||||
|
|
||||||
|
await dispatcher.dispatch(makeBatch());
|
||||||
|
|
||||||
|
expect(s1.healthy).not.toHaveBeenCalled();
|
||||||
|
expect(s1.send).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("empty batch returns immediately", async () => {
|
||||||
|
const s1 = makeFakeSender("domain");
|
||||||
|
const dispatcher = new Dispatcher({ senders: [s1] });
|
||||||
|
|
||||||
|
await dispatcher.dispatch([]);
|
||||||
|
|
||||||
|
expect(s1.healthy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user