@marcuspuchalla/nachos 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +64 -0
- package/LICENSE +674 -0
- package/README.md +345 -0
- package/dist/chunk-2FUTHZQQ.cjs +755 -0
- package/dist/chunk-2FUTHZQQ.cjs.map +1 -0
- package/dist/chunk-2HBCILJS.cjs +2034 -0
- package/dist/chunk-2HBCILJS.cjs.map +1 -0
- package/dist/chunk-7CFYWHS6.js +742 -0
- package/dist/chunk-7CFYWHS6.js.map +1 -0
- package/dist/chunk-PD72MVTX.cjs +160 -0
- package/dist/chunk-PD72MVTX.cjs.map +1 -0
- package/dist/chunk-ZDZ2B5PE.js +149 -0
- package/dist/chunk-ZDZ2B5PE.js.map +1 -0
- package/dist/chunk-ZRPJUEIZ.js +2020 -0
- package/dist/chunk-ZRPJUEIZ.js.map +1 -0
- package/dist/encoder/index.cjs +57 -0
- package/dist/encoder/index.cjs.map +1 -0
- package/dist/encoder/index.d.cts +72 -0
- package/dist/encoder/index.d.ts +72 -0
- package/dist/encoder/index.js +4 -0
- package/dist/encoder/index.js.map +1 -0
- package/dist/index.cjs +606 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +494 -0
- package/dist/index.d.ts +494 -0
- package/dist/index.js +523 -0
- package/dist/index.js.map +1 -0
- package/dist/metafile-cjs.json +1 -0
- package/dist/metafile-esm.json +1 -0
- package/dist/parser/index.cjs +85 -0
- package/dist/parser/index.cjs.map +1 -0
- package/dist/parser/index.d.cts +72 -0
- package/dist/parser/index.d.ts +72 -0
- package/dist/parser/index.js +4 -0
- package/dist/parser/index.js.map +1 -0
- package/dist/types-DvNlfbKB.d.cts +301 -0
- package/dist/types-DvNlfbKB.d.ts +301 -0
- package/dist/useCborSimpleEncoder-ButVU988.d.cts +268 -0
- package/dist/useCborSimpleEncoder-TVxzNJ_9.d.ts +268 -0
- package/dist/useCborTag-B_iaShG6.d.ts +142 -0
- package/dist/useCborTag-BfTIV8HM.d.cts +142 -0
- package/package.json +102 -0
- package/src/__tests__/public-api.test.ts +326 -0
- package/src/encoder/__tests__/cbor-collection-encoder.test.ts +331 -0
- package/src/encoder/__tests__/cbor-integer-encoder.test.ts +283 -0
- package/src/encoder/__tests__/cbor-simple-encoder.test.ts +224 -0
- package/src/encoder/__tests__/cbor-string-encoder.test.ts +345 -0
- package/src/encoder/__tests__/cbor-tag-encoder.test.ts +565 -0
- package/src/encoder/composables/#useCborTagEncoder.ts# +158 -0
- package/src/encoder/composables/useCborCollectionEncoder.ts +424 -0
- package/src/encoder/composables/useCborEncoder.ts +203 -0
- package/src/encoder/composables/useCborIntegerEncoder.ts +188 -0
- package/src/encoder/composables/useCborSimpleEncoder.ts +266 -0
- package/src/encoder/composables/useCborStringEncoder.ts +266 -0
- package/src/encoder/composables/useCborTagEncoder.ts +158 -0
- package/src/encoder/index.ts +35 -0
- package/src/encoder/types.ts +88 -0
- package/src/encoder/utils.ts +80 -0
- package/src/index.ts +434 -0
- package/src/parser/__tests__/ast-tree-structure.test.ts +311 -0
- package/src/parser/__tests__/cbor-collection-errors.test.ts +296 -0
- package/src/parser/__tests__/cbor-collection.test.ts +369 -0
- package/src/parser/__tests__/cbor-deterministic-encoding.test.ts +432 -0
- package/src/parser/__tests__/cbor-diagnostic.test.ts +333 -0
- package/src/parser/__tests__/cbor-duplicate-keys.test.ts +235 -0
- package/src/parser/__tests__/cbor-float-errors.test.ts +222 -0
- package/src/parser/__tests__/cbor-float.test.ts +502 -0
- package/src/parser/__tests__/cbor-integer-errors.test.ts +139 -0
- package/src/parser/__tests__/cbor-integer.test.ts +200 -0
- package/src/parser/__tests__/cbor-map-duplicate-keys.test.ts +403 -0
- package/src/parser/__tests__/cbor-parser-errors.test.ts +126 -0
- package/src/parser/__tests__/cbor-security-dos-protection.test.ts +503 -0
- package/src/parser/__tests__/cbor-sequences.test.ts +150 -0
- package/src/parser/__tests__/cbor-source-map.test.ts +321 -0
- package/src/parser/__tests__/cbor-standard-tags.test.ts +340 -0
- package/src/parser/__tests__/cbor-string-errors.test.ts +227 -0
- package/src/parser/__tests__/cbor-string.test.ts +224 -0
- package/src/parser/__tests__/cbor-tag-advanced.test.ts +500 -0
- package/src/parser/__tests__/cbor-tag-errors.test.ts +447 -0
- package/src/parser/__tests__/cbor-tag-source-map.test.ts +360 -0
- package/src/parser/__tests__/cbor-tag.test.ts +684 -0
- package/src/parser/__tests__/extreme-edge-cases.test.ts +146 -0
- package/src/parser/__tests__/pathBuilder.test.ts +256 -0
- package/src/parser/__tests__/rfc-test-vectors.test.ts +607 -0
- package/src/parser/__tests__/security-limits.test.ts +248 -0
- package/src/parser/__tests__/utils-errors.test.ts +127 -0
- package/src/parser/composables/useCborCollection.ts +509 -0
- package/src/parser/composables/useCborDiagnostic.ts +381 -0
- package/src/parser/composables/useCborFloat.ts +256 -0
- package/src/parser/composables/useCborInteger.ts +114 -0
- package/src/parser/composables/useCborParser.ts +951 -0
- package/src/parser/composables/useCborString.ts +330 -0
- package/src/parser/composables/useCborStringTypes.ts +129 -0
- package/src/parser/composables/useCborTag.ts +739 -0
- package/src/parser/index.ts +56 -0
- package/src/parser/types.ts +371 -0
- package/src/parser/utils/pathBuilder.ts +259 -0
- package/src/parser/utils.ts +398 -0
- package/src/utils/__tests__/logger.test.ts +186 -0
- package/src/utils/logger.ts +96 -0
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { useCborDiagnostic } from '../composables/useCborDiagnostic'
|
|
3
|
+
|
|
4
|
+
describe('useCborDiagnostic', () => {
|
|
5
|
+
const { toDiagnostic, fromDiagnostic } = useCborDiagnostic()
|
|
6
|
+
|
|
7
|
+
describe('primitive values', () => {
|
|
8
|
+
it('should format integers', () => {
|
|
9
|
+
expect(toDiagnostic(0)).toBe('0')
|
|
10
|
+
expect(toDiagnostic(1)).toBe('1')
|
|
11
|
+
expect(toDiagnostic(23)).toBe('23')
|
|
12
|
+
expect(toDiagnostic(100)).toBe('100')
|
|
13
|
+
expect(toDiagnostic(1000000)).toBe('1000000')
|
|
14
|
+
expect(toDiagnostic(-1)).toBe('-1')
|
|
15
|
+
expect(toDiagnostic(-100)).toBe('-100')
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('should format BigInt', () => {
|
|
19
|
+
expect(toDiagnostic(BigInt(100))).toBe('100')
|
|
20
|
+
expect(toDiagnostic(BigInt('18446744073709551615'))).toBe('18446744073709551615')
|
|
21
|
+
expect(toDiagnostic(BigInt(-100))).toBe('-100')
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('should format floats', () => {
|
|
25
|
+
expect(toDiagnostic(1.5)).toBe('1.5')
|
|
26
|
+
expect(toDiagnostic(100000.0)).toBe('100000')
|
|
27
|
+
expect(toDiagnostic(3.14159)).toBe('3.14159')
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('should format special float values', () => {
|
|
31
|
+
expect(toDiagnostic(NaN)).toBe('NaN')
|
|
32
|
+
expect(toDiagnostic(Infinity)).toBe('Infinity')
|
|
33
|
+
expect(toDiagnostic(-Infinity)).toBe('-Infinity')
|
|
34
|
+
expect(toDiagnostic(-0)).toBe('-0.0')
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('should format booleans', () => {
|
|
38
|
+
expect(toDiagnostic(true)).toBe('true')
|
|
39
|
+
expect(toDiagnostic(false)).toBe('false')
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('should format null and undefined', () => {
|
|
43
|
+
expect(toDiagnostic(null)).toBe('null')
|
|
44
|
+
expect(toDiagnostic(undefined)).toBe('undefined')
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
describe('strings', () => {
|
|
49
|
+
it('should format simple strings', () => {
|
|
50
|
+
expect(toDiagnostic('')).toBe('""')
|
|
51
|
+
expect(toDiagnostic('a')).toBe('"a"')
|
|
52
|
+
expect(toDiagnostic('IETF')).toBe('"IETF"')
|
|
53
|
+
expect(toDiagnostic('hello world')).toBe('"hello world"')
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('should escape special characters', () => {
|
|
57
|
+
expect(toDiagnostic('line1\nline2')).toBe('"line1\\nline2"')
|
|
58
|
+
expect(toDiagnostic('tab\there')).toBe('"tab\\there"')
|
|
59
|
+
expect(toDiagnostic('quote"here')).toBe('"quote\\"here"')
|
|
60
|
+
expect(toDiagnostic('back\\slash')).toBe('"back\\\\slash"')
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('should escape control characters', () => {
|
|
64
|
+
expect(toDiagnostic('\x00')).toBe('"\\u0000"')
|
|
65
|
+
expect(toDiagnostic('\x1f')).toBe('"\\u001f"')
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
describe('byte strings', () => {
|
|
70
|
+
it('should format empty byte string', () => {
|
|
71
|
+
expect(toDiagnostic(new Uint8Array([]))).toBe("h''")
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it('should format byte strings', () => {
|
|
75
|
+
expect(toDiagnostic(new Uint8Array([1, 2, 3, 4]))).toBe("h'01020304'")
|
|
76
|
+
expect(toDiagnostic(new Uint8Array([0xde, 0xad, 0xbe, 0xef]))).toBe("h'deadbeef'")
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it('should format single byte', () => {
|
|
80
|
+
expect(toDiagnostic(new Uint8Array([0xff]))).toBe("h'ff'")
|
|
81
|
+
expect(toDiagnostic(new Uint8Array([0x00]))).toBe("h'00'")
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
describe('arrays', () => {
|
|
86
|
+
it('should format empty array', () => {
|
|
87
|
+
expect(toDiagnostic([])).toBe('[]')
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('should format simple arrays', () => {
|
|
91
|
+
expect(toDiagnostic([1, 2, 3])).toBe('[1, 2, 3]')
|
|
92
|
+
expect(toDiagnostic(['a', 'b'])).toBe('["a", "b"]')
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('should format nested arrays', () => {
|
|
96
|
+
expect(toDiagnostic([1, [2, 3]])).toBe('[1, [2, 3]]')
|
|
97
|
+
expect(toDiagnostic([[1, 2], [3, 4]])).toBe('[[1, 2], [3, 4]]')
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('should format indefinite arrays', () => {
|
|
101
|
+
expect(toDiagnostic([1, 2, 3], { indefinite: true })).toBe('[_ 1, 2, 3]')
|
|
102
|
+
expect(toDiagnostic([], { indefinite: true })).toBe('[_ ]')
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('should format mixed type arrays', () => {
|
|
106
|
+
expect(toDiagnostic([1, 'hello', true, null])).toBe('[1, "hello", true, null]')
|
|
107
|
+
})
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
describe('maps and objects', () => {
|
|
111
|
+
it('should format empty objects', () => {
|
|
112
|
+
expect(toDiagnostic({})).toBe('{}')
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('should format simple objects', () => {
|
|
116
|
+
expect(toDiagnostic({ a: 1 })).toBe('{"a": 1}')
|
|
117
|
+
expect(toDiagnostic({ a: 1, b: 2 })).toBe('{"a": 1, "b": 2}')
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('should format nested objects', () => {
|
|
121
|
+
expect(toDiagnostic({ a: { b: 1 } })).toBe('{"a": {"b": 1}}')
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('should format indefinite maps', () => {
|
|
125
|
+
expect(toDiagnostic({ a: 1 }, { indefinite: true })).toBe('{_ "a": 1}')
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it('should format Maps with non-string keys', () => {
|
|
129
|
+
const map = new Map<any, any>([[1, 'one'], [2, 'two']])
|
|
130
|
+
expect(toDiagnostic(map)).toBe('{1: "one", 2: "two"}')
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it('should format empty Maps', () => {
|
|
134
|
+
expect(toDiagnostic(new Map())).toBe('{}')
|
|
135
|
+
})
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
describe('tagged values', () => {
|
|
139
|
+
it('should format simple tags', () => {
|
|
140
|
+
expect(toDiagnostic({ tag: 0, value: '2024-01-01T00:00:00Z' }))
|
|
141
|
+
.toBe('0("2024-01-01T00:00:00Z")')
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
it('should format epoch timestamp tag', () => {
|
|
145
|
+
expect(toDiagnostic({ tag: 1, value: 1363896240 }))
|
|
146
|
+
.toBe('1(1363896240)')
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
it('should format bignum tags', () => {
|
|
150
|
+
expect(toDiagnostic({ tag: 2, value: new Uint8Array([1, 0, 0, 0, 0, 0, 0, 0, 0]) }))
|
|
151
|
+
.toBe("2(h'010000000000000000')")
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
it('should format self-describe CBOR tag', () => {
|
|
155
|
+
expect(toDiagnostic({ tag: 55799, value: [1, 2] }))
|
|
156
|
+
.toBe('55799([1, 2])')
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it('should format Plutus constructor tags', () => {
|
|
160
|
+
expect(toDiagnostic({ tag: 121, value: [1, 2] }))
|
|
161
|
+
.toBe('121([1, 2])')
|
|
162
|
+
expect(toDiagnostic({ tag: 122, value: [] }))
|
|
163
|
+
.toBe('122([])')
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
it('should format nested tags', () => {
|
|
167
|
+
expect(toDiagnostic({ tag: 1, value: { tag: 0, value: 'test' } }))
|
|
168
|
+
.toBe('1(0("test"))')
|
|
169
|
+
})
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
describe('pretty printing', () => {
|
|
173
|
+
it('should pretty print arrays', () => {
|
|
174
|
+
const result = toDiagnostic([1, 2, 3], { pretty: true })
|
|
175
|
+
expect(result).toBe('[\n 1,\n 2,\n 3\n]')
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
it('should pretty print objects', () => {
|
|
179
|
+
const result = toDiagnostic({ a: 1, b: 2 }, { pretty: true })
|
|
180
|
+
expect(result).toBe('{\n "a": 1,\n "b": 2\n}')
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
it('should pretty print nested structures', () => {
|
|
184
|
+
const result = toDiagnostic({ arr: [1, 2] }, { pretty: true })
|
|
185
|
+
expect(result).toContain('{\n')
|
|
186
|
+
expect(result).toContain('"arr":')
|
|
187
|
+
expect(result).toContain('[')
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
it('should use custom indent', () => {
|
|
191
|
+
const result = toDiagnostic([1, 2], { pretty: true, indent: ' ' })
|
|
192
|
+
expect(result).toBe('[\n 1,\n 2\n]')
|
|
193
|
+
})
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
describe('fromDiagnostic (basic parsing)', () => {
|
|
197
|
+
it('should parse null and undefined', () => {
|
|
198
|
+
expect(fromDiagnostic('null')).toBe(null)
|
|
199
|
+
expect(fromDiagnostic('undefined')).toBe(undefined)
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
it('should parse booleans', () => {
|
|
203
|
+
expect(fromDiagnostic('true')).toBe(true)
|
|
204
|
+
expect(fromDiagnostic('false')).toBe(false)
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
it('should parse special floats', () => {
|
|
208
|
+
expect(fromDiagnostic('NaN')).toBeNaN()
|
|
209
|
+
expect(fromDiagnostic('Infinity')).toBe(Infinity)
|
|
210
|
+
expect(fromDiagnostic('-Infinity')).toBe(-Infinity)
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
it('should parse numbers', () => {
|
|
214
|
+
expect(fromDiagnostic('100')).toBe(100)
|
|
215
|
+
expect(fromDiagnostic('-50')).toBe(-50)
|
|
216
|
+
expect(fromDiagnostic('3.14')).toBe(3.14)
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
it('should parse byte strings', () => {
|
|
220
|
+
const result = fromDiagnostic("h'0102'")
|
|
221
|
+
expect(result).toBeInstanceOf(Uint8Array)
|
|
222
|
+
expect(Array.from(result as Uint8Array)).toEqual([1, 2])
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
it('should parse empty byte string', () => {
|
|
226
|
+
const result = fromDiagnostic("h''")
|
|
227
|
+
expect(result).toBeInstanceOf(Uint8Array)
|
|
228
|
+
expect((result as Uint8Array).length).toBe(0)
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
it('should parse strings', () => {
|
|
232
|
+
expect(fromDiagnostic('"hello"')).toBe('hello')
|
|
233
|
+
expect(fromDiagnostic('""')).toBe('')
|
|
234
|
+
})
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
describe('RFC 8949 Appendix A examples', () => {
|
|
238
|
+
// Examples from RFC 8949 Appendix A
|
|
239
|
+
|
|
240
|
+
it('should format RFC examples - integers', () => {
|
|
241
|
+
expect(toDiagnostic(0)).toBe('0')
|
|
242
|
+
expect(toDiagnostic(23)).toBe('23')
|
|
243
|
+
expect(toDiagnostic(24)).toBe('24')
|
|
244
|
+
expect(toDiagnostic(100)).toBe('100')
|
|
245
|
+
expect(toDiagnostic(1000)).toBe('1000')
|
|
246
|
+
expect(toDiagnostic(1000000)).toBe('1000000')
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
it('should format RFC examples - negative integers', () => {
|
|
250
|
+
expect(toDiagnostic(-1)).toBe('-1')
|
|
251
|
+
expect(toDiagnostic(-10)).toBe('-10')
|
|
252
|
+
expect(toDiagnostic(-100)).toBe('-100')
|
|
253
|
+
expect(toDiagnostic(-1000)).toBe('-1000')
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
it('should format RFC examples - floats', () => {
|
|
257
|
+
expect(toDiagnostic(0.0)).toBe('0')
|
|
258
|
+
expect(toDiagnostic(1.0)).toBe('1')
|
|
259
|
+
expect(toDiagnostic(1.5)).toBe('1.5')
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
it('should format RFC examples - strings', () => {
|
|
263
|
+
expect(toDiagnostic('')).toBe('""')
|
|
264
|
+
expect(toDiagnostic('a')).toBe('"a"')
|
|
265
|
+
expect(toDiagnostic('IETF')).toBe('"IETF"')
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
it('should format RFC examples - arrays', () => {
|
|
269
|
+
expect(toDiagnostic([])).toBe('[]')
|
|
270
|
+
expect(toDiagnostic([1, 2, 3])).toBe('[1, 2, 3]')
|
|
271
|
+
expect(toDiagnostic([1, [2, 3], [4, 5]])).toBe('[1, [2, 3], [4, 5]]')
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
it('should format RFC examples - maps', () => {
|
|
275
|
+
expect(toDiagnostic({})).toBe('{}')
|
|
276
|
+
expect(toDiagnostic({ a: 1, b: [2, 3] })).toBe('{"a": 1, "b": [2, 3]}')
|
|
277
|
+
})
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
describe('Cardano-specific examples', () => {
|
|
281
|
+
it('should format UTXO structure', () => {
|
|
282
|
+
const utxo = [
|
|
283
|
+
[
|
|
284
|
+
new Uint8Array([0x48, 0xbd, 0x01, 0xd5]), // txHash (truncated)
|
|
285
|
+
0 // index
|
|
286
|
+
]
|
|
287
|
+
]
|
|
288
|
+
const result = toDiagnostic(utxo)
|
|
289
|
+
expect(result).toBe("[[h'48bd01d5', 0]]")
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
it('should format Plutus constructor', () => {
|
|
293
|
+
const constructor = { tag: 121, value: [1, 2] }
|
|
294
|
+
expect(toDiagnostic(constructor)).toBe('121([1, 2])')
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
it('should format transaction metadata map', () => {
|
|
298
|
+
const metadata = new Map([
|
|
299
|
+
[674, { msg: ['Hello', 'World'] }]
|
|
300
|
+
])
|
|
301
|
+
const result = toDiagnostic(metadata)
|
|
302
|
+
expect(result).toContain('674:')
|
|
303
|
+
expect(result).toContain('"msg"')
|
|
304
|
+
})
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
describe('edge cases', () => {
|
|
308
|
+
it('should handle depth limit', () => {
|
|
309
|
+
const deepNested = { a: { b: { c: { d: 1 } } } }
|
|
310
|
+
const result = toDiagnostic(deepNested, { maxDepth: 2 })
|
|
311
|
+
expect(result).toContain('...')
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
it('should handle Sets', () => {
|
|
315
|
+
const set = new Set([1, 2, 3])
|
|
316
|
+
expect(toDiagnostic(set)).toBe('[1, 2, 3]')
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
it('should handle mixed content arrays', () => {
|
|
320
|
+
const mixed = [
|
|
321
|
+
1,
|
|
322
|
+
'text',
|
|
323
|
+
new Uint8Array([0xff]),
|
|
324
|
+
{ key: 'value' },
|
|
325
|
+
[1, 2],
|
|
326
|
+
true,
|
|
327
|
+
null
|
|
328
|
+
]
|
|
329
|
+
const result = toDiagnostic(mixed)
|
|
330
|
+
expect(result).toBe('[1, "text", h\'ff\', {"key": "value"}, [1, 2], true, null]')
|
|
331
|
+
})
|
|
332
|
+
})
|
|
333
|
+
})
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CBOR Duplicate Map Key Detection Tests
|
|
3
|
+
* Tests RFC 8949 Section 5.6 requirement for strict mode
|
|
4
|
+
*
|
|
5
|
+
* RFC 8949: "A map that has duplicate keys may be well-formed, but it is not valid,
|
|
6
|
+
* and duplicate keys are prohibited by CBOR decoders that are using strict mode."
|
|
7
|
+
*
|
|
8
|
+
* Tests cover:
|
|
9
|
+
* - Duplicate key detection with different modes ('allow', 'warn', 'reject')
|
|
10
|
+
* - Various key types (strings, integers, byte strings)
|
|
11
|
+
* - Nested maps with duplicates
|
|
12
|
+
* - Real-world Cardano scenarios
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { describe, it, expect, vi } from 'vitest'
|
|
16
|
+
import { useCborParser } from '../composables/useCborParser'
|
|
17
|
+
|
|
18
|
+
describe('useCborParser - Duplicate Map Key Detection', () => {
|
|
19
|
+
describe('Allow Mode (default)', () => {
|
|
20
|
+
it('should allow duplicate string keys by default', () => {
|
|
21
|
+
const { parseWithSourceMap } = useCborParser()
|
|
22
|
+
|
|
23
|
+
// Map with duplicate key "a": {"a": 1, "a": 2}
|
|
24
|
+
// a2 6161 01 6161 02
|
|
25
|
+
const duplicateHex = 'a2616101616102'
|
|
26
|
+
|
|
27
|
+
const result = parseWithSourceMap(duplicateHex)
|
|
28
|
+
|
|
29
|
+
expect(result.value).toBeDefined()
|
|
30
|
+
// Last value wins in JavaScript objects
|
|
31
|
+
expect(result.value.get("a")).toBe(2)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('should allow duplicate integer keys by default', () => {
|
|
35
|
+
const { parseWithSourceMap } = useCborParser()
|
|
36
|
+
|
|
37
|
+
// Map with duplicate key 1: {1: 10, 1: 20}
|
|
38
|
+
// a2 01 0a 01 14
|
|
39
|
+
const duplicateHex = 'a2010a0114'
|
|
40
|
+
|
|
41
|
+
const result = parseWithSourceMap(duplicateHex)
|
|
42
|
+
|
|
43
|
+
expect(result.value).toBeDefined()
|
|
44
|
+
expect(result.value.get(1)).toBe(20)
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
describe('Warn Mode', () => {
|
|
49
|
+
it('should warn but not throw on duplicate keys', () => {
|
|
50
|
+
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
51
|
+
|
|
52
|
+
const { parseWithSourceMap } = useCborParser()
|
|
53
|
+
|
|
54
|
+
// Map: {"a": 1, "a": 2}
|
|
55
|
+
const duplicateHex = 'a2616101616102'
|
|
56
|
+
|
|
57
|
+
const result = parseWithSourceMap(duplicateHex, {
|
|
58
|
+
dupMapKeyMode: 'warn'
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
expect(result.value).toBeDefined()
|
|
62
|
+
expect(consoleWarnSpy).toHaveBeenCalled()
|
|
63
|
+
expect(consoleWarnSpy.mock.calls[0][0]).toContain('Duplicate map key')
|
|
64
|
+
|
|
65
|
+
consoleWarnSpy.mockRestore()
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('should warn multiple times for multiple duplicates', () => {
|
|
69
|
+
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
70
|
+
|
|
71
|
+
const { parseWithSourceMap } = useCborParser()
|
|
72
|
+
|
|
73
|
+
// Map: {"a": 1, "a": 2, "a": 3}
|
|
74
|
+
// a3 6161 01 6161 02 6161 03
|
|
75
|
+
const duplicateHex = 'a3616101616102616103'
|
|
76
|
+
|
|
77
|
+
parseWithSourceMap(duplicateHex, {
|
|
78
|
+
dupMapKeyMode: 'warn'
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
// Should warn twice (second and third occurrence)
|
|
82
|
+
expect(consoleWarnSpy).toHaveBeenCalledTimes(2)
|
|
83
|
+
|
|
84
|
+
consoleWarnSpy.mockRestore()
|
|
85
|
+
})
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
describe('Reject Mode (strict)', () => {
|
|
89
|
+
it('should throw error on duplicate string keys', () => {
|
|
90
|
+
const { parseWithSourceMap } = useCborParser()
|
|
91
|
+
|
|
92
|
+
// Map: {"a": 1, "a": 2}
|
|
93
|
+
const duplicateHex = 'a2616101616102'
|
|
94
|
+
|
|
95
|
+
expect(() => {
|
|
96
|
+
parseWithSourceMap(duplicateHex, {
|
|
97
|
+
dupMapKeyMode: 'reject'
|
|
98
|
+
})
|
|
99
|
+
}).toThrow(/Duplicate map key/)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('should throw error on duplicate integer keys', () => {
|
|
103
|
+
const { parseWithSourceMap } = useCborParser()
|
|
104
|
+
|
|
105
|
+
// Map: {1: 10, 1: 20}
|
|
106
|
+
const duplicateHex = 'a2010a0114'
|
|
107
|
+
|
|
108
|
+
expect(() => {
|
|
109
|
+
parseWithSourceMap(duplicateHex, {
|
|
110
|
+
dupMapKeyMode: 'reject'
|
|
111
|
+
})
|
|
112
|
+
}).toThrow(/Duplicate map key/)
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('should throw on first duplicate encountered', () => {
|
|
116
|
+
const { parseWithSourceMap } = useCborParser()
|
|
117
|
+
|
|
118
|
+
// Map: {"a": 1, "a": 2, "b": 3, "b": 4}
|
|
119
|
+
// Should fail at second "a"
|
|
120
|
+
const duplicateHex = 'a4616101616102616203616204'
|
|
121
|
+
|
|
122
|
+
expect(() => {
|
|
123
|
+
parseWithSourceMap(duplicateHex, {
|
|
124
|
+
dupMapKeyMode: 'reject'
|
|
125
|
+
})
|
|
126
|
+
}).toThrow(/Duplicate map key/)
|
|
127
|
+
})
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
describe('Different Key Types', () => {
|
|
131
|
+
it('should NOT flag different keys of same type', () => {
|
|
132
|
+
const { parseWithSourceMap } = useCborParser()
|
|
133
|
+
|
|
134
|
+
// Map: {"a": 1, "b": 2} - different keys, no error
|
|
135
|
+
// a2 6161 01 6162 02
|
|
136
|
+
const uniqueHex = 'a2616101616202'
|
|
137
|
+
|
|
138
|
+
const result = parseWithSourceMap(uniqueHex, {
|
|
139
|
+
dupMapKeyMode: 'reject'
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
expect(result.value).toBeDefined()
|
|
143
|
+
expect(result.value).toEqual(new Map([['a', 1], ['b', 2]]))
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
it('should handle byte string keys correctly', () => {
|
|
147
|
+
const { parseWithSourceMap } = useCborParser()
|
|
148
|
+
|
|
149
|
+
// Map with duplicate byte string key
|
|
150
|
+
// {h'01': 1, h'01': 2}
|
|
151
|
+
// a2 4101 01 4101 02
|
|
152
|
+
const duplicateHex = 'a2410101410102'
|
|
153
|
+
|
|
154
|
+
expect(() => {
|
|
155
|
+
parseWithSourceMap(duplicateHex, {
|
|
156
|
+
dupMapKeyMode: 'reject'
|
|
157
|
+
})
|
|
158
|
+
}).toThrow(/Duplicate map key/)
|
|
159
|
+
})
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
describe('Nested Maps', () => {
|
|
163
|
+
it('should detect duplicates in nested maps', () => {
|
|
164
|
+
const { parseWithSourceMap } = useCborParser()
|
|
165
|
+
|
|
166
|
+
// Map: {"outer": {"inner": 1, "inner": 2}}
|
|
167
|
+
// a1 656f75746572 a2 65696e6e6572 01 65696e6e6572 02
|
|
168
|
+
const duplicateHex = 'a1656f75746572a26569' + '6e6e65720165696e6e657202'
|
|
169
|
+
|
|
170
|
+
expect(() => {
|
|
171
|
+
parseWithSourceMap(duplicateHex, {
|
|
172
|
+
dupMapKeyMode: 'reject'
|
|
173
|
+
})
|
|
174
|
+
}).toThrow(/Duplicate map key/)
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
it('should allow same keys in different nested maps', () => {
|
|
178
|
+
const { parseWithSourceMap } = useCborParser()
|
|
179
|
+
|
|
180
|
+
// Map: {"map1": {"a": 1}, "map2": {"a": 2}}
|
|
181
|
+
// Same key "a" in different maps - OK
|
|
182
|
+
// a2 646d617031 a1616101 646d617032 a1616102
|
|
183
|
+
const validHex = 'a2646d617031a1616101646d617032a1616102'
|
|
184
|
+
|
|
185
|
+
const result = parseWithSourceMap(validHex, {
|
|
186
|
+
dupMapKeyMode: 'reject'
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
expect(result.value).toBeDefined()
|
|
190
|
+
})
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
describe('Edge Cases', () => {
|
|
194
|
+
it('should handle empty maps', () => {
|
|
195
|
+
const { parseWithSourceMap } = useCborParser()
|
|
196
|
+
|
|
197
|
+
// Empty map: {}
|
|
198
|
+
const emptyHex = 'a0'
|
|
199
|
+
|
|
200
|
+
const result = parseWithSourceMap(emptyHex, {
|
|
201
|
+
dupMapKeyMode: 'reject'
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
expect(result.value).toEqual(new Map())
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
it('should handle single-entry maps', () => {
|
|
208
|
+
const { parseWithSourceMap } = useCborParser()
|
|
209
|
+
|
|
210
|
+
// Map: {"a": 1}
|
|
211
|
+
const singleHex = 'a1616101'
|
|
212
|
+
|
|
213
|
+
const result = parseWithSourceMap(singleHex, {
|
|
214
|
+
dupMapKeyMode: 'reject'
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
expect(result.value).toEqual(new Map([['a', 1]]))
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
it('should handle large maps without duplicates', () => {
|
|
221
|
+
const { parseWithSourceMap } = useCborParser()
|
|
222
|
+
|
|
223
|
+
// Map with 10 unique keys
|
|
224
|
+
// {"a": 1, "b": 2, ..., "j": 10}
|
|
225
|
+
const largeHex = 'aa616101616202616303616404616505616606616707616808616909616a0a'
|
|
226
|
+
|
|
227
|
+
const result = parseWithSourceMap(largeHex, {
|
|
228
|
+
dupMapKeyMode: 'reject'
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
expect(result.value).toBeDefined()
|
|
232
|
+
expect(result.value.size).toBe(10)
|
|
233
|
+
})
|
|
234
|
+
})
|
|
235
|
+
})
|