hyperpubee
Version:
Self-publishing over the decentralised internet
374 lines (314 loc) • 12.6 kB
JavaScript
const { strict: nodeAssert } = require('assert')
const { expect } = require('chai')
const PubeeBuilder = require('../lib/creation/pubee-builder')
const {
CONTENT,
EMPTY,
STRUCTURE,
ROOT,
EMBEDDING_HASH,
EMBEDDING_LOCATION,
EMBEDDING,
EMBEDDING_PATCH,
METADATA,
LINK
} = require('../lib/core/constants')
const hexlexi = require('../lib/core/hexlexi')
const { createArrayFromStream, getAbsoluteKey } = require('../lib/core/utils')
const { hyperInterfaceFactory } = require('./fixtures')
describe('Test pubeebuilder', function () {
let builder
let index0, index1
let hyperbee
let hyperInterface
this.beforeEach(async function () {
hyperInterface = await hyperInterfaceFactory()
hyperbee = await hyperInterface.createHyperbee('testbee')
builder = new PubeeBuilder(hyperbee)
index0 = hexlexi.pack(0)
index1 = hexlexi.pack(1)
})
this.afterEach(async function () {
await hyperInterface.close()
})
it('throws an error when passing a non-empty bee to init', async function () {
await hyperbee.put('something', 'which makes it non-empty')
expect(() => new PubeeBuilder(hyperbee)).to.throw(
'Cannot init a pubeebuilder with a non-empty bee'
)
})
it('throws an error when adding an empty content', async function () {
await nodeAssert.rejects(builder.addContent(''), {
message: 'Cannot add an empty content.'
})
})
it('throws an error when adding an empty structure', async function () {
await nodeAssert.rejects(builder.addStructure('name', []), {
message: 'Cannot add an empty structure.'
})
})
it('throws an error when adding a structure without name', async function () {
await nodeAssert.rejects(builder.addStructure('', ['mockChildKey']), {
message: 'Cannot add a structure without name.'
})
})
it('returns the correct hexlex-indexed key when adding content', async function () {
const firstKey = await builder.addContent('some content')
const secondKey = await builder.addContent('some more content')
const expectedKey1 = [CONTENT, EMPTY, hexlexi.pack(0)].join('')
const expectedKey2 = [CONTENT, EMPTY, hexlexi.pack(1)].join('')
expect(firstKey).to.equal(expectedKey1)
expect(secondKey).to.equal(expectedKey2)
})
it('returns the correct hexlex-indexed key when adding content asyncronously', async function () {
const firstKeyPromise = builder.addContent('some content')
const secondKeyPromise = builder.addContent('some more content')
const firstKey = await firstKeyPromise
const secondKey = await secondKeyPromise
const expectedKey1 = [CONTENT, EMPTY, hexlexi.pack(0)].join('')
const expectedKey2 = [CONTENT, EMPTY, hexlexi.pack(1)].join('')
expect(firstKey).to.equal(expectedKey1)
expect(secondKey).to.equal(expectedKey2)
})
describe('Test structure adding', function () {
let contentKey1
let contentKey2
let expectedStructKey1
let expectedStructKey2
let expectedStructKey3
this.beforeEach(async function () {
contentKey1 = await builder.addContent('some content')
contentKey2 = await builder.addContent('some more content')
expectedStructKey1 = [
STRUCTURE,
EMPTY,
'struct1',
EMPTY,
hexlexi.pack(0)
].join('')
expectedStructKey2 = [
STRUCTURE,
EMPTY,
'struct1',
EMPTY,
hexlexi.pack(1)
].join('')
expectedStructKey3 = [
STRUCTURE,
EMPTY,
'highLvlStruct',
EMPTY,
hexlexi.pack(0)
].join('')
})
it('returns the correct hexlex-indexed key when adding structure', async function () {
const structKey1 = await builder.addStructure('struct1', [contentKey1])
const structKey2 = await builder.addStructure('struct1', [contentKey2])
const structKey3 = await builder.addStructure('highLvlStruct', [
structKey1,
structKey2
])
expect(structKey1).to.equal(expectedStructKey1)
expect(structKey2).to.equal(expectedStructKey2)
expect(structKey3).to.equal(expectedStructKey3)
})
it('returns the correct hexlex-indexed key when adding structure asyncronously', async function () {
const promises = [
builder.addStructure('struct1', [contentKey1]),
builder.addStructure('struct1', [contentKey2])
]
const structKey1 = await promises[0]
const structKey2 = await promises[1]
const structKey3 = await builder.addStructure('highLvlStruct', [
structKey1,
structKey2
])
expect(structKey1).to.equal(expectedStructKey1)
expect(structKey2).to.equal(expectedStructKey2)
expect(structKey3).to.equal(expectedStructKey3)
})
it('can add the root', async function () {
const structKey1 = await builder.addStructure('struct1', [contentKey1])
const rootKey = await builder.addRoot([structKey1])
const expectedRootKey = [STRUCTURE, EMPTY, ROOT].join('')
expect(rootKey).to.equal(expectedRootKey)
})
it('cannot add the root twice', async function () {
const structKey1 = await builder.addStructure('struct1', [contentKey1])
await builder.addRoot([structKey1])
await nodeAssert.rejects(builder.addRoot([structKey1]), {
message: 'Cannot add the root more than once.'
})
})
})
describe('Test build method', function () {
let contentKey1
let contentKey2
let structKey1
let structKey2
let highLvlStructKey
this.beforeEach(async function () {
contentKey1 = await builder.addContent('content nr 1')
contentKey2 = await builder.addContent('content nr 2')
structKey1 = await builder.addStructure('struct1', [contentKey1])
structKey2 = await builder.addStructure('struct1', [contentKey2])
highLvlStructKey = await builder.addStructure('highLvlStruct', [
structKey1,
structKey2
])
await builder.addMetadata({})
})
it('throws a validation error when building an empty bee', async function () {
hyperbee = await hyperInterface.createHyperbee('emptyBee')
builder = new PubeeBuilder(hyperbee)
await nodeAssert.rejects(builder.build(), {
name: 'ValidationError'
})
})
it('returns the correct hyperbee when building', async function () {
await builder.addRoot([highLvlStructKey])
const pubee = await builder.build()
const bee = await pubee.getReadOnlyBee()
const content = bee.sub(CONTENT)
const { value: content0 } = await content.get(hexlexi.pack(0))
const { value: content1 } = await content.get(hexlexi.pack(1))
const allContent = await createArrayFromStream(
content.createReadStream()
)
expect(content0).to.equal('content nr 1')
expect(content1).to.equal('content nr 2')
expect(allContent.length).to.equal(2) // Ensure: no additional values
const structureSub = bee.sub(STRUCTURE)
const struct1Sub = structureSub.sub('struct1')
const { value: lowLvlStruct1 } = await struct1Sub.get(hexlexi.pack(0))
const { value: lowLvlStruct2 } = await struct1Sub.get(hexlexi.pack(1))
const { value: highLvlStruct } = await structureSub
.sub('highLvlStruct')
.get(hexlexi.pack(0))
const { value: root } = await structureSub.get(ROOT)
const allStructures = await createArrayFromStream(
structureSub.createReadStream()
)
expect(lowLvlStruct1).to.deep.equal([contentKey1])
expect(lowLvlStruct2).to.deep.equal([contentKey2])
expect(highLvlStruct).to.deep.equal([structKey1, structKey2])
expect(root).to.deep.equal([highLvlStructKey])
expect(allStructures.length).to.equal(4) // Ensure: no additional values
const allEntries = await createArrayFromStream(bee.createReadStream())
expect(allEntries.length).to.equal(4 + 2 + 1) // 4 struct, 2 content, 1 metadata
})
it('can build a hyperbee with embeddings', async function () {
const embedding = {}
embedding[EMBEDDING_HASH] = 'a'.repeat(64)
embedding[EMBEDDING_LOCATION] = getAbsoluteKey(CONTENT, index0)
embedding[EMBEDDING_PATCH] = []
const embeddingKey = await builder.addEmbedding(embedding)
await builder.addRoot([highLvlStructKey, embeddingKey])
const pubee = await builder.build()
const bee = await pubee.getReadOnlyBee()
const { value: extractedEmbedding } = await bee
.sub(EMBEDDING)
.get(index0)
expect(extractedEmbedding.referencedHash).to.equal(
embedding[EMBEDDING_HASH]
)
expect(extractedEmbedding.referencedLocation).to.equal(
embedding[EMBEDDING_LOCATION]
)
})
it('can build a hyperbee with patched embeddings', async function () {
const embedding = {}
embedding[EMBEDDING_HASH] = 'a'.repeat(64)
embedding[EMBEDDING_LOCATION] = getAbsoluteKey(CONTENT, index0)
embedding[EMBEDDING_PATCH] = [[-1, 'Goo'], [1, 'Ba'], [0, 'd dog']]
const embeddingKey = await builder.addEmbedding(embedding)
await builder.addRoot([highLvlStructKey, embeddingKey])
const pubee = await builder.build()
const bee = await pubee.getReadOnlyBee()
const { value: extractedEmbedding } = await bee
.sub(EMBEDDING)
.get(index0)
expect(extractedEmbedding.referencedHash).to.equal(
embedding[EMBEDDING_HASH]
)
expect(extractedEmbedding.referencedLocation).to.equal(
embedding[EMBEDDING_LOCATION]
)
expect(extractedEmbedding.patch).to.deep.equal(
[[-1, 'Goo'], [1, 'Ba'], [0, 'd dog']]
)
})
it('Can build a hyperpubee with a link', async function () {
const link = {
linkedHash: 'a'.repeat(64),
linkedLocation: getAbsoluteKey(CONTENT, index0)
}
const linkKey = await builder.addLink(link)
await builder.addRoot([highLvlStructKey, linkKey])
const pubee = await builder.build()
const bee = await pubee.getReadOnlyBee()
const { value: extractedLink } = await bee
.sub(LINK)
.get(index0)
expect(extractedLink.linkedHash).to.equal(
'a'.repeat(64)
)
expect(extractedLink.linkedLocation).to.equal(
getAbsoluteKey(CONTENT, index0)
)
})
})
describe('test Link builder logic', async function () {
let link
this.beforeEach(function () {
link = {
linkedHash: 'a'.repeat(64),
linkedLocation: getAbsoluteKey(CONTENT, index0)
}
})
it('Returns the correct hexlexed location when adding a link', async function () {
const key = await builder.addLink(link)
const key2 = await builder.addLink(link)
expect(key).to.equal(getAbsoluteKey(LINK, index0))
expect(key2).to.equal(getAbsoluteKey(LINK, index1))
})
})
describe('test addEmbedding', async function () {
it('Returns the correct hexlexed location when adding an embedding', async function () {
const embedding = {}
embedding[EMBEDDING_HASH] = 'a'.repeat(64)
embedding[EMBEDDING_LOCATION] = getAbsoluteKey(CONTENT, index0)
embedding[EMBEDDING_PATCH] = []
const embeddingKey = await builder.addEmbedding(embedding)
expect(embeddingKey).to.equal(getAbsoluteKey(EMBEDDING, index0))
})
})
describe('Test metadata', function () {
it('throws a validation error when building without metadata', async function () {
hyperbee = await hyperInterface.createHyperbee('testbee')
builder = new PubeeBuilder(hyperbee)
const key = await builder.addContent('My content')
await builder.addRoot([key])
await nodeAssert.rejects(builder.build(), {
message: 'A pubee must have a metadata key'
})
})
it('Correctly adds metadata', async function () {
hyperbee = await hyperInterface.createHyperbee('testbee')
builder = new PubeeBuilder(hyperbee)
const key = await builder.addContent('My content')
await builder.addRoot([key])
await builder.addMetadata({ author: 'someone' })
const bee = await builder.build()
expect((await bee.get(METADATA))).to.deep.equal({ author: 'someone' })
})
it('throws when adding metadata twice', async function () {
hyperbee = await hyperInterface.createHyperbee('testbee')
builder = new PubeeBuilder(hyperbee)
await builder.addMetadata({})
await nodeAssert.rejects(builder.addMetadata({}), {
message: 'Metadata can only be added once'
})
})
})
})