nostr-mcp-server 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +498 -0
  3. package/build/__tests__/basic.test.js +87 -0
  4. package/build/__tests__/error-handling.test.js +145 -0
  5. package/build/__tests__/format-conversion.test.js +137 -0
  6. package/build/__tests__/integration.test.js +163 -0
  7. package/build/__tests__/mocks.js +109 -0
  8. package/build/__tests__/nip19-conversion.test.js +268 -0
  9. package/build/__tests__/nips-search.test.js +109 -0
  10. package/build/__tests__/note-creation.test.js +148 -0
  11. package/build/__tests__/note-tools-functions.test.js +173 -0
  12. package/build/__tests__/note-tools-unit.test.js +97 -0
  13. package/build/__tests__/profile-notes-simple.test.js +78 -0
  14. package/build/__tests__/profile-postnote.test.js +120 -0
  15. package/build/__tests__/profile-tools.test.js +90 -0
  16. package/build/__tests__/relay-specification.test.js +136 -0
  17. package/build/__tests__/search-nips-simple.test.js +96 -0
  18. package/build/__tests__/websocket-integration.test.js +257 -0
  19. package/build/__tests__/zap-tools-simple.test.js +72 -0
  20. package/build/__tests__/zap-tools-tests.test.js +197 -0
  21. package/build/index.js +1285 -0
  22. package/build/nips/nips-tools.js +567 -0
  23. package/build/nips-tools.js +421 -0
  24. package/build/note/note-tools.js +296 -0
  25. package/build/note-tools.js +53 -0
  26. package/build/profile/profile-tools.js +260 -0
  27. package/build/utils/constants.js +27 -0
  28. package/build/utils/conversion.js +332 -0
  29. package/build/utils/ephemeral-relay.js +438 -0
  30. package/build/utils/formatting.js +34 -0
  31. package/build/utils/index.js +6 -0
  32. package/build/utils/nip19-tools.js +117 -0
  33. package/build/utils/pool.js +55 -0
  34. package/build/zap/zap-tools.js +980 -0
  35. package/build/zap-tools.js +989 -0
  36. package/package.json +59 -0
@@ -0,0 +1,145 @@
1
+ import { describe, expect, test, jest, beforeEach } from '@jest/globals';
2
+ // Mock the actual Nostr pool functionality
3
+ jest.mock('../utils/pool.js', () => {
4
+ return {
5
+ getRelayPool: jest.fn(() => ({
6
+ connect: jest.fn(),
7
+ close: jest.fn(),
8
+ subscribeMany: jest.fn(),
9
+ })),
10
+ };
11
+ });
12
+ // Mock setTimeout for testing timeouts
13
+ jest.useFakeTimers();
14
+ // Helper function for creating timed out promises
15
+ const createTimedOutPromise = () => new Promise((resolve, reject) => {
16
+ setTimeout(() => reject(new Error('Request timed out')), 10000);
17
+ });
18
+ // Mock getProfile that simulates various error scenarios
19
+ const mockGetProfile = jest.fn();
20
+ // Helpers for common error scenarios
21
+ const simulateTimeoutError = () => {
22
+ mockGetProfile.mockImplementationOnce(() => createTimedOutPromise());
23
+ };
24
+ const simulateNetworkError = () => {
25
+ mockGetProfile.mockImplementationOnce(() => Promise.reject(new Error('Failed to connect to relay')));
26
+ };
27
+ const simulateInvalidPubkey = () => {
28
+ mockGetProfile.mockImplementationOnce(() => Promise.reject(new Error('Invalid pubkey format')));
29
+ };
30
+ const simulateMalformedEvent = () => {
31
+ mockGetProfile.mockImplementationOnce(() => Promise.resolve({
32
+ error: 'Malformed event',
33
+ details: 'Event missing required signature'
34
+ }));
35
+ };
36
+ describe('Error Handling and Edge Cases', () => {
37
+ beforeEach(() => {
38
+ mockGetProfile.mockReset();
39
+ });
40
+ test('timeout handling', async () => {
41
+ simulateTimeoutError();
42
+ try {
43
+ jest.useFakeTimers();
44
+ const profilePromise = mockGetProfile('valid-pubkey');
45
+ // Fast-forward time to trigger timeout
46
+ jest.advanceTimersByTime(10000);
47
+ await profilePromise;
48
+ fail('Expected promise to reject with timeout error');
49
+ }
50
+ catch (error) {
51
+ expect(error.message).toContain('timed out');
52
+ }
53
+ });
54
+ test('invalid pubkey format handling', async () => {
55
+ simulateInvalidPubkey();
56
+ try {
57
+ await mockGetProfile('invalid-pubkey-format');
58
+ fail('Expected promise to reject with invalid pubkey error');
59
+ }
60
+ catch (error) {
61
+ expect(error.message).toContain('Invalid pubkey');
62
+ }
63
+ });
64
+ test('network error handling', async () => {
65
+ simulateNetworkError();
66
+ try {
67
+ await mockGetProfile('valid-pubkey');
68
+ fail('Expected promise to reject with network error');
69
+ }
70
+ catch (error) {
71
+ expect(error.message).toContain('Failed to connect');
72
+ }
73
+ });
74
+ test('malformed event handling', async () => {
75
+ simulateMalformedEvent();
76
+ const result = await mockGetProfile('valid-pubkey');
77
+ expect(result.error).toBeDefined();
78
+ expect(result.error).toContain('Malformed event');
79
+ });
80
+ test('empty pubkey handling', async () => {
81
+ // Empty string pubkey
82
+ simulateInvalidPubkey();
83
+ try {
84
+ await mockGetProfile('');
85
+ fail('Expected promise to reject with invalid pubkey error');
86
+ }
87
+ catch (error) {
88
+ expect(error.message).toContain('Invalid pubkey');
89
+ }
90
+ });
91
+ test('extremely long pubkey handling', async () => {
92
+ // Extremely long input should be rejected
93
+ simulateInvalidPubkey();
94
+ const veryLongPubkey = 'a'.repeat(1000);
95
+ try {
96
+ await mockGetProfile(veryLongPubkey);
97
+ fail('Expected promise to reject with invalid pubkey error');
98
+ }
99
+ catch (error) {
100
+ expect(error.message).toContain('Invalid pubkey');
101
+ }
102
+ });
103
+ test('special character handling in pubkey', async () => {
104
+ // Pubkey with special characters
105
+ simulateInvalidPubkey();
106
+ try {
107
+ await mockGetProfile('npub1<script>alert("xss")</script>');
108
+ fail('Expected promise to reject with invalid pubkey error');
109
+ }
110
+ catch (error) {
111
+ expect(error.message).toContain('Invalid pubkey');
112
+ }
113
+ });
114
+ test('null or undefined pubkey handling', async () => {
115
+ // Null pubkey
116
+ simulateInvalidPubkey();
117
+ try {
118
+ await mockGetProfile(null);
119
+ fail('Expected promise to reject with invalid pubkey error');
120
+ }
121
+ catch (error) {
122
+ expect(error.message).toContain('Invalid pubkey');
123
+ }
124
+ // Undefined pubkey
125
+ simulateInvalidPubkey();
126
+ try {
127
+ await mockGetProfile(undefined);
128
+ fail('Expected promise to reject with invalid pubkey error');
129
+ }
130
+ catch (error) {
131
+ expect(error.message).toContain('Invalid pubkey');
132
+ }
133
+ });
134
+ test('all relays failing scenario', async () => {
135
+ // Simulate all relays failing
136
+ simulateNetworkError();
137
+ try {
138
+ await mockGetProfile('valid-pubkey', { relays: ['wss://relay1.example.com', 'wss://relay2.example.com'] });
139
+ fail('Expected promise to reject when all relays fail');
140
+ }
141
+ catch (error) {
142
+ expect(error.message).toContain('Failed to connect');
143
+ }
144
+ });
145
+ });
@@ -0,0 +1,137 @@
1
+ import { describe, expect, test } from '@jest/globals';
2
+ import * as nip19 from 'nostr-tools/nip19';
3
+ // Import local conversion utilities
4
+ import { npubToHex, hexToNpub } from '../utils/conversion.js';
5
+ describe('Nostr format conversion', () => {
6
+ // Known test vectors - generate these dynamically to ensure they match
7
+ const testHexPubkeys = [
8
+ '3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d',
9
+ '63fe6318dc58583cfe16810f86dd09e18bfd76aabc24a0081ce2856f330504ed'
10
+ ];
11
+ // Generate matching npubs using the library directly
12
+ const knownPairs = testHexPubkeys.map(hex => ({
13
+ hex,
14
+ npub: nip19.npubEncode(hex)
15
+ }));
16
+ // Generate test event IDs and their encoded forms
17
+ const testEventIds = [
18
+ '5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36',
19
+ '9ae37aa68f48645127299e9595f545f942a8314455cbafd9d913ed19c7fc0462'
20
+ ];
21
+ const eventIdPairs = testEventIds.map(hex => ({
22
+ hex,
23
+ note: nip19.noteEncode(hex)
24
+ }));
25
+ test('hex to npub conversion', () => {
26
+ knownPairs.forEach(pair => {
27
+ expect(hexToNpub(pair.hex)).toBe(pair.npub);
28
+ });
29
+ });
30
+ test('npub to hex conversion', () => {
31
+ knownPairs.forEach(pair => {
32
+ const result = npubToHex(pair.npub);
33
+ expect(result).toBe(pair.hex);
34
+ });
35
+ });
36
+ test('hex to note conversion using nostr-tools', () => {
37
+ eventIdPairs.forEach(pair => {
38
+ expect(nip19.noteEncode(pair.hex)).toBe(pair.note);
39
+ });
40
+ });
41
+ test('note to hex conversion using nostr-tools', () => {
42
+ eventIdPairs.forEach(pair => {
43
+ const decoded = nip19.decode(pair.note);
44
+ expect(decoded.type).toBe('note');
45
+ expect(decoded.data).toBe(pair.hex);
46
+ });
47
+ });
48
+ test('nevent encoding and decoding using nostr-tools', () => {
49
+ const eventPointer = {
50
+ id: '5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36',
51
+ relays: ['wss://relay.example.com', 'wss://relay.nostr.org'],
52
+ author: '3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d',
53
+ kind: 1
54
+ };
55
+ const nevent = nip19.neventEncode(eventPointer);
56
+ expect(nevent).toMatch(/^nevent1/);
57
+ const decoded = nip19.decode(nevent);
58
+ expect(decoded.type).toBe('nevent');
59
+ const data = decoded.data;
60
+ expect(data.id).toBe(eventPointer.id);
61
+ expect(data.author).toBe(eventPointer.author);
62
+ expect(data.kind).toBe(eventPointer.kind);
63
+ expect(data.relays).toEqual(eventPointer.relays);
64
+ });
65
+ test('naddr encoding and decoding using nostr-tools', () => {
66
+ const addressPointer = {
67
+ identifier: 'test-article',
68
+ pubkey: '3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d',
69
+ kind: 30023,
70
+ relays: ['wss://relay.example.com']
71
+ };
72
+ const naddr = nip19.naddrEncode(addressPointer);
73
+ expect(naddr).toMatch(/^naddr1/);
74
+ const decoded = nip19.decode(naddr);
75
+ expect(decoded.type).toBe('naddr');
76
+ const data = decoded.data;
77
+ expect(data.identifier).toBe(addressPointer.identifier);
78
+ expect(data.pubkey).toBe(addressPointer.pubkey);
79
+ expect(data.kind).toBe(addressPointer.kind);
80
+ expect(data.relays).toEqual(addressPointer.relays);
81
+ });
82
+ test('validate Nostr identifiers using regex', () => {
83
+ // Valid identifiers
84
+ expect(/^npub1[a-z0-9]{58}$/.test(knownPairs[0].npub)).toBe(true);
85
+ expect(/^note1[a-z0-9]{58}$/.test(eventIdPairs[0].note)).toBe(true);
86
+ // Invalid identifiers with correct regex patterns
87
+ expect(/^npub1[a-z0-9]{58}$/.test('npub1invalid')).toBe(false);
88
+ expect(/^note1[a-z0-9]{58}$/.test('note1invalid')).toBe(false);
89
+ // Fix the regex patterns to be more specific
90
+ expect(/^nevent1[a-z0-9]{58,}$/.test('nevent1invalid')).toBe(false);
91
+ expect(/^naddr1[a-z0-9]{58,}$/.test('naddr1invalid')).toBe(false);
92
+ });
93
+ test('error handling for npub conversion', () => {
94
+ expect(npubToHex('npub1invalid')).toBeNull();
95
+ expect(hexToNpub('invalidhex')).toBeNull();
96
+ });
97
+ test('partial nevent data', () => {
98
+ // Test with minimal data
99
+ const minimalPointer = {
100
+ id: '5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36'
101
+ };
102
+ const nevent = nip19.neventEncode(minimalPointer);
103
+ expect(nevent).toMatch(/^nevent1/);
104
+ const decoded = nip19.decode(nevent);
105
+ expect(decoded.type).toBe('nevent');
106
+ const data = decoded.data;
107
+ expect(data.id).toBe(minimalPointer.id);
108
+ expect(data.relays).toEqual([]);
109
+ expect(data.author).toBeUndefined();
110
+ expect(data.kind).toBeUndefined();
111
+ });
112
+ test('bech32 conversion limits', () => {
113
+ // Test with very long relay list
114
+ const manyRelays = Array(20).fill(0).map((_, i) => `wss://relay${i}.example.com`);
115
+ const eventPointer = {
116
+ id: '5c83da77af1dec6d7289834998ad7aafbd9e2191396d75ec3cc27f5a77226f36',
117
+ relays: manyRelays
118
+ };
119
+ const nevent = nip19.neventEncode(eventPointer);
120
+ expect(nevent).toMatch(/^nevent1/);
121
+ const decoded = nip19.decode(nevent);
122
+ expect(decoded.type).toBe('nevent');
123
+ const data = decoded.data;
124
+ expect(data.id).toBe(eventPointer.id);
125
+ expect(data.relays.length).toBe(manyRelays.length);
126
+ });
127
+ test('edge case: nostr URI handling', () => {
128
+ // Generate a fresh npub
129
+ const hex = '3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d';
130
+ const npub = nip19.npubEncode(hex);
131
+ // Test with nostr: prefix
132
+ const nostrUriNpub = 'nostr:' + npub;
133
+ // When handling the nostr: prefix in our conversion functions
134
+ const result = npubToHex(nostrUriNpub.replace('nostr:', ''));
135
+ expect(result).toBe(hex);
136
+ });
137
+ });
@@ -0,0 +1,163 @@
1
+ import { NostrRelay } from '../utils/ephemeral-relay.js';
2
+ import { schnorr } from '@noble/curves/secp256k1';
3
+ import { randomBytes } from 'crypto';
4
+ import { sha256 } from '@noble/hashes/sha256';
5
+ // Generate a keypair for testing
6
+ function generatePrivateKey() {
7
+ return Buffer.from(randomBytes(32)).toString('hex');
8
+ }
9
+ function getPublicKey(privateKey) {
10
+ return Buffer.from(schnorr.getPublicKey(privateKey)).toString('hex');
11
+ }
12
+ // Create a signed event
13
+ function createSignedEvent(privateKey, kind, content, tags = []) {
14
+ const pubkey = getPublicKey(privateKey);
15
+ const created_at = Math.floor(Date.now() / 1000);
16
+ // Create event
17
+ const event = {
18
+ pubkey,
19
+ created_at,
20
+ kind,
21
+ tags,
22
+ content,
23
+ };
24
+ // Calculate event ID
25
+ const eventData = JSON.stringify([0, event.pubkey, event.created_at, event.kind, event.tags, event.content]);
26
+ const id = Buffer.from(sha256(eventData)).toString('hex');
27
+ // Sign the event
28
+ const sig = Buffer.from(schnorr.sign(id, privateKey)).toString('hex');
29
+ return {
30
+ ...event,
31
+ id,
32
+ sig
33
+ };
34
+ }
35
+ describe('Nostr Integration Tests', () => {
36
+ let relay;
37
+ const testPort = 9700;
38
+ let privateKey;
39
+ let publicKey;
40
+ beforeAll(async () => {
41
+ privateKey = generatePrivateKey();
42
+ publicKey = getPublicKey(privateKey);
43
+ // Start the ephemeral relay
44
+ relay = new NostrRelay(testPort);
45
+ await relay.start();
46
+ });
47
+ afterAll(async () => {
48
+ // Shutdown relay
49
+ await relay.close();
50
+ });
51
+ test('should publish and retrieve a profile', async () => {
52
+ // Create a profile event (kind 0)
53
+ const profileContent = JSON.stringify({
54
+ name: 'Test User',
55
+ about: 'This is a test profile',
56
+ picture: 'https://example.com/avatar.jpg'
57
+ });
58
+ const profileEvent = createSignedEvent(privateKey, 0, profileContent);
59
+ // Store it in the relay
60
+ relay.store(profileEvent);
61
+ // Verify it was stored
62
+ expect(relay.cache.length).toBeGreaterThan(0);
63
+ // Find the profile in the cache
64
+ const retrievedProfile = relay.cache.find(event => event.kind === 0 && event.pubkey === publicKey);
65
+ // Verify profile data
66
+ expect(retrievedProfile).toBeDefined();
67
+ expect(retrievedProfile?.id).toBe(profileEvent.id);
68
+ // Parse the content
69
+ const parsedContent = JSON.parse(retrievedProfile?.content || '{}');
70
+ expect(parsedContent.name).toBe('Test User');
71
+ expect(parsedContent.about).toBe('This is a test profile');
72
+ });
73
+ test('should publish and retrieve a text note', async () => {
74
+ // Create a text note (kind 1)
75
+ const noteContent = 'This is a test note posted from integration tests!';
76
+ const noteEvent = createSignedEvent(privateKey, 1, noteContent);
77
+ // Store it in the relay
78
+ relay.store(noteEvent);
79
+ // Find the note in the cache
80
+ const retrievedNote = relay.cache.find(event => event.kind === 1 && event.pubkey === publicKey && event.content === noteContent);
81
+ // Verify note data
82
+ expect(retrievedNote).toBeDefined();
83
+ expect(retrievedNote?.id).toBe(noteEvent.id);
84
+ expect(retrievedNote?.content).toBe(noteContent);
85
+ });
86
+ test('should publish and retrieve a zap receipt', async () => {
87
+ // Create a mock recipient public key
88
+ const recipientKey = generatePrivateKey();
89
+ const recipientPubkey = getPublicKey(recipientKey);
90
+ // Create zap receipt tags
91
+ const zapTags = [
92
+ ['p', recipientPubkey],
93
+ ['amount', '100000'], // 100 sats in millisats
94
+ ['bolt11', 'lnbc100n...'],
95
+ ['description', ''],
96
+ ];
97
+ // Create a zap receipt (kind 9735)
98
+ const zapEvent = createSignedEvent(privateKey, 9735, '', zapTags);
99
+ // Store it in the relay
100
+ relay.store(zapEvent);
101
+ // Find the zap in the cache
102
+ const retrievedZap = relay.cache.find(event => event.kind === 9735 && event.pubkey === publicKey);
103
+ // Verify zap data
104
+ expect(retrievedZap).toBeDefined();
105
+ expect(retrievedZap?.id).toBe(zapEvent.id);
106
+ // Verify zap tags
107
+ const pTag = retrievedZap?.tags.find(tag => tag[0] === 'p');
108
+ const amountTag = retrievedZap?.tags.find(tag => tag[0] === 'amount');
109
+ expect(pTag?.[1]).toBe(recipientPubkey);
110
+ expect(amountTag?.[1]).toBe('100000');
111
+ });
112
+ test('should filter events correctly', async () => {
113
+ // Create multiple events of different kinds
114
+ const profileEvent = createSignedEvent(privateKey, 0, JSON.stringify({ name: 'Filter Test' }));
115
+ const textNote1 = createSignedEvent(privateKey, 1, 'Filter test note 1');
116
+ const textNote2 = createSignedEvent(privateKey, 1, 'Filter test note 2');
117
+ const reactionEvent = createSignedEvent(privateKey, 7, '+', [['e', 'fake-event-id']]);
118
+ // Store all events
119
+ relay.store(profileEvent);
120
+ relay.store(textNote1);
121
+ relay.store(textNote2);
122
+ relay.store(reactionEvent);
123
+ // Filter for just kind 1 events
124
+ const textNotes = relay.cache.filter(event => event.kind === 1 && event.pubkey === publicKey);
125
+ // We should have at least 3 text notes (2 from this test plus 1 from earlier test)
126
+ expect(textNotes.length).toBeGreaterThanOrEqual(3);
127
+ // Filter for reaction events
128
+ const reactions = relay.cache.filter(event => event.kind === 7 && event.pubkey === publicKey);
129
+ expect(reactions.length).toBeGreaterThanOrEqual(1);
130
+ expect(reactions[0].content).toBe('+');
131
+ });
132
+ // The ephemeral-relay validates events during WebSocket communication,
133
+ // but doesn't validate during direct store() calls - this test verifies this behavior
134
+ test('should store events without validation when using direct store() method', () => {
135
+ // Create a properly signed event
136
+ const signedEvent = createSignedEvent(privateKey, 1, 'Verification test');
137
+ // Store it in the relay
138
+ relay.store(signedEvent);
139
+ // Create an event with invalid signature
140
+ const invalidEvent = {
141
+ pubkey: publicKey,
142
+ created_at: Math.floor(Date.now() / 1000),
143
+ kind: 1,
144
+ tags: [],
145
+ content: 'Invalid signature event',
146
+ id: 'invalid_id_that_doesnt_match_content',
147
+ sig: 'invalid_signature_0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
148
+ };
149
+ // Get the current cache size
150
+ const cacheSizeBefore = relay.cache.length;
151
+ // Store the invalid event (this should succeed since store() doesn't validate)
152
+ relay.store(invalidEvent);
153
+ // Cache size should increase since the invalid event should be added
154
+ const cacheSizeAfter = relay.cache.length;
155
+ // Verify the event was added (expected behavior for direct store calls)
156
+ expect(cacheSizeAfter).toBe(cacheSizeBefore + 1);
157
+ // Find the invalid event in the cache
158
+ const invalidEventInCache = relay.cache.find(event => event.id === 'invalid_id_that_doesnt_match_content');
159
+ expect(invalidEventInCache).toBeDefined();
160
+ // Note: This confirms the current behavior, but in websocket-integration.test.ts we
161
+ // verify that invalid events are properly rejected over WebSocket communication
162
+ });
163
+ });
@@ -0,0 +1,109 @@
1
+ // Mock Nostr events and utility functions for testing
2
+ import { jest } from '@jest/globals';
3
+ export const MOCK_HEX_PUBKEY = '7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e';
4
+ export const MOCK_NPUB = 'npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6';
5
+ export const mockProfile = {
6
+ id: '1234',
7
+ pubkey: MOCK_HEX_PUBKEY,
8
+ created_at: Math.floor(Date.now() / 1000) - 3600,
9
+ kind: 0,
10
+ tags: [],
11
+ content: JSON.stringify({
12
+ name: 'Test User',
13
+ display_name: 'Tester',
14
+ about: 'A test profile for unit tests',
15
+ picture: 'https://example.com/avatar.jpg',
16
+ nip05: 'test@example.com'
17
+ }),
18
+ sig: 'mock_signature'
19
+ };
20
+ export const mockNote = {
21
+ id: '5678',
22
+ pubkey: MOCK_HEX_PUBKEY,
23
+ created_at: Math.floor(Date.now() / 1000) - 1800,
24
+ kind: 1,
25
+ tags: [],
26
+ content: 'This is a test note from the test user.',
27
+ sig: 'mock_signature'
28
+ };
29
+ export const mockLongFormNote = {
30
+ id: '9012',
31
+ pubkey: MOCK_HEX_PUBKEY,
32
+ created_at: Math.floor(Date.now() / 1000) - 86400,
33
+ kind: 30023,
34
+ tags: [
35
+ ['title', 'Test Long Form Content'],
36
+ ['summary', 'This is a test summary of a long form article'],
37
+ ['published_at', (Math.floor(Date.now() / 1000) - 86400).toString()],
38
+ ['d', 'test-identifier']
39
+ ],
40
+ content: 'This is a test long form content article with much more text than a normal note would have.',
41
+ sig: 'mock_signature'
42
+ };
43
+ export const mockZapReceipt = {
44
+ id: 'abcd',
45
+ pubkey: 'lightning_service_pubkey',
46
+ created_at: Math.floor(Date.now() / 1000) - 900,
47
+ kind: 9735,
48
+ tags: [
49
+ ['p', MOCK_HEX_PUBKEY],
50
+ ['bolt11', 'lnbc100n1...'],
51
+ ['description', JSON.stringify({
52
+ content: '',
53
+ created_at: Math.floor(Date.now() / 1000) - 901,
54
+ id: 'zap_request_id',
55
+ kind: 9734,
56
+ pubkey: 'sender_pubkey',
57
+ tags: [
58
+ ['amount', '10000'], // 100 sats in millisats
59
+ ['relays', 'wss://relay.example.com'],
60
+ ['p', MOCK_HEX_PUBKEY]
61
+ ]
62
+ })]
63
+ ],
64
+ content: '',
65
+ sig: 'mock_signature'
66
+ };
67
+ // Mock pool functions
68
+ export const mockPool = {
69
+ get: jest.fn(),
70
+ querySync: jest.fn(),
71
+ close: jest.fn()
72
+ };
73
+ // Mock for getFreshPool function
74
+ export const getFreshPoolMock = jest.fn().mockReturnValue(mockPool);
75
+ // Mock response for lightning service for anonymous zaps
76
+ export const mockLightningServiceResponse = {
77
+ callback: 'https://example.com/callback',
78
+ maxSendable: 100000000,
79
+ minSendable: 1000,
80
+ metadata: JSON.stringify({
81
+ name: 'Test User',
82
+ pubkey: MOCK_HEX_PUBKEY
83
+ }),
84
+ allowsNostr: true,
85
+ nostrPubkey: MOCK_HEX_PUBKEY
86
+ };
87
+ // Mock response for invoice generation
88
+ export const mockInvoiceResponse = {
89
+ pr: 'lnbc100n1...', // Mock lightning invoice
90
+ success: true,
91
+ verify: 'https://example.com/verify'
92
+ };
93
+ // Mock response for NIP search
94
+ export const mockNipSearchResults = [
95
+ {
96
+ number: 57,
97
+ title: 'Lightning Zaps',
98
+ summary: 'This NIP defines a protocol for sending zaps via the Lightning Network.',
99
+ relevance: 0.95,
100
+ content: '# NIP-57\n\n## Lightning Zaps\n\nThis is mock content for the zaps NIP.'
101
+ },
102
+ {
103
+ number: 1,
104
+ title: 'Basic protocol flow description',
105
+ summary: 'Basic protocol flow and interaction between clients and relays.',
106
+ relevance: 0.5,
107
+ content: '# NIP-01\n\n## Basic protocol flow description\n\nThis is mock content for the basic protocol NIP.'
108
+ }
109
+ ];