this repo has no description
1import { test, describe } from 'node:test'
2import assert from 'node:assert'
3import {
4 cborEncode, createCid, cidToString, base32Encode, createTid,
5 generateKeyPair, importPrivateKey, sign, bytesToHex, hexToBytes
6} from '../src/pds.js'
7
8describe('CBOR Encoding', () => {
9 test('encodes simple map', () => {
10 const encoded = cborEncode({ hello: 'world', num: 42 })
11 // Expected: a2 65 68 65 6c 6c 6f 65 77 6f 72 6c 64 63 6e 75 6d 18 2a
12 const expected = new Uint8Array([
13 0xa2, 0x65, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x65, 0x77, 0x6f, 0x72, 0x6c, 0x64,
14 0x63, 0x6e, 0x75, 0x6d, 0x18, 0x2a
15 ])
16 assert.deepStrictEqual(encoded, expected)
17 })
18
19 test('encodes null', () => {
20 const encoded = cborEncode(null)
21 assert.deepStrictEqual(encoded, new Uint8Array([0xf6]))
22 })
23
24 test('encodes booleans', () => {
25 assert.deepStrictEqual(cborEncode(true), new Uint8Array([0xf5]))
26 assert.deepStrictEqual(cborEncode(false), new Uint8Array([0xf4]))
27 })
28
29 test('encodes small integers', () => {
30 assert.deepStrictEqual(cborEncode(0), new Uint8Array([0x00]))
31 assert.deepStrictEqual(cborEncode(1), new Uint8Array([0x01]))
32 assert.deepStrictEqual(cborEncode(23), new Uint8Array([0x17]))
33 })
34
35 test('encodes integers >= 24', () => {
36 assert.deepStrictEqual(cborEncode(24), new Uint8Array([0x18, 0x18]))
37 assert.deepStrictEqual(cborEncode(255), new Uint8Array([0x18, 0xff]))
38 })
39
40 test('encodes negative integers', () => {
41 assert.deepStrictEqual(cborEncode(-1), new Uint8Array([0x20]))
42 assert.deepStrictEqual(cborEncode(-10), new Uint8Array([0x29]))
43 })
44
45 test('encodes strings', () => {
46 const encoded = cborEncode('hello')
47 // 0x65 = text string of length 5
48 assert.deepStrictEqual(encoded, new Uint8Array([0x65, 0x68, 0x65, 0x6c, 0x6c, 0x6f]))
49 })
50
51 test('encodes byte strings', () => {
52 const bytes = new Uint8Array([1, 2, 3])
53 const encoded = cborEncode(bytes)
54 // 0x43 = byte string of length 3
55 assert.deepStrictEqual(encoded, new Uint8Array([0x43, 1, 2, 3]))
56 })
57
58 test('encodes arrays', () => {
59 const encoded = cborEncode([1, 2, 3])
60 // 0x83 = array of length 3
61 assert.deepStrictEqual(encoded, new Uint8Array([0x83, 0x01, 0x02, 0x03]))
62 })
63
64 test('sorts map keys deterministically', () => {
65 const encoded1 = cborEncode({ z: 1, a: 2 })
66 const encoded2 = cborEncode({ a: 2, z: 1 })
67 assert.deepStrictEqual(encoded1, encoded2)
68 // First key should be 'a' (0x61)
69 assert.strictEqual(encoded1[1], 0x61)
70 })
71})
72
73describe('Base32 Encoding', () => {
74 test('encodes bytes to base32lower', () => {
75 const bytes = new Uint8Array([0x01, 0x71, 0x12, 0x20])
76 const encoded = base32Encode(bytes)
77 assert.strictEqual(typeof encoded, 'string')
78 assert.match(encoded, /^[a-z2-7]+$/)
79 })
80})
81
82describe('CID Generation', () => {
83 test('creates CIDv1 with dag-cbor codec', async () => {
84 const data = cborEncode({ test: 'data' })
85 const cid = await createCid(data)
86
87 assert.strictEqual(cid.length, 36) // 2 prefix + 2 multihash header + 32 hash
88 assert.strictEqual(cid[0], 0x01) // CIDv1
89 assert.strictEqual(cid[1], 0x71) // dag-cbor
90 assert.strictEqual(cid[2], 0x12) // sha-256
91 assert.strictEqual(cid[3], 0x20) // 32 bytes
92 })
93
94 test('cidToString returns base32lower with b prefix', async () => {
95 const data = cborEncode({ test: 'data' })
96 const cid = await createCid(data)
97 const cidStr = cidToString(cid)
98
99 assert.strictEqual(cidStr[0], 'b')
100 assert.match(cidStr, /^b[a-z2-7]+$/)
101 })
102
103 test('same input produces same CID', async () => {
104 const data1 = cborEncode({ test: 'data' })
105 const data2 = cborEncode({ test: 'data' })
106 const cid1 = cidToString(await createCid(data1))
107 const cid2 = cidToString(await createCid(data2))
108
109 assert.strictEqual(cid1, cid2)
110 })
111
112 test('different input produces different CID', async () => {
113 const cid1 = cidToString(await createCid(cborEncode({ a: 1 })))
114 const cid2 = cidToString(await createCid(cborEncode({ a: 2 })))
115
116 assert.notStrictEqual(cid1, cid2)
117 })
118})
119
120describe('TID Generation', () => {
121 test('creates 13-character TIDs', () => {
122 const tid = createTid()
123 assert.strictEqual(tid.length, 13)
124 })
125
126 test('uses valid base32-sort characters', () => {
127 const tid = createTid()
128 assert.match(tid, /^[234567abcdefghijklmnopqrstuvwxyz]+$/)
129 })
130
131 test('generates monotonically increasing TIDs', () => {
132 const tid1 = createTid()
133 const tid2 = createTid()
134 const tid3 = createTid()
135
136 assert.ok(tid1 < tid2, `${tid1} should be less than ${tid2}`)
137 assert.ok(tid2 < tid3, `${tid2} should be less than ${tid3}`)
138 })
139
140 test('generates unique TIDs', () => {
141 const tids = new Set()
142 for (let i = 0; i < 100; i++) {
143 tids.add(createTid())
144 }
145 assert.strictEqual(tids.size, 100)
146 })
147})
148
149describe('P-256 Signing', () => {
150 test('generates key pair with correct sizes', async () => {
151 const kp = await generateKeyPair()
152
153 assert.strictEqual(kp.privateKey.length, 32)
154 assert.strictEqual(kp.publicKey.length, 33) // compressed
155 assert.ok(kp.publicKey[0] === 0x02 || kp.publicKey[0] === 0x03)
156 })
157
158 test('can sign data with generated key', async () => {
159 const kp = await generateKeyPair()
160 const key = await importPrivateKey(kp.privateKey)
161 const data = new TextEncoder().encode('test message')
162 const sig = await sign(key, data)
163
164 assert.strictEqual(sig.length, 64) // r (32) + s (32)
165 })
166
167 test('different messages produce different signatures', async () => {
168 const kp = await generateKeyPair()
169 const key = await importPrivateKey(kp.privateKey)
170
171 const sig1 = await sign(key, new TextEncoder().encode('message 1'))
172 const sig2 = await sign(key, new TextEncoder().encode('message 2'))
173
174 assert.notDeepStrictEqual(sig1, sig2)
175 })
176
177 test('bytesToHex and hexToBytes roundtrip', () => {
178 const original = new Uint8Array([0x00, 0x0f, 0xf0, 0xff, 0xab, 0xcd])
179 const hex = bytesToHex(original)
180 const back = hexToBytes(hex)
181
182 assert.strictEqual(hex, '000ff0ffabcd')
183 assert.deepStrictEqual(back, original)
184 })
185})