Compare commits

...
7 Commits
Author SHA1 Message Date
Chris Troutner ac451fd601 Merge pull request #71 from Permissionless-Software-Foundation/ct-unstable
Passing pagination paramter through to PSFFPP
2025-04-16 07:38:00 -07:00
Chris Troutner 6857fe0166 linting 2025-04-16 07:29:40 -07:00
Chris Troutner 89a03e561a feat(GET /pins/:page): Adding pagenation to endpoint 2025-04-16 07:29:27 -07:00
Chris Troutner 22e32a6fc1 Merge pull request #70 from Permissionless-Software-Foundation/ct-unstable
fix(psffpp): Updating to psffpp v1.2.0
2025-04-12 21:05:52 -07:00
Chris Troutner fcf5db7fa4 fix(psffpp): Updating to psffpp v1.2.0 2025-04-12 21:04:17 -07:00
Chris Troutner f8cf903fb9 Merge pull request #69 from Permissionless-Software-Foundation/dh-pin-claim-endpoint
feat(pin): Added pin-claim endpoint to ipfs-bch-wallet-consumer
2025-03-29 10:57:16 -07:00
Daniel Gonzalez c7986d1986 feat(pin): Added pin-claim endpoint to ipfs-bch-wallet-consumer 2025-03-27 11:01:30 -04:00
11 changed files with 695 additions and 25 deletions
+3 -3
View File
@@ -1,6 +1,6 @@
{
"password": "BJdugaoiq10ZJnA7Uskd",
"password": "YsFhsCVCqussWyyLYUjM",
"email": "system@system.com",
"id": "67c114fd4935ba05e48f6bb2",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY3YzExNGZkNDkzNWJhMDVlNDhmNmJiMiIsImlhdCI6MTc0MDcwNzA2OX0.R5nU8J5yhGorp7I5kc8_dRESbo4hwCIrTtqJiVYVhLA"
"id": "67ffbea61a8de3388f82c401",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY3ZmZiZWE2MWE4ZGUzMzg4ZjgyYzQwMSIsImlhdCI6MTc0NDgxMzczNH0.a4wM-CfGudF40yi4lwTsnd_aDZHrRn87zjHYxFZmeC4"
}
+8 -8
View File
@@ -53,7 +53,7 @@
"node-fetch": "npm:@achingbrain/node-fetch@2.6.7",
"nodemailer": "6.7.5",
"passport-local": "1.0.0",
"psffpp": "1.1.9",
"psffpp": "1.2.0",
"public-ip": "6.0.1",
"semver": "7.3.5",
"winston": "3.3.3",
@@ -19902,9 +19902,9 @@
"license": "MIT"
},
"node_modules/psf-multisig-approval": {
"version": "2.0.7",
"resolved": "http://94.130.170.209:4873/psf-multisig-approval/-/psf-multisig-approval-2.0.7.tgz",
"integrity": "sha512-Z00+ozZ3LqBL/5naimg9r15FfPZk01nzNe/YaKGNs4AE8szVK4PxwMTq1su3h4QFHWgDIBth+sbxgJCguy5qzw==",
"version": "2.1.0",
"resolved": "http://94.130.170.209:4873/psf-multisig-approval/-/psf-multisig-approval-2.1.0.tgz",
"integrity": "sha512-QLlOnYzeOx6OQXu5MGRlSP/IhS+X1Y7v3WxUse9riqYHOvvz3WeUPZUB8TVnSsUR6lH5W8Ckshl1lnaWXcPs7Q==",
"license": "MIT",
"dependencies": {
"@chris.troutner/bitcore-lib-cash": "8.25.26",
@@ -19923,13 +19923,13 @@
}
},
"node_modules/psffpp": {
"version": "1.1.9",
"resolved": "http://94.130.170.209:4873/psffpp/-/psffpp-1.1.9.tgz",
"integrity": "sha512-9DKlU+PbwcXLqoq18iOt8Wr6HDVAQqvrW71gNKmJ/1ZK7Tv0bgjxt6agRZAKuj0zDRUH7nPqvcqCs8OqEQ1biQ==",
"version": "1.2.0",
"resolved": "http://94.130.170.209:4873/psffpp/-/psffpp-1.2.0.tgz",
"integrity": "sha512-F3KDh6pzI5ZRxkek6KWFpwIcgx9tocnAKJie0HwNwTBuuS40/LsUnkmhVMghufF603Fp0vO6o3P4vgbTTzw6pg==",
"license": "MIT",
"dependencies": {
"axios": "1.3.5",
"psf-multisig-approval": "2.0.7"
"psf-multisig-approval": "2.1.0"
}
},
"node_modules/psffpp/node_modules/axios": {
+1 -1
View File
@@ -68,7 +68,7 @@
"node-fetch": "npm:@achingbrain/node-fetch@2.6.7",
"nodemailer": "6.7.5",
"passport-local": "1.0.0",
"psffpp": "1.1.9",
"psffpp": "1.2.0",
"public-ip": "6.0.1",
"semver": "7.3.5",
"winston": "3.3.3",
+57 -6
View File
@@ -48,6 +48,7 @@ class IpfsFilesAdapter {
this.getFileMetadata = this.getFileMetadata.bind(this)
this.getPins = this.getPins.bind(this)
this.waitForRPCResponse = this.waitForRPCResponse.bind(this)
this.pinClaim = this.pinClaim.bind(this)
}
// This handler is triggered when RPC data comes in over IPFS.
@@ -63,7 +64,7 @@ class IpfsFilesAdapter {
this.rpcDataQueue.push(data)
} catch (err) {
console.error('Error in files/rpcHandler(): ', err)
// Do not throw error. This is a top-level function.
// Do not throw error. This is a top-level function.
}
}
@@ -80,7 +81,7 @@ class IpfsFilesAdapter {
// Add names to the IPFS IDs for each provider.
const initialServiceProviders =
this.ipfs.ipfsCoordAdapter.state.ipfsFileProviders
this.ipfs.ipfsCoordAdapter.state.ipfsFileProviders
const serviceProviders = []
for (let i = 0; i < initialServiceProviders.length; i++) {
const thisProvider = initialServiceProviders[i]
@@ -142,7 +143,7 @@ class IpfsFilesAdapter {
// Throw an error if this IPFS node has not yet made a connection to a
// wallet service provider.
const selectedProvider =
this.ipfs.ipfsCoordAdapter.state.selectedIpfsFileProvider
this.ipfs.ipfsCoordAdapter.state.selectedIpfsFileProvider
if (!selectedProvider) {
throw new Error('No IPFS File Pin Service provider available yet.')
}
@@ -185,13 +186,16 @@ class IpfsFilesAdapter {
// Throw an error if this IPFS node has not yet made a connection to a
// wallet service provider.
const selectedProvider =
this.ipfs.ipfsCoordAdapter.state.selectedIpfsFileProvider
this.ipfs.ipfsCoordAdapter.state.selectedIpfsFileProvider
if (!selectedProvider) {
throw new Error('No IPFS File Pin Service provider available yet.')
}
const { page } = inObj
const rpcData = {
endpoint: 'getPins'
endpoint: 'getPins',
page
}
// Generate a UUID for the call.
@@ -261,7 +265,7 @@ class IpfsFilesAdapter {
cnt++
// Exit if data was returned, or the window for a response expires.
// Exit if data was returned, or the window for a response expires.
} while (!dataFound && cnt < 10)
// console.log(`dataFound: ${dataFound}, cnt: ${cnt}`)
@@ -271,6 +275,53 @@ class IpfsFilesAdapter {
throw err
}
}
async pinClaim (inObj = {}) {
try {
// Throw an error if this IPFS node has not yet made a connection to a
// ipfs service provider.
const selectedProvider =
this.ipfs.ipfsCoordAdapter.state.selectedIpfsFileProvider
if (!selectedProvider) {
throw new Error('No IPFS File Provider Service is available yet. Try again in a few seconds.')
}
const { proofOfBurnTxid, cid, claimTxid, filename, address } = inObj
const rpcData = {
endpoint: 'pinClaim',
proofOfBurnTxid,
cid,
claimTxid,
filename,
address
}
// Generate a UUID for the call.
const rpcId = this.uid()
// Generate a JSON RPC command.
const cmd = this.jsonrpc.request(rpcId, 'file-pin', rpcData)
const cmdStr = JSON.stringify(cmd)
// console.log('cmdStr: ', cmdStr)
// Send the RPC command to selected wallet service.
const thisNode = this.ipfs.ipfsCoordAdapter.ipfsCoord.thisNode
await this.ipfs.ipfsCoordAdapter.ipfsCoord.useCases.peer.sendPrivateMessage(
selectedProvider,
cmdStr,
thisNode
)
// Wait for data to come back from the wallet service.
const data = await this.waitForRPCResponse(rpcId)
return data
} catch (err) {
console.error('Error in pinClaim()')
throw err
}
}
}
// module.exports = P2wdbAdapter
+26 -2
View File
@@ -41,6 +41,7 @@ class IpfsRESTControllerLib {
this.getPins = this.getPins.bind(this)
this.cid2json = this.cid2json.bind(this)
this.downloadFile = this.downloadFile.bind(this)
this.pinClaim = this.pinClaim.bind(this)
}
/**
@@ -261,11 +262,11 @@ class IpfsRESTControllerLib {
*/
async getPins (ctx) {
try {
// const { cid } = ctx.params
const { page } = ctx.params
const ipfsFiles = this.adapters.ipfsFiles
const pinData = await ipfsFiles.getPins()
const pinData = await ipfsFiles.getPins({ page })
console.log('getPins() pinData: ', pinData)
ctx.body = pinData
@@ -300,6 +301,29 @@ class IpfsRESTControllerLib {
}
}
/**
* @api {post} /ipfs/pin-claim Process pin claim.
* @apiPermission public
* @apiName PinClaim
* @apiGroup REST IPFS
* @apiDescription Process pin claim.
*
* @apiExample Example usage:
* curl -H "Content-Type: application/json" -X POST -d '{ "proofOfBurnTxid": "be4b63156c93f58ed311d403d9f756deda9abbc81d0fef8fbe5d769538b4261c", "cid": "bafybeied3zdwdiro7fqytyha2yfband4lwcrtozmf6shynylt3kexh26dq", "claimTxid": "c71e2f2cdf8658d90c61ac6183b8ffeeb359779807b317386044705d8352f0f2", "filename": "mutable-67ccefcca67097473e78ca10.json", "address": "bitcoincash:qqs2wrahl6azn9qdyrmp9ygeejqvzr8ruv7e9m30fr" }' http://localhost:5001/ipfs/pin-claim *
*/
async pinClaim (ctx) {
try {
const body = ctx.request.body
const result = await this.useCases.ipfs.pinClaim(body)
ctx.body = result
} catch (err) {
console.log('Error in ipfs/controller.js/cid2json(): ', err.message)
this.handleError(ctx, err)
}
}
// DRY error handler
handleError (ctx, err) {
// console.log('handleError() err.status: ', err.status)
+2 -2
View File
@@ -60,9 +60,9 @@ class IpfsRouter {
this.router.get('/download/:cid', this.ipfsRESTController.downloadFile)
this.router.get('/service', this.ipfsRESTController.getService)
this.router.get('/file-info/:cid', this.ipfsRESTController.getFileInfo)
this.router.get('/pins', this.ipfsRESTController.getPins)
this.router.get('/pins/:page', this.ipfsRESTController.getPins)
this.router.get('/cid2json/:cid', this.ipfsRESTController.cid2json)
this.router.post('/pin-claim', this.ipfsRESTController.pinClaim)
// Attach the Controller routes to the Koa app.
app.use(this.router.routes())
app.use(this.router.allowedMethods())
+17
View File
@@ -37,6 +37,7 @@ class IpfsUseCases {
this.getWritePrice = this.getWritePrice.bind(this)
this.getCidMetadata = this.getCidMetadata.bind(this)
this.cid2json = this.cid2json.bind(this)
this.pinClaim = this.pinClaim.bind(this)
// State
this.lastWritePriceUpdate = null // Used to periodically update write price.
@@ -225,9 +226,11 @@ class IpfsUseCases {
// Throw an error if this is not a JSON file
const filename = fileMetadata.filename
if (!filename.endsWith('.json')) {
throw new Error(`CID ${cid} does not resolve to a JSON file.`)
}
// Retrieve the CID content and store it in a Buffer.
const helia = this.adapters.ipfs.ipfs
const fileChunks = []
@@ -281,6 +284,20 @@ class IpfsUseCases {
}
}
async pinClaim (inObj = {}) {
try {
// Throw an error if ipfs-bch-wallet-consumer can not communicate with the ipfs-file-pin-service.
const metadata = await this.adapters.ipfsFiles.pinClaim(inObj)
return {
success: metadata.success,
message: metadata.message
}
} catch (err) {
console.error('Error in ipfs-use-cases.js/pinClaim(): ', err)
throw err
}
}
// async downloadCid2 (inObj = {}) {
// try {
// const { cid } = inObj
@@ -248,4 +248,247 @@ describe('#IPFS REST API', () => {
assert.property(ctx.body, 'thisNode')
})
})
describe('#viewFile', () => {
it('should return 422 status on biz logic error', async () => {
try {
// Force an error
ctx.params = {
cid: 'bafybeib2rphswe7clvclverw7xi7nejkpkh4mdfcurdmcgw7wcup7za6wy'
}
sandbox.stub(uut.useCases.ipfs, 'downloadCid').throws(new Error('test error'))
uut.adapters.ipfs.ipfsCoordAdapter = {}
ctx.request.body = {}
await uut.viewFile(ctx)
assert.fail('Unexpected result')
} catch (err) {
// console.log('err: ', err)
assert.equal(err.status, 422)
assert.include(err.message, 'test error')
}
})
it('should return 200 status on success', async () => {
ctx.params = {
cid: 'bafybeib2rphswe7clvclverw7xi7nejkpkh4mdfcurdmcgw7wcup7za6wy'
}
// Mock dependencies
sandbox.stub(uut.useCases.ipfs, 'downloadCid').resolves({ success: true, readStream: 'readStream' })
uut.adapters.ipfs.ipfsCoordAdapter = {
ipfsCoord: {
thisNode: {}
}
}
ctx.request.body = {}
await uut.viewFile(ctx)
assert.exists(ctx.body)
})
})
describe('#downloadFile', () => {
it('should return 422 status on biz logic error', async () => {
try {
ctx.params = {
cid: 'bafybeib2rphswe7clvclverw7xi7nejkpkh4mdfcurdmcgw7wcup7za6wy'
}
// Force an error
sandbox.stub(uut.useCases.ipfs, 'downloadCid').throws(new Error('test error'))
uut.adapters.ipfs.ipfsCoordAdapter = {}
ctx.request.body = {}
await uut.downloadFile(ctx)
assert.fail('Unexpected result')
} catch (err) {
// console.log('err: ', err)
assert.equal(err.status, 422)
assert.include(err.message, 'test error')
}
})
it('should return 200 status on success', async () => {
ctx.params = {
cid: 'bafybeib2rphswe7clvclverw7xi7nejkpkh4mdfcurdmcgw7wcup7za6wy'
}
// Mock dependencies
sandbox.stub(uut.useCases.ipfs, 'downloadCid').resolves({ success: true, readStream: 'readStream' })
uut.adapters.ipfs.ipfsCoordAdapter = {
ipfsCoord: {
thisNode: {}
}
}
ctx.request.body = {}
await uut.downloadFile(ctx)
assert.exists(ctx.body)
})
})
describe('#getService', () => {
it('should return 422 status on biz logic error', async () => {
try {
// Force an error
uut.adapters.ipfs.ipfsCoordAdapter = {}
ctx.request.body = {}
await uut.getService(ctx)
assert.fail('Unexpected result')
} catch (err) {
// console.log('err: ', err)
assert.equal(err.status, 422)
assert.include(err.message, 'Cannot read properties of undefined')
}
})
it('should return 200 status on success', async () => {
// Mock dependencies
uut.adapters.ipfs.ipfsCoordAdapter = {
state: {
selectedIpfsFileProvider: 'provider'
}
}
ctx.request.body = {}
await uut.getService(ctx)
assert.isTrue(ctx.body.success)
})
})
describe('#getFileInfo', () => {
it('should return 422 status on biz logic error', async () => {
try {
ctx.params = {
cid: 'bafybeib2rphswe7clvclverw7xi7nejkpkh4mdfcurdmcgw7wcup7za6wy'
}
sandbox.stub(uut.adapters.ipfsFiles, 'getFileMetadata').throws(new Error('test error'))
ctx.request.body = {}
await uut.getFileInfo(ctx)
assert.fail('Unexpected result')
} catch (err) {
// console.log('err: ', err)
assert.equal(err.status, 422)
assert.include(err.message, 'test error')
}
})
it('should return 200 status on success', async () => {
ctx.params = {
cid: 'bafybeib2rphswe7clvclverw7xi7nejkpkh4mdfcurdmcgw7wcup7za6wy'
}
// Mock dependencies
sandbox.stub(uut.adapters.ipfsFiles, 'getFileMetadata').resolves({ success: true, filename: 'filename' })
ctx.request.body = {}
await uut.getFileInfo(ctx)
assert.isTrue(ctx.body.success)
})
})
describe('#getPins', () => {
it('should return 422 status on biz logic error', async () => {
try {
sandbox.stub(uut.adapters.ipfsFiles, 'getPins').throws(new Error('test error'))
ctx.request.body = {}
ctx.params = { page: 1 }
await uut.getPins(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(err.status, 422)
assert.include(err.message, 'test error')
}
})
it('should return 200 status on success', async () => {
// Mock dependencies
sandbox.stub(uut.adapters.ipfsFiles, 'getPins').resolves({ success: true })
ctx.request.body = {}
ctx.params = { page: 1 }
await uut.getPins(ctx)
assert.isTrue(ctx.body.success)
})
})
describe('#cid2json', () => {
it('should return 422 status on biz logic error', async () => {
try {
ctx.params = {
cid: 'bafybeib2rphswe7clvclverw7xi7nejkpkh4mdfcurdmcgw7wcup7za6wy'
}
sandbox.stub(uut.useCases.ipfs, 'cid2json').throws(new Error('test error'))
ctx.request.body = {}
await uut.cid2json(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(err.status, 422)
assert.include(err.message, 'test error')
}
})
it('should return 200 status on success', async () => {
ctx.params = {
cid: 'bafybeib2rphswe7clvclverw7xi7nejkpkh4mdfcurdmcgw7wcup7za6wy'
}
// Mock dependencies
sandbox.stub(uut.useCases.ipfs, 'cid2json').resolves({ success: true })
ctx.request.body = {}
await uut.cid2json(ctx)
assert.isTrue(ctx.body.success)
})
})
describe('#pinClaim', () => {
it('should return 422 status on biz logic error', async () => {
try {
sandbox.stub(uut.useCases.ipfs, 'pinClaim').throws(new Error('test error'))
ctx.request.body = {}
await uut.pinClaim(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(err.status, 422)
assert.include(err.message, 'test error')
}
})
it('should return 200 status on success', async () => {
// Mock dependencies
sandbox.stub(uut.useCases.ipfs, 'pinClaim').resolves({ success: true })
ctx.request.body = {}
await uut.pinClaim(ctx)
assert.isTrue(ctx.body.success)
})
})
})
+45 -3
View File
@@ -1,10 +1,26 @@
/*
Mocks for the Adapter library.
*/
class BlockstoreMock {
constructor () {
this.get = async () => {}
}
}
class FsMock {
constructor () {
this.ls = async function* () {
yield { path: 'test.txt', cid: 'QmS4ghgMgfFvqPjB4WKXHaN15ZyT4K4JYZxY5X5x5x5x5' }
}
this.cat = async function* () {
yield Buffer.from('test data')
}
}
}
class IpfsAdapter {
constructor () {
this.ipfs = {
blockstore: new BlockstoreMock(),
fs: new FsMock(),
files: {
stat: () => {}
}
@@ -34,7 +50,9 @@ class IpfsCoordAdapter {
this.peerInputHandler = () => {}
this.state = {
serviceProviders: []
serviceProviders: [],
serviceProvidersByType: '',
selectedIpfsFileProvider:''
}
}
}
@@ -138,5 +156,29 @@ class BchUseCaseMock {
}
}
const bch = new BchUseCaseMock()
const wallet = {
bchWallet: bch
export default { ipfs, localdb, bch }
}
const ipfsFiles = {
pinClaim: () => {
return {
success: true,
message: 'Pin claimed'
}
},
getFileMetadata: () => {
return {
success: true,
message: 'Files metadata'
}
},
getPins: () => {
return {
success: true,
message: 'Pins'
}
}
}
export default { ipfs, localdb, bch, wallet, ipfsFiles }
+23
View File
@@ -67,6 +67,28 @@ class UsageUseCaseMock {
}
}
class IpfsUseCaseMock {
async downloadCid() {
return {}
}
async cid2json() {
return {}
}
async getCidMetadata() {
return {}
}
async getService() {
return {}
}
async pinClaim() {
return {}
}
}
class UseCasesMock {
constuctor(localConfig = {}) {
// this.user = new UserUseCaseMock(localConfig)
@@ -77,6 +99,7 @@ class UseCasesMock {
user = new UserUseCaseMock()
bch = new BchUseCaseMock()
usage = new UsageUseCaseMock()
ipfs = new IpfsUseCaseMock()
}
export default UseCasesMock;
+270
View File
@@ -0,0 +1,270 @@
/*
Unit tests for the ipfs-use-cases.js file.
*/
// Public npm libraries
import { assert } from 'chai'
import sinon from 'sinon'
// Local support libraries
// const testUtils = require('../../utils/test-utils')
// Unit under test (uut)
import UseCases from '../../../src/use-cases/ipfs-use-cases.js'
import adapters from '../mocks/adapters/index.js'
describe('#ipfs-use-cases', () => {
let uut
let sandbox
before(async () => {
// Delete all previous users in the database.
// await testUtils.deleteAllUsers()
})
beforeEach(() => {
sandbox = sinon.createSandbox()
uut = new UseCases({ adapters })
uut.adapters.ipfs.ipfsCoordAdapter = {
state: {
selectedIpfsFileProvider: 'provider'
}
}
})
afterEach(() => sandbox.restore())
describe('#constructor', () => {
it('should throw an error if adapters are not passed in', () => {
try {
uut = new UseCases()
assert.fail('Unexpected code path')
// This is here to prevent the linter from complaining.
assert.isOk(uut)
} catch (err) {
assert.include(
err.message,
'Instance of adapters must be passed in when instantiating IPFS Use Cases library.'
)
}
})
})
describe('#downloadCid', () => {
it('should download a file from IPFS', async () => {
sandbox.stub(uut, 'getCidMetadata').resolves('file.txt')
const result = await uut.downloadCid({ cid: 'bafybeib2rphswe7clvclverw7xi7nejkpkh4mdfcurdmcgw7wcup7za6wy' })
assert.property(result, 'filename')
assert.property(result, 'readStream')
})
it('should handle directory-based CIDs', async () => {
sandbox.stub(uut, 'getCidMetadata').resolves('file.txt')
sandbox.stub(uut.adapters.ipfs.ipfs.fs, 'ls').callsFake(async function * () {
yield { path: 'dir1/test.txt', cid: 'QmS4ghgMgfFvqPjB4WKXHaN15ZyT4K4JYZxY5X5x5x5x5' }
})
const result = await uut.downloadCid({ cid: 'bafybeib2rphswe7clvclverw7xi7nejkpkh4mdfcurdmcgw7wcup7za6wy' })
assert.property(result, 'filename')
assert.property(result, 'readStream')
})
it('should throw an error if cid is not provided ', async () => {
try {
await uut.downloadCid({})
} catch (error) {
assert.include(error.message, 'CID is undefined')
}
})
})
describe('#getWritePrice', () => {
it('should get price', async () => {
const result = await uut.getWritePrice()
assert.isNumber(result)
})
it('should handle undefined psffpp instance', async () => {
uut.psffpp = null
const result = await uut.getWritePrice()
assert.isNumber(result)
})
it('should handle error ', async () => {
try {
uut.psffpp = {
getMcWritePrice: () => {
throw new Error('test error')
}
}
await uut.getWritePrice()
} catch (error) {
assert.include(error.message, 'test error')
}
})
})
describe('#getCidMetadata', () => {
it('should get cid metadata', async () => {
sandbox.stub(uut.adapters.ipfsFiles, 'getFileMetadata').resolves({ success: true, fileMetadata: { dataPinned: true, filename: 'test.json' } })
const result = await uut.getCidMetadata({ cid: 'bafybeib2rphswe7clvclverw7xi7nejkpkh4mdfcurdmcgw7wcup7za6wy' })
assert.isString(result)
assert.equal(result, 'test.json')
})
it('should handle error ', async () => {
try {
sandbox.stub(uut.adapters.ipfsFiles, 'getFileMetadata').throws(new Error('test error'))
await uut.getCidMetadata({ cid: 'bafybeib2rphswe7clvclverw7xi7nejkpkh4mdfcurdmcgw7wcup7za6wy' })
} catch (error) {
assert.include(error.message, 'test error')
}
})
it('should handle non-success response from getFileMetadata', async () => {
try {
sandbox.stub(uut.adapters.ipfsFiles, 'getFileMetadata').resolves({ success: false, message: 'test error' })
await uut.getCidMetadata({ cid: 'bafybeib2rphswe7clvclverw7xi7nejkpkh4mdfcurdmcgw7wcup7za6wy' })
} catch (error) {
assert.include(error.message, 'test error')
}
})
})
describe('#cid2json', () => {
it('should get cid2json', async () => {
uut.adapters.ipfs.ipfsCoordAdapter = {
state: {
selectedIpfsFileProvider: 'provider'
}
}
sandbox.stub(uut.adapters.ipfsFiles, 'getFileMetadata').resolves({ fileMetadata: { dataPinned: true, filename: 'test.json' } })
sandbox.stub(uut.adapters.ipfs.ipfs.fs, 'ls').callsFake(function * () {
yield { name: 'test.json' }
})
sandbox.stub(JSON, 'parse').returns(true)
const result = await uut.cid2json({ cid: 'bafybeib2rphswe7clvclverw7xi7nejkpkh4mdfcurdmcgw7wcup7za6wy' })
assert.isObject(result)
assert.property(result, 'success')
assert.property(result, 'json')
})
it('should throw error if selectedIpfsFileProvider is not defined', async () => {
try {
uut.adapters.ipfs.ipfsCoordAdapter = {
state: {
selectedIpfsFileProvider: null
}
}
await uut.cid2json({ cid: 'bafybeib2rphswe7clvclverw7xi7nejkpkh4mdfcurdmcgw7wcup7za6wy' })
assert.fail('Unexpected code path')
} catch (error) {
assert.include(error.message, 'No IPFS File Provider Service is available yet. Try again in a few seconds')
}
})
it('should throw error if file metadata cant be fetched', async () => {
try {
sandbox.stub(uut.adapters.ipfsFiles, 'getFileMetadata').resolves({ success: false })
await uut.cid2json({ cid: 'bafybeib2rphswe7clvclverw7xi7nejkpkh4mdfcurdmcgw7wcup7za6wy' })
assert.fail('Unexpected code path')
} catch (error) {
assert.include(error.message, 'Could not communicate with instance of ipfs-file-pin-service')
}
})
it('should throw error if cid is not pinned', async () => {
try {
sandbox.stub(uut.adapters.ipfsFiles, 'getFileMetadata').resolves({ success: true, fileMetadata: { dataPinned: false } })
await uut.cid2json({ cid: 'bafybeib2rphswe7clvclverw7xi7nejkpkh4mdfcurdmcgw7wcup7za6wy' })
assert.fail('Unexpected code path')
} catch (error) {
assert.include(error.message, 'has not been pinned by ipfs-file-pin-service instance')
}
})
it('should throw error if filename has no .json extension', async () => {
try {
sandbox.stub(uut.adapters.ipfsFiles, 'getFileMetadata').resolves({ success: true, fileMetadata: { dataPinned: true, filename: 'test' } })
await uut.cid2json({ cid: 'bafybeib2rphswe7clvclverw7xi7nejkpkh4mdfcurdmcgw7wcup7za6wy' })
assert.fail('Unexpected code path')
} catch (error) {
assert.include(error.message, 'does not resolve to a JSON file')
}
})
it('should handle error if file cant be retrieved', async () => {
try {
sandbox.stub(uut.adapters.ipfsFiles, 'getFileMetadata').resolves({ fileMetadata: { dataPinned: true, filename: 'test.json' } })
sandbox.stub(uut.adapters.ipfs.ipfs.fs, 'cat').throws(new Error('test error'))
sandbox.stub(uut.adapters.ipfs.ipfs.fs, 'ls').callsFake(function * () {
yield { name: 'test.json' }
})
await uut.cid2json({ cid: 'bafybeib2rphswe7clvclverw7xi7nejkpkh4mdfcurdmcgw7wcup7za6wy' })
assert.fail('Unexpected code path')
} catch (error) {
assert.include(error.message, 'test error')
}
})
it('should handle error if cid is a directory', async () => {
try {
sandbox.stub(uut.adapters.ipfsFiles, 'getFileMetadata').resolves({ fileMetadata: { dataPinned: true, filename: 'test.json' } })
sandbox.stub(uut.adapters.ipfs.ipfs.fs, 'cat')
.onCall(0).throws(new Error('test error'))
.onCall(1).callsFake(function * () {
yield { name: 'test.json' }
})
sandbox.stub(uut.adapters.ipfs.ipfs.fs, 'ls').callsFake(function * () {
yield { name: 'test/directory/' }
})
await uut.cid2json({ cid: 'bafybeib2rphswe7clvclverw7xi7nejkpkh4mdfcurdmcgw7wcup7za6wy' })
} catch (error) {
assert.include(error.message, 'file does not resolve to a valid JSON file')
}
})
it('should get content from cid/name.json format', async () => {
sandbox.stub(uut.adapters.ipfsFiles, 'getFileMetadata').resolves({ fileMetadata: { dataPinned: true, filename: 'test.json' } })
sandbox.stub(uut.adapters.ipfs.ipfs.fs, 'cat')
.onCall(0).throws(new Error('test error'))
.onCall(1).callsFake(function * () {
yield Buffer.from('{"test": "test"}')
})
sandbox.stub(uut.adapters.ipfs.ipfs.fs, 'ls').callsFake(function * () {
yield { name: 'test/directory/test.json' }
})
const result = await uut.cid2json({ cid: 'bafybeib2rphswe7clvclverw7xi7nejkpkh4mdfcurdmcgw7wcup7za6wy' })
assert.isObject(result)
assert.property(result, 'success')
assert.property(result, 'json')
})
it('should handle if content cant be parsed', async () => {
try {
sandbox.stub(uut.adapters.ipfsFiles, 'getFileMetadata').resolves({ fileMetadata: { dataPinned: true, filename: 'test.json' } })
sandbox.stub(uut.adapters.ipfs.ipfs.fs, 'ls').callsFake(function * () {
yield { name: 'test.json' }
})
sandbox.stub(JSON, 'parse').throws(new Error())
await uut.cid2json({ cid: 'bafybeib2rphswe7clvclverw7xi7nejkpkh4mdfcurdmcgw7wcup7za6wy' })
} catch (error) {
assert.include(error.message, 'not resolve to a valid JSON object')
}
})
})
describe('#pinClaim', () => {
it('should process pinClaim', async () => {
const result = await uut.pinClaim()
assert.isObject(result)
assert.property(result, 'success')
assert.property(result, 'message')
})
it('should handle error ', async () => {
try {
sandbox.stub(uut.adapters.ipfsFiles, 'pinClaim').throws(new Error('test error'))
await uut.pinClaim()
} catch (error) {
assert.include(error.message, 'test error')
}
})
})
})