Skip to content

Mock Relays for Testing

This guide demonstrates how to use NDK's test utilities to create and work with mock relays in your tests.

Installation

First, install the test utilities package:

bash
pnpm add --save-dev @nostr-dev-kit/ndk-test-utils

Basic Mock Relay Usage

Create and use a basic mock relay:

typescript
import { RelayMock } from "@nostr-dev-kit/ndk-test-utils";
import { NDK } from "@nostr-dev-kit/ndk";

describe("Mock Relay Tests", () => {
    let relayMock: RelayMock;
    let ndk: NDK;

    beforeEach(() => {
        // Create a new NDK instance
        ndk = new NDK();
        
        // Create a mock relay
        relayMock = new RelayMock("wss://test.relay", {
            simulateDisconnect: false,  // Don't randomly disconnect
            connectionDelay: 100,       // Simulate 100ms connection delay
            autoConnect: true           // Auto-connect when used
        });
    });

    it("should handle events", async () => {
        // Connect to the mock relay
        await relayMock.connect();

        // Create a subscription
        const sub = relayMock.subscribe({
            subId: "test-sub",
            filters: [{ kinds: [1] }]  // Subscribe to text notes
        });

        // Simulate receiving an event
        const event = {
            kind: 1,
            content: "Hello World",
            // ... other event fields
        };
        await relayMock.simulateEvent(event);

        // Clean up
        await relayMock.disconnect();
    });
});

Advanced Mock Relay Features

Simulating Network Conditions

typescript
// Create a flaky relay that randomly disconnects
const flakyRelay = new RelayMock("wss://flaky.relay", {
    simulateDisconnect: true,
    disconnectChance: 0.3,  // 30% chance to disconnect on operations
    connectionDelay: 500    // 500ms connection delay
});

// Test reconnection behavior
await flakyRelay.connect();
await flakyRelay.simulateDisconnect();  // Force a disconnect
await flakyRelay.connect();             // Should reconnect

Testing Event Publishing

typescript
import { RelayMock, EventGenerator } from "@nostr-dev-kit/ndk-test-utils";

// Create an event generator
const generator = new EventGenerator();

// Generate a test event
const textNote = await generator.textNote("Test message");

// Publish and verify
const publishSpy = vitest.spyOn(relayMock, 'publish');
await relayMock.publish(textNote);

expect(publishSpy).toHaveBeenCalledWith(textNote);

Simulating Relay Messages

typescript
// Simulate various relay messages
relayMock.simulateNotice("Server restarting in 5 minutes");
relayMock.simulateEOSE("subscription-id");
relayMock.simulateOK("event-id", true, "success");

// Simulate raw messages
relayMock.simulateReceiveMessage('["AUTH", "challenge"]');

Testing Multiple Relays

Use RelayPoolMock to test multiple relay scenarios:

typescript
import { RelayPoolMock } from "@nostr-dev-kit/ndk-test-utils";

// Create a mock relay pool
const poolMock = new RelayPoolMock();

// Add multiple relays
poolMock.addRelay("wss://relay1.test");
poolMock.addRelay("wss://relay2.test");

// Get specific relay mock
const relay1 = poolMock.getRelay("wss://relay1.test");
const relay2 = poolMock.getRelay("wss://relay2.test");

// Test event propagation across relays
const event = await generator.textNote("Test across relays");
await relay1.simulateEvent(event);  // Simulate event on first relay
await relay2.simulateEvent(event);  // Simulate same event on second relay

Best Practices

  1. Always clean up relays after tests:
typescript
afterEach(async () => {
    await relayMock.disconnect();
});
  1. Reset event generators between tests:
typescript
beforeEach(() => {
    EventGenerator.setNDK(ndk);  // Reset with fresh NDK instance
});
  1. Use realistic delays to simulate network conditions:
typescript
const relay = new RelayMock("wss://test.relay", {
    connectionDelay: 100,    // Connection time
    operationDelay: 50,     // Time for operations
    disconnectDelay: 75     // Disconnection time
});
  1. Test error scenarios:
typescript
// Test failed connections
const failingRelay = new RelayMock("wss://failing.relay", {
    simulateConnectionError: true
});

try {
    await failingRelay.connect();
} catch (error) {
    expect(error).toBeDefined();
}