@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.
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/src/auth/clients/__tests__/AuthFetch.additional.test.js +827 -0
- package/dist/cjs/src/auth/clients/__tests__/AuthFetch.additional.test.js.map +1 -0
- package/dist/cjs/src/auth/transports/__tests__/SimplifiedFetchTransport.additional.test.js +654 -0
- package/dist/cjs/src/auth/transports/__tests__/SimplifiedFetchTransport.additional.test.js.map +1 -0
- package/dist/cjs/src/transaction/MerklePath.js +132 -0
- package/dist/cjs/src/transaction/MerklePath.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/src/auth/clients/__tests__/AuthFetch.additional.test.js +825 -0
- package/dist/esm/src/auth/clients/__tests__/AuthFetch.additional.test.js.map +1 -0
- package/dist/esm/src/auth/transports/__tests__/SimplifiedFetchTransport.additional.test.js +619 -0
- package/dist/esm/src/auth/transports/__tests__/SimplifiedFetchTransport.additional.test.js.map +1 -0
- package/dist/esm/src/transaction/MerklePath.js +132 -0
- package/dist/esm/src/transaction/MerklePath.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/types/src/auth/clients/__tests__/AuthFetch.additional.test.d.ts +21 -0
- package/dist/types/src/auth/clients/__tests__/AuthFetch.additional.test.d.ts.map +1 -0
- package/dist/types/src/auth/transports/__tests__/SimplifiedFetchTransport.additional.test.d.ts +2 -0
- package/dist/types/src/auth/transports/__tests__/SimplifiedFetchTransport.additional.test.d.ts.map +1 -0
- package/dist/types/src/transaction/MerklePath.d.ts +27 -0
- package/dist/types/src/transaction/MerklePath.d.ts.map +1 -1
- package/dist/types/tsconfig.types.tsbuildinfo +1 -1
- package/dist/umd/bundle.js +1 -1
- package/dist/umd/bundle.js.map +1 -1
- package/docs/reference/storage.md +1 -1
- package/docs/reference/transaction.md +40 -0
- package/package.json +1 -1
- package/src/auth/clients/__tests__/AuthFetch.additional.test.ts +1131 -0
- package/src/auth/transports/__tests__/SimplifiedFetchTransport.additional.test.ts +770 -0
- package/src/compat/__tests/Mnemonic.additional.test.ts +64 -0
- package/src/identity/__tests/IdentityClient.additional.test.ts +767 -0
- package/src/kvstore/__tests/LocalKVStore.additional.test.ts +611 -0
- package/src/kvstore/__tests/kvStoreInterpreter.test.ts +327 -0
- package/src/overlay-tools/__tests/HostReputationTracker.additional.test.ts +561 -0
- package/src/overlay-tools/__tests/LookupResolver.additional.test.ts +612 -0
- package/src/overlay-tools/__tests/withDoubleSpendRetry.test.ts +278 -0
- package/src/primitives/__tests/BigNumber.additional.test.ts +79 -0
- package/src/primitives/__tests/Curve.additional.test.ts +208 -0
- package/src/primitives/__tests/ECDSA.additional.test.ts +122 -0
- package/src/primitives/__tests/Hash.additional.test.ts +59 -0
- package/src/primitives/__tests/JacobianPoint.test.ts +308 -0
- package/src/primitives/__tests/Point.additional.test.ts +503 -0
- package/src/primitives/__tests/PublicKey.additional.test.ts +383 -0
- package/src/primitives/__tests/Random.additional.test.ts +262 -0
- package/src/primitives/__tests/Signature.test.ts +333 -0
- package/src/primitives/__tests/TransactionSignature.additional.test.ts +241 -0
- package/src/registry/__tests/RegistryClient.additional.test.ts +750 -0
- package/src/remittance/__tests/BasicBRC29.additional.test.ts +657 -0
- package/src/remittance/__tests/RemittanceManager.additional.test.ts +1272 -0
- package/src/script/__tests/LockingUnlockingScript.test.ts +79 -0
- package/src/script/__tests/Script.additional.test.ts +100 -0
- package/src/script/__tests/ScriptEvaluationError.test.ts +98 -0
- package/src/script/__tests/Spend.additional.test.ts +837 -0
- package/src/script/templates/__tests/RPuzzle.test.ts +134 -0
- package/src/transaction/MerklePath.ts +155 -0
- package/src/transaction/__tests/BeefParty.additional.test.ts +22 -0
- package/src/transaction/__tests/Broadcaster.test.ts +159 -0
- package/src/transaction/__tests/MerklePath.bench.test.ts +105 -0
- package/src/transaction/__tests/MerklePath.test.ts +80 -0
- package/src/transaction/__tests/Transaction.additional.test.ts +225 -0
- package/src/transaction/broadcasters/__tests/ARC.additional.test.ts +585 -0
- package/src/transaction/broadcasters/__tests/Teranode.test.ts +349 -0
- package/src/transaction/chaintrackers/__tests/BlockHeadersService.test.ts +253 -0
- package/src/transaction/chaintrackers/__tests/DefaultChainTracker.test.ts +44 -0
- package/src/transaction/chaintrackers/__tests/WhatsOnChain.additional.test.ts +193 -0
- package/src/transaction/fee-models/__tests/SatoshisPerKilobyte.test.ts +262 -0
- package/src/transaction/http/__tests/BinaryFetchClient.test.ts +212 -0
- package/src/transaction/http/__tests/DefaultHttpClient.additional.test.ts +192 -0
- package/src/transaction/http/__tests/DefaultHttpClient.test.ts +71 -0
- package/src/wallet/__tests/ProtoWallet.additional.test.ts +134 -0
- package/src/wallet/__tests/WERR.test.ts +212 -0
- package/src/wallet/__tests/WalletClient.additional.test.ts +699 -0
- package/src/wallet/__tests/WalletClient.substrate.test.ts +759 -0
- package/src/wallet/__tests/WalletError.test.ts +290 -0
- package/src/wallet/__tests/validationHelpers.test.ts +1218 -0
- package/src/wallet/substrates/__tests/HTTPWalletJSON.test.ts +496 -0
- 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
|
+
})
|