mirror of
https://github.com/Permissionless-Software-Foundation/bch-dex.git
synced 2026-09-21 16:52:00 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3cdf49bb56 | ||
|
|
28615403b8 | ||
|
|
bb10bd819a | ||
|
|
3882217e6a | ||
|
|
8cd2896115 | ||
|
|
e570519ec8 | ||
|
|
2f74b74110 | ||
|
|
fddb5499f0 | ||
|
|
fed98d51be | ||
|
|
8e3ee3a0fb | ||
|
|
e57faf8f26 | ||
|
|
61fa6b0ed6 | ||
|
|
99843061ab | ||
|
|
3a23763800 |
Vendored
+2
-2
@@ -47,8 +47,8 @@ export default {
|
||||
useFullStackCash: process.env.USE_FULLSTACKCASH ? true : false,
|
||||
consumerUrl: process.env.CONSUMER_URL
|
||||
? process.env.CONSUMER_URL
|
||||
// : 'https://free-bch.fullstack.cash',
|
||||
: 'https://dev-consumer.psfoundation.info',
|
||||
: 'https://free-bch.fullstack.cash',
|
||||
// : 'https://dev-consumer.psfoundation.info',
|
||||
// : 'https://wa-usa-bch-consumer.fullstackcash.nl',
|
||||
|
||||
// P2WDB URL that will accept API calls from the p2wdb npm library.
|
||||
|
||||
@@ -46,7 +46,9 @@ const Offer = new mongoose.Schema({
|
||||
mutableDataCid: { type: String, default: null },
|
||||
tokenIconUrl: { type: String, default: null },
|
||||
tokenCategories: { type: Array, default: [] },
|
||||
tokenTags: { type: Array, default: [] }
|
||||
tokenTags: { type: Array, default: [] },
|
||||
userDataStr: { type: String }, // Token user data
|
||||
lastUpdatedTokenData: { type: String, default: null } // ISO timestamp of the last time token data was updated.
|
||||
})
|
||||
|
||||
export default mongoose.model('offer', Offer)
|
||||
|
||||
@@ -56,6 +56,7 @@ class WalletAdapter {
|
||||
this.completeTx = this.completeTx.bind(this)
|
||||
this.reclaimTokens = this.reclaimTokens.bind(this)
|
||||
this.moveTokensFromCustomWallet = this.moveTokensFromCustomWallet.bind(this)
|
||||
this.cid2json = this.cid2json.bind(this)
|
||||
}
|
||||
|
||||
// Open the wallet file, or create one if the file doesn't exist.
|
||||
@@ -677,6 +678,25 @@ class WalletAdapter {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
async cid2json (urlOrCid) {
|
||||
try {
|
||||
// Input validation
|
||||
if (!urlOrCid || typeof urlOrCid !== 'string') {
|
||||
throw new Error('urlOrCid must be a string!')
|
||||
}
|
||||
// Extract the cid from the url or cid.
|
||||
const cid = urlOrCid.split('/').pop()
|
||||
console.log('cid to json: ', cid)
|
||||
const jsonRes = await this.bchWallet.cid2json({ cid })
|
||||
const json = jsonRes.json
|
||||
// console.log('json: ', json)
|
||||
return json
|
||||
} catch (err) {
|
||||
console.error('Error in wallet.js/cid2json()', err.message)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default WalletAdapter
|
||||
|
||||
@@ -34,6 +34,7 @@ class OfferRESTControllerLib {
|
||||
this.listFungibleOffers = this.listFungibleOffers.bind(this)
|
||||
this.takeOffer = this.takeOffer.bind(this)
|
||||
this.listOffersByAddress = this.listOffersByAddress.bind(this)
|
||||
this.syncOfferMutableData = this.syncOfferMutableData.bind(this)
|
||||
this.handleError = this.handleError.bind(this)
|
||||
}
|
||||
|
||||
@@ -395,6 +396,18 @@ class OfferRESTControllerLib {
|
||||
}
|
||||
}
|
||||
|
||||
async syncOfferMutableData (ctx) {
|
||||
try {
|
||||
const tokenId = ctx.request.body.tokenId
|
||||
const offer = await this.useCases.offer.syncOfferMutableData(tokenId)
|
||||
|
||||
ctx.body = offer
|
||||
} catch (err) {
|
||||
console.log('Error in syncOfferMutableData REST API handler: ', err)
|
||||
this.handleError(ctx, err)
|
||||
}
|
||||
}
|
||||
|
||||
// DRY error handler
|
||||
handleError (ctx, err) {
|
||||
console.log('err', err.message)
|
||||
|
||||
@@ -55,6 +55,7 @@ class OfferRouter {
|
||||
// Define the routes and attach the controller.
|
||||
// this.router.post('/', _this.offerRESTController.createOffer) // Deprecated.
|
||||
this.router.post('/take', this.offerRESTController.takeOffer)
|
||||
this.router.post('/mutable/sync/', this.offerRESTController.syncOfferMutableData)
|
||||
this.router.get('/list/all/:page', this.offerRESTController.listOffers)
|
||||
this.router.get('/list/nft/:page', this.offerRESTController.listNftOffers)
|
||||
this.router.get('/list/fungible/:page', this.offerRESTController.listFungibleOffers)
|
||||
|
||||
+172
-14
@@ -65,6 +65,7 @@ class OfferUseCases {
|
||||
this.flagOffer = this.flagOffer.bind(this)
|
||||
this.loadOffers = this.loadOffers.bind(this)
|
||||
this.listOffersByAddress = this.listOffersByAddress.bind(this)
|
||||
this.syncOfferMutableData = this.syncOfferMutableData.bind(this)
|
||||
|
||||
// State
|
||||
this.seenOffers = []
|
||||
@@ -129,20 +130,57 @@ class OfferUseCases {
|
||||
|
||||
// Get data about the token.
|
||||
const tokenId = offerEntity.tokenId
|
||||
// const tokenData = await this.adapters.wallet.bchWallet.getTokenData(tokenId)
|
||||
const tokenData = await this.retryQueue.addToQueue(this.adapters.wallet.bchWallet.getTokenData, tokenId)
|
||||
// console.log(`tokenData: ${JSON.stringify(tokenData, null, 2)}`)
|
||||
let tokenData = null
|
||||
try {
|
||||
tokenData = await this.retryQueue.addToQueue(this.adapters.wallet.bchWallet.getTokenData, tokenId)
|
||||
} catch (err) {
|
||||
console.error('Error in OfferUseCases/createOffer() getting token data: ', err.message)
|
||||
}
|
||||
|
||||
// Generate a 'display category' for the token. This will allow the
|
||||
// front end UI to figure out how to display the token.
|
||||
const displayCategory = this.categorizeToken(offerEntity, tokenData)
|
||||
console.log('displayCategory: ', displayCategory)
|
||||
offerEntity.displayCategory = displayCategory
|
||||
// Do additional data analysis if the token data was successfully retrieved.
|
||||
if (tokenData) {
|
||||
// Store the mutable and immutable data cids.
|
||||
const mutableDataCid = tokenData.mutableData
|
||||
const immutableDataCid = tokenData.immutableData
|
||||
offerEntity.mutableDataCid = mutableDataCid
|
||||
offerEntity.immutableDataCid = immutableDataCid
|
||||
|
||||
// Detect if user set the NSFW flag.
|
||||
// const nsfw = false
|
||||
// nsfw = await this.retryQueue.addToQueue(this.detectNsfw, tokenData)
|
||||
// offerEntity.nsfw = nsfw
|
||||
// Get the mutable data from the cid if it exists.
|
||||
if (mutableDataCid && typeof mutableDataCid === 'string') {
|
||||
let mutableData = null
|
||||
try {
|
||||
mutableData = await this.retryQueue.addToQueue(this.adapters.wallet.cid2json, mutableDataCid)
|
||||
} catch (err) {
|
||||
console.error('Error in OfferUseCases/createOffer() getting mutable data: ', err.message)
|
||||
}
|
||||
console.log('mutableData: ', mutableData)
|
||||
|
||||
if (mutableData) {
|
||||
try {
|
||||
offerEntity.tokenIconUrl = mutableData.tokenIcon
|
||||
offerEntity.tokenCategories = mutableData.category
|
||||
offerEntity.tokenTags = mutableData.tags
|
||||
offerEntity.lastUpdatedTokenData = new Date().getTime()
|
||||
|
||||
offerEntity.userDataStr = JSON.stringify(mutableData.userData)
|
||||
} catch (error) {
|
||||
// skip error
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log('offerEntity: ', offerEntity)
|
||||
|
||||
// Generate a 'display category' for the token. This will allow the
|
||||
// front end UI to figure out how to display the token.
|
||||
const displayCategory = this.categorizeToken(offerEntity, tokenData)
|
||||
console.log('displayCategory: ', displayCategory)
|
||||
offerEntity.displayCategory = displayCategory
|
||||
|
||||
// Detect if user set the NSFW flag.
|
||||
// const nsfw = false
|
||||
// nsfw = await this.retryQueue.addToQueue(this.detectNsfw, tokenData)
|
||||
// offerEntity.nsfw = nsfw
|
||||
}
|
||||
|
||||
// Add offer to the local database.
|
||||
const offerModel = new this.OfferModel(offerEntity)
|
||||
@@ -150,7 +188,7 @@ class OfferUseCases {
|
||||
|
||||
return true
|
||||
} catch (err) {
|
||||
console.error('Error in createOffer()', err.message)
|
||||
console.error('\n\nError in createOffer()', err.message, '\n\n', err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -267,7 +305,7 @@ class OfferUseCases {
|
||||
}
|
||||
}
|
||||
|
||||
async listNftOffers (page = 0, nsfw = false) {
|
||||
/* async listNftOffers (page = 0, nsfw = false) {
|
||||
try {
|
||||
const data = await this.OfferModel.find({
|
||||
displayCategory: { $ne: 'fungible' },
|
||||
@@ -287,6 +325,42 @@ class OfferUseCases {
|
||||
console.error('Error in use-cases/offer/listNftOffers()')
|
||||
throw error
|
||||
}
|
||||
} */
|
||||
|
||||
async listNftOffers (page = 0, nsfw = false) {
|
||||
try {
|
||||
const query = {
|
||||
displayCategory: { $ne: 'fungible' },
|
||||
nsfw
|
||||
}
|
||||
// Total query offers
|
||||
const totalOffers = await this.OfferModel.countDocuments(query)
|
||||
// Total pages
|
||||
const totalPages = Math.ceil(totalOffers / NFT_ENTRIES_PER_PAGE)
|
||||
|
||||
const data = await this.OfferModel.find(query)
|
||||
// Sort entries so newest entries show first.
|
||||
.sort('-timestamp')
|
||||
// Skip to the start of the selected page.
|
||||
.skip(page * NFT_ENTRIES_PER_PAGE)
|
||||
// Only return 20 results.
|
||||
.limit(NFT_ENTRIES_PER_PAGE)
|
||||
|
||||
// console.log('listNftOffers() returning this data: ', data)
|
||||
|
||||
return {
|
||||
data,
|
||||
pagination: {
|
||||
currentPage: page,
|
||||
totalPages,
|
||||
totalOffers,
|
||||
pageSize: NFT_ENTRIES_PER_PAGE
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error in use-cases/offer/listNftOffers()')
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async listFungibleOffers (page = 0) {
|
||||
@@ -862,6 +936,90 @@ class OfferUseCases {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// update offer model mutable data
|
||||
async syncOfferMutableData (tokenId) {
|
||||
try {
|
||||
// validate input
|
||||
if (!tokenId || typeof tokenId !== 'string') {
|
||||
throw new Error('tokenId must be a string!')
|
||||
}
|
||||
|
||||
// validate existing offer with the associated tokenId
|
||||
const offer = await this.OfferModel.findOne({ tokenId })
|
||||
if (!offer) throw new Error('Associated offer not found!')
|
||||
|
||||
// Verify last update timestamp. This prevents users from spamming the API with requests.
|
||||
// It only updates the mutable data if it has been more than 5 minutes since the last update.
|
||||
const lastUpdateTs = Number(offer.lastUpdatedTokenData)
|
||||
const now = new Date().getTime()
|
||||
const period = 5
|
||||
if (lastUpdateTs) {
|
||||
// add 5 minutes to the last update
|
||||
const lastUpdate = new Date(lastUpdateTs)
|
||||
console.log('lastUpdate', lastUpdate)
|
||||
lastUpdate.setMinutes(lastUpdate.getMinutes() + period)
|
||||
|
||||
console.log(new Date().getMinutes() + ' ' + lastUpdate.getMinutes())
|
||||
// if now is less than the lastUpdate + 5 minutos , them skip.
|
||||
if (now < lastUpdate.getTime()) {
|
||||
console.log('Skipping , token lastUpdate is less than 5 minutes.')
|
||||
return offer
|
||||
}
|
||||
}
|
||||
|
||||
let tokenData = null
|
||||
try {
|
||||
tokenData = await this.retryQueue.addToQueue(this.adapters.wallet.bchWallet.getTokenData, tokenId)
|
||||
} catch (err) {
|
||||
// Dev Note: If getTokenData() fails, the code below will
|
||||
console.error('Error in OfferUseCases/createOffer() getting token data: ', err.message)
|
||||
}
|
||||
|
||||
// Do additional data analysis if the token data was successfully retrieved.
|
||||
if (tokenData) {
|
||||
// Store the mutable and immutable data cids.
|
||||
const mutableDataCid = tokenData.mutableData
|
||||
const immutableDataCid = tokenData.immutableData
|
||||
offer.mutableDataCid = mutableDataCid
|
||||
offer.immutableDataCid = immutableDataCid
|
||||
|
||||
// Get the mutable data from the cid if it exists.
|
||||
if (mutableDataCid && typeof mutableDataCid === 'string') {
|
||||
let mutableData = null
|
||||
try {
|
||||
mutableData = await this.retryQueue.addToQueue(this.adapters.wallet.cid2json, mutableDataCid)
|
||||
} catch (err) {
|
||||
console.error('Error in OfferUseCases/createOffer() getting mutable data: ', err.message)
|
||||
}
|
||||
console.log('mutableData: ', mutableData)
|
||||
|
||||
if (mutableData) {
|
||||
try {
|
||||
offer.tokenIconUrl = mutableData.tokenIcon
|
||||
offer.tokenCategories = mutableData.category
|
||||
offer.tokenTags = mutableData.tags
|
||||
|
||||
offer.userDataStr = JSON.stringify(mutableData.userData)
|
||||
} catch (error) {
|
||||
// skip error
|
||||
}
|
||||
}
|
||||
}
|
||||
// Save update time stamp
|
||||
offer.lastUpdatedTokenData = new Date().getTime()
|
||||
}
|
||||
|
||||
// Save the updated offer data to the database.
|
||||
await offer.save()
|
||||
|
||||
// Return the updated offer data.
|
||||
return offer
|
||||
} catch (err) {
|
||||
console.error('Error in syncOfferMutableData(): ', err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default OfferUseCases
|
||||
|
||||
@@ -493,4 +493,35 @@ describe('#wallet', () => {
|
||||
}
|
||||
})
|
||||
})
|
||||
describe('#cid2json', () => {
|
||||
it('should throw error if cid is not provided', async () => {
|
||||
try {
|
||||
await uut.cid2json()
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (error) {
|
||||
assert.equal(error.message, 'urlOrCid must be a string!')
|
||||
}
|
||||
})
|
||||
|
||||
it('should convert cid to json', async () => {
|
||||
try {
|
||||
sandbox.stub(uut.bchWallet, 'cid2json').resolves({ json: offerMockData.mutableDataMock })
|
||||
const cid = 'bafkreidr6wfd6mcmwpea7abm5uk5rrprc2wfbcvo5wdcmtlolrpjab5oqm'
|
||||
const result = await uut.cid2json(cid)
|
||||
assert.isObject(result)
|
||||
} catch (error) {
|
||||
assert.fail('Unexpected code path')
|
||||
}
|
||||
})
|
||||
it('should convert ipfs url to json', async () => {
|
||||
try {
|
||||
sandbox.stub(uut.bchWallet, 'cid2json').resolves({ json: offerMockData.mutableDataMock })
|
||||
const url = 'https://ipfs.io/ipfs/bafkreidr6wfd6mcmwpea7abm5uk5rrprc2wfbcvo5wdcmtlolrpjab5oqm'
|
||||
const result = await uut.cid2json(url)
|
||||
assert.isObject(result)
|
||||
} catch (error) {
|
||||
assert.fail('Unexpected code path')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -229,6 +229,27 @@ describe('#Offer-REST-Router', () => {
|
||||
}
|
||||
})
|
||||
})
|
||||
describe('#syncOfferMutableData', () => {
|
||||
it('should sync mutable data', async () => {
|
||||
ctx.request.body = { tokenId: 'tokenId' }
|
||||
sandbox.stub(uut.useCases.offer, 'syncOfferMutableData').resolves({})
|
||||
await uut.syncOfferMutableData(ctx)
|
||||
assert.isObject(ctx.body)
|
||||
})
|
||||
it('should catch and throw an error', async () => {
|
||||
try {
|
||||
ctx.request.body = { tokenId: 'tokenId' }
|
||||
sandbox
|
||||
.stub(uut.useCases.offer, 'syncOfferMutableData')
|
||||
.throws(new Error('test error'))
|
||||
|
||||
await uut.syncOfferMutableData(ctx)
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#handleError', () => {
|
||||
it('should still throw error if there is no message', () => {
|
||||
|
||||
@@ -149,7 +149,7 @@ const localdb = {
|
||||
static findById () {}
|
||||
static find () {}
|
||||
static findOne () {}
|
||||
|
||||
static countDocuments(){}
|
||||
async save () {
|
||||
return {}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ class MockBchWallet {
|
||||
}
|
||||
};
|
||||
this.optimize = async () => { };
|
||||
this.cid2json = async () => {};
|
||||
this.ar = new AdapterRoute()
|
||||
// Environment variable is used by wallet-balance.unit.js to force an error.
|
||||
if (process.env.NO_UTXO) {
|
||||
|
||||
@@ -71,6 +71,9 @@ class Offer {
|
||||
async acceptCounterOffer() {
|
||||
return {}
|
||||
}
|
||||
async syncOfferMutableData(){
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
class Order {
|
||||
|
||||
@@ -126,6 +126,20 @@ const fungibleTokenData01 = {
|
||||
"immutableData": "troutsblog.com",
|
||||
"mutableData": ""
|
||||
}
|
||||
const mutableDataMock = {
|
||||
schema: 'ps007-v1.0.3',
|
||||
schemaDocs: 'https://github.com/Permissionless-Software-Foundation/specifications/blob/master/ps007-token-data-schema.md',
|
||||
tokenIcon: 'https://files.tokentiger.com/ipfs/view/bafkreidr6wfd6mcmwpea7abm5uk5rrprc2wfbcvo5wdcmtlolrpjab5oqm',
|
||||
fullSizedUrl: '',
|
||||
nsfw: false,
|
||||
userData: { currentUrl: 'https://tokentiger.com' },
|
||||
jsonLd: {},
|
||||
about: 'This is AI generated art.',
|
||||
category: '',
|
||||
tags: [],
|
||||
mediaType: 'image',
|
||||
currentOwner: {},
|
||||
}
|
||||
|
||||
const offerMockData = {
|
||||
data: {
|
||||
@@ -214,5 +228,6 @@ export default {
|
||||
fungibleTokenData01,
|
||||
offerMockData,
|
||||
deserealizeTxMockNoOperatorOut,
|
||||
deserealizeTxMock
|
||||
deserealizeTxMock,
|
||||
mutableDataMock
|
||||
};
|
||||
|
||||
@@ -108,6 +108,73 @@ describe('#offer-use-case', () => {
|
||||
assert.isFalse(result)
|
||||
})
|
||||
|
||||
it('should create offer with mutable data', async () => {
|
||||
const tokenDataMock = mockData.nftTokenData01
|
||||
const offerObj = mockData.offerMockData
|
||||
const mutableDataMock = mockData.mutableDataMock
|
||||
|
||||
// Mock dependencies
|
||||
// sandbox.stub(uut.adapters.wallet.bchWallet, 'utxoIsValid').resolves(false)
|
||||
sandbox.stub(uut, 'findOfferByTxid').throws(new Error('offer not found'))
|
||||
sandbox.stub(uut.retryQueue, 'addToQueue')
|
||||
.onCall(0).resolves({}) // Utxo Status call
|
||||
.onCall(1).resolves(tokenDataMock) // Token Data call
|
||||
.onCall(2).resolves(mutableDataMock)
|
||||
|
||||
const result = await uut.createOffer(offerObj)
|
||||
assert.isTrue(result)
|
||||
})
|
||||
|
||||
it('should skip userData stringify error', async () => {
|
||||
const tokenDataMock = mockData.nftTokenData01
|
||||
const offerObj = mockData.offerMockData
|
||||
const mutableDataMock = mockData.mutableDataMock
|
||||
mutableDataMock.userData = { n: 10n }
|
||||
// Mock dependencies
|
||||
// sandbox.stub(uut.adapters.wallet.bchWallet, 'utxoIsValid').resolves(false)
|
||||
sandbox.stub(uut, 'findOfferByTxid').throws(new Error('offer not found'))
|
||||
sandbox.stub(uut.retryQueue, 'addToQueue')
|
||||
.onCall(0).resolves({}) // Utxo Status call
|
||||
.onCall(1).resolves(tokenDataMock) // Token Data call
|
||||
.onCall(2).resolves(mutableDataMock)
|
||||
|
||||
const result = await uut.createOffer(offerObj)
|
||||
assert.isTrue(result)
|
||||
})
|
||||
|
||||
it('should handle error getting token data', async () => {
|
||||
// const tokenDataMock = mockData.nftTokenData01
|
||||
const offerObj = mockData.offerMockData
|
||||
const mutableDataMock = mockData.mutableDataMock
|
||||
mutableDataMock.userData = { n: 10n }
|
||||
// Mock dependencies
|
||||
// sandbox.stub(uut.adapters.wallet.bchWallet, 'utxoIsValid').resolves(false)
|
||||
sandbox.stub(uut, 'findOfferByTxid').throws(new Error('offer not found'))
|
||||
sandbox.stub(uut.retryQueue, 'addToQueue')
|
||||
.onCall(0).resolves({}) // Utxo Status call
|
||||
.onCall(1).throws(new Error('test error'))
|
||||
|
||||
const result = await uut.createOffer(offerObj)
|
||||
assert.isTrue(result)
|
||||
})
|
||||
|
||||
it('should handle error getting mutable data', async () => {
|
||||
const tokenDataMock = mockData.nftTokenData01
|
||||
const offerObj = mockData.offerMockData
|
||||
const mutableDataMock = mockData.mutableDataMock
|
||||
mutableDataMock.userData = { n: 10n }
|
||||
// Mock dependencies
|
||||
// sandbox.stub(uut.adapters.wallet.bchWallet, 'utxoIsValid').resolves(false)
|
||||
sandbox.stub(uut, 'findOfferByTxid').throws(new Error('offer not found'))
|
||||
sandbox.stub(uut.retryQueue, 'addToQueue')
|
||||
.onCall(0).resolves({}) // Utxo Status call
|
||||
.onCall(1).resolves(tokenDataMock) // Token Data call
|
||||
.onCall(2).throws(new Error('test error')) // cid2json call
|
||||
|
||||
const result = await uut.createOffer(offerObj)
|
||||
assert.isTrue(result)
|
||||
})
|
||||
|
||||
it('should create offer', async () => {
|
||||
const tokenDataMock = mockData.simpleNftTokenData01
|
||||
const offerObj = mockData.offerMockData
|
||||
@@ -400,7 +467,7 @@ describe('#offer-use-case', () => {
|
||||
}
|
||||
})
|
||||
|
||||
it('should list orders', async () => {
|
||||
it('should list offers', async () => {
|
||||
const queryMock = {
|
||||
sort () {
|
||||
return this
|
||||
@@ -414,7 +481,11 @@ describe('#offer-use-case', () => {
|
||||
sandbox.stub(uut.OfferModel, 'find').returns(queryMock)
|
||||
|
||||
const result = await uut.listNftOffers(1, true)
|
||||
assert.isArray(result)
|
||||
assert.isObject(result)
|
||||
assert.property(result, 'data')
|
||||
assert.property(result, 'pagination')
|
||||
assert.isArray(result.data)
|
||||
assert.isObject(result.pagination)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -781,27 +852,29 @@ describe('#offer-use-case', () => {
|
||||
assert.equal(result, 'N/A')
|
||||
})
|
||||
|
||||
it('should return if utxo cant be validated', async () => {
|
||||
it('should return N/A on axios error', async () => {
|
||||
// Mock dependencies
|
||||
const mock = Object.assign({}, mockData.offerMockData.data)
|
||||
sandbox.stub(uut.orderUseCase, 'findOrderByUtxo').resolves(mock)
|
||||
sandbox.stub(uut.adapters.wallet.bchWallet, 'utxoIsValid').throws(new Error('test error'))
|
||||
// sandbox.stub(uut.adapters.wallet.bchWallet, 'utxoIsValid').throws(new Error('test error'))
|
||||
const axiosErr = new Error('axios err')
|
||||
axiosErr.isAxiosError = true
|
||||
sandbox.stub(uut.retryQueue, 'addToQueue').throws(axiosErr)
|
||||
|
||||
const result = await uut.acceptCounterOffer({ data: { /** .... */ } })
|
||||
assert.equal(result, 'N/A')
|
||||
})
|
||||
it('should return if utxo can not be validated', async () => {
|
||||
// Mock dependencies
|
||||
const mock = Object.assign({}, mockData.offerMockData.data)
|
||||
sandbox.stub(uut.orderUseCase, 'findOrderByUtxo').resolves(mock)
|
||||
// sandbox.stub(uut.adapters.wallet.bchWallet, 'utxoIsValid').throws(new Error('test error'))
|
||||
sandbox.stub(uut.retryQueue, 'addToQueue').throws(new Error('test error'))
|
||||
|
||||
const result = await uut.acceptCounterOffer({ data: { /** .... */ } })
|
||||
assert.equal(result, 'N/A')
|
||||
})
|
||||
|
||||
it('should handle axios error', async () => {
|
||||
// Mock dependencies
|
||||
const mock = Object.assign({}, mockData.offerMockData.data)
|
||||
const stubErr = new Error()
|
||||
stubErr.isAxiosError = true
|
||||
sandbox.stub(uut.orderUseCase, 'findOrderByUtxo').resolves(mock)
|
||||
sandbox.stub(uut.adapters.wallet.bchWallet, 'utxoIsValid').throws(stubErr)
|
||||
|
||||
const result = await uut.acceptCounterOffer({ data: { /** .... */ } })
|
||||
assert.equal(result, 'N/A')
|
||||
})
|
||||
it('should return if utxo is invalid', async () => {
|
||||
// Mock dependencies
|
||||
const mock = Object.assign({}, mockData.offerMockData.data)
|
||||
@@ -949,4 +1022,112 @@ describe('#offer-use-case', () => {
|
||||
assert.notEqual('N/A')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#syncOfferMutableData', () => {
|
||||
it('should throw error if tokenId is not provided!', async () => {
|
||||
try {
|
||||
await uut.syncOfferMutableData()
|
||||
assert.fail('unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'tokenId must be a string!')
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw error if tokenId is not found!', async () => {
|
||||
try {
|
||||
// Mock dependencies
|
||||
sandbox.stub(uut.OfferModel, 'findOne').resolves(null)
|
||||
|
||||
await uut.syncOfferMutableData('tokenId')
|
||||
assert.fail('unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Associated offer not found!')
|
||||
}
|
||||
})
|
||||
|
||||
it('should return current data if lastUpdatedTokenData is less than 5 minutes', async () => {
|
||||
// Mock dependencies
|
||||
const lastUpdatedTokenData = new Date().getTime()
|
||||
const offerMock = Object.assign({}, mockData.offerMockData)
|
||||
offerMock.lastUpdatedTokenData = lastUpdatedTokenData
|
||||
// stub
|
||||
sandbox.stub(uut.OfferModel, 'findOne').returns(offerMock)
|
||||
const spy = sandbox.stub(uut.retryQueue, 'addToQueue').resolves(true)
|
||||
|
||||
const offer = await uut.syncOfferMutableData('tokenId')
|
||||
|
||||
assert.isObject(offer)
|
||||
assert.isTrue(spy.notCalled, 'it should not call retryQueue functions.')
|
||||
})
|
||||
|
||||
it('should sync offer', async () => {
|
||||
// create a timestamp 6 minutes in the past
|
||||
|
||||
// Create offer mock
|
||||
const offerMock = Object.assign({}, mockData.offerMockData)
|
||||
offerMock.save = () => { }
|
||||
|
||||
// Stub functions
|
||||
sandbox.stub(uut.OfferModel, 'findOne').returns(offerMock)
|
||||
sandbox.stub(uut.retryQueue, 'addToQueue')
|
||||
.onCall(0).resolves(mockData.simpleNftTokenData01) // Token Data call
|
||||
.onCall(1).resolves(mockData.mutableDataMock)
|
||||
|
||||
const offer = await uut.syncOfferMutableData('tokenId')
|
||||
assert.isObject(offer)
|
||||
assert.isNumber(offer.lastUpdatedTokenData)
|
||||
})
|
||||
|
||||
it('should skip userData stringify error', async () => {
|
||||
// create mutable data mock
|
||||
const mutableDataMock = mockData.mutableDataMock
|
||||
mutableDataMock.userData = { n: 10n }
|
||||
|
||||
// create offer mock
|
||||
const offerMock = Object.assign({}, mockData.offerMockData)
|
||||
offerMock.save = () => { }
|
||||
|
||||
// Stub functions
|
||||
sandbox.stub(uut.OfferModel, 'findOne').returns(offerMock)
|
||||
sandbox.stub(uut.retryQueue, 'addToQueue')
|
||||
.onCall(0).resolves(mockData.simpleNftTokenData01) // Token Data call
|
||||
.onCall(1).resolves(mutableDataMock)
|
||||
|
||||
const offer = await uut.syncOfferMutableData('tokenId')
|
||||
|
||||
assert.isObject(offer)
|
||||
assert.isNumber(offer.lastUpdatedTokenData)
|
||||
})
|
||||
|
||||
it('should handle error getting token data', async () => {
|
||||
// create offer mock
|
||||
const offerMock = Object.assign({}, mockData.offerMockData)
|
||||
offerMock.save = () => { }
|
||||
|
||||
// Stub functions
|
||||
sandbox.stub(uut.OfferModel, 'findOne').returns(offerMock)
|
||||
sandbox.stub(uut.retryQueue, 'addToQueue')
|
||||
.onCall(0).throws(new Error('tokendata error')) // Token Data call
|
||||
.onCall(1).resolves(mockData.mutableDataMock)
|
||||
|
||||
const offer = await uut.syncOfferMutableData('tokenId')
|
||||
assert.isObject(offer)
|
||||
})
|
||||
|
||||
it('should handle error getting mutable data', async () => {
|
||||
// create offer mock
|
||||
const offerMock = Object.assign({}, mockData.offerMockData)
|
||||
offerMock.save = () => { }
|
||||
|
||||
// Stub functions
|
||||
sandbox.stub(uut.OfferModel, 'findOne').returns(offerMock)
|
||||
sandbox.stub(uut.retryQueue, 'addToQueue')
|
||||
.onCall(0).resolves(mockData.simpleNftTokenData01) // Token Data call
|
||||
.onCall(1).throws(new Error('mutable data error'))
|
||||
|
||||
const offer = await uut.syncOfferMutableData('tokenId')
|
||||
assert.isObject(offer)
|
||||
assert.isNumber(offer.lastUpdatedTokenData)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user