@bsv/sdk 2.0.12 → 2.0.13

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 (77) hide show
  1. package/dist/cjs/package.json +1 -1
  2. package/dist/cjs/src/auth/clients/__tests__/AuthFetch.additional.test.js +827 -0
  3. package/dist/cjs/src/auth/clients/__tests__/AuthFetch.additional.test.js.map +1 -0
  4. package/dist/cjs/src/auth/transports/__tests__/SimplifiedFetchTransport.additional.test.js +654 -0
  5. package/dist/cjs/src/auth/transports/__tests__/SimplifiedFetchTransport.additional.test.js.map +1 -0
  6. package/dist/cjs/src/transaction/MerklePath.js +132 -0
  7. package/dist/cjs/src/transaction/MerklePath.js.map +1 -1
  8. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  9. package/dist/esm/src/auth/clients/__tests__/AuthFetch.additional.test.js +825 -0
  10. package/dist/esm/src/auth/clients/__tests__/AuthFetch.additional.test.js.map +1 -0
  11. package/dist/esm/src/auth/transports/__tests__/SimplifiedFetchTransport.additional.test.js +619 -0
  12. package/dist/esm/src/auth/transports/__tests__/SimplifiedFetchTransport.additional.test.js.map +1 -0
  13. package/dist/esm/src/transaction/MerklePath.js +132 -0
  14. package/dist/esm/src/transaction/MerklePath.js.map +1 -1
  15. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  16. package/dist/types/src/auth/clients/__tests__/AuthFetch.additional.test.d.ts +21 -0
  17. package/dist/types/src/auth/clients/__tests__/AuthFetch.additional.test.d.ts.map +1 -0
  18. package/dist/types/src/auth/transports/__tests__/SimplifiedFetchTransport.additional.test.d.ts +2 -0
  19. package/dist/types/src/auth/transports/__tests__/SimplifiedFetchTransport.additional.test.d.ts.map +1 -0
  20. package/dist/types/src/transaction/MerklePath.d.ts +27 -0
  21. package/dist/types/src/transaction/MerklePath.d.ts.map +1 -1
  22. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  23. package/dist/umd/bundle.js +1 -1
  24. package/dist/umd/bundle.js.map +1 -1
  25. package/docs/reference/storage.md +1 -1
  26. package/docs/reference/transaction.md +40 -0
  27. package/package.json +1 -1
  28. package/src/auth/clients/__tests__/AuthFetch.additional.test.ts +1131 -0
  29. package/src/auth/transports/__tests__/SimplifiedFetchTransport.additional.test.ts +770 -0
  30. package/src/compat/__tests/Mnemonic.additional.test.ts +64 -0
  31. package/src/identity/__tests/IdentityClient.additional.test.ts +767 -0
  32. package/src/kvstore/__tests/LocalKVStore.additional.test.ts +611 -0
  33. package/src/kvstore/__tests/kvStoreInterpreter.test.ts +327 -0
  34. package/src/overlay-tools/__tests/HostReputationTracker.additional.test.ts +561 -0
  35. package/src/overlay-tools/__tests/LookupResolver.additional.test.ts +612 -0
  36. package/src/overlay-tools/__tests/withDoubleSpendRetry.test.ts +278 -0
  37. package/src/primitives/__tests/BigNumber.additional.test.ts +79 -0
  38. package/src/primitives/__tests/Curve.additional.test.ts +208 -0
  39. package/src/primitives/__tests/ECDSA.additional.test.ts +122 -0
  40. package/src/primitives/__tests/Hash.additional.test.ts +59 -0
  41. package/src/primitives/__tests/JacobianPoint.test.ts +308 -0
  42. package/src/primitives/__tests/Point.additional.test.ts +503 -0
  43. package/src/primitives/__tests/PublicKey.additional.test.ts +383 -0
  44. package/src/primitives/__tests/Random.additional.test.ts +262 -0
  45. package/src/primitives/__tests/Signature.test.ts +333 -0
  46. package/src/primitives/__tests/TransactionSignature.additional.test.ts +241 -0
  47. package/src/registry/__tests/RegistryClient.additional.test.ts +750 -0
  48. package/src/remittance/__tests/BasicBRC29.additional.test.ts +657 -0
  49. package/src/remittance/__tests/RemittanceManager.additional.test.ts +1272 -0
  50. package/src/script/__tests/LockingUnlockingScript.test.ts +79 -0
  51. package/src/script/__tests/Script.additional.test.ts +100 -0
  52. package/src/script/__tests/ScriptEvaluationError.test.ts +98 -0
  53. package/src/script/__tests/Spend.additional.test.ts +837 -0
  54. package/src/script/templates/__tests/RPuzzle.test.ts +134 -0
  55. package/src/transaction/MerklePath.ts +155 -0
  56. package/src/transaction/__tests/BeefParty.additional.test.ts +22 -0
  57. package/src/transaction/__tests/Broadcaster.test.ts +159 -0
  58. package/src/transaction/__tests/MerklePath.bench.test.ts +105 -0
  59. package/src/transaction/__tests/MerklePath.test.ts +80 -0
  60. package/src/transaction/__tests/Transaction.additional.test.ts +225 -0
  61. package/src/transaction/broadcasters/__tests/ARC.additional.test.ts +585 -0
  62. package/src/transaction/broadcasters/__tests/Teranode.test.ts +349 -0
  63. package/src/transaction/chaintrackers/__tests/BlockHeadersService.test.ts +253 -0
  64. package/src/transaction/chaintrackers/__tests/DefaultChainTracker.test.ts +44 -0
  65. package/src/transaction/chaintrackers/__tests/WhatsOnChain.additional.test.ts +193 -0
  66. package/src/transaction/fee-models/__tests/SatoshisPerKilobyte.test.ts +262 -0
  67. package/src/transaction/http/__tests/BinaryFetchClient.test.ts +212 -0
  68. package/src/transaction/http/__tests/DefaultHttpClient.additional.test.ts +192 -0
  69. package/src/transaction/http/__tests/DefaultHttpClient.test.ts +71 -0
  70. package/src/wallet/__tests/ProtoWallet.additional.test.ts +134 -0
  71. package/src/wallet/__tests/WERR.test.ts +212 -0
  72. package/src/wallet/__tests/WalletClient.additional.test.ts +699 -0
  73. package/src/wallet/__tests/WalletClient.substrate.test.ts +759 -0
  74. package/src/wallet/__tests/WalletError.test.ts +290 -0
  75. package/src/wallet/__tests/validationHelpers.test.ts +1218 -0
  76. package/src/wallet/substrates/__tests/HTTPWalletJSON.test.ts +496 -0
  77. package/src/wallet/substrates/__tests/HTTPWalletWire.test.ts +273 -0
@@ -0,0 +1,192 @@
1
+ // Additional coverage for DefaultHttpClient.ts lines 13 and 28-31:
2
+ // Line 13 — noHttpClient.request throws 'No method available...'
3
+ // Lines 28-31 — catch(e) path when require('https') throws → returns noHttpClient
4
+ // — else path when require is undefined → returns noHttpClient
5
+ //
6
+ // Strategy: jest.isolateModules() reloads the module fresh for every sub-test,
7
+ // allowing us to control whether `https` throws on require.
8
+
9
+ describe('defaultHttpClient — noHttpClient fallback paths', () => {
10
+ afterEach(() => {
11
+ if ('window' in globalThis) {
12
+ delete (globalThis as { window?: unknown }).window
13
+ }
14
+ jest.resetModules()
15
+ })
16
+
17
+ // --------------------------------------------------------------------------
18
+ // Lines 28-29: require('https') throws → fall back to noHttpClient
19
+ // Line 13: noHttpClient.request() throws the expected error message
20
+ // --------------------------------------------------------------------------
21
+
22
+ it('returns a noHttpClient that throws when require("https") throws', async () => {
23
+ // Make the https module unavailable so the try/catch in DefaultHttpClient fires
24
+ jest.mock('https', () => {
25
+ throw new Error('https module not available')
26
+ })
27
+
28
+ // window must be absent so the fetch branch is skipped
29
+ if ('window' in globalThis) {
30
+ delete (globalThis as { window?: unknown }).window
31
+ }
32
+
33
+ let defaultHttpClient: any
34
+
35
+ jest.isolateModules(() => {
36
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
37
+ const mod = require('../../../transaction/http/DefaultHttpClient')
38
+ defaultHttpClient = mod.defaultHttpClient
39
+ })
40
+
41
+ // The function must still return a client object (noHttpClient)
42
+ const client = (defaultHttpClient as any)()
43
+ expect(client).toBeDefined()
44
+ expect(typeof client.request).toBe('function')
45
+
46
+ // Calling request() must throw 'No method available to perform HTTP request'
47
+ await expect(
48
+ client.request('https://example.com', { method: 'GET' })
49
+ ).rejects.toThrow('No method available to perform HTTP request')
50
+ })
51
+
52
+ // --------------------------------------------------------------------------
53
+ // Additional: verify the error message text exactly (line 13 text coverage)
54
+ // --------------------------------------------------------------------------
55
+
56
+ it('noHttpClient.request throws with the exact message text', async () => {
57
+ jest.mock('https', () => {
58
+ throw new Error('https not available')
59
+ })
60
+
61
+ if ('window' in globalThis) {
62
+ delete (globalThis as { window?: unknown }).window
63
+ }
64
+
65
+ let client: any
66
+
67
+ jest.isolateModules(() => {
68
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
69
+ const mod = require('../../../transaction/http/DefaultHttpClient')
70
+ client = mod.defaultHttpClient()
71
+ })
72
+
73
+ let thrown: Error | undefined
74
+ try {
75
+ await client.request('https://example.com', { method: 'POST', data: {} })
76
+ } catch (e) {
77
+ thrown = e as Error
78
+ }
79
+
80
+ expect(thrown).toBeInstanceOf(Error)
81
+ expect(thrown?.message).toBe('No method available to perform HTTP request')
82
+ })
83
+
84
+ // --------------------------------------------------------------------------
85
+ // Sanity: confirm the Node.js happy path still works after mock is cleared
86
+ // --------------------------------------------------------------------------
87
+
88
+ it('returns a working NodejsHttpClient when require("https") succeeds (Node.js path)', () => {
89
+ if ('window' in globalThis) {
90
+ delete (globalThis as { window?: unknown }).window
91
+ }
92
+
93
+ let defaultHttpClient: any
94
+ jest.isolateModules(() => {
95
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
96
+ const mod = require('../../../transaction/http/DefaultHttpClient')
97
+ defaultHttpClient = mod.defaultHttpClient
98
+ })
99
+
100
+ const client = defaultHttpClient()
101
+ expect(client).toBeDefined()
102
+ expect(typeof client.request).toBe('function')
103
+ })
104
+ })
105
+
106
+ // --------------------------------------------------------------------------
107
+ // Additional coverage for BinaryFetchClient.ts lines 97, 112-115:
108
+ // Line 97 — noHttpClient.request throws 'No method available...'
109
+ // Lines 112-113 — catch(e) path when require('https') throws
110
+ // Lines 114-115 — else path when require is undefined
111
+ // --------------------------------------------------------------------------
112
+
113
+ describe('binaryHttpClient — noHttpClient fallback paths', () => {
114
+ afterEach(() => {
115
+ if ('window' in globalThis) {
116
+ delete (globalThis as { window?: unknown }).window
117
+ }
118
+ jest.resetModules()
119
+ })
120
+
121
+ it('returns a noHttpClient that throws when require("https") throws', async () => {
122
+ jest.mock('https', () => {
123
+ throw new Error('https module not available')
124
+ })
125
+
126
+ if ('window' in globalThis) {
127
+ delete (globalThis as { window?: unknown }).window
128
+ }
129
+
130
+ let binaryHttpClient: any
131
+
132
+ jest.isolateModules(() => {
133
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
134
+ const mod = require('../../../transaction/http/BinaryFetchClient')
135
+ binaryHttpClient = mod.binaryHttpClient
136
+ })
137
+
138
+ const client = binaryHttpClient()
139
+ expect(client).toBeDefined()
140
+ expect(typeof client.request).toBe('function')
141
+
142
+ // Line 97: calling request on the noHttpClient throws
143
+ await expect(
144
+ client.request('https://example.com', { method: 'GET' })
145
+ ).rejects.toThrow('No method available to perform HTTP request')
146
+ })
147
+
148
+ it('noHttpClient.request in binaryHttpClient throws with the exact message text', async () => {
149
+ jest.mock('https', () => {
150
+ throw new Error('https not available')
151
+ })
152
+
153
+ if ('window' in globalThis) {
154
+ delete (globalThis as { window?: unknown }).window
155
+ }
156
+
157
+ let client: any
158
+
159
+ jest.isolateModules(() => {
160
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
161
+ const mod = require('../../../transaction/http/BinaryFetchClient')
162
+ client = mod.binaryHttpClient()
163
+ })
164
+
165
+ let thrown: Error | undefined
166
+ try {
167
+ await client.request('https://example.com', { method: 'POST', data: new Uint8Array([1, 2]) })
168
+ } catch (e) {
169
+ thrown = e as Error
170
+ }
171
+
172
+ expect(thrown).toBeInstanceOf(Error)
173
+ expect(thrown?.message).toBe('No method available to perform HTTP request')
174
+ })
175
+
176
+ it('returns a working BinaryNodejsHttpClient when require("https") succeeds', () => {
177
+ if ('window' in globalThis) {
178
+ delete (globalThis as { window?: unknown }).window
179
+ }
180
+
181
+ let binaryHttpClient: any
182
+ jest.isolateModules(() => {
183
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
184
+ const mod = require('../../../transaction/http/BinaryFetchClient')
185
+ binaryHttpClient = mod.binaryHttpClient
186
+ })
187
+
188
+ const client = binaryHttpClient()
189
+ expect(client).toBeDefined()
190
+ expect(typeof client.request).toBe('function')
191
+ })
192
+ })
@@ -0,0 +1,71 @@
1
+ import { defaultHttpClient } from '../../../transaction/http/DefaultHttpClient'
2
+
3
+ describe('defaultHttpClient', () => {
4
+ afterEach(() => {
5
+ // Restore window if it was set
6
+ if ('window' in globalThis) {
7
+ delete (globalThis as any).window
8
+ }
9
+ })
10
+
11
+ it('returns a FetchHttpClient (with a request method) when window.fetch is available', async () => {
12
+ const mockFetch = jest.fn().mockResolvedValue({
13
+ ok: true,
14
+ status: 200,
15
+ statusText: 'OK',
16
+ headers: {
17
+ get: (_key: string) => 'application/json'
18
+ },
19
+ json: async () => ({ result: 'ok' })
20
+ })
21
+
22
+ global.window = { fetch: mockFetch } as unknown as Window & typeof globalThis
23
+
24
+ const client = defaultHttpClient()
25
+ expect(client).toBeDefined()
26
+ expect(typeof client.request).toBe('function')
27
+
28
+ // Verify it actually uses the window.fetch by making a request
29
+ await client.request('https://example.com', { method: 'GET' })
30
+ expect(mockFetch).toHaveBeenCalledTimes(1)
31
+ })
32
+
33
+ it('returns an httpClient whose request() uses the bound window.fetch', async () => {
34
+ const mockFetch = jest.fn().mockResolvedValue({
35
+ ok: true,
36
+ status: 200,
37
+ statusText: 'OK',
38
+ headers: {
39
+ get: (_key: string) => null
40
+ },
41
+ text: async () => 'plain text body'
42
+ })
43
+
44
+ global.window = { fetch: mockFetch } as unknown as Window & typeof globalThis
45
+
46
+ const client = defaultHttpClient()
47
+ const response = await client.request('https://example.com/api', {
48
+ method: 'POST',
49
+ headers: { 'Content-Type': 'application/json' },
50
+ data: { foo: 'bar' }
51
+ })
52
+
53
+ expect(mockFetch).toHaveBeenCalledTimes(1)
54
+ const [calledUrl, calledOptions] = mockFetch.mock.calls[0]
55
+ expect(calledUrl).toBe('https://example.com/api')
56
+ expect(calledOptions.method).toBe('POST')
57
+ expect(response.ok).toBe(true)
58
+ expect(response.status).toBe(200)
59
+ })
60
+
61
+ it('returns a NodejsHttpClient when window is absent but require is available (Node.js path)', async () => {
62
+ // Remove window to force the Node.js path
63
+ if ('window' in globalThis) {
64
+ delete (globalThis as any).window
65
+ }
66
+
67
+ const client = defaultHttpClient()
68
+ expect(client).toBeDefined()
69
+ expect(typeof client.request).toBe('function')
70
+ })
71
+ })
@@ -0,0 +1,134 @@
1
+ import ProtoWallet from '../../wallet/ProtoWallet'
2
+ import PrivateKey from '../../primitives/PrivateKey'
3
+
4
+ function walletWithNullKeyDeriver (): ProtoWallet {
5
+ const wallet = new ProtoWallet(PrivateKey.fromRandom())
6
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
7
+ ;(wallet as any).keyDeriver = undefined
8
+ return wallet
9
+ }
10
+
11
+ describe('ProtoWallet – additional coverage', () => {
12
+ describe('getPublicKey', () => {
13
+ it('throws when identityKey is true and keyDeriver is null', async () => {
14
+ const wallet = walletWithNullKeyDeriver()
15
+ await expect(wallet.getPublicKey({ identityKey: true })).rejects.toThrow(
16
+ 'keyDeriver is undefined'
17
+ )
18
+ })
19
+
20
+ it('throws when protocolID and keyID are missing (non-identityKey path)', async () => {
21
+ const wallet = new ProtoWallet(PrivateKey.fromRandom())
22
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
23
+ await expect(wallet.getPublicKey({} as any)).rejects.toThrow(
24
+ 'protocolID and keyID are required'
25
+ )
26
+ })
27
+
28
+ it('throws when keyDeriver is null and identityKey is false', async () => {
29
+ const wallet = walletWithNullKeyDeriver()
30
+ await expect(
31
+ wallet.getPublicKey({
32
+ protocolID: [1, 'test'],
33
+ keyID: 'key1'
34
+ })
35
+ ).rejects.toThrow('keyDeriver is undefined')
36
+ })
37
+ })
38
+
39
+ describe('encrypt', () => {
40
+ it('throws when keyDeriver is null', async () => {
41
+ const wallet = walletWithNullKeyDeriver()
42
+ await expect(
43
+ wallet.encrypt({
44
+ plaintext: [1, 2, 3],
45
+ protocolID: [1, 'test'],
46
+ keyID: 'k1'
47
+ })
48
+ ).rejects.toThrow('keyDeriver is undefined')
49
+ })
50
+ })
51
+
52
+ describe('decrypt', () => {
53
+ it('throws when keyDeriver is null', async () => {
54
+ const wallet = walletWithNullKeyDeriver()
55
+ await expect(
56
+ wallet.decrypt({
57
+ ciphertext: [1, 2, 3],
58
+ protocolID: [1, 'test'],
59
+ keyID: 'k1'
60
+ })
61
+ ).rejects.toThrow('keyDeriver is undefined')
62
+ })
63
+ })
64
+
65
+ describe('createHmac', () => {
66
+ it('throws when keyDeriver is null', async () => {
67
+ const wallet = walletWithNullKeyDeriver()
68
+ await expect(
69
+ wallet.createHmac({
70
+ data: [1, 2, 3],
71
+ protocolID: [1, 'test'],
72
+ keyID: 'k1'
73
+ })
74
+ ).rejects.toThrow('keyDeriver is undefined')
75
+ })
76
+ })
77
+
78
+ describe('verifyHmac', () => {
79
+ it('throws when keyDeriver is null', async () => {
80
+ const wallet = walletWithNullKeyDeriver()
81
+ await expect(
82
+ wallet.verifyHmac({
83
+ data: [1, 2, 3],
84
+ hmac: new Array(32).fill(0),
85
+ protocolID: [1, 'test'],
86
+ keyID: 'k1'
87
+ })
88
+ ).rejects.toThrow('keyDeriver is undefined')
89
+ })
90
+ })
91
+
92
+ describe('createSignature', () => {
93
+ it('throws when both data and hashToDirectlySign are missing', async () => {
94
+ const wallet = new ProtoWallet(PrivateKey.fromRandom())
95
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
96
+ await expect(wallet.createSignature({} as any)).rejects.toThrow(
97
+ 'args.data or args.hashToDirectlySign must be valid'
98
+ )
99
+ })
100
+
101
+ it('throws when keyDeriver is null', async () => {
102
+ const wallet = walletWithNullKeyDeriver()
103
+ await expect(
104
+ wallet.createSignature({
105
+ data: [1, 2, 3],
106
+ protocolID: [1, 'test'],
107
+ keyID: 'k1'
108
+ })
109
+ ).rejects.toThrow('keyDeriver is undefined')
110
+ })
111
+ })
112
+
113
+ describe('verifySignature', () => {
114
+ it('throws when both data and hashToDirectlyVerify are missing', async () => {
115
+ const wallet = new ProtoWallet(PrivateKey.fromRandom())
116
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
117
+ await expect(wallet.verifySignature({} as any)).rejects.toThrow(
118
+ 'args.data or args.hashToDirectlyVerify must be valid'
119
+ )
120
+ })
121
+
122
+ it('throws when keyDeriver is null', async () => {
123
+ const wallet = walletWithNullKeyDeriver()
124
+ await expect(
125
+ wallet.verifySignature({
126
+ data: [1, 2, 3],
127
+ signature: [1, 2, 3],
128
+ protocolID: [1, 'test'],
129
+ keyID: 'k1'
130
+ })
131
+ ).rejects.toThrow('keyDeriver is undefined')
132
+ })
133
+ })
134
+ })
@@ -0,0 +1,212 @@
1
+ import { WERR_REVIEW_ACTIONS } from '../WERR_REVIEW_ACTIONS'
2
+ import { WERR_INSUFFICIENT_FUNDS } from '../WERR_INSUFFICIENT_FUNDS'
3
+ import { WERR_INVALID_PARAMETER } from '../WERR_INVALID_PARAMETER'
4
+ import type { ReviewActionResult, SendWithResult } from '../Wallet.interfaces'
5
+
6
+ // ---------------------------------------------------------------------------
7
+ // WERR_REVIEW_ACTIONS
8
+ // ---------------------------------------------------------------------------
9
+ describe('WERR_REVIEW_ACTIONS', () => {
10
+ const reviewActionResults: ReviewActionResult[] = [
11
+ { txid: 'aaaa', status: 'success' }
12
+ ]
13
+ const sendWithResults: SendWithResult[] = [
14
+ { txid: 'bbbb', status: 'unproven' }
15
+ ]
16
+
17
+ it('is an instance of Error', () => {
18
+ const err = new WERR_REVIEW_ACTIONS(reviewActionResults, sendWithResults)
19
+ expect(err).toBeInstanceOf(Error)
20
+ })
21
+
22
+ it('has the fixed message about review requirements', () => {
23
+ const err = new WERR_REVIEW_ACTIONS(reviewActionResults, sendWithResults)
24
+ expect(err.message).toBe(
25
+ 'Undelayed createAction or signAction results require review.'
26
+ )
27
+ })
28
+
29
+ it('has code 5', () => {
30
+ const err = new WERR_REVIEW_ACTIONS(reviewActionResults, sendWithResults)
31
+ expect(err.code).toBe(5)
32
+ })
33
+
34
+ it('has name WERR_REVIEW_ACTIONS', () => {
35
+ const err = new WERR_REVIEW_ACTIONS(reviewActionResults, sendWithResults)
36
+ expect(err.name).toBe('WERR_REVIEW_ACTIONS')
37
+ })
38
+
39
+ it('has isError true', () => {
40
+ const err = new WERR_REVIEW_ACTIONS(reviewActionResults, sendWithResults)
41
+ expect(err.isError).toBe(true)
42
+ })
43
+
44
+ it('stores reviewActionResults on the instance', () => {
45
+ const err = new WERR_REVIEW_ACTIONS(reviewActionResults, sendWithResults)
46
+ expect(err.reviewActionResults).toEqual(reviewActionResults)
47
+ })
48
+
49
+ it('stores sendWithResults on the instance', () => {
50
+ const err = new WERR_REVIEW_ACTIONS(reviewActionResults, sendWithResults)
51
+ expect(err.sendWithResults).toEqual(sendWithResults)
52
+ })
53
+
54
+ it('txid defaults to undefined when not provided', () => {
55
+ const err = new WERR_REVIEW_ACTIONS(reviewActionResults, sendWithResults)
56
+ expect(err.txid).toBeUndefined()
57
+ })
58
+
59
+ it('stores optional txid when provided', () => {
60
+ const err = new WERR_REVIEW_ACTIONS(reviewActionResults, sendWithResults, 'txid123')
61
+ expect(err.txid).toBe('txid123')
62
+ })
63
+
64
+ it('tx defaults to undefined when not provided', () => {
65
+ const err = new WERR_REVIEW_ACTIONS(reviewActionResults, sendWithResults)
66
+ expect(err.tx).toBeUndefined()
67
+ })
68
+
69
+ it('stores optional tx when provided', () => {
70
+ const tx = [0xde, 0xad, 0xbe, 0xef]
71
+ const err = new WERR_REVIEW_ACTIONS(reviewActionResults, sendWithResults, undefined, tx)
72
+ expect(err.tx).toEqual(tx)
73
+ })
74
+
75
+ it('noSendChange defaults to undefined when not provided', () => {
76
+ const err = new WERR_REVIEW_ACTIONS(reviewActionResults, sendWithResults)
77
+ expect(err.noSendChange).toBeUndefined()
78
+ })
79
+
80
+ it('stores optional noSendChange when provided', () => {
81
+ const noSendChange = ['outpoint:0', 'outpoint:1']
82
+ const err = new WERR_REVIEW_ACTIONS(
83
+ reviewActionResults,
84
+ sendWithResults,
85
+ undefined,
86
+ undefined,
87
+ noSendChange
88
+ )
89
+ expect(err.noSendChange).toEqual(noSendChange)
90
+ })
91
+
92
+ it('all five optional/required fields are accessible together', () => {
93
+ const tx = [1, 2, 3]
94
+ const noSendChange = ['out:0']
95
+ const err = new WERR_REVIEW_ACTIONS(
96
+ reviewActionResults,
97
+ sendWithResults,
98
+ 'txid_full',
99
+ tx,
100
+ noSendChange
101
+ )
102
+ expect(err.reviewActionResults).toEqual(reviewActionResults)
103
+ expect(err.sendWithResults).toEqual(sendWithResults)
104
+ expect(err.txid).toBe('txid_full')
105
+ expect(err.tx).toEqual(tx)
106
+ expect(err.noSendChange).toEqual(noSendChange)
107
+ })
108
+ })
109
+
110
+ // ---------------------------------------------------------------------------
111
+ // WERR_INSUFFICIENT_FUNDS
112
+ // ---------------------------------------------------------------------------
113
+ describe('WERR_INSUFFICIENT_FUNDS', () => {
114
+ it('is an instance of Error', () => {
115
+ const err = new WERR_INSUFFICIENT_FUNDS(1000, 500)
116
+ expect(err).toBeInstanceOf(Error)
117
+ })
118
+
119
+ it('has code 7', () => {
120
+ const err = new WERR_INSUFFICIENT_FUNDS(1000, 500)
121
+ expect(err.code).toBe(7)
122
+ })
123
+
124
+ it('has name WERR_INSUFFICIENT_FUNDS', () => {
125
+ const err = new WERR_INSUFFICIENT_FUNDS(1000, 500)
126
+ expect(err.name).toBe('WERR_INSUFFICIENT_FUNDS')
127
+ })
128
+
129
+ it('has isError true', () => {
130
+ const err = new WERR_INSUFFICIENT_FUNDS(1000, 500)
131
+ expect(err.isError).toBe(true)
132
+ })
133
+
134
+ it('stores totalSatoshisNeeded on the instance', () => {
135
+ const err = new WERR_INSUFFICIENT_FUNDS(2500, 1200)
136
+ expect(err.totalSatoshisNeeded).toBe(2500)
137
+ })
138
+
139
+ it('stores moreSatoshisNeeded on the instance', () => {
140
+ const err = new WERR_INSUFFICIENT_FUNDS(2500, 1200)
141
+ expect(err.moreSatoshisNeeded).toBe(1200)
142
+ })
143
+
144
+ it('message contains moreSatoshisNeeded value', () => {
145
+ const err = new WERR_INSUFFICIENT_FUNDS(3000, 750)
146
+ expect(err.message).toContain('750')
147
+ })
148
+
149
+ it('message contains totalSatoshisNeeded value', () => {
150
+ const err = new WERR_INSUFFICIENT_FUNDS(3000, 750)
151
+ expect(err.message).toContain('3000')
152
+ })
153
+
154
+ it('message contains both values and key phrase', () => {
155
+ const err = new WERR_INSUFFICIENT_FUNDS(1000, 500)
156
+ expect(err.message).toMatch(/500 more satoshis are needed/)
157
+ expect(err.message).toMatch(/total of 1000/)
158
+ })
159
+
160
+ it('handles zero values', () => {
161
+ const err = new WERR_INSUFFICIENT_FUNDS(0, 0)
162
+ expect(err.totalSatoshisNeeded).toBe(0)
163
+ expect(err.moreSatoshisNeeded).toBe(0)
164
+ expect(err.message).toContain('0')
165
+ })
166
+ })
167
+
168
+ // ---------------------------------------------------------------------------
169
+ // WERR_INVALID_PARAMETER
170
+ // ---------------------------------------------------------------------------
171
+ describe('WERR_INVALID_PARAMETER', () => {
172
+ it('is an instance of Error', () => {
173
+ const err = new WERR_INVALID_PARAMETER('myParam', 'a valid string')
174
+ expect(err).toBeInstanceOf(Error)
175
+ })
176
+
177
+ it('has code 6', () => {
178
+ const err = new WERR_INVALID_PARAMETER('myParam', 'a valid string')
179
+ expect(err.code).toBe(6)
180
+ })
181
+
182
+ it('has name WERR_INVALID_PARAMETER', () => {
183
+ const err = new WERR_INVALID_PARAMETER('myParam', 'a valid string')
184
+ expect(err.name).toBe('WERR_INVALID_PARAMETER')
185
+ })
186
+
187
+ it('has isError true', () => {
188
+ const err = new WERR_INVALID_PARAMETER('myParam', 'a valid string')
189
+ expect(err.isError).toBe(true)
190
+ })
191
+
192
+ it('stores parameter on the instance', () => {
193
+ const err = new WERR_INVALID_PARAMETER('outputValue', 'a positive number')
194
+ expect(err.parameter).toBe('outputValue')
195
+ })
196
+
197
+ it('message includes the parameter name and mustBe description', () => {
198
+ const err = new WERR_INVALID_PARAMETER('amount', 'greater than zero')
199
+ expect(err.message).toBe('The amount parameter must be greater than zero')
200
+ })
201
+
202
+ it('uses default mustBe text ("valid.") when mustBe is not provided', () => {
203
+ const err = new WERR_INVALID_PARAMETER('lockingScript')
204
+ expect(err.message).toBe('The lockingScript parameter must be valid.')
205
+ })
206
+
207
+ it('handles an empty string for parameter', () => {
208
+ const err = new WERR_INVALID_PARAMETER('', 'non-empty')
209
+ expect(err.parameter).toBe('')
210
+ expect(err.message).toContain('non-empty')
211
+ })
212
+ })