@bsv/message-box-client
Version:
A client for P2P messaging and payments
164 lines (134 loc) • 5.62 kB
text/typescript
/* eslint-env jest */
import { MessageBoxClient } from '../../MessageBoxClient.js'
import { WalletClient } from '@bsv/sdk'
import { webcrypto } from 'crypto'
(global as any).self = { crypto: webcrypto }
jest.setTimeout(20000)
const MESSAGEBOX_HOST = 'http://localhost:5001'
const walletA = new WalletClient('json-api', 'localhost')
const walletB = new WalletClient('json-api', 'localhost')
const clientA = new MessageBoxClient({
host: MESSAGEBOX_HOST,
walletClient: walletA,
networkPreset: 'local',
enableLogging: true
})
const clientB = new MessageBoxClient({
host: MESSAGEBOX_HOST,
walletClient: walletB,
networkPreset: 'local',
enableLogging: true
})
let identityKeyA: string
let identityKeyB: string
beforeAll(async () => {
identityKeyA = (await walletA.getPublicKey({ identityKey: true })).publicKey
identityKeyB = (await walletB.getPublicKey({ identityKey: true })).publicKey
await clientA.initializeConnection()
await clientB.initializeConnection()
})
afterAll(async () => {
await clientA.disconnectWebSocket()
await clientB.disconnectWebSocket()
})
describe('Overlay Integration Tests', () => {
const selfBox = 'overlay_self_box'
const peerBox = 'forwarded_overlay_box'
test('clientA broadcasts overlay advertisement', async () => {
const result = await clientA.anointHost(MESSAGEBOX_HOST)
expect(result).toHaveProperty('txid')
await new Promise(resolve => setTimeout(resolve, 3000))
})
test('clientA resolves own host via overlay', async () => {
const resolved = await (clientA as any).resolveHostForRecipient(identityKeyA)
console.log('[TEST] Resolved host:', resolved)
expect(resolved).toBe(MESSAGEBOX_HOST)
})
test('clientA sends message to self via overlay', async () => {
const response = await clientA.sendMessage({
recipient: identityKeyA,
messageBox: selfBox,
body: 'hello via overlay'
})
expect(response.status).toBe('success')
})
test('clientA lists self messages via overlay', async () => {
const messages = await clientA.listMessages({ messageBox: selfBox })
expect(messages.length).toBeGreaterThan(0)
expect(messages.at(-1)?.body).toContain('hello via overlay')
})
test('clientA acknowledges self messages via overlay', async () => {
const messages = await clientA.listMessages({ messageBox: selfBox })
const ids = messages.map(m => m.messageId).filter(Boolean)
expect(ids.length).toBeGreaterThan(0)
const status = await clientA.acknowledgeMessage({ messageIds: ids })
expect(status).toBe('success')
})
test('clientB broadcasts overlay advertisement', async () => {
const result = await clientB.anointHost(MESSAGEBOX_HOST)
expect(result).toHaveProperty('txid')
await new Promise(resolve => setTimeout(resolve, 3000))
})
test('clientA sends message to clientB via overlay', async () => {
const response = await clientA.sendMessage({
recipient: identityKeyB,
messageBox: peerBox,
body: 'delivered to peer via overlay'
})
expect(response.status).toBe('success')
})
test('clientB receives overlay message from clientA', async () => {
const messages = await clientB.listMessages({ messageBox: peerBox })
expect(messages.some(m => m.body.includes('delivered to peer via overlay'))).toBe(true)
})
test('clientB acknowledges overlay message from clientA', async () => {
const messages = await clientB.listMessages({ messageBox: peerBox })
const ids = messages.map(m => m.messageId).filter(Boolean)
const result = await clientB.acknowledgeMessage({ messageIds: ids })
expect(result).toBe('success')
})
test('clientA verifies clientB host resolution', async () => {
const resolved = await (clientA as any).resolveHostForRecipient(identityKeyB)
expect(resolved).toBe(MESSAGEBOX_HOST)
})
test('overlay advertisement is idempotent', async () => {
const result1 = await clientA.anointHost(MESSAGEBOX_HOST)
const result2 = await clientA.anointHost(MESSAGEBOX_HOST)
expect(result1.txid).not.toBe(result2.txid)
})
test('clientA sends and acknowledges multiple messages to clientB', async () => {
const contents = ['msg1', 'msg2', 'msg3']
for (const msg of contents) {
const response = await clientA.sendMessage({
recipient: identityKeyB,
messageBox: peerBox,
body: msg
})
expect(response.status).toBe('success')
}
await new Promise(resolve => setTimeout(resolve, 1000))
const messages = await clientB.listMessages({ messageBox: peerBox })
const matched = contents.every(c => messages.some(m => m.body.includes(c)))
expect(matched).toBe(true)
const ids = messages.map(m => m.messageId).filter(Boolean)
const status = await clientB.acknowledgeMessage({ messageIds: ids })
expect(status).toBe('success')
})
test('clientA reinitializes with init() and correctly anoints host', async () => {
const tempClient = new MessageBoxClient({
walletClient: walletA,
networkPreset: 'local',
enableLogging: true
// No host provided here!
})
// Call init manually
await tempClient.init(MESSAGEBOX_HOST)
// Verify client is initialized and host is correct
expect((tempClient as any).initialized).toBe(true)
expect((tempClient as any).host).toBe(MESSAGEBOX_HOST)
// Optionally, test that resolving our own identity also works
const resolvedHost = await (tempClient as any).resolveHostForRecipient(identityKeyA)
expect(resolvedHost).toBe(MESSAGEBOX_HOST)
await tempClient.disconnectWebSocket()
})
})