test(buzz): consolidate lifecycle fixtures (#118316)

This commit is contained in:
Peter Steinberger
2026-08-02 19:42:22 -07:00
committed by GitHub
parent 1fedcca9a9
commit 628672aaa1
2 changed files with 154 additions and 379 deletions
+100 -207
View File
@@ -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);
+54 -172
View File
@@ -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;