@enbox/dwn-sdk-js 0.1.1 → 0.1.2
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/browser.mjs +6 -6
- package/dist/browser.mjs.map +3 -3
- package/dist/esm/src/utils/protocols.js +2 -2
- package/dist/esm/src/utils/protocols.js.map +1 -1
- package/dist/esm/tests/fuzz/arbitraries/dwn-message.arbitrary.js +72 -0
- package/dist/esm/tests/fuzz/arbitraries/dwn-message.arbitrary.js.map +1 -0
- package/dist/esm/tests/fuzz/arbitraries/filter.arbitrary.js +53 -0
- package/dist/esm/tests/fuzz/arbitraries/filter.arbitrary.js.map +1 -0
- package/dist/esm/tests/fuzz/arbitraries/jws.arbitrary.js +55 -0
- package/dist/esm/tests/fuzz/arbitraries/jws.arbitrary.js.map +1 -0
- package/dist/esm/tests/fuzz/arbitraries/protocol-definition.arbitrary.js +106 -0
- package/dist/esm/tests/fuzz/arbitraries/protocol-definition.arbitrary.js.map +1 -0
- package/dist/esm/tests/fuzz/arbitraries/store.arbitrary.js +139 -0
- package/dist/esm/tests/fuzz/arbitraries/store.arbitrary.js.map +1 -0
- package/dist/esm/tests/fuzz/cid.fuzz.spec.js +74 -0
- package/dist/esm/tests/fuzz/cid.fuzz.spec.js.map +1 -0
- package/dist/esm/tests/fuzz/compound-index.fuzz.spec.js +203 -0
- package/dist/esm/tests/fuzz/compound-index.fuzz.spec.js.map +1 -0
- package/dist/esm/tests/fuzz/data-store.fuzz.spec.js +146 -0
- package/dist/esm/tests/fuzz/data-store.fuzz.spec.js.map +1 -0
- package/dist/esm/tests/fuzz/data-stream.fuzz.spec.js +44 -0
- package/dist/esm/tests/fuzz/data-stream.fuzz.spec.js.map +1 -0
- package/dist/esm/tests/fuzz/encoder.fuzz.spec.js +76 -0
- package/dist/esm/tests/fuzz/encoder.fuzz.spec.js.map +1 -0
- package/dist/esm/tests/fuzz/encryption.fuzz.spec.js +132 -0
- package/dist/esm/tests/fuzz/encryption.fuzz.spec.js.map +1 -0
- package/dist/esm/tests/fuzz/filter.fuzz.spec.js +149 -0
- package/dist/esm/tests/fuzz/filter.fuzz.spec.js.map +1 -0
- package/dist/esm/tests/fuzz/get-rule-set-at-path.fuzz.spec.js +82 -0
- package/dist/esm/tests/fuzz/get-rule-set-at-path.fuzz.spec.js.map +1 -0
- package/dist/esm/tests/fuzz/hd-key.fuzz.spec.js +77 -0
- package/dist/esm/tests/fuzz/hd-key.fuzz.spec.js.map +1 -0
- package/dist/esm/tests/fuzz/immutable-properties.fuzz.spec.js +86 -0
- package/dist/esm/tests/fuzz/immutable-properties.fuzz.spec.js.map +1 -0
- package/dist/esm/tests/fuzz/index-level.fuzz.spec.js +195 -0
- package/dist/esm/tests/fuzz/index-level.fuzz.spec.js.map +1 -0
- package/dist/esm/tests/fuzz/jws.fuzz.spec.js +100 -0
- package/dist/esm/tests/fuzz/jws.fuzz.spec.js.map +1 -0
- package/dist/esm/tests/fuzz/message-store.fuzz.spec.js +154 -0
- package/dist/esm/tests/fuzz/message-store.fuzz.spec.js.map +1 -0
- package/dist/esm/tests/fuzz/object.fuzz.spec.js +82 -0
- package/dist/esm/tests/fuzz/object.fuzz.spec.js.map +1 -0
- package/dist/esm/tests/fuzz/process-message.fuzz.spec.js +85 -0
- package/dist/esm/tests/fuzz/process-message.fuzz.spec.js.map +1 -0
- package/dist/esm/tests/fuzz/protocol-definition.fuzz.spec.js +145 -0
- package/dist/esm/tests/fuzz/protocol-definition.fuzz.spec.js.map +1 -0
- package/dist/esm/tests/fuzz/protocol-validation.fuzz.spec.js +146 -0
- package/dist/esm/tests/fuzz/protocol-validation.fuzz.spec.js.map +1 -0
- package/dist/esm/tests/fuzz/protocols-utils.fuzz.spec.js +41 -0
- package/dist/esm/tests/fuzz/protocols-utils.fuzz.spec.js.map +1 -0
- package/dist/esm/tests/fuzz/records-utils.fuzz.spec.js +81 -0
- package/dist/esm/tests/fuzz/records-utils.fuzz.spec.js.map +1 -0
- package/dist/esm/tests/fuzz/resumable-task-store.fuzz.spec.js +106 -0
- package/dist/esm/tests/fuzz/resumable-task-store.fuzz.spec.js.map +1 -0
- package/dist/esm/tests/fuzz/schema-validation.fuzz.spec.js +126 -0
- package/dist/esm/tests/fuzz/schema-validation.fuzz.spec.js.map +1 -0
- package/dist/esm/tests/fuzz/secp256k1.fuzz.spec.js +74 -0
- package/dist/esm/tests/fuzz/secp256k1.fuzz.spec.js.map +1 -0
- package/dist/esm/tests/fuzz/secp256r1.fuzz.spec.js +60 -0
- package/dist/esm/tests/fuzz/secp256r1.fuzz.spec.js.map +1 -0
- package/dist/esm/tests/fuzz/time.fuzz.spec.js +82 -0
- package/dist/esm/tests/fuzz/time.fuzz.spec.js.map +1 -0
- package/dist/esm/tests/fuzz/url.fuzz.spec.js +118 -0
- package/dist/esm/tests/fuzz/url.fuzz.spec.js.map +1 -0
- package/dist/types/tests/fuzz/arbitraries/dwn-message.arbitrary.d.ts +31 -0
- package/dist/types/tests/fuzz/arbitraries/dwn-message.arbitrary.d.ts.map +1 -0
- package/dist/types/tests/fuzz/arbitraries/filter.arbitrary.d.ts +27 -0
- package/dist/types/tests/fuzz/arbitraries/filter.arbitrary.d.ts.map +1 -0
- package/dist/types/tests/fuzz/arbitraries/jws.arbitrary.d.ts +26 -0
- package/dist/types/tests/fuzz/arbitraries/jws.arbitrary.d.ts.map +1 -0
- package/dist/types/tests/fuzz/arbitraries/protocol-definition.arbitrary.d.ts +31 -0
- package/dist/types/tests/fuzz/arbitraries/protocol-definition.arbitrary.d.ts.map +1 -0
- package/dist/types/tests/fuzz/arbitraries/store.arbitrary.d.ts +71 -0
- package/dist/types/tests/fuzz/arbitraries/store.arbitrary.d.ts.map +1 -0
- package/dist/types/tests/fuzz/cid.fuzz.spec.d.ts +2 -0
- package/dist/types/tests/fuzz/cid.fuzz.spec.d.ts.map +1 -0
- package/dist/types/tests/fuzz/compound-index.fuzz.spec.d.ts +2 -0
- package/dist/types/tests/fuzz/compound-index.fuzz.spec.d.ts.map +1 -0
- package/dist/types/tests/fuzz/data-store.fuzz.spec.d.ts +2 -0
- package/dist/types/tests/fuzz/data-store.fuzz.spec.d.ts.map +1 -0
- package/dist/types/tests/fuzz/data-stream.fuzz.spec.d.ts +2 -0
- package/dist/types/tests/fuzz/data-stream.fuzz.spec.d.ts.map +1 -0
- package/dist/types/tests/fuzz/encoder.fuzz.spec.d.ts +2 -0
- package/dist/types/tests/fuzz/encoder.fuzz.spec.d.ts.map +1 -0
- package/dist/types/tests/fuzz/encryption.fuzz.spec.d.ts +2 -0
- package/dist/types/tests/fuzz/encryption.fuzz.spec.d.ts.map +1 -0
- package/dist/types/tests/fuzz/filter.fuzz.spec.d.ts +2 -0
- package/dist/types/tests/fuzz/filter.fuzz.spec.d.ts.map +1 -0
- package/dist/types/tests/fuzz/get-rule-set-at-path.fuzz.spec.d.ts +2 -0
- package/dist/types/tests/fuzz/get-rule-set-at-path.fuzz.spec.d.ts.map +1 -0
- package/dist/types/tests/fuzz/hd-key.fuzz.spec.d.ts +2 -0
- package/dist/types/tests/fuzz/hd-key.fuzz.spec.d.ts.map +1 -0
- package/dist/types/tests/fuzz/immutable-properties.fuzz.spec.d.ts +2 -0
- package/dist/types/tests/fuzz/immutable-properties.fuzz.spec.d.ts.map +1 -0
- package/dist/types/tests/fuzz/index-level.fuzz.spec.d.ts +2 -0
- package/dist/types/tests/fuzz/index-level.fuzz.spec.d.ts.map +1 -0
- package/dist/types/tests/fuzz/jws.fuzz.spec.d.ts +2 -0
- package/dist/types/tests/fuzz/jws.fuzz.spec.d.ts.map +1 -0
- package/dist/types/tests/fuzz/message-store.fuzz.spec.d.ts +2 -0
- package/dist/types/tests/fuzz/message-store.fuzz.spec.d.ts.map +1 -0
- package/dist/types/tests/fuzz/object.fuzz.spec.d.ts +2 -0
- package/dist/types/tests/fuzz/object.fuzz.spec.d.ts.map +1 -0
- package/dist/types/tests/fuzz/process-message.fuzz.spec.d.ts +2 -0
- package/dist/types/tests/fuzz/process-message.fuzz.spec.d.ts.map +1 -0
- package/dist/types/tests/fuzz/protocol-definition.fuzz.spec.d.ts +2 -0
- package/dist/types/tests/fuzz/protocol-definition.fuzz.spec.d.ts.map +1 -0
- package/dist/types/tests/fuzz/protocol-validation.fuzz.spec.d.ts +2 -0
- package/dist/types/tests/fuzz/protocol-validation.fuzz.spec.d.ts.map +1 -0
- package/dist/types/tests/fuzz/protocols-utils.fuzz.spec.d.ts +2 -0
- package/dist/types/tests/fuzz/protocols-utils.fuzz.spec.d.ts.map +1 -0
- package/dist/types/tests/fuzz/records-utils.fuzz.spec.d.ts +2 -0
- package/dist/types/tests/fuzz/records-utils.fuzz.spec.d.ts.map +1 -0
- package/dist/types/tests/fuzz/resumable-task-store.fuzz.spec.d.ts +2 -0
- package/dist/types/tests/fuzz/resumable-task-store.fuzz.spec.d.ts.map +1 -0
- package/dist/types/tests/fuzz/schema-validation.fuzz.spec.d.ts +2 -0
- package/dist/types/tests/fuzz/schema-validation.fuzz.spec.d.ts.map +1 -0
- package/dist/types/tests/fuzz/secp256k1.fuzz.spec.d.ts +2 -0
- package/dist/types/tests/fuzz/secp256k1.fuzz.spec.d.ts.map +1 -0
- package/dist/types/tests/fuzz/secp256r1.fuzz.spec.d.ts +2 -0
- package/dist/types/tests/fuzz/secp256r1.fuzz.spec.d.ts.map +1 -0
- package/dist/types/tests/fuzz/time.fuzz.spec.d.ts +2 -0
- package/dist/types/tests/fuzz/time.fuzz.spec.d.ts.map +1 -0
- package/dist/types/tests/fuzz/url.fuzz.spec.d.ts +2 -0
- package/dist/types/tests/fuzz/url.fuzz.spec.d.ts.map +1 -0
- package/package.json +8 -5
- package/src/utils/protocols.ts +2 -2
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test';
|
|
2
|
+
import fc from 'fast-check';
|
|
3
|
+
import { ContentEncryptionAlgorithm, Encryption } from '../../src/utils/encryption.js';
|
|
4
|
+
const numRuns = Number(process.env.FAST_CHECK_NUM_RUNS) || 200;
|
|
5
|
+
/** Generates a random 32-byte key (256-bit) for AES-256-GCM or XChaCha20. */
|
|
6
|
+
function aes256Key() {
|
|
7
|
+
return fc.uint8Array({ minLength: 32, maxLength: 32 });
|
|
8
|
+
}
|
|
9
|
+
/** Generates a random 12-byte IV for AES-256-GCM. */
|
|
10
|
+
function aesGcmIv() {
|
|
11
|
+
return fc.uint8Array({ minLength: 12, maxLength: 12 });
|
|
12
|
+
}
|
|
13
|
+
/** Generates a random 24-byte nonce for XChaCha20-Poly1305. */
|
|
14
|
+
function xchachaIv() {
|
|
15
|
+
return fc.uint8Array({ minLength: 24, maxLength: 24 });
|
|
16
|
+
}
|
|
17
|
+
describe('Encryption — fuzz', () => {
|
|
18
|
+
describe('AES-256-GCM — encrypt/decrypt roundtrip', () => {
|
|
19
|
+
it('should roundtrip arbitrary plaintext through AES-256-GCM', async () => {
|
|
20
|
+
await fc.assert(fc.asyncProperty(aes256Key(), aesGcmIv(), fc.uint8Array({ minLength: 0, maxLength: 1024 }), async (key, iv, plaintext) => {
|
|
21
|
+
const { ciphertext, tag } = await Encryption.aeadEncrypt(ContentEncryptionAlgorithm.A256GCM, key, iv, plaintext);
|
|
22
|
+
const decrypted = await Encryption.aeadDecrypt(ContentEncryptionAlgorithm.A256GCM, key, iv, ciphertext, tag);
|
|
23
|
+
expect(decrypted).toEqual(plaintext);
|
|
24
|
+
}), { numRuns });
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
describe('XChaCha20-Poly1305 — encrypt/decrypt roundtrip', () => {
|
|
28
|
+
it('should roundtrip arbitrary plaintext through XChaCha20-Poly1305', async () => {
|
|
29
|
+
await fc.assert(fc.asyncProperty(aes256Key(), xchachaIv(), fc.uint8Array({ minLength: 0, maxLength: 1024 }), async (key, iv, plaintext) => {
|
|
30
|
+
const { ciphertext, tag } = await Encryption.aeadEncrypt(ContentEncryptionAlgorithm.XC20P, key, iv, plaintext);
|
|
31
|
+
const decrypted = await Encryption.aeadDecrypt(ContentEncryptionAlgorithm.XC20P, key, iv, ciphertext, tag);
|
|
32
|
+
expect(decrypted).toEqual(plaintext);
|
|
33
|
+
}), { numRuns });
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
describe('AES-256-GCM — authenticated output differs from plaintext', () => {
|
|
37
|
+
it('should produce authenticated output (ciphertext + tag) that differs from the plaintext', async () => {
|
|
38
|
+
await fc.assert(fc.asyncProperty(aes256Key(), aesGcmIv(), fc.uint8Array({ minLength: 1, maxLength: 512 }), async (key, iv, plaintext) => {
|
|
39
|
+
const { ciphertext, tag } = await Encryption.aeadEncrypt(ContentEncryptionAlgorithm.A256GCM, key, iv, plaintext);
|
|
40
|
+
// The full AEAD output is ciphertext || tag. The 16-byte tag
|
|
41
|
+
// guarantees the output is always longer than the input, so a
|
|
42
|
+
// byte-level match is impossible — unlike comparing raw
|
|
43
|
+
// ciphertext bytes alone, which can coincide for short inputs.
|
|
44
|
+
const authenticatedOutput = new Uint8Array([...ciphertext, ...tag]);
|
|
45
|
+
const areEqual = plaintext.length === authenticatedOutput.length &&
|
|
46
|
+
plaintext.every((byte, i) => byte === authenticatedOutput[i]);
|
|
47
|
+
expect(areEqual).toBe(false);
|
|
48
|
+
}), { numRuns });
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
describe('AES-256-GCM — wrong key fails decryption', () => {
|
|
52
|
+
it('should fail to decrypt with a different key', async () => {
|
|
53
|
+
await fc.assert(fc.asyncProperty(aes256Key(), aes256Key(), aesGcmIv(), fc.uint8Array({ minLength: 1, maxLength: 256 }), async (key1, key2, iv, plaintext) => {
|
|
54
|
+
// Skip if keys are identical
|
|
55
|
+
if (key1.every((byte, i) => byte === key2[i])) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const { ciphertext, tag } = await Encryption.aeadEncrypt(ContentEncryptionAlgorithm.A256GCM, key1, iv, plaintext);
|
|
59
|
+
try {
|
|
60
|
+
await Encryption.aeadDecrypt(ContentEncryptionAlgorithm.A256GCM, key2, iv, ciphertext, tag);
|
|
61
|
+
// If decryption somehow succeeds, the result should differ from plaintext
|
|
62
|
+
// (astronomically unlikely with AEAD)
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// Expected: AEAD authentication should fail with wrong key
|
|
66
|
+
}
|
|
67
|
+
}), { numRuns });
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe('XChaCha20-Poly1305 — wrong key fails decryption', () => {
|
|
71
|
+
it('should fail to decrypt with a different key', async () => {
|
|
72
|
+
await fc.assert(fc.asyncProperty(aes256Key(), aes256Key(), xchachaIv(), fc.uint8Array({ minLength: 1, maxLength: 256 }), async (key1, key2, iv, plaintext) => {
|
|
73
|
+
// Skip if keys are identical
|
|
74
|
+
if (key1.every((byte, i) => byte === key2[i])) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const { ciphertext, tag } = await Encryption.aeadEncrypt(ContentEncryptionAlgorithm.XC20P, key1, iv, plaintext);
|
|
78
|
+
try {
|
|
79
|
+
await Encryption.aeadDecrypt(ContentEncryptionAlgorithm.XC20P, key2, iv, ciphertext, tag);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// Expected: AEAD authentication should fail with wrong key
|
|
83
|
+
}
|
|
84
|
+
}), { numRuns });
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
describe('AES-256-GCM — tampered ciphertext fails decryption', () => {
|
|
88
|
+
it('should fail to decrypt when ciphertext is modified', async () => {
|
|
89
|
+
await fc.assert(fc.asyncProperty(aes256Key(), aesGcmIv(), fc.uint8Array({ minLength: 1, maxLength: 256 }), fc.nat(), async (key, iv, plaintext, flipIndex) => {
|
|
90
|
+
const { ciphertext, tag } = await Encryption.aeadEncrypt(ContentEncryptionAlgorithm.A256GCM, key, iv, plaintext);
|
|
91
|
+
if (ciphertext.length === 0) {
|
|
92
|
+
return; // skip empty ciphertext
|
|
93
|
+
}
|
|
94
|
+
// Flip a bit in the ciphertext
|
|
95
|
+
const tampered = new Uint8Array(ciphertext);
|
|
96
|
+
const idx = flipIndex % tampered.length;
|
|
97
|
+
tampered[idx] ^= 0xFF;
|
|
98
|
+
try {
|
|
99
|
+
await Encryption.aeadDecrypt(ContentEncryptionAlgorithm.A256GCM, key, iv, tampered, tag);
|
|
100
|
+
// If decryption somehow succeeds, data integrity has been compromised
|
|
101
|
+
// (astronomically unlikely with AEAD)
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// Expected: AEAD authentication should fail on tampered ciphertext
|
|
105
|
+
}
|
|
106
|
+
}), { numRuns });
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
describe('encrypt — deterministic output for same inputs', () => {
|
|
110
|
+
it('should produce the same ciphertext and tag for the same key, IV, and plaintext', async () => {
|
|
111
|
+
await fc.assert(fc.asyncProperty(aes256Key(), aesGcmIv(), fc.uint8Array({ minLength: 0, maxLength: 256 }), async (key, iv, plaintext) => {
|
|
112
|
+
const result1 = await Encryption.aeadEncrypt(ContentEncryptionAlgorithm.A256GCM, key, iv, plaintext);
|
|
113
|
+
const result2 = await Encryption.aeadEncrypt(ContentEncryptionAlgorithm.A256GCM, key, iv, plaintext);
|
|
114
|
+
expect(result1.ciphertext).toEqual(result2.ciphertext);
|
|
115
|
+
expect(result1.tag).toEqual(result2.tag);
|
|
116
|
+
}), { numRuns });
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
describe('aeadDecrypt — crash resistance with malformed inputs', () => {
|
|
120
|
+
it('should throw (not crash) on wrong-length IVs for AES-GCM', async () => {
|
|
121
|
+
await fc.assert(fc.asyncProperty(aes256Key(), fc.uint8Array({ minLength: 0, maxLength: 50 }).filter((iv) => iv.length !== 12), fc.uint8Array({ minLength: 1, maxLength: 64 }), fc.uint8Array({ minLength: 16, maxLength: 16 }), async (key, badIv, ciphertext, tag) => {
|
|
122
|
+
try {
|
|
123
|
+
await Encryption.aeadDecrypt(ContentEncryptionAlgorithm.A256GCM, key, badIv, ciphertext, tag);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// Expected: wrong IV length should throw
|
|
127
|
+
}
|
|
128
|
+
}), { numRuns: Math.min(numRuns, 50) });
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
//# sourceMappingURL=encryption.fuzz.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encryption.fuzz.spec.js","sourceRoot":"","sources":["../../../../tests/fuzz/encryption.fuzz.spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC;AAEhD,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,OAAO,EAAE,0BAA0B,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAEvF,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,IAAI,GAAG,CAAC;AAE/D,6EAA6E;AAC7E,SAAS,SAAS;IAChB,OAAO,EAAE,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC;AACzD,CAAC;AAED,qDAAqD;AACrD,SAAS,QAAQ;IACf,OAAO,EAAE,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC;AACzD,CAAC;AAED,+DAA+D;AAC/D,SAAS,SAAS;IAChB,OAAO,EAAE,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC;AACzD,CAAC;AAED,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IAEjC,QAAQ,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACvD,EAAE,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;YACxE,MAAM,EAAE,CAAC,MAAM,CACb,EAAE,CAAC,aAAa,CACd,SAAS,EAAE,EACX,QAAQ,EAAE,EACV,EAAE,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,EAChD,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE;gBAC3B,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,MAAM,UAAU,CAAC,WAAW,CACtD,0BAA0B,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,SAAS,CACvD,CAAC;gBACF,MAAM,SAAS,GAAG,MAAM,UAAU,CAAC,WAAW,CAC5C,0BAA0B,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,CAC7D,CAAC;gBACF,MAAM,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YACvC,CAAC,CACF,EACD,EAAE,OAAO,EAAE,CACZ,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,gDAAgD,EAAE,GAAG,EAAE;QAC9D,EAAE,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;YAC/E,MAAM,EAAE,CAAC,MAAM,CACb,EAAE,CAAC,aAAa,CACd,SAAS,EAAE,EACX,SAAS,EAAE,EACX,EAAE,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,EAChD,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE;gBAC3B,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,MAAM,UAAU,CAAC,WAAW,CACtD,0BAA0B,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,SAAS,CACrD,CAAC;gBACF,MAAM,SAAS,GAAG,MAAM,UAAU,CAAC,WAAW,CAC5C,0BAA0B,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,CAC3D,CAAC;gBACF,MAAM,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YACvC,CAAC,CACF,EACD,EAAE,OAAO,EAAE,CACZ,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACzE,EAAE,CAAC,wFAAwF,EAAE,KAAK,IAAI,EAAE;YACtG,MAAM,EAAE,CAAC,MAAM,CACb,EAAE,CAAC,aAAa,CACd,SAAS,EAAE,EACX,QAAQ,EAAE,EACV,EAAE,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,EAC/C,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE;gBAC3B,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,MAAM,UAAU,CAAC,WAAW,CACtD,0BAA0B,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,SAAS,CACvD,CAAC;gBACF,8DAA8D;gBAC9D,8DAA8D;gBAC9D,wDAAwD;gBACxD,+DAA+D;gBAC/D,MAAM,mBAAmB,GAAG,IAAI,UAAU,CAAC,CAAC,GAAG,UAAU,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC;gBACpE,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,KAAK,mBAAmB,CAAC,MAAM;oBAC9D,SAAS,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,KAAK,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC;gBAChE,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC/B,CAAC,CACF,EACD,EAAE,OAAO,EAAE,CACZ,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,0CAA0C,EAAE,GAAG,EAAE;QACxD,EAAE,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;YAC3D,MAAM,EAAE,CAAC,MAAM,CACb,EAAE,CAAC,aAAa,CACd,SAAS,EAAE,EACX,SAAS,EAAE,EACX,QAAQ,EAAE,EACV,EAAE,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,EAC/C,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE;gBAClC,6BAA6B;gBAC7B,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC9C,OAAO;gBACT,CAAC;gBAED,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,MAAM,UAAU,CAAC,WAAW,CACtD,0BAA0B,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,CACxD,CAAC;gBAEF,IAAI,CAAC;oBACH,MAAM,UAAU,CAAC,WAAW,CAC1B,0BAA0B,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,CAC9D,CAAC;oBACF,0EAA0E;oBAC1E,sCAAsC;gBACxC,CAAC;gBAAC,MAAM,CAAC;oBACP,2DAA2D;gBAC7D,CAAC;YACH,CAAC,CACF,EACD,EAAE,OAAO,EAAE,CACZ,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,iDAAiD,EAAE,GAAG,EAAE;QAC/D,EAAE,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;YAC3D,MAAM,EAAE,CAAC,MAAM,CACb,EAAE,CAAC,aAAa,CACd,SAAS,EAAE,EACX,SAAS,EAAE,EACX,SAAS,EAAE,EACX,EAAE,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,EAC/C,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE;gBAClC,6BAA6B;gBAC7B,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC9C,OAAO;gBACT,CAAC;gBAED,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,MAAM,UAAU,CAAC,WAAW,CACtD,0BAA0B,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,CACtD,CAAC;gBAEF,IAAI,CAAC;oBACH,MAAM,UAAU,CAAC,WAAW,CAC1B,0BAA0B,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,UAAU,EAAE,GAAG,CAC5D,CAAC;gBACJ,CAAC;gBAAC,MAAM,CAAC;oBACP,2DAA2D;gBAC7D,CAAC;YACH,CAAC,CACF,EACD,EAAE,OAAO,EAAE,CACZ,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAClE,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;YAClE,MAAM,EAAE,CAAC,MAAM,CACb,EAAE,CAAC,aAAa,CACd,SAAS,EAAE,EACX,QAAQ,EAAE,EACV,EAAE,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,EAC/C,EAAE,CAAC,GAAG,EAAE,EACR,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE;gBACtC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,MAAM,UAAU,CAAC,WAAW,CACtD,0BAA0B,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,SAAS,CACvD,CAAC;gBAEF,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBAC5B,OAAO,CAAC,wBAAwB;gBAClC,CAAC;gBAED,+BAA+B;gBAC/B,MAAM,QAAQ,GAAG,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC;gBAC5C,MAAM,GAAG,GAAG,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC;gBACxC,QAAQ,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;gBAEtB,IAAI,CAAC;oBACH,MAAM,UAAU,CAAC,WAAW,CAC1B,0BAA0B,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,QAAQ,EAAE,GAAG,CAC3D,CAAC;oBACF,sEAAsE;oBACtE,sCAAsC;gBACxC,CAAC;gBAAC,MAAM,CAAC;oBACP,mEAAmE;gBACrE,CAAC;YACH,CAAC,CACF,EACD,EAAE,OAAO,EAAE,CACZ,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,gDAAgD,EAAE,GAAG,EAAE;QAC9D,EAAE,CAAC,gFAAgF,EAAE,KAAK,IAAI,EAAE;YAC9F,MAAM,EAAE,CAAC,MAAM,CACb,EAAE,CAAC,aAAa,CACd,SAAS,EAAE,EACX,QAAQ,EAAE,EACV,EAAE,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,EAC/C,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE;gBAC3B,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,WAAW,CAC1C,0BAA0B,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,SAAS,CACvD,CAAC;gBACF,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,WAAW,CAC1C,0BAA0B,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,SAAS,CACvD,CAAC;gBACF,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;gBACvD,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC3C,CAAC,CACF,EACD,EAAE,OAAO,EAAE,CACZ,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,sDAAsD,EAAE,GAAG,EAAE;QACpE,EAAE,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;YACxE,MAAM,EAAE,CAAC,MAAM,CACb,EAAE,CAAC,aAAa,CACd,SAAS,EAAE,EACX,EAAE,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,KAAK,EAAE,CAAC,EAC/E,EAAE,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,EAC9C,EAAE,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,EAC/C,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,EAAE;gBACpC,IAAI,CAAC;oBACH,MAAM,UAAU,CAAC,WAAW,CAC1B,0BAA0B,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,CAChE,CAAC;gBACJ,CAAC;gBAAC,MAAM,CAAC;oBACP,yCAAyC;gBAC3C,CAAC;YACH,CAAC,CACF,EACD,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,CACnC,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test';
|
|
2
|
+
import fc from 'fast-check';
|
|
3
|
+
import { equalFilter, filter, keyValues, oneOfFilter, rangeFilter } from './arbitraries/filter.arbitrary.js';
|
|
4
|
+
import { FilterSelector, FilterUtility } from '../../src/utils/filter.js';
|
|
5
|
+
const numRuns = Number(process.env.FAST_CHECK_NUM_RUNS) || 200;
|
|
6
|
+
describe('FilterUtility — fuzz', () => {
|
|
7
|
+
describe('matchFilter — empty filter matches everything', () => {
|
|
8
|
+
it('should return true for any keyValues when filter is empty', () => {
|
|
9
|
+
fc.assert(fc.property(keyValues(), (kv) => {
|
|
10
|
+
const result = FilterUtility.matchFilter(kv, {});
|
|
11
|
+
expect(result).toBe(true);
|
|
12
|
+
}), { numRuns });
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
describe('matchFilter — crash resistance', () => {
|
|
16
|
+
it('should not crash on arbitrary filter and keyValues combinations', () => {
|
|
17
|
+
fc.assert(fc.property(keyValues(), filter(), (kv, f) => {
|
|
18
|
+
// Should return a boolean without crashing
|
|
19
|
+
const result = FilterUtility.matchFilter(kv, f);
|
|
20
|
+
expect(typeof result).toBe('boolean');
|
|
21
|
+
}), { numRuns });
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
describe('matchAnyFilter — empty array matches everything', () => {
|
|
25
|
+
it('should return true when the filter array is empty', () => {
|
|
26
|
+
fc.assert(fc.property(keyValues(), (kv) => {
|
|
27
|
+
const result = FilterUtility.matchAnyFilter(kv, []);
|
|
28
|
+
expect(result).toBe(true);
|
|
29
|
+
}), { numRuns });
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
describe('matchFilter — equal filter consistency', () => {
|
|
33
|
+
it('should match when the indexed value equals the filter value', () => {
|
|
34
|
+
fc.assert(fc.property(fc.string({ minLength: 1, maxLength: 10 }), equalFilter(), (key, value) => {
|
|
35
|
+
const kv = { [key]: value };
|
|
36
|
+
const f = { [key]: value };
|
|
37
|
+
expect(FilterUtility.matchFilter(kv, f)).toBe(true);
|
|
38
|
+
}), { numRuns });
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
describe('matchFilter — oneOf filter contains value', () => {
|
|
42
|
+
it('should match when the indexed value is in the OneOfFilter array', () => {
|
|
43
|
+
fc.assert(fc.property(fc.string({ minLength: 1, maxLength: 10 }), oneOfFilter(), (key, values) => {
|
|
44
|
+
// Use the first value from the OneOf array as the indexed value
|
|
45
|
+
const indexValue = values[0];
|
|
46
|
+
const kv = { [key]: indexValue };
|
|
47
|
+
const f = { [key]: values };
|
|
48
|
+
expect(FilterUtility.matchFilter(kv, f)).toBe(true);
|
|
49
|
+
}), { numRuns });
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
describe('matchFilter — range filter within bounds', () => {
|
|
53
|
+
it('should match when a numeric value falls within the range', () => {
|
|
54
|
+
fc.assert(fc.property(fc.string({ minLength: 1, maxLength: 10 }), fc.integer({ min: -500, max: 500 }), (key, value) => {
|
|
55
|
+
// Create a range that definitely includes the value
|
|
56
|
+
const f = { [key]: { gte: value - 1, lt: value + 2 } };
|
|
57
|
+
const kv = { [key]: value };
|
|
58
|
+
expect(FilterUtility.matchFilter(kv, f)).toBe(true);
|
|
59
|
+
}), { numRuns });
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
describe('matchFilter — missing key returns false', () => {
|
|
63
|
+
it('should return false when the filter references a key not in keyValues', () => {
|
|
64
|
+
fc.assert(fc.property(fc.string({ minLength: 1, maxLength: 10 }), equalFilter(), (key, value) => {
|
|
65
|
+
// Empty keyValues, so any filter property should fail
|
|
66
|
+
const kv = {};
|
|
67
|
+
const f = { [key]: value };
|
|
68
|
+
expect(FilterUtility.matchFilter(kv, f)).toBe(false);
|
|
69
|
+
}), { numRuns });
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
describe('constructPrefixFilterAsRangeFilter — prefix match property', () => {
|
|
73
|
+
it('should match strings that start with the prefix', () => {
|
|
74
|
+
fc.assert(fc.property(fc.string({ minLength: 1, maxLength: 20 }), fc.string({ minLength: 0, maxLength: 20 }), (prefix, suffix) => {
|
|
75
|
+
const rangeFilterResult = FilterUtility.constructPrefixFilterAsRangeFilter(prefix);
|
|
76
|
+
const value = prefix + suffix;
|
|
77
|
+
// The value should be >= prefix and < prefix + \uffff
|
|
78
|
+
expect(value >= rangeFilterResult.gte).toBe(true);
|
|
79
|
+
expect(value < rangeFilterResult.lt).toBe(true);
|
|
80
|
+
}), { numRuns });
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
describe('isEqualFilter / isRangeFilter / isOneOfFilter — mutual exclusivity', () => {
|
|
84
|
+
it('should classify each filter value into exactly one category', () => {
|
|
85
|
+
fc.assert(fc.property(fc.oneof(equalFilter(), rangeFilter(), oneOfFilter()), (filterValue) => {
|
|
86
|
+
const isEqual = FilterUtility.isEqualFilter(filterValue);
|
|
87
|
+
const isRange = FilterUtility.isRangeFilter(filterValue);
|
|
88
|
+
const isOneOf = FilterUtility.isOneOfFilter(filterValue);
|
|
89
|
+
// Exactly one should be true
|
|
90
|
+
const trueCount = [isEqual, isRange, isOneOf].filter(Boolean).length;
|
|
91
|
+
expect(trueCount).toBe(1);
|
|
92
|
+
}), { numRuns });
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
describe('convertRangeCriterion — consistency', () => {
|
|
96
|
+
it('should produce a valid RangeFilter or undefined for any input', () => {
|
|
97
|
+
fc.assert(fc.property(fc.record({
|
|
98
|
+
from: fc.option(fc.string({ minLength: 1, maxLength: 30 }), { nil: undefined }),
|
|
99
|
+
to: fc.option(fc.string({ minLength: 1, maxLength: 30 }), { nil: undefined }),
|
|
100
|
+
}), (criterion) => {
|
|
101
|
+
const result = FilterUtility.convertRangeCriterion(criterion);
|
|
102
|
+
if (criterion.from === undefined && criterion.to === undefined) {
|
|
103
|
+
expect(result).toBeUndefined();
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
expect(result).toBeDefined();
|
|
107
|
+
if (criterion.from !== undefined) {
|
|
108
|
+
expect(result.gte).toBe(criterion.from);
|
|
109
|
+
}
|
|
110
|
+
if (criterion.to !== undefined) {
|
|
111
|
+
expect(result.lt).toBe(criterion.to);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}), { numRuns });
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
describe('FilterSelector — fuzz', () => {
|
|
119
|
+
describe('reduceFilter — returns a subset', () => {
|
|
120
|
+
it('should return a filter with at most 1 property', () => {
|
|
121
|
+
fc.assert(fc.property(filter(), (f) => {
|
|
122
|
+
const reduced = FilterSelector.reduceFilter(f);
|
|
123
|
+
const reducedKeys = Object.keys(reduced);
|
|
124
|
+
// If input had 0 or 1 keys, reduced should be the same
|
|
125
|
+
const inputKeys = Object.keys(f);
|
|
126
|
+
if (inputKeys.length <= 1) {
|
|
127
|
+
expect(reduced).toEqual(f);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
// Otherwise, reduced should have exactly 1 key
|
|
131
|
+
expect(reducedKeys.length).toBe(1);
|
|
132
|
+
}
|
|
133
|
+
}), { numRuns });
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
describe('reduceFilter — reduced key is from original filter', () => {
|
|
137
|
+
it('should select a key that exists in the original filter', () => {
|
|
138
|
+
fc.assert(fc.property(filter(), (f) => {
|
|
139
|
+
const reduced = FilterSelector.reduceFilter(f);
|
|
140
|
+
const reducedKeys = Object.keys(reduced);
|
|
141
|
+
const originalKeys = Object.keys(f);
|
|
142
|
+
for (const key of reducedKeys) {
|
|
143
|
+
expect(originalKeys).toContain(key);
|
|
144
|
+
}
|
|
145
|
+
}), { numRuns });
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
//# sourceMappingURL=filter.fuzz.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter.fuzz.spec.js","sourceRoot":"","sources":["../../../../tests/fuzz/filter.fuzz.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test';
|
|
2
|
+
import fc from 'fast-check';
|
|
3
|
+
import { getRuleSetAtPath } from '../../src/utils/protocols.js';
|
|
4
|
+
const numRuns = Number(process.env.FAST_CHECK_NUM_RUNS) || 100;
|
|
5
|
+
describe('getRuleSetAtPath — fuzz', () => {
|
|
6
|
+
/**
|
|
7
|
+
* Arbitrary for a valid type name segment (no slashes, non-empty, no $ prefix).
|
|
8
|
+
* Deliberately includes Object.prototype names like "toString" and "valueOf"
|
|
9
|
+
* to verify that the function is immune to prototype pollution.
|
|
10
|
+
*/
|
|
11
|
+
const arbTypeName = fc.string({ minLength: 1, maxLength: 10 })
|
|
12
|
+
.filter((s) => !s.includes('/') && !s.startsWith('$'));
|
|
13
|
+
describe('single-level path resolution', () => {
|
|
14
|
+
it('should return the rule set for a root-level type', () => {
|
|
15
|
+
fc.assert(fc.property(arbTypeName, (typeName) => {
|
|
16
|
+
const ruleSet = { $size: { min: 0, max: 100 } };
|
|
17
|
+
const structure = { [typeName]: ruleSet };
|
|
18
|
+
const result = getRuleSetAtPath(typeName, structure);
|
|
19
|
+
expect(result).toBe(ruleSet);
|
|
20
|
+
}), { numRuns });
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
describe('nested path resolution', () => {
|
|
24
|
+
it('should walk nested structure segments correctly', () => {
|
|
25
|
+
fc.assert(fc.property(fc.array(arbTypeName, { minLength: 2, maxLength: 4 })
|
|
26
|
+
.filter((segs) => new Set(segs).size === segs.length), // unique segments
|
|
27
|
+
(segments) => {
|
|
28
|
+
// Build a nested structure: segments[0] > segments[1] > ... > leaf
|
|
29
|
+
const leafRuleSet = { $role: true };
|
|
30
|
+
let current = { [segments[segments.length - 1]]: leafRuleSet };
|
|
31
|
+
for (let i = segments.length - 2; i >= 0; i--) {
|
|
32
|
+
current = { [segments[i]]: current };
|
|
33
|
+
}
|
|
34
|
+
const protocolPath = segments.join('/');
|
|
35
|
+
const result = getRuleSetAtPath(protocolPath, current);
|
|
36
|
+
expect(result).toBe(leafRuleSet);
|
|
37
|
+
}), { numRuns });
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
describe('missing path returns undefined', () => {
|
|
41
|
+
it('should return undefined for a path that does not exist', () => {
|
|
42
|
+
fc.assert(fc.property(arbTypeName, arbTypeName.filter((s) => s !== 'existing'), (missingName, _otherName) => {
|
|
43
|
+
const structure = { existing: { $size: { min: 0 } } };
|
|
44
|
+
// Only if missingName !== 'existing' will this be undefined
|
|
45
|
+
if (missingName === 'existing') {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const result = getRuleSetAtPath(missingName, structure);
|
|
49
|
+
expect(result).toBeUndefined();
|
|
50
|
+
}), { numRuns });
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
describe('deep missing path returns undefined', () => {
|
|
54
|
+
it('should return undefined when an intermediate segment is missing', () => {
|
|
55
|
+
fc.assert(fc.property(arbTypeName, arbTypeName, arbTypeName, (seg1, seg2, missing) => {
|
|
56
|
+
// Only test when 'missing' differs from seg2 to guarantee the path doesn't exist
|
|
57
|
+
if (missing === seg2) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const structure = {
|
|
61
|
+
[seg1]: {
|
|
62
|
+
[seg2]: { $role: true },
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
const result = getRuleSetAtPath(`${seg1}/${missing}`, structure);
|
|
66
|
+
expect(result).toBeUndefined();
|
|
67
|
+
}), { numRuns });
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe('prototype pollution immunity', () => {
|
|
71
|
+
it('should return undefined for Object.prototype property names not in the structure', () => {
|
|
72
|
+
const prototypeNames = Object.getOwnPropertyNames(Object.prototype)
|
|
73
|
+
.filter((name) => /^[a-zA-Z]+$/.test(name)); // only names valid as protocolPath segments
|
|
74
|
+
const structure = { message: { $size: { min: 0 } } };
|
|
75
|
+
for (const name of prototypeNames) {
|
|
76
|
+
const result = getRuleSetAtPath(name, structure);
|
|
77
|
+
expect(result).toBeUndefined();
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
//# sourceMappingURL=get-rule-set-at-path.fuzz.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-rule-set-at-path.fuzz.spec.js","sourceRoot":"","sources":["../../../../tests/fuzz/get-rule-set-at-path.fuzz.spec.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,UAAU,CAAC;AAEhD,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAEhE,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,IAAI,GAAG,CAAC;AAE/D,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IAEvC;;;;OAIG;IACH,MAAM,WAAW,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;SAC3D,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;IAEzD,QAAQ,CAAC,8BAA8B,EAAE,GAAG,EAAE;QAC5C,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;YAC1D,EAAE,CAAC,MAAM,CACP,EAAE,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,EAAE;gBACpC,MAAM,OAAO,GAAoB,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC;gBACjE,MAAM,SAAS,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;gBAC1C,MAAM,MAAM,GAAG,gBAAgB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;gBACrD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC/B,CAAC,CAAC,EACF,EAAE,OAAO,EAAE,CACZ,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;QACtC,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;YACzD,EAAE,CAAC,MAAM,CACP,EAAE,CAAC,QAAQ,CACT,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;iBAClD,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,MAAM,CAAC,EAAE,kBAAkB;YAC3E,CAAC,QAAQ,EAAE,EAAE;gBACX,mEAAmE;gBACnE,MAAM,WAAW,GAAoB,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;gBAErD,IAAI,OAAO,GAAuC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;gBACnG,KAAK,IAAI,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;oBAC9C,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,OAAqC,EAAE,CAAC;gBACrE,CAAC;gBAED,MAAM,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACxC,MAAM,MAAM,GAAG,gBAAgB,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;gBACvD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACnC,CAAC,CACF,EACD,EAAE,OAAO,EAAE,CACZ,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,gCAAgC,EAAE,GAAG,EAAE;QAC9C,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;YAChE,EAAE,CAAC,MAAM,CACP,EAAE,CAAC,QAAQ,CACT,WAAW,EACX,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,UAAU,CAAC,EAC3C,CAAC,WAAW,EAAE,UAAU,EAAE,EAAE;gBAC1B,MAAM,SAAS,GAAG,EAAE,QAAQ,EAAE,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,EAAqB,EAAE,CAAC;gBACzE,4DAA4D;gBAC5D,IAAI,WAAW,KAAK,UAAU,EAAE,CAAC;oBAC/B,OAAO;gBACT,CAAC;gBACD,MAAM,MAAM,GAAG,gBAAgB,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;gBACxD,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,EAAE,CAAC;YACjC,CAAC,CACF,EACD,EAAE,OAAO,EAAE,CACZ,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,qCAAqC,EAAE,GAAG,EAAE;QACnD,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;YACzE,EAAE,CAAC,MAAM,CACP,EAAE,CAAC,QAAQ,CACT,WAAW,EACX,WAAW,EACX,WAAW,EACX,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE;gBACtB,iFAAiF;gBACjF,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;oBACrB,OAAO;gBACT,CAAC;gBACD,MAAM,SAAS,GAAG;oBAChB,CAAC,IAAI,CAAC,EAAE;wBACN,CAAC,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAqB;qBACb;iBAChC,CAAC;gBACF,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,IAAI,IAAI,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;gBACjE,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,EAAE,CAAC;YACjC,CAAC,CACF,EACD,EAAE,OAAO,EAAE,CACZ,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,8BAA8B,EAAE,GAAG,EAAE;QAC5C,EAAE,CAAC,kFAAkF,EAAE,GAAG,EAAE;YAC1F,MAAM,cAAc,GAAG,MAAM,CAAC,mBAAmB,CAAC,MAAM,CAAC,SAAS,CAAC;iBAChE,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,4CAA4C;YAE3F,MAAM,SAAS,GAAG,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,EAAqB,EAAE,CAAC;YAExE,KAAK,MAAM,IAAI,IAAI,cAAc,EAAE,CAAC;gBAClC,MAAM,MAAM,GAAG,gBAAgB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;gBACjD,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,EAAE,CAAC;YACjC,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test';
|
|
2
|
+
import fc from 'fast-check';
|
|
3
|
+
import { DwnErrorCode } from '../../src/core/dwn-error.js';
|
|
4
|
+
import { Encoder } from '../../src/utils/encoder.js';
|
|
5
|
+
import { HdKey } from '../../src/utils/hd-key.js';
|
|
6
|
+
const numRuns = Number(process.env.FAST_CHECK_NUM_RUNS) || 100;
|
|
7
|
+
describe('HdKey — fuzz', () => {
|
|
8
|
+
/**
|
|
9
|
+
* Generate a valid 32-byte key for use as base key material.
|
|
10
|
+
*/
|
|
11
|
+
const arbPrivateKey = fc.uint8Array({ minLength: 32, maxLength: 32 });
|
|
12
|
+
/**
|
|
13
|
+
* A non-empty derivation path segment (no empty strings allowed).
|
|
14
|
+
*/
|
|
15
|
+
const arbSegment = fc.string({ minLength: 1, maxLength: 20 });
|
|
16
|
+
/**
|
|
17
|
+
* A valid derivation path (1–5 non-empty segments).
|
|
18
|
+
*/
|
|
19
|
+
const arbPath = fc.array(arbSegment, { minLength: 1, maxLength: 5 });
|
|
20
|
+
describe('derivePrivateKeyBytes — determinism', () => {
|
|
21
|
+
it('should produce identical output for identical inputs', async () => {
|
|
22
|
+
await fc.assert(fc.asyncProperty(arbPrivateKey, arbPath, async (key, path) => {
|
|
23
|
+
const result1 = await HdKey.derivePrivateKeyBytes(key, path);
|
|
24
|
+
const result2 = await HdKey.derivePrivateKeyBytes(key, path);
|
|
25
|
+
expect(Encoder.bytesToBase64Url(result1)).toBe(Encoder.bytesToBase64Url(result2));
|
|
26
|
+
}), { numRuns });
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
describe('derivePrivateKeyBytes — output size', () => {
|
|
30
|
+
it('should always produce a 32-byte derived key', async () => {
|
|
31
|
+
await fc.assert(fc.asyncProperty(arbPrivateKey, arbPath, async (key, path) => {
|
|
32
|
+
const result = await HdKey.derivePrivateKeyBytes(key, path);
|
|
33
|
+
expect(result.byteLength).toBe(32);
|
|
34
|
+
}), { numRuns });
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
describe('derivePrivateKeyBytes — path order sensitivity', () => {
|
|
38
|
+
it('should produce different keys for reversed non-palindrome paths', async () => {
|
|
39
|
+
await fc.assert(fc.asyncProperty(arbPrivateKey, fc.array(arbSegment, { minLength: 2, maxLength: 5 }).filter((segs) => {
|
|
40
|
+
// ensure path is NOT a palindrome so reversing changes it
|
|
41
|
+
const reversed = [...segs].reverse();
|
|
42
|
+
return segs.some((s, i) => s !== reversed[i]);
|
|
43
|
+
}), async (key, path) => {
|
|
44
|
+
const forward = await HdKey.derivePrivateKeyBytes(key, path);
|
|
45
|
+
const reversed = await HdKey.derivePrivateKeyBytes(key, [...path].reverse());
|
|
46
|
+
// different path order should produce different key (with overwhelming probability)
|
|
47
|
+
const forwardB64 = Encoder.bytesToBase64Url(forward);
|
|
48
|
+
const reversedB64 = Encoder.bytesToBase64Url(reversed);
|
|
49
|
+
expect(forwardB64).not.toBe(reversedB64);
|
|
50
|
+
}), { numRuns });
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
describe('derivePrivateKeyBytes — incremental derivation equivalence', () => {
|
|
54
|
+
it('should produce the same key whether derived all at once or incrementally', async () => {
|
|
55
|
+
await fc.assert(fc.asyncProperty(arbPrivateKey, fc.array(arbSegment, { minLength: 2, maxLength: 5 }), fc.integer({ min: 1 }).chain((splitCandidate) => fc.constant(splitCandidate)), async (key, path, splitCandidate) => {
|
|
56
|
+
// Split the path at an arbitrary point
|
|
57
|
+
const splitIndex = (splitCandidate % (path.length - 1)) + 1;
|
|
58
|
+
const prefix = path.slice(0, splitIndex);
|
|
59
|
+
const suffix = path.slice(splitIndex);
|
|
60
|
+
// Derive all at once
|
|
61
|
+
const allAtOnce = await HdKey.derivePrivateKeyBytes(key, path);
|
|
62
|
+
// Derive incrementally: first prefix, then suffix from intermediate key
|
|
63
|
+
const intermediate = await HdKey.derivePrivateKeyBytes(key, prefix);
|
|
64
|
+
const incremental = await HdKey.derivePrivateKeyBytes(intermediate, suffix);
|
|
65
|
+
expect(Encoder.bytesToBase64Url(allAtOnce)).toBe(Encoder.bytesToBase64Url(incremental));
|
|
66
|
+
}), { numRuns });
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
describe('derivePrivateKeyBytes — empty segment rejection', () => {
|
|
70
|
+
it('should throw HdKeyDerivationPathInvalid when any segment is empty', async () => {
|
|
71
|
+
await fc.assert(fc.asyncProperty(arbPrivateKey, fc.array(fc.string({ maxLength: 10 }), { minLength: 1, maxLength: 5 }).filter((segs) => segs.some((s) => s === '')), async (key, pathWithEmpty) => {
|
|
72
|
+
await expect(HdKey.derivePrivateKeyBytes(key, pathWithEmpty)).rejects.toThrow(DwnErrorCode.HdKeyDerivationPathInvalid);
|
|
73
|
+
}), { numRuns });
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
//# sourceMappingURL=hd-key.fuzz.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hd-key.fuzz.spec.js","sourceRoot":"","sources":["../../../../tests/fuzz/hd-key.fuzz.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test';
|
|
2
|
+
import fc from 'fast-check';
|
|
3
|
+
import { DwnErrorCode } from '../../src/core/dwn-error.js';
|
|
4
|
+
import { verifyEqualityOfImmutableProperties } from '../../src/interfaces/records-write-query.js';
|
|
5
|
+
const numRuns = Number(process.env.FAST_CHECK_NUM_RUNS) || 100;
|
|
6
|
+
/** The properties that verifyEqualityOfImmutableProperties treats as mutable. */
|
|
7
|
+
const mutableProps = ['dataCid', 'dataSize', 'dataFormat', 'datePublished', 'published', 'messageTimestamp', 'tags'];
|
|
8
|
+
/** Generates a minimal RecordsWrite descriptor with all immutable and mutable fields. */
|
|
9
|
+
function recordsWriteDescriptor() {
|
|
10
|
+
return fc.record({
|
|
11
|
+
// Immutable fields
|
|
12
|
+
interface: fc.constant('Records'),
|
|
13
|
+
method: fc.constant('Write'),
|
|
14
|
+
protocol: fc.string({ minLength: 5, maxLength: 30 }),
|
|
15
|
+
protocolPath: fc.string({ minLength: 3, maxLength: 20 }),
|
|
16
|
+
schema: fc.string({ minLength: 5, maxLength: 30 }),
|
|
17
|
+
dateCreated: fc.constant('2024-01-01T00:00:00.000000Z'),
|
|
18
|
+
// Mutable fields
|
|
19
|
+
dataCid: fc.string({ minLength: 10, maxLength: 40 }),
|
|
20
|
+
dataSize: fc.integer({ min: 0, max: 100000 }),
|
|
21
|
+
dataFormat: fc.string({ minLength: 3, maxLength: 20 }),
|
|
22
|
+
messageTimestamp: fc.constant('2024-06-01T00:00:00.000000Z'),
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/** Wraps a descriptor in a minimal RecordsWriteMessage shape. */
|
|
26
|
+
function wrapMessage(descriptor) {
|
|
27
|
+
return { descriptor };
|
|
28
|
+
}
|
|
29
|
+
describe('verifyEqualityOfImmutableProperties — fuzz', () => {
|
|
30
|
+
describe('reflexivity', () => {
|
|
31
|
+
it('should never throw when comparing a message with itself', () => {
|
|
32
|
+
fc.assert(fc.property(recordsWriteDescriptor(), (descriptor) => {
|
|
33
|
+
const msg = wrapMessage(descriptor);
|
|
34
|
+
expect(verifyEqualityOfImmutableProperties(msg, msg)).toBe(true);
|
|
35
|
+
}), { numRuns });
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
describe('symmetry', () => {
|
|
39
|
+
it('should succeed in both directions when only mutable fields differ', () => {
|
|
40
|
+
fc.assert(fc.property(recordsWriteDescriptor(), recordsWriteDescriptor(), (desc1, desc2) => {
|
|
41
|
+
// Copy immutable fields from desc1 to desc2 so only mutable fields differ
|
|
42
|
+
for (const key of Object.keys(desc1)) {
|
|
43
|
+
if (!mutableProps.includes(key)) {
|
|
44
|
+
desc2[key] = desc1[key];
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const msg1 = wrapMessage(desc1);
|
|
48
|
+
const msg2 = wrapMessage(desc2);
|
|
49
|
+
expect(verifyEqualityOfImmutableProperties(msg1, msg2)).toBe(true);
|
|
50
|
+
expect(verifyEqualityOfImmutableProperties(msg2, msg1)).toBe(true);
|
|
51
|
+
}), { numRuns });
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe('mutable properties are free', () => {
|
|
55
|
+
it('should not throw when only mutable properties are changed', () => {
|
|
56
|
+
fc.assert(fc.property(recordsWriteDescriptor(), fc.string({ minLength: 5, maxLength: 40 }), fc.integer({ min: 0, max: 100000 }), fc.string({ minLength: 3, maxLength: 20 }), (descriptor, newDataCid, newDataSize, newDataFormat) => {
|
|
57
|
+
const modified = {
|
|
58
|
+
...descriptor,
|
|
59
|
+
dataCid: newDataCid,
|
|
60
|
+
dataSize: newDataSize,
|
|
61
|
+
dataFormat: newDataFormat,
|
|
62
|
+
messageTimestamp: '2025-01-01T00:00:00.000000Z',
|
|
63
|
+
};
|
|
64
|
+
const msg1 = wrapMessage(descriptor);
|
|
65
|
+
const msg2 = wrapMessage(modified);
|
|
66
|
+
expect(verifyEqualityOfImmutableProperties(msg1, msg2)).toBe(true);
|
|
67
|
+
}), { numRuns });
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe('immutable properties are strict', () => {
|
|
71
|
+
it('should throw when any immutable property is changed', () => {
|
|
72
|
+
const immutableProps = ['interface', 'method', 'protocol', 'protocolPath', 'schema', 'dateCreated'];
|
|
73
|
+
fc.assert(fc.property(recordsWriteDescriptor(), fc.constantFrom(...immutableProps), fc.string({ minLength: 1, maxLength: 10 }), (descriptor, propToChange, newValue) => {
|
|
74
|
+
// Ensure the new value is actually different
|
|
75
|
+
if (descriptor[propToChange] === newValue) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const modified = { ...descriptor, [propToChange]: newValue };
|
|
79
|
+
const msg1 = wrapMessage(descriptor);
|
|
80
|
+
const msg2 = wrapMessage(modified);
|
|
81
|
+
expect(() => verifyEqualityOfImmutableProperties(msg1, msg2)).toThrow(DwnErrorCode.RecordsWriteImmutablePropertyChanged);
|
|
82
|
+
}), { numRuns });
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
//# sourceMappingURL=immutable-properties.fuzz.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"immutable-properties.fuzz.spec.js","sourceRoot":"","sources":["../../../../tests/fuzz/immutable-properties.fuzz.spec.ts"],"names":[],"mappings":"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"}
|