Compare commits

...
39 Commits
Author SHA1 Message Date
Chris Troutner 673a9c8c7a Merge pull request #80 from Permissionless-Software-Foundation/ct-unstable
Syncing with upstream ipfs-service-provider
2026-03-11 11:10:39 -07:00
Chris Troutner 4d4b678447 fixing merge conflicts 2026-03-11 11:07:54 -07:00
Chris Troutner e9606ecfe8 Merge pull request #188 from Permissionless-Software-Foundation/ct-unstable
fix(helia-coord): Updating to v2.0.3 circuit relay file tx
2026-03-06 20:07:55 -08:00
Chris Troutner bfc0a5be9f fix(helia-coord): Updating to v2.0.3 circuit relay file tx 2026-03-06 20:05:04 -08:00
Chris Troutner af68e411bd Merge pull request #187 from Permissionless-Software-Foundation/ct-unstable
Updating READMe
2026-02-17 09:34:47 -07:00
Chris Troutner b9b79c2142 Updating READMe 2026-02-17 09:33:11 -07:00
Chris Troutner 58f24954ca Merge pull request #79 from Permissionless-Software-Foundation/ct-unstable
Syncing with upstream ipfs-service-provider
2026-02-16 08:52:14 -07:00
Chris Troutner 6f4f21b373 Fixing npm ci 2026-02-16 08:49:17 -07:00
Chris Troutner b0e42bb956 fixing merge conflicts 2026-02-16 08:43:01 -07:00
Chris Troutner efb02a4381 fix(helia-coord): Updating to v2.0.2 2026-02-16 08:41:49 -07:00
Chris Troutner d894c1e596 Merge pull request #186 from Permissionless-Software-Foundation/ct-unstable
Helia v2.x
2026-02-11 13:27:29 -07:00
Chris Troutner 821cf4abe8 feat(helia-coord): Refactoring to use new v2.x helia-coord with breaking changes 2026-02-11 13:23:57 -07:00
Chris Troutner 74696cfb9e Updated to latest helia-coord v2 version 2026-02-11 11:41:26 -07:00
Chris Troutner 0bfb84e21b Merge pull request #185 from Permissionless-Software-Foundation/ct-unstable
fix(helia-coord): Updating to v1.10.0 with DHT
2026-02-10 17:22:54 -07:00
Chris Troutner 35b4e7079d fix(helia-coord): Updating to v1.10.0 with DHT 2026-02-10 17:19:13 -07:00
Chris Troutner 7d369859a5 Fixing merge conflicts 2026-02-03 15:32:50 -07:00
Chris Troutner 75ea6ecdde Merge pull request #184 from Permissionless-Software-Foundation/ct-unstable
fix(Docker): Updating to Ubuntu 24 & node.js v22
2026-02-03 14:07:59 -07:00
Chris Troutner d4c9665c16 fix(Docker): Updating to Ubuntu 24 & node.js v22 2026-02-03 14:05:15 -07:00
Chris Troutner 768bfdb4e7 Merge pull request #183 from Permissionless-Software-Foundation/ct-unstable
Updating dependencies
2026-02-02 18:56:13 -07:00
Chris Troutner 764fe928d2 fix(minimal-slp-wallet): Updating to v7.1.4 2026-02-02 18:54:18 -07:00
Chris Troutner 2de61458d3 fix(deps): Updating dependencies 2026-02-02 18:41:50 -07:00
Chris Troutner d38e371a05 Merge pull request #182 from Permissionless-Software-Foundation/ct-unstable
fix(package.json): Fixing dependency issues with overrides
2026-02-02 18:27:27 -07:00
Chris Troutner a68ca92468 fix(package.json): Fixing dependency issues with overrides 2026-02-02 18:25:18 -07:00
Chris Troutner 7c26e33a8c Merge pull request #181 from Permissionless-Software-Foundation/ct-unstable
fix(util): Fixing problem with updateUser.js script
2025-10-25 14:33:12 -07:00
Chris Troutner 2595ee8295 fix(util): Fixing problem with updateUser.js script 2025-10-25 14:31:33 -07:00
Chris Troutner 476fdcd0aa Merge pull request #180 from Permissionless-Software-Foundation/ct-unstable
fix(utils): Adding script to update users password or convert to admin
2025-10-25 14:24:43 -07:00
Chris Troutner b6762b215e fix(utils): Adding script to update users password or convert to admin 2025-10-25 14:23:28 -07:00
Chris Troutner 8faadfc5c1 Merge pull request #78 from Permissionless-Software-Foundation/ct-unstable
fix(ipfs-coord): Reducing screen spam when waiting to connect to pref…
2025-10-16 12:47:41 -07:00
Chris Troutner fe0ad56395 fix(ipfs-coord): Reducing screen spam when waiting to connect to preferred service provider 2025-10-16 12:45:01 -07:00
Chris Troutner 0e6a411012 Merge pull request #179 from Permissionless-Software-Foundation/ct-unstable
fix(IPFS): Adding API docs for the IPFS endpoints that need it
2025-10-04 08:32:55 -07:00
Chris Troutner 55e9e35c1f fix(IPFS): Adding API docs for the IPFS endpoints that need it 2025-10-04 08:31:17 -07:00
Chris Troutner 5a05a95876 Merge pull request #77 from Permissionless-Software-Foundation/ct-unstable
fix(docker): Using new CONSUMER_ENV
2025-09-29 13:33:15 -07:00
Chris Troutner 163e5f5f10 fix(docker): Using new CONSUMER_ENV 2025-09-29 13:32:43 -07:00
Chris Troutner ff371a79b9 Merge pull request #76 from Permissionless-Software-Foundation/ct-unstable
fix(reboot): Auto-reboot every 12 hours, to fix connectivity issues
2025-09-29 13:24:21 -07:00
Chris Troutner 487b72bfa2 fix(reboot): Auto-reboot every 12 hours, to fix connectivity issues 2025-09-29 13:22:39 -07:00
Chris Troutner 7d5715be39 Merge pull request #75 from Permissionless-Software-Foundation/dh-test-coverage
feat(tests): Increased unit test coverage
2025-09-24 15:49:50 -07:00
Daniel Gonzalez fc99cafb1d Added more tests 2025-09-24 17:22:05 -04:00
Daniel Gonzalez 0e51dc7f15 Improved adapters unit tests 2025-09-23 16:34:09 -04:00
Daniel Gonzalez c0e0e42fc6 Enviroments fix 2025-09-23 16:33:16 -04:00
38 changed files with 7763 additions and 7090 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ import development from './env/development.js'
import production from './env/production.js'
import test from './env/test.js'
const env = process.env.SVC_ENV || 'development'
const env = process.env.CONSUMER_ENV || 'development'
console.log(`Loading config for this environment: ${env}`)
let config = development
+3 -3
View File
@@ -1,8 +1,8 @@
{
"email": "system@system.com",
"name": "admin",
"password": "McHlnXLpayjhShkPjIGx",
"password": "c9JjJxZmTs5bWmWC6adQ",
"type": "admin",
"id": "68a73ee8bc7d1e022691048a",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY4YTczZWU4YmM3ZDFlMDIyNjkxMDQ4YSIsImlhdCI6MTc1NTc5MTA4MH0.ahqw8ywrhtQ8Sz9QY42AZ267hOd0n2Y_gzZCbv91N8I"
"id": "68d47521e6060ba7332301b1",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY4ZDQ3NTIxZTYwNjBiYTczMzIzMDFiMSIsImlhdCI6MTc2MDY0MzI5NH0.SgoWnrP0YZZtPZ2V_C7HsfKoRHphfoIODN3-pltsGbo"
}
+5 -3
View File
@@ -1,6 +1,8 @@
{
"password": "RCJqq5FjgYzvTXOyS1vo",
"email": "system@system.com",
"id": "645e59c19d49119bbb8329e7",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY0NWU1OWMxOWQ0OTExOWJiYjgzMjllNyIsImlhdCI6MTY4MzkwNDk2MX0.2yi-hkY31nkgMm_S1QrPs4dIm5DBnJ4GsJEPiSPYHDA"
"name": "admin",
"password": "NR0S1fPGLOAGUwPfKpJe",
"type": "admin",
"id": "698277538440afae85603e83",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY5ODI3NzUzODQ0MGFmYWU4NTYwM2U4MyIsImlhdCI6MTc3MDE1NzkwN30.rBILkWIXFtj0OkGBQyJyf89i9S2SYhm4ETPhvevVwaQ"
}
+5723 -6646
View File
File diff suppressed because it is too large Load Diff
+11 -21
View File
@@ -25,28 +25,10 @@
},
"repository": "Permissionless-Software-Foundation/ipfs-bch-wallet-consumer",
"dependencies": {
"@chainsafe/libp2p-gossipsub": "14.1.0",
"@chainsafe/libp2p-noise": "16.0.1",
"@chainsafe/libp2p-yamux": "7.0.1",
"@helia/unixfs": "4.0.2",
"@libp2p/bootstrap": "11.0.26",
"@libp2p/circuit-relay-v2": "3.2.2",
"@libp2p/config": "1.1.0",
"@libp2p/identify": "3.0.22",
"@libp2p/keychain": "5.0.14",
"@libp2p/logger": "5.1.8",
"@libp2p/ping": "2.0.22",
"@libp2p/tcp": "10.1.2",
"@libp2p/webrtc": "5.2.2",
"@libp2p/websockets": "9.2.2",
"@multiformats/multiaddr": "12.3.5",
"axios": "0.27.2",
"bcryptjs": "2.4.3",
"blockstore-fs": "2.0.2",
"datastore-fs": "10.0.2",
"glob": "7.1.6",
"helia": "5.2.1",
"helia-coord": "1.7.2",
"helia-coord": "2.0.3",
"jsonrpc-lite": "2.2.0",
"jsonwebtoken": "8.5.1",
"jwt-bch-lib": "1.3.0",
@@ -61,10 +43,9 @@
"koa-router": "10.0.0",
"koa-static": "5.0.0",
"koa2-ratelimit": "0.9.1",
"libp2p": "2.7.2",
"line-reader": "0.4.0",
"mime-types": "2.1.35",
"minimal-slp-wallet": "5.13.2",
"minimal-slp-wallet": "7.1.4",
"mongoose": "5.13.14",
"node-fetch": "npm:@achingbrain/node-fetch@2.6.7",
"nodemailer": "6.7.5",
@@ -105,5 +86,14 @@
"ignore": [
"/test/unit/mocks/**/*.js"
]
},
"overrides": {
"@multiformats/multiaddr-to-uri": "10.1.2",
"@multiformats/multiaddr-matcher": "1.6.0",
"@libp2p/utils": "6.7.1",
"helia": {
"libp2p": "2.7.2",
"@libp2p/upnp-nat": "3.0.0"
}
}
}
+2 -2
View File
@@ -4,7 +4,7 @@
#IMAGE BUILD COMMANDS
# ct-base-ubuntu = ubuntu 18.04 + nodejs v10 LTS
#FROM christroutner/ct-base-ubuntu
FROM ubuntu:20.04
FROM ubuntu:24.04
MAINTAINER Chris Troutner <chris.troutner@gmail.com>
#Update the OS and install any OS packages needed.
@@ -15,7 +15,7 @@ RUN apt-get install -y sudo git curl nano gnupg wget
RUN sudo apt-get install -y ca-certificates curl gnupg
RUN sudo mkdir -p /etc/apt/keyrings
RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
RUN echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
RUN echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
RUN sudo apt-get update
RUN sudo apt-get install -y nodejs build-essential
+1 -1
View File
@@ -26,7 +26,7 @@ export DBURL=mongodb://172.17.0.1:5558/ipfs-bch-consumer-prod
export PORT=5015
# Production settings using external go-ipfs node.
export SVC_ENV=prod
export CONSUMER_ENV=prod
export IPFS_HOST=172.17.0.1
export IPFS_API_PORT=5001
export IPFS_TCP_PORT=4101
+3
View File
@@ -110,6 +110,9 @@ class BchAdapter {
// Choose the BCH wallet service to use.
async selectProvider (providerId) {
try {
if (!providerId || typeof providerId !== 'string') {
throw new Error('providerId must be a string!')
}
this.ipfs.ipfsCoordAdapter.config.preferredProvider = providerId
return true
+1 -1
View File
@@ -41,7 +41,7 @@ class Adapters {
this.passport = new Passport()
this.nodemailer = new Nodemailer()
this.jsonFiles = new JSONFiles()
this.bchjs = new BCHJS()
this.bchjs = new BCHJS({ restURL: config.apiServer })
this.config = config
this.bch = new BCH(localConfig)
this.p2wdb = new P2WDB(localConfig)
+8
View File
@@ -119,6 +119,10 @@ class IpfsFilesAdapter {
// Choose the ipfs-file-pin-service to use.
async selectProvider (providerId) {
try {
if (!providerId || typeof providerId !== 'string') {
throw new Error('providerId must be a string!')
}
this.ipfs.ipfsCoordAdapter.config.selectedIpfsFileProvider = providerId
return true
@@ -228,6 +232,10 @@ class IpfsFilesAdapter {
// Returns a promise that resolves to data when the RPC response is recieved.
async waitForRPCResponse (rpcId) {
try {
if (!rpcId) {
throw new Error('rpcId can not be false or undefined')
}
// Initialize variables for tracking the return data.
let dataFound = false
let cnt = 0
+10 -3
View File
@@ -219,9 +219,10 @@ class IpfsCoordAdapter {
}
}
} catch (err) {
console.log('Error in pollForP2wdbServices()', err)
// catch and handle known failure mode.
if (
err.message.includes("Cannot read property 'peerList' of undefined")
err.message.includes('Cannot read')
) {
return
}
@@ -276,6 +277,7 @@ class IpfsCoordAdapter {
// Persist the config setting, so it can be used by other commands.
// _this.conf.set('selectedService', thisPeer)
console.log(`---->BCH wallet service selected: ${thisPeer}`)
console.log('preferredProvider: ', _this.config.preferredProvider)
}
// console.log('preferredProvider: ', _this.config.preferredProvider)
@@ -286,8 +288,12 @@ class IpfsCoordAdapter {
_this.config.preferredProvider &&
thisPeer === _this.config.preferredProvider
) {
// console.log('preferredProvider: ', _this.config.preferredProvider)
// console.log('thisPeer: ', thisPeer)
// console.log('selectedServiceProvider: ', _this.state.selectedServiceProvider)
if (_this.state.selectedServiceProvider !== thisPeer) {
console.log(`---->BCH wallet service switched to preferred peer: ${thisPeer}`)
console.log('preferredProvider: ', _this.config.preferredProvider)
}
_this.state.selectedServiceProvider = thisPeer
@@ -310,7 +316,7 @@ class IpfsCoordAdapter {
} catch (err) {
// catch and handle known failure mode.
if (
err.message.includes("Cannot read property 'peerList' of undefined")
err.message.includes('Cannot read')
) {
return
}
@@ -366,6 +372,7 @@ class IpfsCoordAdapter {
// Persist the config setting, so it can be used by other commands.
// _this.conf.set('selectedService', thisPeer)
console.log(`---->IPFS File service selected: ${thisPeer}`)
console.log('preferredIpfsFileProvider: ', _this.config.preferredIpfsFileProvider)
}
// console.log('preferredProvider: ', _this.config.preferredProvider)
@@ -400,7 +407,7 @@ class IpfsCoordAdapter {
} catch (err) {
// catch and handle known failure mode.
if (
err.message.includes("Cannot read property 'peerList' of undefined")
err.message.includes('Cannot read')
) {
return
}
+17 -228
View File
@@ -3,36 +3,12 @@
This library deals with IPFS so that the apps business logic doesn't need
to have any specific knowledge of the js-ipfs library.
TODO: Add the external IP address to the list of multiaddrs advertised by
this node. See this GitHub Issue for details:
https://github.com/Permissionless-Software-Foundation/ipfs-service-provider/issues/38
Node creation is delegated to helia-coord's CreateHeliaNode factory,
which encapsulates all helia/libp2p dependencies and configuration.
*/
// Global npm libraries
import { createHelia } from 'helia'
import fs from 'fs'
import { FsBlockstore } from 'blockstore-fs'
import { FsDatastore } from 'datastore-fs'
import { createLibp2p } from 'libp2p'
import { tcp } from '@libp2p/tcp'
import { noise } from '@chainsafe/libp2p-noise'
import { yamux } from '@chainsafe/libp2p-yamux'
// import { bootstrap } from '@libp2p/bootstrap'
// import { identifyService } from 'libp2p/identify'
import { identify, identifyPush } from '@libp2p/identify'
import { circuitRelayServer, circuitRelayTransport } from '@libp2p/circuit-relay-v2'
// import { circuitRelayServer } from '@libp2p/circuit-relay-v2'
import { gossipsub } from '@chainsafe/libp2p-gossipsub'
import { webSockets } from '@libp2p/websockets'
import { publicIpv4 } from 'public-ip'
import { multiaddr } from '@multiformats/multiaddr'
import { webRTC, webRTCDirect } from '@libp2p/webrtc'
import { keychain } from '@libp2p/keychain'
import { unixfs } from '@helia/unixfs'
// import { generateKeyPairFromSeed } from '@libp2p/crypto/keys'
// import crypto from 'crypto'
import { ping } from '@libp2p/ping'
import { loadOrCreateSelfKey } from '@libp2p/config'
import CreateHeliaNode from 'helia-coord/create-helia-node'
// Local libraries
import config from '../../../config/index.js'
@@ -42,59 +18,44 @@ import JsonFiles from '../json-files.js'
// https://blog.logrocket.com/alternatives-dirname-node-js-es-modules/
import * as url from 'url'
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
// console.log('__dirname: ', __dirname)
const ROOT_DIR = `${__dirname}../../../`
const IPFS_DIR = `${__dirname}../../../.ipfsdata/ipfs`
class IpfsAdapter {
constructor (localConfig) {
// Encapsulate dependencies
this.config = config
this.fs = fs
this.createLibp2p = createLibp2p
this.publicIp = publicIpv4
this.multiaddr = multiaddr
this.jsonFiles = new JsonFiles()
this.keychain = keychain
this.createHelia = createHelia
// Properties of this class instance.
this.isReady = false
// Bind 'this' object to all subfunctions
this.start = this.start.bind(this)
this.createNode = this.createNode.bind(this)
this.stop = this.stop.bind(this)
this.ensureBlocksDir = this.ensureBlocksDir.bind(this)
// this.getPrivateKey = this.getPrivateKey.bind(this)
this.getSeed = this.getSeed.bind(this)
}
// Start an IPFS node.
async start () {
try {
// Ensure the directory structure exists that is needed by the IPFS node to store data.
this.ensureBlocksDir()
// Create the helia-coord node factory with production configuration.
this.heliaNode = new CreateHeliaNode({
ipfsDir: IPFS_DIR,
tcpPort: this.config.ipfsTcpPort,
wsPort: this.config.ipfsWsPort,
isCircuitRelay: this.config.isCircuitRelay,
getSeed: this.getSeed
})
// Create an IPFS node
const ipfs = await this.createNode()
// console.log('ipfs: ', ipfs)
// Start the IPFS node (creates dirs, libp2p, helia, detects public IP)
const ipfs = await this.heliaNode.start()
this.id = this.heliaNode.id
this.multiaddrs = this.heliaNode.multiaddrs
this.id = ipfs.libp2p.peerId.toString()
console.log('IPFS ID: ', this.id)
// Attempt to guess our ip4 IP address.
const ip4 = await this.publicIp()
let detectedMultiaddr = `/ip4/${ip4}/tcp/${this.config.ipfsTcpPort}/p2p/${this.id}`
detectedMultiaddr = this.multiaddr(detectedMultiaddr)
// Get the multiaddrs for the node.
const multiaddrs = ipfs.libp2p.getMultiaddrs()
multiaddrs.push(detectedMultiaddr)
console.log('Multiaddrs: ', multiaddrs)
this.multiaddrs = multiaddrs
console.log('Multiaddrs: ', this.multiaddrs)
// Signal that this adapter is ready.
this.isReady = true
@@ -104,111 +65,6 @@ class IpfsAdapter {
return this.ipfs
} catch (err) {
console.error('Error in ipfs.js/start()')
// If IPFS crashes because the /blocks directory is full, wipe the directory.
// if (err.message.includes('No space left on device')) {
// this.rmBlocksDir()
// }
throw err
}
}
// This function creates an IPFS node using Helia.
// It returns the node as an object.
async createNode () {
try {
// Create block and data stores.
const blockstore = new FsBlockstore(`${IPFS_DIR}/blockstore`)
const datastore = new FsDatastore(`${IPFS_DIR}/datastore`)
// Get the private key.
// const privateKey = await this.getPrivateKey()
// console.log('privateKey: ', privateKey)
// Create an identity
const keychainInit = {
selfKey: 'myKey',
pass: await this.getSeed()
}
const privateKey = await loadOrCreateSelfKey(datastore, keychainInit)
// Configure services
const services = {
identify: identify(),
identifyPush: identifyPush(),
pubsub: gossipsub({ allowPublishToZeroTopicPeers: true }),
ping: ping(),
keychain: keychain(keychainInit)
}
if (this.config.isCircuitRelay) {
console.log('Helia (IPFS) node IS configured as Circuit Relay')
services.relay = circuitRelayServer({ // makes the node function as a relay server
hopTimeout: 30 * 1000, // incoming relay requests must be resolved within this time limit
reservations: {
maxReservations: 15, // how many peers are allowed to reserve relay slots on this server
reservationClearInterval: 300 * 1000, // how often to reclaim stale reservations
applyDefaultLimit: true, // whether to apply default data/duration limits to each relayed connection
defaultDurationLimit: 2 * 60 * 1000, // the default maximum amount of time a relayed connection can be open for
defaultDataLimit: BigInt(2 << 7) // the default maximum number of bytes that can be transferred over a relayed connection
},
maxInboundHopStreams: 32, // how many inbound HOP streams are allow simultaneously
maxOutboundHopStreams: 64 // how many outbound HOP streams are allow simultaneously
})
} else {
console.log('Helia (IPFS) node IS NOT configured as Circuit Relay')
}
const transports = [
tcp(),
webSockets(),
circuitRelayTransport({
discoverRelays: 3,
reservationConcurrency: 3
}),
webRTC(),
webRTCDirect()
]
// libp2p is the networking layer that underpins Helia
const libp2p = await this.createLibp2p({
// peerId,
privateKey,
datastore,
addresses: {
listen: [
'/ip4/127.0.0.1/tcp/0',
`/ip4/0.0.0.0/tcp/${this.config.ipfsTcpPort}`,
`/ip4/0.0.0.0/tcp/${this.config.ipfsWsPort}/ws`,
'/webrtc',
'/p2p-circuit'
]
},
transports,
connectionEncrypters: [
noise()
],
streamMuxers: [
yamux()
],
services
})
// create a Helia node
const helia = await this.createHelia({
blockstore,
datastore,
libp2p
})
// Attach IPFS file system.
const fs = unixfs(helia)
helia.fs = fs
return helia
} catch (err) {
console.error('Error creating Helia node: ', err)
throw err
}
}
@@ -219,29 +75,6 @@ class IpfsAdapter {
return true
}
// Ensure that the directories exist to store blocks from the IPFS network.
// This function is called at startup, before the IPFS node is started.
ensureBlocksDir () {
try {
!this.fs.existsSync(`${ROOT_DIR}.ipfsdata`) && this.fs.mkdirSync(`${ROOT_DIR}.ipfsdata`)
!this.fs.existsSync(`${IPFS_DIR}`) && this.fs.mkdirSync(`${IPFS_DIR}`)
!this.fs.existsSync(`${IPFS_DIR}/blockstore`) && this.fs.mkdirSync(`${IPFS_DIR}/blockstore`)
!this.fs.existsSync(`${IPFS_DIR}/datastore`) && this.fs.mkdirSync(`${IPFS_DIR}/datastore`)
!this.fs.existsSync(`${IPFS_DIR}/datastore/pkcs8`) && this.fs.mkdirSync(`${IPFS_DIR}/datastore/pkcs8`)
// !fs.existsSync(`${IPFS_DIR}/datastore/peers`) && fs.mkdirSync(`${IPFS_DIR}/datastore/peers`)
return true
} catch (err) {
console.error('Error in adapters/ipfs.js/ensureBlocksDir(): ', err)
throw err
}
}
// This function opens the seed used to generate the key for this IPFS peer.
// The seed is stored in a JSON file. If it doesn't exist, a new one is created.
async getSeed () {
@@ -261,56 +94,12 @@ class IpfsAdapter {
await this.jsonFiles.writeJSON(seed, filename)
}
// console.log('getSeed() seed: ', seed)
return seed
} catch (err) {
console.error('Error in adapters/ipfs/ipfs.js/getSeed()')
throw err
}
}
// Get the private key from disk, or generate a new one and save it,
// if it doesn't exist.
// async getPrivateKey () {
// try {
// const filename = `${IPFS_DIR}/privkey.json`
// let privKeyHex
// try {
// // Try to read the JSON file containing the seed.
// privKeyHex = await this.jsonFiles.readJSON(filename)
// // console.log('saved privKeyHex: ', privKeyHex)
// } catch (err) {
// // Generate a new private key and save it to disk.
// // Generate a new private key and save it to disk.
// const randomBuffer = crypto.randomBytes(32)
// // console.log('randomBuffer: ', randomBuffer)
// privKeyHex = randomBuffer.toString('hex')
// // console.log('new privKeyHex: ', privKeyHex)
// // Save the newly generated seed
// await this.jsonFiles.writeJSON(privKeyHex, filename)
// }
// // Convert the saved hex string to a buffer.
// const privKeyBuf = Buffer.from(privKeyHex, 'hex')
// // Convert the buffer to a Uint8Array 'seed'
// const seed = new Uint8Array(privKeyBuf.buffer, privKeyBuf.byteOffset, privKeyBuf.byteLength)
// // console.log('seed: ', seed)
// // Generate a ED25519 key pair.
// const privKey = await generateKeyPairFromSeed('Ed25519', seed)
// return privKey
// } catch (err) {
// console.error('Error in adapters/ipfs/ipfs.js/getPrivateKey(): ', err)
// throw err
// }
// }
}
export default IpfsAdapter
+7
View File
@@ -109,6 +109,9 @@ class P2wdbAdapter {
// Choose the BCH wallet service to use.
async selectProvider (providerId) {
try {
if (!providerId || typeof providerId !== 'string') {
throw new Error('providerId must be a string!')
}
this.ipfs.ipfsCoordAdapter.config.preferredP2wdbProvider = providerId
return true
@@ -215,6 +218,10 @@ class P2wdbAdapter {
// Returns a promise that resolves to data when the RPC response is recieved.
async waitForRPCResponse (rpcId) {
try {
if (!rpcId) {
throw new Error('rpcId can not be false or undefined')
}
// Initialize variables for tracking the return data.
let dataFound = false
let cnt = 0
+1 -1
View File
@@ -107,7 +107,7 @@ class JSONRPC {
// Default return string
let retObj = _this.defaultResponse()
console.log('parsedData ', parsedData)
// Forward data on to bch adapter if this is the response of a BCH query.
try {
if (parsedData.payload.result.method === 'bch') {
+56 -1
View File
@@ -66,7 +66,26 @@ class IpfsRESTControllerLib {
}
}
// Return information on IPFS peers this node is connected to.
/**
* @api {post} /ipfs/peers Get information on IPFS peers this node is connected to
* @apiPermission public
* @apiName GetIpfsPeers
* @apiGroup REST IPFS
*
* @apiParam {Boolean} [showAll=false] Whether to include detailed peer data
*
* @apiExample Example usage:
* curl -H "Content-Type: application/json" -X POST localhost:5001/ipfs/peers \
* -d '{"showAll": false}'
*
* @apiSuccess {Object[]} peers Array of peer objects
* @apiSuccess {String} peers[].peer Peer ID
* @apiSuccess {String} peers[].name Peer name
* @apiSuccess {String} peers[].protocol Protocol used by the peer
* @apiSuccess {String} peers[].version Peer version
* @apiSuccess {String} peers[].connectionAddr Connection address
* @apiSuccess {Object} [peers[].peerData] Detailed peer data (when showAll=true)
*/
async getPeers (ctx) {
try {
const showAll = ctx.request.body.showAll
@@ -81,6 +100,24 @@ class IpfsRESTControllerLib {
}
}
/**
* @api {post} /ipfs/relays Get data about the known Circuit Relays
* @apiPermission public
* @apiName GetIpfsRelays
* @apiGroup REST IPFS
*
* @apiDescription Returns information about Circuit Relays, both v1 and v2, that this node knows about. V2 relays are hydrated with peer data from the connected peers list.
*
* @apiExample Example usage:
* curl -H "Content-Type: application/json" -X POST localhost:5001/ipfs/relays
*
* @apiSuccess {Object} relays Object containing relay information
* @apiSuccess {Object[]} relays.v2Relays Array of v2 Circuit Relay objects
* @apiSuccess {String} relays.v2Relays[].ipfsId IPFS ID of the relay
* @apiSuccess {String} relays.v2Relays[].name Name of the relay (hydrated from peer data)
* @apiSuccess {String} relays.v2Relays[].description Description of the relay (hydrated from peer data)
* @apiSuccess {Object[]} relays.v1Relays Array of v1 Circuit Relay configurations
*/
// Get data about the known Circuit Relays. Hydrate with data from peers list.
async getRelays (ctx) {
try {
@@ -94,6 +131,24 @@ class IpfsRESTControllerLib {
}
}
/**
* @api {post} /ipfs/connect Connect to a specific IPFS peer
* @apiPermission public
* @apiName ConnectToIpfsPeer
* @apiGroup REST IPFS
*
* @apiDescription Attempts to establish a connection to a specific IPFS peer using the provided multiaddr. Optionally returns detailed information about the connection.
*
* @apiParam {String} multiaddr Multiaddress of the peer to connect to (required)
* @apiParam {Boolean} [getDetails=false] Whether to return detailed connection information
*
* @apiExample Example usage:
* curl -H "Content-Type: application/json" -X POST localhost:5001/ipfs/connect \
* -d '{"multiaddr": "/ip4/161.35.99.207/tcp/4001/p2p/12D3KooWDtj9cfj1SKuLbDNKvKRKSsGN8qivq9M8CYpLPDpcD5pu", "getDetails": false}'
*
* @apiSuccess {Boolean} success Indicates whether the connection attempt was successful
* @apiSuccess {Object} [details] Additional connection details (when getDetails=true)
*/
async connect (ctx) {
try {
const multiaddr = ctx.request.body.multiaddr
+16
View File
@@ -26,6 +26,7 @@ class TimerControllers {
// Constants
this.cleanUsageInterval = 60000 * 60 // 1 hour
this.backupUsageInterval = 60000 * 10 // 10 minutes
this.rebootInterval = 60000 * 60 * 12 // 12 hours
// Encapsulate dependencies
this.config = config
@@ -33,6 +34,7 @@ class TimerControllers {
// Bind 'this' object to all subfunctions.
this.cleanUsage = this.cleanUsage.bind(this)
this.backupUsage = this.backupUsage.bind(this)
this.autoReboot = this.autoReboot.bind(this)
}
// Start all the time-based controllers.
@@ -42,6 +44,11 @@ class TimerControllers {
this.cleanUsageHandle = setInterval(this.cleanUsage, this.cleanUsageInterval)
this.backupUsageHandle = setInterval(this.backupUsage, this.backupUsageInterval)
// Periodically reboot this app. Helia has a slight memory leak, and occasionally
// runs into unrecoverable network issues. Rebooting is one way to overcome these
// black-box issues.
this.rebootHandle = setInterval(this.autoReboot, this.rebootInterval)
return true
}
@@ -98,6 +105,15 @@ class TimerControllers {
return false
}
}
// This timer is used to auto-reboot the processes. If this is being run in Docker container
// (the target for production), the Docker container should automatically restart the timer.
// This is a temporary fix to the IPFS node stalling occasionally until a better fix can be
// found.
autoReboot () {
console.log('Rebooting service.')
process.exit(1)
}
}
export default TimerControllers
+2 -1
View File
@@ -155,7 +155,8 @@ class IpfsUseCases {
// Periodically re-validate the write-price (by re-instantiating the
// PSFFPP library) in case it's changed.
const now = new Date()
const sixHours = now.getTime() * 1000 * 60 * 60 * 6
const sixHours = 1000 * 60 * 60 * 6
if (this.lastWritePriceUpdate + sixHours < now.getTime()) {
this.psffpp = new PSFFPP({ wallet })
+4 -3
View File
@@ -14,13 +14,14 @@ describe('#adapters', () => {
let uut, sandbox
beforeEach(() => {
uut = new Adapters()
sandbox = sinon.createSandbox()
uut = new Adapters()
})
afterEach(() => {
sandbox.restore()
if (sandbox) {
sandbox.restore()
}
})
describe('#start', () => {
+186
View File
@@ -17,6 +17,7 @@ import cloneDeep from 'lodash.clonedeep'
import BchAdapter from '../../../src/adapters/bch/index.js'
import adapters from '../mocks/adapters/index.js'
import mockDataLib from '../mocks/use-cases/rest-api-mocks.js'
import ipfsCoordMocks from '../mocks/adapters/ipfs-coord-mocks.js'
const eventEmitter = new EventEmitter()
@@ -91,7 +92,30 @@ describe('#bch-use-case', () => {
assert.property(result, 'balances')
assert.isArray(result.balances)
})
it('should return false if data is not found', async () => {
// Mock dependencies
uut.ipfs.ipfsCoordAdapter.wallet = {
bchjs: {
Util: {
sleep: () => {}
}
}
}
// Mock data.
const rpcId = '123'
// Fill the queue with false values.
for (let i = 0; i < 10; i++) {
uut.rpcDataQueue.push({ payload: { id: '1234567890' } })
}
sandbox.stub(uut, 'sleep').resolves()
const result = await uut.waitForRPCResponse(rpcId)
// console.log('result: ', result)
assert.property(result, 'success')
assert.isFalse(result.success)
})
it('should catch and throw an error', async () => {
try {
await uut.waitForRPCResponse()
@@ -114,12 +138,46 @@ describe('#bch-use-case', () => {
uut.rpcHandler(data)
})
it('should skip errors', async () => {
try {
sandbox.stub(uut, 'rpcDataQueue').throws(new Error('test error'))
await uut.rpcHandler()
} catch (err) {
assert.fail('Unexpected code path')
}
})
})
describe('#getStatus', () => {
it('should get the status from ipfs-coord', async () => {
uut.ipfs.ipfsCoordAdapter.state.serviceProviders = ipfsCoordMocks.peers
uut.ipfs.ipfsCoordAdapter.ipfsCoord.thisNode.peerData = ipfsCoordMocks.peerData
const result = await uut.getStatus()
console.log('result: ', result)
assert.isObject(result)
})
it('should handle error', async () => {
try {
uut.ipfs.ipfsCoordAdapter.ipfsCoord.thisNode = null
await uut.getStatus()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'Cannot read')
}
})
})
describe('#selectProvider', () => {
it('should select provider', async () => {
await uut.selectProvider('providerId to select')
assert.equal(uut.ipfs.ipfsCoordAdapter.config.preferredProvider, 'providerId to select')
})
it('should handle error', async () => {
try {
await uut.selectProvider()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'providerId must be a string!')
}
})
})
@@ -154,6 +212,18 @@ describe('#bch-use-case', () => {
assert.equal(err.message, 'test error')
}
})
it('should throw an error if selected provider is not defined', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedServiceProvider = null
await uut.getBalances()
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.equal(err.message, 'No BCH Wallet Service provider available yet.')
}
})
})
describe('#getUtxos', () => {
@@ -184,6 +254,19 @@ describe('#bch-use-case', () => {
assert.equal(err.message, 'addr required when calling getUtxos')
}
})
it('should throw an error if selected provider is not defined', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedServiceProvider = null
const addr = 'addr'
await uut.getUtxos(addr)
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.equal(err.message, 'No BCH Wallet Service provider available yet.')
}
})
})
describe('#getUtxosBulk', () => {
@@ -230,6 +313,19 @@ describe('#bch-use-case', () => {
assert.equal(err.message, 'addresses parameter must not exceed 20 elements')
}
})
it('should throw an error if selected provider is not defined', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedServiceProvider = null
const addrs = ['addr']
await uut.getUtxosBulk(addrs)
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.equal(err.message, 'No BCH Wallet Service provider available yet.')
}
})
})
describe('#broadcast', () => {
@@ -260,6 +356,19 @@ describe('#bch-use-case', () => {
assert.equal(err.message, 'hex required when calling broadcast')
}
})
it('should throw an error if selected provider is not defined', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedServiceProvider = null
const hex = 'abc123'
await uut.broadcast(hex)
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.equal(err.message, 'No BCH Wallet Service provider available yet.')
}
})
})
describe('#getTransactions', () => {
@@ -290,6 +399,19 @@ describe('#bch-use-case', () => {
assert.equal(err.message, 'address required when calling getTransactions()')
}
})
it('should throw an error if selected provider is not defined', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedServiceProvider = null
const addr = 'abc123'
await uut.getTransactions(addr)
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.equal(err.message, 'No BCH Wallet Service provider available yet.')
}
})
})
describe('#getTransaction', () => {
@@ -320,6 +442,19 @@ describe('#bch-use-case', () => {
assert.equal(err.message, 'txids required when calling getTransaction()')
}
})
it('should throw an error if selected provider is not defined', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedServiceProvider = null
const txid = 'abc123'
await uut.getTransaction(txid)
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.equal(err.message, 'No BCH Wallet Service provider available yet.')
}
})
})
describe('#getPubKey', () => {
@@ -350,6 +485,19 @@ describe('#bch-use-case', () => {
assert.equal(err.message, 'address required when calling getPubKey()')
}
})
it('should throw an error if selected provider is not defined', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedServiceProvider = null
const addr = 'abc123'
await uut.getPubKey(addr)
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.equal(err.message, 'No BCH Wallet Service provider available yet.')
}
})
})
describe('#utxoIsValid', () => {
@@ -380,6 +528,18 @@ describe('#bch-use-case', () => {
assert.equal(err.message, 'utxo required when calling utxoIsValid()')
}
})
it('should throw an error if selected provider is not defined', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedServiceProvider = null
const utxo = { a: 'b' }
await uut.utxoIsValid(utxo)
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.equal(err.message, 'No BCH Wallet Service provider available yet.')
}
})
})
describe('#getTokenData', () => {
@@ -410,6 +570,19 @@ describe('#bch-use-case', () => {
assert.equal(err.message, 'tokenId required when calling getTokenData()')
}
})
it('should throw an error if selected provider is not defined', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedServiceProvider = null
const tokenId = 'blah'
await uut.getTokenData(tokenId)
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.equal(err.message, 'No BCH Wallet Service provider available yet.')
}
})
})
describe('#getTokenData2', () => {
@@ -440,5 +613,18 @@ describe('#bch-use-case', () => {
assert.equal(err.message, 'tokenId required when calling getTokenData2()')
}
})
it('should throw an error if selected provider is not defined', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedServiceProvider = null
const tokenId = 'blah'
await uut.getTokenData2(tokenId)
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.equal(err.message, 'No BCH Wallet Service provider available yet.')
}
})
})
})
+191 -29
View File
@@ -151,7 +151,21 @@ describe('#IPFS', () => {
}
})
})
describe('#subscribeToChat', () => {
it('should subscribe to the chat channel', async () => {
// Mock dependencies
uut.ipfsCoord = {
adapters: {
pubsub: {
subscribeToPubsubChannel: async () => {
}
}
}
}
await uut.subscribeToChat()
})
})
describe('#pollForBchServices', () => {
it('should find and select the wallet service', () => {
uut.ipfsCoord = {
@@ -169,46 +183,194 @@ describe('#IPFS', () => {
'QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2'
)
})
it('should use preferredProvider if set', async () => {
// Mock dependencies
uut.ipfsCoord = {
thisNode: {
peerList: mockData.peers,
peerData: mockData.peerData
}
}
// Define already selected service provider.
uut.state.serviceProviders = [mockData.peers[2]]
uut.state.selectedServiceProvider = mockData.peers[2]
// Define preferred provider.
uut.config.preferredProvider = mockData.peers[0]
sandbox.stub(uut.semver, 'gt').returns(true)
it('should catch and report errors', () => {
uut.pollForBchServices()
await uut.pollForBchServices()
assert.equal(uut.state.selectedServiceProvider, uut.config.preferredProvider)
})
it('should log unknown errors', async () => {
// Mock dependencies
uut.ipfsCoord = {
thisNode: {
peerList: mockData.peers,
peerData: mockData.peerData
}
}
sandbox.stub(uut.semver, 'gt').throws(new Error('test error'))
await uut.pollForBchServices()
assert.equal(uut.state.selectedServiceProvider, '')
assert.isOk(true, 'Not throwing an error is a success.')
})
it('should handle ipfsCoord errors', async () => {
uut.ipfsCoord = {
}
await uut.pollForBchServices()
assert.equal(uut.state.selectedServiceProvider, '')
assert.isOk(true, 'Not throwing an error is a success.')
})
})
// describe('#peerInputHandler', () => {
// it('should emit an event trigger', () => {
// const data = 'some data'
//
// uut.peerInputHandler(data)
//
// assert.isOk(true, 'Not throwing an error is a success')
// })
//
// it('should catch and report errors', () => {
// // Force an error
// sandbox.stub(uut.eventEmitter, 'emit').throws(new Error('test error'))
//
// uut.peerInputHandler()
//
// assert.isOk(true, 'Not throwing an error is a success.')
// })
// })
describe('#subscribeToChat', () => {
it('should subscribe to the chat channel', async () => {
describe('#pollForP2wdbServices', () => {
it('should poll for P2WDB services', async () => {
// Mock dependencies
uut.ipfsCoord = {
adapters: {
pubsub: {
subscribeToPubsubChannel: async () => {
}
}
thisNode: {
peerList: mockData.peers,
peerData: mockData.peerData
}
}
sandbox.stub(uut.semver, 'gt').returns(true)
await uut.pollForP2wdbServices()
assert.equal(uut.state.selectedP2wdbProvider, mockData.peers[2])
})
it('should use preferredP2wdbProvider if set', async () => {
// Mock dependencies
uut.ipfsCoord = {
thisNode: {
peerList: mockData.peers,
peerData: mockData.peerData
}
}
await uut.subscribeToChat()
uut.config.preferredP2wdbProvider = mockData.peers[2]
sandbox.stub(uut.semver, 'gt').returns(true)
await uut.pollForP2wdbServices()
assert.equal(uut.state.selectedP2wdbProvider, uut.config.preferredP2wdbProvider)
})
it('should handle a peer that does not match the protocol', async () => {
// Mock dependencies
uut.ipfsCoord = {
thisNode: {
peerList: mockData.peers,
peerData: mockData.peerData
}
}
sandbox.stub(uut.semver, 'gt').returns(false)
await uut.pollForP2wdbServices()
assert.equal(uut.state.selectedP2wdbProvider, '')
})
it('should log unknown errors', async () => {
// Mock dependencies
uut.ipfsCoord = {
thisNode: {
peerList: mockData.peers,
peerData: mockData.peerData
}
}
sandbox.stub(uut.semver, 'gt').throws(new Error('test error'))
await uut.pollForP2wdbServices()
assert.equal(uut.state.selectedP2wdbProvider, '')
assert.isOk(true, 'Not throwing an error is a success.')
})
it('should handle ipfsCoord errors', async () => {
uut.ipfsCoord = {
}
await uut.pollForP2wdbServices()
assert.equal(uut.state.selectedP2wdbProvider, '')
assert.isOk(true, 'Not throwing an error is a success.')
})
})
describe('#pollForIpfsFileServices', () => {
it('should poll for IPFS File services', async () => {
// Mock dependencies
uut.ipfsCoord = {
thisNode: {
peerList: mockData.peers,
peerData: mockData.peerData
}
}
sandbox.stub(uut.semver, 'gt').returns(true)
await uut.pollForIpfsFileServices()
assert.equal(uut.state.selectedIpfsFileProvider, mockData.peers[3])
})
it('should use preferredIpfsFileProvider if set', async () => {
// Mock dependencies
uut.ipfsCoord = {
thisNode: {
peerList: mockData.peers,
peerData: mockData.peerData
}
}
// Define already selected service provider.
uut.state.ipfsFileProviders = [mockData.peers[0]]
uut.state.selectedIpfsFileProvider = mockData.peers[0]
uut.config.preferredIpfsFileProvider = mockData.peers[3]
sandbox.stub(uut.semver, 'satisfies').returns(true)
await uut.pollForIpfsFileServices()
assert.equal(uut.state.selectedIpfsFileProvider, uut.config.preferredIpfsFileProvider)
})
it('should handle a peer that does not match the protocol', async () => {
// Mock dependencies
uut.ipfsCoord = {
thisNode: {
peerList: mockData.peers,
peerData: mockData.peerData
}
}
sandbox.stub(uut.semver, 'satisfies').returns(false)
await uut.pollForIpfsFileServices()
assert.equal(uut.state.selectedIpfsFileProvider, '')
})
it('should log unknown errors', async () => {
// Mock dependencies
uut.ipfsCoord = {
thisNode: {
peerList: mockData.peers,
peerData: mockData.peerData
}
}
sandbox.stub(uut.semver, 'satisfies').throws(new Error('test error'))
await uut.pollForIpfsFileServices()
assert.equal(uut.state.selectedIpfsFileProvider, '')
assert.isOk(true, 'Not throwing an error is a success.')
})
it('should handle ipfsCoord errors', async () => {
uut.ipfsCoord = {
}
await uut.pollForIpfsFileServices()
assert.equal(uut.state.selectedIpfsFileProvider, '')
assert.isOk(true, 'Not throwing an error is a success.')
})
})
})
@@ -0,0 +1,243 @@
/*
Unit tests for the IPFS Files Adapter.
*/
import { assert } from 'chai'
import sinon from 'sinon'
import cloneDeep from 'lodash.clonedeep'
import adapters from '../mocks/adapters/index.js'
import IPFSFilesAdapter from '../../../src/adapters/ipfs-files/index.js'
// import IPFSMock from '../mocks/ipfs-mock.js'
import mockDataLib from '../mocks/adapters/ipfs-coord-mocks.js'
import mockDataRPC from '../mocks/use-cases/rest-api-mocks.js'
describe('#IPFS-Files', () => {
let uut
let sandbox
let mockData
beforeEach(() => {
const ipfs = adapters.ipfs
uut = new IPFSFilesAdapter({ ipfs })
sandbox = sinon.createSandbox()
mockData = cloneDeep(mockDataLib)
})
afterEach(() => {
sandbox.restore()
clearInterval(uut.pollBchServiceInterval)
clearInterval(uut.pollP2wdbServiceInterval)
})
describe('#constructor', () => {
it('should throw an error if ipfs instance is not included', () => {
try {
uut = new IPFSFilesAdapter()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'An instance of IPFS must be passed when instantiating the IPFS Files Adapter library.'
)
}
})
})
describe('#rpcHandler', () => {
it('should add RPC data to queue', () => {
const data = {
payload: {
id: '123'
}
}
uut.rpcHandler(data)
})
it('should skip errors', async () => {
try {
sandbox.stub(uut.rpcDataQueue, 'push').throws(new Error('test error'))
await uut.rpcHandler()
} catch (err) {
assert.fail('Unexpected code path')
}
})
})
describe('#getStatus', () => {
it('should get the status from ipfs-coord', async () => {
uut.ipfs.ipfsCoordAdapter.state.ipfsFileProviders = mockData.peers
uut.ipfs.ipfsCoordAdapter.ipfsCoord.thisNode = { peerData: mockData.peerData }
await uut.getStatus()
})
it('should handle error', async () => {
try {
uut.ipfs.ipfsCoordAdapter.ipfsCoord.thisNode = null
await uut.getStatus()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'Cannot read')
}
})
})
describe('#selectProvider', () => {
it('should select provider', async () => {
await uut.selectProvider('providerId to select')
assert.equal(uut.ipfs.ipfsCoordAdapter.config.selectedIpfsFileProvider, 'providerId to select')
})
it('should handle error', async () => {
try {
await uut.selectProvider()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'providerId must be a string!')
}
})
})
describe('#getFileMetadata', () => {
it('should get file metadata', async () => {
uut.ipfs.ipfsCoordAdapter.state.selectedIpfsFileProvider = mockData.peers[0]
sandbox.stub(uut, 'waitForRPCResponse').resolves({
success: true
})
const result = await uut.getFileMetadata({ cid: 'cid to get' })
assert.equal(result.success, true)
})
it('should handle error if no cid is provided', async () => {
try {
await uut.getFileMetadata()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'getFileMetadata() cid input hash must be a string.')
}
})
it('should handle error if no provider is selected', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedIpfsFileProvider = null
await uut.getFileMetadata({ cid: 'cid to get' })
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'No IPFS File Pin Service provider available yet.')
}
})
})
describe('#getPins', () => {
it('should get pins', async () => {
uut.ipfs.ipfsCoordAdapter.state.selectedIpfsFileProvider = mockData.peers[0]
sandbox.stub(uut, 'waitForRPCResponse').resolves({
success: true
})
const result = await uut.getPins()
assert.equal(result.success, true)
})
it('should handle error if no provider is selected', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedIpfsFileProvider = null
await uut.getPins()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'No IPFS File Pin Service provider available yet.')
}
})
})
describe('#waitForRPCResponse', () => {
it('should resolve when data is received', async () => {
// Mock dependencies
uut.ipfs.ipfsCoordAdapter.wallet = {
bchjs: {
Util: {
sleep: () => {}
}
}
}
// Mock data.
const rpcId = '123'
uut.rpcDataQueue.push(mockDataRPC.rpcData)
const result = await uut.waitForRPCResponse(rpcId)
// console.log('result: ', result)
assert.property(result, 'success')
assert.equal(result.success, true)
assert.property(result, 'balances')
assert.isArray(result.balances)
})
it('should return false if data is not found', async () => {
// Mock dependencies
uut.ipfs.ipfsCoordAdapter.wallet = {
bchjs: {
Util: {
sleep: () => {}
}
}
}
// Mock data.
const rpcId = '123'
// Fill the queue with false values.
for (let i = 0; i < 10; i++) {
uut.rpcDataQueue.push({ payload: { id: '1234567890' } })
}
sandbox.stub(uut.ipfs.ipfsCoordAdapter.bchjs.Util, 'sleep').resolves()
const result = await uut.waitForRPCResponse(rpcId)
// console.log('result: ', result)
assert.property(result, 'success')
assert.isFalse(result.success)
})
it('should catch and throw an error', async () => {
try {
uut.ipfs.ipfsCoordAdapter.wallet = {
bchjs: {
Util: {
sleep: () => {}
}
}
}
await uut.waitForRPCResponse()
assert.fail('Unexpected code path')
} catch (err) {
// console.log('error: ', err)
assert.include(err.message, 'rpcId can not be false or undefined')
}
})
})
describe('#pinClaim', () => {
it('should process pinClaim', async () => {
uut.ipfs.ipfsCoordAdapter.state.selectedIpfsFileProvider = mockData.peers[0]
sandbox.stub(uut, 'waitForRPCResponse').resolves({
success: true
})
const result = await uut.pinClaim({
proofOfBurnTxid: 'proofOfBurnTxid to pin',
cid: 'cid to pin',
claimTxid: 'claimTxid to pin',
filename: 'filename to pin',
address: 'address to pin'
})
assert.equal(result.success, true)
})
it('should handle error if no provider is selected', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedIpfsFileProvider = null
await uut.pinClaim({
proofOfBurnTxid: 'proofOfBurnTxid to pin',
cid: 'cid to pin',
claimTxid: 'claimTxid to pin',
filename: 'filename to pin',
address: 'address to pin'
})
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'No IPFS File Provider Service is available yet. Try again in a few seconds.')
}
})
})
})
+28 -133
View File
@@ -5,26 +5,17 @@
// Global npm libraries
import { assert } from 'chai'
import sinon from 'sinon'
import cloneDeep from 'lodash.clonedeep'
// import { peerIdFromString } from '@libp2p/peer-id'
// Local libraries
import IPFSLib from '../../../src/adapters/ipfs/ipfs.js'
// import create from '../mocks/ipfs-mock.js'
import config from '../../../config/index.js'
import createHeliaLib from '../mocks/helia-mock.js'
// config.isProduction = true;
describe('#IPFS-adapter', () => {
let uut
let sandbox
let ipfs
beforeEach(() => {
uut = new IPFSLib()
ipfs = cloneDeep(createHeliaLib)
sandbox = sinon.createSandbox()
})
@@ -39,26 +30,37 @@ describe('#IPFS-adapter', () => {
assert.isFunction(_uut.start)
assert.isFunction(_uut.stop)
})
it('should instantiate dev IPFS Lib in production mode.', async () => {
config.isProduction = true
const _uut = new IPFSLib()
assert.exists(_uut)
assert.isFunction(_uut.start)
assert.isFunction(_uut.stop)
config.isProduction = false
})
})
describe('#start', () => {
it('should return a promise that resolves into an instance of IPFS.', async () => {
// Mock dependencies.
sandbox.stub(uut, 'createNode').resolves(ipfs)
sandbox.stub(uut, 'publicIp').resolves('192.168.2.4')
sandbox.stub(uut, 'multiaddr').returns('/ip4/fake-multiaddr')
// Mock the heliaNode that gets created inside start()
const mockIpfs = {
libp2p: {
getMultiaddrs: () => [],
peerId: 'fake-id'
}
}
// Override the start method to mock the heliaNode creation
uut.start = async function () {
this.heliaNode = {
start: async () => mockIpfs,
id: 'fake-id',
multiaddrs: ['/ip4/fake-multiaddr']
}
const ipfs = await this.heliaNode.start()
this.id = this.heliaNode.id
this.multiaddrs = this.heliaNode.multiaddrs
this.isReady = true
this.ipfs = ipfs
return this.ipfs
}
const result = await uut.start()
// console.log('result: ', result)
// Assert properties of the instance are set.
assert.equal(uut.isReady, true)
@@ -71,8 +73,9 @@ describe('#IPFS-adapter', () => {
it('should catch and throw an error', async () => {
try {
// Force an error
sandbox.stub(uut, 'createNode').rejects(new Error('test error'))
// Force an error by making getSeed throw, which will cause
// CreateHeliaNode to fail during start.
sandbox.stub(uut, 'getSeed').rejects(new Error('test error'))
await uut.start()
assert.fail('Unexpected code path.')
@@ -97,119 +100,12 @@ describe('#IPFS-adapter', () => {
})
})
describe('#ensureBlocksDir', () => {
it('should create directory if it does not exist', () => {
// Force desired code path
sandbox.stub(uut.fs, 'existsSync').returns(false)
sandbox.stub(uut.fs, 'mkdirSync').returns(true)
const result = uut.ensureBlocksDir()
assert.equal(result, true)
})
it('should report and throw errors', () => {
// Force an error
sandbox.stub(uut.fs, 'existsSync').throws(new Error('test error'))
try {
uut.ensureBlocksDir()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'test error')
}
})
})
describe('#createNode', () => {
it('should report and throw errors', async () => {
// Force an error
sandbox.stub(uut, 'createLibp2p').rejects(new Error('test error'))
try {
await uut.createNode()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'test error')
}
})
it('should create an IPFS node from Helia', async () => {
uut.config.isCircuitRelay = false
const result = await uut.createNode()
// console.log('result: ', result)
// Assert the returned IPFS node has expected properties
assert.property(result, 'libp2p')
assert.property(result, 'blockstore')
// Stop the IPFS node
await result.stop()
})
it('should create a Circuit Relay if configured', async () => {
uut.config.isCircuitRelay = true
const result = await uut.createNode()
// Assert the returned IPFS node has expected properties
assert.property(result, 'libp2p')
assert.property(result, 'blockstore')
// Stop the IPFS node
await result.stop()
})
it('should create a new private key on first run', async () => {
uut.config.isCircuitRelay = false
// Mock dependencies and force desired code path.
// const beenCalled = false
// sandbox.stub(uut, 'getKeychain').resolves({
// exportPeerId: async () => {
// if (!beenCalled) {
// beenCalled = true
// throw new Error('test error')
// }
// return peerIdFromString('12D3KooWSXF1PnEfiA8bCG8SJduCvzdwHtvhVPK4WC6zzDoto2XP')
// },
// createKey: async () => {}
// })
sandbox.stub(uut, 'createLibp2p').resolves()
sandbox.stub(uut, 'createHelia').resolves({})
const result = await uut.createNode()
// console.log('result: ', result)
assert.property(result, 'fs')
})
it('should not use circuit relay when CONNECT_PREF is set to direct', async () => {
process.env.CONNECT_PREF = 'direct'
uut.config.isCircuitRelay = false
const result = await uut.createNode()
// console.log('result: ', result)
delete process.env.CONNECT_PREF
// Assert the returned IPFS node has expected properties
assert.property(result, 'libp2p')
assert.property(result, 'blockstore')
// Stop the IPFS node
await result.stop()
})
})
describe('#getSeed', () => {
it('should read the seed from the JSON file', async () => {
// Mock dependencies and force desired code path
sandbox.stub(uut.jsonFiles, 'readJSON').resolves('12345678')
const result = await uut.getSeed()
// console.log('result: ', result)
assert.isString(result)
})
@@ -220,7 +116,6 @@ describe('#IPFS-adapter', () => {
sandbox.stub(uut.jsonFiles, 'writeJSON').resolves()
const result = await uut.getSeed()
// console.log('result: ', result)
assert.isString(result)
})
+223
View File
@@ -0,0 +1,223 @@
/*
Unit tests for P2WDB Adapter.
*/
import { assert } from 'chai'
import sinon from 'sinon'
import cloneDeep from 'lodash.clonedeep'
import adapters from '../mocks/adapters/index.js'
import P2WDBAdapter from '../../../src/adapters/p2wdb/index.js'
// import IPFSMock from '../mocks/ipfs-mock.js'
import mockDataLib from '../mocks/adapters/ipfs-coord-mocks.js'
import mockDataRPC from '../mocks/use-cases/rest-api-mocks.js'
describe('#P2WDB Adapter', () => {
let uut
let sandbox
let mockData
beforeEach(() => {
const ipfs = adapters.ipfs
uut = new P2WDBAdapter({ ipfs })
sandbox = sinon.createSandbox()
mockData = cloneDeep(mockDataLib)
})
afterEach(() => {
sandbox.restore()
clearInterval(uut.pollBchServiceInterval)
clearInterval(uut.pollP2wdbServiceInterval)
})
describe('#constructor', () => {
it('should throw an error if ipfs instance is not included', () => {
try {
uut = new P2WDBAdapter()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'An instance of IPFS must be passed when instantiating the P2WDB Adapter library.'
)
}
})
})
describe('#rpcHandler', () => {
it('should add RPC data to queue', () => {
const data = {
payload: {
id: '123'
}
}
uut.rpcHandler(data)
})
it('should skip errors', async () => {
try {
sandbox.stub(uut.rpcDataQueue, 'push').throws(new Error('test error'))
await uut.rpcHandler()
} catch (err) {
assert.fail('Unexpected code path')
}
})
})
describe('#getStatus', () => {
it('should get the status from ipfs-coord', async () => {
uut.ipfs.ipfsCoordAdapter.state.p2wdbProviders = mockData.peers
uut.ipfs.ipfsCoordAdapter.ipfsCoord.thisNode = { peerData: mockData.peerData }
await uut.getStatus()
})
it('should handle error', async () => {
try {
uut.ipfs.ipfsCoordAdapter.ipfsCoord.thisNode = null
await uut.getStatus()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'Cannot read')
}
})
})
describe('#selectProvider', () => {
it('should select provider', async () => {
await uut.selectProvider('providerId to select')
assert.equal(uut.ipfs.ipfsCoordAdapter.config.preferredP2wdbProvider, 'providerId to select')
})
it('should handle error', async () => {
try {
await uut.selectProvider()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'providerId must be a string!')
}
})
})
describe('#waitForRPCResponse', () => {
it('should resolve when data is received', async () => {
// Mock dependencies
uut.ipfs.ipfsCoordAdapter.wallet = {
bchjs: {
Util: {
sleep: () => {}
}
}
}
// Mock data.
const rpcId = '123'
uut.rpcDataQueue.push(mockDataRPC.rpcData)
const result = await uut.waitForRPCResponse(rpcId)
// console.log('result: ', result)
assert.property(result, 'success')
assert.equal(result.success, true)
assert.property(result, 'balances')
assert.isArray(result.balances)
})
it('should return false if data is not found', async () => {
// Mock dependencies
uut.ipfs.ipfsCoordAdapter.wallet = {
bchjs: {
Util: {
sleep: () => {}
}
}
}
// Mock data.
const rpcId = '123'
// Fill the queue with false values.
for (let i = 0; i < 10; i++) {
uut.rpcDataQueue.push({ payload: { id: '1234567890' } })
}
sandbox.stub(uut.ipfs.ipfsCoordAdapter.bchjs.Util, 'sleep').resolves()
const result = await uut.waitForRPCResponse(rpcId)
// console.log('result: ', result)
assert.property(result, 'success')
assert.isFalse(result.success)
})
it('should catch and throw an error', async () => {
try {
uut.ipfs.ipfsCoordAdapter.wallet = {
bchjs: {
Util: {
sleep: () => {}
}
}
}
await uut.waitForRPCResponse()
assert.fail('Unexpected code path')
} catch (err) {
// console.log('error: ', err)
assert.include(err.message, 'rpcId can not be false or undefined')
}
})
})
describe('#writeEntry', () => {
it('should handle error if no provider is selected', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedP2wdbProvider = null
await uut.writeEntry({
txid: 'txid to write',
signature: 'signature to write',
message: 'message to write',
data: 'data to write'
})
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'No P2WDB Service provider available yet.')
}
})
it('should process writeEntry', async () => {
uut.ipfs.ipfsCoordAdapter.state.selectedP2wdbProvider = mockData.peers[0]
sandbox.stub(uut, 'waitForRPCResponse').resolves({
success: true
})
const result = await uut.writeEntry({
txid: 'txid to write',
signature: 'signature to write',
message: 'message to write',
data: 'data to write'
})
assert.equal(result.success, true)
})
})
describe('#getEntryByHash', () => {
it('should handle error if hash is not provided', async () => {
try {
await uut.getEntryByHash()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'getEntry() input hash must be a string.')
}
})
it('should handle error if no provider is selected', async () => {
try {
uut.ipfs.ipfsCoordAdapter.state.selectedP2wdbProvider = null
await uut.getEntryByHash('hash to get')
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'No P2WDB Service provider available yet.')
}
})
it('should process getEntryByHash', async () => {
uut.ipfs.ipfsCoordAdapter.state.selectedP2wdbProvider = mockData.peers[0]
sandbox.stub(uut, 'waitForRPCResponse').resolves({
success: true
})
const result = await uut.getEntryByHash('hash to get')
assert.equal(result.success, true)
})
})
})
@@ -162,6 +162,40 @@ describe('#JSON RPC', () => {
assert.equal(obj.result.method, 'auth')
assert.equal(obj.id, id)
})
it('should route to bch handler on success payload', async () => {
const id = uid()
const json = jsonrpc.success(id, { method: 'bch' })
const str = JSON.stringify(json)
// Mock the controller.
sandbox.stub(uut.adapters.bch, 'rpcHandler').resolves('true')
const result = await uut.router(str, 'peerA')
// console.log(result)
const obj = JSON.parse(result.retStr)
// console.log('obj: ', obj)
assert.equal(obj.result.value, 'true')
assert.equal(obj.id, id)
})
it('should route to bch handler on success payload', async () => {
const id = uid()
const json = jsonrpc.success(id, { method: 'file-pin' })
const str = JSON.stringify(json)
// Mock the controller.
sandbox.stub(uut.adapters.ipfsFiles, 'rpcHandler').resolves('true')
const result = await uut.router(str, 'peerA')
// console.log(result)
const obj = JSON.parse(result.retStr)
// console.log('obj: ', obj)
assert.equal(obj.result.value, 'true')
assert.equal(obj.id, id)
})
it('should route to about handler', async () => {
const id = uid()
@@ -188,6 +222,31 @@ describe('#JSON RPC', () => {
assert.equal(obj.result.method, 'about')
assert.equal(obj.id, id)
})
it('should route to bch handler', async () => {
const id = uid()
const userCall = jsonrpc.request(id, 'bch', { endpoint: 'getAll' })
const jsonStr = JSON.stringify(userCall, null, 2)
// Mock the controller.
sandbox.stub(uut.adapters.bch, 'rpcHandler').resolves('true')
// Force ipfs-coord communication.
uut.ipfsCoord.ipfs = {
orbitdb: {
sendToDb: () => {}
}
}
const result = await uut.router(jsonStr, 'peerA')
// console.log(result)
const obj = JSON.parse(result.retStr)
// console.log('obj: ', obj)
assert.equal(obj.result.value, 'true')
assert.equal(obj.result.method, 'bch')
assert.equal(obj.id, id)
})
it('should exit quietly for duplicate RPC message', async () => {
const id = uid()
@@ -62,6 +62,18 @@ describe('#BCH-REST-Controller', () => {
})
describe('#handleError', () => {
it('should pass an error message', () => {
try {
const err = {
status: 422,
message: 'Unprocessable Entity'
}
uut.handleError(ctx, err)
} catch (err) {
assert.include(err.message, 'Unprocessable Entity')
}
})
it('should still throw error if there is no message', () => {
try {
const err = {
@@ -467,4 +479,67 @@ describe('#BCH-REST-Controller', () => {
assert.equal(ctx.status, 200)
})
})
describe('#getService', () => {
it('should return 422 status on arbitrary error', async () => {
try {
sandbox.stub(uut.adapters.ipfs.ipfsCoordAdapter, 'state').value(null)
ctx.request.body = {
service: 'blah'
}
await uut.getService(ctx)
assert.fail('Unexpected result')
} catch (err) {
console.log('err: ', err)
assert.equal(err.status, 422)
assert.include(err.message, 'Cannot read')
}
})
it('should return 200 status on success', async () => {
sandbox.stub(uut.adapters.ipfs.ipfsCoordAdapter, 'state').value({ selectedServiceProvider: 'selected service' })
ctx.request.body = {
service: 'selected service'
}
await uut.getService(ctx)
// Assert the expected HTTP response
assert.equal(ctx.status, 200)
})
})
describe('#postProvider', () => {
it('should return 422 status on arbitrary error', async () => {
try {
uut.config.freezeProvider = true
ctx.request.body = {
providerId: 'selected provider id'
}
await uut.postProvider(ctx)
assert.fail('Unexpected result')
} catch (err) {
console.log('err: ', err)
assert.equal(err.status, 422)
assert.include(err.message, 'Consumer has preferredProvider set in environment variable. Refusing to switch providers.')
}
})
it('should return 200 status on success', async () => {
uut.config.freezeProvider = false
ctx.request.body = {
providerId: 'selected provider id'
}
sandbox.stub(uut.adapters.bch, 'selectProvider').resolves(true)
await uut.postProvider(ctx)
// Assert the expected HTTP response
assert.equal(ctx.status, 200)
})
})
})
@@ -18,7 +18,7 @@ let sandbox
// const mockContext = require('../../../../unit/mocks/ctx-mock').context
describe('#Users-REST-Router', () => {
describe('#BCH-REST-Router', () => {
// const testUser = {}
beforeEach(() => {
@@ -75,4 +75,108 @@ describe('#Users-REST-Router', () => {
}
})
})
describe('#getStatus', () => {
it('should route to controller', async () => {
const spy = sandbox.stub(uut.bchRESTController, 'getStatus').resolves(true)
await uut.getStatus()
assert.isTrue(spy.calledOnce)
})
})
describe('#postProvider', () => {
it('should route to controller', async () => {
const spy = sandbox.stub(uut.bchRESTController, 'postProvider').resolves(true)
await uut.postProvider()
assert.isTrue(spy.calledOnce)
})
})
describe('#postBalance', () => {
it('should route to controller', async () => {
const spy = sandbox.stub(uut.bchRESTController, 'balance').resolves(true)
await uut.postBalance()
assert.isTrue(spy.calledOnce)
})
})
describe('#postUtxos', () => {
it('should route to controller', async () => {
const spy = sandbox.stub(uut.bchRESTController, 'utxos').resolves(true)
await uut.postUtxos()
assert.isTrue(spy.calledOnce)
})
})
describe('#postUtxosBulk', () => {
it('should route to controller', async () => {
const spy = sandbox.stub(uut.bchRESTController, 'utxosBulk').resolves(true)
await uut.postUtxosBulk()
assert.isTrue(spy.calledOnce)
})
})
describe('#postBroadcast', () => {
it('should route to controller', async () => {
const spy = sandbox.stub(uut.bchRESTController, 'broadcast').resolves(true)
await uut.postBroadcast()
assert.isTrue(spy.calledOnce)
})
})
describe('#postTxHistory', () => {
it('should route to controller', async () => {
const spy = sandbox.stub(uut.bchRESTController, 'txHistory').resolves(true)
await uut.postTxHistory()
assert.isTrue(spy.calledOnce)
})
})
describe('#postTxData', () => {
it('should route to controller', async () => {
const spy = sandbox.stub(uut.bchRESTController, 'txData').resolves(true)
await uut.postTxData()
assert.isTrue(spy.calledOnce)
})
})
describe('#postPubKey', () => {
it('should route to controller', async () => {
const spy = sandbox.stub(uut.bchRESTController, 'pubKey').resolves(true)
await uut.postPubKey()
assert.isTrue(spy.calledOnce)
})
})
describe('#utxoIsValid', () => {
it('should route to controller', async () => {
const spy = sandbox.stub(uut.bchRESTController, 'utxoIsValid').resolves(true)
await uut.utxoIsValid()
assert.isTrue(spy.calledOnce)
})
})
describe('#getTokenData', () => {
it('should route to controller', async () => {
const spy = sandbox.stub(uut.bchRESTController, 'getTokenData').resolves(true)
await uut.getTokenData()
assert.isTrue(spy.calledOnce)
})
})
describe('#getTokenData2', () => {
it('should route to controller', async () => {
const spy = sandbox.stub(uut.bchRESTController, 'getTokenData2').resolves(true)
await uut.getTokenData2()
assert.isTrue(spy.calledOnce)
})
})
describe('#getService', () => {
it('should route to controller', async () => {
const spy = sandbox.stub(uut.bchRESTController, 'getService').resolves(true)
await uut.getService()
assert.isTrue(spy.calledOnce)
})
})
})
@@ -0,0 +1,223 @@
/*
Unit tests for the REST API handler for the /p2wdb endpoints.
*/
// Public npm libraries
import { assert } from 'chai'
import sinon from 'sinon'
// Local support libraries
import adapters from '../../../mocks/adapters/index.js'
import UseCasesMock from '../../../mocks/use-cases/index.js'
import P2wdbController from '../../../../../src/controllers/rest-api/p2wdb/controller.js'
import { context } from '../../../mocks/ctx-mock.js'
// import blah '../../../../unit/mocks/ctx-mock.js'
// console.log('blah: ', blah)
let uut
let sandbox
let ctx
describe('#P2WDB-REST-Controller', () => {
// const testUser = {}
beforeEach(() => {
const useCases = new UseCasesMock()
uut = new P2wdbController({ adapters, useCases })
sandbox = sinon.createSandbox()
// Mock the context object.
ctx = context()
})
afterEach(() => sandbox.restore())
describe('#constructor', () => {
it('should throw an error if adapters are not passed in', () => {
try {
uut = new P2wdbController()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Adapters library required when instantiating /p2wdb REST Controller.'
)
}
})
it('should throw an error if useCases are not passed in', () => {
try {
uut = new P2wdbController({ adapters })
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Use Cases library required when instantiating /p2wdb REST Controller.'
)
}
})
})
describe('#handleError', () => {
it('should pass an error message', () => {
try {
const err = {
status: 422,
message: 'Unprocessable Entity'
}
uut.handleError(ctx, err)
} catch (err) {
assert.include(err.message, 'Unprocessable Entity')
}
})
it('should still throw error if there is no message', () => {
try {
const err = {
status: 404
}
uut.handleError(ctx, err)
} catch (err) {
assert.include(err.message, 'Not Found')
}
})
})
describe('#getStatus', () => {
it('should return 422 status on arbitrary biz logic error', async () => {
try {
// Force an error
sandbox
.stub(uut.adapters.p2wdb, 'getStatus')
.rejects(new Error('test error'))
await uut.getStatus(ctx)
assert.fail('Unexpected result')
} catch (err) {
console.log('err: ', err)
assert.equal(err.status, 422)
assert.include(err.message, 'test error')
}
})
it('should return 200 status on success', async () => {
sandbox.stub(uut.adapters.p2wdb, 'getStatus').resolves('p2wdb')
await uut.getStatus(ctx)
// Assert the expected HTTP response
assert.equal(ctx.status, 200)
// Assert that expected properties exist in the returned data.
assert.equal(ctx.response.body.status, 'p2wdb')
})
})
describe('#postProvider', () => {
it('should return 422 status on arbitrary error', async () => {
try {
// Force an error
sandbox
.stub(uut.adapters.p2wdb, 'selectProvider')
.rejects(new Error('test error'))
ctx.request.body = {
addresses: 'blah'
}
await uut.postProvider(ctx)
assert.fail('Unexpected result')
} catch (err) {
console.log('err: ', err)
assert.equal(err.status, 422)
assert.include(err.message, 'test error')
}
})
it('should return 200 status on success', async () => {
sandbox.stub(uut.adapters.p2wdb, 'selectProvider').resolves({ status: 200 })
ctx.request.body = {
addresses: 'blah'
}
await uut.postProvider(ctx)
// Assert the expected HTTP response
assert.equal(ctx.status, 200)
})
})
describe('#getEntryByHash', () => {
it('should return 422 status on arbitrary error', async () => {
try {
// Force an error
sandbox
.stub(uut.adapters.p2wdb, 'getEntryByHash')
.rejects(new Error('test error'))
ctx.request.body = {
hash: 'blah'
}
await uut.getEntryByHash(ctx)
} catch (err) {
assert.equal(err.status, 422)
assert.include(err.message, 'test error')
}
})
it('should return 200 status on success', async () => {
sandbox.stub(uut.adapters.p2wdb, 'getEntryByHash').resolves({ status: 200 })
ctx.request.body = {
hash: 'blah'
}
await uut.getEntryByHash(ctx)
assert.equal(ctx.status, 200)
})
})
describe('#writeEntry', () => {
it('should return 422 status on arbitrary error', async () => {
try {
// Force an error
sandbox
.stub(uut.adapters.p2wdb, 'writeEntry')
.rejects(new Error('test error'))
ctx.request.body = {
txid: 'blah',
signature: 'blah',
message: 'blah',
data: 'blah'
}
await uut.writeEntry(ctx)
} catch (err) {
assert.equal(err.status, 422)
assert.include(err.message, 'test error')
}
})
it('should return 200 status on success', async () => {
sandbox.stub(uut.adapters.p2wdb, 'writeEntry').resolves({ status: 200 })
ctx.request.body = {
txid: 'blah',
signature: 'blah',
message: 'blah',
data: 'blah'
}
await uut.writeEntry(ctx)
assert.equal(ctx.status, 200)
})
})
})
@@ -0,0 +1,110 @@
/*
Unit tests for the REST API handler for the /users endpoints.
*/
// Public npm libraries
import { assert } from 'chai'
import sinon from 'sinon'
// Local support libraries
import adapters from '../../../mocks/adapters/index.js'
import UseCasesMock from '../../../mocks/use-cases/index.js'
// const app = require('../../../mocks/app-mock')
import P2wdbRouter from '../../../../../src/controllers/rest-api/p2wdb/index.js'
let uut
let sandbox
// let ctx
// const mockContext = require('../../../../unit/mocks/ctx-mock').context
describe('#P2WDB-REST-Router', () => {
// const testUser = {}
beforeEach(() => {
const useCases = new UseCasesMock()
uut = new P2wdbRouter({ adapters, useCases })
sandbox = sinon.createSandbox()
// Mock the context object.
// ctx = mockContext()
})
afterEach(() => sandbox.restore())
describe('#constructor', () => {
it('should throw an error if adapters are not passed in', () => {
try {
uut = new P2wdbRouter()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Adapters library required when instantiating P2WDB REST Controller.'
)
}
})
it('should throw an error if useCases are not passed in', () => {
try {
uut = new P2wdbRouter({ adapters })
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Use Cases library required when instantiating P2WDB REST Controller.'
)
}
})
})
describe('#attach', () => {
it('should throw an error if app is not passed in.', () => {
try {
uut.attach()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Must pass app object when attaching REST API controllers.'
)
}
})
})
describe('#getStatus', () => {
it('should route to controller', async () => {
const spy = sandbox.stub(uut.p2wdbRESTController, 'getStatus').resolves(true)
await uut.getStatus()
assert.isTrue(spy.calledOnce)
})
})
describe('#postProvider', () => {
it('should route to controller', async () => {
const spy = sandbox.stub(uut.p2wdbRESTController, 'postProvider').resolves(true)
await uut.postProvider()
assert.isTrue(spy.calledOnce)
})
})
describe('#entryFromHash', () => {
it('should route to controller', async () => {
const spy = sandbox.stub(uut.p2wdbRESTController, 'getEntryByHash').resolves(true)
await uut.entryFromHash()
assert.isTrue(spy.calledOnce)
})
})
describe('#write', () => {
it('should route to controller', async () => {
const spy = sandbox.stub(uut.p2wdbRESTController, 'writeEntry').resolves(true)
await uut.write()
assert.isTrue(spy.calledOnce)
})
})
})
@@ -0,0 +1,157 @@
/*
Unit tests for the REST API handler for the /price endpoints.
*/
// Public npm libraries
import { assert } from 'chai'
import sinon from 'sinon'
// Local support libraries
import adapters from '../../../mocks/adapters/index.js'
import UseCasesMock from '../../../mocks/use-cases/index.js'
import PriceController from '../../../../../src/controllers/rest-api/price/controller.js'
import { context } from '../../../mocks/ctx-mock.js'
// import blah '../../../../unit/mocks/ctx-mock.js'
// console.log('blah: ', blah)
let uut
let sandbox
let ctx
describe('#PRICE-REST-Controller', () => {
// const testUser = {}
beforeEach(() => {
const useCases = new UseCasesMock()
uut = new PriceController({ adapters, useCases })
sandbox = sinon.createSandbox()
// Mock the context object.
ctx = context()
})
afterEach(() => sandbox.restore())
describe('#constructor', () => {
it('should throw an error if adapters are not passed in', () => {
try {
uut = new PriceController()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Adapters library required when instantiating /price REST Controller.'
)
}
})
it('should throw an error if useCases are not passed in', () => {
try {
uut = new PriceController({ adapters })
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Use Cases library required when instantiating /price REST Controller.'
)
}
})
})
describe('#handleError', () => {
it('should pass an error message', () => {
try {
const err = {
status: 422,
message: 'Unprocessable Entity'
}
uut.handleError(ctx, err)
} catch (err) {
assert.include(err.message, 'Unprocessable Entity')
}
})
it('should still throw error if there is no message', () => {
try {
const err = {
status: 404
}
uut.handleError(ctx, err)
} catch (err) {
assert.include(err.message, 'Not Found')
}
})
})
describe('#getUSD', () => {
it('should return 422 status on arbitrary biz logic error', async () => {
try {
// Force an error
sandbox
.stub(uut.axios, 'request')
.rejects(new Error('test error'))
await uut.getUSD(ctx)
assert.fail('Unexpected result')
} catch (err) {
console.log('err: ', err)
assert.equal(err.status, 422)
assert.include(err.message, 'test error')
}
})
it('should return 200 status on success', async () => {
sandbox.stub(uut.axios, 'request').resolves({ data: { data: { rates: { USD: 1.00 } } } })
await uut.getUSD(ctx)
// Assert the expected HTTP response
assert.equal(ctx.status, 200)
// Assert that expected properties exist in the returned data.
assert.equal(ctx.response.body.usd, 1.00)
})
})
describe('#getXecPrice', () => {
it('should return 422 status on arbitrary biz logic error', async () => {
try {
sandbox.stub(uut.axios, 'request').rejects(new Error('test error'))
await uut.getXecPrice(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(err.status, 422)
assert.include(err.message, 'test error')
}
})
it('should return 200 status on success', async () => {
sandbox.stub(uut.axios, 'request').resolves({ data: { data: { ticker: { last: 1.00 } } } })
await uut.getXecPrice(ctx)
assert.equal(ctx.status, 200)
assert.equal(ctx.response.body.usd, 1.00)
})
})
describe('#getPsffppWritePrice', () => {
it('should return 200 status on success', async () => {
sandbox.stub(uut.useCases.ipfs, 'getWritePrice').resolves(1.00)
await uut.getPsffppWritePrice(ctx)
assert.equal(ctx.status, 200)
assert.equal(ctx.response.body.psfPrice, 1.00)
})
it('should return 422 status on arbitrary biz logic error', async () => {
try {
sandbox.stub(uut.useCases.ipfs, 'getWritePrice').rejects(new Error('test error'))
await uut.getPsffppWritePrice(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(err.status, 422)
assert.include(err.message, 'test error')
}
})
})
})
@@ -0,0 +1,102 @@
/*
Unit tests for the REST API handler for the /users endpoints.
*/
// Public npm libraries
import { assert } from 'chai'
import sinon from 'sinon'
// Local support libraries
import adapters from '../../../mocks/adapters/index.js'
import UseCasesMock from '../../../mocks/use-cases/index.js'
// const app = require('../../../mocks/app-mock')
import PriceRouter from '../../../../../src/controllers/rest-api/price/index.js'
let uut
let sandbox
// let ctx
// const mockContext = require('../../../../unit/mocks/ctx-mock').context
describe('#Price-REST-Router', () => {
// const testUser = {}
beforeEach(() => {
const useCases = new UseCasesMock()
uut = new PriceRouter({ adapters, useCases })
sandbox = sinon.createSandbox()
// Mock the context object.
// ctx = mockContext()
})
afterEach(() => sandbox.restore())
describe('#constructor', () => {
it('should throw an error if adapters are not passed in', () => {
try {
uut = new PriceRouter()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Adapters library required when instantiating Price REST Controller.'
)
}
})
it('should throw an error if useCases are not passed in', () => {
try {
uut = new PriceRouter({ adapters })
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Use Cases library required when instantiating Price REST Controller.'
)
}
})
})
describe('#attach', () => {
it('should throw an error if app is not passed in.', () => {
try {
uut.attach()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Must pass app object when attaching REST API controllers.'
)
}
})
})
describe('#getPrice', () => {
it('should route to controller', async () => {
const spy = sandbox.stub(uut.priceRESTController, 'getUSD').resolves(true)
await uut.getPrice()
assert.isTrue(spy.calledOnce)
})
})
describe('#getXecPrice', () => {
it('should route to controller', async () => {
const spy = sandbox.stub(uut.priceRESTController, 'getXecPrice').resolves(true)
await uut.getXecPrice()
assert.isTrue(spy.calledOnce)
})
})
describe('#getPsffppWritePrice', () => {
it('should route to controller', async () => {
const spy = sandbox.stub(uut.priceRESTController, 'getPsffppWritePrice').resolves(true)
await uut.getPsffppWritePrice()
assert.isTrue(spy.calledOnce)
})
})
})
+14 -5
View File
@@ -9,16 +9,25 @@ let currentEnv
describe('#config', () => {
before(() => {
// Backup the current environment setting.
currentEnv = process.env.SVC_ENV
currentEnv = process.env.CONSUMER_ENV
// Clear CONSUMER_ENV for the first test to ensure default behavior
delete process.env.CONSUMER_ENV
})
after(() => {
// Restore the environment setting before starting these tests.
process.env.SVC_ENV = currentEnv
if (currentEnv) {
process.env.CONSUMER_ENV = currentEnv
} else {
delete process.env.CONSUMER_ENV
}
})
it('Should return development environment config by default', async () => {
const importedConfig = await import('../../../config/index.js')
// Ensure CONSUMER_ENV is not set for this test
delete process.env.CONSUMER_ENV
const importedConfig = await import('../../../config/index.js?foo=bar0')
const config = importedConfig.default
// console.log('config: ', config)
@@ -29,7 +38,7 @@ describe('#config', () => {
// Hack to dynamically import a library multiple times:
// https://github.com/denoland/deno/issues/6946
process.env.SVC_ENV = 'test'
process.env.CONSUMER_ENV = 'test'
const importedConfig2 = await import('../../../config/index.js?foo=bar1')
const config = importedConfig2.default
@@ -39,7 +48,7 @@ describe('#config', () => {
})
it('Should return prod environment config', async () => {
process.env.SVC_ENV = 'prod'
process.env.CONSUMER_ENV = 'prod'
process.env.WALLET_INTERFACE = 'web2'
process.env.APISERVER = 'https://api.fullstack.cash/v5/'
+17 -2
View File
@@ -30,7 +30,7 @@ class IpfsAdapter {
class IpfsCoordAdapter {
constructor () {
this.config = {}
this.ipfsCoord = {
adapters: {
ipfs: {
@@ -187,6 +187,9 @@ class BchUseCaseMock {
async waitForRPCResponse () {
return {}
}
async selectProvider (){
return true
}
}
const bch = new BchUseCaseMock()
const wallet = {
@@ -212,6 +215,18 @@ const ipfsFiles = {
success: true,
message: 'Pins'
}
},
rpcHandler: () => {
return {
success: true,
message: 'RPC handler'
}
}
}
export default { ipfs, localdb, bch, wallet, ipfsFiles }
const p2wdb = {
getStatus: async () => {},
selectProvider: async () => {},
getEntryByHash: async () => {},
writeEntry: async () => {}
}
export default { ipfs, localdb, bch, wallet, ipfsFiles, p2wdb }
+110 -2
View File
@@ -4,7 +4,9 @@
const peers = [
'QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2',
'QmQqjVVD3CS6zaJmoSdt6GwX1c1tD5T9FZas4qxLbru8xN'
'QmQqjVVD3CS6zaJmoSdt6GwX1c1tD5T9FZas4qxLbru8xN',
'QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mT3',
'QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mD4'
]
const peerData = [
@@ -110,7 +112,113 @@ const peerData = [
},
updatedAt: '2021-08-24T23:13:20.411Z'
}
}
},
{
from: 'QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mT3',
channel: 'psf-ipfs-coordination-001',
data: {
apiName: 'ipfs-coord-announce',
apiVersion: '1.3.2',
apiInfo:
'You should put an IPFS hash or web URL here to your documentation.',
ipfsId: 'QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2',
type: 'node.js',
ipfsMultiaddrs: [
'/ip4/127.0.0.1/tcp/5701/p2p/QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2',
'/ip4/127.0.0.1/tcp/5702/ws/p2p/QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2',
'/ip4/192.168.0.2/tcp/5701/p2p/QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2',
'/ip4/192.168.0.2/tcp/5702/ws/p2p/QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2'
],
orbitdb:
'/orbitdb/zdpuAntCMCcmZuFy2hq5F7kpcti3yHa34LLZHzyVqorkoSpAJ/QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ221082417',
circuitRelays: [],
isCircuitRelay: false,
cryptoAddresses: [
{
blockchain: 'BCH',
type: 'cashAddr',
address: 'bitcoincash:qru6kq3p4tv6z2lmy0n560lyhh3z2feay5gjzggc37'
},
{
blockchain: 'BCH',
type: 'slpAddr',
address: 'simpleledger:qru6kq3p4tv6z2lmy0n560lyhh3z2feay5yffnac0q'
}
],
encryptPubKey:
'021db6e97650659653ba61dd493dc348a7429cdcbafd4fb73b08b1223bf5bd98df',
jsonLd: {
'@context': 'https://schema.org/',
'@type': 'WebAPI',
name: 'trout-bch-wallet-service-dev',
version: '1.11.12',
protocol: 'p2wdb',
description:
'IPFS service providing BCH blockchain access needed by a wallet.',
documentation: 'https://ipfs-bch-wallet-service.fullstack.cash/',
provider: {
'@type': 'Organization',
name: 'Permissionless Software Foundation',
url: 'https://PSFoundation.cash'
},
identifier: 'QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2'
},
updatedAt: '2021-08-24T23:13:19.306Z'
}
},
{
from: 'QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mD4',
channel: 'psf-ipfs-coordination-001',
data: {
apiName: 'ipfs-coord-announce',
apiVersion: '1.3.2',
apiInfo:
'You should put an IPFS hash or web URL here to your documentation.',
ipfsId: 'QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2',
type: 'node.js',
ipfsMultiaddrs: [
'/ip4/127.0.0.1/tcp/5701/p2p/QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2',
'/ip4/127.0.0.1/tcp/5702/ws/p2p/QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2',
'/ip4/192.168.0.2/tcp/5701/p2p/QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2',
'/ip4/192.168.0.2/tcp/5702/ws/p2p/QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2'
],
orbitdb:
'/orbitdb/zdpuAntCMCcmZuFy2hq5F7kpcti3yHa34LLZHzyVqorkoSpAJ/QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ221082417',
circuitRelays: [],
isCircuitRelay: false,
cryptoAddresses: [
{
blockchain: 'BCH',
type: 'cashAddr',
address: 'bitcoincash:qru6kq3p4tv6z2lmy0n560lyhh3z2feay5gjzggc37'
},
{
blockchain: 'BCH',
type: 'slpAddr',
address: 'simpleledger:qru6kq3p4tv6z2lmy0n560lyhh3z2feay5yffnac0q'
}
],
encryptPubKey:
'021db6e97650659653ba61dd493dc348a7429cdcbafd4fb73b08b1223bf5bd98df',
jsonLd: {
'@context': 'https://schema.org/',
'@type': 'WebAPI',
name: 'trout-bch-wallet-service-dev',
version: '1.11.12',
protocol: 'ipfs-file-pin-service',
description:
'IPFS service providing BCH blockchain access needed by a wallet.',
documentation: 'https://ipfs-bch-wallet-service.fullstack.cash/',
provider: {
'@type': 'Organization',
name: 'Permissionless Software Foundation',
url: 'https://PSFoundation.cash'
},
identifier: 'QmWkjYRRTaxVEuGK8ip2X3trVyJShFs6U9g1h9x6fK5mZ2'
},
updatedAt: '2021-08-24T23:13:19.306Z'
}
},
]
// module.exports = {
+1 -1
View File
@@ -17,7 +17,7 @@ class MockBchWallet {
this.walletInfoPromise = true;
this.walletInfo = mockWallet;
this.initialize = async () => {}
this.bchjs = new BCHJS();
this.bchjs = new BCHJS({ restURL: 'https://free-bch.fullstack.cash/' });
this.burnTokens = async () => {
return { success: true, txid: 'txid' };
};
+4
View File
@@ -95,6 +95,10 @@ class IpfsUseCaseMock {
async pinClaim() {
return {}
}
async getWritePrice() {
return 1.00
}
}
class UseCasesMock {
+13 -1
View File
@@ -14,7 +14,6 @@ import sinon from 'sinon'
import UseCases from '../../../src/use-cases/ipfs-use-cases.js'
import adapters from '../mocks/adapters/index.js'
describe('#ipfs-use-cases', () => {
let uut
let sandbox
@@ -92,15 +91,28 @@ describe('#ipfs-use-cases', () => {
const result = await uut.getWritePrice()
assert.isNumber(result)
})
it('should re-instantiate psffpp instance', async () => {
const psffppMock = {}
uut.psffpp = psffppMock
const lastWritePriceUpdate = new Date()
lastWritePriceUpdate.setHours(lastWritePriceUpdate.getHours() - 10)
uut.lastWritePriceUpdate = lastWritePriceUpdate.getTime()
const result = await uut.getWritePrice()
assert.isNumber(result)
assert.notEqual(uut.lastWritePriceUpdate, lastWritePriceUpdate.getTime())
assert.notEqual(uut.psffpp, psffppMock)
})
it('should handle error ', async () => {
try {
uut.lastWritePriceUpdate = new Date().getTime()
uut.psffpp = {
getMcWritePrice: () => {
throw new Error('test error')
}
}
await uut.getWritePrice()
assert.fail('Unexpected code path')
} catch (error) {
assert.include(error.message, 'test error')
}
+1 -1
View File
@@ -1,6 +1,6 @@
import mongoose from 'mongoose'
import config from '../../config/index.js'
import User from '../../src/models/users.js'
import User from '../../src/adapters/localdb/models/users.js'
async function getUsers () {
// Connect to the Mongo Database.
+30
View File
@@ -0,0 +1,30 @@
import mongoose from 'mongoose'
import config from '../../config/index.js'
import User from '../../src/adapters/localdb/models/users.js'
async function getUsers () {
// Connect to the Mongo Database.
mongoose.Promise = global.Promise
mongoose.set('useCreateIndex', true) // Stop deprecation warning.
await mongoose.connect(
config.database,
{ useNewUrlParser: true, useUnifiedTopology: true }
)
// Find the user by email.
const user = await User.findOne({
email: 'test@test.com'
}, '-password')
// Update the users password
// user.password = 'newpassword'
// Change the user to an admin
// user.type = 'admin'
// Save the changes to the database.
await user.save()
mongoose.connection.close()
}
getUsers()