Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion desktop/src/shared/api/relayClientSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,9 @@ export class RelayClient {
);

const generation = ++this.connectionGeneration;
let pendingInbound: unknown[] | null = [];
this.onMessageChannel = new Channel<unknown>((message) => {
if (pendingInbound) return void pendingInbound.push(message);
void this.handleWsMessage(message, generation).catch((error) => {
if (generation !== this.connectionGeneration) return;
this.resetConnection(
Expand All @@ -560,7 +562,7 @@ export class RelayClient {
}
this.wsId = wsId;

await new Promise<void>((resolve, reject) => {
const authentication = new Promise<void>((resolve, reject) => {
const timeout = window.setTimeout(() => {
const error = new Error("Relay authentication timed out.");
this.authRequest = null;
Expand All @@ -576,6 +578,11 @@ export class RelayClient {
};
});

while (pendingInbound.length > 0) {
await this.handleWsMessage(pendingInbound.shift(), generation);
}
pendingInbound = null;
await authentication;
this.stabilityTimer = window.setTimeout(() => {
this.stabilityTimer = null;
this.reconnectDelayMs = RECONNECT_BASE_DELAY_MS;
Expand Down
11 changes: 9 additions & 2 deletions desktop/src/testing/e2eBridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,8 @@ type E2eConfig = {
nostrBindSignDelayMs?: number;
/** Reject successive mock WebSocket connect attempts, then resume. */
websocketConnectErrors?: string[];
/** Deliver AUTH synchronously, before the mock connect command resolves. */
websocketAuthBeforeConnectResolves?: boolean;
stallWebsocketSends?: boolean;
userSearchDelayMs?: number;
// NIP-IA gate inputs — see tests/helpers/bridge.ts:MockBridgeOptions for
Expand Down Expand Up @@ -8758,9 +8760,14 @@ async function connectMockSocket(args: { onMessage: unknown }) {
subscriptions: new Map(),
});

window.setTimeout(() => {
if (getConfig()?.mock?.websocketAuthBeforeConnectResolves) {
sendWsText(handler, ["AUTH", `mock-challenge-${wsId}`]);
}, 0);
await new Promise<void>((resolve) => window.setTimeout(resolve, 50));
} else {
window.setTimeout(() => {
sendWsText(handler, ["AUTH", `mock-challenge-${wsId}`]);
}, 0);
}

return wsId;
}
Expand Down
24 changes: 24 additions & 0 deletions desktop/tests/e2e/relay-reconnect.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,30 @@ test.beforeEach(async ({ page }) => {
await installMockBridge(page);
});

test("AUTH arriving before connect resolves does not lose the first send", async ({
page,
}) => {
await installMockBridge(page, {
websocketAuthBeforeConnectResolves: true,
});
await page.goto("/");
await expect
.poll(
() =>
page.evaluate(() => window.__BUZZ_E2E_GET_RELAY_CONNECTION_STATE__?.()),
{ timeout: 5_000 },
)
.toBe("connected");
await page.getByTestId("channel-general").click();
await expect(page.getByTestId("chat-title")).toHaveText("general");

const message = `first send after early auth ${Date.now()}`;
await page.getByTestId("message-input").fill(message);
await page.getByTestId("send-message").click();

await expect(page.getByTestId("message-timeline")).toContainText(message);
});

test("failed initial relay dial retries automatically", async ({ page }) => {
await installMockBridge(page, {
websocketConnectErrors: ["mock relay pod unavailable"],
Expand Down
2 changes: 2 additions & 0 deletions desktop/tests/helpers/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,8 @@ type MockBridgeOptions = {
nostrBindSignDelayMs?: number;
/** Reject successive mock WebSocket connect attempts, then resume. */
websocketConnectErrors?: string[];
/** Deliver AUTH synchronously, before the mock connect command resolves. */
websocketAuthBeforeConnectResolves?: boolean;
stallWebsocketSends?: boolean;
userSearchDelayMs?: number;
/**
Expand Down
Loading