fix(desktop): preserve early relay auth challenges

Co-authored-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@buzz.block.builderlab.xyz>
Signed-off-by: npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf <95cae996907d7cab9f5dbf43c0f53edeac6ab0b032a6feae4abfd784e467b3f5@buzz.block.builderlab.xyz>
This commit is contained in:
npub1jh9wn95s0472h86ahapupaf7m6kx4v9sx2n0atj2hltcfer8k06s5n3pyf
2026-07-28 10:17:25 -04:00
parent 2ce2d71cc3
commit 2c7105ec08
4 changed files with 43 additions and 3 deletions
+8 -1
View File
@@ -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(
@@ -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;
@@ -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;
+9 -2
View File
@@ -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
@@ -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;
}
+24
View File
@@ -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"],
+2
View File
@@ -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;
/**