+568
-264
lib/mod.test.ts
+568
-264
lib/mod.test.ts
···
9
9
const Message = p.message({ text: p.string() }, { text: 1 });
10
10
11
11
const cases = [
12
-
'',
13
-
'hello world',
14
-
'hello 🚀',
15
-
'a'.repeat(1000),
16
-
'Café',
17
-
'おはようございます☀️',
18
-
'नमस्ते',
19
-
'Здравствуйте',
20
-
'你'.repeat(43),
21
-
'🌟'.repeat(32),
22
-
'🚀🌟💻',
23
-
'🏳️🌈🏳️⚧️',
12
+
{
13
+
text: '',
14
+
expected: Uint8Array.from([0x0a, 0x00]),
15
+
}, // field 1, length 0
16
+
{
17
+
text: 'hello world',
18
+
expected: Uint8Array.from([
19
+
0x0a,
20
+
0x0b,
21
+
0x68,
22
+
0x65,
23
+
0x6c,
24
+
0x6c,
25
+
0x6f,
26
+
0x20,
27
+
0x77,
28
+
0x6f,
29
+
0x72,
30
+
0x6c,
31
+
0x64,
32
+
]),
33
+
}, // field 1, length 11
34
+
{
35
+
text: 'hello 🚀',
36
+
expected: Uint8Array.from([0x0a, 0x0a, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0xf0, 0x9f, 0x9a, 0x80]),
37
+
}, // field 1, length 10, UTF-8 rocket
38
+
{
39
+
text: 'Café',
40
+
expected: Uint8Array.from([0x0a, 0x05, 0x43, 0x61, 0x66, 0xc3, 0xa9]),
41
+
}, // field 1, length 5, UTF-8 café
42
+
43
+
{
44
+
text: 'a'.repeat(1000),
45
+
expected: null,
46
+
},
47
+
{
48
+
text: 'おはようございます☀️',
49
+
expected: null,
50
+
},
51
+
{
52
+
text: 'नमस्ते',
53
+
expected: null,
54
+
},
55
+
{
56
+
text: 'Здравствуйте',
57
+
expected: null,
58
+
},
59
+
{
60
+
text: '你'.repeat(43),
61
+
expected: null,
62
+
},
63
+
{
64
+
text: '🌟'.repeat(32),
65
+
expected: null,
66
+
},
67
+
{
68
+
text: '🚀🌟💻',
69
+
expected: null,
70
+
},
71
+
{
72
+
text: '🏳️🌈🏳️⚧️',
73
+
expected: null,
74
+
},
24
75
];
25
76
26
-
for (const text of cases) {
77
+
for (const { text, expected } of cases) {
27
78
const encoded = p.encode(Message, { text });
28
-
const decoded = p.decode(Message, encoded);
79
+
if (expected !== null) {
80
+
assertEquals(encoded, expected);
81
+
}
29
82
83
+
const decoded = p.decode(Message, encoded);
30
84
assertEquals(decoded, { text });
31
85
}
32
86
});
···
35
89
const Message = p.message({ value: p.int32() }, { value: 1 });
36
90
37
91
const cases = [
38
-
0,
39
-
1,
40
-
-1,
41
-
127,
42
-
-128,
43
-
255,
44
-
-256,
45
-
32767,
46
-
-32768,
47
-
65535,
48
-
-65536,
49
-
2147483647, // max int32
50
-
-2147483648, // min int32
92
+
{ // field 1, varint 0
93
+
value: 0,
94
+
expected: Uint8Array.from([0x08, 0x00]),
95
+
},
96
+
{ // field 1, varint 1
97
+
value: 1,
98
+
expected: Uint8Array.from([0x08, 0x01]),
99
+
},
100
+
{ // field 1, varint -1 (int32)
101
+
value: -1,
102
+
expected: Uint8Array.from([0x08, 0xff, 0xff, 0xff, 0xff, 0x0f]),
103
+
},
104
+
{ // field 1, varint 127
105
+
value: 127,
106
+
expected: Uint8Array.from([0x08, 0x7f]),
107
+
},
108
+
{ // field 1, varint -128
109
+
value: -128,
110
+
expected: Uint8Array.from([0x08, 0x80, 0xff, 0xff, 0xff, 0x0f]),
111
+
},
112
+
{ // field 1, varint 255
113
+
value: 255,
114
+
expected: Uint8Array.from([0x08, 0xff, 0x01]),
115
+
},
116
+
{ // field 1, varint -256
117
+
value: -256,
118
+
expected: Uint8Array.from([0x08, 0x80, 0xfe, 0xff, 0xff, 0x0f]),
119
+
},
120
+
{ // field 1, varint 32767
121
+
value: 32767,
122
+
expected: Uint8Array.from([0x08, 0xff, 0xff, 0x01]),
123
+
},
124
+
{ // field 1, varint -32768
125
+
value: -32768,
126
+
expected: Uint8Array.from([0x08, 0x80, 0x80, 0xfe, 0xff, 0x0f]),
127
+
},
128
+
{ // field 1, varint 65535
129
+
value: 65535,
130
+
expected: Uint8Array.from([0x08, 0xff, 0xff, 0x03]),
131
+
},
132
+
{ // field 1, varint -65536
133
+
value: -65536,
134
+
expected: Uint8Array.from([0x08, 0x80, 0x80, 0xfc, 0xff, 0x0f]),
135
+
},
136
+
{ // max int32
137
+
value: 2147483647,
138
+
expected: null,
139
+
},
140
+
{ // min int32
141
+
value: -2147483648,
142
+
expected: null,
143
+
},
51
144
];
52
145
53
-
for (const value of cases) {
146
+
for (const { value, expected } of cases) {
54
147
const encoded = p.encode(Message, { value });
55
-
const decoded = p.decode(Message, encoded);
148
+
if (expected !== null) {
149
+
assertEquals(encoded, expected);
150
+
}
56
151
152
+
const decoded = p.decode(Message, encoded);
57
153
assertEquals(decoded, { value });
58
154
}
59
155
});
···
62
158
const Message = p.message({ value: p.int64() }, { value: 1 });
63
159
64
160
const cases = [
65
-
0n,
66
-
1n,
67
-
-1n,
68
-
127n,
69
-
-128n,
70
-
9223372036854775807n, // max int64
71
-
-9223372036854775808n, // min int64
161
+
{ // field 1, varint 0
162
+
value: 0n,
163
+
expected: Uint8Array.from([0x08, 0x00]),
164
+
},
165
+
{ // field 1, varint 1
166
+
value: 1n,
167
+
expected: Uint8Array.from([0x08, 0x01]),
168
+
},
169
+
{ // field 1, varint -1 (int64)
170
+
value: -1n,
171
+
expected: Uint8Array.from([0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01]),
172
+
},
173
+
{ // field 1, varint 127
174
+
value: 127n,
175
+
expected: Uint8Array.from([0x08, 0x7f]),
176
+
},
177
+
{ // field 1, varint -128
178
+
value: -128n,
179
+
expected: Uint8Array.from([0x08, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01]),
180
+
},
181
+
{ // max int64
182
+
value: 9223372036854775807n,
183
+
expected: null,
184
+
},
185
+
{ // min int64
186
+
value: -9223372036854775808n,
187
+
expected: null,
188
+
},
72
189
];
73
190
74
-
for (const value of cases) {
191
+
for (const { value, expected } of cases) {
75
192
const encoded = p.encode(Message, { value });
76
-
const decoded = p.decode(Message, encoded);
193
+
if (expected !== null) {
194
+
assertEquals(encoded, expected);
195
+
}
77
196
197
+
const decoded = p.decode(Message, encoded);
78
198
assertEquals(decoded, { value });
79
199
}
80
200
});
···
83
203
const Message = p.message({ value: p.uint32() }, { value: 1 });
84
204
85
205
const cases = [
86
-
0,
87
-
1,
88
-
127,
89
-
255,
90
-
32767,
91
-
65535,
92
-
2147483647,
93
-
4294967295, // max uint32
206
+
{ value: 0, expected: Uint8Array.from([0x08, 0x00]) }, // field 1, varint 0
207
+
{ value: 1, expected: Uint8Array.from([0x08, 0x01]) }, // field 1, varint 1
208
+
{ value: 127, expected: Uint8Array.from([0x08, 0x7f]) }, // field 1, varint 127
209
+
{ value: 255, expected: Uint8Array.from([0x08, 0xff, 0x01]) }, // field 1, varint 255
210
+
{ value: 32767, expected: Uint8Array.from([0x08, 0xff, 0xff, 0x01]) }, // field 1, varint 32767
211
+
{ value: 65535, expected: Uint8Array.from([0x08, 0xff, 0xff, 0x03]) }, // field 1, varint 65535
212
+
{ value: 2147483647, expected: Uint8Array.from([0x08, 0xff, 0xff, 0xff, 0xff, 0x07]) }, // field 1, varint max int32
213
+
{ value: 4294967295, expected: Uint8Array.from([0x08, 0xff, 0xff, 0xff, 0xff, 0x0f]) }, // max uint32
94
214
];
95
215
96
-
for (const value of cases) {
216
+
for (const { value, expected } of cases) {
97
217
const encoded = p.encode(Message, { value });
98
-
const decoded = p.decode(Message, encoded);
218
+
if (expected !== null) {
219
+
assertEquals(encoded, expected);
220
+
}
99
221
222
+
const decoded = p.decode(Message, encoded);
100
223
assertEquals(decoded, { value });
101
224
}
102
225
});
···
105
228
const Message = p.message({ value: p.uint64() }, { value: 1 });
106
229
107
230
const cases = [
108
-
0n,
109
-
1n,
110
-
127n,
111
-
255n,
112
-
18446744073709551615n, // max uint64
231
+
{ value: 0n, expected: Uint8Array.from([0x08, 0x00]) }, // field 1, varint 0
232
+
{ value: 1n, expected: Uint8Array.from([0x08, 0x01]) }, // field 1, varint 1
233
+
{ value: 127n, expected: Uint8Array.from([0x08, 0x7f]) }, // field 1, varint 127
234
+
{ value: 255n, expected: Uint8Array.from([0x08, 0xff, 0x01]) }, // field 1, varint 255
235
+
{ value: 18446744073709551615n, expected: null }, // max uint64
113
236
];
114
237
115
-
for (const value of cases) {
238
+
for (const { value, expected } of cases) {
116
239
const encoded = p.encode(Message, { value });
117
-
const decoded = p.decode(Message, encoded);
240
+
if (expected !== null) {
241
+
assertEquals(encoded, expected);
242
+
}
118
243
244
+
const decoded = p.decode(Message, encoded);
119
245
assertEquals(decoded, { value });
120
246
}
121
247
});
···
124
250
const schema = p.message({ value: p.sint32() }, { value: 1 });
125
251
126
252
const testCases = [
127
-
0,
128
-
1,
129
-
-1,
130
-
2,
131
-
-2,
132
-
127,
133
-
-128,
134
-
2147483647, // max int32
135
-
-2147483648, // min int32
253
+
{ value: 0, expected: Uint8Array.from([0x08, 0x00]) }, // field 1, zigzag 0 -> varint 0
254
+
{ value: 1, expected: Uint8Array.from([0x08, 0x02]) }, // field 1, zigzag 1 -> varint 2
255
+
{ value: -1, expected: Uint8Array.from([0x08, 0x01]) }, // field 1, zigzag -1 -> varint 1
256
+
{ value: 2, expected: Uint8Array.from([0x08, 0x04]) }, // field 1, zigzag 2 -> varint 4
257
+
{ value: -2, expected: Uint8Array.from([0x08, 0x03]) }, // field 1, zigzag -2 -> varint 3
258
+
{ value: 127, expected: Uint8Array.from([0x08, 0xfe, 0x01]) }, // field 1, zigzag 127 -> varint 254
259
+
{ value: -128, expected: Uint8Array.from([0x08, 0xff, 0x01]) }, // field 1, zigzag -128 -> varint 255
260
+
{ value: 2147483647, expected: null }, // max int32
261
+
{ value: -2147483648, expected: null }, // min int32
136
262
];
137
263
138
-
for (const value of testCases) {
264
+
for (const { value, expected } of testCases) {
139
265
const encoded = p.encode(schema, { value });
140
-
const decoded = p.decode(schema, encoded);
266
+
if (expected !== null) {
267
+
assertEquals(encoded, expected);
268
+
}
141
269
270
+
const decoded = p.decode(schema, encoded);
142
271
assertEquals(decoded, { value });
143
272
}
144
273
});
···
147
276
const Message = p.message({ value: p.sint64() }, { value: 1 });
148
277
149
278
const cases = [
150
-
0n,
151
-
1n,
152
-
-1n,
153
-
2n,
154
-
-2n,
155
-
127n,
156
-
-128n,
157
-
9223372036854775807n, // max int64
158
-
-9223372036854775808n, // min int64
279
+
{ value: 0n, expected: Uint8Array.from([0x08, 0x00]) }, // field 1, zigzag 0 -> varint 0
280
+
{ value: 1n, expected: Uint8Array.from([0x08, 0x02]) }, // field 1, zigzag 1 -> varint 2
281
+
{ value: -1n, expected: Uint8Array.from([0x08, 0x01]) }, // field 1, zigzag -1 -> varint 1
282
+
{ value: 2n, expected: Uint8Array.from([0x08, 0x04]) }, // field 1, zigzag 2 -> varint 4
283
+
{ value: -2n, expected: Uint8Array.from([0x08, 0x03]) }, // field 1, zigzag -2 -> varint 3
284
+
{ value: 127n, expected: Uint8Array.from([0x08, 0xfe, 0x01]) }, // field 1, zigzag 127 -> varint 254
285
+
{ value: -128n, expected: Uint8Array.from([0x08, 0xff, 0x01]) }, // field 1, zigzag -128 -> varint 255
286
+
{ value: 9223372036854775807n, expected: null }, // max int64
287
+
{ value: -9223372036854775808n, expected: null }, // min int64
159
288
];
160
289
161
-
for (const value of cases) {
290
+
for (const { value, expected } of cases) {
162
291
const encoded = p.encode(Message, { value });
163
-
const decoded = p.decode(Message, encoded);
292
+
if (expected !== null) {
293
+
assertEquals(encoded, expected);
294
+
}
164
295
296
+
const decoded = p.decode(Message, encoded);
165
297
assertEquals(decoded, { value });
166
298
}
167
299
});
···
170
302
const Message = p.message({ value: p.float() }, { value: 1 });
171
303
172
304
const cases = [
173
-
0.0,
174
-
1.0,
175
-
-1.0,
176
-
3.14159,
177
-
-3.14159,
178
-
1.5e10,
179
-
-1.5e10,
180
-
3.4028235e38, // close to max float32
181
-
1.175494e-38, // close to min positive float32
182
-
Infinity,
183
-
-Infinity,
184
-
NaN,
305
+
{ value: 0.0, expected: Uint8Array.from([0x0d, 0x00, 0x00, 0x00, 0x00]) }, // field 1, float32 0.0 (little-endian)
306
+
{ value: 1.0, expected: Uint8Array.from([0x0d, 0x00, 0x00, 0x80, 0x3f]) }, // field 1, float32 1.0 (little-endian)
307
+
{ value: -1.0, expected: Uint8Array.from([0x0d, 0x00, 0x00, 0x80, 0xbf]) }, // field 1, float32 -1.0 (little-endian)
308
+
{ value: 3.14159, expected: null },
309
+
{ value: -3.14159, expected: null },
310
+
{ value: 1.5e10, expected: null },
311
+
{ value: -1.5e10, expected: null },
312
+
{ value: 3.4028235e38, expected: null }, // close to max float32
313
+
{ value: 1.175494e-38, expected: null }, // close to min positive float32
314
+
{ value: Infinity, expected: null },
315
+
{ value: -Infinity, expected: null },
316
+
{ value: NaN, expected: null },
185
317
];
186
318
187
-
for (const value of cases) {
319
+
for (const { value, expected } of cases) {
188
320
const encoded = p.encode(Message, { value });
321
+
if (expected !== null) {
322
+
assertEquals(encoded, expected);
323
+
}
324
+
189
325
const decoded = p.decode(Message, encoded);
190
326
191
327
// Special handling for infinity values
···
205
341
const Message = p.message({ value: p.double() }, { value: 1 });
206
342
207
343
const cases = [
208
-
0.0,
209
-
1.0,
210
-
-1.0,
211
-
3.141592653589793,
212
-
-3.141592653589793,
213
-
1.7976931348623157e+308, // close to max double
214
-
2.2250738585072014e-308, // close to min positive double
215
-
Infinity,
216
-
-Infinity,
217
-
NaN,
344
+
{ // field 1, double 0.0 (little-endian)
345
+
value: 0.0,
346
+
expected: Uint8Array.from([0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
347
+
},
348
+
{ // field 1, double 1.0 (little-endian)
349
+
value: 1.0,
350
+
expected: Uint8Array.from([0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f]),
351
+
},
352
+
{ // field 1, double -1.0 (little-endian)
353
+
value: -1.0,
354
+
expected: Uint8Array.from([0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xbf]),
355
+
},
356
+
{
357
+
value: 3.141592653589793,
358
+
expected: null,
359
+
},
360
+
{
361
+
value: -3.141592653589793,
362
+
expected: null,
363
+
},
364
+
{ // close to max double
365
+
value: 1.7976931348623157e+308,
366
+
expected: null,
367
+
},
368
+
{ // close to min positive double
369
+
value: 2.2250738585072014e-308,
370
+
expected: null,
371
+
},
372
+
{
373
+
value: Infinity,
374
+
expected: null,
375
+
},
376
+
{
377
+
value: -Infinity,
378
+
expected: null,
379
+
},
380
+
{
381
+
value: NaN,
382
+
expected: null,
383
+
},
218
384
];
219
385
220
-
for (const value of cases) {
386
+
for (const { value, expected } of cases) {
221
387
const encoded = p.encode(Message, { value });
222
-
const decoded = p.decode(Message, encoded);
388
+
if (expected !== null) {
389
+
assertEquals(encoded, expected);
390
+
}
223
391
392
+
const decoded = p.decode(Message, encoded);
224
393
assertEquals(decoded, { value });
225
394
}
226
395
});
···
283
452
Deno.test('boolean encoding/decoding', () => {
284
453
const Message = p.message({ value: p.boolean() }, { value: 1 });
285
454
286
-
const cases = [true, false];
455
+
const cases = [
456
+
{ value: true, expected: Uint8Array.from([0x08, 0x01]) }, // field 1, varint 1
457
+
{ value: false, expected: Uint8Array.from([0x08, 0x00]) }, // field 1, varint 0
458
+
];
287
459
288
-
for (const value of cases) {
460
+
for (const { value, expected } of cases) {
289
461
const encoded = p.encode(Message, { value });
462
+
assertEquals(encoded, expected);
463
+
290
464
const decoded = p.decode(Message, encoded);
291
-
292
465
assertEquals(decoded, { value });
293
466
}
294
467
});
···
297
470
const Message = p.message({ data: p.bytes() }, { data: 1 });
298
471
299
472
const cases = [
300
-
Uint8Array.from([]),
301
-
Uint8Array.from([0]),
302
-
Uint8Array.from([1, 2, 3, 4, 5]),
303
-
Uint8Array.from([255, 254, 253]),
304
-
new Uint8Array(Array.from({ length: 1000 }, (_, i) => i % 256)), // large array
473
+
{ // field 1, length 0
474
+
data: new Uint8Array(0),
475
+
expected: Uint8Array.from([0x0a, 0x00]),
476
+
},
477
+
{ // field 1, length 1, byte 0
478
+
data: Uint8Array.from([0]),
479
+
expected: Uint8Array.from([0x0a, 0x01, 0x00]),
480
+
},
481
+
{ // field 1, length 5
482
+
data: Uint8Array.from([1, 2, 3, 4, 5]),
483
+
expected: Uint8Array.from([0x0a, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05]),
484
+
},
485
+
{ // field 1, length 3
486
+
data: Uint8Array.from([255, 254, 253]),
487
+
expected: Uint8Array.from([0x0a, 0x03, 0xff, 0xfe, 0xfd]),
488
+
},
489
+
{ // large array
490
+
data: new Uint8Array(Array.from({ length: 1000 }, (_, i) => i % 256)),
491
+
expected: null,
492
+
},
305
493
];
306
494
307
-
for (const data of cases) {
495
+
for (const { data, expected } of cases) {
308
496
const encoded = p.encode(Message, { data });
309
-
const decoded = p.decode(Message, encoded);
497
+
if (expected !== null) {
498
+
assertEquals(encoded, expected);
499
+
}
310
500
501
+
const decoded = p.decode(Message, encoded);
311
502
assertEquals(decoded, { data });
312
503
}
313
504
});
···
316
507
const Message = p.message({ value: p.fixed32() }, { value: 1 });
317
508
318
509
const cases = [
319
-
0,
320
-
1,
321
-
255,
322
-
65535,
323
-
4294967295, // max uint32
510
+
{ // field 1, fixed32 0 (little-endian)
511
+
value: 0,
512
+
expected: Uint8Array.from([0x0d, 0x00, 0x00, 0x00, 0x00]),
513
+
},
514
+
{ // field 1, fixed32 1 (little-endian)
515
+
value: 1,
516
+
expected: Uint8Array.from([0x0d, 0x01, 0x00, 0x00, 0x00]),
517
+
},
518
+
{ // field 1, fixed32 255 (little-endian)
519
+
value: 255,
520
+
expected: Uint8Array.from([0x0d, 0xff, 0x00, 0x00, 0x00]),
521
+
},
522
+
{ // field 1, fixed32 65535 (little-endian)
523
+
value: 65535,
524
+
expected: Uint8Array.from([0x0d, 0xff, 0xff, 0x00, 0x00]),
525
+
},
526
+
{ // field 1, fixed32 max uint32 (little-endian)
527
+
value: 4294967295,
528
+
expected: Uint8Array.from([0x0d, 0xff, 0xff, 0xff, 0xff]),
529
+
},
324
530
];
325
531
326
-
for (const value of cases) {
532
+
for (const { value, expected } of cases) {
327
533
const encoded = p.encode(Message, { value });
534
+
assertEquals(encoded, expected);
535
+
328
536
const decoded = p.decode(Message, encoded);
329
-
330
537
assertEquals(decoded, { value });
331
538
}
332
539
});
···
335
542
const Message = p.message({ value: p.fixed64() }, { value: 1 });
336
543
337
544
const cases = [
338
-
0n,
339
-
1n,
340
-
255n,
341
-
65535n,
342
-
18446744073709551615n, // max uint64
545
+
{ // field 1, fixed64 0 (little-endian)
546
+
value: 0n,
547
+
expected: Uint8Array.from([0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
548
+
},
549
+
{ // field 1, fixed64 1 (little-endian)
550
+
value: 1n,
551
+
expected: Uint8Array.from([0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
552
+
},
553
+
{ // field 1, fixed64 255 (little-endian)
554
+
value: 255n,
555
+
expected: Uint8Array.from([0x09, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
556
+
},
557
+
{ // field 1, fixed64 65535 (little-endian)
558
+
value: 65535n,
559
+
expected: Uint8Array.from([0x09, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
560
+
},
561
+
{ // field 1, fixed64 max uint64 (little-endian)
562
+
value: 18446744073709551615n,
563
+
expected: Uint8Array.from([0x09, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]),
564
+
},
343
565
];
344
566
345
-
for (const value of cases) {
567
+
for (const { value, expected } of cases) {
346
568
const encoded = p.encode(Message, { value });
569
+
assertEquals(encoded, expected);
570
+
347
571
const decoded = p.decode(Message, encoded);
348
-
349
572
assertEquals(decoded, { value });
350
573
}
351
574
});
···
354
577
const Message = p.message({ value: p.sfixed32() }, { value: 1 });
355
578
356
579
const cases = [
357
-
0,
358
-
1,
359
-
-1,
360
-
2147483647, // max int32
361
-
-2147483648, // min int32
580
+
{ value: 0, expected: Uint8Array.from([0x0d, 0x00, 0x00, 0x00, 0x00]) }, // field 1, sfixed32 0 (little-endian)
581
+
{ value: 1, expected: Uint8Array.from([0x0d, 0x01, 0x00, 0x00, 0x00]) }, // field 1, sfixed32 1 (little-endian)
582
+
{ value: -1, expected: Uint8Array.from([0x0d, 0xff, 0xff, 0xff, 0xff]) }, // field 1, sfixed32 -1 (little-endian, two's complement)
583
+
{ value: 2147483647, expected: Uint8Array.from([0x0d, 0xff, 0xff, 0xff, 0x7f]) }, // field 1, sfixed32 max int32 (little-endian)
584
+
{ value: -2147483648, expected: Uint8Array.from([0x0d, 0x00, 0x00, 0x00, 0x80]) }, // field 1, sfixed32 min int32 (little-endian)
362
585
];
363
586
364
-
for (const value of cases) {
587
+
for (const { value, expected } of cases) {
365
588
const encoded = p.encode(Message, { value });
366
-
const decoded = p.decode(Message, encoded);
589
+
assertEquals(encoded, expected);
367
590
591
+
const decoded = p.decode(Message, encoded);
368
592
assertEquals(decoded, { value });
369
593
}
370
594
});
···
373
597
const Message = p.message({ value: p.sfixed64() }, { value: 1 });
374
598
375
599
const cases = [
376
-
0n,
377
-
1n,
378
-
-1n,
379
-
9223372036854775807n, // max int64
380
-
-9223372036854775808n, // min int64
600
+
{ // field 1, sfixed64 0 (little-endian)
601
+
value: 0n,
602
+
expected: Uint8Array.from([0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
603
+
},
604
+
{ // field 1, sfixed64 1 (little-endian)
605
+
value: 1n,
606
+
expected: Uint8Array.from([0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
607
+
},
608
+
{ // field 1, sfixed64 -1 (little-endian, two's complement)
609
+
value: -1n,
610
+
expected: Uint8Array.from([0x09, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]),
611
+
},
612
+
{ // field 1, sfixed64 max int64 (little-endian)
613
+
value: 9223372036854775807n,
614
+
expected: Uint8Array.from([0x09, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]),
615
+
},
616
+
{ // field 1, sfixed64 min int64 (little-endian)
617
+
value: -9223372036854775808n,
618
+
expected: Uint8Array.from([0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]),
619
+
},
381
620
];
382
621
383
-
for (const value of cases) {
622
+
for (const { value, expected } of cases) {
384
623
const encoded = p.encode(Message, { value });
624
+
assertEquals(encoded, expected);
625
+
385
626
const decoded = p.decode(Message, encoded);
386
-
387
627
assertEquals(decoded, { value });
388
628
}
389
629
});
···
395
635
Deno.test('repeated fields', () => {
396
636
const cases = [
397
637
{
398
-
numbers: [],
399
-
strings: [],
638
+
data: { numbers: [], strings: [] },
639
+
unpacked: new Uint8Array(0), // empty message
640
+
packed: Uint8Array.from([0x08, 0x00, 0x12, 0x00]), // field 1: tag + length 0, field 2: tag + length 0
400
641
},
401
642
{
402
-
numbers: [1],
403
-
strings: ['hello'],
643
+
data: { numbers: [1], strings: ['hello'] },
644
+
unpacked: Uint8Array.from([0x08, 0x01, 0x12, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f]), // field 1: varint 1, field 2: length 5 + "hello"
645
+
packed: Uint8Array.from([0x08, 0x01, 0x01, 0x12, 0x06, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f]), // field 1: tag + length 1 + varint 1, field 2: tag + length 6 + (length 5 + "hello")
404
646
},
405
647
{
406
-
numbers: [1, 2, 3, -1, -2],
407
-
strings: ['hello', 'world', ''],
648
+
data: { numbers: [1, 2, 3], strings: ['hi'] },
649
+
unpacked: Uint8Array.from([0x08, 0x01, 0x08, 0x02, 0x08, 0x03, 0x12, 0x02, 0x68, 0x69]), // field 1: 1,2,3, field 2: "hi"
650
+
packed: Uint8Array.from([0x08, 0x03, 0x01, 0x02, 0x03, 0x12, 0x03, 0x02, 0x68, 0x69]), // field 1: tag + length 3 + varints 1,2,3, field 2: tag + length 3 + (length 2 + "hi")
408
651
},
409
652
{
410
-
numbers: Array.from({ length: 100 }, (_, i) => i),
411
-
strings: Array.from({ length: 100 }, (_, i) => `item${i}`),
653
+
data: {
654
+
numbers: Array.from({ length: 100 }, (_, i) => i),
655
+
strings: Array.from({ length: 100 }, (_, i) => `item${i}`),
656
+
},
657
+
unpacked: null, // too large for expected bytes
658
+
packed: null, // too large for expected bytes
412
659
},
413
660
];
414
661
662
+
// Test non-packed repeated fields
415
663
{
416
664
const Message = p.message({
417
665
numbers: p.repeated(p.int32(), false),
···
421
669
strings: 2,
422
670
});
423
671
424
-
for (const data of cases) {
672
+
for (const { data, unpacked } of cases) {
425
673
const encoded = p.encode(Message, data);
674
+
if (unpacked !== null) {
675
+
assertEquals(encoded, unpacked);
676
+
}
677
+
426
678
const decoded = p.decode(Message, encoded);
427
-
428
679
assertEquals(decoded, data);
429
680
}
430
681
}
431
682
683
+
// Test packed repeated fields (different wire format)
432
684
{
433
685
const Message = p.message({
434
686
numbers: p.repeated(p.int32(), true),
···
438
690
strings: 2,
439
691
});
440
692
441
-
for (const data of cases) {
693
+
for (const { data, packed } of cases) {
442
694
const encoded = p.encode(Message, data);
443
-
const decoded = p.decode(Message, encoded);
695
+
if (packed !== null) {
696
+
assertEquals(encoded, packed);
697
+
}
444
698
699
+
const decoded = p.decode(Message, encoded);
445
700
assertEquals(decoded, data);
446
701
}
447
702
}
···
460
715
withoutDefault: 4,
461
716
});
462
717
463
-
{
464
-
const full = {
465
-
required: 'hello',
466
-
withDefault: 'custom',
467
-
withFunctionDefault: 99,
468
-
withoutDefault: 'present',
469
-
};
470
-
471
-
const encoded = p.encode(Message, full);
472
-
const decoded = p.decode(Message, encoded);
473
-
474
-
assertEquals(decoded, full);
475
-
}
476
-
{
477
-
const minimal = { required: 'hello' };
478
-
479
-
const encoded = p.encode(Message, minimal);
480
-
const decoded = p.decode(Message, encoded);
481
-
482
-
assertEquals(decoded, {
483
-
required: 'hello',
484
-
withDefault: 'default_value',
485
-
withFunctionDefault: 42,
486
-
// withoutDefault should be undefined (not present)
487
-
});
488
-
}
718
+
const cases = [
719
+
{
720
+
data: {
721
+
required: 'hello',
722
+
withDefault: 'custom',
723
+
withFunctionDefault: 99,
724
+
withoutDefault: 'present',
725
+
},
726
+
expected: Uint8Array.from([
727
+
0x0a,
728
+
0x05,
729
+
0x68,
730
+
0x65,
731
+
0x6c,
732
+
0x6c,
733
+
0x6f, // field 1: "hello"
734
+
0x12,
735
+
0x06,
736
+
0x63,
737
+
0x75,
738
+
0x73,
739
+
0x74,
740
+
0x6f,
741
+
0x6d, // field 2: "custom"
742
+
0x18,
743
+
0x63, // field 3: varint 99
744
+
0x22,
745
+
0x07,
746
+
0x70,
747
+
0x72,
748
+
0x65,
749
+
0x73,
750
+
0x65,
751
+
0x6e,
752
+
0x74, // field 4: "present"
753
+
]),
754
+
},
755
+
{
756
+
data: { required: 'hello' },
757
+
expected: Uint8Array.from([0x0a, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f]), // field 1: "hello" only
758
+
},
759
+
{
760
+
data: {
761
+
required: 'hello',
762
+
withDefault: 'custom_value',
763
+
},
764
+
expected: Uint8Array.from([
765
+
0x0a,
766
+
0x05,
767
+
0x68,
768
+
0x65,
769
+
0x6c,
770
+
0x6c,
771
+
0x6f, // field 1: "hello"
772
+
0x12,
773
+
0x0c,
774
+
0x63,
775
+
0x75,
776
+
0x73,
777
+
0x74,
778
+
0x6f,
779
+
0x6d,
780
+
0x5f,
781
+
0x76,
782
+
0x61,
783
+
0x6c,
784
+
0x75,
785
+
0x65, // field 2: "custom_value"
786
+
]),
787
+
},
788
+
];
489
789
490
-
{
491
-
const partial = {
492
-
required: 'hello',
493
-
withDefault: 'custom_value',
494
-
};
790
+
for (const { data, expected } of cases) {
791
+
const encoded = p.encode(Message, data);
792
+
assertEquals(encoded, expected);
495
793
496
-
const encoded = p.encode(Message, partial);
497
794
const decoded = p.decode(Message, encoded);
498
795
499
-
assertEquals(decoded, {
500
-
required: 'hello',
501
-
withDefault: 'custom_value',
502
-
withFunctionDefault: 42,
503
-
// withoutDefault should be undefined (not present)
504
-
});
796
+
// Handle default values in decoded output
797
+
if (!data.withDefault && !data.withFunctionDefault && !data.withoutDefault) {
798
+
assertEquals(decoded, {
799
+
required: data.required,
800
+
withDefault: 'default_value',
801
+
withFunctionDefault: 42,
802
+
// withoutDefault should be undefined (not present)
803
+
});
804
+
} else if (!data.withFunctionDefault && !data.withoutDefault) {
805
+
assertEquals(decoded, {
806
+
required: data.required,
807
+
withDefault: data.withDefault,
808
+
withFunctionDefault: 42,
809
+
// withoutDefault should be undefined (not present)
810
+
});
811
+
} else {
812
+
assertEquals(decoded, data);
813
+
}
505
814
}
506
815
});
507
816
···
511
820
const encoded = p.encode(Message, {});
512
821
const decoded = p.decode(Message, encoded);
513
822
823
+
// Empty message should encode to empty buffer
824
+
assertEquals(encoded, new Uint8Array(0));
514
825
assertEquals(decoded, {});
515
826
});
516
827
517
828
Deno.test('nested messages', () => {
518
-
const Address = p.message({
519
-
street: p.string(),
520
-
city: p.string(),
521
-
zipCode: p.optional(p.string()),
522
-
}, {
523
-
street: 1,
524
-
city: 2,
525
-
zipCode: 3,
526
-
});
527
-
528
-
const Person = p.message({
529
-
name: p.string(),
530
-
age: p.int32(),
531
-
address: Address,
532
-
addresses: p.repeated(Address),
533
-
}, {
534
-
name: 1,
535
-
age: 2,
536
-
address: 3,
537
-
addresses: 4,
538
-
});
829
+
{
830
+
const Simple = p.message({
831
+
inner: p.optional(p.message({ value: p.int32() }, { value: 1 })),
832
+
}, { inner: 2 });
539
833
540
-
const data = {
541
-
name: 'John Doe',
542
-
age: 30,
543
-
address: {
544
-
street: '123 Main St',
545
-
city: 'Anytown',
546
-
zipCode: '12345',
547
-
},
548
-
addresses: [
834
+
const cases = [
549
835
{
550
-
street: '456 Oak Ave',
551
-
city: 'Other City',
836
+
data: { inner: { value: 42 } },
837
+
expected: Uint8Array.from([0x12, 0x02, 0x08, 0x2a]), // field 2: length 2, field 1: varint 42
552
838
},
553
839
{
554
-
street: '789 Pine Rd',
555
-
city: 'Another City',
556
-
zipCode: '67890',
840
+
data: {},
841
+
expected: new Uint8Array(0), // empty message
842
+
},
843
+
];
844
+
845
+
for (const { data, expected } of cases) {
846
+
const encoded = p.encode(Simple, data);
847
+
assertEquals(encoded, expected);
848
+
849
+
const decoded = p.decode(Simple, encoded);
850
+
assertEquals(decoded, data);
851
+
}
852
+
}
853
+
854
+
{
855
+
const Address = p.message({
856
+
street: p.string(),
857
+
city: p.string(),
858
+
zipCode: p.optional(p.string()),
859
+
}, {
860
+
street: 1,
861
+
city: 2,
862
+
zipCode: 3,
863
+
});
864
+
865
+
const Person = p.message({
866
+
name: p.string(),
867
+
age: p.int32(),
868
+
address: Address,
869
+
addresses: p.repeated(Address),
870
+
}, {
871
+
name: 1,
872
+
age: 2,
873
+
address: 3,
874
+
addresses: 4,
875
+
});
876
+
877
+
const data = {
878
+
name: 'John Doe',
879
+
age: 30,
880
+
address: {
881
+
street: '123 Main St',
882
+
city: 'Anytown',
883
+
zipCode: '12345',
557
884
},
558
-
],
559
-
};
885
+
addresses: [
886
+
{
887
+
street: '456 Oak Ave',
888
+
city: 'Other City',
889
+
},
890
+
{
891
+
street: '789 Pine Rd',
892
+
city: 'Another City',
893
+
zipCode: '67890',
894
+
},
895
+
],
896
+
};
560
897
561
-
const encoded = p.encode(Person, data);
562
-
const decoded = p.decode(Person, encoded);
898
+
const encoded = p.encode(Person, data);
899
+
const decoded = p.decode(Person, encoded);
563
900
564
-
assertEquals(decoded, data);
901
+
assertEquals(decoded, data);
902
+
}
565
903
});
566
904
567
905
Deno.test('self-referential messages', () => {
···
817
1155
const decoded = p.decode(Message, buffer);
818
1156
819
1157
assertEquals(decoded, { value: 24 });
820
-
});
821
-
822
-
Deno.test('encoding produces correct wire format', () => {
823
-
// Test that our encoding matches expected protobuf wire format
824
-
const schema = p.message({
825
-
a: p.int32(),
826
-
b: p.string(),
827
-
}, {
828
-
a: 1,
829
-
b: 2,
830
-
});
831
-
832
-
const encoded = p.encode(schema, { a: 150, b: 'testing' });
833
-
834
-
// Manual verification of wire format:
835
-
// Field 1 (a=150): tag=1<<3|0=8, value=150 (varint) = [8, 150, 1]
836
-
// Field 2 (b="testing"): tag=2<<3|2=18, length=7, "testing" = [18, 7, 116, 101, 115, 116, 105, 110, 103]
837
-
838
-
const expected = Uint8Array.from([
839
-
8,
840
-
150,
841
-
1, // field 1: int32 value 150
842
-
18,
843
-
7,
844
-
116,
845
-
101,
846
-
115,
847
-
116,
848
-
105,
849
-
110,
850
-
103, // field 2: string "testing"
851
-
]);
852
-
853
-
assertEquals(encoded, expected);
854
1158
});
855
1159
856
1160
// #endregion