test(client): cover pairing restart resume flow

This commit is contained in:
nav
2026-04-09 04:06:06 +00:00
parent b10ebc541e
commit 7cdda2e335

View File

@@ -479,4 +479,114 @@ describe("Yonexus.Client runtime flow", () => {
expect(runtime.state.pendingPairing).toBeDefined();
expect(runtime.state.lastPairingFailure).toBe("invalid_code");
});
it("PF-10: restart during pending pairing resumes waiting for out-of-band code", async () => {
const now = 1_710_000_000;
const storeState = createMockStateStore({
identifier: "client-a",
updatedAt: now
});
const firstTransportState = createMockTransport();
const firstRuntime = createYonexusClientRuntime({
config: {
mainHost: "ws://localhost:8787",
identifier: "client-a",
notifyBotToken: "stub-token",
adminUserId: "admin-user"
},
transport: firstTransportState.transport,
stateStore: storeState.store,
now: () => now
});
await firstRuntime.start();
firstRuntime.handleTransportStateChange("connected");
await firstRuntime.handleMessage(
encodeBuiltin(
buildPairRequest(
{
identifier: "client-a",
expiresAt: now + 300,
ttlSeconds: 300,
adminNotification: "sent",
codeDelivery: "out_of_band"
},
{ requestId: "req-pair", timestamp: now }
)
)
);
expect(firstRuntime.state.phase).toBe("waiting_pair_confirm");
expect(firstRuntime.state.pendingPairing).toMatchObject({
expiresAt: now + 300,
ttlSeconds: 300,
adminNotification: "sent"
});
await firstRuntime.stop();
const secondTransportState = createMockTransport();
const secondRuntime = createYonexusClientRuntime({
config: {
mainHost: "ws://localhost:8787",
identifier: "client-a",
notifyBotToken: "stub-token",
adminUserId: "admin-user"
},
transport: secondTransportState.transport,
stateStore: storeState.store,
now: () => now + 5
});
await secondRuntime.start();
secondRuntime.handleTransportStateChange("connected");
const hello = decodeBuiltin(secondTransportState.sent[0]);
expect(hello.type).toBe("hello");
expect(hello.payload).toMatchObject({
identifier: "client-a",
hasSecret: false,
hasKeyPair: true
});
await secondRuntime.handleMessage(
encodeBuiltin(
buildHelloAck(
{
identifier: "client-a",
nextAction: "waiting_pair_confirm"
},
{ requestId: "req-hello-resume", timestamp: now + 5 }
)
)
);
await secondRuntime.handleMessage(
encodeBuiltin(
buildPairRequest(
{
identifier: "client-a",
expiresAt: now + 300,
ttlSeconds: 295,
adminNotification: "sent",
codeDelivery: "out_of_band"
},
{ requestId: "req-pair-resume", timestamp: now + 5 }
)
)
);
expect(secondRuntime.state.phase).toBe("waiting_pair_confirm");
expect(secondRuntime.state.pendingPairing).toMatchObject({
expiresAt: now + 300,
ttlSeconds: 295,
adminNotification: "sent"
});
expect(secondRuntime.submitPairingCode("PAIR-CODE-123", "req-pair-resume")).toBe(true);
const pairConfirm = decodeBuiltin(secondTransportState.sent.at(-1)!);
expect(pairConfirm.type).toBe("pair_confirm");
expect((pairConfirm.payload as PairConfirmPayload).pairingCode).toBe("PAIR-CODE-123");
});
});