chore: Check required params. (#46)

* chore: Check required params.

* fix: Linting.

* feat: Specify a list of peers with write access.

* test: Identify OrbitDB tests.

* test: IPFS access controller.

* test: Omit "OrbitDB" from test labels.

* test: Correctly set write options.

* fix: Linting.
This commit is contained in:
Hayden Young
2023-03-25 06:00:08 +08:00
committed by GitHub
parent 3ff24c298f
commit b9422463d0
11 changed files with 385 additions and 332 deletions

View File

@@ -77,8 +77,7 @@
"examples/**",
"benchmarks/**",
"test/fixtures/**",
"test/browser/**",
"test/access-controllers/**"
"test/browser/**"
]
},
"localMaintainers": [

View File

@@ -57,7 +57,7 @@ const OrbitDB = async ({ ipfs, id, identity, keystore, directory } = {}) => {
let databases = {}
const open = async (address, { type, meta, Store } = {}) => {
const open = async (address, { type, meta, write, Store } = {}) => {
let name, manifest, accessController
if (type && !databaseTypes[type]) {
@@ -75,14 +75,14 @@ const OrbitDB = async ({ ipfs, id, identity, keystore, directory } = {}) => {
const { value } = await Block.decode({ bytes, codec, hasher })
manifest = value
const acAddress = manifest.accessController.replaceAll('/ipfs/', '')
accessController = await IPFSAccessController({ ipfs, identities, identity, address: acAddress, storage: manifestStorage })
accessController = await IPFSAccessController({ ipfs, identities, identity, address: acAddress, storage: manifestStorage, write })
name = manifest.name
type = type || manifest.type
meta = manifest.meta
} else {
// If the address given was not valid, eg. just the name of the database
type = type || 'events'
accessController = await IPFSAccessController({ ipfs, identities, identity, storage: manifestStorage })
accessController = await IPFSAccessController({ ipfs, identities, identity, storage: manifestStorage, write })
const m = await DBManifest(manifestStorage, address, type, accessController.address, { meta })
manifest = m.manifest
address = OrbitDBAddress(m.hash)

View File

@@ -11,6 +11,11 @@ const hashStringEncoding = base58btc
// Creates a DB manifest file and saves it in IPFS
export default async (storage, name, type, accessControllerAddress, { meta } = {}) => {
if (!storage) throw new Error('storage is required')
if (!name) throw new Error('name is required')
if (!type) throw new Error('type is required')
if (!accessControllerAddress) throw new Error('accessControllerAddress is required')
const manifest = Object.assign(
{
name,

View File

@@ -4,5 +4,5 @@
"recursive": true,
"exit": true,
"slow": 1000,
"exclude": ["test/browser/**/*.js", "test/access-controllers/**/*.js"]
"exclude": ["test/browser/**/*.js"]
}

View File

@@ -1,128 +1,106 @@
// import assert from 'assert'
// import rmrf from 'rimraf'
// import OrbitDB from '../../src/OrbitDB.js'
// import IdentityProvider from 'orbit-db-identity-provider'
// import Keystore from 'orbit-db-keystore'
// import IPFSAccessController from 'orbit-db-access-controllers/ipfs'
// import AccessControllers from 'orbit-db-access-controllers'
import { strictEqual, deepStrictEqual, notStrictEqual } from 'assert'
import rmrf from 'rimraf'
import * as IPFS from 'ipfs'
import Keystore from '../../src/key-store.js'
import Identities from '../../src/identities/identities.js'
import IPFSAccessController from '../../src/access-controllers/ipfs.js'
import config from '../config.js'
import connectPeers from '../utils/connect-nodes.js'
// // Include test utilities
// import {
// config,
// startIpfs,
// stopIpfs,
// testAPIs
// } from 'orbit-db-test-utils'
describe('IPFSAccessController', function () {
const dbPath1 = './orbitdb/tests/ipfs-access-controller/1'
const dbPath2 = './orbitdb/tests/ipfs-access-controller/2'
// const dbPath1 = './orbitdb/tests/ipfs-access-controller/1'
// const dbPath2 = './orbitdb/tests/ipfs-access-controller/2'
this.timeout(config.timeout)
// Object.keys(testAPIs).forEach(API => {
// describe(`orbit-db - IPFSAccessController (${API})`, function () {
// this.timeout(config.timeout)
let ipfs1, ipfs2
let identities1, identities2
let testIdentity1, testIdentity2
// let ipfsd1, ipfsd2, ipfs1, ipfs2, id1, id2
// let orbitdb1, orbitdb2
before(async () => {
ipfs1 = await IPFS.create({ ...config.daemon1, repo: './ipfs1' })
ipfs2 = await IPFS.create({ ...config.daemon2, repo: './ipfs2' })
await connectPeers(ipfs1, ipfs2)
// before(async () => {
// rmrf.sync(dbPath1)
// rmrf.sync(dbPath2)
// ipfsd1 = await startIpfs(API, config.daemon1)
// ipfsd2 = await startIpfs(API, config.daemon2)
// ipfs1 = ipfsd1.api
// ipfs2 = ipfsd2.api
const keystore1 = await Keystore({ path: dbPath1 + '/keys' })
const keystore2 = await Keystore({ path: dbPath2 + '/keys' })
// const keystore1 = new Keystore(dbPath1 + '/keys')
// const keystore2 = new Keystore(dbPath2 + '/keys')
identities1 = await Identities({ keystore: keystore1 })
identities2 = await Identities({ keystore: keystore2 })
// id1 = await IdentityProvider.createIdentity({ id: 'A', keystore: keystore1 })
// id2 = await IdentityProvider.createIdentity({ id: 'B', keystore: keystore2 })
testIdentity1 = await identities1.createIdentity({ id: 'userA' })
testIdentity2 = await identities2.createIdentity({ id: 'userB' })
})
// orbitdb1 = await OrbitDB.createInstance(ipfs1, {
// AccessControllers,
// directory: dbPath1,
// identity: id1
// })
after(async () => {
if (ipfs1) {
await ipfs1.stop()
}
// orbitdb2 = await OrbitDB.createInstance(ipfs2, {
// AccessControllers,
// directory: dbPath2,
// identity: id2
// })
// })
if (ipfs2) {
await ipfs2.stop()
}
// after(async () => {
// if (orbitdb1) {
// await orbitdb1.stop()
// }
await rmrf('./orbitdb')
await rmrf('./ipfs1')
await rmrf('./ipfs2')
})
// if (orbitdb2) {
// await orbitdb2.stop()
// }
let accessController
// if (ipfsd1) {
// await stopIpfs(ipfsd1)
// }
before(async () => {
accessController = await IPFSAccessController({
ipfs: ipfs1,
identities: identities1,
identity: testIdentity1
})
})
// if (ipfsd2) {
// await stopIpfs(ipfsd2)
// }
// })
it('creates an access controller', () => {
notStrictEqual(accessController, null)
notStrictEqual(accessController, undefined)
})
// describe('Constructor', function () {
// let accessController
it('sets the controller type', () => {
strictEqual(accessController.type, 'ipfs')
})
// before(async () => {
// accessController = await IPFSAccessController.create(orbitdb1, {
// write: [id1.id]
// })
// })
it('sets default write', async () => {
deepStrictEqual(accessController.write, [testIdentity1.id])
})
// it('creates an access controller', () => {
// assert.notStrictEqual(accessController, null)
// assert.notStrictEqual(accessController, undefined)
// })
it('user with write access can append', async () => {
const mockEntry = {
identity: testIdentity1.hash,
v: 1
// ...
// doesn't matter what we put here, only identity is used for the check
}
const canAppend = await accessController.canAppend(mockEntry)
strictEqual(canAppend, true)
})
// it('sets the controller type', () => {
// assert.strictEqual(accessController.type, 'ipfs')
// })
it('user without write cannot append', async () => {
const mockEntry = {
identity: testIdentity2.hash,
v: 1
// ...
// doesn't matter what we put here, only identity is used for the check
}
const canAppend = await accessController.canAppend(mockEntry)
strictEqual(canAppend, false)
})
// it('has IPFS instance', async () => {
// const peerId1 = await accessController._ipfs.id()
// const peerId2 = await ipfs1.id()
// assert.strictEqual(String(peerId1.id), String(peerId2.id))
// })
it('replicates the access controller', async () => {
const replicatedAccessController = await IPFSAccessController({
ipfs: ipfs2,
identities: identities2,
identity: testIdentity2,
address: accessController.address
})
// it('sets default capabilities', async () => {
// assert.deepStrictEqual(accessController.write, [id1.id])
// })
// it('allows owner to append after creation', async () => {
// const mockEntry = {
// identity: id1,
// v: 1
// // ...
// // doesn't matter what we put here, only identity is used for the check
// }
// const canAppend = await accessController.canAppend(mockEntry, id1.provider)
// assert.strictEqual(canAppend, true)
// })
// })
// describe('save and load', function () {
// let accessController, manifest
// before(async () => {
// accessController = await IPFSAccessController.create(orbitdb1, {
// write: ['A', 'B', id1.id]
// })
// manifest = await accessController.save()
// await accessController.load(manifest.address)
// })
// it('has correct capabalities', async () => {
// assert.deepStrictEqual(accessController.write, ['A', 'B', id1.id])
// })
// })
// })
// })
strictEqual(replicatedAccessController.type, accessController.type)
strictEqual(replicatedAccessController.address, accessController.address)
deepStrictEqual(replicatedAccessController.write, accessController.write)
})
})

View File

@@ -107,6 +107,7 @@ describe('Database', function () {
await headsStorage.close()
await rmrf(headsPath)
await rmrf('./custom-directory')
})
it('uses given MemoryStorage for headsStorage', async () => {

102
test/manifest.test.js Normal file
View File

@@ -0,0 +1,102 @@
import { strictEqual, deepStrictEqual } from 'assert'
import path from 'path'
import rmrf from 'rimraf'
import * as IPFS from 'ipfs'
import Manifest from '../src/manifest.js'
import IPFSBlockStorage from '../src/storage/ipfs-block.js'
import config from './config.js'
describe('Manifest', () => {
const repo = './ipfs'
let ipfs
let storage
before(async () => {
ipfs = await IPFS.create({ ...config.daemon1, repo })
storage = await IPFSBlockStorage({ ipfs })
})
after(async () => {
await storage.close()
await ipfs.stop()
await rmrf(repo)
})
it('creates a manifest', async () => {
const name = 'manifest'
const type = 'manifest-test'
const accessController = '123'
const expectedHash = 'zdpuAtUvd7EhN9Xu2KSCxkjG1oS1SN6EnnZ8sxvJMPiJhbQWF'
const expectedManifest = {
name,
type,
accessController: path.join('/ipfs', accessController)
}
const { hash, manifest } = await Manifest(storage, name, type, accessController)
strictEqual(hash, expectedHash)
deepStrictEqual(manifest, expectedManifest)
})
it('creates a manifest with metadata', async () => {
const name = 'manifest'
const type = 'manifest-test'
const accessController = '123'
const expectedHash = 'zdpuAmNAMNnzKJ2kWgo4H42ZDG7nFCSGEWtV76UvL5dWrNweQ'
const meta = { name, type, description: 'more information about the database' }
const { hash, manifest } = await Manifest(storage, name, type, accessController, { meta })
strictEqual(hash, expectedHash)
deepStrictEqual(manifest.meta, meta)
})
it('throws an error if storage is not specified', async () => {
let err
try {
await Manifest()
} catch (e) {
err = e.toString()
}
strictEqual(err, 'Error: storage is required')
})
it('throws an error if name is not specified', async () => {
let err
try {
await Manifest(storage)
} catch (e) {
err = e.toString()
}
strictEqual(err, 'Error: name is required')
})
it('throws an error if type is not specified', async () => {
let err
try {
await Manifest(storage, 'manifest')
} catch (e) {
err = e.toString()
}
strictEqual(err, 'Error: type is required')
})
it('throws an error if accessControllerAddress is not specified', async () => {
let err
try {
await Manifest(storage, 'manifest', 'manifest-test')
} catch (e) {
err = e.toString()
}
strictEqual(err, 'Error: accessControllerAddress is required')
})
})

View File

@@ -1,7 +1,7 @@
import { strictEqual, notStrictEqual, deepStrictEqual } from 'assert'
import { OrbitDBAddress, isValidAddress, parseAddress } from '../src/index.js'
describe('OrbitDB Address', function () {
describe('Address', function () {
describe('Creating an address from full address string', () => {
it('creates an address from full address string', () => {
const address = '/orbitdb/zdpuAuK3BHpS7NvMBivynypqciYCuy2UW77XYBPUYRnLjnw13'

View File

@@ -83,8 +83,8 @@ describe('orbit-db - Multiple Databases', function () {
let options = {}
// Set write access for both clients
options.write = [
orbitdb1.identity.publicKey,
orbitdb2.identity.publicKey
orbitdb1.identity.id,
orbitdb2.identity.id
]
let connected1Count = 0

View File

@@ -0,0 +1,182 @@
import { strictEqual } from 'assert'
import rmrf from 'rimraf'
import path from 'path'
import * as IPFS from 'ipfs'
import OrbitDB from '../src/OrbitDB.js'
import config from './config.js'
import waitFor from './utils/wait-for.js'
import connectPeers from './utils/connect-nodes.js'
const dbPath = './orbitdb/tests/write-permissions'
describe('Write Permissions', function () {
this.timeout(20000)
let ipfs1, ipfs2
let orbitdb1, orbitdb2
before(async () => {
ipfs1 = await IPFS.create({ ...config.daemon1, repo: './ipfs1' })
ipfs2 = await IPFS.create({ ...config.daemon2, repo: './ipfs2' })
await connectPeers(ipfs1, ipfs2)
orbitdb1 = await OrbitDB({ ipfs: ipfs1, id: 'user1', directory: path.join(dbPath, '1') })
orbitdb2 = await OrbitDB({ ipfs: ipfs2, id: 'user2', directory: path.join(dbPath, '2') })
})
after(async () => {
if (orbitdb1) {
await orbitdb1.stop()
}
if (orbitdb2) {
await orbitdb2.stop()
}
if (ipfs1) {
await ipfs1.stop()
}
if (ipfs2) {
await ipfs2.stop()
}
await rmrf('./orbitdb')
await rmrf('./ipfs1')
await rmrf('./ipfs2')
})
it('throws an error if a peer writes to a log with default write access', async () => {
let err
let connected = false
const onConnected = async (peerId, heads) => {
connected = true
}
const db1 = await orbitdb1.open('write-test')
const db2 = await orbitdb2.open(db1.address)
db2.events.on('join', onConnected)
await waitFor(() => connected, () => true)
await db1.add('record 1')
try {
await db2.add('record 2')
} catch (e) {
err = e.toString()
}
strictEqual(err, `Error: Could not append entry:\nKey "${db2.identity.hash}" is not allowed to write to the log`)
await db1.close()
await db2.close()
})
it('allows anyone to write to the log', async () => {
let connected = false
let updateCount = 0
const onConnected = async (peerId, heads) => {
connected = true
}
const onUpdate = async (entry) => {
++updateCount
}
const db1 = await orbitdb1.open('write-test', { write: ['*'] })
const db2 = await orbitdb2.open(db1.address)
db2.events.on('join', onConnected)
db2.events.on('update', onUpdate)
await waitFor(() => connected, () => true)
await db1.add('record 1')
await db2.add('record 2')
await waitFor(() => updateCount === 2, () => true)
strictEqual((await db1.all()).length, (await db2.all()).length)
await db1.close()
await db2.close()
})
it('allows specific peers to write to the log', async () => {
let connected = false
let updateCount = 0
const options = {
// Set write access for both clients
write: [
orbitdb1.identity.id,
orbitdb2.identity.id
]
}
const onConnected = async (peerId, heads) => {
connected = true
}
const onUpdate = async (entry) => {
++updateCount
}
const db1 = await orbitdb1.open('write-test', options)
const db2 = await orbitdb2.open(db1.address)
db2.events.on('join', onConnected)
db2.events.on('update', onUpdate)
await waitFor(() => connected, () => true)
await db1.add('record 1')
await db2.add('record 2')
await waitFor(() => updateCount === 2, () => true)
strictEqual((await db1.all()).length, (await db2.all()).length)
await db1.close()
await db2.close()
})
it('throws an error if peer does not have write access', async () => {
let err
let connected = false
const options = {
write: [
orbitdb1.identity.id
]
}
const onConnected = async (peerId, heads) => {
connected = true
}
const db1 = await orbitdb1.open('write-test', options)
const db2 = await orbitdb2.open(db1.address)
db2.events.on('join', onConnected)
await waitFor(() => connected, () => true)
await db1.add('record 1')
try {
await db2.add('record 2')
} catch (e) {
err = e.toString()
}
strictEqual(err, `Error: Could not append entry:\nKey "${db2.identity.hash}" is not allowed to write to the log`)
await db1.close()
await db2.close()
})
})

View File

@@ -1,214 +0,0 @@
// import assert from 'assert'
// import rmrf from 'rimraf'
// import path from 'path'
// import OrbitDB from '../src/OrbitDB.js'
// // Include test utilities
// import {
// config,
// startIpfs,
// stopIpfs,
// testAPIs
// } from 'orbit-db-test-utils'
// import databases from './utils/databases.js'
// const dbPath = './orbitdb/tests/write-permissions'
// Object.keys(testAPIs).forEach(API => {
// describe(`orbit-db - Write Permissions (${API})`, function() {
// this.timeout(20000)
// let ipfsd, ipfs, orbitdb1, orbitdb2
// before(async () => {
// rmrf.sync(dbPath)
// ipfsd = await startIpfs(API, config.daemon1)
// ipfs = ipfsd.api
// orbitdb1 = await OrbitDB.createInstance(ipfs, { directory: path.join(dbPath, '1') })
// orbitdb2 = await OrbitDB.createInstance(ipfs, { directory: path.join(dbPath, '2') })
// })
// after(async () => {
// if(orbitdb1)
// await orbitdb1.stop()
// if(orbitdb2)
// await orbitdb2.stop()
// if (ipfsd)
// await stopIpfs(ipfsd)
// })
// describe('allows multiple peers to write to the databases', function() {
// databases.forEach(async (database) => {
// it(database.type + ' allows multiple writers', async () => {
// let options = {
// // Set write access for both clients
// accessController: {
// write: [
// orbitdb1.identity.id,
// orbitdb2.identity.id
// ]
// }
// }
// const db1 = await database.create(orbitdb1, 'sync-test', options)
// options = Object.assign({}, options, { sync: true })
// const db2 = await database.create(orbitdb2, db1.address.toString(), options)
// await database.tryInsert(db1)
// await database.tryInsert(db2)
// assert.deepEqual(database.getTestValue(db1), database.expectedValue)
// assert.deepEqual(database.getTestValue(db2), database.expectedValue)
// await db1.close()
// await db2.close()
// })
// })
// })
// describe('syncs databases', function() {
// databases.forEach(async (database) => {
// it(database.type + ' syncs', async () => {
// let options = {
// // Set write access for both clients
// accessController: {
// write: [
// orbitdb1.identity.id,
// orbitdb2.identity.id
// ]
// }
// }
// const db1 = await database.create(orbitdb1, 'sync-test', options)
// options = Object.assign({}, options, { sync: true })
// const db2 = await database.create(orbitdb2, db1.address.toString(), options)
// await database.tryInsert(db2)
// assert.strictEqual(database.query(db1).length, 0)
// db1.sync(db2._oplog.heads)
// return new Promise(resolve => {
// setTimeout(async () => {
// const value = database.getTestValue(db1)
// assert.deepEqual(value, database.expectedValue)
// await db1.close()
// await db2.close()
// resolve()
// }, 300)
// })
// })
// })
// })
// describe('syncs databases that anyone can write to', function() {
// databases.forEach(async (database) => {
// it(database.type + ' syncs', async () => {
// let options = {
// // Set write permission for everyone
// accessController: {
// write: ['*']
// }
// }
// const db1 = await database.create(orbitdb1, 'sync-test-public-dbs', options)
// options = Object.assign({}, options, { sync: true })
// const db2 = await database.create(orbitdb2, db1.address.toString(), options)
// await database.tryInsert(db2)
// assert.strictEqual(database.query(db1).length, 0)
// db1.sync(db2._oplog.heads)
// return new Promise(resolve => {
// setTimeout(async () => {
// const value = database.getTestValue(db1)
// assert.deepEqual(value, database.expectedValue)
// await db1.close()
// await db2.close()
// resolve()
// }, 300)
// })
// })
// })
// })
// describe('doesn\'t sync if peer is not allowed to write to the database', function() {
// databases.forEach(async (database) => {
// it(database.type + ' doesn\'t sync', async () => {
// let options = {
// // Only peer 1 can write
// accessController: {
// write: [orbitdb1.identity.id]
// }
// }
// let err
// options = Object.assign({}, options, { path: path.join(dbPath, '/sync-test/1') })
// const db1 = await database.create(orbitdb1, 'write error test 1', options)
// options = Object.assign({}, options, { path: path.join(dbPath, '/sync-test/2'), sync: true })
// const db2 = await database.create(orbitdb2, 'write error test 1', options)
// try {
// // Catch replication event if the update from peer 2 got synced and into the database
// db1.events.on('replicated', () => err = new Error('Shouldn\'t replicate!'))
// // Try to update from peer 2, this shouldn't be allowed
// await database.tryInsert(db2)
// } catch (e) {
// // Make sure peer 2's instance throws an error
// err = e.toString()
// }
// assert.strictEqual(err, `Error: Could not append entry, key "${orbitdb2.identity.id}" is not allowed to write to the log`)
// // Make sure nothing was added to the database
// assert.strictEqual(database.query(db1).length, 0)
// // Try to sync peer 1 with peer 2, this shouldn't produce anything
// // at peer 1 (nothing was supposed to be added to the database by peer 2)
// db1.sync(db2._oplog.heads)
// return new Promise((resolve, reject) => {
// setTimeout(async () => {
// // Make sure nothing was added
// assert.strictEqual(database.query(db1).length, 0)
// await db1.close()
// await db2.close()
// if (!err) {
// reject(new Error('tryInsert should throw an err'))
// } else {
// resolve()
// }
// }, 300)
// })
// })
// })
// })
// describe('throws an error if peer is not allowed to write to the database', function() {
// databases.forEach(async (database) => {
// it(database.type + ' throws an error', async () => {
// let options = {
// // No write access (only creator of the database can write)
// accessController: {
// write: []
// }
// }
// let err
// try {
// const db1 = await database.create(orbitdb1, 'write error test 2', options)
// options = Object.assign({}, options, { sync: true })
// const db2 = await database.create(orbitdb2, db1.address.toString(), options)
// await database.tryInsert(db2)
// } catch (e) {
// err = e.toString()
// }
// assert.strictEqual(err, `Error: Could not append entry, key "${orbitdb2.identity.id}" is not allowed to write to the log`)
// })
// })
// })
// })
// })