From 4339ace8cd9ef7298bc646075d0d733a38a1a034 Mon Sep 17 00:00:00 2001 From: HDegroote <75906619+HDegroote@users.noreply.github.com> Date: Wed, 15 Jan 2025 10:32:49 +0100 Subject: [PATCH 1/5] Port existing snapshot test to separate file --- test/all.js | 1 + test/core.js | 25 ------------------------- test/snapshot.js | 27 +++++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 25 deletions(-) create mode 100644 test/snapshot.js diff --git a/test/all.js b/test/all.js index 3b5361f..1d484a8 100644 --- a/test/all.js +++ b/test/all.js @@ -10,6 +10,7 @@ async function runTests () { await import('./atomic.js') await import('./basic.js') await import('./core.js') + await import('./snapshot.js') await import('./streams.js') test.resume() diff --git a/test/core.js b/test/core.js index b19ca46..5d61a18 100644 --- a/test/core.js +++ b/test/core.js @@ -69,31 +69,6 @@ test('read and write hypercore blocks across multiple cores', async (t) => { await storage.close() }) -test('read and write hypercore blocks from snapshot', async (t) => { - const core = await createCore(t) - await writeBlocks(core, 2) - - const snap = core.snapshot() - await writeBlocks(core, 2, { start: 2 }) - - { - const rx = snap.read() - const proms = [rx.getBlock(0), rx.getBlock(1), rx.getBlock(2)] - rx.tryFlush() - const res = await Promise.all(proms) - t.is(b4a.toString(res[0]), 'block0') - t.is(b4a.toString(res[1]), 'block1') - t.is(res[2], null) - } - - { - const rx = core.read() - const p = rx.getBlock(2) - rx.tryFlush() - t.is(b4a.toString(await p), 'block2', 'sanity check: does exist in non-snapshot core') - } -}) - test('delete hypercore block', async (t) => { const core = await createCore(t) await writeBlocks(core, 2) diff --git a/test/snapshot.js b/test/snapshot.js new file mode 100644 index 0000000..a7f10a7 --- /dev/null +++ b/test/snapshot.js @@ -0,0 +1,27 @@ +const test = require('brittle') +const b4a = require('b4a') +const { + createCore, + writeBlocks, + readBlocks +} = require('./helpers') + +test('read and write hypercore blocks from snapshot', async (t) => { + const core = await createCore(t) + await writeBlocks(core, 2) + + const snap = core.snapshot() + await writeBlocks(core, 2, { start: 2 }) + + { + const res = await readBlocks(snap, 3) + t.is(b4a.toString(res[0]), 'block0') + t.is(b4a.toString(res[1]), 'block1') + t.is(res[2], null) + } + + { + const res = await readBlocks(core, 3) + t.is(b4a.toString(res[2]), 'block2', 'sanity check: does exist in non-snapshot core') + } +}) From 276450de3fd9b44e97def4c3e923f10e1a17e0d9 Mon Sep 17 00:00:00 2001 From: HDegroote <75906619+HDegroote@users.noreply.github.com> Date: Wed, 15 Jan 2025 10:45:40 +0100 Subject: [PATCH 2/5] Add failing 'cannot write to snapshot' test --- test/snapshot.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/snapshot.js b/test/snapshot.js index a7f10a7..55342b3 100644 --- a/test/snapshot.js +++ b/test/snapshot.js @@ -25,3 +25,16 @@ test('read and write hypercore blocks from snapshot', async (t) => { t.is(b4a.toString(res[2]), 'block2', 'sanity check: does exist in non-snapshot core') } }) + +test.solo('cannot write to snapshot', async (t) => { + const core = await createCore(t) + + const snap = core.snapshot() + await writeBlocks(snap, 2) + + // TODO: verify that writing to a snapshot shouldn't just throw + t.alike(await readBlocks(snap, 3), [null, null, null], 'Cannot write to a snaphot (noop)') + + // TODO: clarify ([ , , null ]) + t.alike(await readBlocks(core, 3), [null, null, null], 'Writing to a snapshot has no impact on actual core') +}) From 925245d86e32b8e504cd576404971c86d65b9b6a Mon Sep 17 00:00:00 2001 From: HDegroote <75906619+HDegroote@users.noreply.github.com> Date: Wed, 15 Jan 2025 11:06:01 +0100 Subject: [PATCH 3/5] Add test for snapshots of atom cores --- test/snapshot.js | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/test/snapshot.js b/test/snapshot.js index 55342b3..381ff95 100644 --- a/test/snapshot.js +++ b/test/snapshot.js @@ -26,7 +26,7 @@ test('read and write hypercore blocks from snapshot', async (t) => { } }) -test.solo('cannot write to snapshot', async (t) => { +test.skip('cannot write to snapshot', async (t) => { const core = await createCore(t) const snap = core.snapshot() @@ -38,3 +38,43 @@ test.solo('cannot write to snapshot', async (t) => { // TODO: clarify ([ , , null ]) t.alike(await readBlocks(core, 3), [null, null, null], 'Writing to a snapshot has no impact on actual core') }) + +test('snapshots from atomized core do not get updated', async (t) => { + const core = await createCore(t) + const atom = core.createAtom() + const atomCore = core.atomize(atom) + + const atomInitSnap = atomCore.snapshot() + const initSnap = core.snapshot() + t.alike(await readBlocks(initSnap, 2), [null, null], 'sanity check') + + await writeBlocks(atomCore, 2) + const expected = [b4a.from('block0'), b4a.from('block1')] + + t.alike(await readBlocks(atomInitSnap, 2), [null, null], 'init atom snap did not change') + t.alike(await readBlocks(initSnap, 2), [null, null], 'init snap did not change') + + const atomPostWriteSnap = atomCore.snapshot() + const corePostWriteSnap = core.snapshot() + + t.alike(await readBlocks(atomPostWriteSnap, 2), expected, 'sanity check') + t.alike(await readBlocks(corePostWriteSnap, 2), [null, null], 'sanity check') + t.alike(await readBlocks(atomInitSnap, 2), [null, null], 'init atom snap did not change') + t.alike(await readBlocks(initSnap, 2), [null, null], 'init snap did not change') + + await writeBlocks(atomCore, 2, { pre: 'override-' }) + const expectedOverride = [b4a.from('override-block0'), b4a.from('override-block1')] + t.alike(await readBlocks(atomCore, 2), expectedOverride, 'sanity check') + + t.alike(await readBlocks(atomPostWriteSnap, 2), expected, 'post-write atom snap did not change') + t.alike(await readBlocks(atomInitSnap, 2), [null, null], 'init atom snap did not change') + + await atom.flush() + + t.alike(await readBlocks(atomPostWriteSnap, 2), expected, 'prev atom snap did not change') + t.alike(await readBlocks(atomInitSnap, 2), [null, null], 'init atom snap did not change') + t.alike(await readBlocks(corePostWriteSnap, 2), [null, null], 'core post-write snap did not change') + t.alike(await readBlocks(initSnap, 2), [null, null], 'init snap did not change') + + t.alike(await readBlocks(core, 2), expectedOverride, 'sanity check') +}) From 8666f8a41aa03db5460bb33455514be9aeb47128 Mon Sep 17 00:00:00 2001 From: HDegroote <75906619+HDegroote@users.noreply.github.com> Date: Wed, 15 Jan 2025 11:31:20 +0100 Subject: [PATCH 4/5] Add test for immutable snapshot (all operations) --- test/snapshot.js | 92 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/test/snapshot.js b/test/snapshot.js index 381ff95..8882e15 100644 --- a/test/snapshot.js +++ b/test/snapshot.js @@ -3,7 +3,14 @@ const b4a = require('b4a') const { createCore, writeBlocks, - readBlocks + readBlocks, + readTreeNodes, + getAuth, + getHead, + getDependency, + getHints, + getUserData, + getBitfieldPages } = require('./helpers') test('read and write hypercore blocks from snapshot', async (t) => { @@ -78,3 +85,86 @@ test('snapshots from atomized core do not get updated', async (t) => { t.alike(await readBlocks(core, 2), expectedOverride, 'sanity check') }) + +test('snapshots immutable (all operations)', async (t) => { + const core = await createCore(t) + await writeBlocks(core, 2) + + const snap = core.snapshot() + + { + await writeBlocks(core, 2, { start: 2 }) + + const tx = core.write() + tx.putTreeNode({ + index: 0, + size: 1, + hash: b4a.from('a'.repeat(64), 'hex') + }) + tx.setAuth({ + key: b4a.alloc(32), + discoveryKey: b4a.alloc(32), + manifest: null, + keyPair: null, + encryptionKey: b4a.from('a'.repeat(64, 'hex')) + }) + tx.setHead({ + fork: 1, + length: 3, + rootHash: b4a.from('a'.repeat(64), 'hex'), + signature: b4a.from('b'.repeat(64), 'hex') + }) + tx.setDependency({ + dataPointer: 1, + length: 3 + }) + tx.setHints({ + contiguousLength: 1 + }) + tx.putUserData('key', b4a.from('value')) + tx.putBitfieldPage(0, b4a.from('bitfield-data-1')) + + await tx.flush() + } + + t.alike( + await readBlocks(core, 4), + [b4a.from('block0'), b4a.from('block1'), b4a.from('block2'), b4a.from('block3')], + 'sanity check' + ) + + t.not(await readBlocks(core, 3), [null, null, null], 'sanity check (core itself got updated') + t.alike( + await readBlocks(snap, 4), + [b4a.from('block0'), b4a.from('block1'), null, null], + 'snap blocks unchanged' + ) + + t.not(await readTreeNodes(core, 2), [null, null], 'sanity check (core itself got updated)') + t.alike(await readTreeNodes(snap, 2), [null, null], 'tree nodes unchanged') + + const origAuth = { + key: b4a.alloc(32), + discoveryKey: b4a.alloc(32), + manifest: null, + keyPair: null, + encryptionKey: null + } + t.not(await getAuth(core), origAuth, 'sanity check (core itself got updated)') + t.alike(await getAuth(snap), origAuth, 'auth unchanged') + + t.not(await getHead(core), null, 'sanity check (core itself got updated)') + t.alike(await getHead(snap), null, 'head unchanged') + + t.not(await getDependency(core), null, 'sanity check (core itself got updated)') + t.alike(await getDependency(snap), null, 'dependency unchanged') + + t.not(await getHints(core), null, 'sanity check (core itself got updated)') + t.alike(await getHints(snap), null, 'hints unchanged') + + t.not(await getUserData(core, 'key'), null, 'sanity check (core itself got updated)') + t.alike(await getUserData(snap, 'key'), null, 'userdata unchanged') + + t.not(await getBitfieldPages(core, 2), [null, null], 'sanity check (core itself got updated)') + t.alike(await getBitfieldPages(snap, 2), [null, null], 'bitfield pages unchanged') +}) From 73f54e00ad8589167a4c43f973b9de04d696bbbe Mon Sep 17 00:00:00 2001 From: HDegroote <75906619+HDegroote@users.noreply.github.com> Date: Wed, 15 Jan 2025 11:31:51 +0100 Subject: [PATCH 5/5] Rm cannot-write-to-snapshot test --- test/snapshot.js | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/test/snapshot.js b/test/snapshot.js index 8882e15..28dccf1 100644 --- a/test/snapshot.js +++ b/test/snapshot.js @@ -33,19 +33,6 @@ test('read and write hypercore blocks from snapshot', async (t) => { } }) -test.skip('cannot write to snapshot', async (t) => { - const core = await createCore(t) - - const snap = core.snapshot() - await writeBlocks(snap, 2) - - // TODO: verify that writing to a snapshot shouldn't just throw - t.alike(await readBlocks(snap, 3), [null, null, null], 'Cannot write to a snaphot (noop)') - - // TODO: clarify ([ , , null ]) - t.alike(await readBlocks(core, 3), [null, null, null], 'Writing to a snapshot has no impact on actual core') -}) - test('snapshots from atomized core do not get updated', async (t) => { const core = await createCore(t) const atom = core.createAtom()