Files
wownerogue/test/nostrChatProvider_ultra.test.js
jwinterm fad2a695bd Wownerogue
A browser-based roguelike synchronized with crypto block timing. A run is a race
against the next block: reach the exit before the block lands, or the house wins.
The same codebase runs the solo dungeon, the social Tavern, and operator-selected
PvP and race rulesets, with separate free and paid leaderboards.

Self-hostable under the MIT License. See README.md to run it, docs/ARCHITECTURE.md
for the design, and docs/DEPLOY.md to operate an instance.
2026-07-27 08:23:01 -04:00

129 lines
6.2 KiB
JavaScript

/**
* Pillar 5: global cross-server chat over nostr, layered on the ChatProvider seam.
*/
const NostrChatProvider = require('../src/network/chat/NostrChatProvider');
const { buildChatProvider } = require('../src/network/chat');
const { createBridgeSigner } = require('../src/network/chat/nostr/bridgeSigner');
const { loadNostrTools } = require('../src/utils/nostrLoader');
function fakeLocal() {
return {
initialize: jest.fn().mockResolvedValue(),
publish: jest.fn().mockResolvedValue(),
getHistory: jest.fn().mockResolvedValue(['history']),
shutdown: jest.fn().mockResolvedValue()
};
}
function fakeTransport() {
return {
publish: jest.fn().mockResolvedValue(),
subscribe: jest.fn(function (filters, onEvent) { this.filters = filters; this.onEvent = onEvent; }),
close: jest.fn()
};
}
let _n = 0;
function fakeSigner(pubkey = 'ab'.repeat(32)) {
return { pubkey, sign: (tpl) => ({ ...tpl, id: `evt-${++_n}`, pubkey, sig: 'sig' }) };
}
describe('NostrChatProvider decorates the local provider', () => {
test('publish always delivers locally; getHistory/shutdown delegate', async () => {
const local = fakeLocal();
const p = new NostrChatProvider({ local }); // no transport/signer
await p.publish({ scope: 'global', username: 'bob', text: 'hi' });
expect(local.publish).toHaveBeenCalledWith(expect.objectContaining({ text: 'hi' }));
expect(await p.getHistory({ scope: 'global' })).toEqual(['history']);
await p.shutdown();
expect(local.shutdown).toHaveBeenCalled();
});
test('global message is signed and fanned out to the relay', async () => {
const local = fakeLocal(), transport = fakeTransport(), signer = fakeSigner();
const p = new NostrChatProvider({ local, transport, signer, channelTag: 'wowngeon-global', kind: 1 });
await p.publish({ scope: 'global', username: 'bob', text: 'gm', ts: 1_700_000_000_000 });
expect(local.publish).toHaveBeenCalled();
expect(transport.publish).toHaveBeenCalledTimes(1);
const ev = transport.publish.mock.calls[0][0];
expect(ev.kind).toBe(1);
expect(ev.content).toBe('gm');
expect(ev.tags).toEqual(expect.arrayContaining([['t', 'wowngeon-global'], ['n', 'bob']]));
expect(ev.created_at).toBe(1_700_000_000); // ms -> s
});
test('room-scoped and remote messages are NOT fanned out', async () => {
const local = fakeLocal(), transport = fakeTransport(), signer = fakeSigner();
const p = new NostrChatProvider({ local, transport, signer });
await p.publish({ scope: 'tavern:1', username: 'x', text: 'local only' });
await p.publish({ scope: 'global', username: 'x', text: 'echo', remote: true });
expect(local.publish).toHaveBeenCalledTimes(2);
expect(transport.publish).not.toHaveBeenCalled();
});
});
describe('NostrChatProvider receives remote messages', () => {
function connected() {
const local = fakeLocal(), transport = fakeTransport(), signer = fakeSigner();
const p = new NostrChatProvider({ local, transport, signer, now: () => 1_700_000_000_000 });
return { local, transport, signer, p };
}
test('a remote event is delivered locally-only, escaped, never re-published', async () => {
const { local, transport, p } = connected();
await p.initialize();
expect(transport.subscribe).toHaveBeenCalled();
p.onEvent = transport.onEvent; // convenience
transport.onEvent({ id: 'r1', pubkey: 'cc'.repeat(32), created_at: 1_700_000_000, content: '<b>hi</b>', tags: [['t', 'wowngeon-global'], ['n', 'alice']] });
expect(local.publish).toHaveBeenCalledWith(expect.objectContaining({
scope: 'global', username: 'alice', text: '&lt;b&gt;hi&lt;/b&gt;', remote: true
}));
expect(transport.publish).not.toHaveBeenCalled(); // no echo loop
});
test('our own echoes and duplicate ids are dropped', async () => {
const { local, transport, signer, p } = connected();
await p.initialize();
transport.onEvent({ id: 'mine', pubkey: signer.pubkey, content: 'x', tags: [] }); // our pubkey -> drop
transport.onEvent({ id: 'dup', pubkey: 'cc'.repeat(32), content: 'first', tags: [] });
transport.onEvent({ id: 'dup', pubkey: 'cc'.repeat(32), content: 'again', tags: [] }); // same id -> drop
expect(local.publish).toHaveBeenCalledTimes(1);
});
test('remote delivery is rate-limited', async () => {
const local = fakeLocal(), transport = fakeTransport();
const p = new NostrChatProvider({ local, transport, signer: fakeSigner(), maxRemotePerMin: 2, now: () => 1000 });
await p.initialize();
for (let i = 0; i < 5; i++) transport.onEvent({ id: `x${i}`, pubkey: 'dd'.repeat(32), content: 'spam', tags: [] });
expect(local.publish).toHaveBeenCalledTimes(2); // capped
});
});
describe('buildChatProvider (opt-in seam)', () => {
const local = fakeLocal();
test('disabled -> returns the local provider unchanged (behavior-preserving)', () => {
expect(buildChatProvider({ local, env: {} })).toBe(local);
});
test('scope=local -> returns the plain local provider', () => {
expect(buildChatProvider({ local, env: { NOSTR_CHAT_ENABLED: 'true', NOSTR_CHAT_SCOPE: 'local' } })).toBe(local);
});
test('scope=global -> wraps in a NostrChatProvider', () => {
const p = buildChatProvider({ local, env: { NOSTR_CHAT_ENABLED: 'true', NOSTR_RELAYS: 'wss://relay.smirk.cash' } });
expect(p).toBeInstanceOf(NostrChatProvider);
});
});
describe('bridge signer (real nostr-tools round-trip)', () => {
test('null key -> no signer; a real key signs a verifiable event', () => {
expect(createBridgeSigner(null)).toBeNull();
const tools = loadNostrTools();
const sk = tools.generateSecretKey();
const hex = Buffer.from(sk).toString('hex');
const signer = createBridgeSigner(hex);
expect(signer.pubkey).toBe(tools.getPublicKey(sk));
const ev = signer.sign({ kind: 1, tags: [['t', 'wowngeon-global']], content: 'hello' });
expect(tools.verifyEvent(ev)).toBe(true);
expect(ev.content).toBe('hello');
});
});