|
1 | 1 | import { describe, it, expect, vi } from 'vitest'; |
2 | 2 | import { Messenger } from '../src/p2p/messenger.js'; |
3 | 3 | import type { ProtocolRouter } from '@origintrail-official/dkg-core'; |
4 | | -import type { DiscoveryClient } from '../src/discovery.js'; |
5 | 4 |
|
6 | | -// 12D3Koo... peer ids must be valid base58 to satisfy peerIdFromString. Two |
7 | | -// arbitrary valid ed25519 peer IDs taken from existing tests. |
8 | 5 | const PEER_A = '12D3KooWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; |
9 | 6 | const PEER_B = '12D3KooWQz2bQbQueABKRSjV9koF8VYsXk5TdCsUmPf5zAEZg3q6'; |
10 | | -const RELAY_ADDR = '/ip4/178.104.54.178/tcp/9090/p2p/12D3KooWSmU3owJvB9sFw8uApDgKrv2VBMecsGGvgAc4Gq6hB57M'; |
11 | 7 |
|
12 | 8 | interface MockSetup { |
13 | | - libp2pConnections: Array<unknown>; |
14 | 9 | routerSendMock: ReturnType<typeof vi.fn>; |
15 | | - peerStoreMergeMock: ReturnType<typeof vi.fn>; |
16 | | - findAgentMock: ReturnType<typeof vi.fn>; |
17 | 10 | } |
18 | 11 |
|
19 | | -function makeMessenger(overrides: Partial<MockSetup> = {}): { messenger: Messenger; mocks: MockSetup; callOrder: string[] } { |
20 | | - const callOrder: string[] = []; |
21 | | - |
| 12 | +function makeMessenger(overrides: Partial<MockSetup> = {}): { |
| 13 | + messenger: Messenger; |
| 14 | + mocks: MockSetup; |
| 15 | +} { |
22 | 16 | const mocks: MockSetup = { |
23 | | - libp2pConnections: overrides.libp2pConnections ?? [], |
24 | | - routerSendMock: overrides.routerSendMock ?? vi.fn(async () => { |
25 | | - callOrder.push('router.send'); |
26 | | - return new Uint8Array([0x01, 0x02]); |
27 | | - }), |
28 | | - peerStoreMergeMock: overrides.peerStoreMergeMock ?? vi.fn(async () => { |
29 | | - callOrder.push('peerStore.merge'); |
30 | | - }), |
31 | | - findAgentMock: overrides.findAgentMock ?? vi.fn(async () => { |
32 | | - callOrder.push('discovery.findAgentByPeerId'); |
33 | | - return { peerId: PEER_B, name: 'remote', agentUri: 'urn:agent:remote', relayAddress: RELAY_ADDR }; |
34 | | - }), |
| 17 | + routerSendMock: |
| 18 | + overrides.routerSendMock ?? |
| 19 | + vi.fn(async () => new Uint8Array([0x01, 0x02])), |
35 | 20 | }; |
36 | 21 |
|
37 | 22 | const messenger = new Messenger({ |
38 | | - libp2p: { |
39 | | - getConnections: () => mocks.libp2pConnections, |
40 | | - peerStore: { merge: mocks.peerStoreMergeMock }, |
41 | | - }, |
42 | 23 | router: { send: mocks.routerSendMock } as unknown as ProtocolRouter, |
43 | | - discovery: { findAgentByPeerId: mocks.findAgentMock } as unknown as DiscoveryClient, |
44 | 24 | }); |
45 | 25 |
|
46 | | - return { messenger, mocks, callOrder }; |
| 26 | + return { messenger, mocks }; |
47 | 27 | } |
48 | 28 |
|
49 | 29 | describe('Messenger.sendToPeer', () => { |
50 | | - it('skips relay prime when peer is already connected', async () => { |
51 | | - const { messenger, mocks } = makeMessenger({ |
52 | | - libp2pConnections: [{ remotePeer: { toString: () => PEER_B } }], |
53 | | - }); |
54 | | - |
55 | | - await messenger.sendToPeer(PEER_B, '/dkg/test/1.0.0', new Uint8Array([0xff])); |
| 30 | + it('delegates to router.send with peerId / protocol / data', async () => { |
| 31 | + const { messenger, mocks } = makeMessenger(); |
56 | 32 |
|
57 | | - expect(mocks.findAgentMock).not.toHaveBeenCalled(); |
58 | | - expect(mocks.peerStoreMergeMock).not.toHaveBeenCalled(); |
59 | | - expect(mocks.routerSendMock).toHaveBeenCalledOnce(); |
| 33 | + const out = await messenger.sendToPeer( |
| 34 | + PEER_B, |
| 35 | + '/dkg/test/1.0.0', |
| 36 | + new Uint8Array([0xff]), |
| 37 | + ); |
| 38 | + |
| 39 | + expect(mocks.routerSendMock).toHaveBeenCalledWith( |
| 40 | + PEER_B, |
| 41 | + '/dkg/test/1.0.0', |
| 42 | + expect.any(Uint8Array), |
| 43 | + undefined, |
| 44 | + ); |
| 45 | + expect(out).toEqual(new Uint8Array([0x01, 0x02])); |
60 | 46 | }); |
61 | 47 |
|
62 | | - it('primes /p2p-circuit multiaddr when peer not connected and profile advertises a relay', async () => { |
| 48 | + it('forwards timeoutMs to router.send', async () => { |
63 | 49 | const { messenger, mocks } = makeMessenger(); |
64 | 50 |
|
65 | | - await messenger.sendToPeer(PEER_B, '/dkg/test/1.0.0', new Uint8Array([0xff])); |
66 | | - |
67 | | - expect(mocks.findAgentMock).toHaveBeenCalledWith(PEER_B); |
68 | | - expect(mocks.peerStoreMergeMock).toHaveBeenCalledOnce(); |
69 | | - const mergeCall = mocks.peerStoreMergeMock.mock.calls[0]; |
70 | | - const multiaddrs = (mergeCall[1] as { multiaddrs: Array<{ toString(): string }> }).multiaddrs; |
71 | | - expect(multiaddrs[0].toString()).toContain('/p2p-circuit/p2p/' + PEER_B); |
72 | | - expect(mocks.routerSendMock).toHaveBeenCalledOnce(); |
73 | | - }); |
74 | | - |
75 | | - it('skips relay prime when discovery returns no relayAddress', async () => { |
76 | | - const { messenger, mocks } = makeMessenger({ |
77 | | - findAgentMock: vi.fn(async () => ({ peerId: PEER_B, name: 'remote', agentUri: 'urn:agent:remote' })), |
| 51 | + await messenger.sendToPeer(PEER_A, '/dkg/test/1.0.0', new Uint8Array([0xff]), { |
| 52 | + timeoutMs: 5000, |
78 | 53 | }); |
79 | 54 |
|
80 | | - await messenger.sendToPeer(PEER_B, '/dkg/test/1.0.0', new Uint8Array([0xff])); |
81 | | - |
82 | | - expect(mocks.findAgentMock).toHaveBeenCalled(); |
83 | | - expect(mocks.peerStoreMergeMock).not.toHaveBeenCalled(); |
84 | | - expect(mocks.routerSendMock).toHaveBeenCalledOnce(); |
| 55 | + expect(mocks.routerSendMock).toHaveBeenCalledWith( |
| 56 | + PEER_A, |
| 57 | + '/dkg/test/1.0.0', |
| 58 | + expect.any(Uint8Array), |
| 59 | + 5000, |
| 60 | + ); |
85 | 61 | }); |
86 | 62 |
|
87 | | - it('tolerates discovery throwing — proceeds to router.send anyway', async () => { |
88 | | - const { messenger, mocks } = makeMessenger({ |
89 | | - findAgentMock: vi.fn(async () => { throw new Error('discovery boom'); }), |
| 63 | + it('propagates router.send errors to the caller', async () => { |
| 64 | + const { messenger } = makeMessenger({ |
| 65 | + routerSendMock: vi.fn(async () => { |
| 66 | + throw new Error('transport boom'); |
| 67 | + }), |
90 | 68 | }); |
91 | 69 |
|
92 | | - await messenger.sendToPeer(PEER_B, '/dkg/test/1.0.0', new Uint8Array([0xff])); |
93 | | - |
94 | | - expect(mocks.peerStoreMergeMock).not.toHaveBeenCalled(); |
95 | | - expect(mocks.routerSendMock).toHaveBeenCalledOnce(); |
| 70 | + await expect( |
| 71 | + messenger.sendToPeer(PEER_B, '/dkg/test/1.0.0', new Uint8Array([0xff])), |
| 72 | + ).rejects.toThrow('transport boom'); |
96 | 73 | }); |
97 | 74 |
|
98 | | - it('regression: ensureCircuitRelayAddress fires BEFORE router.send (the Laptop B bug)', async () => { |
99 | | - const { messenger, callOrder } = makeMessenger(); |
100 | | - |
101 | | - await messenger.sendToPeer(PEER_B, '/dkg/test/1.0.0', new Uint8Array([0xff])); |
102 | | - |
103 | | - // The bug fixed by this primitive: forwardJoinRequest used to call |
104 | | - // router.send directly without first priming the relay route, causing |
105 | | - // "no reachable curator" failures for NAT'd peers. The fix is structural |
106 | | - // — every send through Messenger primes first. |
107 | | - const mergeIdx = callOrder.indexOf('peerStore.merge'); |
108 | | - const sendIdx = callOrder.indexOf('router.send'); |
109 | | - expect(mergeIdx).toBeGreaterThanOrEqual(0); |
110 | | - expect(sendIdx).toBeGreaterThanOrEqual(0); |
111 | | - expect(mergeIdx).toBeLessThan(sendIdx); |
112 | | - }); |
113 | | - |
114 | | - it('forwards timeoutMs to router.send', async () => { |
115 | | - const { messenger, mocks } = makeMessenger({ |
116 | | - libp2pConnections: [{ remotePeer: { toString: () => PEER_B } }], |
117 | | - }); |
118 | | - |
119 | | - await messenger.sendToPeer(PEER_A, '/dkg/test/1.0.0', new Uint8Array([0xff]), { timeoutMs: 5000 }); |
120 | | - |
121 | | - expect(mocks.routerSendMock).toHaveBeenCalledWith(PEER_A, '/dkg/test/1.0.0', expect.any(Uint8Array), 5000); |
122 | | - }); |
| 75 | + // Note: Messenger no longer holds a PeerResolver — the resolver is |
| 76 | + // owned by ProtocolRouter (RFC 07 PR-3) so resolution happens once |
| 77 | + // per send rather than twice. The structural property "resolver |
| 78 | + // primes peerStore before dialProtocol" still holds; it's just |
| 79 | + // verified at the router layer now (see protocol-router-resolver.test.ts). |
123 | 80 | }); |
0 commit comments