@dtudury/streamo 1.0.1 → 3.0.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.
@@ -1,90 +0,0 @@
1
- /**
2
- * Universal test utility for stream/ tests.
3
- *
4
- * In Node.js: wraps node:test describe/test — tests actually execute via `node --test`
5
- * In browser: no-op for now; browser test runner to be rebuilt as a stream module
6
- *
7
- * Usage in test files:
8
- *
9
- * import { describe, assert } from './utils/testing.js'
10
- *
11
- * describe(import.meta.url, ({ test }) => {
12
- * test('my case', () => {
13
- * assert.equal(actual, expected)
14
- * })
15
- * test('async case', async () => {
16
- * const result = await someAsyncThing()
17
- * assert.ok(result)
18
- * })
19
- * })
20
- */
21
-
22
- const IS_NODE = typeof process !== 'undefined' && process.versions?.node != null
23
-
24
- // ── Assertions ────────────────────────────────────────────────────────────
25
- // Throws on failure, works in any environment.
26
-
27
- class AssertionError extends Error {
28
- constructor (message) {
29
- super(message)
30
- this.name = 'AssertionError'
31
- }
32
- }
33
-
34
- function fmt (v) {
35
- try {
36
- if (v instanceof Uint8Array) return `Uint8Array[${[...v]}]`
37
- return JSON.stringify(v)
38
- } catch {
39
- return String(v)
40
- }
41
- }
42
-
43
- export const assert = {
44
- ok (val, msg) {
45
- if (!val) throw new AssertionError(msg ?? `expected truthy, got ${fmt(val)}`)
46
- },
47
- equal (actual, expected, msg) {
48
- if (actual !== expected) throw new AssertionError(msg ?? `${fmt(actual)} !== ${fmt(expected)}`)
49
- },
50
- notEqual (actual, expected, msg) {
51
- if (actual === expected) throw new AssertionError(msg ?? `expected values to differ, both were ${fmt(actual)}`)
52
- },
53
- deepEqual (actual, expected, msg) {
54
- if (fmt(actual) !== fmt(expected)) throw new AssertionError(msg ?? `${fmt(actual)} !== ${fmt(expected)}`)
55
- },
56
- throws (fn, msg) {
57
- let threw = false
58
- try { fn() } catch { threw = true }
59
- if (!threw) throw new AssertionError(msg ?? 'expected function to throw')
60
- }
61
- }
62
-
63
- // ── describe / test ───────────────────────────────────────────────────────
64
-
65
- let _impl
66
-
67
- if (IS_NODE) {
68
- const { describe: nodeDescribe, test: nodeTest } = await import('node:test')
69
- _impl = {
70
- describe (name, fn) {
71
- nodeDescribe(name, () => fn({
72
- test: (testName, testFn) => nodeTest(testName, () => testFn({ assert }))
73
- }))
74
- }
75
- }
76
- } else {
77
- // Browser test runner: TODO rebuild as a first-class stream module
78
- _impl = { describe () {} }
79
- }
80
-
81
- /**
82
- * Declare a group of tests. Pass import.meta.url as the name so the
83
- * test runner can show which file each group came from.
84
- *
85
- * @param {string} name typically import.meta.url
86
- * @param {function({ test: function }): void} fn
87
- */
88
- export function describe (name, fn) {
89
- _impl.describe(name, fn)
90
- }
package/smoke.test.js DELETED
@@ -1,132 +0,0 @@
1
- import { describe } from './public/streamo/utils/testing.js'
2
- import { Streamo } from './public/streamo/Streamo.js'
3
- import { Repo } from './public/streamo/Repo.js'
4
- import { RepoRegistry } from './public/streamo/RepoRegistry.js'
5
- import { archiveSync } from './public/streamo/archiveSync.js'
6
- import { webSync } from './public/streamo/webSync.js'
7
- import { Signer } from './public/streamo/Signer.js'
8
- import WebSocket from 'ws'
9
- import { rm, mkdtemp } from 'fs/promises'
10
- import { tmpdir } from 'os'
11
- import { join } from 'path'
12
-
13
- // 1 iteration keeps key derivation fast without compromising what we're testing
14
- const KEY_ITERATIONS = 1
15
- const toHex = bytes => Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('')
16
-
17
- async function makeKey (name = 'smoke') {
18
- const signer = new Signer('test', 'test', KEY_ITERATIONS)
19
- const { publicKey } = await signer.keysFor(name)
20
- return { signer, publicKey, publicKeyHex: toHex(publicKey) }
21
- }
22
-
23
- async function startServer (publicKeyHex, stream) {
24
- const registry = new RepoRegistry(() => stream)
25
- const server = await webSync(registry, publicKeyHex, 0, 'smoke-test', KEY_ITERATIONS)
26
- const { port } = server.address()
27
- const close = () => new Promise(resolve => server.close(resolve))
28
- return { port, close }
29
- }
30
-
31
- describe(import.meta.url, ({ test }) => {
32
- test('GET /api/info returns primaryKeyHex and name', async ({ assert }) => {
33
- const { publicKeyHex } = await makeKey()
34
- const { port, close } = await startServer(publicKeyHex, new Streamo())
35
- try {
36
- const info = await fetch(`http://localhost:${port}/api/info`).then(r => r.json())
37
- assert.equal(info.primaryKeyHex, publicKeyHex)
38
- assert.equal(info.name, 'smoke-test')
39
- } finally {
40
- await close()
41
- }
42
- })
43
-
44
- test('GET /streams/:key/raw loads into a fresh Streamo', async ({ assert }) => {
45
- const { publicKeyHex } = await makeKey()
46
- const stream = new Streamo()
47
- stream.set({ hello: 'world' })
48
- const { port, close } = await startServer(publicKeyHex, stream)
49
- try {
50
- const buf = await fetch(`http://localhost:${port}/streams/${publicKeyHex}/raw`)
51
- .then(r => r.arrayBuffer())
52
- const fresh = new Streamo()
53
- await fresh.makeWritableStream().getWriter().write(new Uint8Array(buf))
54
- assert.deepEqual(fresh.get(), { hello: 'world' })
55
- } finally {
56
- await close()
57
- }
58
- })
59
-
60
- test('WebSocket syncs existing chunks to a connecting client', async ({ assert }) => {
61
- const { publicKeyHex } = await makeKey()
62
- const stream = new Streamo()
63
- stream.set({ synced: true })
64
- const { port, close } = await startServer(publicKeyHex, stream)
65
- try {
66
- const client = new Streamo()
67
- const writer = client.makeWritableStream().getWriter()
68
- const ws = new WebSocket(`ws://localhost:${port}`)
69
-
70
- await new Promise((resolve, reject) => {
71
- ws.on('open', () => ws.send(publicKeyHex))
72
- ws.on('message', async data => {
73
- await writer.write(new Uint8Array(data))
74
- if (client.byteLength >= stream.byteLength) resolve()
75
- })
76
- ws.on('error', reject)
77
- setTimeout(() => reject(new Error('WS sync timed out')), 2000)
78
- })
79
-
80
- ws.close()
81
- assert.deepEqual(client.get(), { synced: true })
82
- } finally {
83
- await close()
84
- }
85
- })
86
-
87
- test('set() with a path works when root is VARIABLE-encoded (old server data)', ({ assert }) => {
88
- // Old server archives encoded the root value as VARIABLE (a boxed address).
89
- // asRefs() previously returned the VARIABLE address number rather than the
90
- // inner object's refs, causing refs['toggle'] === undefined → crash.
91
- const stream = new Streamo()
92
- // encodeVariable to simulate old-style encoded root
93
- const rootCode = stream.encodeVariable({ toggle: { value: false, label: 'enabled' }, counter: { value: 0 } })
94
- stream.append(rootCode)
95
- // get() should see through VARIABLE
96
- assert.equal(stream.get('toggle', 'value'), false)
97
- // set() with a 2-level path must not crash
98
- stream.set('toggle', 'value', true)
99
- assert.equal(stream.get('toggle', 'value'), true)
100
- assert.equal(stream.get('counter', 'value'), 0)
101
- })
102
-
103
- test('set() after sign() reads/writes user data, not the signature chunk', async ({ assert }) => {
104
- const { signer } = await makeKey()
105
- const stream = new Streamo()
106
- stream.set({ count: 0 })
107
- await stream.sign(signer, 'smoke')
108
- // Before the fix, set() with a path would crash here because byteLength - 1
109
- // pointed to the signature chunk instead of the data chunk.
110
- stream.set('count', stream.get('count') + 1)
111
- assert.equal(stream.get('count'), 1)
112
- // get() must also skip past the signature
113
- assert.deepEqual(stream.get(), { count: 1 })
114
- })
115
-
116
- test('archiveSync persists data and reloads it on a fresh Streamo', async ({ assert }) => {
117
- const { publicKeyHex } = await makeKey('archive')
118
- const dir = await mkdtemp(join(tmpdir(), 'smoke-'))
119
- try {
120
- const stream1 = new Streamo()
121
- await archiveSync(stream1, dir, publicKeyHex)
122
- stream1.set({ persisted: true })
123
- await new Promise(r => setTimeout(r, 100)) // let write loop flush
124
-
125
- const stream2 = new Streamo()
126
- await archiveSync(stream2, dir, publicKeyHex)
127
- assert.deepEqual(stream2.get(), { persisted: true })
128
- } finally {
129
- await rm(dir, { recursive: true, force: true })
130
- }
131
- })
132
- })