mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-23 02:45:38 -06:00
test(buzz): consolidate lifecycle fixtures (#118316)
This commit is contained in:
committed by
GitHub
parent
1fedcca9a9
commit
628672aaa1
@@ -125,11 +125,41 @@ const CHANNEL_ID = "7c4a6d2a-2ed9-4b4e-a5e2-4d705ee9b34c";
|
||||
const SECOND_CHANNEL_ID = "45cedd86-f853-45b7-8fea-812b7fe63d7a";
|
||||
const BOT_PUBLIC_KEY = getPublicKey(Uint8Array.from(Buffer.from(PRIVATE_KEY, "hex")));
|
||||
const SENDER_PUBLIC_KEY = getPublicKey(Uint8Array.from(Buffer.from(SENDER_PRIVATE_KEY, "hex")));
|
||||
const SENDER_SECRET_KEY = Uint8Array.from(Buffer.from(SENDER_PRIVATE_KEY, "hex"));
|
||||
const RELAY_PUBLIC_KEY = "f".repeat(64);
|
||||
const tempDirs = new Set<string>();
|
||||
let previousStateDir: string | undefined;
|
||||
let stateDir: string;
|
||||
|
||||
function startTestBus(
|
||||
overrides: Partial<Parameters<typeof startBuzzBus>[0]> = {},
|
||||
): Promise<BuzzBus> {
|
||||
return startBuzzBus({
|
||||
accountId: ACCOUNT_ID,
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
channelIds: [CHANNEL_ID],
|
||||
onMessage: async () => {},
|
||||
...overrides,
|
||||
});
|
||||
}
|
||||
|
||||
function sendTestTextOneShot(
|
||||
overrides: Partial<Parameters<typeof sendBuzzTextOneShot>[0]> = {},
|
||||
): Promise<string> {
|
||||
return sendBuzzTextOneShot({
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
channelId: CHANNEL_ID,
|
||||
text: "hello",
|
||||
...overrides,
|
||||
});
|
||||
}
|
||||
|
||||
function signSenderEvent(template: Parameters<typeof finalizeEvent>[0]): Event {
|
||||
return finalizeEvent(template, SENDER_SECRET_KEY);
|
||||
}
|
||||
|
||||
function subscriptionIncludesKind(
|
||||
subscription: (typeof relayMocks.subscriptions)[number],
|
||||
kind: number,
|
||||
@@ -204,12 +234,8 @@ describe("Buzz bus lifecycle", () => {
|
||||
|
||||
it("rejects an over-capacity room set before opening the relay", async () => {
|
||||
await expect(
|
||||
startBuzzBus({
|
||||
accountId: ACCOUNT_ID,
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
startTestBus({
|
||||
channelIds: Array.from({ length: 1_021 }, (_, index) => `room-${index}`),
|
||||
onMessage: async () => {},
|
||||
}),
|
||||
).rejects.toThrow("Buzz supports at most 1020 configured rooms per account");
|
||||
|
||||
@@ -231,15 +257,7 @@ describe("Buzz bus lifecycle", () => {
|
||||
),
|
||||
);
|
||||
|
||||
await expect(
|
||||
startBuzzBus({
|
||||
accountId: ACCOUNT_ID,
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
channelIds: [CHANNEL_ID],
|
||||
onMessage: async () => {},
|
||||
}),
|
||||
).rejects.toThrow("auth rejected");
|
||||
await expect(startTestBus()).rejects.toThrow("auth rejected");
|
||||
|
||||
expect(relayMocks.connect).toHaveBeenCalledOnce();
|
||||
expect(relayMocks.close).toHaveBeenCalledOnce();
|
||||
@@ -262,13 +280,7 @@ describe("Buzz bus lifecycle", () => {
|
||||
}),
|
||||
),
|
||||
);
|
||||
const start = startBuzzBus({
|
||||
accountId: ACCOUNT_ID,
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
channelIds: [CHANNEL_ID],
|
||||
onMessage: async () => {},
|
||||
});
|
||||
const start = startTestBus();
|
||||
const rejection = expect(start).rejects.toThrow("Timed out setting up Buzz relay session");
|
||||
|
||||
await vi.advanceTimersByTimeAsync(20_000);
|
||||
@@ -282,11 +294,7 @@ describe("Buzz bus lifecycle", () => {
|
||||
it("publishes and closes a standalone authenticated send", async () => {
|
||||
relayMocks.auth.mockResolvedValue("ok");
|
||||
|
||||
const messageId = await sendBuzzTextOneShot({
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
channelId: CHANNEL_ID,
|
||||
text: "hello",
|
||||
const messageId = await sendTestTextOneShot({
|
||||
threadId: "root-id",
|
||||
replyToId: "parent-id",
|
||||
});
|
||||
@@ -307,13 +315,7 @@ describe("Buzz bus lifecycle", () => {
|
||||
|
||||
it("sends room and thread typing without waiting for a relay acknowledgement", async () => {
|
||||
relayMocks.auth.mockResolvedValue("ok");
|
||||
const bus = await startBuzzBus({
|
||||
accountId: ACCOUNT_ID,
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
channelIds: [CHANNEL_ID],
|
||||
onMessage: async () => {},
|
||||
});
|
||||
const bus = await startTestBus();
|
||||
|
||||
await bus.sendTyping({
|
||||
channelId: CHANNEL_ID,
|
||||
@@ -346,13 +348,7 @@ describe("Buzz bus lifecycle", () => {
|
||||
|
||||
it("drops typing while the active relay is disconnected", async () => {
|
||||
relayMocks.auth.mockResolvedValue("ok");
|
||||
const bus = await startBuzzBus({
|
||||
accountId: ACCOUNT_ID,
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
channelIds: [CHANNEL_ID],
|
||||
onMessage: async () => {},
|
||||
});
|
||||
const bus = await startTestBus();
|
||||
relayMocks.connected = false;
|
||||
relayMocks.send.mockClear();
|
||||
|
||||
@@ -374,12 +370,8 @@ describe("Buzz bus lifecycle", () => {
|
||||
],
|
||||
});
|
||||
|
||||
const bus = await startBuzzBus({
|
||||
accountId: ACCOUNT_ID,
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
const bus = await startTestBus({
|
||||
channelIds: [CHANNEL_ID, SECOND_CHANNEL_ID],
|
||||
onMessage: async () => {},
|
||||
});
|
||||
|
||||
expect(relayMocks.subscriptions[0]?.filter.kinds).toEqual([39_000]);
|
||||
@@ -415,23 +407,17 @@ describe("Buzz bus lifecycle", () => {
|
||||
],
|
||||
});
|
||||
relayMocks.roomHistoryEvents = [
|
||||
finalizeEvent(
|
||||
{
|
||||
kind: 9,
|
||||
created_at: 1_700_000_000,
|
||||
content: "historical message",
|
||||
tags: [["h", CHANNEL_ID]],
|
||||
},
|
||||
Uint8Array.from(Buffer.from(SENDER_PRIVATE_KEY, "hex")),
|
||||
),
|
||||
signSenderEvent({
|
||||
kind: 9,
|
||||
created_at: 1_700_000_000,
|
||||
content: "historical message",
|
||||
tags: [["h", CHANNEL_ID]],
|
||||
}),
|
||||
];
|
||||
relayMocks.stallRoomEoseChannelId = SECOND_CHANNEL_ID;
|
||||
const onMessage = vi.fn(async (_message: BuzzInboundMessage) => {});
|
||||
|
||||
const start = startBuzzBus({
|
||||
accountId: ACCOUNT_ID,
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
const start = startTestBus({
|
||||
channelIds: [CHANNEL_ID, SECOND_CHANNEL_ID],
|
||||
onMessage,
|
||||
});
|
||||
@@ -465,11 +451,7 @@ describe("Buzz bus lifecycle", () => {
|
||||
});
|
||||
const onFatalError = vi.fn();
|
||||
|
||||
const bus = await startBuzzBus({
|
||||
accountId: ACCOUNT_ID,
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
channelIds: [CHANNEL_ID],
|
||||
const bus = await startTestBus({
|
||||
onMessage,
|
||||
onFatalError,
|
||||
});
|
||||
@@ -524,11 +506,7 @@ describe("Buzz bus lifecycle", () => {
|
||||
});
|
||||
},
|
||||
);
|
||||
const bus = await startBuzzBus({
|
||||
accountId: ACCOUNT_ID,
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
channelIds: [CHANNEL_ID],
|
||||
const bus = await startTestBus({
|
||||
onMessage,
|
||||
});
|
||||
|
||||
@@ -543,13 +521,7 @@ describe("Buzz bus lifecycle", () => {
|
||||
|
||||
it("leaves room subscription shutdown to the relay", async () => {
|
||||
relayMocks.auth.mockResolvedValue("ok");
|
||||
const bus = await startBuzzBus({
|
||||
accountId: ACCOUNT_ID,
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
channelIds: [CHANNEL_ID],
|
||||
onMessage: async () => {},
|
||||
});
|
||||
const bus = await startTestBus();
|
||||
const roomSubscription = relayMocks.subscriptions.find((entry) =>
|
||||
subscriptionIncludesKind(entry, 9),
|
||||
);
|
||||
@@ -577,12 +549,7 @@ describe("Buzz bus lifecycle", () => {
|
||||
],
|
||||
},
|
||||
];
|
||||
const bus = await startBuzzBus({
|
||||
accountId: ACCOUNT_ID,
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
channelIds: [CHANNEL_ID],
|
||||
onMessage: async () => {},
|
||||
const bus = await startTestBus({
|
||||
onRoomDirectoryChanged,
|
||||
});
|
||||
await vi.waitFor(() => expect(bus.directory.listGroups({})[0]?.name).toBe("Engineering"));
|
||||
@@ -664,12 +631,7 @@ describe("Buzz bus lifecycle", () => {
|
||||
};
|
||||
const onFatalError = vi.fn();
|
||||
|
||||
const bus = await startBuzzBus({
|
||||
accountId: ACCOUNT_ID,
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
channelIds: [CHANNEL_ID],
|
||||
onMessage: async () => {},
|
||||
const bus = await startTestBus({
|
||||
onFatalError,
|
||||
});
|
||||
|
||||
@@ -686,18 +648,15 @@ describe("Buzz bus lifecycle", () => {
|
||||
it("loads room metadata and current member profiles on the active bus", async () => {
|
||||
relayMocks.auth.mockResolvedValue("ok");
|
||||
relayMocks.profileEvents = [
|
||||
finalizeEvent(
|
||||
{
|
||||
kind: 0,
|
||||
created_at: 1_700_000_000,
|
||||
content: JSON.stringify({
|
||||
display_name: "Alice",
|
||||
picture: "https://example.com/alice.png",
|
||||
}),
|
||||
tags: [],
|
||||
},
|
||||
Uint8Array.from(Buffer.from(SENDER_PRIVATE_KEY, "hex")),
|
||||
),
|
||||
signSenderEvent({
|
||||
kind: 0,
|
||||
created_at: 1_700_000_000,
|
||||
content: JSON.stringify({
|
||||
display_name: "Alice",
|
||||
picture: "https://example.com/alice.png",
|
||||
}),
|
||||
tags: [],
|
||||
}),
|
||||
];
|
||||
relayMocks.roomMetadataEvents = [
|
||||
{
|
||||
@@ -714,13 +673,8 @@ describe("Buzz bus lifecycle", () => {
|
||||
},
|
||||
];
|
||||
|
||||
const bus = await startBuzzBus({
|
||||
accountId: ACCOUNT_ID,
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
channelIds: [CHANNEL_ID],
|
||||
const bus = await startTestBus({
|
||||
profileName: "OpenClaw",
|
||||
onMessage: async () => {},
|
||||
});
|
||||
|
||||
await vi.waitFor(() =>
|
||||
@@ -764,13 +718,7 @@ describe("Buzz bus lifecycle", () => {
|
||||
Uint8Array.from(Buffer.from(joinedPrivateKey, "hex")),
|
||||
),
|
||||
];
|
||||
const bus = await startBuzzBus({
|
||||
accountId: ACCOUNT_ID,
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
channelIds: [CHANNEL_ID],
|
||||
onMessage: async () => {},
|
||||
});
|
||||
const bus = await startTestBus();
|
||||
expect(bus.directory.listPeers({}).map((entry) => entry.id)).not.toContain(joinedPublicKey);
|
||||
|
||||
relayMocks.membershipEvents = [
|
||||
@@ -815,14 +763,7 @@ describe("Buzz bus lifecycle", () => {
|
||||
relayMocks.auth.mockResolvedValue("ok");
|
||||
relayMocks.publish.mockRejectedValue(new Error("rejected"));
|
||||
|
||||
await expect(
|
||||
sendBuzzTextOneShot({
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
channelId: CHANNEL_ID,
|
||||
text: "hello",
|
||||
}),
|
||||
).rejects.toThrow("rejected");
|
||||
await expect(sendTestTextOneShot()).rejects.toThrow("rejected");
|
||||
|
||||
expect(relayMocks.close).toHaveBeenCalledOnce();
|
||||
});
|
||||
@@ -830,22 +771,13 @@ describe("Buzz bus lifecycle", () => {
|
||||
it("deduplicates replayed relay events by event id", async () => {
|
||||
relayMocks.auth.mockResolvedValue("ok");
|
||||
const onMessage = vi.fn(async (_message: BuzzInboundMessage) => {});
|
||||
const bus = await startBuzzBus({
|
||||
accountId: ACCOUNT_ID,
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
channelIds: [CHANNEL_ID],
|
||||
onMessage,
|
||||
const bus = await startTestBus({ onMessage });
|
||||
const event = signSenderEvent({
|
||||
kind: 9,
|
||||
created_at: 1_700_000_000,
|
||||
content: "hello",
|
||||
tags: [["h", CHANNEL_ID]],
|
||||
});
|
||||
const event = finalizeEvent(
|
||||
{
|
||||
kind: 9,
|
||||
created_at: 1_700_000_000,
|
||||
content: "hello",
|
||||
tags: [["h", CHANNEL_ID]],
|
||||
},
|
||||
Uint8Array.from(Buffer.from(SENDER_PRIVATE_KEY, "hex")),
|
||||
);
|
||||
|
||||
const messageSubscription = relayMocks.subscriptions.find((entry) =>
|
||||
subscriptionIncludesKind(entry, 9),
|
||||
@@ -863,13 +795,7 @@ describe("Buzz bus lifecycle", () => {
|
||||
const onMessage = vi.fn(async (message: BuzzInboundMessage) => {
|
||||
receivedKinds.push(message.kind);
|
||||
});
|
||||
const bus = await startBuzzBus({
|
||||
accountId: ACCOUNT_ID,
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
channelIds: [CHANNEL_ID],
|
||||
onMessage,
|
||||
});
|
||||
const bus = await startTestBus({ onMessage });
|
||||
const messageSubscription = relayMocks.subscriptions.find((entry) =>
|
||||
subscriptionIncludesKind(entry, 9),
|
||||
);
|
||||
@@ -877,28 +803,22 @@ describe("Buzz bus lifecycle", () => {
|
||||
[...BUZZ_INBOUND_MESSAGE_KINDS],
|
||||
);
|
||||
|
||||
const richEvent = finalizeEvent(
|
||||
{
|
||||
kind: BUZZ_RICH_MESSAGE_KIND,
|
||||
created_at: 1_700_000_000,
|
||||
content: "**rich**",
|
||||
tags: [["h", CHANNEL_ID]],
|
||||
},
|
||||
Uint8Array.from(Buffer.from(SENDER_PRIVATE_KEY, "hex")),
|
||||
);
|
||||
const diffEvent = finalizeEvent(
|
||||
{
|
||||
kind: BUZZ_DIFF_MESSAGE_KIND,
|
||||
created_at: 1_700_000_001,
|
||||
content: "@@ -1 +1 @@\n-old\n+new",
|
||||
tags: [
|
||||
["h", CHANNEL_ID],
|
||||
["repo", "https://github.com/openclaw/openclaw"],
|
||||
["commit", "abcdef1"],
|
||||
],
|
||||
},
|
||||
Uint8Array.from(Buffer.from(SENDER_PRIVATE_KEY, "hex")),
|
||||
);
|
||||
const richEvent = signSenderEvent({
|
||||
kind: BUZZ_RICH_MESSAGE_KIND,
|
||||
created_at: 1_700_000_000,
|
||||
content: "**rich**",
|
||||
tags: [["h", CHANNEL_ID]],
|
||||
});
|
||||
const diffEvent = signSenderEvent({
|
||||
kind: BUZZ_DIFF_MESSAGE_KIND,
|
||||
created_at: 1_700_000_001,
|
||||
content: "@@ -1 +1 @@\n-old\n+new",
|
||||
tags: [
|
||||
["h", CHANNEL_ID],
|
||||
["repo", "https://github.com/openclaw/openclaw"],
|
||||
["commit", "abcdef1"],
|
||||
],
|
||||
});
|
||||
|
||||
messageSubscription?.handlers.onevent(richEvent);
|
||||
messageSubscription?.handlers.onevent(diffEvent);
|
||||
@@ -924,11 +844,7 @@ describe("Buzz bus lifecycle", () => {
|
||||
const onMessageError = vi.fn();
|
||||
const onFatalError = vi.fn();
|
||||
const onProfilePublished = vi.fn();
|
||||
const bus = await startBuzzBus({
|
||||
accountId: ACCOUNT_ID,
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
channelIds: [CHANNEL_ID],
|
||||
const bus = await startTestBus({
|
||||
onMessage: async () => {
|
||||
throw new Error("dispatch failed");
|
||||
},
|
||||
@@ -937,15 +853,12 @@ describe("Buzz bus lifecycle", () => {
|
||||
onFatalError,
|
||||
onProfilePublished,
|
||||
});
|
||||
const event = finalizeEvent(
|
||||
{
|
||||
kind: 9,
|
||||
created_at: 1_700_000_000,
|
||||
content: "hello",
|
||||
tags: [["h", CHANNEL_ID]],
|
||||
},
|
||||
Uint8Array.from(Buffer.from(SENDER_PRIVATE_KEY, "hex")),
|
||||
);
|
||||
const event = signSenderEvent({
|
||||
kind: 9,
|
||||
created_at: 1_700_000_000,
|
||||
content: "hello",
|
||||
tags: [["h", CHANNEL_ID]],
|
||||
});
|
||||
|
||||
relayMocks.subscriptions
|
||||
.find((entry) => subscriptionIncludesKind(entry, 9))
|
||||
@@ -972,12 +885,7 @@ describe("Buzz bus lifecycle", () => {
|
||||
relayMocks.stallProfileQueryEose = true;
|
||||
const onFatalError = vi.fn();
|
||||
const onProfileError = vi.fn();
|
||||
const bus = await startBuzzBus({
|
||||
accountId: ACCOUNT_ID,
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
channelIds: [CHANNEL_ID],
|
||||
onMessage: async () => {},
|
||||
const bus = await startTestBus({
|
||||
profileName: "Configured Agent Name",
|
||||
onFatalError,
|
||||
onProfileError,
|
||||
@@ -1005,23 +913,14 @@ describe("Buzz bus lifecycle", () => {
|
||||
|
||||
it("deduplicates replayed events after the bus restarts", async () => {
|
||||
relayMocks.auth.mockResolvedValue("ok");
|
||||
const event = finalizeEvent(
|
||||
{
|
||||
kind: 9,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
content: "hello",
|
||||
tags: [["h", CHANNEL_ID]],
|
||||
},
|
||||
Uint8Array.from(Buffer.from(SENDER_PRIVATE_KEY, "hex")),
|
||||
);
|
||||
const firstOnMessage = vi.fn(async () => {});
|
||||
const firstBus = await startBuzzBus({
|
||||
accountId: ACCOUNT_ID,
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
channelIds: [CHANNEL_ID],
|
||||
onMessage: firstOnMessage,
|
||||
const event = signSenderEvent({
|
||||
kind: 9,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
content: "hello",
|
||||
tags: [["h", CHANNEL_ID]],
|
||||
});
|
||||
const firstOnMessage = vi.fn(async () => {});
|
||||
const firstBus = await startTestBus({ onMessage: firstOnMessage });
|
||||
relayMocks.subscriptions
|
||||
.find((entry) => subscriptionIncludesKind(entry, 9))
|
||||
?.handlers.onevent(event);
|
||||
@@ -1029,13 +928,7 @@ describe("Buzz bus lifecycle", () => {
|
||||
await firstBus.close();
|
||||
|
||||
const secondOnMessage = vi.fn(async () => {});
|
||||
const secondBus = await startBuzzBus({
|
||||
accountId: ACCOUNT_ID,
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
channelIds: [CHANNEL_ID],
|
||||
onMessage: secondOnMessage,
|
||||
});
|
||||
const secondBus = await startTestBus({ onMessage: secondOnMessage });
|
||||
relayMocks.subscriptions
|
||||
.findLast((entry) => subscriptionIncludesKind(entry, 9))
|
||||
?.handlers.onevent(event);
|
||||
|
||||
@@ -43,6 +43,49 @@ const CHANNEL_ID = "7c4a6d2a-2ed9-4b4e-a5e2-4d705ee9b34c";
|
||||
const PRIVATE_KEY = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
|
||||
const BOT_PUBLIC_KEY = "a".repeat(64);
|
||||
|
||||
function createBuzzConfig(name?: string): OpenClawConfig {
|
||||
return {
|
||||
channels: {
|
||||
buzz: {
|
||||
...(name ? { name } : {}),
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
groups: { [CHANNEL_ID]: {} },
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
}
|
||||
|
||||
function startTestGateway(
|
||||
options: {
|
||||
profileName?: string;
|
||||
setStatus?: ReturnType<typeof vi.fn>;
|
||||
logInfo?: ReturnType<typeof vi.fn>;
|
||||
logError?: ReturnType<typeof vi.fn>;
|
||||
invalidateDirectoryCache?: ReturnType<typeof vi.fn>;
|
||||
omitLog?: boolean;
|
||||
} = {},
|
||||
) {
|
||||
const abortController = new AbortController();
|
||||
const cfg = createBuzzConfig(options.profileName);
|
||||
const account = resolveBuzzAccount({ cfg });
|
||||
const setStatus = options.setStatus ?? vi.fn();
|
||||
const lifecycle = startBuzzGatewayAccount({
|
||||
cfg,
|
||||
accountId: account.accountId,
|
||||
account,
|
||||
runtime: {},
|
||||
abortSignal: abortController.signal,
|
||||
...(options.omitLog
|
||||
? {}
|
||||
: { log: { info: options.logInfo ?? vi.fn(), error: options.logError ?? vi.fn() } }),
|
||||
getStatus: vi.fn(),
|
||||
setStatus,
|
||||
invalidateDirectoryCache: options.invalidateDirectoryCache,
|
||||
} as unknown as ChannelGatewayContext<ResolvedBuzzAccount>);
|
||||
return { abortController, cfg, account, setStatus, lifecycle };
|
||||
}
|
||||
|
||||
function createMockBus(): BuzzBus {
|
||||
return {
|
||||
publicKey: BOT_PUBLIC_KEY,
|
||||
@@ -105,28 +148,11 @@ describe("Buzz gateway lifecycle", () => {
|
||||
});
|
||||
|
||||
it("invalidates cached room targets after initial discovery and newer room metadata", async () => {
|
||||
const abortController = new AbortController();
|
||||
const cfg = {
|
||||
channels: {
|
||||
buzz: {
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
groups: { [CHANNEL_ID]: {} },
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
const account = resolveBuzzAccount({ cfg });
|
||||
const invalidateDirectoryCache = vi.fn();
|
||||
const lifecycle = startBuzzGatewayAccount({
|
||||
cfg,
|
||||
accountId: account.accountId,
|
||||
account,
|
||||
runtime: {},
|
||||
abortSignal: abortController.signal,
|
||||
getStatus: vi.fn(),
|
||||
setStatus: vi.fn(),
|
||||
const { abortController, lifecycle } = startTestGateway({
|
||||
invalidateDirectoryCache,
|
||||
} as unknown as ChannelGatewayContext<ResolvedBuzzAccount>);
|
||||
omitLog: true,
|
||||
});
|
||||
|
||||
await vi.waitFor(() => expect(gatewayMocks.startBuzzBus).toHaveBeenCalledOnce());
|
||||
expect(invalidateDirectoryCache).toHaveBeenCalledOnce();
|
||||
@@ -139,29 +165,8 @@ describe("Buzz gateway lifecycle", () => {
|
||||
|
||||
it("restarts the account lifecycle when the bus reports a failure", async () => {
|
||||
gatewayMocks.resolveAgentIdentity.mockReturnValue({ name: "Molt" });
|
||||
const abortController = new AbortController();
|
||||
const cfg = {
|
||||
channels: {
|
||||
buzz: {
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
groups: { [CHANNEL_ID]: {} },
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
const account = resolveBuzzAccount({ cfg });
|
||||
const setStatus = vi.fn();
|
||||
const ctx = {
|
||||
cfg,
|
||||
accountId: account.accountId,
|
||||
account,
|
||||
runtime: {},
|
||||
abortSignal: abortController.signal,
|
||||
log: { info: vi.fn(), error: vi.fn() },
|
||||
getStatus: vi.fn(),
|
||||
setStatus,
|
||||
} as unknown as ChannelGatewayContext<ResolvedBuzzAccount>;
|
||||
const lifecycle = startBuzzGatewayAccount(ctx);
|
||||
const { abortController, account, lifecycle } = startTestGateway({ setStatus });
|
||||
|
||||
await vi.waitFor(() => expect(gatewayMocks.startBuzzBus).toHaveBeenCalledOnce());
|
||||
expect(gatewayMocks.startBuzzBus.mock.calls[0]?.[0].profileName).toBe("Molt");
|
||||
@@ -194,15 +199,7 @@ describe("Buzz gateway lifecycle", () => {
|
||||
});
|
||||
|
||||
it("uses a one-shot authenticated connection when no gateway bus is running", async () => {
|
||||
const cfg = {
|
||||
channels: {
|
||||
buzz: {
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
groups: { [CHANNEL_ID]: {} },
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
const cfg = createBuzzConfig();
|
||||
|
||||
const result = await buzzOutboundAdapter.sendText({
|
||||
cfg,
|
||||
@@ -230,15 +227,7 @@ describe("Buzz gateway lifecycle", () => {
|
||||
});
|
||||
|
||||
it("drops heartbeat typing when no gateway bus is running", async () => {
|
||||
const cfg = {
|
||||
channels: {
|
||||
buzz: {
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
groups: { [CHANNEL_ID]: {} },
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
const cfg = createBuzzConfig();
|
||||
|
||||
await sendBuzzTyping({
|
||||
cfg,
|
||||
@@ -252,29 +241,7 @@ describe("Buzz gateway lifecycle", () => {
|
||||
});
|
||||
|
||||
it("reuses the gateway bus for sends in the running process", async () => {
|
||||
const abortController = new AbortController();
|
||||
const cfg = {
|
||||
channels: {
|
||||
buzz: {
|
||||
name: "BuzzClaw",
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
groups: { [CHANNEL_ID]: {} },
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
const account = resolveBuzzAccount({ cfg });
|
||||
const ctx = {
|
||||
cfg,
|
||||
accountId: account.accountId,
|
||||
account,
|
||||
runtime: {},
|
||||
abortSignal: abortController.signal,
|
||||
log: { info: vi.fn(), error: vi.fn() },
|
||||
getStatus: vi.fn(),
|
||||
setStatus: vi.fn(),
|
||||
} as unknown as ChannelGatewayContext<ResolvedBuzzAccount>;
|
||||
const lifecycle = startBuzzGatewayAccount(ctx);
|
||||
const { abortController, cfg, lifecycle } = startTestGateway({ profileName: "BuzzClaw" });
|
||||
await vi.waitFor(() => expect(gatewayMocks.startBuzzBus).toHaveBeenCalledOnce());
|
||||
expect(gatewayMocks.startBuzzBus.mock.calls[0]?.[0].profileName).toBe("BuzzClaw");
|
||||
expect(gatewayMocks.resolveAgentRoute).not.toHaveBeenCalled();
|
||||
@@ -299,28 +266,7 @@ describe("Buzz gateway lifecycle", () => {
|
||||
});
|
||||
|
||||
it("uses the active bus for heartbeat typing without destabilizing the account", async () => {
|
||||
const abortController = new AbortController();
|
||||
const cfg = {
|
||||
channels: {
|
||||
buzz: {
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
groups: { [CHANNEL_ID]: {} },
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
const account = resolveBuzzAccount({ cfg });
|
||||
const ctx = {
|
||||
cfg,
|
||||
accountId: account.accountId,
|
||||
account,
|
||||
runtime: {},
|
||||
abortSignal: abortController.signal,
|
||||
log: { info: vi.fn(), error: vi.fn() },
|
||||
getStatus: vi.fn(),
|
||||
setStatus: vi.fn(),
|
||||
} as unknown as ChannelGatewayContext<ResolvedBuzzAccount>;
|
||||
const lifecycle = startBuzzGatewayAccount(ctx);
|
||||
const { abortController, cfg, lifecycle } = startTestGateway();
|
||||
await vi.waitFor(() => expect(gatewayMocks.startBuzzBus).toHaveBeenCalledOnce());
|
||||
|
||||
await sendBuzzTyping({
|
||||
@@ -351,28 +297,7 @@ describe("Buzz gateway lifecycle", () => {
|
||||
|
||||
it("uses the rolling lookback after a failed initial session", async () => {
|
||||
gatewayMocks.startBuzzBus.mockRejectedValueOnce(new Error("connect failed"));
|
||||
const abortController = new AbortController();
|
||||
const cfg = {
|
||||
channels: {
|
||||
buzz: {
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
groups: { [CHANNEL_ID]: {} },
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
const account = resolveBuzzAccount({ cfg });
|
||||
const ctx = {
|
||||
cfg,
|
||||
accountId: account.accountId,
|
||||
account,
|
||||
runtime: {},
|
||||
abortSignal: abortController.signal,
|
||||
log: { info: vi.fn(), error: vi.fn() },
|
||||
getStatus: vi.fn(),
|
||||
setStatus: vi.fn(),
|
||||
} as unknown as ChannelGatewayContext<ResolvedBuzzAccount>;
|
||||
const lifecycle = startBuzzGatewayAccount(ctx);
|
||||
const { abortController, lifecycle } = startTestGateway();
|
||||
|
||||
await vi.waitFor(() => expect(gatewayMocks.startBuzzBus).toHaveBeenCalledTimes(2), {
|
||||
timeout: 3_000,
|
||||
@@ -386,30 +311,9 @@ describe("Buzz gateway lifecycle", () => {
|
||||
});
|
||||
|
||||
it("keeps the account running when one message fails", async () => {
|
||||
const abortController = new AbortController();
|
||||
const cfg = {
|
||||
channels: {
|
||||
buzz: {
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
groups: { [CHANNEL_ID]: {} },
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
const account = resolveBuzzAccount({ cfg });
|
||||
const setStatus = vi.fn();
|
||||
const logError = vi.fn();
|
||||
const ctx = {
|
||||
cfg,
|
||||
accountId: account.accountId,
|
||||
account,
|
||||
runtime: {},
|
||||
abortSignal: abortController.signal,
|
||||
log: { info: vi.fn(), error: logError },
|
||||
getStatus: vi.fn(),
|
||||
setStatus,
|
||||
} as unknown as ChannelGatewayContext<ResolvedBuzzAccount>;
|
||||
const lifecycle = startBuzzGatewayAccount(ctx);
|
||||
const { abortController, account, lifecycle } = startTestGateway({ setStatus, logError });
|
||||
|
||||
await vi.waitFor(() => expect(gatewayMocks.startBuzzBus).toHaveBeenCalledOnce());
|
||||
gatewayMocks.onMessageError?.(new Error("dispatch failed"));
|
||||
@@ -426,30 +330,8 @@ describe("Buzz gateway lifecycle", () => {
|
||||
});
|
||||
|
||||
it("reconnects with a rolling lookback without trusting sender time", async () => {
|
||||
const abortController = new AbortController();
|
||||
const cfg = {
|
||||
channels: {
|
||||
buzz: {
|
||||
relayUrl: "wss://buzz.example.com",
|
||||
privateKey: PRIVATE_KEY,
|
||||
groups: { [CHANNEL_ID]: {} },
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
const account = resolveBuzzAccount({ cfg });
|
||||
const invalidateDirectoryCache = vi.fn();
|
||||
const ctx = {
|
||||
cfg,
|
||||
accountId: account.accountId,
|
||||
account,
|
||||
runtime: {},
|
||||
abortSignal: abortController.signal,
|
||||
log: { info: vi.fn(), error: vi.fn() },
|
||||
getStatus: vi.fn(),
|
||||
setStatus: vi.fn(),
|
||||
invalidateDirectoryCache,
|
||||
} as unknown as ChannelGatewayContext<ResolvedBuzzAccount>;
|
||||
const lifecycle = startBuzzGatewayAccount(ctx);
|
||||
const { abortController, lifecycle } = startTestGateway({ invalidateDirectoryCache });
|
||||
|
||||
await vi.waitFor(() => expect(gatewayMocks.startBuzzBus).toHaveBeenCalledOnce());
|
||||
const createdAt = Math.floor(Date.now() / 1000) + 24 * 60 * 60;
|
||||
|
||||
Reference in New Issue
Block a user