mirror of
https://github.com/Permissionless-Software-Foundation/bch-dex.git
synced 2026-09-23 01:22:03 -07:00
Checking-in pre-crash progress
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
[](http://standardjs.com) [](https://github.com/semantic-release/semantic-release)
|
||||
|
||||
This is a prototype web service that monitors the [P2WDB]() for trading signals, to trade BCH and SLP tokens. It's inspired by the [SWaP Prototype]().
|
||||
This is a prototype web service that monitors the [P2WDB](https://github.com/Permissionless-Software-Foundation/ipfs-p2wdb-service) for trading signals, to trade BCH and SLP tokens. It's inspired by the [SWaP Prototype](https://github.com/vinarmani/swap-protocol/blob/master/swap-protocol-spec.md).
|
||||
|
||||
This repository was forked from [ipfs-torlist-service](https://github.com/Permissionless-Software-Foundation/ipfs-torlist-service).
|
||||
|
||||
|
||||
Generated
+2
-2
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "ipfs-torlist-service",
|
||||
"name": "ipfs-swap-service",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "ipfs-torlist-service",
|
||||
"name": "ipfs-swap-service",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
+9
-6
@@ -10,7 +10,8 @@ class Bch {
|
||||
constructor () {
|
||||
// Encapsulate dependencies
|
||||
this.bchjs = new BCHJS()
|
||||
this.PSF_TOKEN_ID = '38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0'
|
||||
this.PSF_TOKEN_ID =
|
||||
'38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0'
|
||||
this.msgLib = new MsgLib({ bchjs: this.bchjs })
|
||||
}
|
||||
|
||||
@@ -18,15 +19,15 @@ class Bch {
|
||||
_verifySignature (verifyObj) {
|
||||
try {
|
||||
// Expand the input object.
|
||||
const { slpAddress, signature, entry } = verifyObj
|
||||
const { offerBchAddr, signature, sigMsg } = verifyObj
|
||||
|
||||
// Convert to BCH address.
|
||||
const scrubbedAddr = this.bchjs.SLP.Address.toCashAddress(slpAddress)
|
||||
// const scrubbedAddr = this.bchjs.SLP.Address.toCashAddress(slpAddress)
|
||||
|
||||
const isValid = this.bchjs.BitcoinCash.verifyMessage(
|
||||
scrubbedAddr,
|
||||
offerBchAddr,
|
||||
signature,
|
||||
entry
|
||||
sigMsg
|
||||
)
|
||||
|
||||
return isValid
|
||||
@@ -43,7 +44,9 @@ class Bch {
|
||||
throw new Error('slpAddress must be a string')
|
||||
}
|
||||
let psfBalance = 0
|
||||
const balances = await this.bchjs.SLP.Utils.balancesForAddress(slpAddress)
|
||||
const balances = await this.bchjs.SLP.Utils.balancesForAddress(
|
||||
slpAddress
|
||||
)
|
||||
|
||||
// Sums all the balances of all tokens
|
||||
// that match the psf token ID
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
const mongoose = require('mongoose')
|
||||
|
||||
const Offer = new mongoose.Schema({
|
||||
// SWaP Protocol Properties
|
||||
lokadId: { type: String },
|
||||
messageType: { type: Number },
|
||||
messageClass: { type: Number },
|
||||
tokenId: { type: String },
|
||||
buyOrSell: { type: String },
|
||||
rateInSats: { type: String },
|
||||
minSatsToExchange: { type: String },
|
||||
signature: { type: String },
|
||||
sigMsg: { type: String },
|
||||
utxoTxid: { type: String },
|
||||
utxoVout: { type: Number },
|
||||
|
||||
//
|
||||
offerIpfsId: { type: String },
|
||||
offerBchAddr: { type: String },
|
||||
offerPubKey: { type: String }
|
||||
})
|
||||
|
||||
module.exports = mongoose.model('offer', Offer)
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
REST API Controller library for the /offer route
|
||||
*/
|
||||
|
||||
// const { wlogger } = require('../../../adapters/wlogger')
|
||||
|
||||
let _this
|
||||
|
||||
class OfferRESTControllerLib {
|
||||
constructor (localConfig = {}) {
|
||||
// Dependency Injection.
|
||||
this.adapters = localConfig.adapters
|
||||
if (!this.adapters) {
|
||||
throw new Error(
|
||||
'Instance of Adapters library required when instantiating /offer REST Controller.'
|
||||
)
|
||||
}
|
||||
this.useCases = localConfig.useCases
|
||||
if (!this.useCases) {
|
||||
throw new Error(
|
||||
'Instance of Use Cases library required when instantiating /offer REST Controller.'
|
||||
)
|
||||
}
|
||||
|
||||
// Encapsulate dependencies
|
||||
this.OfferModel = this.adapters.localdb.Offer
|
||||
// this.userUseCases = this.useCases.user
|
||||
|
||||
_this = this
|
||||
}
|
||||
|
||||
// No api-doc documentation because this wont be a public endpoint
|
||||
async createOffer (ctx) {
|
||||
try {
|
||||
console.log('body: ', ctx.request.body)
|
||||
|
||||
const offerObj = ctx.request.body.entry
|
||||
|
||||
const offer = await _this.useCases.offer.createoffer(offerObj)
|
||||
|
||||
ctx.body = { offer }
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
// console.log('err: ', err)
|
||||
// ctx.throw(422, err.message)
|
||||
_this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
// DRY error handler
|
||||
handleError (ctx, err) {
|
||||
console.log('err', err.message)
|
||||
// If an HTTP status is specified by the buisiness logic, use that.
|
||||
if (err.status) {
|
||||
if (err.message) {
|
||||
ctx.throw(err.status, err.message)
|
||||
} else {
|
||||
ctx.throw(err.status)
|
||||
}
|
||||
} else {
|
||||
// By default use a 422 error if the HTTP status is not specified.
|
||||
ctx.throw(422, err.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = OfferRESTControllerLib
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
REST API library for /offer route.
|
||||
*/
|
||||
|
||||
// Public npm libraries.
|
||||
const Router = require('koa-router')
|
||||
|
||||
// Local libraries.
|
||||
const OfferRESTControllerLib = require('./controller')
|
||||
|
||||
let _this
|
||||
|
||||
class OfferRouter {
|
||||
constructor (localConfig = {}) {
|
||||
// Dependency Injection.
|
||||
this.adapters = localConfig.adapters
|
||||
if (!this.adapters) {
|
||||
throw new Error(
|
||||
'Instance of Adapters library required when instantiating /offer REST Controller.'
|
||||
)
|
||||
}
|
||||
this.useCases = localConfig.useCases
|
||||
if (!this.useCases) {
|
||||
throw new Error(
|
||||
'Instance of Use Cases library required when instantiating /offer REST Controller.'
|
||||
)
|
||||
}
|
||||
|
||||
const dependencies = {
|
||||
adapters: this.adapters,
|
||||
useCases: this.useCases
|
||||
}
|
||||
|
||||
// Encapsulate dependencies.
|
||||
this.offerRESTController = new OfferRESTControllerLib(dependencies)
|
||||
|
||||
// Instantiate the router and set the base route.
|
||||
const baseUrl = '/offer'
|
||||
this.router = new Router({ prefix: baseUrl })
|
||||
|
||||
_this = this
|
||||
}
|
||||
|
||||
attach (app) {
|
||||
if (!app) {
|
||||
throw new Error(
|
||||
'Must pass app object when attaching REST API controllers.'
|
||||
)
|
||||
}
|
||||
|
||||
// Define the routes and attach the controller.
|
||||
this.router.post('/', _this.offerRESTController.createOffer)
|
||||
|
||||
// Attach the Controller routes to the Koa app.
|
||||
app.use(_this.router.routes())
|
||||
app.use(_this.router.allowedMethods())
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = OfferRouter
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
Offer Entity
|
||||
An offer is created when a new Signal is detected. It's destroyed when the
|
||||
UTXO described in the Signal has been detected as spent.
|
||||
|
||||
{
|
||||
lokadId: 'SWP', // Placeholder for now, use for backwards compatibility
|
||||
messageType: 1, // SLP Atomic Swap
|
||||
messageClass: 1,
|
||||
tokenId: '38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0',
|
||||
buyOrSell: 'sell',
|
||||
rateInSats: 7972, // Price per token in sats
|
||||
minSatsToExchange: 8100, // Minum size of UTXO to use, e.g. 1 token + miner fees
|
||||
|
||||
// Signature from the address holding the tokens, provides 'proof of reserves'
|
||||
signature: 'H2Sq0UPh0jgs1Zt3JERHtbzfPGXJk9DgJ0FVxVa6iUqiIh6XcvEUFBbvYIuODQs3hYSCkkjcuzbvzNEiv69kFKg=',
|
||||
sigMsg: 'test',
|
||||
address: 'bitcoincash:qphjncqpnv444jq8acqk4dkm3296c50xhqggeatvn8',
|
||||
|
||||
// UTXO for sale
|
||||
utxoTxid: 'b9457808be70c39a9cc6c5857cbef856b35fdc91a59debfe06acfc45b11955e3',
|
||||
utxoVout: 2
|
||||
}
|
||||
*/
|
||||
class Offer {
|
||||
validate ({ data } = {}) {
|
||||
// Input Validation
|
||||
// if (!entry || typeof entry !== "string") {
|
||||
// throw new Error("Property 'entry' must be a string!");
|
||||
// }
|
||||
// if (!description || typeof description !== "string") {
|
||||
// throw new Error("Property 'description' must be a string!");
|
||||
// }
|
||||
// if (!slpAddress || typeof slpAddress !== "string") {
|
||||
// throw new Error("Property 'slpAddress' must be a string!");
|
||||
// }
|
||||
// if (!signature || typeof signature !== "string") {
|
||||
// throw new Error("Property 'signature' must be a string!");
|
||||
// }
|
||||
// if (!category || typeof category !== "string") {
|
||||
// throw new Error("Property 'category' must be a string!");
|
||||
// }
|
||||
|
||||
const offerData = {
|
||||
data
|
||||
}
|
||||
|
||||
return offerData
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Offer
|
||||
@@ -0,0 +1,66 @@
|
||||
const { wlogger } = require('../adapters/wlogger')
|
||||
|
||||
const OfferEntity = require('../entities/offer')
|
||||
class OfferLib {
|
||||
constructor (localConfig = {}) {
|
||||
// console.log('User localConfig: ', localConfig)
|
||||
this.adapters = localConfig.adapters
|
||||
if (!this.adapters) {
|
||||
throw new Error(
|
||||
'Instance of adapters must be passed in when instantiating Offer Use Cases library.'
|
||||
)
|
||||
}
|
||||
|
||||
// Encapsulate dependencies
|
||||
this.OfferEntity = new OfferEntity()
|
||||
this.OfferModel = this.adapters.localdb.Offer
|
||||
this.bchjs = this.adapters.bchjs
|
||||
}
|
||||
|
||||
// Create a new offer model and add it to the Mongo database.
|
||||
async createOffer (entryObj) {
|
||||
try {
|
||||
// Input Validation
|
||||
const offerEntity = this.OfferEntity.validate(entryObj)
|
||||
|
||||
// Verify that the entry was signed by a specific BCH address.
|
||||
const isValidSignature = this.bchjs._verifySignature(offerEntity)
|
||||
if (!isValidSignature) {
|
||||
throw new Error('Invalid signature')
|
||||
}
|
||||
|
||||
// Verify psf tokens balance
|
||||
|
||||
// const psfBalance = await this.bchjs.getPSFTokenBalance(entryEntity.slpAddress)
|
||||
//
|
||||
// if (psfBalance < 10) {
|
||||
// throw new Error('Insufficient psf balance')
|
||||
// }
|
||||
//
|
||||
// const merit = await this.bchjs.getMerit(entryEntity.slpAddress)
|
||||
//
|
||||
// const updatedEntry = {
|
||||
// entry: entryEntity.entry.trim(),
|
||||
// slpAddress: entryEntity.slpAddress.trim(),
|
||||
// description: entryEntity.description.trim(),
|
||||
// signature: entryEntity.signature.trim(),
|
||||
// category: entryEntity.category.trim(),
|
||||
// balance: psfBalance,
|
||||
// merit
|
||||
// }
|
||||
//
|
||||
// const entryModel = new this.EntryModel(updatedEntry)
|
||||
// await entryModel.save()
|
||||
//
|
||||
// return entryModel
|
||||
|
||||
return true
|
||||
} catch (err) {
|
||||
// console.log("Error in use-cases/entry.js/createEntry()", err.message)
|
||||
wlogger.error('Error in use-cases/entry.js/createOffer())')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = OfferLib
|
||||
@@ -22,11 +22,12 @@ describe('bch', () => {
|
||||
|
||||
describe('#_verifySignature', () => {
|
||||
it('should return true for valid signature', () => {
|
||||
const slpAddress = 'simpleledger:qp49th03gvjn58d6fxzaga6u09w4z56smyuk43lzkd'
|
||||
const offerBchAddr =
|
||||
'bitcoincash:qphjncqpnv444jq8acqk4dkm3296c50xhqggeatvn8'
|
||||
const signature =
|
||||
'H1Bv2xUBGZBTuNsUghix03Yp8n8YPPkfsPq6LktwDpC2e1estOfYx96NH3/eaHJpQpPSHSb6pQYaiR3KZ6Z9lRc='
|
||||
const entry = 'example.com'
|
||||
const verifyObj = { slpAddress, signature, entry }
|
||||
'Hz8bi9CsHaYkk5SGtHLU0aaxFspEXz7IdBNn6xV8ejE6OCuIRoZuVE9QJsGSlJ3Rt0ez2LWD0e292NZ84rRwnfk='
|
||||
const sigMsg = 'example.com'
|
||||
const verifyObj = { offerBchAddr, signature, sigMsg }
|
||||
|
||||
const result = uut._verifySignature(verifyObj)
|
||||
|
||||
@@ -34,12 +35,12 @@ describe('bch', () => {
|
||||
})
|
||||
|
||||
it('should return false for invalid signature', () => {
|
||||
const slpAddress = 'simpleledger:qp49th03gvjn58d6fxzaga6u09w4z56smyuk43lzkd'
|
||||
const offerBchAddr =
|
||||
'bitcoincash:qphjncqpnv444jq8acqk4dkm3296c50xhqggeatvn8'
|
||||
const signature =
|
||||
'ICcj+ShSRIllp0iTqQK49Ltnycg1upaT7dK5CPAwNIBqEtegn305dPBf5IMdx/ScuyOBWPEfOqab2V73TbuK6Us='
|
||||
const entry = 'example.com'
|
||||
|
||||
const verifyObj = { slpAddress, signature, entry }
|
||||
'Hz8bi9CsHaYkk5SGtHLU0aaxFspEXz7IdBNn6xV8ejE6OCuIRoZuVE9QJsGSlJ3Rt0ez2LWD0e292NZ84rRwnfd='
|
||||
const sigMsg = 'example.com'
|
||||
const verifyObj = { offerBchAddr, signature, sigMsg }
|
||||
|
||||
const result = uut._verifySignature(verifyObj)
|
||||
|
||||
@@ -53,11 +54,12 @@ describe('bch', () => {
|
||||
.stub(uut.bchjs.BitcoinCash, 'verifyMessage')
|
||||
.throws(new Error('test error'))
|
||||
|
||||
const slpAddress = 'simpleledger:qp49th03gvjn58d6fxzaga6u09w4z56smyuk43lzkd'
|
||||
const offerBchAddr =
|
||||
'bitcoincash:qphjncqpnv444jq8acqk4dkm3296c50xhqggeatvn8'
|
||||
const signature =
|
||||
'ICcj+ShSRIllp0iTqQK49Ltnycg1upaT7dK5CPAwNIBqEtegn305dPBf5IMdx/ScuyOBWPEfOqab2V73TbuK6Us='
|
||||
const entry = 'example.com'
|
||||
const verifyObj = { slpAddress, signature, entry }
|
||||
'Hz8bi9CsHaYkk5SGtHLU0aaxFspEXz7IdBNn6xV8ejE6OCuIRoZuVE9QJsGSlJ3Rt0ez2LWD0e292NZ84rRwnfk='
|
||||
const sigMsg = 'example.com'
|
||||
const verifyObj = { offerBchAddr, signature, sigMsg }
|
||||
uut._verifySignature(verifyObj)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
@@ -83,7 +85,8 @@ describe('bch', () => {
|
||||
.stub(uut.bchjs.SLP.Utils, 'balancesForAddress')
|
||||
.resolves(mockData.psfBalances)
|
||||
|
||||
const slpAddress = 'simpleledger:qp49th03gvjn58d6fxzaga6u09w4z56smyuk43lzkd'
|
||||
const slpAddress =
|
||||
'simpleledger:qp49th03gvjn58d6fxzaga6u09w4z56smyuk43lzkd'
|
||||
const result = await uut.getPSFTokenBalance(slpAddress)
|
||||
|
||||
assert.isNumber(result)
|
||||
@@ -96,7 +99,8 @@ describe('bch', () => {
|
||||
.stub(uut.bchjs.SLP.Utils, 'balancesForAddress')
|
||||
.resolves(mockData.noPsfBalances)
|
||||
|
||||
const slpAddress = 'simpleledger:qp49th03gvjn58d6fxzaga6u09w4z56smyuk43lzkd'
|
||||
const slpAddress =
|
||||
'simpleledger:qp49th03gvjn58d6fxzaga6u09w4z56smyuk43lzkd'
|
||||
const result = await uut.getPSFTokenBalance(slpAddress)
|
||||
|
||||
assert.isNumber(result)
|
||||
@@ -105,11 +109,10 @@ describe('bch', () => {
|
||||
|
||||
it('should return 0 for empty balances', async () => {
|
||||
// Mock live network calls.
|
||||
sandbox
|
||||
.stub(uut.bchjs.SLP.Utils, 'balancesForAddress')
|
||||
.resolves([])
|
||||
sandbox.stub(uut.bchjs.SLP.Utils, 'balancesForAddress').resolves([])
|
||||
|
||||
const slpAddress = 'simpleledger:qp49th03gvjn58d6fxzaga6u09w4z56smyuk43lzkd'
|
||||
const slpAddress =
|
||||
'simpleledger:qp49th03gvjn58d6fxzaga6u09w4z56smyuk43lzkd'
|
||||
const result = await uut.getPSFTokenBalance(slpAddress)
|
||||
|
||||
assert.isNumber(result)
|
||||
@@ -136,11 +139,10 @@ describe('bch', () => {
|
||||
it('should return the merit ', async () => {
|
||||
try {
|
||||
// Mock live network calls.
|
||||
sandbox
|
||||
.stub(uut.msgLib.merit, 'agMerit')
|
||||
.resolves(100)
|
||||
sandbox.stub(uut.msgLib.merit, 'agMerit').resolves(100)
|
||||
|
||||
const slpAddr = 'simpleledger:qqgnksc6zr4nzxrye69fq625wu2myxey6uh9kzjy96'
|
||||
const slpAddr =
|
||||
'simpleledger:qqgnksc6zr4nzxrye69fq625wu2myxey6uh9kzjy96'
|
||||
const merit = await uut.getMerit(slpAddr)
|
||||
assert.isNumber(merit)
|
||||
} catch (err) {
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
Unit tests for the Offer Use Case library.
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
const assert = require('chai').assert
|
||||
const sinon = require('sinon')
|
||||
|
||||
// Local support libraries
|
||||
// const testUtils = require('../../utils/test-utils')
|
||||
|
||||
// Unit under test (uut)
|
||||
const EntryLib = require('../../../src/use-cases/entry')
|
||||
const adapters = require('../mocks/adapters')
|
||||
|
||||
describe('#users-use-case', () => {
|
||||
let uut
|
||||
let sandbox
|
||||
|
||||
before(async () => {
|
||||
// Delete all previous users in the database.
|
||||
// await testUtils.deleteAllUsers()
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
uut = new EntryLib({ adapters })
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('#constructor', () => {
|
||||
it('should throw an error if adapters are not passed in', () => {
|
||||
try {
|
||||
uut = new EntryLib()
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
'Instance of adapters must be passed in when instantiating User Use Cases library.'
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#createEntry', () => {
|
||||
it('should throw an error if entry is not provided', async () => {
|
||||
try {
|
||||
await uut.createEntry({})
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, "Property 'entry' must be a string!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw an error if description is not provided', async () => {
|
||||
try {
|
||||
const inputData = {
|
||||
entry: 'entry'
|
||||
}
|
||||
await uut.createEntry(inputData)
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, "Property 'description' must be a string!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw an error if slpAddress is not provided', async () => {
|
||||
try {
|
||||
const inputData = {
|
||||
entry: 'entry',
|
||||
description: 'test'
|
||||
}
|
||||
await uut.createEntry(inputData)
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, "Property 'slpAddress' must be a string!")
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw an error if signature is not provided', async () => {
|
||||
try {
|
||||
const inputData = {
|
||||
entry: 'entry',
|
||||
description: 'test',
|
||||
slpAddress: 'simpleledger:qpnty9t0w93fez04h7yzevujpv8pun204qqp0jfafg'
|
||||
}
|
||||
await uut.createEntry(inputData)
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, "Property 'signature' must be a string!")
|
||||
}
|
||||
})
|
||||
it('should throw an error if category is not provided', async () => {
|
||||
try {
|
||||
const inputData = {
|
||||
entry: 'entry',
|
||||
description: 'test',
|
||||
slpAddress: 'simpleledger:qpnty9t0w93fez04h7yzevujpv8pun204qqp0jfafg',
|
||||
signature:
|
||||
'IFytRg6KpvTHCzcW0ZwVhPqdKtRGpoRDcuEb958yIgJFUJlb1F5qPzt/JnlYE7r012BSFj+UT67DZVTU8oNB5vw='
|
||||
}
|
||||
await uut.createEntry(inputData)
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, "Property 'category' must be a string!")
|
||||
}
|
||||
})
|
||||
it('should catch and throw DB errors', async () => {
|
||||
try {
|
||||
// Force an error with the database.
|
||||
sandbox.stub(uut, 'EntryModel').throws(new Error('test error'))
|
||||
|
||||
const inputData = {
|
||||
entry: 'entry',
|
||||
description: 'test',
|
||||
slpAddress: 'simpleledger:qpnty9t0w93fez04h7yzevujpv8pun204qqp0jfafg',
|
||||
signature:
|
||||
'IFytRg6KpvTHCzcW0ZwVhPqdKtRGpoRDcuEb958yIgJFUJlb1F5qPzt/JnlYE7r012BSFj+UT67DZVTU8oNB5vw=',
|
||||
category: 'test'
|
||||
}
|
||||
|
||||
await uut.createEntry(inputData)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
it('should throw error if signature is invalid', async () => {
|
||||
try {
|
||||
// Mocking bchjs functions
|
||||
sandbox.stub(uut.bchjs, '_verifySignature').callsFake(() => {
|
||||
return false
|
||||
})
|
||||
|
||||
const inputData = {
|
||||
entry: 'entry',
|
||||
description: 'test',
|
||||
slpAddress: 'simpleledger:qpnty9t0w93fez04h7yzevujpv8pun204qqp0jfafg',
|
||||
signature:
|
||||
'IFytRg6KpvTHCzcW0ZwVhPqdKtRGpoRDcuEb958yIgJFUJlb1F5qPzt/JnlYE7r012BSFj+UT67DZVTU8oNB5vw=',
|
||||
category: 'test'
|
||||
}
|
||||
|
||||
await uut.createEntry(inputData)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Invalid signature')
|
||||
}
|
||||
})
|
||||
it('should throw error for insufficient psf balance', async () => {
|
||||
try {
|
||||
// Mocking bchjs functions
|
||||
sandbox.stub(uut.bchjs, 'getPSFTokenBalance').resolves(0)
|
||||
|
||||
const inputData = {
|
||||
entry: 'entry',
|
||||
description: 'test',
|
||||
slpAddress: 'simpleledger:qpnty9t0w93fez04h7yzevujpv8pun204qqp0jfafg',
|
||||
signature:
|
||||
'IFytRg6KpvTHCzcW0ZwVhPqdKtRGpoRDcuEb958yIgJFUJlb1F5qPzt/JnlYE7r012BSFj+UT67DZVTU8oNB5vw=',
|
||||
category: 'test'
|
||||
}
|
||||
|
||||
await uut.createEntry(inputData)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Insufficient psf balance')
|
||||
}
|
||||
})
|
||||
|
||||
it('should create a new entry in the DB', async () => {
|
||||
// Mocking bchjs functions
|
||||
// sandbox.stub(uut.bchjs, '_verifySignature').resolves(true)
|
||||
// sandbox.stub(uut.bchjs, 'getPSFTokenBalance').resolves(100)
|
||||
// sandbox.stub(uut.bchjs, 'getMerit').resolves(100)
|
||||
|
||||
const inputData = {
|
||||
entry: 'entry',
|
||||
description: 'test',
|
||||
slpAddress: 'simpleledger:qpnty9t0w93fez04h7yzevujpv8pun204qqp0jfafg',
|
||||
signature:
|
||||
'IFytRg6KpvTHCzcW0ZwVhPqdKtRGpoRDcuEb958yIgJFUJlb1F5qPzt/JnlYE7r012BSFj+UT67DZVTU8oNB5vw=',
|
||||
category: 'test'
|
||||
}
|
||||
|
||||
const result = await uut.createEntry(inputData)
|
||||
|
||||
assert.property(result, 'entry')
|
||||
assert.isString(result.entry)
|
||||
|
||||
assert.property(result, 'slpAddress')
|
||||
assert.isString(result.slpAddress)
|
||||
|
||||
assert.property(result, 'description')
|
||||
assert.isString(result.description)
|
||||
|
||||
assert.property(result, 'signature')
|
||||
assert.isString(result.signature)
|
||||
|
||||
assert.property(result, 'category')
|
||||
assert.isString(result.category)
|
||||
|
||||
assert.property(result, 'balance')
|
||||
assert.isNumber(result.balance)
|
||||
|
||||
assert.property(result, 'merit')
|
||||
assert.isNumber(result.merit)
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user