Skip to content

Commit 086e750

Browse files
committed
Added tests for base 128 encoding/decoding and lexicographic order
1 parent 1abd9ed commit 086e750

File tree

2 files changed

+105
-0
lines changed

2 files changed

+105
-0
lines changed

tests/utils.test.ts

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import type { KeyPath } from '@/types';
2+
import nodeCrypto from 'crypto';
23
import * as utils from '@/utils';
4+
import * as testUtils from './utils';
35

46
describe('utils', () => {
57
const keyPaths: Array<KeyPath> = [
@@ -91,4 +93,96 @@ describe('utils', () => {
9193
);
9294
},
9395
);
96+
test('base128 encoding/decoding', () => {
97+
// Base 128 alphabet is alphabetical and starts at 0x01 (skips 0x00)
98+
// it uses the same rfc4648 algorithm that base64 uses
99+
const table = [
100+
[[], []],
101+
[[0x00], [0x01, 0x01]],
102+
[[0x01], [0x01, 0x41]],
103+
[[0x02], [0x02, 0x01]],
104+
[[0x03], [0x02, 0x41]],
105+
[[0x04], [0x03, 0x01]],
106+
[[0x05], [0x03, 0x41]],
107+
[[0x06], [0x04, 0x01]],
108+
[[0x07], [0x04, 0x41]],
109+
[[0x08], [0x05, 0x01]],
110+
// Larger single bytes
111+
[[0xec], [0x77, 0x01]],
112+
[[0xed], [0x77, 0x41]],
113+
[[0xee], [0x78, 0x01]],
114+
[[0xef], [0x78, 0x41]],
115+
[[0xfe], [0x80, 0x01]],
116+
[[0xff], [0x80, 0x41]],
117+
// 2 bytes
118+
[
119+
[0x00, 0x00],
120+
[0x01, 0x01, 0x01],
121+
],
122+
[
123+
[0x00, 0x01],
124+
[0x01, 0x01, 0x21],
125+
],
126+
[
127+
[0xfe, 0x00],
128+
[0x80, 0x01, 0x01],
129+
],
130+
[
131+
[0xfe, 0x01],
132+
[0x80, 0x01, 0x21],
133+
],
134+
[
135+
[0xff, 0x00],
136+
[0x80, 0x41, 0x01],
137+
],
138+
[
139+
[0xff, 0x01],
140+
[0x80, 0x41, 0x21],
141+
],
142+
[
143+
[0xff, 0xff],
144+
[0x80, 0x80, 0x61],
145+
],
146+
];
147+
for (const [input, output] of table) {
148+
const inputEncoded = utils.encodePart(Buffer.from(input));
149+
expect(inputEncoded).toStrictEqual(Buffer.from(output));
150+
const inputEncodedDecoded = utils.decodePart(inputEncoded);
151+
expect(inputEncodedDecoded).toStrictEqual(Buffer.from(input));
152+
}
153+
});
154+
test('base128 preserves lexicographic order', () => {
155+
const parts: Array<[number, Buffer]> = Array.from(
156+
{ length: 1000 },
157+
(_, i) => [i, nodeCrypto.randomBytes(testUtils.getRandomInt(0, 101))],
158+
);
159+
const partsEncoded: Array<[number, Buffer]> = parts.map(([i, p]) => [
160+
i,
161+
utils.encodePart(p),
162+
]);
163+
parts.sort(([_i, a], [_j, b]) => Buffer.compare(a, b));
164+
partsEncoded.sort(([_i, a], [_j, b]) => Buffer.compare(a, b));
165+
expect(parts.map(([i]) => i)).toStrictEqual(partsEncoded.map(([i]) => i));
166+
for (const [j, [i, pE]] of partsEncoded.entries()) {
167+
expect([i, utils.decodePart(pE)]).toStrictEqual(parts[j]);
168+
}
169+
});
170+
test('Buffer.compare sorts byte by byte', () => {
171+
const arr = [
172+
Buffer.from([]),
173+
Buffer.from([0x01]),
174+
Buffer.from([0x00, 0x00, 0x00]),
175+
Buffer.from([0x00, 0x00]),
176+
];
177+
arr.sort(Buffer.compare);
178+
expect(arr).toStrictEqual([
179+
// Therefore empty buffer sorts first
180+
Buffer.from([]),
181+
Buffer.from([0x00, 0x00]),
182+
// Therefore `aa` is earlier than `aaa`
183+
Buffer.from([0x00, 0x00, 0x00]),
184+
// Therefore `aa` is earlier than `z`
185+
Buffer.from([0x01]),
186+
]);
187+
});
94188
});

tests/utils.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@ import { random, cipher, util as forgeUtil } from 'node-forge';
33
const ivSize = 16;
44
const authTagSize = 16;
55

6+
/**
7+
* Get a random integer between min and max
8+
* Returned value greater or equal to min and less than max
9+
*/
10+
function getRandomInt(min: number, max: number): number {
11+
min = Math.ceil(min);
12+
max = Math.floor(max);
13+
return Math.floor(Math.random() * (max - min) + min); // The maximum is exclusive and the minimum is inclusive
14+
}
15+
616
async function getRandomBytes(size: number): Promise<Buffer> {
717
const p = new Promise<string>((resolve, reject) => {
818
random.getBytes(size, (e, bytes) => {
@@ -108,6 +118,7 @@ function bytes2BigInt(bytes: Uint8Array): bigint {
108118
}
109119

110120
export {
121+
getRandomInt,
111122
getRandomBytes,
112123
getRandomBytesSync,
113124
generateKey,

0 commit comments

Comments
 (0)