Files
psf-bch-api-base/test/unit/adapters/nostr-relay-unit.js
T
2025-11-09 16:16:37 -08:00

305 lines
8.2 KiB
JavaScript

/*
Unit tests for NostrRelayAdapter.
*/
/*
// npm libraries
import { assert } from 'chai'
import sinon from 'sinon'
// Mocking data libraries
import {
mockKind1Event,
validEventId
} from '../mocks/event-mocks.js'
import {
mockOkAccepted,
mockEventMessage,
mockEoseMessage,
mockClosedMessage
} from '../mocks/nostr-relay-mocks.js'
// Unit under test
// Note: WebSocket mocking for ES modules is complex. These tests focus on
// testing the adapter's logic that can be tested without full WebSocket mocking.
import NostrRelayAdapter from '../../../src/adapters/nostr-relay.js'
describe('#nostr-relay.js', () => {
let sandbox
let uut
beforeEach(() => {
sandbox = sinon.createSandbox()
uut = new NostrRelayAdapter({
relayUrl: 'wss://test-relay.example.com'
})
})
afterEach(() => {
sandbox.restore()
})
describe('#connect()', () => {
it('should return immediately if already connected', async () => {
// Manually set connection state
uut.isConnected = true
uut.ws = { close: sandbox.stub() }
await uut.connect()
// Should not create new connection
assert.isTrue(uut.isConnected)
})
// Note: Full WebSocket connection testing requires integration tests
// due to ES module import limitations
})
describe('#sendEvent()', () => {
it('should queue message when disconnected', async () => {
uut.isConnected = false
uut.ws = null
// Mock connect to resolve immediately
uut.connect = sandbox.stub().resolves()
// Start sending (will queue)
uut.sendEvent(mockKind1Event).catch(() => {
// Expected to fail or timeout without real WebSocket
})
// Should queue message and attempt connection
// Wait a bit for async operations
await new Promise(resolve => setTimeout(resolve, 10))
assert.isTrue(uut.pendingMessages.length > 0 || uut.connect.called)
})
it('should set up event resolver', async () => {
uut.isConnected = true
uut.ws = { send: sandbox.stub() }
// Mock sendMessage to resolve immediately
uut.sendMessage = sandbox.stub().resolves()
// Start sending
const sendPromise = uut.sendEvent(mockKind1Event).catch(() => {
// Expected without real WebSocket response
})
// Wait a tick for Promise constructor to run
await new Promise(resolve => setImmediate(resolve))
// Verify resolver was set up
assert.isTrue(uut.eventResolvers.has(mockKind1Event.id))
// Clean up
uut.eventResolvers.delete(mockKind1Event.id)
// Prevent timeout error
sendPromise.catch(() => {})
})
// Note: Full sendEvent testing with WebSocket responses requires integration tests
})
describe('#sendReq()', () => {
it('should store handlers for subscription', async () => {
uut.isConnected = true
uut.ws = { send: sandbox.stub() }
uut.connect = sandbox.stub().resolves()
const subscriptionId = 'test-sub-123'
const filters = [{ kinds: [1] }]
const handlers = {
onEvent: sandbox.stub(),
onEose: sandbox.stub(),
onClosed: sandbox.stub()
}
await uut.sendReq(subscriptionId, filters, handlers)
// Assert handlers were stored
assert.isTrue(uut.subscriptionHandlers.has(subscriptionId))
assert.deepEqual(uut.subscriptionHandlers.get(subscriptionId), handlers)
})
it('should connect before sending if disconnected', async () => {
uut.isConnected = false
uut.connect = sandbox.stub().resolves()
const subscriptionId = 'test-sub-123'
const filters = [{ kinds: [1] }]
const handlers = {}
await uut.sendReq(subscriptionId, filters, handlers)
assert.isTrue(uut.connect.called)
})
})
describe('#sendClose()', () => {
it('should clean up handlers for subscription', async () => {
uut.isConnected = true
uut.ws = { send: sandbox.stub() }
const subscriptionId = 'test-sub-123'
uut.subscriptionHandlers.set(subscriptionId, {})
uut.messageHandlers.set(subscriptionId, {})
await uut.sendClose(subscriptionId)
// Assert handlers were cleaned up
assert.isFalse(uut.subscriptionHandlers.has(subscriptionId))
assert.isFalse(uut.messageHandlers.has(subscriptionId))
})
})
describe('#handleMessage()', () => {
it('should handle EVENT message', () => {
// Use the subscription ID from the mock message
const subscriptionId = 'subscription-id-123'
const onEventHandler = sandbox.stub()
uut.subscriptionHandlers.set(subscriptionId, {
onEvent: onEventHandler
})
const message = mockEventMessage
uut.handleMessage(message)
assert.isTrue(onEventHandler.calledOnce)
assert.deepEqual(onEventHandler.getCall(0).args[0], mockKind1Event)
})
it('should handle EOSE message', () => {
// Use the subscription ID from the mock message
const subscriptionId = 'subscription-id-123'
const onEoseHandler = sandbox.stub()
uut.subscriptionHandlers.set(subscriptionId, {
onEose: onEoseHandler
})
const message = mockEoseMessage
uut.handleMessage(message)
assert.isTrue(onEoseHandler.calledOnce)
})
it('should handle CLOSED message', () => {
// Use the subscription ID from the mock message
const subscriptionId = 'subscription-id-123'
const onClosedHandler = sandbox.stub()
uut.subscriptionHandlers.set(subscriptionId, {
onClosed: onClosedHandler
})
const message = mockClosedMessage
uut.handleMessage(message)
assert.isTrue(onClosedHandler.calledOnce)
assert.equal(onClosedHandler.getCall(0).args[0], 'subscription closed')
})
it('should handle OK message', () => {
const eventId = validEventId
let resolver = null
uut.eventResolvers.set(eventId, (result) => {
resolver = result
})
const message = mockOkAccepted
uut.handleMessage(message)
assert.isNotNull(resolver)
assert.isTrue(resolver.accepted)
assert.isFalse(uut.eventResolvers.has(eventId))
})
it('should handle NOTICE message', () => {
const message = ['NOTICE', 'rate limited']
// Should not throw
uut.handleMessage(message)
})
it('should ignore invalid message format', () => {
const message = 'invalid'
// Should not throw
uut.handleMessage(message)
})
it('should ignore empty messages', () => {
const message = []
// Should not throw
uut.handleMessage(message)
})
})
describe('#disconnect()', () => {
it('should disconnect from relay', async () => {
const mockWs = { close: sandbox.stub() }
uut.isConnected = true
uut.ws = mockWs
await uut.disconnect()
assert.isTrue(mockWs.close.called)
assert.isFalse(uut.isConnected)
assert.isNull(uut.ws)
})
it('should handle disconnect when already disconnected', async () => {
uut.isConnected = false
uut.ws = null
await uut.disconnect()
assert.isFalse(uut.isConnected)
})
})
describe('#handleError()', () => {
it('should handle WebSocket errors', () => {
uut.isConnected = true
const error = new Error('WebSocket error')
uut.handleError(error)
assert.isFalse(uut.isConnected)
})
})
describe('#handleClose()', () => {
it('should attempt reconnection on close', async () => {
uut.isConnected = true
uut.reconnectAttempts = 0
uut.maxReconnectAttempts = 5
// Mock connect to avoid actual connection
uut.connect = sandbox.stub().resolves()
uut.handleClose()
// Wait for reconnection attempt
await new Promise(resolve => setTimeout(resolve, 110))
// Should attempt reconnection
assert.equal(uut.reconnectAttempts, 1)
})
it('should stop reconnecting after max attempts', async () => {
uut.isConnected = true
uut.reconnectAttempts = 5
uut.maxReconnectAttempts = 5
uut.connect = sandbox.stub().resolves()
uut.handleClose()
await new Promise(resolve => setTimeout(resolve, 110))
// Should not increment beyond max
assert.equal(uut.reconnectAttempts, 5)
})
})
})
*/