Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,182 @@
import type { TenantId } from "@suaveplan/api-types/tenant";
import type { BrokerEnvelope } from "@suaveplan/api-ws/broker";
import {
afterAll,
beforeAll,
describe,
expect,
it,
} from "@suaveplan/testing/runner";
import { RedisFixture } from "@suaveplan/testing-integration-redis";
import { createRedisWsBroker } from "../../src/broker/broker.js";
import {
bufferKey,
decodeBufferMember,
} from "../../src/event-buffer/event-buffer.js";
import { createRespClient } from "../../src/resp-client/resp-client.js";
const tenant = "tenant-it" as unknown as TenantId;
let fixture: RedisFixture | undefined;
let url: string | undefined;
let dockerAvailable = false;
beforeAll(async () => {
fixture = new RedisFixture();
try {
const config = await fixture.start();
url = config.url;
dockerAvailable = true;
} catch {
// Docker not available — every test below skips itself via dockerAvailable.
dockerAvailable = false;
}
}, 120_000);
afterAll(async () => {
if (fixture && dockerAvailable) {
await fixture.stop();
}
});
function skipIfNoDocker(): boolean {
if (!dockerAvailable) {
return true;
}
return false;
}
describe("Redis broker — real-container integration", () => {
it("publish from broker A is delivered to subscriber on broker B", {
timeout: 30_000,
}, async () => {
if (skipIfNoDocker()) return;
const brokerA = await createRedisWsBroker<{ id: number }>({
connection: url!,
hostId: "host-A",
channelPrefix: "it.broadcast.",
keyPrefix: "it.ws:",
});
const brokerB = await createRedisWsBroker<{ id: number }>({
connection: url!,
hostId: "host-B",
channelPrefix: "it.broadcast.",
keyPrefix: "it.ws:",
});
try {
const received: Array<BrokerEnvelope<{ id: number }>> = [];
await brokerB.subscribe("orders", (env) => {
received.push(env);
});
// Tiny delay so the SUBSCRIBE is established before the publish.
await new Promise<void>((r) => setTimeout(r, 100));
await brokerA.publish("orders", {
topic: "orders",
tenant,
payload: { id: 1 },
});
// Wait for fan-out
for (let i = 0; i < 50 && received.length === 0; i += 1) {
await new Promise<void>((r) => setTimeout(r, 100));
}
expect(received.length).toBe(1);
expect(received[0]!.payload.id).toBe(1);
expect(received[0]!.originHost).toBe("host-A");
} finally {
await brokerA.dispose();
await brokerB.dispose();
}
});
it("event buffer sorted-set: write, cap, and replay round-trip on a real Redis", {
timeout: 30_000,
}, async () => {
if (skipIfNoDocker()) return;
const broker = await createRedisWsBroker({
connection: url!,
channelPrefix: "it.broadcast2.",
keyPrefix: "it.ws2:",
eventBufferMaxEvents: 4,
eventBufferTtlSec: 60,
});
try {
for (let i = 1; i <= 6; i += 1) {
await broker.writeBuffer(tenant, "feed", {
eventId: i,
payload: new Uint8Array([i, i + 1]),
});
}
const events = await broker.readBuffer(tenant, "feed", 0);
// Cap = 4, so eventIds 3..6 survive.
expect(events.map((e) => e.eventId)).toEqual([3, 4, 5, 6]);
const tail = await broker.readBuffer(tenant, "feed", 4);
expect(tail.map((e) => e.eventId)).toEqual([5, 6]);
// Independently confirm via raw client that the TTL is set.
const raw = createRespClient({ url: url! });
await new Promise<void>((r) => setTimeout(r, 50));
const key = bufferKey("it.ws2:", tenant, "feed");
const card = await raw.zcard(key);
expect(card).toBe(4);
// Member format is "<id>|<base64>" — decodeBufferMember keeps payload intact.
const range = await raw.zrangebyscore(key, "-inf", "+inf");
expect(range.length).toBe(4);
const decoded = decodeBufferMember(range[0]!);
expect(decoded.eventId).toBe(3);
await raw.quit();
} finally {
await broker.dispose();
}
});
it("healthz reports up against a live container and surfaces resolved options", {
timeout: 30_000,
}, async () => {
if (skipIfNoDocker()) return;
const broker = await createRedisWsBroker({
connection: url!,
channelPrefix: "it.broadcast3.",
keyPrefix: "it.ws3:",
});
try {
expect(broker.healthz().status).toBe("up");
expect(broker.options.keyPrefix).toBe("it.ws3:");
expect(broker.options.channelPrefix).toBe("it.broadcast3.");
} finally {
await broker.dispose();
}
});
it("compressed payload round-trips end-to-end through real Redis", {
timeout: 30_000,
}, async () => {
if (skipIfNoDocker()) return;
const broker = await createRedisWsBroker<{ big: string }>({
connection: url!,
channelPrefix: "it.broadcast4.",
keyPrefix: "it.ws4:",
compressionThresholdBytes: 64,
});
try {
const received: Array<BrokerEnvelope<{ big: string }>> = [];
await broker.subscribe("compressed", (e) => {
received.push(e);
});
await new Promise<void>((r) => setTimeout(r, 100));
const big = "abcdef".repeat(2_000);
await broker.publish("compressed", {
topic: "compressed",
tenant,
payload: { big },
});
for (let i = 0; i < 50 && received.length === 0; i += 1) {
await new Promise<void>((r) => setTimeout(r, 100));
}
expect(received.length).toBe(1);
expect(received[0]!.payload.big).toBe(big);
} finally {
await broker.dispose();
}
});
});