Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add basic snapshot tests #61

Merged
merged 5 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions test/all.js
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
25 changes: 0 additions & 25 deletions test/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
157 changes: 157 additions & 0 deletions test/snapshot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
const test = require('brittle')
const b4a = require('b4a')
const {
createCore,
writeBlocks,
readBlocks,
readTreeNodes,
getAuth,
getHead,
getDependency,
getHints,
getUserData,
getBitfieldPages
} = 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')
}
})

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')
})

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')
})
Loading