Compare commits

..
19 Commits
Author SHA1 Message Date
Chris Troutner fb00c1bc86 Merge pull request #4 from Permissionless-Software-Foundation/ct-unstable
Getting Offer and Order creation working with updated deps.
2022-03-09 13:55:56 -08:00
Chris Troutner 7964bfbae7 fix(tests): Fixing broken unit tests 2022-03-09 13:52:45 -08:00
Chris Troutner a13a1c91e8 Got Offer creation working 2022-03-09 10:08:51 -08:00
Chris Troutner b19d4da345 Added integration tests for offer use case 2022-03-09 09:37:17 -08:00
Chris Troutner 443f405f56 fix(package.json): Updating dependencies 2022-03-09 08:11:20 -08:00
Chris Troutner 61bb5169b3 fix(p2wdb): Refactored p2wdb adapter and unit tests 2022-03-09 07:53:26 -08:00
Chris Troutner f812440555 fix(tests): Updated unit tests for node v16 2022-03-08 15:23:56 -08:00
Chris Troutner 693c98fb15 Merge branch 'master' of https://github.com/Permissionless-Software-Foundation/ipfs-service-provider into ct-unstable 2022-03-08 15:04:53 -08:00
Chris Troutner fdf73610eb Merge pull request #79 from Permissionless-Software-Foundation/ct-unstable
fix(tests): Updating tests for node.js v16
2022-03-08 09:42:42 -08:00
Chris Troutner b09e77c079 fix(tests): Updating tests for node.js v16 2022-03-08 09:39:54 -08:00
Chris Troutner 1a4c602234 Syncing with upstream ipfs-service-provider 2022-03-07 17:58:03 -08:00
Chris Troutner dedc6e2d30 syncing with upstream ipfs-service-provider 2022-03-07 17:56:52 -08:00
Chris Troutner 68ad2561b1 Merge pull request #78 from Permissionless-Software-Foundation/ct-unstable
fix(ipfs-coord): Updating to v7.1.4
2022-03-05 14:22:25 -08:00
Chris Troutner 791d11e597 Merge branch 'master' into ct-unstable 2022-03-05 14:20:19 -08:00
Chris Troutner 5489e76f55 fix(ipfs-coord): Updating to v7.1.4 2022-03-05 14:20:13 -08:00
Chris Troutner 496a1dd07c Merge pull request #77 from Permissionless-Software-Foundation/ct-unstable
fix(ipfs-coord): Updating to v7.1.3
2022-03-05 08:26:32 -08:00
Chris Troutner a7b8ca9ad5 linting 2022-03-05 08:24:50 -08:00
Chris Troutner 6eb16eb11a fix(ipfs-coord): Updating to v7.1.3 2022-03-05 08:24:36 -08:00
Chris Troutner d1fb676a8f Customizing for bch 2022-03-01 18:36:15 -08:00
36 changed files with 3762 additions and 12119 deletions
+5 -3
View File
@@ -15,7 +15,7 @@ const ipfsCoordName = process.env.COORD_NAME
module.exports = {
// Configure TCP port.
port: process.env.PORT || 5002,
port: process.env.PORT || 5700,
// Password for HTML UI that displays logs.
logPass: 'test',
@@ -37,6 +37,9 @@ module.exports = {
? process.env.CONSUMER_URL
: 'https://free-bch.fullstack.cash',
// P2WDB URL that will accept API calls from the p2wdb npm library.
p2wdbUrl: process.env.P2WDB_URL ? process.env.P2WDB_URL : 'https://p2wdb.fullstack.cash',
// FullStack.cash account information, used for automatic JWT handling.
getJwtAtStartup: process.env.GET_JWT_AT_STARTUP ? true : false,
authServer: process.env.AUTHSERVER
@@ -67,8 +70,7 @@ module.exports = {
name: ipfsCoordName,
version,
protocol: 'generic-service',
description:
'This is a generic IPFS Serivice Provider that uses JSON RPC over IPFS to communicate with it. This instance has not been customized. Source code: https://github.com/Permissionless-Software-Foundation/ipfs-service-provider',
description: 'This is a generic IPFS Serivice Provider that uses JSON RPC over IPFS to communicate with it. This instance has not been customized. Source code: https://github.com/Permissionless-Software-Foundation/ipfs-service-provider',
documentation: 'https://ipfs-service-provider.fullstack.cash/',
provider: {
'@type': 'Organization',
+1 -1
View File
@@ -7,6 +7,6 @@
module.exports = {
session: 'secret-boilerplate-token',
token: 'secret-jwt-token',
database: 'mongodb://localhost:27017/swap-service-dev',
database: 'mongodb://localhost:27017/bch-swap-service-dev',
env: 'dev'
}
+1 -1
View File
@@ -13,6 +13,6 @@ module.exports = {
// database: 'mongodb://172.17.0.1:5555/ipfs-service-prod',
database: process.env.DBURL
? process.env.DBURL
: 'mongodb://172.17.0.1:5555/swap-service-prod',
: 'mongodb://172.17.0.1:5555/bch-swap-service-prod',
env: 'prod'
}
+1 -1
View File
@@ -7,6 +7,6 @@
module.exports = {
session: 'secret-boilerplate-token',
token: 'secret-jwt-token',
database: 'mongodb://localhost:27017/swap-service-test',
database: 'mongodb://localhost:27017/bch-swap-service-test',
env: 'test'
}
+4 -4
View File
@@ -47,15 +47,15 @@ These are just a brief, high-level overview. Review the [Specifications](./speci
## Writing to the Global Database
Adding data to the global P2WDB is triggered by the _Client_ calling a REST API endpoint on `bch-dex`. `bch-dex` [p2wdb npm library](https://www.npmjs.com/package/p2wdb) can be leveraged for easy reading and writing to the P2WDB.
Adding data to the global P2WDB is triggered by the _Client_ calling a REST API endpoint on `bch-dex`. The [p2wdb npm library](https://www.npmjs.com/package/p2wdb) can be leveraged for easy reading and writing to the P2WDB.
Writing data follows these steps:
- Tokens and BCH are held by a wallet which is under the controlled of `bch-dex`.
- The _Client_ submits data to the POST `/offer` REST API endpoint to create a new Offer.
- `bch-dex` will move the funds into a segregated UTXO, and will use that UTXO to create an Offer. The Offer data is written to the P2WDB.
- `bch-dex` will receive a webhook call to its POST `/order` endpoint by the P2WDB. This event will trigger the import of the new data into the apps local Mongo database, and generate a new Order model.
- This webhook event is mirrored by every instance of `bch-dex` on the network. Each P2WDB peer on the network will independently validate the new database entry.
- `bch-dex` will move the funds into a segregated UTXO, and will use that UTXO to create an Offer. The Offer data is written to the P2WDB. The Offer data is also saved to the local MongoDB.
- After submitting the data to the P2WDB, `bch-dex` will receive a webhook call to its POST `/order` endpoint by the P2WDB. This event will trigger the import of the new data into the apps local Mongo database, and generate a new Order model.
- This webhook event is mirrored by every instance of `bch-dex` on the network. Each P2WDB peer on the network will independently validate the new database entry and create a new Order model.
## Reading from the Local Database
+3070 -11755
View File
File diff suppressed because it is too large Load Diff
+4 -3
View File
@@ -9,7 +9,7 @@
"test:all": "export BCH_DEX=test && nyc --reporter=text mocha --exit --timeout 15000 --recursive test/unit test/e2e/automated/",
"test:unit": "export BCH_DEX=test && mocha --exit --timeout 15000 --recursive test/unit/",
"test:e2e:auto": "export BCH_DEX=test && mocha --exit --timeout 15000 test/e2e/automated/",
"test:integration": "export BCH_DEX=test && mocha --exit --timeout 30000 --recursive test/integration",
"test:integration": "export BCH_DEX=test && mocha --exit --timeout 45000 --recursive test/integration",
"test:temp": "export BCH_DEX=test && mocha --exit --timeout 15000 -g '#rate-limit' test/unit/json-rpc/",
"lint": "standard --env mocha --fix",
"docs": "./node_modules/.bin/apidoc -i src/ -o docs",
@@ -33,7 +33,7 @@
"bcryptjs": "2.4.3",
"glob": "7.1.6",
"ipfs": "0.60.0",
"ipfs-coord": "6.8.12",
"ipfs-coord": "7.1.4",
"ipfs-http-client": "55.0.0",
"jsonrpc-lite": "2.2.0",
"jsonwebtoken": "8.5.1",
@@ -51,10 +51,11 @@
"koa2-ratelimit": "0.9.1",
"libp2p": "^0.36.2",
"line-reader": "0.4.0",
"minimal-slp-wallet": "../minimal-slp-wallet",
"minimal-slp-wallet": "4.4.2",
"mongoose": "5.13.13",
"node-fetch": "npm:@achingbrain/node-fetch@2.6.7",
"nodemailer": "6.4.17",
"p2wdb": "1.3.1",
"passport-local": "1.0.0",
"public-ip": "^4.0.4",
"winston": "3.3.3",
+3 -2
View File
@@ -18,7 +18,7 @@ const JSONFiles = require('./json-files')
const FullStackJWT = require('./fullstack-jwt')
const BCHAdapter = require('./bch')
const WalletAdapter = require('./wallet')
const P2wdbAdapter = require('./p2wdb')
const P2wdbAdapter = require('./p2wdb-adapter')
//
// // Instantiate adapter libraries.
@@ -52,10 +52,11 @@ class Adapters {
this.nodemailer = new Nodemailer()
this.jsonFiles = new JSONFiles()
this.bchjs = new BCHJS()
localConfig.bchjs = this.bchjs
this.bch = new BCHAdapter()
this.config = config
this.wallet = new WalletAdapter()
this.p2wdb = new P2wdbAdapter()
this.p2wdb = new P2wdbAdapter(localConfig)
// Get a valid JWT API key and instance bch-js.
this.fullStackJwt = new FullStackJWT(config)
+1 -1
View File
@@ -91,7 +91,7 @@ class IpfsCoordAdapter {
attachRPCRouter (router) {
try {
_this.ipfsCoord.privateLog = router
_this.ipfsCoord.adapters.orbit.privateLog = router
_this.ipfsCoord.adapters.pubsub.privateLog = router
} catch (err) {
console.error('Error in attachRPCRouter()')
throw err
+2
View File
@@ -6,6 +6,7 @@
const Users = require('./models/users')
const Entries = require('./models/entry')
const Order = require('./models/order')
const Offer = require('./models/offer')
class LocalDB {
constructor () {
@@ -13,6 +14,7 @@ class LocalDB {
this.Users = Users
this.Entry = Entries
this.Order = Order
this.Offer = Offer
}
}
+1
View File
@@ -14,6 +14,7 @@ const Offer = new mongoose.Schema({
utxoTxid: { type: String },
utxoVout: { type: Number },
numTokens: { type: Number },
hdIndex: { type: Number }, // HD index address holding the UTXO for this offer.
//
offerIpfsId: { type: String },
+107
View File
@@ -0,0 +1,107 @@
/*
Adapter library for interacting with the P2WDB
*/
// Public npm libraries.
const axios = require('axios')
const { Write, Read } = require('p2wdb/index')
// Local libraries
const config = require('../../config')
class P2wdbAdapter {
constructor (localConfig = {}) {
// Dependency injection
this.bchjs = localConfig.bchjs
if (!this.bchjs) {
throw new Error('Must pass an instance of bch-js when instantiating p2wdb.js adapter.')
}
// Encapsulate dependencies
this.axios = axios
this.config = config
this.Write = Write
this.Read = Read
// Allow the localConfig to overwrite the config file values.
this.p2wdbURL = localConfig.p2wdbURL || config.p2wdbUrl
}
// Write some data to the P2WDB
async write (inputObj = {}) {
try {
const { appId = 'test', data = {}, wif } = inputObj
if (typeof wif !== 'string' || !wif) {
throw new Error('wif input must be a private key starting with the letter L or K')
}
const p2write = this.instantiateWriteLib(wif)
const result = await p2write.postEntry(data, appId)
return result.hash
} catch (err) {
console.error('Error in p2wdb.js/write()')
throw err
}
}
// Instantiate the Write library with a WIF, handling the various config
// settings.
instantiateWriteLib (wif) {
try {
// Object used to configure the Write library.
let configObj = {}
if (this.config.useFullStackCash) {
// Use web 2 infrastructure.
// bch-js and FullStack.cash
configObj = {
wif,
serverURL: this.p2wdbURL,
restURL: this.bchjs.restURL,
apiToken: this.bchjs.apiToken
}
} else {
// Use web 3 infrastructure.
// ipfs-bch-wallet-consumer and bch-consumer library
configObj = {
wif,
serverURL: this.p2wdbURL,
interface: 'consumer-api',
restURL: this.config.consumerUrl
}
}
const write = new this.Write(configObj)
return write
} catch (err) {
console.error('Error in p2wdb-adapter.js/instantitateWriteLib()')
throw err
}
}
// Returns a promise that resolves to true or false, to indicate if the
// WIF controls the required funds to write to the P2WDB.
async checkForSufficientFunds (wif) {
try {
if (typeof wif !== 'string' || !wif) {
throw new Error('invalid wif to check for funds')
}
// Instatiate the Write library
const p2write = this.instantiateWriteLib(wif)
return await p2write.checkForSufficientFunds()
} catch (err) {
console.error('Error in p2wdb.js/checkForSufficientFunds()')
throw err
}
}
}
module.exports = P2wdbAdapter
-57
View File
@@ -1,57 +0,0 @@
/*
Adapter library for interacting with the P2WDB
*/
// Public npm libraries.
const axios = require('axios')
// Local libraries
const config = require('../../config')
// Global constants
let P2WDB_SERVER = 'http://localhost:5001/entry/write'
// const P2WDB_SERVER = 'https://p2wdb.fullstack.cash/entry/write'
class P2wdbAdapter {
constructor (localConfig = {}) {
// Encapsulate dependencies
this.axios = axios
this.config = config
P2WDB_SERVER = `http://localhost:${config.p2wdbPort}/entry/write`
}
async write (inputObj) {
try {
const { txid, signature, message, appId, data } = inputObj
// TODO: Input validation
const now = new Date()
const dataObj = {
appId,
data,
timestamp: now.toISOString(),
localTimeStamp: now.toLocaleString()
}
const bodyData = {
txid,
message,
signature,
data: JSON.stringify(dataObj)
}
const result = await this.axios.post(P2WDB_SERVER, bodyData)
// console.log(`Response from API: ${JSON.stringify(result.data, null, 2)}`)
return result.data.hash
} catch (err) {
console.error('Error in p2wdb.js/write()')
throw err
}
}
}
module.exports = P2wdbAdapter
+60 -60
View File
@@ -10,9 +10,9 @@ const JsonFiles = require('./json-files')
const config = require('../../config')
const WALLET_FILE = `${__dirname.toString()}/../../wallet.json`
const PROOF_OF_BURN_QTY = 0.01
const P2WDB_TOKEN_ID =
'38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0'
// const PROOF_OF_BURN_QTY = 0.01
// const P2WDB_TOKEN_ID =
// '38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0'
class WalletAdapter {
constructor (localConfig = {}) {
@@ -178,63 +178,63 @@ class WalletAdapter {
}
}
// Burn enough PSF to generate a valide proof-of-burn for writing to the P2WDB.
async burnPsf () {
try {
// TODO: Throw error if this.bchWallet has not been instantiated.
// console.log('walletData: ', walletData)
// console.log(
// `walletData.utxos.utxoStore.slpUtxos: ${JSON.stringify(
// walletData.utxos.utxoStore.slpUtxos,
// null,
// 2,
// )}`,
// )
// Get token UTXOs held by the wallet.
const tokenUtxos = this.bchWallet.utxos.utxoStore.slpUtxos.type1.tokens
// console.log(`tokenUtxos: ${JSON.stringify(tokenUtxos, null, 2)}`)
// Find a token UTXO that contains PSF with a quantity higher than needed
// to generate a proof-of-burn.
let tokenUtxo = {}
for (let i = 0; i < tokenUtxos.length; i++) {
const thisUtxo = tokenUtxos[i]
// If token ID matches.
if (thisUtxo.tokenId === P2WDB_TOKEN_ID) {
if (parseFloat(thisUtxo.qtyStr) >= PROOF_OF_BURN_QTY) {
tokenUtxo = thisUtxo
break
}
}
}
if (tokenUtxo.tokenId !== P2WDB_TOKEN_ID) {
throw new Error(
`Token UTXO of with ID of ${P2WDB_TOKEN_ID} and quantity greater than ${PROOF_OF_BURN_QTY} could not be found in wallet.`
)
}
// console.log(`tokenUtxo: ${JSON.stringify(tokenUtxo, null, 2)}`)
const result = await this.bchWallet.burnTokens(
PROOF_OF_BURN_QTY,
P2WDB_TOKEN_ID
)
// console.log('walletData.burnTokens() result: ', result)
return result
// return {
// success: true,
// txid: 'fakeTxid',
// }
} catch (err) {
console.error('Error in burnPsf(): ', err)
throw err
}
}
// Burn enough PSF to generate a valide proof-of-burn for writing to the P2WDB.
// async burnPsf () {
// try {
// // TODO: Throw error if this.bchWallet has not been instantiated.
//
// // console.log('walletData: ', walletData)
// // console.log(
// // `walletData.utxos.utxoStore.slpUtxos: ${JSON.stringify(
// // walletData.utxos.utxoStore.slpUtxos,
// // null,
// // 2,
// // )}`,
// // )
//
// // Get token UTXOs held by the wallet.
// const tokenUtxos = this.bchWallet.utxos.utxoStore.slpUtxos.type1.tokens
// // console.log(`tokenUtxos: ${JSON.stringify(tokenUtxos, null, 2)}`)
//
// // Find a token UTXO that contains PSF with a quantity higher than needed
// // to generate a proof-of-burn.
// let tokenUtxo = {}
// for (let i = 0; i < tokenUtxos.length; i++) {
// const thisUtxo = tokenUtxos[i]
//
// // If token ID matches.
// if (thisUtxo.tokenId === P2WDB_TOKEN_ID) {
// if (parseFloat(thisUtxo.qtyStr) >= PROOF_OF_BURN_QTY) {
// tokenUtxo = thisUtxo
// break
// }
// }
// }
//
// if (tokenUtxo.tokenId !== P2WDB_TOKEN_ID) {
// throw new Error(
// `Token UTXO of with ID of ${P2WDB_TOKEN_ID} and quantity greater than ${PROOF_OF_BURN_QTY} could not be found in wallet.`
// )
// }
// // console.log(`tokenUtxo: ${JSON.stringify(tokenUtxo, null, 2)}`)
//
// const result = await this.bchWallet.burnTokens(
// PROOF_OF_BURN_QTY,
// P2WDB_TOKEN_ID
// )
// // console.log('walletData.burnTokens() result: ', result)
//
// return result
//
// // return {
// // success: true,
// // txid: 'fakeTxid',
// // }
// } catch (err) {
// console.error('Error in burnPsf(): ', err)
// throw err
// }
// }
}
module.exports = WalletAdapter
+5
View File
@@ -1,5 +1,8 @@
/*
This is the JSON RPC router for the users API
CT 3/5/22: This library can probably be deleted. Handling of the /about endpoint
is now directly controlled by the ipfs-coord library.
*/
// Public npm libraries
@@ -33,6 +36,8 @@ class AboutRPC {
// This is a bit different than other router libraries, because there is
// only one response, which is a string about this node.
async aboutRouter (rpcData) {
console.log('debugging: aboutRouter from ipfs-service-provider triggered')
return {
success: true,
status: 200,
@@ -49,6 +49,18 @@ class OrderRESTControllerLib {
}
}
// curl -X GET http://localhost:5700/order/list
async listOrders (ctx) {
try {
const orders = await _this.useCases.order.listOrders()
ctx.body = orders
} catch (err) {
console.log('Error in listOrders REST API handler.')
_this.handleError(ctx, err)
}
}
// DRY error handler
handleError (ctx, err) {
console.log('err', err.message)
+1
View File
@@ -50,6 +50,7 @@ class OrderRouter {
// Define the routes and attach the controller.
this.router.post('/', _this.orderRESTController.createOrder)
this.router.get('/list', _this.orderRESTController.listOrders)
// Attach the Controller routes to the Koa app.
app.use(_this.router.routes())
+25 -23
View File
@@ -47,30 +47,23 @@ class OfferLib {
// Update the offer with the new UTXO information.
offerEntity.utxoTxid = utxoInfo.txid
offerEntity.utxoVout = utxoInfo.vout
// Burn PSF token to pay for P2WDB write.
const txid = await this.adapters.wallet.burnPsf()
console.log('burn txid: ', txid)
console.log(`https://simpleledger.info/tx/${txid}`)
// generate signature.
const now = new Date()
const message = now.toISOString()
const signature = await this.adapters.wallet.generateSignature(message)
// console.log('signature: ', signature)
const p2wdbObj = {
txid,
signature,
message,
appId: this.config.p2wdbAppId,
data: offerEntity
}
offerEntity.hdIndex = utxoInfo.hdIndex
// Add offer to P2WDB.
const p2wdbObj = {
wif: this.adapters.wallet.bchWallet.walletInfo.privateKey,
data: offerEntity,
appId: this.config.p2wdbAppId
}
const hash = await this.adapters.p2wdb.write(p2wdbObj)
// console.log('hash: ', hash)
// Create a MongoDB model to hold the Offer
offerEntity.p2wdbHash = hash
console.log(`creating new offer model: ${JSON.stringify(offerEntity, null, 2)}`)
const offer = new this.OfferModel(offerEntity)
await offer.save()
return hash
} catch (err) {
// console.log("Error in use-cases/entry.js/createEntry()", err.message)
@@ -97,7 +90,8 @@ class OfferLib {
const utxoInfo = {
txid,
vout: 0
vout: 0,
hdIndex: keyPair.hdIndex
}
return utxoInfo
@@ -112,6 +106,12 @@ class OfferLib {
async ensureFunds (offerEntity) {
try {
// console.log('this.adapters.wallet: ', this.adapters.wallet.bchWallet)
// console.log(`walletInfo: ${JSON.stringify(this.adapters.wallet.bchWallet.walletInfo, null, 2)}`)
// Ensure the app wallet has enough funds to write to the P2WDB.
const wif = this.adapters.wallet.bchWallet.walletInfo.privateKey
const canWriteToP2WDB = await this.adapters.p2wdb.checkForSufficientFunds(wif)
if (!canWriteToP2WDB) throw new Error('App wallet does not have funds for writing to the P2WDB.')
// Get UTXOs.
const utxos = this.adapters.wallet.bchWallet.utxos.utxoStore
@@ -129,16 +129,18 @@ class OfferLib {
// Get the total amount of tokens in the wallet that match the token
// in the offer.
let totalTokenBalance = 0
tokenUtxos.map(x => (totalTokenBalance += parseFloat(x.tokenQty)))
// console.log('totalTokenBalance: ', totalTokenBalance)
tokenUtxos.map(x => (totalTokenBalance += parseFloat(x.qtyStr)))
console.log('totalTokenBalance: ', totalTokenBalance)
// If there are fewer tokens in the wallet than what's in the offer,
// throw an error.
if (totalTokenBalance <= offerEntity.numTokens) {
if (totalTokenBalance <= offerEntity.numTokens || isNaN(totalTokenBalance)) {
throw new Error(
'App wallet does not have enough tokens to satisfy the SELL offer.'
)
}
//
} else {
// Buy Offer
throw new Error('Buy orders are not supported yet.')
+9
View File
@@ -56,6 +56,15 @@ class OrderUseCases {
throw err
}
}
async listOrders () {
try {
return this.OrderModel.find({})
} catch (error) {
console.error('Error in use-cases/order/listOrders()')
throw error
}
}
}
module.exports = OrderUseCases
+1 -1
View File
@@ -6,7 +6,7 @@
const axios = require('axios')
const LOCALHOST = 'http://localhost:5002'
const LOCALHOST = 'http://localhost:5700'
async function start () {
try {
@@ -0,0 +1,54 @@
/*
Integration tests for hte p2wdb-adapter.js library
These integration tests may not necessarily pass. They require a WIF set
as an environment variable, and loaded with enough BCH and PSF to make
a write to the P2WDB.
If these tests fail, it does not necessarily mean there is a problem with the
code. Check that the WIF envrionment variable meets the requirements.
*/
// Public npm libraries
const BCHJS = require('@psf/bch-js')
// Local libraries
const P2wdbAdapter = require('../../../src/adapters/p2wdb-adapter')
const wif = process.env.WIF
if (!wif) {
throw new Error('You must provide a BCH WIF private key as an environment variable to run this test.')
}
describe('#p2wdb-adapter.js', () => {
let uut
beforeEach(() => {
const bchjs = new BCHJS()
uut = new P2wdbAdapter({ bchjs })
})
describe('#checkForSufficientFunds', () => {
it('should check for sufficient funds', async () => {
const result = await uut.checkForSufficientFunds(wif)
console.log('Provided WIF has funds for making a write: ', result)
})
})
describe('#write', () => {
it('should write data to the P2WDB', async () => {
const inObj = {
appId: 'test234',
data: {
key: 'value'
},
wif
}
const result = await uut.write(inObj)
console.log('result: ', result)
})
})
})
@@ -0,0 +1,65 @@
/*
Integration tests for the offer-use-case.js library
For these tests to pass, the app wallet must have Trout test tokens with
a token ID of a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2
*/
// Public npm libraries
const assert = require('chai').assert
// Local npm libraries
const Adapters = require('../../../src/adapters')
const Offer = require('../../../src/use-cases/offer')
describe('#offer-use-case.js', () => {
let uut
before(async () => {
const adapters = new Adapters()
adapters.config.getJwtAtStartup = false
await adapters.start()
uut = new Offer({ adapters })
})
describe('#ensureFunds', () => {
it('should throw error if wallet does not have enough funds', async () => {
try {
const offerEntity = {
lokadId: 'SWP',
messageType: 1,
messageClass: 1,
tokenId: 'token-id',
buyOrSell: 'sell',
rateInSats: 1000,
minSatsToExchange: 1250,
numTokens: 1
}
await uut.ensureFunds(offerEntity)
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'App wallet does not have enough tokens to satisfy the SELL offer.')
}
})
it('should return true if wallet has enough funds', async () => {
const offerEntity = {
lokadId: 'SWP',
messageType: 1,
messageClass: 1,
tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
buyOrSell: 'sell',
rateInSats: 1000,
minSatsToExchange: 1250,
numTokens: 1
}
const result = await uut.ensureFunds(offerEntity)
assert.equal(result, true)
})
})
})
@@ -74,8 +74,9 @@ describe('#IPFS', () => {
}
},
adapters: {
orbit: {
privateLog: () => {}
pubsub: {
privateLog: () => {
}
}
}
}
@@ -96,7 +97,7 @@ describe('#IPFS', () => {
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'Cannot read property')
assert.include(err.message, 'Cannot read')
}
})
})
+22 -21
View File
@@ -52,7 +52,8 @@ describe('#IPFS-adapter', () => {
it('should stop the IPFS node', async () => {
// Mock dependencies
uut.ipfs = {
stop: () => {}
stop: () => {
}
}
const result = await uut.stop()
@@ -61,24 +62,24 @@ describe('#IPFS-adapter', () => {
})
})
describe('#rmBlocksDir', () => {
it('should delete the /blocks directory', () => {
const result = uut.rmBlocksDir()
assert.equal(result, true)
})
it('should catch and throw an error', () => {
try {
// Force an error
sandbox.stub(uut.fs, 'rmdirSync').throws(new Error('test error'))
uut.rmBlocksDir()
assert.fail('Unexpected code path')
} catch (err) {
assert.equal(err.message, 'test error')
}
})
})
// describe('#rmBlocksDir', () => {
// it('should delete the /blocks directory', () => {
// const result = uut.rmBlocksDir()
//
// assert.equal(result, true)
// })
//
// it('should catch and throw an error', () => {
// try {
// // Force an error
// sandbox.stub(uut.fs, 'rmdirSync').throws(new Error('test error'))
//
// uut.rmBlocksDir()
//
// assert.fail('Unexpected code path')
// } catch (err) {
// assert.equal(err.message, 'test error')
// }
// })
// })
})
+108 -15
View File
@@ -5,48 +5,141 @@
// Public npm libraries.
const assert = require('chai').assert
const sinon = require('sinon')
const BCHJS = require('@psf/bch-js')
// Local libraries.
const P2wdbAdapter = require('../../../src/adapters/p2wdb')
const P2wdbAdapter = require('../../../src/adapters/p2wdb-adapter')
describe('#P2wdbAdapter', () => {
let uut, sandbox
beforeEach(() => {
uut = new P2wdbAdapter()
const bchjs = new BCHJS()
uut = new P2wdbAdapter({ bchjs })
sandbox = sinon.createSandbox()
})
afterEach(() => sandbox.restore())
describe('#write', () => {
it('should write an offer to the P2WDB', async () => {
const inputObj = {
txid: 'testTxid',
signature: 'testSignature',
message: 'testMessage',
appId: 'testAppId',
data: { key: 'value' }
describe('#constructor', () => {
it('should throw error if bch-js is not passed in', () => {
try {
uut = new P2wdbAdapter()
assert.fail('Unexpected coded path')
} catch (err) {
assert.include(err.message, 'Must pass an instance of bch-js when instantiating p2wdb.js adapter.')
}
})
})
// Mock axios to prevent live network call
sandbox.stub(uut.axios, 'post').resolves({ data: { hash: 'testhash' } })
describe('#instantiateWriteLib', () => {
it('should instantiate Write library using web 3 interface', async () => {
// Force code path
uut.config.useFullStackCash = false
const result = await uut.write(inputObj)
// Mock dependencies
uut.Write =
class Write {
constructor () {
this.interface = 'web3'
}
}
const wif = 'L1tcvcqa5PztqqDH4ZEcUmHA9aSHhTau5E2Zwp1xEK5CrKBrjP3m'
const result = uut.instantiateWriteLib(wif)
// console.log('result: ', result)
assert.equal(result, 'testhash')
assert.equal(result.interface, 'web3')
})
it('should instantiate Write library using web 2 interface', async () => {
// Force code path
uut.config.useFullStackCash = true
// Mock dependencies
uut.Write =
class Write {
constructor () {
this.interface = 'fullstack'
}
}
const wif = 'L1tcvcqa5PztqqDH4ZEcUmHA9aSHhTau5E2Zwp1xEK5CrKBrjP3m'
const result = uut.instantiateWriteLib(wif)
// console.log('result: ', result)
assert.equal(result.interface, 'fullstack')
})
it('should catch and throw errors', async () => {
try {
await uut.instantiateWriteLib()
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.include(err.message, 'WIF private key required')
}
})
})
describe('#checkForSufficientFunds', () => {
it('should throw error if WIF is not provided', async () => {
try {
await uut.checkForSufficientFunds()
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.include(err.message, 'invalid wif to check for funds')
}
})
it('should return status of funds', async () => {
// Mock dependencies
sandbox.stub(uut, 'instantiateWriteLib').returns({
checkForSufficientFunds: () => true
})
const wif = 'L1tcvcqa5PztqqDH4ZEcUmHA9aSHhTau5E2Zwp1xEK5CrKBrjP3m'
const result = await uut.checkForSufficientFunds(wif)
assert.equal(result, true)
})
})
describe('#write', async () => {
it('should throw error if WIF is not provided', async () => {
try {
await uut.write()
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.include(err.message, 'Cannot destructure property')
assert.include(err.message, 'wif input must be a private key starting with the letter L or K')
}
})
it('should write data to the P2WDB', async () => {
// Mock dependencies
sandbox.stub(uut, 'instantiateWriteLib').returns({
postEntry: async () => {
return { hash: 'fake-hash' }
}
})
const wif = 'L1tcvcqa5PztqqDH4ZEcUmHA9aSHhTau5E2Zwp1xEK5CrKBrjP3m'
const inObj = { wif }
const result = await uut.write(inObj)
// console.log('result: ', result)
assert.equal(result, 'fake-hash')
})
})
})
+41 -40
View File
@@ -84,7 +84,8 @@ describe('#wallet', () => {
try {
// Force an error
uut.WALLET_FILE = ''
uut.BchWallet = () => {}
uut.BchWallet = () => {
}
await uut.openWallet()
// console.log('result: ', result)
@@ -120,7 +121,7 @@ describe('#wallet', () => {
assert.fail('Unexpected code path')
} catch (err) {
// console.log('err: ', err)
assert.include(err.message, 'Cannot read property')
assert.include(err.message, 'Cannot read')
}
})
})
@@ -155,44 +156,44 @@ describe('#wallet', () => {
})
})
describe('#burnPsf', () => {
it('should burn PSF tokens and return the txid', async () => {
// mock instance of minimal-slp-wallet
uut.bchWallet = new MockBchWallet()
const result = await uut.burnPsf()
// console.log('result: ', result)
assert.equal(result.success, true)
assert.equal(result.txid, 'txid')
})
it('should throw error if no PSF tokens are found', async () => {
try {
// mock instance of minimal-slp-wallet
uut.bchWallet = new MockBchWallet()
// Remove the PSF token from the mock data.
uut.bchWallet.utxos.utxoStore.slpUtxos.type1.tokens.pop()
await uut.burnPsf()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'Token UTXO of with ID of')
}
})
it('should catch and throw an error', async () => {
try {
await uut.burnPsf()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'Cannot read property')
}
})
})
// describe('#burnPsf', () => {
// it('should burn PSF tokens and return the txid', async () => {
// // mock instance of minimal-slp-wallet
// uut.bchWallet = new MockBchWallet()
//
// const result = await uut.burnPsf()
// // console.log('result: ', result)
//
// assert.equal(result.success, true)
// assert.equal(result.txid, 'txid')
// })
//
// it('should throw error if no PSF tokens are found', async () => {
// try {
// // mock instance of minimal-slp-wallet
// uut.bchWallet = new MockBchWallet()
//
// // Remove the PSF token from the mock data.
// uut.bchWallet.utxos.utxoStore.slpUtxos.type1.tokens.pop()
//
// await uut.burnPsf()
//
// assert.fail('Unexpected code path')
// } catch (err) {
// assert.include(err.message, 'Token UTXO of with ID of')
// }
// })
//
// it('should catch and throw an error', async () => {
// try {
// await uut.burnPsf()
//
// assert.fail('Unexpected code path')
// } catch (err) {
// assert.include(err.message, 'Cannot read')
// }
// })
// })
describe('#incrementNextAddress', () => {
it('should increment the nextAddress property', async () => {
@@ -333,7 +333,7 @@ describe('#UserRPC', () => {
assert.equal(result.endpoint, 'updateUser')
assert.equal(result.success, false)
assert.equal(result.status, 422)
assert.include(result.message, 'Cannot read property')
assert.include(result.message, 'Cannot read')
})
})
@@ -375,7 +375,7 @@ describe('#UserRPC', () => {
assert.equal(result.endpoint, 'getUser')
assert.equal(result.success, false)
assert.equal(result.status, 422)
assert.include(result.message, 'Cannot read property')
assert.include(result.message, 'Cannot read')
})
})
@@ -14,7 +14,8 @@ let ctx
const mockContext = require('../../../../unit/mocks/ctx-mock').context
describe('Contact', () => {
before(async () => {})
before(async () => {
})
beforeEach(() => {
uut = new ContactController()
@@ -36,7 +37,7 @@ describe('Contact', () => {
} catch (err) {
// console.log(err)
assert.equal(err.status, 422)
assert.include(err.message, 'Cannot read property')
assert.include(err.message, 'Cannot read')
}
})
@@ -69,7 +69,7 @@ describe('#Entry-REST-Controller', () => {
} catch (err) {
// console.log(err)
assert.equal(err.status, 422)
assert.include(err.message, 'Cannot read property')
assert.include(err.message, 'Cannot read')
}
})
@@ -79,8 +79,7 @@ describe('#Entry-REST-Controller', () => {
entry: 'entry',
description: 'test',
slpAddress: 'simpleledger:qpnty9t0w93fez04h7yzevujpv8pun204qqp0jfafg',
signature:
'IFytRg6KpvTHCzcW0ZwVhPqdKtRGpoRDcuEb958yIgJFUJlb1F5qPzt/JnlYE7r012BSFj+UT67DZVTU8oNB5vw=',
signature: 'IFytRg6KpvTHCzcW0ZwVhPqdKtRGpoRDcuEb958yIgJFUJlb1F5qPzt/JnlYE7r012BSFj+UT67DZVTU8oNB5vw=',
category: 'test'
}
}
@@ -14,7 +14,8 @@ let ctx
const mockContext = require('../../../../unit/mocks/ctx-mock').context
describe('Logapi', () => {
before(async () => {})
before(async () => {
})
beforeEach(() => {
uut = new LogsApiController()
@@ -35,7 +36,7 @@ describe('Logapi', () => {
assert.fail('Unexpected result')
} catch (err) {
assert.equal(err.status, 422)
assert.include(err.message, 'Cannot read property')
assert.include(err.message, 'Cannot read')
}
})
@@ -69,7 +69,7 @@ describe('#Users-REST-Controller', () => {
} catch (err) {
// console.log(err)
assert.equal(err.status, 422)
assert.include(err.message, 'Cannot read property')
assert.include(err.message, 'Cannot read')
}
})
@@ -91,9 +91,9 @@ describe('#Users-REST-Controller', () => {
assert.property(ctx.response.body, 'user')
assert.property(ctx.response.body, 'token')
// Used by downstream tests.
// testUser = ctx.response.body.user
// console.log('testUser: ', testUser)
// Used by downstream tests.
// testUser = ctx.response.body.user
// console.log('testUser: ', testUser)
})
})
@@ -181,7 +181,7 @@ describe('#Users-REST-Controller', () => {
} catch (err) {
// console.log(err)
assert.equal(err.status, 422)
assert.include(err.message, 'Cannot read property')
assert.include(err.message, 'Cannot read')
}
})
@@ -223,7 +223,7 @@ describe('#Users-REST-Controller', () => {
} catch (err) {
// console.log(err)
assert.equal(err.status, 422)
assert.include(err.message, 'Cannot read property')
assert.include(err.message, 'Cannot read')
}
})
+22 -5
View File
@@ -13,7 +13,8 @@ const ipfs = {
ipfsCoord: {
useCases: {
peer: {
sendPrivateMessage: () => {}
sendPrivateMessage: () => {
}
}
}
}
@@ -82,6 +83,18 @@ const localdb = {
static find () {}
static findOne () {}
async save () {
return {}
}
},
Offer: class Offer {
constructor (obj) {}
static findById () {}
static find () {}
static findOne () {}
async save () {
return {}
}
@@ -106,8 +119,10 @@ const bch = {
// }
const { MockBchWallet } = require('./wallet')
const wallet = {
burnPsf: async () => {},
generateSignature: async () => {},
burnPsf: async () => {
},
generateSignature: async () => {
},
getKeyPair: async () => {
return { cashAddress: 'fakeAddr', wif: 'fakeWif', hdIndex: 1 }
},
@@ -115,7 +130,9 @@ const wallet = {
}
const p2wdb = {
write: async () => {}
write: async () => {
},
checkForSufficientFunds: async () => true
}
module.exports = { ipfs, localdb, bch, wallet, p2wdb, bchjs }
module.exports = { ipfs, localdb, bch, wallet, p2wdb, bchjs}
+60 -49
View File
@@ -5,11 +5,9 @@
const BCHJS = require('@psf/bch-js')
const mockWallet = {
mnemonic:
'course abstract aerobic deer try switch turtle diet fence affair butter top',
mnemonic: 'course abstract aerobic deer try switch turtle diet fence affair butter top',
privateKey: 'L5D2UAam8tvo3uii5kpgaGyjvVMimdrXu8nWGQSQjuuAix6ji1YQ',
publicKey:
'0379433ffc401483ade310469953c1cba77c71af904f07c15bde330d7198b4d6dc',
publicKey: '0379433ffc401483ade310469953c1cba77c71af904f07c15bde330d7198b4d6dc',
cashAddress: 'bitcoincash:qzl0d3gcqeypv4cy7gh8rgdszxa9vvm2acv7fqtd00',
address: 'bitcoincash:qzl0d3gcqeypv4cy7gh8rgdszxa9vvm2acv7fqtd00',
slpAddress: 'simpleledger:qzl0d3gcqeypv4cy7gh8rgdszxa9vvm2acq9zm7d33',
@@ -29,7 +27,8 @@ class MockBchWallet {
this.sendTokens = async () => {
return 'fakeTxid'
}
this.getUtxos = async () => {}
this.getUtxos = async () => {
}
// Environment variable is used by wallet-balance.unit.js to force an error.
if (process.env.NO_UTXO) {
@@ -41,8 +40,7 @@ class MockBchWallet {
bchUtxos: [
{
height: 700685,
tx_hash:
'1fc577caaff5626a8477162581e57bae1b19dc6aa6c10638013c2b1ba14dc654',
tx_hash: '1fc577caaff5626a8477162581e57bae1b19dc6aa6c10638013c2b1ba14dc654',
tx_pos: 0,
value: 1000,
txid: '1fc577caaff5626a8477162581e57bae1b19dc6aa6c10638013c2b1ba14dc654',
@@ -51,8 +49,7 @@ class MockBchWallet {
},
{
height: 700685,
tx_hash:
'1fc577caaff5626a8477162581e57bae1b19dc6aa6c10638013c2b1ba14dc654',
tx_hash: '1fc577caaff5626a8477162581e57bae1b19dc6aa6c10638013c2b1ba14dc654',
tx_pos: 2,
value: 19406,
txid: '1fc577caaff5626a8477162581e57bae1b19dc6aa6c10638013c2b1ba14dc654',
@@ -66,47 +63,61 @@ class MockBchWallet {
mintBatons: [],
tokens: [
{
height: 700522,
tx_hash:
'bb5691b50930816be78dad76d203a1c97ac94c03f6051b2fa0159c71c43aa3d0',
tx_pos: 1,
value: 546,
txid: 'bb5691b50930816be78dad76d203a1c97ac94c03f6051b2fa0159c71c43aa3d0',
vout: 1,
utxoType: 'token',
transactionType: 'send',
tokenId:
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
tokenTicker: 'TROUT',
tokenName: "Trout's test token",
tokenDocumentUrl: 'troutsblog.com',
tokenDocumentHash: '',
decimals: 2,
tokenType: 1,
isValid: true,
tokenQty: '4.25'
'height': 717331,
'tx_hash': '74889580bb1a5f8c026aa2f55118ac9917df3332f7abae72a70343daa1c29621',
'tx_pos': 1,
'value': 546,
'txid': '74889580bb1a5f8c026aa2f55118ac9917df3332f7abae72a70343daa1c29621',
'vout': 1,
'isSlp': true,
'type': 'token',
'qty': '10',
'tokenId': '600ee24d0f208aebc2bdd2c4ee1b9acb6d57343561442e8676b5bbea311d5a0f',
'address': 'bitcoincash:qqraj35x6l2qyqhjm5l7qlt7z2245ez8l5z3dwkeq5',
'ticker': 'FLIPS',
'name': 'FLIPS',
'documentUri': '',
'documentHash': '',
'decimals': 1,
'qtyStr': '1'
},
{
height: 0,
tx_hash:
'c0ac066ce6efa1fa4763bf85a91c738e57c12b8765731bd07f0d8f5a55ce582f',
tx_pos: 1,
value: 546,
txid: 'c0ac066ce6efa1fa4763bf85a91c738e57c12b8765731bd07f0d8f5a55ce582f',
vout: 1,
utxoType: 'token',
transactionType: 'send',
tokenId:
'38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0',
tokenTicker: 'PSF',
tokenName: 'Permissionless Software Foundation',
tokenDocumentUrl: 'psfoundation.cash',
tokenDocumentHash: '',
decimals: 8,
tokenType: 1,
isValid: true,
tokenQty: '1',
qtyStr: '1'
'height': 730597,
'tx_hash': '52520faddfafc46b8f8c9548b097f3a3b82a5bf363b5095047b9c5f83247fe36',
'tx_pos': 1,
'value': 546,
'txid': '52520faddfafc46b8f8c9548b097f3a3b82a5bf363b5095047b9c5f83247fe36',
'vout': 1,
'isSlp': true,
'type': 'token',
'qty': '34999991',
'tokenId': '38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0',
'address': 'bitcoincash:qqraj35x6l2qyqhjm5l7qlt7z2245ez8l5z3dwkeq5',
'ticker': 'PSF',
'name': 'Permissionless Software Foundation',
'documentUri': 'psfoundation.cash',
'documentHash': '',
'decimals': 8,
'qtyStr': '0.34999991'
},
{
'height': 730597,
'tx_hash': '5dc7e7c91382aed1666a51212dfb74050261e12c3c4f62b6b1e57f42d6c51ee1',
'tx_pos': 2,
'value': 546,
'txid': '5dc7e7c91382aed1666a51212dfb74050261e12c3c4f62b6b1e57f42d6c51ee1',
'vout': 2,
'isSlp': true,
'type': 'token',
'qty': '18898',
'tokenId': 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
'address': 'bitcoincash:qqraj35x6l2qyqhjm5l7qlt7z2245ez8l5z3dwkeq5',
'ticker': 'TROUT',
'name': "Trout's test token",
'documentUri': 'troutsblog.com',
'documentHash': '',
'decimals': 2,
'qtyStr': '188.98'
}
]
},
@@ -122,4 +133,4 @@ class MockBchWallet {
}
}
module.exports = { MockBchWallet, mockWallet }
module.exports = { MockBchWallet, mockWallet}
+48 -50
View File
@@ -46,60 +46,13 @@ describe('#offer-use-case', () => {
})
})
describe('#createOffer', () => {
it('should create an offer and return the hash', async () => {
const entryObj = {
lokadId: 'SWP',
messageType: 1,
messageClass: 1,
tokenId: 'token-id',
buyOrSell: 'sell',
rateInSats: 1000,
minSatsToExchange: 1250,
numTokens: 1
}
// Mock dependencies
sandbox.stub(uut.adapters.wallet, 'burnPsf').resolves('fakeTxid')
sandbox.stub(uut.offerEntity, 'validate').returns(entryObj)
sandbox.stub(uut, 'ensureFunds').resolves()
sandbox.stub(uut, 'moveTokens').resolves({ txid: 'fakeTxid', vout: 0 })
sandbox.stub(uut.adapters.wallet.bchWallet, 'getUtxos').resolves()
sandbox
.stub(uut.adapters.wallet, 'generateSignature')
.resolves('fakeSignature')
sandbox.stub(uut.adapters.p2wdb, 'write').resolves('fakeHash')
const result = await uut.createOffer(entryObj)
console.log('result: ', result)
assert.isString(result)
})
it('should catch and throw an error', async () => {
try {
// Force an error
sandbox
.stub(uut.offerEntity, 'validate')
.throws(new Error('test error'))
await uut.createOffer()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'test error')
}
})
})
describe('#ensureFunds', () => {
it('should return true if wallet has enough funds for a sell order', async () => {
const offerEntity = {
lokadId: 'SWP',
messageType: 1,
messageClass: 1,
tokenId:
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
buyOrSell: 'sell',
rateInSats: 1000,
minSatsToExchange: 0,
@@ -123,8 +76,7 @@ describe('#offer-use-case', () => {
lokadId: 'SWP',
messageType: 1,
messageClass: 1,
tokenId:
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
buyOrSell: 'sell',
rateInSats: 1000,
minSatsToExchange: 0,
@@ -141,4 +93,50 @@ describe('#offer-use-case', () => {
assert.equal(result.vout, 0)
})
})
describe('#createOffer', () => {
it('should create an offer and return the hash', async () => {
const entryObj = {
lokadId: 'SWP',
messageType: 1,
messageClass: 1,
tokenId: 'token-id',
buyOrSell: 'sell',
rateInSats: 1000,
minSatsToExchange: 1250,
numTokens: 1
}
// Mock dependencies
// sandbox.stub(uut.adapters.wallet, 'burnPsf').resolves('fakeTxid')
sandbox.stub(uut.offerEntity, 'validate').returns(entryObj)
sandbox.stub(uut, 'ensureFunds').resolves()
sandbox.stub(uut, 'moveTokens').resolves({ txid: 'fakeTxid', vout: 0, hdIndex: 1 })
sandbox.stub(uut.adapters.wallet.bchWallet, 'getUtxos').resolves()
// sandbox
// .stub(uut.adapters.wallet, 'generateSignature')
// .resolves('fakeSignature')
sandbox.stub(uut.adapters.p2wdb, 'write').resolves('fakeHash')
const result = await uut.createOffer(entryObj)
console.log('result: ', result)
assert.isString(result)
})
it('should catch and throw an error', async () => {
try {
// Force an error
sandbox
.stub(uut.offerEntity, 'validate')
.throws(new Error('test error'))
await uut.createOffer()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'test error')
}
})
})
})
+9 -9
View File
@@ -153,7 +153,7 @@ describe('#users-use-case', () => {
await uut.getAllUsers()
// console.log(`users: ${JSON.stringify(users, null, 2)}`)
// assert.isArray(users)
// assert.isArray(users)
})
it('should catch and throw an error', async () => {
@@ -228,8 +228,8 @@ describe('#users-use-case', () => {
// Assert that the expected properties for the user model exist.
// assert.property(result, 'type')
assert.property(result, '_id')
// assert.property(result, 'email')
// assert.property(result, 'name')
// assert.property(result, 'email')
// assert.property(result, 'name')
})
})
@@ -241,7 +241,7 @@ describe('#users-use-case', () => {
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.include(err.message, 'Cannot read property')
assert.include(err.message, 'Cannot read')
}
})
@@ -346,7 +346,7 @@ describe('#users-use-case', () => {
// assert.equal(result.name, 'testy tester')
// })
// TODO: verify that an admin can change the type of a user
// TODO: verify that an admin can change the type of a user
})
describe('#authUser', () => {
@@ -356,9 +356,9 @@ describe('#users-use-case', () => {
await uut.authUser('test@test.com', 'password')
// console.log('user: ', user)
// assert.property(user, '_id')
// assert.property(user, 'email')
// assert.property(user, 'name')
// assert.property(user, '_id')
// assert.property(user, 'email')
// assert.property(user, 'name')
})
it('should throw an error if no user matches the login', async () => {
@@ -399,7 +399,7 @@ describe('#users-use-case', () => {
assert.fail('Unexpected code path.')
} catch (err) {
// console.log(err)
assert.include(err.message, 'Cannot read property')
assert.include(err.message, 'Cannot read')
}
})