Syncing with upstream ipfs-service-provider

This commit is contained in:
Chris Troutner
2025-01-26 19:48:38 -07:00
11 changed files with 678 additions and 10 deletions
@@ -0,0 +1,66 @@
/*
Unit tests for the REST API middleware that handle response errors.
*/
// Public npm libraries
import { assert } from 'chai'
import sinon from 'sinon'
// Local libraries
import errorMiddleware from '../../../../../src/controllers/rest-api/middleware/error.js'
import { context as mockContext } from '../../../../unit/mocks/ctx-mock.js'
describe('#Validators', () => {
let ctx
let sandbox
beforeEach(() => {
// Mock the context object.
ctx = mockContext()
sandbox = sinon.createSandbox()
})
afterEach(() => sandbox.restore())
describe('#errorMiddleware', () => {
it('should run next function', async () => {
// Spy on next
const next = sinon.spy(() => { })
errorMiddleware()(ctx, next)
assert.isTrue(next.calledOnce)
})
it('should handle unknown status error', async () => {
try {
const next = async () => {
const e = new Error('test error')
e.status = null
throw e
}
await errorMiddleware()(ctx, next)
assert.fail('Unexpected code path')
} catch (error) {
assert.equal(ctx.status, 500)
assert.equal(ctx.body, 'test error')
}
})
it('should handle known status error', async () => {
try {
const next = async () => {
const e = new Error('test error')
e.status = 422
throw e
}
await errorMiddleware()(ctx, next)
assert.fail('Unexpected code path')
} catch (error) {
assert.equal(ctx.status, 422)
assert.equal(ctx.body, 'test error')
}
})
})
})
@@ -0,0 +1,163 @@
/*
Unit tests for the REST API handler for the /usage endpoints.
*/
// Public npm libraries
import { assert } from 'chai'
import sinon from 'sinon'
// Local support libraries
import adapters from '../../../mocks/adapters/index.js'
import UseCasesMock from '../../../mocks/use-cases/index.js'
import UsageController from '../../../../../src/controllers/rest-api/usage/controller.js'
import { context as mockContext } from '../../../mocks/ctx-mock.js'
let uut
let sandbox
let ctx
describe('#Usage-REST-Controller', () => {
// const testUser = {}
beforeEach(() => {
const useCases = new UseCasesMock()
uut = new UsageController({ adapters, useCases })
sandbox = sinon.createSandbox()
// Mock the context object.
ctx = mockContext()
})
afterEach(() => sandbox.restore())
describe('#constructor', () => {
it('should throw an error if adapters are not passed in', () => {
try {
uut = new UsageController()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Adapters library required when instantiating /usage REST Controller.'
)
}
})
it('should throw an error if useCases are not passed in', () => {
try {
uut = new UsageController({ adapters })
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Use Cases library required when instantiating /usage REST Controller.'
)
}
})
})
describe('#Get /usage', () => {
it('should return 422 status on biz logic error', async () => {
try {
sandbox.stub(uut.useCases.usage, 'getRestSummary').throws(new Error('test error'))
await uut.getStatus(ctx)
assert.fail('Unexpected result')
} catch (err) {
// console.log(err)
assert.equal(err.status, 422)
assert.include(err.message, 'test error')
}
})
it('should return 200 status on success', async () => {
await uut.getStatus(ctx)
// Assert the expected HTTP response
assert.equal(ctx.status, 200)
// Assert that expected properties exist in the returned data.
assert.property(ctx.response.body, 'status')
})
})
describe('#Get /ips', () => {
it('should return 422 status on biz logic error', async () => {
try {
sandbox.stub(uut.useCases.usage, 'getTopIps').throws(new Error('test error'))
await uut.getTopIps(ctx)
assert.fail('Unexpected result')
} catch (err) {
// console.log(err)
assert.equal(err.status, 422)
assert.include(err.message, 'test error')
}
})
it('should return 200 status on success', async () => {
await uut.getTopIps(ctx)
// Assert the expected HTTP response
assert.equal(ctx.status, 200)
// Assert that expected properties exist in the returned data.
assert.property(ctx.response.body, 'ips')
})
})
describe('#Get /endpoints', () => {
it('should return 422 status on biz logic error', async () => {
try {
sandbox.stub(uut.useCases.usage, 'getTopEndpoints').throws(new Error('test error'))
await uut.getTopEndpoints(ctx)
assert.fail('Unexpected result')
} catch (err) {
// console.log(err)
assert.equal(err.status, 422)
assert.include(err.message, 'test error')
}
})
it('should return 200 status on success', async () => {
await uut.getTopEndpoints(ctx)
// Assert the expected HTTP response
assert.equal(ctx.status, 200)
// Assert that expected properties exist in the returned data.
assert.property(ctx.response.body, 'endpoints')
})
})
describe('#handleError', () => {
it('should pass an error message', () => {
try {
const err = {
status: 422,
message: 'Unprocessable Entity'
}
uut.handleError(ctx, err)
} catch (err) {
assert.include(err.message, 'Unprocessable Entity')
}
})
it('should still throw error if there is no message', () => {
try {
const err = {
status: 404
}
uut.handleError(ctx, err)
} catch (err) {
assert.include(err.message, 'Not Found')
}
})
})
})
@@ -0,0 +1,75 @@
/*
Unit tests for the REST API handler for the /usage endpoints.
*/
// Public npm libraries
import { assert } from 'chai'
import sinon from 'sinon'
// Local support libraries
import adapters from '../../../mocks/adapters/index.js'
import UseCasesMock from '../../../mocks/use-cases/index.js'
import UsageRouter from '../../../../../src/controllers/rest-api/usage/index.js'
let uut
let sandbox
// let ctx
// const mockContext = require('../../../../unit/mocks/ctx-mock').context
describe('#Usage-REST-Router', () => {
beforeEach(() => {
const useCases = new UseCasesMock()
uut = new UsageRouter({ adapters, useCases })
sandbox = sinon.createSandbox()
})
afterEach(() => sandbox.restore())
describe('#constructor', () => {
it('should throw an error if adapters are not passed in', () => {
try {
uut = new UsageRouter()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Adapters library required when instantiating IPFS REST Controller.'
)
}
})
it('should throw an error if useCases are not passed in', () => {
try {
uut = new UsageRouter({ adapters })
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Use Cases library required when instantiating IPFS REST Controller.'
)
}
})
})
describe('#attach', () => {
it('should throw an error if app is not passed in.', () => {
try {
uut.attach()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Must pass app object when attaching REST API controllers.'
)
}
})
})
})
@@ -79,4 +79,19 @@ describe('#Timer-Controllers', () => {
assert.equal(result, false)
})
})
describe('#cleanUsage', () => {
it('should kick off the Use Case', async () => {
const result = await uut.cleanUsage()
assert.equal(result, true)
})
it('should return false on error', async () => {
sandbox.stub(uut.useCases.usage, 'cleanUsage').throws(new Error('test error'))
const result = await uut.cleanUsage()
assert.equal(result, false)
})
})
})
+19
View File
@@ -49,6 +49,24 @@ class BchUseCaseMock {
}
}
class UsageUseCaseMock {
async cleanUsage() {
return {}
}
async getRestSummary() {
return true
}
async getTopIps(params) {
return true
}
async getTopEndpoints(existingUser, newData) {
return true
}
}
class UseCasesMock {
constuctor(localConfig = {}) {
// this.user = new UserUseCaseMock(localConfig)
@@ -58,6 +76,7 @@ class UseCasesMock {
user = new UserUseCaseMock()
bch = new BchUseCaseMock()
usage = new UsageUseCaseMock()
}
export default UseCasesMock;
+255
View File
@@ -0,0 +1,255 @@
/*
Unit tests for the use-cases/usage-use-cases.js business logic library.
*/
// Public npm libraries
import { assert } from 'chai'
import sinon from 'sinon'
// Local support libraries
import adapters from '../mocks/adapters/index.js'
// Mock
import { context as mockContext } from '../mocks/ctx-mock.js'
// Unit under test (uut)
import { UsageUseCases, restCalls, usageMiddleware } from '../../../src/use-cases/usage-use-cases.js'
describe('#usage-use-case', () => {
let uut
let sandbox
let ctx
before(async () => {
})
beforeEach(() => {
sandbox = sinon.createSandbox()
uut = new UsageUseCases({ adapters })
// Set as empty array
restCalls.splice(0, restCalls.length)
ctx = mockContext()
})
afterEach(() => sandbox.restore())
describe('#constructor', () => {
it('should throw an error if adapters are not passed in', () => {
try {
uut = new UsageUseCases()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of adapters must be passed in when instantiating Usage Use Cases library.'
)
}
})
})
describe('#cleanUsage', () => {
it('should delete older data than 24 hours', () => {
const now = new Date() // Mock date
// set older mock data
restCalls.push({
timestamp: now.getTime() - (60000 * 60 * 24),
ip: '127.0.0.1'
})
// Set recently mock data
restCalls.push({
timestamp: now.getTime(),
ip: 'localhost'
})
const result = uut.cleanUsage()
assert.isArray(result)
assert.equal(result.length, 1)
assert.equal(result[0].ip, 'localhost')
})
it('should handle error', () => {
try {
// Force an error
sandbox.stub(restCalls, 'filter').throws(new Error('uut error'))
uut.cleanUsage()
assert.fail('Unexpected code path')
} catch (error) {
assert.equal(error.message, 'uut error')
}
})
})
describe('#getRestSummary', () => {
it('should get the number of rest calls', () => {
// Set mock data
restCalls.push({
ip: 'localhost'
})
const result = uut.getRestSummary()
assert.isNumber(result)
assert.equal(result, 1)
})
it('should handle error', () => {
try {
// Force an error
sandbox.stub(console, 'log').throws(new Error('uut error'))
uut.getRestSummary()
assert.fail('Unexpected code path')
} catch (error) {
assert.equal(error.message, 'uut error')
}
})
})
describe('#getTopIps', () => {
it('should get top IPs', () => {
// Set mock data
restCalls.push({
ip: 'localhost'
})
// Set mock data
restCalls.push({
ip: 'localhost'
})
const result = uut.getTopIps()
assert.isArray(result)
assert.property(result[0], 'ip')
assert.property(result[0], 'cnt')
assert.equal(result[0].ip, 'localhost')
assert.equal(result[0].cnt, '2')
})
it('should return a maximum of 20 values', () => {
// Fill Array with 21 values
for (let i = 0; i < 21; i++) {
restCalls.push({
ip: `localhost-${i}`
})
}
const result = uut.getTopIps()
assert.isArray(result)
assert.property(result[0], 'ip')
assert.property(result[0], 'cnt')
assert.equal(result.length, 20)
})
it('should handle error', () => {
try {
// Set mock data
restCalls.push(null)
uut.getTopIps()
assert.fail('Unexpected code path')
} catch (error) {
assert.include(error.message, 'Cannot read properties')
}
})
})
describe('#getTopEndpoints', () => {
it('should get top Endpoints', () => {
// Set mock data
restCalls.push({
ip: 'localhost',
url: '/api/v1/users',
method: 'GET'
})
// Set mock data
restCalls.push({
ip: 'localhost',
url: '/api/v1/users',
method: 'GET'
})
const result = uut.getTopEndpoints()
assert.isArray(result)
assert.property(result[0], 'endpoint')
assert.property(result[0], 'cnt')
assert.equal(result[0].endpoint, 'GET /api/v1/users')
assert.equal(result[0].cnt, '2')
})
it('should return a maximum of 20 values', () => {
// Fill Array with 21 values
for (let i = 0; i < 21; i++) {
restCalls.push({
ip: 'localhost',
url: `/api/v1/users-${i}`,
method: 'GET'
})
}
const result = uut.getTopEndpoints()
assert.isArray(result)
assert.property(result[0], 'endpoint')
assert.property(result[0], 'cnt')
assert.equal(result.length, 20)
})
it('should handle error', () => {
try {
// Set mock data
restCalls.push(null)
uut.getTopEndpoints()
assert.fail('Unexpected code path')
} catch (error) {
assert.include(error.message, 'Cannot read properties')
}
})
})
describe('#usageMiddleware', () => {
it('should update restCalls state', async () => {
// Spy on next
const next = sinon.spy(() => { })
await usageMiddleware()(ctx, next)
assert.equal(restCalls.length, 1)
assert.isTrue(next.called)
})
it('should handle error', async () => {
try {
const next = () => { throw new Error('uut error') }
await usageMiddleware()(ctx, next)
assert.fail('Unexpected code path')
} catch (error) {
assert.equal(error.message, 'uut error')
assert.equal(ctx.status, 500)
assert.equal(restCalls.length, 0)
}
})
})
})