@bsv/sdk 2.0.11 → 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/overlay-tools/HostReputationTracker.js +21 -13
- package/dist/cjs/src/overlay-tools/HostReputationTracker.js.map +1 -1
- package/dist/cjs/src/primitives/PrivateKey.js +3 -3
- package/dist/cjs/src/primitives/PrivateKey.js.map +1 -1
- package/dist/cjs/src/script/Spend.js +17 -9
- package/dist/cjs/src/script/Spend.js.map +1 -1
- package/dist/cjs/src/storage/StorageDownloader.js +6 -6
- package/dist/cjs/src/storage/StorageDownloader.js.map +1 -1
- package/dist/cjs/src/storage/StorageUtils.js +1 -1
- package/dist/cjs/src/storage/StorageUtils.js.map +1 -1
- package/dist/cjs/src/transaction/MerklePath.js +168 -27
- 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/overlay-tools/HostReputationTracker.js +21 -13
- package/dist/esm/src/overlay-tools/HostReputationTracker.js.map +1 -1
- package/dist/esm/src/primitives/PrivateKey.js +3 -3
- package/dist/esm/src/primitives/PrivateKey.js.map +1 -1
- package/dist/esm/src/script/Spend.js +17 -9
- package/dist/esm/src/script/Spend.js.map +1 -1
- package/dist/esm/src/storage/StorageDownloader.js +6 -6
- package/dist/esm/src/storage/StorageDownloader.js.map +1 -1
- package/dist/esm/src/storage/StorageUtils.js +1 -1
- package/dist/esm/src/storage/StorageUtils.js.map +1 -1
- package/dist/esm/src/transaction/MerklePath.js +168 -27
- 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/overlay-tools/HostReputationTracker.d.ts.map +1 -1
- package/dist/types/src/script/Spend.d.ts.map +1 -1
- 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 +3 -3
- 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/auth/utils/__tests/validateCertificates.test.ts +12 -9
- 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/LocalKVStore.test.ts +4 -6
- package/src/kvstore/__tests/kvStoreInterpreter.test.ts +327 -0
- package/src/overlay-tools/HostReputationTracker.ts +17 -14
- 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/PrivateKey.ts +3 -3
- 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/Spend.ts +19 -11
- 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/storage/StorageDownloader.ts +6 -6
- package/src/storage/StorageUtils.ts +1 -1
- package/src/transaction/MerklePath.ts +196 -36
- 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 +232 -21
- 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,1218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for src/wallet/validationHelpers.ts
|
|
3
|
+
*
|
|
4
|
+
* validationHelpers.ts is at ~20% coverage (215 missed lines).
|
|
5
|
+
* All exported functions are covered here. Private helpers are exercised
|
|
6
|
+
* indirectly through the exported functions that call them.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import WERR_INVALID_PARAMETER from '../WERR_INVALID_PARAMETER'
|
|
10
|
+
import {
|
|
11
|
+
parseWalletOutpoint,
|
|
12
|
+
validateSatoshis,
|
|
13
|
+
validateOptionalInteger,
|
|
14
|
+
validateInteger,
|
|
15
|
+
validatePositiveIntegerOrZero,
|
|
16
|
+
validateStringLength,
|
|
17
|
+
validateBase64String,
|
|
18
|
+
isHexString,
|
|
19
|
+
validateCreateActionInput,
|
|
20
|
+
validateCreateActionOutput,
|
|
21
|
+
validateCreateActionOptions,
|
|
22
|
+
validateCreateActionArgs,
|
|
23
|
+
validateSignActionOptions,
|
|
24
|
+
validateSignActionArgs,
|
|
25
|
+
validateAbortActionArgs,
|
|
26
|
+
validateWalletPayment,
|
|
27
|
+
validateBasketInsertion,
|
|
28
|
+
validateInternalizeOutput,
|
|
29
|
+
validateOriginator,
|
|
30
|
+
validateOptionalOutpointString,
|
|
31
|
+
validateOutpointString,
|
|
32
|
+
validateRelinquishOutputArgs,
|
|
33
|
+
validateRelinquishCertificateArgs,
|
|
34
|
+
validateListCertificatesArgs,
|
|
35
|
+
validateAcquireIssuanceCertificateArgs,
|
|
36
|
+
validateAcquireDirectCertificateArgs,
|
|
37
|
+
validateProveCertificateArgs,
|
|
38
|
+
validateDiscoverByIdentityKeyArgs,
|
|
39
|
+
validateDiscoverByAttributesArgs,
|
|
40
|
+
validateListOutputsArgs,
|
|
41
|
+
validateListActionsArgs,
|
|
42
|
+
specOpThrowReviewActions
|
|
43
|
+
} from '../validationHelpers'
|
|
44
|
+
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
// Shared test data
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
// Valid 64-char hex txid
|
|
50
|
+
const VALID_TXID = 'a'.repeat(64)
|
|
51
|
+
// Valid outpoint string
|
|
52
|
+
const VALID_OUTPOINT = `${VALID_TXID}.0`
|
|
53
|
+
// Valid compressed pubkey hex (66 chars)
|
|
54
|
+
const VALID_PUBKEY_HEX = '02' + 'ab'.repeat(32)
|
|
55
|
+
// Valid base64 strings
|
|
56
|
+
const VALID_BASE64 = 'SGVsbG8=' // "Hello" in base64
|
|
57
|
+
const VALID_BASE64_NOPAD = 'SGVsbG8' // without padding (valid 4n+3)
|
|
58
|
+
|
|
59
|
+
// ============================================================================
|
|
60
|
+
// parseWalletOutpoint
|
|
61
|
+
// ============================================================================
|
|
62
|
+
|
|
63
|
+
describe('parseWalletOutpoint', () => {
|
|
64
|
+
it('splits "txid.vout" into txid string and numeric vout', () => {
|
|
65
|
+
const result = parseWalletOutpoint(`${VALID_TXID}.3`)
|
|
66
|
+
expect(result.txid).toBe(VALID_TXID)
|
|
67
|
+
expect(result.vout).toBe(3)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('handles vout 0', () => {
|
|
71
|
+
expect(parseWalletOutpoint(`${VALID_TXID}.0`).vout).toBe(0)
|
|
72
|
+
})
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
// ============================================================================
|
|
76
|
+
// validateSatoshis
|
|
77
|
+
// ============================================================================
|
|
78
|
+
|
|
79
|
+
describe('validateSatoshis', () => {
|
|
80
|
+
it('accepts 0 satoshis', () => {
|
|
81
|
+
expect(validateSatoshis(0, 'amount')).toBe(0)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('accepts maximum satoshis (21e14)', () => {
|
|
85
|
+
expect(validateSatoshis(21e14, 'amount')).toBe(21e14)
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('throws for undefined', () => {
|
|
89
|
+
expect(() => validateSatoshis(undefined, 'amount')).toThrow(WERR_INVALID_PARAMETER)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it('throws for a float', () => {
|
|
93
|
+
expect(() => validateSatoshis(1.5, 'amount')).toThrow(WERR_INVALID_PARAMETER)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('throws for a negative value', () => {
|
|
97
|
+
expect(() => validateSatoshis(-1, 'amount')).toThrow(WERR_INVALID_PARAMETER)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('throws when value exceeds 21e14', () => {
|
|
101
|
+
expect(() => validateSatoshis(21e14 + 1, 'amount')).toThrow(WERR_INVALID_PARAMETER)
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('throws when below optional min', () => {
|
|
105
|
+
expect(() => validateSatoshis(5, 'amount', 10)).toThrow(WERR_INVALID_PARAMETER)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('accepts when exactly at optional min', () => {
|
|
109
|
+
expect(validateSatoshis(10, 'amount', 10)).toBe(10)
|
|
110
|
+
})
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
// ============================================================================
|
|
114
|
+
// validateInteger
|
|
115
|
+
// ============================================================================
|
|
116
|
+
|
|
117
|
+
describe('validateInteger', () => {
|
|
118
|
+
it('returns the value when valid', () => {
|
|
119
|
+
expect(validateInteger(5, 'n')).toBe(5)
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('returns defaultValue when v is undefined', () => {
|
|
123
|
+
expect(validateInteger(undefined, 'n', 42)).toBe(42)
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it('throws when undefined and no defaultValue', () => {
|
|
127
|
+
expect(() => validateInteger(undefined, 'n')).toThrow(WERR_INVALID_PARAMETER)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it('throws for a non-integer', () => {
|
|
131
|
+
expect(() => validateInteger(1.5, 'n')).toThrow(WERR_INVALID_PARAMETER)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it('throws when below min', () => {
|
|
135
|
+
expect(() => validateInteger(0, 'n', undefined, 1)).toThrow(WERR_INVALID_PARAMETER)
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
it('throws when above max', () => {
|
|
139
|
+
expect(() => validateInteger(11, 'n', undefined, undefined, 10)).toThrow(WERR_INVALID_PARAMETER)
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('accepts value at min boundary', () => {
|
|
143
|
+
expect(validateInteger(1, 'n', undefined, 1)).toBe(1)
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
it('accepts value at max boundary', () => {
|
|
147
|
+
expect(validateInteger(10, 'n', undefined, undefined, 10)).toBe(10)
|
|
148
|
+
})
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
// ============================================================================
|
|
152
|
+
// validateOptionalInteger
|
|
153
|
+
// ============================================================================
|
|
154
|
+
|
|
155
|
+
describe('validateOptionalInteger', () => {
|
|
156
|
+
it('returns undefined when v is undefined', () => {
|
|
157
|
+
expect(validateOptionalInteger(undefined, 'n')).toBeUndefined()
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it('returns the value when valid', () => {
|
|
161
|
+
expect(validateOptionalInteger(5, 'n')).toBe(5)
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('throws for an invalid value', () => {
|
|
165
|
+
expect(() => validateOptionalInteger(1.5, 'n')).toThrow(WERR_INVALID_PARAMETER)
|
|
166
|
+
})
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
// ============================================================================
|
|
170
|
+
// validatePositiveIntegerOrZero
|
|
171
|
+
// ============================================================================
|
|
172
|
+
|
|
173
|
+
describe('validatePositiveIntegerOrZero', () => {
|
|
174
|
+
it('accepts 0', () => {
|
|
175
|
+
expect(validatePositiveIntegerOrZero(0, 'n')).toBe(0)
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
it('accepts positive integers', () => {
|
|
179
|
+
expect(validatePositiveIntegerOrZero(100, 'n')).toBe(100)
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
it('throws for negative integers', () => {
|
|
183
|
+
expect(() => validatePositiveIntegerOrZero(-1, 'n')).toThrow(WERR_INVALID_PARAMETER)
|
|
184
|
+
})
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
// ============================================================================
|
|
188
|
+
// validateStringLength
|
|
189
|
+
// ============================================================================
|
|
190
|
+
|
|
191
|
+
describe('validateStringLength', () => {
|
|
192
|
+
it('returns the string when within bounds', () => {
|
|
193
|
+
expect(validateStringLength('hello', 's', 1, 10)).toBe('hello')
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
it('throws when string is too short', () => {
|
|
197
|
+
expect(() => validateStringLength('hi', 's', 5)).toThrow(WERR_INVALID_PARAMETER)
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
it('throws when string is too long', () => {
|
|
201
|
+
expect(() => validateStringLength('hello world', 's', 1, 5)).toThrow(WERR_INVALID_PARAMETER)
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
it('accepts when no bounds provided', () => {
|
|
205
|
+
expect(validateStringLength('any string at all', 's')).toBe('any string at all')
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
it('handles multi-byte UTF-8 characters correctly', () => {
|
|
209
|
+
// '€' is 3 UTF-8 bytes
|
|
210
|
+
const euro = '€'
|
|
211
|
+
expect(() => validateStringLength(euro, 's', 1, 2)).toThrow(WERR_INVALID_PARAMETER)
|
|
212
|
+
expect(validateStringLength(euro, 's', 1, 3)).toBe(euro)
|
|
213
|
+
})
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
// ============================================================================
|
|
217
|
+
// validateBase64String
|
|
218
|
+
// ============================================================================
|
|
219
|
+
|
|
220
|
+
describe('validateBase64String', () => {
|
|
221
|
+
it('accepts a valid padded base64 string', () => {
|
|
222
|
+
expect(validateBase64String(VALID_BASE64, 's')).toBe(VALID_BASE64)
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
it('trims whitespace before validation', () => {
|
|
226
|
+
expect(validateBase64String(` ${VALID_BASE64} `, 's')).toBe(VALID_BASE64)
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
it('throws for an empty string', () => {
|
|
230
|
+
expect(() => validateBase64String('', 's')).toThrow(WERR_INVALID_PARAMETER)
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
it('throws for a string with invalid characters', () => {
|
|
234
|
+
expect(() => validateBase64String('abc!', 's')).toThrow(WERR_INVALID_PARAMETER)
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
it('throws when padding appears within the last 2 chars boundary', () => {
|
|
238
|
+
// '=' at position i is only valid when i >= length - 2
|
|
239
|
+
// 'a=bc' has '=' at i=1, length=4, so i < length-2 (1 < 2) → throws
|
|
240
|
+
expect(() => validateBase64String('a=bc', 's')).toThrow(WERR_INVALID_PARAMETER)
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
it('throws for more than 2 padding characters', () => {
|
|
244
|
+
expect(() => validateBase64String('a===', 's')).toThrow(WERR_INVALID_PARAMETER)
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
it('throws when bytes are below min', () => {
|
|
248
|
+
// VALID_BASE64 = "SGVsbG8=" → 5 decoded bytes
|
|
249
|
+
expect(() => validateBase64String(VALID_BASE64, 's', 6)).toThrow(WERR_INVALID_PARAMETER)
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
it('throws when bytes exceed max', () => {
|
|
253
|
+
expect(() => validateBase64String(VALID_BASE64, 's', undefined, 3)).toThrow(WERR_INVALID_PARAMETER)
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
it('throws for unpadded base64 where length % 4 == 3 (not accepted by this validator)', () => {
|
|
257
|
+
// This validator requires explicit padding or length % 4 == 0.
|
|
258
|
+
// 'SGVsbG8' has 7 chars, 7 % 4 == 3. Since paddingCount=0, mod(3) != 4-0(4), so it throws.
|
|
259
|
+
expect(() => validateBase64String(VALID_BASE64_NOPAD, 's')).toThrow(WERR_INVALID_PARAMETER)
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
it('accepts all valid base64 characters (A-Z, a-z, 0-9, +, /)', () => {
|
|
263
|
+
const valid = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
|
|
264
|
+
// length 64 → multiple of 4, no padding needed
|
|
265
|
+
expect(() => validateBase64String(valid, 's')).not.toThrow()
|
|
266
|
+
})
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
// ============================================================================
|
|
270
|
+
// isHexString
|
|
271
|
+
// ============================================================================
|
|
272
|
+
|
|
273
|
+
describe('isHexString', () => {
|
|
274
|
+
it('returns true for a valid lowercase hex string', () => {
|
|
275
|
+
expect(isHexString('deadbeef')).toBe(true)
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
it('returns true for a valid uppercase hex string', () => {
|
|
279
|
+
expect(isHexString('DEADBEEF')).toBe(true)
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
it('returns false for an odd-length string', () => {
|
|
283
|
+
expect(isHexString('abc')).toBe(false)
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
it('returns false for a string with non-hex characters', () => {
|
|
287
|
+
expect(isHexString('gg')).toBe(false)
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
it('trims whitespace before checking', () => {
|
|
291
|
+
expect(isHexString(' deadbeef ')).toBe(true)
|
|
292
|
+
})
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
// ============================================================================
|
|
296
|
+
// validateOutpointString / validateOptionalOutpointString
|
|
297
|
+
// ============================================================================
|
|
298
|
+
|
|
299
|
+
describe('validateOutpointString', () => {
|
|
300
|
+
it('returns "txid.vout" for a valid outpoint', () => {
|
|
301
|
+
const result = validateOutpointString(VALID_OUTPOINT, 'output')
|
|
302
|
+
expect(result).toContain('.')
|
|
303
|
+
const [txid, vout] = result.split('.')
|
|
304
|
+
expect(txid).toHaveLength(64)
|
|
305
|
+
expect(Number(vout)).toBe(0)
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
it('throws when there is no dot separator', () => {
|
|
309
|
+
expect(() => validateOutpointString('notanoutpoint', 'output')).toThrow(WERR_INVALID_PARAMETER)
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
it('throws when vout is not numeric', () => {
|
|
313
|
+
expect(() => validateOutpointString(`${VALID_TXID}.abc`, 'output')).toThrow(WERR_INVALID_PARAMETER)
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
it('throws when txid is not valid hex', () => {
|
|
317
|
+
expect(() => validateOutpointString('zzzzzzzz.0', 'output')).toThrow(WERR_INVALID_PARAMETER)
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
it('throws when vout is negative', () => {
|
|
321
|
+
expect(() => validateOutpointString(`${VALID_TXID}.-1`, 'output')).toThrow(WERR_INVALID_PARAMETER)
|
|
322
|
+
})
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
describe('validateOptionalOutpointString', () => {
|
|
326
|
+
it('returns undefined for undefined input', () => {
|
|
327
|
+
expect(validateOptionalOutpointString(undefined, 'output')).toBeUndefined()
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
it('validates when a value is provided', () => {
|
|
331
|
+
expect(validateOptionalOutpointString(VALID_OUTPOINT, 'output')).toBeDefined()
|
|
332
|
+
})
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
// ============================================================================
|
|
336
|
+
// validateCreateActionInput
|
|
337
|
+
// ============================================================================
|
|
338
|
+
|
|
339
|
+
describe('validateCreateActionInput', () => {
|
|
340
|
+
const validBase = {
|
|
341
|
+
outpoint: VALID_OUTPOINT,
|
|
342
|
+
inputDescription: 'A valid input description',
|
|
343
|
+
sequenceNumber: 0xffffffff
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
it('accepts input with unlockingScript only', () => {
|
|
347
|
+
const result = validateCreateActionInput({
|
|
348
|
+
...validBase,
|
|
349
|
+
unlockingScript: 'aabb' // 2 bytes (4 hex chars / 2)
|
|
350
|
+
})
|
|
351
|
+
expect(result.unlockingScriptLength).toBe(2)
|
|
352
|
+
expect(result.unlockingScript).toBe('aabb')
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
it('accepts input with unlockingScriptLength only', () => {
|
|
356
|
+
const result = validateCreateActionInput({
|
|
357
|
+
...validBase,
|
|
358
|
+
unlockingScriptLength: 107
|
|
359
|
+
})
|
|
360
|
+
expect(result.unlockingScriptLength).toBe(107)
|
|
361
|
+
expect(result.unlockingScript).toBeUndefined()
|
|
362
|
+
})
|
|
363
|
+
|
|
364
|
+
it('throws when neither unlockingScript nor unlockingScriptLength is provided', () => {
|
|
365
|
+
expect(() =>
|
|
366
|
+
validateCreateActionInput({ ...validBase })
|
|
367
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
it('throws when unlockingScriptLength does not match actual script length', () => {
|
|
371
|
+
expect(() =>
|
|
372
|
+
validateCreateActionInput({
|
|
373
|
+
...validBase,
|
|
374
|
+
unlockingScript: 'aabb', // 1 byte
|
|
375
|
+
unlockingScriptLength: 5 // mismatch
|
|
376
|
+
})
|
|
377
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
378
|
+
})
|
|
379
|
+
|
|
380
|
+
it('uses default sequenceNumber 0xffffffff when not provided', () => {
|
|
381
|
+
const result = validateCreateActionInput({
|
|
382
|
+
...validBase,
|
|
383
|
+
sequenceNumber: undefined as any,
|
|
384
|
+
unlockingScriptLength: 10
|
|
385
|
+
})
|
|
386
|
+
expect(result.sequenceNumber).toBe(0xffffffff)
|
|
387
|
+
})
|
|
388
|
+
|
|
389
|
+
it('throws for too-short inputDescription', () => {
|
|
390
|
+
expect(() =>
|
|
391
|
+
validateCreateActionInput({
|
|
392
|
+
...validBase,
|
|
393
|
+
inputDescription: 'ab',
|
|
394
|
+
unlockingScriptLength: 10
|
|
395
|
+
})
|
|
396
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
397
|
+
})
|
|
398
|
+
})
|
|
399
|
+
|
|
400
|
+
// ============================================================================
|
|
401
|
+
// validateCreateActionOutput
|
|
402
|
+
// ============================================================================
|
|
403
|
+
|
|
404
|
+
describe('validateCreateActionOutput', () => {
|
|
405
|
+
const validBase = {
|
|
406
|
+
lockingScript: 'aabbcc',
|
|
407
|
+
satoshis: 1000,
|
|
408
|
+
outputDescription: 'A valid output description'
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
it('accepts a minimal valid output', () => {
|
|
412
|
+
const result = validateCreateActionOutput(validBase)
|
|
413
|
+
expect(result.satoshis).toBe(1000)
|
|
414
|
+
expect(result.lockingScript).toBe('aabbcc')
|
|
415
|
+
})
|
|
416
|
+
|
|
417
|
+
it('normalises tags via validateTag (trim + lowercase)', () => {
|
|
418
|
+
const result = validateCreateActionOutput({
|
|
419
|
+
...validBase,
|
|
420
|
+
tags: [' MyTag ', 'ANOTHER']
|
|
421
|
+
})
|
|
422
|
+
expect(result.tags).toEqual(['mytag', 'another'])
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
it('accepts optional basket', () => {
|
|
426
|
+
const result = validateCreateActionOutput({
|
|
427
|
+
...validBase,
|
|
428
|
+
basket: 'my-basket'
|
|
429
|
+
})
|
|
430
|
+
expect(result.basket).toBe('my-basket')
|
|
431
|
+
})
|
|
432
|
+
|
|
433
|
+
it('throws for invalid satoshis', () => {
|
|
434
|
+
expect(() =>
|
|
435
|
+
validateCreateActionOutput({ ...validBase, satoshis: -1 })
|
|
436
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
437
|
+
})
|
|
438
|
+
|
|
439
|
+
it('throws for invalid locking script (odd hex length)', () => {
|
|
440
|
+
expect(() =>
|
|
441
|
+
validateCreateActionOutput({ ...validBase, lockingScript: 'abc' })
|
|
442
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
443
|
+
})
|
|
444
|
+
})
|
|
445
|
+
|
|
446
|
+
// ============================================================================
|
|
447
|
+
// validateCreateActionOptions
|
|
448
|
+
// ============================================================================
|
|
449
|
+
|
|
450
|
+
describe('validateCreateActionOptions', () => {
|
|
451
|
+
it('applies all defaults when options is undefined', () => {
|
|
452
|
+
const v = validateCreateActionOptions(undefined)
|
|
453
|
+
expect(v.signAndProcess).toBe(true)
|
|
454
|
+
expect(v.acceptDelayedBroadcast).toBe(true)
|
|
455
|
+
expect(v.returnTXIDOnly).toBe(false)
|
|
456
|
+
expect(v.noSend).toBe(false)
|
|
457
|
+
expect(v.randomizeOutputs).toBe(true)
|
|
458
|
+
expect(v.knownTxids).toEqual([])
|
|
459
|
+
expect(v.sendWith).toEqual([])
|
|
460
|
+
expect(v.noSendChange).toEqual([])
|
|
461
|
+
})
|
|
462
|
+
|
|
463
|
+
it('applies all defaults when options is an empty object', () => {
|
|
464
|
+
const v = validateCreateActionOptions({})
|
|
465
|
+
expect(v.signAndProcess).toBe(true)
|
|
466
|
+
expect(v.randomizeOutputs).toBe(true)
|
|
467
|
+
})
|
|
468
|
+
|
|
469
|
+
it('preserves explicit boolean overrides', () => {
|
|
470
|
+
const v = validateCreateActionOptions({
|
|
471
|
+
signAndProcess: false,
|
|
472
|
+
returnTXIDOnly: true,
|
|
473
|
+
noSend: true,
|
|
474
|
+
randomizeOutputs: false
|
|
475
|
+
})
|
|
476
|
+
expect(v.signAndProcess).toBe(false)
|
|
477
|
+
expect(v.returnTXIDOnly).toBe(true)
|
|
478
|
+
expect(v.noSend).toBe(true)
|
|
479
|
+
expect(v.randomizeOutputs).toBe(false)
|
|
480
|
+
})
|
|
481
|
+
|
|
482
|
+
it('parses noSendChange outpoint strings', () => {
|
|
483
|
+
const v = validateCreateActionOptions({
|
|
484
|
+
noSendChange: [VALID_OUTPOINT]
|
|
485
|
+
})
|
|
486
|
+
expect(v.noSendChange).toHaveLength(1)
|
|
487
|
+
expect(v.noSendChange[0].txid).toHaveLength(64)
|
|
488
|
+
})
|
|
489
|
+
})
|
|
490
|
+
|
|
491
|
+
// ============================================================================
|
|
492
|
+
// validateCreateActionArgs
|
|
493
|
+
// ============================================================================
|
|
494
|
+
|
|
495
|
+
describe('validateCreateActionArgs', () => {
|
|
496
|
+
const minimalArgs = {
|
|
497
|
+
description: 'A valid action description'
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
it('validates a minimal args object with just a description', () => {
|
|
501
|
+
const v = validateCreateActionArgs(minimalArgs as any)
|
|
502
|
+
expect(v.description).toBe('A valid action description')
|
|
503
|
+
expect(v.inputs).toEqual([])
|
|
504
|
+
expect(v.outputs).toEqual([])
|
|
505
|
+
})
|
|
506
|
+
|
|
507
|
+
it('sets isRemixChange = true when no inputs and no outputs and no sendWith', () => {
|
|
508
|
+
const v = validateCreateActionArgs(minimalArgs as any)
|
|
509
|
+
expect(v.isRemixChange).toBe(true)
|
|
510
|
+
expect(v.isNewTx).toBe(true)
|
|
511
|
+
})
|
|
512
|
+
|
|
513
|
+
it('sets isSignAction = true when input lacks an unlockingScript', () => {
|
|
514
|
+
const v = validateCreateActionArgs({
|
|
515
|
+
...minimalArgs,
|
|
516
|
+
inputs: [
|
|
517
|
+
{
|
|
518
|
+
outpoint: VALID_OUTPOINT,
|
|
519
|
+
inputDescription: 'An input with no unlock',
|
|
520
|
+
unlockingScriptLength: 107
|
|
521
|
+
}
|
|
522
|
+
]
|
|
523
|
+
} as any)
|
|
524
|
+
expect(v.isSignAction).toBe(true)
|
|
525
|
+
})
|
|
526
|
+
|
|
527
|
+
it('sets isSignAction = false when all inputs have compiled unlocking scripts and signAndProcess = true', () => {
|
|
528
|
+
const v = validateCreateActionArgs({
|
|
529
|
+
...minimalArgs,
|
|
530
|
+
inputs: [
|
|
531
|
+
{
|
|
532
|
+
outpoint: VALID_OUTPOINT,
|
|
533
|
+
inputDescription: 'An input with unlock',
|
|
534
|
+
unlockingScript: 'aabb',
|
|
535
|
+
unlockingScriptLength: 2
|
|
536
|
+
}
|
|
537
|
+
],
|
|
538
|
+
options: { signAndProcess: true }
|
|
539
|
+
} as any)
|
|
540
|
+
expect(v.isSignAction).toBe(false)
|
|
541
|
+
})
|
|
542
|
+
|
|
543
|
+
it('sets isTestWerrReviewActions when the specOp label is present', () => {
|
|
544
|
+
const v = validateCreateActionArgs({
|
|
545
|
+
...minimalArgs,
|
|
546
|
+
labels: [specOpThrowReviewActions]
|
|
547
|
+
} as any)
|
|
548
|
+
expect(v.isTestWerrReviewActions).toBe(true)
|
|
549
|
+
})
|
|
550
|
+
|
|
551
|
+
it('throws for a description that is too short', () => {
|
|
552
|
+
expect(() =>
|
|
553
|
+
validateCreateActionArgs({ description: 'hi' } as any)
|
|
554
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
555
|
+
})
|
|
556
|
+
|
|
557
|
+
it('validates labels via validateLabel (trim + lowercase)', () => {
|
|
558
|
+
const v = validateCreateActionArgs({
|
|
559
|
+
...minimalArgs,
|
|
560
|
+
labels: [' MyLabel ']
|
|
561
|
+
} as any)
|
|
562
|
+
expect(v.labels).toEqual(['mylabel'])
|
|
563
|
+
})
|
|
564
|
+
})
|
|
565
|
+
|
|
566
|
+
// ============================================================================
|
|
567
|
+
// validateSignActionOptions
|
|
568
|
+
// ============================================================================
|
|
569
|
+
|
|
570
|
+
describe('validateSignActionOptions', () => {
|
|
571
|
+
it('applies defaults when options is undefined', () => {
|
|
572
|
+
const v = validateSignActionOptions(undefined)
|
|
573
|
+
expect(v.acceptDelayedBroadcast).toBe(true)
|
|
574
|
+
expect(v.returnTXIDOnly).toBe(false)
|
|
575
|
+
expect(v.noSend).toBe(false)
|
|
576
|
+
expect(v.sendWith).toEqual([])
|
|
577
|
+
})
|
|
578
|
+
|
|
579
|
+
it('preserves explicit values', () => {
|
|
580
|
+
const v = validateSignActionOptions({ noSend: true, returnTXIDOnly: true })
|
|
581
|
+
expect(v.noSend).toBe(true)
|
|
582
|
+
expect(v.returnTXIDOnly).toBe(true)
|
|
583
|
+
})
|
|
584
|
+
})
|
|
585
|
+
|
|
586
|
+
// ============================================================================
|
|
587
|
+
// validateSignActionArgs
|
|
588
|
+
// ============================================================================
|
|
589
|
+
|
|
590
|
+
describe('validateSignActionArgs', () => {
|
|
591
|
+
const minimalSignArgs = {
|
|
592
|
+
spends: {},
|
|
593
|
+
reference: VALID_BASE64
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
it('returns a valid object with flags set', () => {
|
|
597
|
+
const v = validateSignActionArgs(minimalSignArgs as any)
|
|
598
|
+
expect(v.isNewTx).toBe(true)
|
|
599
|
+
expect(v.isSendWith).toBe(false)
|
|
600
|
+
})
|
|
601
|
+
|
|
602
|
+
it('sets isSendWith when sendWith is non-empty', () => {
|
|
603
|
+
const v = validateSignActionArgs({
|
|
604
|
+
...minimalSignArgs,
|
|
605
|
+
options: { sendWith: [VALID_TXID] }
|
|
606
|
+
} as any)
|
|
607
|
+
expect(v.isSendWith).toBe(true)
|
|
608
|
+
})
|
|
609
|
+
})
|
|
610
|
+
|
|
611
|
+
// ============================================================================
|
|
612
|
+
// validateAbortActionArgs
|
|
613
|
+
// ============================================================================
|
|
614
|
+
|
|
615
|
+
describe('validateAbortActionArgs', () => {
|
|
616
|
+
it('accepts a valid base64 reference', () => {
|
|
617
|
+
const v = validateAbortActionArgs({ reference: VALID_BASE64 })
|
|
618
|
+
expect(v.reference).toBe(VALID_BASE64)
|
|
619
|
+
})
|
|
620
|
+
|
|
621
|
+
it('throws for an invalid reference', () => {
|
|
622
|
+
expect(() =>
|
|
623
|
+
validateAbortActionArgs({ reference: '!invalid!' })
|
|
624
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
625
|
+
})
|
|
626
|
+
})
|
|
627
|
+
|
|
628
|
+
// ============================================================================
|
|
629
|
+
// validateWalletPayment
|
|
630
|
+
// ============================================================================
|
|
631
|
+
|
|
632
|
+
describe('validateWalletPayment', () => {
|
|
633
|
+
it('returns undefined when called with undefined', () => {
|
|
634
|
+
expect(validateWalletPayment(undefined)).toBeUndefined()
|
|
635
|
+
})
|
|
636
|
+
|
|
637
|
+
it('validates a complete wallet payment structure', () => {
|
|
638
|
+
const v = validateWalletPayment({
|
|
639
|
+
derivationPrefix: VALID_BASE64,
|
|
640
|
+
derivationSuffix: VALID_BASE64,
|
|
641
|
+
senderIdentityKey: VALID_PUBKEY_HEX
|
|
642
|
+
})
|
|
643
|
+
expect(v).toBeDefined()
|
|
644
|
+
expect(v!.senderIdentityKey).toBe(VALID_PUBKEY_HEX.toLowerCase())
|
|
645
|
+
})
|
|
646
|
+
|
|
647
|
+
it('throws for an invalid derivationPrefix', () => {
|
|
648
|
+
expect(() =>
|
|
649
|
+
validateWalletPayment({
|
|
650
|
+
derivationPrefix: '!!!',
|
|
651
|
+
derivationSuffix: VALID_BASE64,
|
|
652
|
+
senderIdentityKey: VALID_PUBKEY_HEX
|
|
653
|
+
})
|
|
654
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
655
|
+
})
|
|
656
|
+
})
|
|
657
|
+
|
|
658
|
+
// ============================================================================
|
|
659
|
+
// validateBasketInsertion
|
|
660
|
+
// ============================================================================
|
|
661
|
+
|
|
662
|
+
describe('validateBasketInsertion', () => {
|
|
663
|
+
it('returns undefined when called with undefined', () => {
|
|
664
|
+
expect(validateBasketInsertion(undefined)).toBeUndefined()
|
|
665
|
+
})
|
|
666
|
+
|
|
667
|
+
it('validates a basket insertion with basket and tags', () => {
|
|
668
|
+
const v = validateBasketInsertion({
|
|
669
|
+
basket: 'my-basket',
|
|
670
|
+
tags: ['tag1', 'TAG2']
|
|
671
|
+
})
|
|
672
|
+
expect(v).toBeDefined()
|
|
673
|
+
expect(v!.basket).toBe('my-basket')
|
|
674
|
+
expect(v!.tags).toEqual(['tag1', 'tag2'])
|
|
675
|
+
})
|
|
676
|
+
|
|
677
|
+
it('throws for an empty basket name', () => {
|
|
678
|
+
expect(() =>
|
|
679
|
+
validateBasketInsertion({ basket: '' })
|
|
680
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
681
|
+
})
|
|
682
|
+
})
|
|
683
|
+
|
|
684
|
+
// ============================================================================
|
|
685
|
+
// validateInternalizeOutput
|
|
686
|
+
// ============================================================================
|
|
687
|
+
|
|
688
|
+
describe('validateInternalizeOutput', () => {
|
|
689
|
+
it('accepts a "wallet payment" output', () => {
|
|
690
|
+
const v = validateInternalizeOutput({
|
|
691
|
+
outputIndex: 0,
|
|
692
|
+
protocol: 'wallet payment',
|
|
693
|
+
paymentRemittance: {
|
|
694
|
+
derivationPrefix: VALID_BASE64,
|
|
695
|
+
derivationSuffix: VALID_BASE64,
|
|
696
|
+
senderIdentityKey: VALID_PUBKEY_HEX
|
|
697
|
+
}
|
|
698
|
+
})
|
|
699
|
+
expect(v.protocol).toBe('wallet payment')
|
|
700
|
+
expect(v.outputIndex).toBe(0)
|
|
701
|
+
})
|
|
702
|
+
|
|
703
|
+
it('accepts a "basket insertion" output', () => {
|
|
704
|
+
const v = validateInternalizeOutput({
|
|
705
|
+
outputIndex: 1,
|
|
706
|
+
protocol: 'basket insertion',
|
|
707
|
+
insertionRemittance: { basket: 'default' }
|
|
708
|
+
})
|
|
709
|
+
expect(v.protocol).toBe('basket insertion')
|
|
710
|
+
})
|
|
711
|
+
|
|
712
|
+
it('throws for an unknown protocol', () => {
|
|
713
|
+
expect(() =>
|
|
714
|
+
validateInternalizeOutput({
|
|
715
|
+
outputIndex: 0,
|
|
716
|
+
protocol: 'unknown' as any
|
|
717
|
+
})
|
|
718
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
719
|
+
})
|
|
720
|
+
|
|
721
|
+
it('throws for a negative outputIndex', () => {
|
|
722
|
+
expect(() =>
|
|
723
|
+
validateInternalizeOutput({
|
|
724
|
+
outputIndex: -1,
|
|
725
|
+
protocol: 'basket insertion'
|
|
726
|
+
})
|
|
727
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
728
|
+
})
|
|
729
|
+
})
|
|
730
|
+
|
|
731
|
+
// ============================================================================
|
|
732
|
+
// validateOriginator
|
|
733
|
+
// ============================================================================
|
|
734
|
+
|
|
735
|
+
describe('validateOriginator', () => {
|
|
736
|
+
it('returns undefined for undefined input', () => {
|
|
737
|
+
expect(validateOriginator(undefined)).toBeUndefined()
|
|
738
|
+
})
|
|
739
|
+
|
|
740
|
+
it('normalises to lowercase and trims whitespace', () => {
|
|
741
|
+
expect(validateOriginator(' Example.COM ')).toBe('example.com')
|
|
742
|
+
})
|
|
743
|
+
|
|
744
|
+
it('accepts a simple domain name', () => {
|
|
745
|
+
expect(validateOriginator('example.com')).toBe('example.com')
|
|
746
|
+
})
|
|
747
|
+
|
|
748
|
+
it('throws for an empty originator after trimming', () => {
|
|
749
|
+
expect(() => validateOriginator(' ')).toThrow(WERR_INVALID_PARAMETER)
|
|
750
|
+
})
|
|
751
|
+
|
|
752
|
+
it('throws for a domain part exceeding 63 bytes', () => {
|
|
753
|
+
const longPart = 'a'.repeat(64)
|
|
754
|
+
expect(() => validateOriginator(`${longPart}.com`)).toThrow(WERR_INVALID_PARAMETER)
|
|
755
|
+
})
|
|
756
|
+
|
|
757
|
+
it('throws for an originator exceeding 250 total bytes', () => {
|
|
758
|
+
const longOriginator = 'a'.repeat(251)
|
|
759
|
+
expect(() => validateOriginator(longOriginator)).toThrow(WERR_INVALID_PARAMETER)
|
|
760
|
+
})
|
|
761
|
+
})
|
|
762
|
+
|
|
763
|
+
// ============================================================================
|
|
764
|
+
// validateRelinquishOutputArgs
|
|
765
|
+
// ============================================================================
|
|
766
|
+
|
|
767
|
+
describe('validateRelinquishOutputArgs', () => {
|
|
768
|
+
it('validates a complete set of args', () => {
|
|
769
|
+
const v = validateRelinquishOutputArgs({
|
|
770
|
+
basket: 'default',
|
|
771
|
+
output: VALID_OUTPOINT
|
|
772
|
+
})
|
|
773
|
+
expect(v.basket).toBe('default')
|
|
774
|
+
expect(v.output).toContain('.')
|
|
775
|
+
})
|
|
776
|
+
|
|
777
|
+
it('throws for invalid basket', () => {
|
|
778
|
+
expect(() =>
|
|
779
|
+
validateRelinquishOutputArgs({ basket: '', output: VALID_OUTPOINT })
|
|
780
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
781
|
+
})
|
|
782
|
+
})
|
|
783
|
+
|
|
784
|
+
// ============================================================================
|
|
785
|
+
// validateRelinquishCertificateArgs
|
|
786
|
+
// ============================================================================
|
|
787
|
+
|
|
788
|
+
describe('validateRelinquishCertificateArgs', () => {
|
|
789
|
+
it('validates a valid certificate reference', () => {
|
|
790
|
+
const v = validateRelinquishCertificateArgs({
|
|
791
|
+
type: VALID_BASE64,
|
|
792
|
+
serialNumber: VALID_BASE64,
|
|
793
|
+
certifier: VALID_PUBKEY_HEX
|
|
794
|
+
})
|
|
795
|
+
expect(v.type).toBe(VALID_BASE64)
|
|
796
|
+
})
|
|
797
|
+
|
|
798
|
+
it('throws for an invalid type', () => {
|
|
799
|
+
expect(() =>
|
|
800
|
+
validateRelinquishCertificateArgs({
|
|
801
|
+
type: '!!!',
|
|
802
|
+
serialNumber: VALID_BASE64,
|
|
803
|
+
certifier: VALID_PUBKEY_HEX
|
|
804
|
+
})
|
|
805
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
806
|
+
})
|
|
807
|
+
})
|
|
808
|
+
|
|
809
|
+
// ============================================================================
|
|
810
|
+
// validateListCertificatesArgs
|
|
811
|
+
// ============================================================================
|
|
812
|
+
|
|
813
|
+
describe('validateListCertificatesArgs', () => {
|
|
814
|
+
const validArgs = {
|
|
815
|
+
certifiers: [VALID_PUBKEY_HEX],
|
|
816
|
+
types: [VALID_BASE64],
|
|
817
|
+
limit: 10,
|
|
818
|
+
offset: 0
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
it('validates a minimal set of args with defaults', () => {
|
|
822
|
+
const v = validateListCertificatesArgs(validArgs as any)
|
|
823
|
+
expect(v.limit).toBe(10)
|
|
824
|
+
expect(v.offset).toBe(0)
|
|
825
|
+
expect(v.privileged).toBe(false)
|
|
826
|
+
})
|
|
827
|
+
|
|
828
|
+
it('applies default limit of 10 when limit is undefined', () => {
|
|
829
|
+
const v = validateListCertificatesArgs({ ...validArgs, limit: undefined } as any)
|
|
830
|
+
expect(v.limit).toBe(10)
|
|
831
|
+
})
|
|
832
|
+
|
|
833
|
+
it('throws when limit exceeds 10000', () => {
|
|
834
|
+
expect(() =>
|
|
835
|
+
validateListCertificatesArgs({ ...validArgs, limit: 10001 } as any)
|
|
836
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
837
|
+
})
|
|
838
|
+
|
|
839
|
+
it('throws when limit is below 1', () => {
|
|
840
|
+
expect(() =>
|
|
841
|
+
validateListCertificatesArgs({ ...validArgs, limit: 0 } as any)
|
|
842
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
843
|
+
})
|
|
844
|
+
})
|
|
845
|
+
|
|
846
|
+
// ============================================================================
|
|
847
|
+
// validateAcquireIssuanceCertificateArgs
|
|
848
|
+
// ============================================================================
|
|
849
|
+
|
|
850
|
+
describe('validateAcquireIssuanceCertificateArgs', () => {
|
|
851
|
+
const validIssuanceArgs: any = {
|
|
852
|
+
acquisitionProtocol: 'issuance',
|
|
853
|
+
type: VALID_BASE64,
|
|
854
|
+
certifier: VALID_PUBKEY_HEX,
|
|
855
|
+
certifierUrl: 'https://example.com/certify',
|
|
856
|
+
fields: { name: 'Alice' },
|
|
857
|
+
privileged: false
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
it('validates a valid issuance request', () => {
|
|
861
|
+
const v = validateAcquireIssuanceCertificateArgs(validIssuanceArgs)
|
|
862
|
+
expect(v.certifierUrl).toBe('https://example.com/certify')
|
|
863
|
+
expect(v.subject).toBe('')
|
|
864
|
+
})
|
|
865
|
+
|
|
866
|
+
it('throws when acquisitionProtocol is not "issuance"', () => {
|
|
867
|
+
expect(() =>
|
|
868
|
+
validateAcquireIssuanceCertificateArgs({ ...validIssuanceArgs, acquisitionProtocol: 'direct' })
|
|
869
|
+
).toThrow('Only acquire certificate via issuance requests allowed here.')
|
|
870
|
+
})
|
|
871
|
+
|
|
872
|
+
it('throws when serialNumber is present (not valid for issuance)', () => {
|
|
873
|
+
expect(() =>
|
|
874
|
+
validateAcquireIssuanceCertificateArgs({ ...validIssuanceArgs, serialNumber: VALID_BASE64 })
|
|
875
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
876
|
+
})
|
|
877
|
+
|
|
878
|
+
it('throws when signature is present', () => {
|
|
879
|
+
expect(() =>
|
|
880
|
+
validateAcquireIssuanceCertificateArgs({ ...validIssuanceArgs, signature: VALID_PUBKEY_HEX })
|
|
881
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
882
|
+
})
|
|
883
|
+
|
|
884
|
+
it('throws when revocationOutpoint is present', () => {
|
|
885
|
+
expect(() =>
|
|
886
|
+
validateAcquireIssuanceCertificateArgs({ ...validIssuanceArgs, revocationOutpoint: VALID_OUTPOINT })
|
|
887
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
888
|
+
})
|
|
889
|
+
|
|
890
|
+
it('throws when keyringRevealer is present', () => {
|
|
891
|
+
expect(() =>
|
|
892
|
+
validateAcquireIssuanceCertificateArgs({ ...validIssuanceArgs, keyringRevealer: 'certifier' })
|
|
893
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
894
|
+
})
|
|
895
|
+
|
|
896
|
+
it('throws when keyringForSubject is present', () => {
|
|
897
|
+
expect(() =>
|
|
898
|
+
validateAcquireIssuanceCertificateArgs({ ...validIssuanceArgs, keyringForSubject: {} })
|
|
899
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
900
|
+
})
|
|
901
|
+
|
|
902
|
+
it('throws when certifierUrl is missing', () => {
|
|
903
|
+
expect(() =>
|
|
904
|
+
validateAcquireIssuanceCertificateArgs({ ...validIssuanceArgs, certifierUrl: undefined })
|
|
905
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
906
|
+
})
|
|
907
|
+
|
|
908
|
+
it('throws when privileged is true but privilegedReason is absent', () => {
|
|
909
|
+
expect(() =>
|
|
910
|
+
validateAcquireIssuanceCertificateArgs({ ...validIssuanceArgs, privileged: true })
|
|
911
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
912
|
+
})
|
|
913
|
+
|
|
914
|
+
it('accepts privileged=true with a valid privilegedReason', () => {
|
|
915
|
+
const v = validateAcquireIssuanceCertificateArgs({
|
|
916
|
+
...validIssuanceArgs,
|
|
917
|
+
privileged: true,
|
|
918
|
+
privilegedReason: 'A valid reason'
|
|
919
|
+
})
|
|
920
|
+
expect(v.privileged).toBe(true)
|
|
921
|
+
})
|
|
922
|
+
})
|
|
923
|
+
|
|
924
|
+
// ============================================================================
|
|
925
|
+
// validateAcquireDirectCertificateArgs
|
|
926
|
+
// ============================================================================
|
|
927
|
+
|
|
928
|
+
describe('validateAcquireDirectCertificateArgs', () => {
|
|
929
|
+
const validDirectArgs: any = {
|
|
930
|
+
acquisitionProtocol: 'direct',
|
|
931
|
+
type: VALID_BASE64,
|
|
932
|
+
serialNumber: VALID_BASE64,
|
|
933
|
+
certifier: VALID_PUBKEY_HEX,
|
|
934
|
+
revocationOutpoint: VALID_OUTPOINT,
|
|
935
|
+
fields: { name: 'Bob' },
|
|
936
|
+
signature: VALID_PUBKEY_HEX,
|
|
937
|
+
keyringRevealer: 'certifier',
|
|
938
|
+
keyringForSubject: { fieldA: VALID_BASE64 },
|
|
939
|
+
privileged: false
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
it('validates a valid direct acquisition request', () => {
|
|
943
|
+
const v = validateAcquireDirectCertificateArgs(validDirectArgs)
|
|
944
|
+
expect(v.subject).toBe('')
|
|
945
|
+
expect(v.keyringRevealer).toBe('certifier')
|
|
946
|
+
})
|
|
947
|
+
|
|
948
|
+
it('throws when acquisitionProtocol is not "direct"', () => {
|
|
949
|
+
expect(() =>
|
|
950
|
+
validateAcquireDirectCertificateArgs({ ...validDirectArgs, acquisitionProtocol: 'issuance' })
|
|
951
|
+
).toThrow('Only acquire direct certificate requests allowed here.')
|
|
952
|
+
})
|
|
953
|
+
|
|
954
|
+
it('throws when serialNumber is missing', () => {
|
|
955
|
+
expect(() =>
|
|
956
|
+
validateAcquireDirectCertificateArgs({ ...validDirectArgs, serialNumber: undefined })
|
|
957
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
958
|
+
})
|
|
959
|
+
|
|
960
|
+
it('throws when signature is missing', () => {
|
|
961
|
+
expect(() =>
|
|
962
|
+
validateAcquireDirectCertificateArgs({ ...validDirectArgs, signature: undefined })
|
|
963
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
964
|
+
})
|
|
965
|
+
|
|
966
|
+
it('throws when revocationOutpoint is missing', () => {
|
|
967
|
+
expect(() =>
|
|
968
|
+
validateAcquireDirectCertificateArgs({ ...validDirectArgs, revocationOutpoint: undefined })
|
|
969
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
970
|
+
})
|
|
971
|
+
|
|
972
|
+
it('throws when keyringRevealer is missing', () => {
|
|
973
|
+
expect(() =>
|
|
974
|
+
validateAcquireDirectCertificateArgs({ ...validDirectArgs, keyringRevealer: undefined })
|
|
975
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
976
|
+
})
|
|
977
|
+
|
|
978
|
+
it('throws when keyringForSubject is null/undefined', () => {
|
|
979
|
+
expect(() =>
|
|
980
|
+
validateAcquireDirectCertificateArgs({ ...validDirectArgs, keyringForSubject: undefined })
|
|
981
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
982
|
+
})
|
|
983
|
+
|
|
984
|
+
it('throws when privileged is true but privilegedReason is absent', () => {
|
|
985
|
+
expect(() =>
|
|
986
|
+
validateAcquireDirectCertificateArgs({ ...validDirectArgs, privileged: true, privilegedReason: undefined })
|
|
987
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
988
|
+
})
|
|
989
|
+
|
|
990
|
+
it('validates a keyringRevealer hex string (non-"certifier")', () => {
|
|
991
|
+
const v = validateAcquireDirectCertificateArgs({
|
|
992
|
+
...validDirectArgs,
|
|
993
|
+
keyringRevealer: VALID_PUBKEY_HEX
|
|
994
|
+
})
|
|
995
|
+
expect(v.keyringRevealer).toBe(VALID_PUBKEY_HEX.toLowerCase())
|
|
996
|
+
})
|
|
997
|
+
})
|
|
998
|
+
|
|
999
|
+
// ============================================================================
|
|
1000
|
+
// validateProveCertificateArgs
|
|
1001
|
+
// ============================================================================
|
|
1002
|
+
|
|
1003
|
+
describe('validateProveCertificateArgs', () => {
|
|
1004
|
+
const validArgs: any = {
|
|
1005
|
+
certificate: {
|
|
1006
|
+
type: VALID_BASE64,
|
|
1007
|
+
serialNumber: VALID_BASE64,
|
|
1008
|
+
certifier: VALID_PUBKEY_HEX,
|
|
1009
|
+
subject: VALID_PUBKEY_HEX
|
|
1010
|
+
},
|
|
1011
|
+
fieldsToReveal: ['fieldA', 'fieldB'],
|
|
1012
|
+
verifier: VALID_PUBKEY_HEX,
|
|
1013
|
+
privileged: false
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
it('validates a complete prove certificate request', () => {
|
|
1017
|
+
const v = validateProveCertificateArgs(validArgs)
|
|
1018
|
+
expect(v.verifier).toBeDefined()
|
|
1019
|
+
expect(v.fieldsToReveal).toEqual(['fieldA', 'fieldB'])
|
|
1020
|
+
expect(v.privileged).toBe(false)
|
|
1021
|
+
})
|
|
1022
|
+
|
|
1023
|
+
it('throws when privileged is true but privilegedReason is absent', () => {
|
|
1024
|
+
expect(() =>
|
|
1025
|
+
validateProveCertificateArgs({ ...validArgs, privileged: true })
|
|
1026
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
1027
|
+
})
|
|
1028
|
+
|
|
1029
|
+
it('accepts privileged=true with a valid reason', () => {
|
|
1030
|
+
const v = validateProveCertificateArgs({
|
|
1031
|
+
...validArgs,
|
|
1032
|
+
privileged: true,
|
|
1033
|
+
privilegedReason: 'A good reason'
|
|
1034
|
+
})
|
|
1035
|
+
expect(v.privileged).toBe(true)
|
|
1036
|
+
})
|
|
1037
|
+
|
|
1038
|
+
it('handles undefined optional certificate fields', () => {
|
|
1039
|
+
const v = validateProveCertificateArgs({
|
|
1040
|
+
...validArgs,
|
|
1041
|
+
certificate: {}
|
|
1042
|
+
})
|
|
1043
|
+
expect(v.type).toBeUndefined()
|
|
1044
|
+
expect(v.certifier).toBeUndefined()
|
|
1045
|
+
})
|
|
1046
|
+
})
|
|
1047
|
+
|
|
1048
|
+
// ============================================================================
|
|
1049
|
+
// validateDiscoverByIdentityKeyArgs
|
|
1050
|
+
// ============================================================================
|
|
1051
|
+
|
|
1052
|
+
describe('validateDiscoverByIdentityKeyArgs', () => {
|
|
1053
|
+
const validArgs: any = {
|
|
1054
|
+
identityKey: VALID_PUBKEY_HEX,
|
|
1055
|
+
limit: 10,
|
|
1056
|
+
offset: 0
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
it('validates a valid request', () => {
|
|
1060
|
+
const v = validateDiscoverByIdentityKeyArgs(validArgs)
|
|
1061
|
+
expect(v.identityKey).toBe(VALID_PUBKEY_HEX.toLowerCase())
|
|
1062
|
+
expect(v.seekPermission).toBe(false)
|
|
1063
|
+
})
|
|
1064
|
+
|
|
1065
|
+
it('applies default limit of 10', () => {
|
|
1066
|
+
const v = validateDiscoverByIdentityKeyArgs({ ...validArgs, limit: undefined })
|
|
1067
|
+
expect(v.limit).toBe(10)
|
|
1068
|
+
})
|
|
1069
|
+
|
|
1070
|
+
it('throws for identity key that is not 66 chars', () => {
|
|
1071
|
+
expect(() =>
|
|
1072
|
+
validateDiscoverByIdentityKeyArgs({ ...validArgs, identityKey: '0234' })
|
|
1073
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
1074
|
+
})
|
|
1075
|
+
})
|
|
1076
|
+
|
|
1077
|
+
// ============================================================================
|
|
1078
|
+
// validateDiscoverByAttributesArgs
|
|
1079
|
+
// ============================================================================
|
|
1080
|
+
|
|
1081
|
+
describe('validateDiscoverByAttributesArgs', () => {
|
|
1082
|
+
const validArgs: any = {
|
|
1083
|
+
attributes: { name: 'Alice' },
|
|
1084
|
+
limit: 10,
|
|
1085
|
+
offset: 0
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
it('validates a valid request', () => {
|
|
1089
|
+
const v = validateDiscoverByAttributesArgs(validArgs)
|
|
1090
|
+
expect(v.attributes).toEqual({ name: 'Alice' })
|
|
1091
|
+
expect(v.seekPermission).toBe(false)
|
|
1092
|
+
})
|
|
1093
|
+
|
|
1094
|
+
it('applies default limit of 10', () => {
|
|
1095
|
+
const v = validateDiscoverByAttributesArgs({ ...validArgs, limit: undefined })
|
|
1096
|
+
expect(v.limit).toBe(10)
|
|
1097
|
+
})
|
|
1098
|
+
|
|
1099
|
+
it('throws for a field name that is too long', () => {
|
|
1100
|
+
const longName = 'a'.repeat(51)
|
|
1101
|
+
expect(() =>
|
|
1102
|
+
validateDiscoverByAttributesArgs({ ...validArgs, attributes: { [longName]: 'value' } })
|
|
1103
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
1104
|
+
})
|
|
1105
|
+
})
|
|
1106
|
+
|
|
1107
|
+
// ============================================================================
|
|
1108
|
+
// validateListOutputsArgs
|
|
1109
|
+
// ============================================================================
|
|
1110
|
+
|
|
1111
|
+
describe('validateListOutputsArgs', () => {
|
|
1112
|
+
const validArgs: any = {
|
|
1113
|
+
basket: 'default',
|
|
1114
|
+
limit: 10,
|
|
1115
|
+
offset: 0
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
it('validates minimal args', () => {
|
|
1119
|
+
const v = validateListOutputsArgs(validArgs)
|
|
1120
|
+
expect(v.basket).toBe('default')
|
|
1121
|
+
expect(v.tagQueryMode).toBe('any')
|
|
1122
|
+
expect(v.includeLockingScripts).toBe(false)
|
|
1123
|
+
expect(v.includeTransactions).toBe(false)
|
|
1124
|
+
})
|
|
1125
|
+
|
|
1126
|
+
it('sets includeLockingScripts = true when include = "locking scripts"', () => {
|
|
1127
|
+
const v = validateListOutputsArgs({ ...validArgs, include: 'locking scripts' })
|
|
1128
|
+
expect(v.includeLockingScripts).toBe(true)
|
|
1129
|
+
expect(v.includeTransactions).toBe(false)
|
|
1130
|
+
})
|
|
1131
|
+
|
|
1132
|
+
it('sets includeTransactions = true when include = "entire transactions"', () => {
|
|
1133
|
+
const v = validateListOutputsArgs({ ...validArgs, include: 'entire transactions' })
|
|
1134
|
+
expect(v.includeTransactions).toBe(true)
|
|
1135
|
+
expect(v.includeLockingScripts).toBe(false)
|
|
1136
|
+
})
|
|
1137
|
+
|
|
1138
|
+
it('accepts tagQueryMode "all"', () => {
|
|
1139
|
+
const v = validateListOutputsArgs({ ...validArgs, tagQueryMode: 'all' })
|
|
1140
|
+
expect(v.tagQueryMode).toBe('all')
|
|
1141
|
+
})
|
|
1142
|
+
|
|
1143
|
+
it('throws for invalid tagQueryMode', () => {
|
|
1144
|
+
expect(() =>
|
|
1145
|
+
validateListOutputsArgs({ ...validArgs, tagQueryMode: 'none' })
|
|
1146
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
1147
|
+
})
|
|
1148
|
+
|
|
1149
|
+
it('applies default limit', () => {
|
|
1150
|
+
const v = validateListOutputsArgs({ ...validArgs, limit: undefined })
|
|
1151
|
+
expect(v.limit).toBe(10)
|
|
1152
|
+
})
|
|
1153
|
+
})
|
|
1154
|
+
|
|
1155
|
+
// ============================================================================
|
|
1156
|
+
// validateListActionsArgs
|
|
1157
|
+
// ============================================================================
|
|
1158
|
+
|
|
1159
|
+
describe('validateListActionsArgs', () => {
|
|
1160
|
+
const validArgs: any = {
|
|
1161
|
+
labels: ['my-label'],
|
|
1162
|
+
limit: 10,
|
|
1163
|
+
offset: 0
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
it('validates minimal args', () => {
|
|
1167
|
+
const v = validateListActionsArgs(validArgs)
|
|
1168
|
+
expect(v.labels).toEqual(['my-label'])
|
|
1169
|
+
expect(v.labelQueryMode).toBe('any')
|
|
1170
|
+
expect(v.includeInputs).toBe(false)
|
|
1171
|
+
expect(v.includeOutputs).toBe(false)
|
|
1172
|
+
})
|
|
1173
|
+
|
|
1174
|
+
it('accepts labelQueryMode "all"', () => {
|
|
1175
|
+
const v = validateListActionsArgs({ ...validArgs, labelQueryMode: 'all' })
|
|
1176
|
+
expect(v.labelQueryMode).toBe('all')
|
|
1177
|
+
})
|
|
1178
|
+
|
|
1179
|
+
it('throws for invalid labelQueryMode', () => {
|
|
1180
|
+
expect(() =>
|
|
1181
|
+
validateListActionsArgs({ ...validArgs, labelQueryMode: 'none' })
|
|
1182
|
+
).toThrow(WERR_INVALID_PARAMETER)
|
|
1183
|
+
})
|
|
1184
|
+
|
|
1185
|
+
it('applies default limit of 10', () => {
|
|
1186
|
+
const v = validateListActionsArgs({ ...validArgs, limit: undefined })
|
|
1187
|
+
expect(v.limit).toBe(10)
|
|
1188
|
+
})
|
|
1189
|
+
|
|
1190
|
+
it('applies boolean include flags', () => {
|
|
1191
|
+
const v = validateListActionsArgs({
|
|
1192
|
+
...validArgs,
|
|
1193
|
+
includeInputs: true,
|
|
1194
|
+
includeOutputs: true,
|
|
1195
|
+
includeLabels: true,
|
|
1196
|
+
includeInputSourceLockingScripts: true,
|
|
1197
|
+
includeInputUnlockingScripts: true,
|
|
1198
|
+
includeOutputLockingScripts: true
|
|
1199
|
+
})
|
|
1200
|
+
expect(v.includeInputs).toBe(true)
|
|
1201
|
+
expect(v.includeOutputs).toBe(true)
|
|
1202
|
+
expect(v.includeLabels).toBe(true)
|
|
1203
|
+
expect(v.includeInputSourceLockingScripts).toBe(true)
|
|
1204
|
+
expect(v.includeInputUnlockingScripts).toBe(true)
|
|
1205
|
+
expect(v.includeOutputLockingScripts).toBe(true)
|
|
1206
|
+
})
|
|
1207
|
+
})
|
|
1208
|
+
|
|
1209
|
+
// ============================================================================
|
|
1210
|
+
// specOpThrowReviewActions constant
|
|
1211
|
+
// ============================================================================
|
|
1212
|
+
|
|
1213
|
+
describe('specOpThrowReviewActions', () => {
|
|
1214
|
+
it('is a non-empty string constant', () => {
|
|
1215
|
+
expect(typeof specOpThrowReviewActions).toBe('string')
|
|
1216
|
+
expect(specOpThrowReviewActions.length).toBeGreaterThan(0)
|
|
1217
|
+
})
|
|
1218
|
+
})
|