Compare commits

..
62 Commits
Author SHA1 Message Date
Chris Troutner 7b9bde456e Merge pull request #31 from Permissionless-Software-Foundation/ct-unstable
Syncing with upstream ipfs-service-provider
2022-06-21 15:59:09 -07:00
Chris Troutner ed0d468c2d Fixing merge conflicts 2022-06-21 15:56:38 -07:00
Chris Troutner fe1b428dac Adding production env var 2022-06-21 15:34:31 -07:00
Chris Troutner 0333f77f5a Fixing broken link in README 2022-06-20 07:30:10 -07:00
Chris Troutner 3c908d1a4d Merge pull request #90 from Permissionless-Software-Foundation/ct-unstable
Increasing test coverage to 100%
2022-06-20 07:22:10 -07:00
Chris Troutner eb46a4c130 fix(tests): Got to 100% test coverage for entire repo 2022-06-20 07:19:36 -07:00
Chris Troutner d0efdcfa07 fix(ipfs-coord): Increased unit test coverage to 100% 2022-06-20 07:01:43 -07:00
Chris Troutner 62a7fcb16d fix(tests): Got bin/server.js unit test coverage to 100% 2022-06-19 20:21:40 -07:00
Chris Troutner d8addd5b27 Trying to build unit tests for bin/server 2022-06-19 20:00:25 -07:00
Chris Troutner bf6f1cf62d Merge pull request #89 from Permissionless-Software-Foundation/ct-unstable
Updating dependencies
2022-06-19 13:25:07 -07:00
Chris Troutner b7df7c47a5 Updated ipfs 2022-06-19 13:23:52 -07:00
Chris Troutner ae260db4af Updated some dependencies 2022-06-19 13:19:31 -07:00
Chris Troutner 778098ca12 fix(ipfs-coord): Updating to v7.1.19 2022-05-28 09:29:31 -07:00
Chris Troutner 0c4bb9c4a1 fix(ipfs-coord): Updating to v7.1.18 2022-05-28 09:09:44 -07:00
Chris Troutner 9cb975656d Merge pull request #88 from Permissionless-Software-Foundation/ct-unstable
fix(ipfs-coord): Updating to v7.1.17
2022-05-28 08:00:57 -07:00
Chris Troutner 4f6564423e fix(ipfs-coord): Updating to v7.1.17 2022-05-28 07:59:47 -07:00
Chris Troutner 54e7e74ae5 Merge pull request #87 from Permissionless-Software-Foundation/ct-unstable
Subscribing to chat channel at startup
2022-05-26 09:14:50 -07:00
Chris Troutner 324c372119 fix(startup): Subscribing to chat pubsub channel at startup 2022-05-26 09:12:15 -07:00
Chris Troutner da62f1bbe3 linting 2022-05-26 08:24:45 -07:00
Chris Troutner 129047fe88 fix(startup): Announcing config settings at startup 2022-05-26 08:24:30 -07:00
Chris Troutner 3f652688b0 Merge pull request #30 from Permissionless-Software-Foundation/ct-unstable
fix(docker): Using updated image
2022-05-16 15:53:39 -07:00
Chris Troutner 3ccbfdb614 fix(docker): Using updated image 2022-05-16 15:52:34 -07:00
Chris Troutner 5b4d00b949 Merge pull request #29 from Permissionless-Software-Foundation/ct-unstable
fix(docker): mounting startup script on startup
2022-05-16 15:18:42 -07:00
Chris Troutner a666dbcafd fix(docker): mounting startup script on startup 2022-05-16 15:16:48 -07:00
Chris Troutner 3d6d544c21 Merge pull request #28 from Permissionless-Software-Foundation/ct-unstable
fix(docker): Using image rather than dockerfile
2022-05-16 14:51:22 -07:00
Chris Troutner 2ca54865bc fix(docker): Using image rather than dockerfile 2022-05-16 14:18:09 -07:00
Chris Troutner df778d6d8e Merge pull request #27 from Permissionless-Software-Foundation/ct-unstable
Syncing with upstream ipfs-service-provider & removing event emitter dependency.
2022-05-16 14:08:55 -07:00
Chris Troutner 3c64f44dfd Reducing screen spam 2022-05-16 14:06:37 -07:00
Chris Troutner 45ea47d4d0 fix(config): Fixing env var setting 2022-05-16 13:56:19 -07:00
Chris Troutner 150e3d6799 Fixing env var 2022-05-16 13:54:04 -07:00
Chris Troutner b3011bcb69 linting 2022-05-16 13:32:46 -07:00
Chris Troutner ccbe9a6707 feat(event emitter): Removing dependency on event emitter 2022-05-16 13:32:16 -07:00
Chris Troutner 23ebdaa7f3 Reducing debug level 2022-05-16 12:35:51 -07:00
Chris Troutner fb182ced58 Merge pull request #86 from Permissionless-Software-Foundation/ct-unstable
Updating docker-compose file
2022-05-16 12:33:03 -07:00
Chris Troutner bec7c11e46 Updating README 2022-05-16 12:31:53 -07:00
Chris Troutner fb10e10226 fix(production): Updating docker images 2022-05-16 12:30:58 -07:00
Chris Troutner 5b37e47747 Merge pull request #85 from Permissionless-Software-Foundation/ct-unstable
fix(bch-js): Updating to v6.4.1
2022-05-16 12:15:47 -07:00
Chris Troutner a3cea782ba fix(bch-js): Updating to v6.4.1 2022-05-16 12:14:20 -07:00
Chris Troutner 14ffdae194 Merge pull request #84 from Permissionless-Software-Foundation/dh-test-coverage
feat(coverage): Got ipfs-coord.js to 100% test coverage
2022-05-16 12:09:18 -07:00
Chris Troutner 5792871e0a Merge pull request #26 from Permissionless-Software-Foundation/ct-unstable
Adding utxoIsValid and getTokenData endpoints
2022-05-10 13:19:27 -07:00
Chris Troutner c2ec08f03d feat(getTokenData): Added endpoint for getting data associated with a token 2022-05-10 13:18:24 -07:00
Chris Troutner e4489cfcac linting 2022-05-10 13:06:21 -07:00
Chris Troutner e8c08beeb3 feat(utxoIsValid): Adding endpoint support for utxoIsValid 2022-05-10 13:06:03 -07:00
Chris Troutner 4a64423885 fix(bch-js): Updated to v6.3.4 2022-05-10 11:53:55 -07:00
Daniel Gonzalez 7e3ce1564d feat(coverage): Got ipfs-coord.js to 100% test coverage 2022-03-25 22:12:11 -04:00
Chris Troutner 8154dd96f9 Merge pull request #83 from Permissionless-Software-Foundation/ct-unstable
fix(deps): Updating bch-js
2022-03-20 07:48:42 -07:00
Chris Troutner e37223cc88 fix(deps): Updating bch-js 2022-03-20 07:46:08 -07:00
Chris Troutner 5002ef2b90 Merge pull request #82 from Permissionless-Software-Foundation/dh-test-coverage-ipfsjs
feat(test): Got ipfs.js to 100% test coverage
2022-03-18 11:27:32 -07:00
Chris Troutner 5d95109ad4 more linting 2022-03-18 11:24:15 -07:00
Chris Troutner 13b3150f62 minor linting 2022-03-18 11:24:04 -07:00
Daniel Gonzalez b3a4a7c1ab feat(test): Got ipfs.js to 100% test coverage 2022-03-15 18:58:55 -04:00
Chris Troutner d6b64fce59 Merge pull request #25 from Permissionless-Software-Foundation/ct-unstable
fix(production): Updating IPFS container from v11 to v12
2022-03-15 14:15:22 -07:00
Chris Troutner 8c12222d9c fix(production): Updating IPFS container from v11 to v12 2022-03-15 14:13:22 -07:00
Chris Troutner 26a79d1880 Merge pull request #24 from Permissionless-Software-Foundation/ct-unstable
fix(production): Slight tweeks to docker container
2022-03-15 13:33:12 -07:00
Chris Troutner 3f96b608b5 fix(production): Slight tweeks to docker container 2022-03-15 13:31:00 -07:00
Chris Troutner a669fc6440 Merge pull request #23 from Permissionless-Software-Foundation/ct-unstable
fix(ipfs-coord): Updating to v7.1.5
2022-03-10 09:58:44 -08:00
Chris Troutner 42ee03a6d9 Merge branch 'master' of https://github.com/Permissionless-Software-Foundation/ipfs-service-provider into ct-unstable 2022-03-10 09:56:50 -08:00
Chris Troutner 733e8fd405 Merge pull request #81 from Permissionless-Software-Foundation/ct-unstable
fix(ipfs-coord): Updating to v7.1.6
2022-03-10 09:29:32 -08:00
Chris Troutner baee72c1c6 fix(ipfs-coord): Updating to v7.1.6 2022-03-10 09:27:13 -08:00
Chris Troutner 5c2ab8a8cf Merge branch 'master' of https://github.com/Permissionless-Software-Foundation/ipfs-service-provider into ct-unstable 2022-03-10 08:47:54 -08:00
Chris Troutner 11618a796f Merge pull request #80 from Permissionless-Software-Foundation/ct-unstable
fix(ipfs-coord): Updating to v7.1.5
2022-03-10 08:35:21 -08:00
Chris Troutner 025ae20704 fix(ipfs-coord): Updating to v7.1.5 2022-03-10 08:33:53 -08:00
28 changed files with 8352 additions and 9558 deletions
+2 -6
View File
@@ -38,13 +38,11 @@ npm install
The [docker](./production/docker) directory contains a Dockerfile for building a production deployment.
```
docker-compose build --no-cache
docker-compose pull
docker-compose up -d
```
- Bring the containers up once, then take them back down with `docker-compose down`.
- Copy the [swarm.key](./swarm.key) file to `ipfs-service-provider/production/data/go-ipfs/data/swarm.key`.
- Bring the containers back up with `docker-compose up -d`.
You can bring the containers back up with `docker-compose up -d`.
### Operation Notes
@@ -97,5 +95,3 @@ Snapshots pinned to IPFS will be listed here.
## License
[MIT](./LICENSE.md)
test
+33 -23
View File
@@ -24,31 +24,37 @@ const config = require('../config') // this first.
const AdminLib = require('../src/adapters/admin')
const errorMiddleware = require('../src/controllers/rest-api/middleware/error')
const { wlogger } = require('../src/adapters/wlogger')
const Controllers = require('../src/controllers')
class Server {
constructor () {
// Encapsulate dependencies
this.adminLib = new AdminLib()
this.controllers = new Controllers()
this.mongoose = mongoose
this.config = config
this.process = process
}
async startServer () {
try {
// Create a Koa instance.
const app = new Koa()
app.keys = [config.session]
app.keys = [this.config.session]
// Connect to the Mongo Database.
mongoose.Promise = global.Promise
mongoose.set('useCreateIndex', true) // Stop deprecation warning.
this.mongoose.Promise = global.Promise
this.mongoose.set('useCreateIndex', true) // Stop deprecation warning.
console.log(
`Connecting to MongoDB with this connection string: ${config.database}`
`Connecting to MongoDB with this connection string: ${this.config.database}`
)
await mongoose.connect(config.database, {
await this.mongoose.connect(this.config.database, {
useUnifiedTopology: true,
useNewUrlParser: true
})
console.log(`Starting environment: ${config.env}`)
console.log(`Debug level: ${config.debugLevel}`)
console.log(`Starting environment: ${this.config.env}`)
console.log(`Debug level: ${this.config.debugLevel}`)
// MIDDLEWARE START
@@ -74,19 +80,17 @@ class Server {
app.use(cors({ origin: '*' }))
// Attach REST API and JSON RPC controllers to the app.
const Controllers = require('../src/controllers')
const controllers = new Controllers()
await controllers.attachRESTControllers(app)
await this.controllers.attachRESTControllers(app)
app.controllers = controllers
app.controllers = this.controllers
// MIDDLEWARE END
console.log(`Running server in environment: ${config.env}`)
wlogger.info(`Running server in environment: ${config.env}`)
console.log(`Running server in environment: ${this.config.env}`)
wlogger.info(`Running server in environment: ${this.config.env}`)
await app.listen(config.port)
console.log(`Server started on ${config.port}`)
this.server = await app.listen(this.config.port)
console.log(`Server started on ${this.config.port}`)
// Create the system admin user.
const success = await this.adminLib.createSystemUser()
@@ -94,10 +98,16 @@ class Server {
// Attach the other IPFS controllers.
// Skip if this is a test environment.
if (config.env !== 'test') {
await controllers.attachControllers(app)
if (this.config.env !== 'test') {
await this.controllers.attachControllers(app)
}
// Display configuration settings
console.log('\nConfiguration:')
console.log(`Circuit Relay: ${this.config.isCircuitRelay}`)
console.log(`IPFS TCP port: ${this.config.ipfsTcpPort}`)
console.log(`IPFS WS port: ${this.config.ipfsWsPort}\n`)
return app
} catch (err) {
console.error('Could not start server. Error: ', err)
@@ -105,14 +115,14 @@ class Server {
console.log(
'Exiting after 5 seconds. Depending on process manager to restart.'
)
await sleep(5000)
process.exit(1)
await this.sleep(5000)
this.process.exit(1)
}
}
sleep (ms) {
return new Promise((resolve) => setTimeout(resolve, ms))
}
}
function sleep (ms) {
return new Promise((resolve) => setTimeout(resolve, ms))
}
module.exports = Server
+4 -2
View File
@@ -94,9 +94,11 @@ module.exports = {
: '',
// Settings for production, using external go-ipfs node.
isProduction: process.env.SVC_ENV === 'production' ? true : false,
isProduction: process.env.CONSUMER_ENV === 'production' ? true : false,
ipfsHost: process.env.IPFS_HOST ? process.env.IPFS_HOST : 'localhost',
ipfsApiPort: process.env.IPFS_API_PORT
? parseInt(process.env.IPFS_API_PORT)
: 5001
: 5001,
chatPubSubChan: 'psf-ipfs-chat-001'
}
+7608 -9382
View File
File diff suppressed because it is too large Load Diff
+10 -10
View File
@@ -23,12 +23,12 @@
},
"repository": "Permissionless-Software-Foundation/ipfs-bch-wallet-consumer",
"dependencies": {
"@psf/bch-js": "5.3.2",
"axios": "0.25.0",
"@psf/bch-js": "6.4.1",
"axios": "0.27.2",
"bcryptjs": "2.4.3",
"glob": "7.1.6",
"ipfs": "0.60.0",
"ipfs-coord": "7.1.4",
"ipfs": "0.62.3",
"ipfs-coord": "7.1.19",
"ipfs-http-client": "55.0.0",
"jsonrpc-lite": "2.2.0",
"jsonwebtoken": "8.5.1",
@@ -45,9 +45,9 @@
"koa-static": "5.0.0",
"koa2-ratelimit": "0.9.1",
"line-reader": "0.4.0",
"mongoose": "5.11.15",
"mongoose": "5.13.14",
"node-fetch": "npm:@achingbrain/node-fetch@2.6.7",
"nodemailer": "6.4.17",
"nodemailer": "6.7.5",
"passport-local": "1.0.0",
"public-ip": "4.0.4",
"semver": "^7.3.5",
@@ -55,7 +55,7 @@
"winston-daily-rotate-file": "4.5.0"
},
"devDependencies": {
"apidoc": "0.26.0",
"apidoc": "0.51.1",
"chai": "4.3.0",
"coveralls": "3.1.0",
"eslint": "7.19.0",
@@ -65,10 +65,10 @@
"eslint-plugin-prettier": "3.3.1",
"eslint-plugin-standard": "4.0.0",
"husky": "4.3.8",
"lodash.clonedeep": "^4.5.0",
"mocha": "8.2.1",
"lodash.clonedeep": "4.5.0",
"mocha": "10.0.0",
"nyc": "15.1.0",
"semantic-release": "17.4.4",
"semantic-release": "19.0.3",
"sinon": "9.2.4",
"standard": "16.0.3",
"uuid": "8.3.2"
+4 -4
View File
@@ -12,7 +12,7 @@ RUN apt-get update
RUN apt-get install -y sudo git curl nano gnupg wget
#Install Node and NPM
RUN curl -sL https://deb.nodesource.com/setup_14.x -o nodesource_setup.sh
RUN curl -sL https://deb.nodesource.com/setup_16.x -o nodesource_setup.sh
RUN bash nodesource_setup.sh
RUN apt-get install -y nodejs build-essential
@@ -46,11 +46,11 @@ RUN npm set registry http://94.130.170.209:4873/
# Clone the rest.bitcoin.com repository
WORKDIR /home/safeuser
RUN git clone https://github.com/Permissionless-Software-Foundation/ipfs-service-provider
RUN git clone https://github.com/Permissionless-Software-Foundation/ipfs-bch-wallet-consumer
# Switch to the desired branch. `master` is usually stable,
# and `stage` has the most up-to-date changes.
WORKDIR /home/safeuser/ipfs-service-provider
WORKDIR /home/safeuser/ipfs-bch-wallet-consumer
# For development: switch to unstable branch
RUN git checkout ct-unstable
@@ -69,7 +69,7 @@ VOLUME /home/safeuser/keys
EXPOSE 5010
# Start the application.
COPY start-production.sh start-production.sh
#COPY start-production.sh start-production.sh
CMD ["./start-production.sh"]
#CMD ["npm", "start"]
+14 -12
View File
@@ -3,9 +3,9 @@
version: '3.9'
services:
mongo-ipfs-service:
mongo-bch-consumer:
image: mongo
container_name: mongo-ipfs-service
container_name: mongo-bch-consumer
ports:
- '5555:27017' # <host port>:<container port>
volumes:
@@ -13,9 +13,9 @@ services:
command: mongod --logpath=/dev/null # -- quiet
restart: always
ipfs:
image: ipfs/go-ipfs:v0.11.0
container_name: ipfs
ipfs-bch-consumer:
image: ipfs/go-ipfs:v0.12.0
container_name: ipfs-bch-consumer
environment:
MY_ENV_VAR: 'placeholder'
logging:
@@ -30,7 +30,7 @@ services:
- 172.17.0.1:8080:8080
volumes:
- ../data/go-ipfs/data:/data/ipfs
- ../data/go-ipfs/export:/export
- ../../swarm.key:/data/ipfs/swarm.key
# https://docs.docker.com/compose/compose-file/compose-file-v3/#command
# https://github.com/ipfs/go-ipfs/blob/91c52657166bcf86f2476926e4fe56694dc26562/Dockerfile#L115
command:
@@ -42,9 +42,10 @@ services:
]
restart: always
ipfs-service:
build: .
container_name: ipfs-service
bch-consumer:
#build: .
image: christroutner/ipfs-bch-wallet-consumer:v1.8.2
container_name: bch-consumer
logging:
driver: 'json-file'
options:
@@ -52,10 +53,11 @@ services:
max-file: '10'
mem_limit: 500mb
links:
- mongo-ipfs-service
- ipfs
- mongo-bch-consumer
- ipfs-bch-consumer
ports:
- '5010:5010' # <host port>:<container port>
volumes:
- ../data/ipfsdata:/home/safeuser/ipfs-service-provider/.ipfsdata
- ../data/ipfsdata:/home/safeuser/ipfs-bch-wallet-consumer/.ipfsdata
- ./start-production.sh:/home/safeuser/ipfs-bch-wallet-consumer/start-production.sh
restart: always
+4 -4
View File
@@ -8,7 +8,7 @@
export MNEMONIC="olive two muscle bottom coral ancient wait legend bronze useful process session"
# The human readable name this IPFS node identifies as.
export COORD_NAME=ipfs-service-provider-generic
export COORD_NAME=bch-consumer-generic
# Allow this node to function as a circuit relay. It must not be behind a firewall.
#export ENABLE_CIRCUIT_RELAY=true
@@ -17,19 +17,19 @@ export COORD_NAME=ipfs-service-provider-generic
#export CR_DOMAIN=subdomain.yourdomain.com
# Debug level. 0 = minimal info. 2 = max info.
export DEBUG_LEVEL=2
export DEBUG_LEVEL=1
# END: Optional configuration settings
# Production database connection string.
export DBURL=mongodb://172.17.0.1:5555/ipfs-service-prod
export DBURL=mongodb://172.17.0.1:5555/ipfs-bch-consumer-prod
# Configure REST API port
export PORT=5010
# Production settings using external go-ipfs node.
export SVC_ENV=production
export CONSUMER_ENV=production
export IPFS_HOST=172.17.0.1
export IPFS_API_PORT=5001
export IPFS_TCP_PORT=4001
+3 -1
View File
@@ -13,7 +13,7 @@ export COORD_NAME=trout-dev-bch-wallet-consumer
export MNEMONIC="stomach mean catch march census author cousin size south romance pumpkin trap"
# 0 = less verbose. 3 = most verbose
export DEBUG_LEVEL=2
export DEBUG_LEVEL=1
# Production settings that use external IPFS node.
# https://github.com/christroutner/docker-ipfs
@@ -28,4 +28,6 @@ export IPFS_TCP_PORT=4001
# MongoDB connection string.
export DBURL=mongodb://localhost:27017/ipfs-service-dev
export CONSUMER_ENV=production
npm start
+91 -7
View File
@@ -21,15 +21,15 @@ class BchAdapter {
'An instance of IPFS must be passed when instantiating the BCH Adapter library.'
)
}
this.eventEmitter = localConfig.eventEmitter
if (!this.eventEmitter) {
throw new Error(
'An instance of an EventEmitter must be passed when instantiating the adapters.'
)
}
// this.eventEmitter = localConfig.eventEmitter
// if (!this.eventEmitter) {
// throw new Error(
// 'An instance of an EventEmitter must be passed when instantiating the adapters.'
// )
// }
// Connect the RPC handler when the event fires with new data.
this.eventEmitter.on('rpcData', this.rpcHandler)
// this.eventEmitter.on('rpcData', this.rpcHandler)
// Encapsulate dependencies
this.uid = uid
@@ -387,6 +387,90 @@ class BchAdapter {
}
}
async utxoIsValid (utxo) {
try {
// Throw an error if this IPFS node has not yet made a connection to a
// wallet service provider.
const selectedProvider =
this.ipfs.ipfsCoordAdapter.state.selectedServiceProvider
if (!selectedProvider) {
throw new Error('No BCH Wallet Service provider available yet.')
}
const rpcData = {
endpoint: 'utxoIsValid',
utxo
}
// Generate a UUID for the call.
const rpcId = this.uid()
// Generate a JSON RPC command.
const cmd = this.jsonrpc.request(rpcId, 'bch', rpcData)
const cmdStr = JSON.stringify(cmd)
console.log('cmdStr: ', cmdStr)
// Send the RPC command to selected wallet service.
const thisNode = this.ipfs.ipfsCoordAdapter.ipfsCoord.thisNode
await this.ipfs.ipfsCoordAdapter.ipfsCoord.useCases.peer.sendPrivateMessage(
selectedProvider,
cmdStr,
thisNode
)
// Wait for data to come back from the wallet service.
const data = await this.waitForRPCResponse(rpcId)
return data
} catch (err) {
console.log('utxoIsValid() error: ', err)
wlogger.error('Error in adapters/bch.js/utxoIsValid()')
throw err
}
}
async getTokenData (tokenId) {
try {
// Throw an error if this IPFS node has not yet made a connection to a
// wallet service provider.
const selectedProvider =
this.ipfs.ipfsCoordAdapter.state.selectedServiceProvider
if (!selectedProvider) {
throw new Error('No BCH Wallet Service provider available yet.')
}
const rpcData = {
endpoint: 'getTokenData',
tokenId
}
// Generate a UUID for the call.
const rpcId = this.uid()
// Generate a JSON RPC command.
const cmd = this.jsonrpc.request(rpcId, 'bch', rpcData)
const cmdStr = JSON.stringify(cmd)
console.log('cmdStr: ', cmdStr)
// Send the RPC command to selected wallet service.
const thisNode = this.ipfs.ipfsCoordAdapter.ipfsCoord.thisNode
await this.ipfs.ipfsCoordAdapter.ipfsCoord.useCases.peer.sendPrivateMessage(
selectedProvider,
cmdStr,
thisNode
)
// Wait for data to come back from the wallet service.
const data = await this.waitForRPCResponse(rpcId)
return data
} catch (err) {
console.log('getTokenData() error: ', err)
wlogger.error('Error in adapters/bch.js/getTokenData()')
throw err
}
}
// Returns a promise that resolves to data when the RPC response is recieved.
async waitForRPCResponse (rpcId) {
try {
+8 -6
View File
@@ -24,12 +24,12 @@ const config = require('../../config')
class Adapters {
constructor (localConfig = {}) {
// Dependency injection.
this.eventEmitter = localConfig.eventEmitter
if (!this.eventEmitter) {
throw new Error(
'An instance of an EventEmitter must be passed when instantiating the adapters.'
)
}
// this.eventEmitter = localConfig.eventEmitter
// if (!this.eventEmitter) {
// throw new Error(
// 'An instance of an EventEmitter must be passed when instantiating the adapters.'
// )
// }
// Encapsulate dependencies
this.ipfs = new IPFSAdapter(localConfig)
@@ -60,6 +60,8 @@ class Adapters {
// Start the IPFS node.
await this.ipfs.start()
return true
} catch (err) {
console.error('Error in adapters/index.js/start()')
throw err
+12 -8
View File
@@ -2,18 +2,19 @@
top-level IPFS library that combines the individual IPFS-based libraries.
*/
// Local libraries
const IpfsAdapter = require('./ipfs')
const IpfsCoordAdapter = require('./ipfs-coord')
class IPFS {
constructor (localConfig = {}) {
// Dependency injection.
this.eventEmitter = localConfig.eventEmitter
if (!this.eventEmitter) {
throw new Error(
'An instance of an EventEmitter must be passed when instantiating the ipfs-coord adapter.'
)
}
// this.eventEmitter = localConfig.eventEmitter
// if (!this.eventEmitter) {
// throw new Error(
// 'An instance of an EventEmitter must be passed when instantiating the ipfs-coord adapter.'
// )
// }
// Encapsulate dependencies
this.ipfsAdapter = new IpfsAdapter()
@@ -39,12 +40,15 @@ class IPFS {
// Start ipfs-coord
this.ipfsCoordAdapter = new this.IpfsCoordAdapter({
ipfs: this.ipfs,
eventEmitter: this.eventEmitter
ipfs: this.ipfs
// eventEmitter: this.eventEmitter
})
await this.ipfsCoordAdapter.start()
console.log('ipfs-coord is ready.')
// Subscribe to the chat pubsub channel
await this.ipfsCoordAdapter.subscribeToChat()
return true
} catch (err) {
console.error('Error in adapters/ipfs/index.js/start()')
+23 -14
View File
@@ -31,12 +31,12 @@ class IpfsCoordAdapter {
'Instance of IPFS must be passed when instantiating ipfs-coord.'
)
}
this.eventEmitter = localConfig.eventEmitter
if (!this.eventEmitter) {
throw new Error(
'An instance of an EventEmitter must be passed when instantiating the ipfs-coord adapter.'
)
}
// this.eventEmitter = localConfig.eventEmitter
// if (!this.eventEmitter) {
// throw new Error(
// 'An instance of an EventEmitter must be passed when instantiating the ipfs-coord adapter.'
// )
// }
// Encapsulate dependencies
this.IpfsCoord = IpfsCoord
@@ -291,15 +291,24 @@ class IpfsCoordAdapter {
// This method handles input coming in from other IPFS peers.
// It passes the data on to the REST API library by emitting an event.
peerInputHandler (data) {
try {
// console.log('peerInputHandler triggered with this data: ', data)
// peerInputHandler (data) {
// try {
// // console.log('peerInputHandler triggered with this data: ', data)
//
// this.eventEmitter.emit('rpcData', data)
// } catch (err) {
// console.error('Error in ipfs-coord.js/peerInputHandler(): ', err)
// // Do not throw error. This is a top-level function.
// }
// }
this.eventEmitter.emit('rpcData', data)
} catch (err) {
console.error('Error in ipfs-coord.js/peerInputHandler(): ', err)
// Do not throw error. This is a top-level function.
}
// Subscribe to the chat pubsub channel
async subscribeToChat () {
await this.ipfsCoord.adapters.pubsub.subscribeToPubsubChannel(
this.config.chatPubSubChan,
console.log,
this.ipfsCoord.thisNode
)
}
}
+18 -18
View File
@@ -102,9 +102,9 @@ class IpfsAdapter {
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()
}
// if (err.message.includes('No space left on device')) {
// this.rmBlocksDir()
// }
throw err
}
@@ -120,21 +120,21 @@ class IpfsAdapter {
// Dev Note: It's assumed this node is not pinning any data and that
// everything in this directory is transient. This folder will regularly
// fill up and prevent IPFS from starting.
rmBlocksDir () {
try {
const dir = `${IPFS_DIR}/blocks`
console.log(`Deleting ${dir} directory...`)
this.fs.rmdirSync(dir, { recursive: true })
console.log(`${dir} directory is deleted!`)
return true // Signal successful execution.
} catch (err) {
console.log('Error in rmBlocksDir()')
throw err
}
}
// rmBlocksDir () {
// try {
// const dir = `${IPFS_DIR}/blocks`
// console.log(`Deleting ${dir} directory...`)
//
// this.fs.rmdirSync(dir, { recursive: true })
//
// console.log(`${dir} directory is deleted!`)
//
// return true // Signal successful execution.
// } catch (err) {
// console.log('Error in rmBlocksDir()')
// throw err
// }
// }
}
module.exports = IpfsAdapter
+7 -7
View File
@@ -21,15 +21,15 @@ class P2wdbAdapter {
'An instance of IPFS must be passed when instantiating the P2WDB Adapter library.'
)
}
this.eventEmitter = localConfig.eventEmitter
if (!this.eventEmitter) {
throw new Error(
'An instance of an EventEmitter must be passed when instantiating the adapters.'
)
}
// this.eventEmitter = localConfig.eventEmitter
// if (!this.eventEmitter) {
// throw new Error(
// 'An instance of an EventEmitter must be passed when instantiating the adapters.'
// )
// }
// Connect the RPC handler when the event fires with new data.
this.eventEmitter.on('rpcData', this.rpcHandler)
// this.eventEmitter.on('rpcData', this.rpcHandler)
// Encapsulate dependencies
this.uid = uid
+3 -3
View File
@@ -5,7 +5,7 @@
*/
// Public npm libraries.
const EventEmitter = require('events')
// const EventEmitter = require('events')
// Local libraries
const config = require('../../config')
@@ -20,8 +20,8 @@ class Controllers {
this.config = config
// Initialize EventEmitters used to pass event-driven data around the app.
this.eventEmitter = new EventEmitter()
localConfig.eventEmitter = this.eventEmitter
// this.eventEmitter = new EventEmitter()
// localConfig.eventEmitter = this.eventEmitter
// Initialize Clean Architecture classes. Inject dependencies.
this.adapters = new Adapters(localConfig)
+14 -1
View File
@@ -90,13 +90,14 @@ class JSONRPC {
}
// Process response from REST API.
_this.adapters.ipfs.ipfsCoordAdapter.peerInputHandler(parsedData)
// _this.adapters.ipfs.ipfsCoordAdapter.peerInputHandler(parsedData)
// Log the incoming JSON RPC command.
wlogger.info(
`JSON RPC received from ${from}, ID: ${parsedData.payload.id}, type: ${parsedData.type}`
)
}
// console.log(`parsedData: ${JSON.stringify(parsedData, null, 2)}`)
// Added the property "from" to the parsedData object;
// necessary for calculating rate limits (based on the IPFS ID).
@@ -105,6 +106,16 @@ class JSONRPC {
// Default return string
let retObj = _this.defaultResponse()
// Forward data on to bch adapter if this is the response of a BCH query.
try {
if (parsedData.payload.result.method === 'bch') {
console.log('routing to BCH adapter')
retObj = await _this.adapters.bch.rpcHandler(parsedData)
}
} catch (err) {
/* exit quietly */
}
// Route the command to the appropriate route handler.
switch (parsedData.payload.method) {
case 'users':
@@ -116,6 +127,8 @@ class JSONRPC {
case 'about':
retObj = await _this.aboutController.aboutRouter(parsedData)
break
case 'bch':
retObj = await _this.adapters.bch.rpcHandler(parsedData)
// default:
// // Process response from REST API.
// _this.adapters.ipfs.ipfsCoordAdapter.peerInputHandler(parsedData)
+117
View File
@@ -489,6 +489,8 @@ class BchRESTControllerLib {
* "pubkey": {
* "success": true,
* "publicKey": "033f267fec0f7eb2b27f8c2e3052b3d03b09d36b47de4082ffb638ffb334ef0eee"
* }
* }
* }
* }
*/
@@ -505,6 +507,121 @@ class BchRESTControllerLib {
}
}
/**
* @api {REST} /bch/utxoIsValid utxoIsValid
* @apiPermission public
* @apiName utxoIsValid
* @apiGroup REST BCH
* @apiDescription Verify if UTXO is valid
* Given a UTXO object (txid and vout), a full node is queried to verify that
* the UTXO still exists in the mempool (true), or if it has been spent (false).
*
* - jsonrpc: "" - jsonrpc version
* - id: "" - jsonrpc id
* - result: {} - Result of the petition with the RPC information
* - success: - Request status
* - isValid: - Boolean: true or false
*
* @apiExample Example usage:
* {"jsonrpc":"2.0","id":"555","method":"bch","params":{ "endpoint": "utxoIsValid", "utxo": {"tx_hash": "17754221b29f189532d4fc2ae89fb467ad2dede30fdec4854eb2129b3ba90d7a", "tx_pos": 0}}}
*
* @apiSuccessExample {json} Success-Response:
* {
* "jsonrpc":"2.0",
* "id":"555",
* "result":{
* "method":"bch",
* "reciever":"QmU86vLVbUY1UhziKB6rak7GPKRA2QHWvzNm2AjEvXNsT6",
* "value":{
* "success": true,
* "status": 200,
* "endpoint": "utxoIsValid",
* "isValid": true
* }
* }
*
* }
*/
async utxoIsValid (ctx) {
try {
const utxo = ctx.request.body.utxo
const data = await this.adapters.bch.utxoIsValid(utxo)
// console.log(`utxoIsValid data: ${JSON.stringify(data, null, 2)}`)
ctx.body = data
} catch (err) {
this.handleError(ctx, err)
}
}
/**
* @api {REST} /bch getTokenData
* @apiPermission public
* @apiName getTokenData
* @apiGroup REST BCH
* @apiDescription Get data associated with a token
* Given a token ID, this endpoint will retrieve the IPFS CIDs associated with
* the tokens mutable and immutable data. This is extension of the PS002
* specification for mutable data for tokens:
* https://github.com/Permissionless-Software-Foundation/specifications/blob/master/ps002-slp-mutable-data.md
*
* - jsonrpc: "" - jsonrpc version
* - id: "" - jsonrpc id
* - result: {} - Result of the petition with the RPC information
* - success: - Request status
* - getTokenData: - Address public key
*
* @apiExample Example usage:
* {"jsonrpc":"2.0","id":"555","method":"bch","params":{ "endpoint": "getTokenData", "tokenId": "c85042ab08a2099f27de880a30f9a42874202751d834c42717a20801a00aab0d" }}
*
* @apiSuccessExample {json} Success-Response:
* {
* "jsonrpc":"2.0",
* "id":"555",
* "result":{
* "method":"bch",
* "reciever":"QmU86vLVbUY1UhziKB6rak7GPKRA2QHWvzNm2AjEvXNsT6",
* "value":{
* "success": true,
* "status": 200,
* "endpoint": "getTokenData",
* "tokenData": {
* "genesisData": {
* "type": 1,
* "ticker": 'MT2',
* "name": 'Mutable Token',
* "tokenId": 'c85042ab08a2099f27de880a30f9a42874202751d834c42717a20801a00aab0d',
* "documentUri": 'ipfs://bafybeie7oxpsr7evcnlptecxfdhaqlot4732phukd2ekgvuzoir2frost4',
* "documentHash": '56ed1a5768076a318d02b5db64e125544dca57ab6b2cc7ca61dfa4645d244463',
* "decimals": 0,
* "mintBatonIsActive": true,
* "tokensInCirculationBN": '1000',
* "tokensInCirculationStr": '1000',
* "blockCreated": 739412,
* "totalBurned": '0',
* "totalMinted": '1000'
* },
* "immutableData": 'ipfs://bafybeie7oxpsr7evcnlptecxfdhaqlot4732phukd2ekgvuzoir2frost4',
* "mutableData": 'ipfs://bafybeigotuony53ley3n63hqwyxiqruqn5uamskmci6f645putnc46jju4'
* }
* }
* }
* }
*/
async getTokenData (ctx) {
try {
const tokenId = ctx.request.body.tokenId
const data = await this.adapters.bch.getTokenData(tokenId)
console.log(`getTokenData data: ${JSON.stringify(data, null, 2)}`)
ctx.body = data
} catch (err) {
this.handleError(ctx, err)
}
}
// DRY error handler
handleError (ctx, err) {
// If an HTTP status is specified by the buisiness logic, use that.
+10
View File
@@ -59,6 +59,8 @@ class BchRouter {
this.router.post('/txHistory', this.postTxHistory)
this.router.post('/txData', this.postTxData)
this.router.post('/pubkey', this.postPubKey)
this.router.post('/utxoIsValid', this.utxoIsValid)
this.router.post('/getTokenData', this.getTokenData)
// Attach the Controller routes to the Koa app.
app.use(this.router.routes())
@@ -96,6 +98,14 @@ class BchRouter {
async postPubKey (ctx, next) {
await _this.bchRESTController.pubKey(ctx, next)
}
async utxoIsValid (ctx, next) {
await _this.bchRESTController.utxoIsValid(ctx, next)
}
async getTokenData (ctx, next) {
await _this.bchRESTController.getTokenData(ctx, next)
}
}
module.exports = BchRouter
+6 -6
View File
@@ -14,12 +14,12 @@ class UseCases {
'Instance of adapters must be passed in when instantiating Use Cases library.'
)
}
this.eventEmitter = localConfig.eventEmitter
if (!this.eventEmitter) {
throw new Error(
'Instance of Event Emitter must be passed in when instantiating Use Cases library.'
)
}
// this.eventEmitter = localConfig.eventEmitter
// if (!this.eventEmitter) {
// throw new Error(
// 'Instance of Event Emitter must be passed in when instantiating Use Cases library.'
// )
// }
// console.log('use-cases/index.js localConfig: ', localConfig)
this.user = new UserUseCases(localConfig)
+53
View File
@@ -0,0 +1,53 @@
/*
Unit tests for the adapters index.js library
*/
// Global npm libraries
const assert = require('chai').assert
const sinon = require('sinon')
// Local libraries
const Adapters = require('../../../src/adapters')
describe('#adapters', () => {
let uut, sandbox
beforeEach(() => {
uut = new Adapters()
sandbox = sinon.createSandbox()
})
afterEach(() => {
sandbox.restore()
})
describe('#start', () => {
it('should start the async adapters', async () => {
// Mock dependencies
uut.config.getJwtAtStartup = true
sandbox.stub(uut.fullStackJwt, 'getJWT').resolves()
sandbox.stub(uut.fullStackJwt, 'instanceBchjs').resolves()
sandbox.stub(uut.ipfs, 'start').resolves()
const result = await uut.start()
assert.equal(result, true)
})
it('should catch and throw an error', async () => {
try {
// Force an error
uut.config.getJwtAtStartup = false
sandbox.stub(uut.ipfs, 'start').rejects(new Error('test error'))
await uut.start()
assert.fail('Unexpected result')
} catch (err) {
// console.log('err: ', err)
assert.include(err.message, 'test error')
}
})
})
})
+73 -13
View File
@@ -54,19 +54,19 @@ describe('#bch-use-case', () => {
}
})
it('should throw an error if adapters are not passed in', () => {
try {
const ipfs = {}
uut = new BchAdapter({ ipfs })
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'An instance of an EventEmitter must be passed when instantiating the adapters.'
)
}
})
// it('should throw an error if adapters are not passed in', () => {
// try {
// const ipfs = {}
// uut = new BchAdapter({ ipfs })
//
// assert.fail('Unexpected code path')
// } catch (err) {
// assert.include(
// err.message,
// 'An instance of an EventEmitter must be passed when instantiating the adapters.'
// )
// }
// })
})
describe('#waitForRPCResponse', () => {
@@ -284,4 +284,64 @@ describe('#bch-use-case', () => {
}
})
})
describe('#utxoIsValid', () => {
it('should validate a utxo', async () => {
// Force connection to a wallet service
uut.ipfs.ipfsCoordAdapter.state = {
selectedServiceProvider: 'abc123'
}
// Mock depenencies
sandbox.stub(uut, 'waitForRPCResponse').resolves({ key: 'value' })
const utxo = { a: 'b' }
const result = await uut.utxoIsValid(utxo)
// console.log('result: ', result)
assert.equal(result.key, 'value')
})
it('should catch and throw an error', async () => {
try {
await uut.utxoIsValid()
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.equal(err.message, 'test error')
}
})
})
describe('#getTokenData', () => {
it('should validate a utxo', async () => {
// Force connection to a wallet service
uut.ipfs.ipfsCoordAdapter.state = {
selectedServiceProvider: 'abc123'
}
// Mock depenencies
sandbox.stub(uut, 'waitForRPCResponse').resolves({ key: 'value' })
const tokenId = 'blah'
const result = await uut.getTokenData(tokenId)
// console.log('result: ', result)
assert.equal(result.key, 'value')
})
it('should catch and throw an error', async () => {
try {
await uut.getTokenData()
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.equal(err.message, 'test error')
}
})
})
})
+55 -29
View File
@@ -9,7 +9,7 @@ const cloneDeep = require('lodash.clonedeep')
const IPFSCoordAdapter = require('../../../src/adapters/ipfs/ipfs-coord')
const IPFSMock = require('../mocks/ipfs-mock')
const IPFSCoordMock = require('../mocks/ipfs-coord-mock')
const EventEmitter = require('events')
const config = require('../../../config')
const mockDataLib = require('../mocks/adapters/ipfs-coord-mocks')
describe('#IPFS', () => {
@@ -19,7 +19,7 @@ describe('#IPFS', () => {
beforeEach(() => {
const ipfs = IPFSMock.create()
uut = new IPFSCoordAdapter({ ipfs, eventEmitter: new EventEmitter() })
uut = new IPFSCoordAdapter({ ipfs })
sandbox = sinon.createSandbox()
@@ -47,19 +47,19 @@ describe('#IPFS', () => {
}
})
it('should throw an error if EventEmitter instance is not included', () => {
try {
const ipfs = IPFSMock.create()
uut = new IPFSCoordAdapter({ ipfs })
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'An instance of an EventEmitter must be passed when instantiating the ipfs-coord adapter.'
)
}
})
// it('should throw an error if EventEmitter instance is not included', () => {
// try {
// const ipfs = IPFSMock.create()
// uut = new IPFSCoordAdapter({ ipfs })
//
// assert.fail('Unexpected code path')
// } catch (err) {
// assert.include(
// err.message,
// 'An instance of an EventEmitter must be passed when instantiating the ipfs-coord adapter.'
// )
// }
// })
})
describe('#start', () => {
@@ -86,6 +86,16 @@ describe('#IPFS', () => {
assert.equal(result, true)
})
it('should return a promise that resolves into an instance of IPFS in production mode', async () => {
uut.config.isProduction = true
// Mock dependencies.
uut.IpfsCoord = IPFSCoordMock
const result = await uut.start()
// console.log('result: ', result)
assert.equal(result, true)
config.isProduction = false
})
})
describe('#attachRPCRouter', () => {
@@ -152,22 +162,38 @@ describe('#IPFS', () => {
})
})
describe('#peerInputHandler', () => {
it('should emit an event trigger', () => {
const data = 'some data'
// 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.')
// })
// })
uut.peerInputHandler(data)
describe('#subscribeToChat', () => {
it('should subscribe to the chat channel', async () => {
// Mock dependencies
uut.ipfsCoord = {
adapters: {
pubsub: {
subscribeToPubsubChannel: async () => {
}
}
}
}
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.')
await uut.subscribeToChat()
})
})
})
+35 -2
View File
@@ -4,10 +4,11 @@
const assert = require('chai').assert
const sinon = require('sinon')
const IPFSLib = require('../../../src/adapters/ipfs/ipfs')
const IPFSMock = require('../mocks/ipfs-mock')
const config = require('../../../config')
// config.isProduction = true;
describe('#IPFS-adapter', () => {
let uut
let sandbox
@@ -18,7 +19,27 @@ describe('#IPFS-adapter', () => {
sandbox = sinon.createSandbox()
})
afterEach(() => sandbox.restore())
afterEach(() => {
sandbox.restore()
})
describe('#constructor', () => {
it('should instantiate IPFS Lib in dev mode.', async () => {
const _uut = new IPFSLib()
assert.exists(_uut)
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 () => {
@@ -33,6 +54,18 @@ describe('#IPFS-adapter', () => {
assert.property(result, 'config')
})
it('should return a promise that resolves into an instance of IPFS in production mode.', async () => {
// Mock dependencies.
uut.IPFS = IPFSMock
uut.config.isProduction = true
const result = await uut.start()
// console.log('result: ', result)
assert.equal(uut.isReady, true)
assert.property(result, 'config')
})
it('should catch and throw an error', async () => {
try {
// Force an error
@@ -322,4 +322,76 @@ describe('#BCH-REST-Controller', () => {
assert.equal(ctx.status, 200)
})
})
describe('#utxoIsValid', () => {
it('should return 422 status on arbitrary error', async () => {
try {
// Force an error
sandbox
.stub(uut.adapters.bch, 'utxoIsValid')
.rejects(new Error('test error'))
ctx.request.body = {
utxo: 'blah'
}
await uut.utxoIsValid(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.bch, 'utxoIsValid').resolves({ status: 200 })
ctx.request.body = {
utxo: 'blah'
}
await uut.utxoIsValid(ctx)
// Assert the expected HTTP response
assert.equal(ctx.status, 200)
})
})
describe('#getTokenData', () => {
it('should return 422 status on arbitrary error', async () => {
try {
// Force an error
sandbox
.stub(uut.adapters.bch, 'getTokenData')
.rejects(new Error('test error'))
ctx.request.body = {
tokenId: 'blah'
}
await uut.getTokenData(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.bch, 'getTokenData').resolves({ status: 200 })
ctx.request.body = {
tokenId: 'blah'
}
await uut.getTokenData(ctx)
// Assert the expected HTTP response
assert.equal(ctx.status, 200)
})
})
})
+63
View File
@@ -0,0 +1,63 @@
/*
Unit tests for the bin/server.js file
*/
// Public npm libraries
const assert = require('chai').assert
const sinon = require('sinon')
// Local libraries
const Server = require('../../../bin/server')
describe('#server', () => {
let uut, sandbox
beforeEach(() => {
sandbox = sinon.createSandbox()
uut = new Server()
})
afterEach(() => sandbox.restore())
describe('#startServer', () => {
it('should start the server', async () => {
// Mock dependencies
sandbox.stub(uut.mongoose, 'connect').resolves()
sandbox.stub(uut.controllers, 'attachRESTControllers').resolves()
sandbox.stub(uut.adminLib, 'createSystemUser').resolves(true)
sandbox.stub(uut.controllers, 'attachControllers').resolves()
uut.config.env = 'dev'
const result = await uut.startServer()
// console.log('result: ', result)
assert.property(result, 'env')
// Turn off the server.
uut.server.close()
// Restor config env
uut.config.env = 'test'
})
it('should exit on failure', async () => {
// Force an error
sandbox.stub(uut.mongoose, 'connect').rejects(new Error('test error'))
// Prevent default behavior of exiting the program.
sandbox.stub(uut, 'sleep').resolves()
sandbox.stub(uut.process, 'exit').returns()
await uut.startServer()
// Not throwing an error is a success
})
})
describe('#sleep', () => {
it('should execute', async () => {
await uut.sleep(1)
})
})
})
+8
View File
@@ -93,6 +93,14 @@ class BchUseCaseMock {
return {}
}
async utxoIsValid() {
return {}
}
async getTokenData() {
return {}
}
async waitForRPCResponse () {
return {}
}
+2
View File
@@ -8,6 +8,8 @@ class IPFSCoord {
}
async start () {}
async subscribeToChat() {}
}
module.exports = IPFSCoord