forked from ipfs-service-provider

This commit is contained in:
Chris Troutner
2021-11-23 08:38:01 -08:00
commit 8f7f3896a3
115 changed files with 58101 additions and 0 deletions
+38
View File
@@ -0,0 +1,38 @@
/*
Unit tests for controllers index.js file.
*/
// Public npm libraries
// const assert = require('chai').assert
const sinon = require('sinon')
const Controllers = require('../../../src/controllers')
describe('#Controllers', () => {
let uut
let sandbox
beforeEach(() => {
sandbox = sinon.createSandbox()
uut = new Controllers()
})
afterEach(() => sandbox.restore())
describe('#attachControllers', () => {
it('should attach the controllers', async () => {
// mock IPFS
sandbox.stub(uut.adapters, 'start').resolves({})
uut.adapters.ipfs.ipfsCoordAdapter = {
attachRPCRouter: () => {}
}
const app = {
use: () => {}
}
await uut.attachControllers(app)
})
})
})
@@ -0,0 +1,298 @@
/*
Unit tests for the rpc/index.js library.
*/
// Public npm libraries
const assert = require('chai').assert
const jsonrpc = require('jsonrpc-lite')
const sinon = require('sinon')
const { v4: uid } = require('uuid')
// Set the environment variable to signal this is a test.
process.env.SVC_ENV = 'test'
// Local libraries.
const JSONRPC = require('../../../../src/controllers/json-rpc')
const adapters = require('../../mocks/adapters')
const UseCasesMock = require('../../mocks/use-cases')
describe('#JSON RPC', () => {
let uut
let sandbox
beforeEach(() => {
sandbox = sinon.createSandbox()
const useCases = new UseCasesMock()
uut = new JSONRPC({ adapters, useCases })
})
afterEach(() => sandbox.restore())
describe('#constructor', () => {
it('should throw an error if adapters are not passed in', () => {
try {
uut = new JSONRPC()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Adapters library required when instantiating JSON RPC Controllers.'
)
}
})
it('should throw an error if useCases are not passed in', () => {
try {
uut = new JSONRPC({ adapters })
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Use Cases library required when instantiating JSON RPC Controllers.'
)
}
})
})
describe('#router', () => {
it('should exit quietly if given a random string', async () => {
const str = 'random string message'
await uut.router(str)
assert.isOk('Not throwing an error is a pass.')
})
it('should exit quietly if invalid JSON RPC message received', async () => {
const malformedRpc = '{"jsonrpc":"2.0"}'
await uut.router(malformedRpc, 'peerA')
assert.isOk('Not throwing an error is a pass.')
})
it('should return default response if routing is not possible', async () => {
const id = uid()
const json = jsonrpc.request(id, 'unknownMethod', {})
const str = JSON.stringify(json)
const result = await uut.router(str, 'peerA')
// console.log('result: ', result)
const jsonObj = jsonrpc.parse(result.retStr)
// console.log(`jsonObj: ${JSON.stringify(jsonObj, null, 2)}`)
// Assert the expected properties exist on the returned object.
assert.property(jsonObj, 'payload')
assert.property(jsonObj, 'type')
assert.property(jsonObj.payload, 'jsonrpc')
assert.property(jsonObj.payload, 'id')
assert.property(jsonObj.payload, 'result')
assert.property(jsonObj.payload.result, 'reciever')
assert.property(jsonObj.payload.result.value, 'success')
assert.property(jsonObj.payload.result.value, 'message')
// Assert the expected values exist.
assert.equal(jsonObj.payload.id, id)
assert.equal(jsonObj.payload.result.value.success, false)
assert.equal(jsonObj.payload.result.value.status, 422)
assert.equal(
jsonObj.payload.result.value.message,
'Input does not match routing rules.'
)
})
it('should catch and handle errors', async () => {
// Force an error
sandbox.stub(uut.jsonrpc, 'parse').throws(new Error('test error'))
const malformedRpc = '{"jsonrpc":"2.0"}'
await uut.router(malformedRpc, 'peerA')
assert.isOk('Not throwing an error is a pass.')
})
it('should route to users handler', async () => {
const id = uid()
const userCall = jsonrpc.request(id, 'users', { endpoint: 'getAll' })
const jsonStr = JSON.stringify(userCall, null, 2)
// Mock the users controller.
sandbox.stub(uut.userController, 'userRouter').resolves('true')
const result = await uut.router(jsonStr, 'peerA')
// console.log(result)
const obj = JSON.parse(result.retStr)
// console.log('obj: ', obj)
assert.equal(obj.result.value, 'true')
assert.equal(obj.result.method, 'users')
assert.equal(obj.id, id)
})
it('should route to auth handler', async () => {
const id = uid()
const userCall = jsonrpc.request(id, 'auth', { endpoint: 'getAll' })
const jsonStr = JSON.stringify(userCall, null, 2)
// Mock the controller.
sandbox.stub(uut.authController, 'authRouter').resolves('true')
const result = await uut.router(jsonStr, 'peerA')
// console.log(result)
const obj = JSON.parse(result.retStr)
// console.log('obj: ', obj)
assert.equal(obj.result.value, 'true')
assert.equal(obj.result.method, 'auth')
assert.equal(obj.id, id)
})
it('should route to about handler', async () => {
const id = uid()
const userCall = jsonrpc.request(id, 'about', { endpoint: 'getAll' })
const jsonStr = JSON.stringify(userCall, null, 2)
// Mock the controller.
sandbox.stub(uut.aboutController, 'aboutRouter').resolves('true')
// Force ipfs-coord communication.
uut.ipfsCoord.ipfs = {
orbitdb: {
sendToDb: () => {}
}
}
const result = await uut.router(jsonStr, 'peerA')
// console.log(result)
const obj = JSON.parse(result.retStr)
// console.log('obj: ', obj)
assert.equal(obj.result.value, 'true')
assert.equal(obj.result.method, 'about')
assert.equal(obj.id, id)
})
it('should exit quietly for duplicate RPC message', async () => {
const id = uid()
const json = jsonrpc.request(id, 'unknownMethod', {})
const str = JSON.stringify(json)
// Call router once.
await uut.router(str, 'peerA')
// Call the router again with the same input.
const result = await uut.router(str, 'peerA')
assert.equal(result, false)
})
it('should ignore metric queries from ipfs-coord', async () => {
const id = uid()
const json = jsonrpc.request(id, 'unknownMethod', {})
const str = JSON.stringify(json)
// Force the RPC type to be 'success', to indicate an RPC that was
// processed internal to ipfs-coord.
sandbox.stub(uut.jsonrpc, 'parse').returns({
payload: {},
type: 'success'
})
// Call the router again with the same input.
const result = await uut.router(str, 'peerA')
assert.equal(result, false)
})
it('should report errors when trying to send messages to peers', async () => {
const id = uid()
const json = jsonrpc.request(id, 'unknownMethod', {})
const str = JSON.stringify(json)
// Force issue with sendPrivateMessage()
sandbox
.stub(uut.ipfsCoord.useCases.peer, 'sendPrivateMessage')
.rejects('test error')
// Call the router again with the same input.
const result = await uut.router(str, 'peerA')
// console.log('result: ', result)
assert.property(result, 'from')
assert.property(result, 'retStr')
})
})
describe('#_checkIfAlreadyProcessed', () => {
it('should return false the first time an RPC command is seen', () => {
const data = {
payload: {
jsonrpc: '2.0',
id: '6c515f3c-cf8a-42ec-870e-e416edd4923f',
method: 'unknownMethod',
params: {}
},
type: 'request'
}
const result = uut._checkIfAlreadyProcessed(data)
assert.equal(result, false)
})
it('should return true the second time an RPC command is seen', () => {
const data = {
payload: {
jsonrpc: '2.0',
id: '6c515f3c-cf8a-42ec-870e-e416edd4923f',
method: 'unknownMethod',
params: {}
},
type: 'request'
}
// First call.
uut._checkIfAlreadyProcessed(data)
// Second call.
const result = uut._checkIfAlreadyProcessed(data)
assert.equal(result, true)
})
it('should push out old data from the cache for new data', () => {
const data = {
payload: {
jsonrpc: '2.0',
id: '6c515f3c-cf8a-42ec-870e-e416edd4923f',
method: 'unknownMethod',
params: {}
},
type: 'request'
}
uut.MSG_CACHE_SIZE = 0
const result = uut._checkIfAlreadyProcessed(data)
assert.equal(result, false)
})
it('should return true on error', () => {
const result = uut._checkIfAlreadyProcessed()
assert.equal(result, true)
})
})
})
@@ -0,0 +1,307 @@
/*
Unit tests for the JSON RPC validator middleware.
TODO: ensureTargetUserOrAdmin: it should exit quietly if user is an admin.
*/
// Public npm libraries
const jsonrpc = require('jsonrpc-lite')
const sinon = require('sinon')
const assert = require('chai').assert
const { v4: uid } = require('uuid')
// Set the environment variable to signal this is a test.
process.env.SVC_ENV = 'test'
// Local libraries
const Validators = require('../../../../src/controllers/json-rpc/validators')
const adapters = require('../../mocks/adapters')
describe('#validators', () => {
let uut
let sandbox
beforeEach(() => {
sandbox = sinon.createSandbox()
uut = new Validators({ adapters })
})
afterEach(() => sandbox.restore())
describe('#constructor', () => {
it('should throw an error if adapters is not passed in.', () => {
try {
uut = new Validators()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Adapters library required when instantiating JSON RPC Validators library.'
)
}
})
})
describe('#ensureUser', () => {
it('should return user model for valid JWT token', async () => {
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const userCall = jsonrpc.request(id, 'users', {
endpoint: 'getAll',
apiToken: 'fakeJWTToken'
})
const jsonStr = JSON.stringify(userCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
// Mock external dependencies.
sandbox.stub(uut.jwt, 'verify').returns(true)
sandbox.stub(uut.UserModel, 'findById').resolves(true)
const user = await uut.ensureUser(rpcData)
// console.log('user: ', user)
// For this test, we return a value of 'true' instead of actual user data.
assert.equal(user, true)
})
it('should throw an error if JWT token is not included', async () => {
try {
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const userCall = jsonrpc.request(id, 'users', {
endpoint: 'getAll'
})
const jsonStr = JSON.stringify(userCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
await uut.ensureUser(rpcData)
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.include(err.message, 'apiToken JWT required as a parameter')
}
})
it('should throw an error if JWT token can not be decoded', async () => {
try {
const token =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYwNmQxYTlkNTgxNTVjMjIzNWFmMTNhMSIsImlhdCI6MTYxNzc2Mjk3M30.6JkM1v0n71Mzsd3qzClzlMKtq6HlD0umoauG23N9FFF'
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const userCall = jsonrpc.request(id, 'users', {
endpoint: 'getAll',
apiToken: token
})
const jsonStr = JSON.stringify(userCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
await uut.ensureUser(rpcData)
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.include(err.message, 'invalid signature')
}
})
it('should throw an error if the user can not be found', async () => {
try {
// Force 'error not found' error
sandbox.stub(uut.UserModel, 'findById').resolves(null)
sandbox.stub(uut.jwt, 'verify').returns(true)
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const userCall = jsonrpc.request(id, 'users', {
endpoint: 'getAll',
apiToken: 'fakeJWTToken'
})
const jsonStr = JSON.stringify(userCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
await uut.ensureUser(rpcData)
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.include(err.message, 'User not found!')
}
})
})
describe('#ensureTargetUserOrAdmin', () => {
it('should return user model for valid JWT token', async () => {
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const userCall = jsonrpc.request(id, 'users', {
endpoint: 'deleteUser',
apiToken: 'fakeJWTToken',
userId: 'abc123'
})
const jsonStr = JSON.stringify(userCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
// Mock external dependencies.
sandbox.stub(uut.jwt, 'verify').returns(true)
sandbox.stub(uut.UserModel, 'findById').resolves({ _id: 'abc123' })
const user = await uut.ensureTargetUserOrAdmin(rpcData)
// console.log('user: ', user)
// Assert that the mocked data expected is returned.
assert.equal(user._id, 'abc123')
})
it('should throw error if JWT token is not provided', async () => {
try {
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const userCall = jsonrpc.request(id, 'users', {
endpoint: 'deleteUser'
})
const jsonStr = JSON.stringify(userCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
await uut.ensureTargetUserOrAdmin(rpcData)
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.include(err.message, 'apiToken JWT required as a parameter')
}
})
it('should throw error if user ID is not specified', async () => {
try {
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const userCall = jsonrpc.request(id, 'users', {
endpoint: 'deleteUser',
apiToken: 'fakeJWTToken'
})
const jsonStr = JSON.stringify(userCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
await uut.ensureTargetUserOrAdmin(rpcData)
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.include(err.message, 'userId must be specified')
}
})
it('should throw error if JWT token can not be decoded', async () => {
try {
const token =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYwNmU0YzkxYzdlYWNjN2Q4NWJjOGI0NCIsImlhdCI6MTYxNzg0MTI5N30.n1sas7YlqtmhBlNDBY_IXxQCrIQTiE8UITqy0PJAFFF'
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const userCall = jsonrpc.request(id, 'users', {
endpoint: 'deleteUser',
apiToken: token,
userId: 'abc123'
})
const jsonStr = JSON.stringify(userCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
await uut.ensureTargetUserOrAdmin(rpcData)
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.include(err.message, 'invalid signature')
}
})
it('should throw an error if user can not be found', async () => {
try {
// Mock external dependencies.
sandbox.stub(uut.jwt, 'verify').returns(true)
sandbox.stub(uut.UserModel, 'findById').resolves(null)
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const userCall = jsonrpc.request(id, 'users', {
endpoint: 'deleteUser',
apiToken: 'fakeJWTToken',
userId: 'abc123'
})
const jsonStr = JSON.stringify(userCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
await uut.ensureTargetUserOrAdmin(rpcData)
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.include(err.message, 'User not found!')
}
})
it('should throw an error if JWT is from a different user', async () => {
try {
// Mock external dependencies.
sandbox.stub(uut.jwt, 'verify').returns(true)
sandbox.stub(uut.UserModel, 'findById').resolves({ _id: 'badId' })
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const userCall = jsonrpc.request(id, 'users', {
endpoint: 'deleteUser',
apiToken: 'fakeJWTToken',
userId: 'abc123'
})
const jsonStr = JSON.stringify(userCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
await uut.ensureTargetUserOrAdmin(rpcData)
assert.fail('Unexpected code path')
} catch (err) {
// console.log(err)
assert.include(err.message, 'User is neither admin nor target user.')
}
})
it('should return true if user is an admin', async () => {
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const userCall = jsonrpc.request(id, 'users', {
endpoint: 'deleteUser',
apiToken: 'fakeJWTToken',
userId: 'abc123'
})
const jsonStr = JSON.stringify(userCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
// Mock external dependencies.
sandbox.stub(uut.jwt, 'verify').returns(true)
sandbox
.stub(uut.UserModel, 'findById')
.resolves({ _id: 'abc123', type: 'admin' })
const user = await uut.ensureTargetUserOrAdmin(rpcData)
// console.log('user: ', user)
// Assert that the mocked data expected is returned.
assert.equal(user, true)
})
})
})
@@ -0,0 +1,109 @@
/*
Unit tests for the JSON RPC validator middleware.
TODO: ensureTargetUserOrAdmin: it should exit quietly if user is an admin.
*/
// Public npm libraries
const sinon = require('sinon')
const assert = require('chai').assert
// Set the environment variable to signal this is a test.
process.env.SVC_ENV = 'test'
// Local libraries
const RateLimit = require('../../../../src/controllers/json-rpc/rate-limit')
describe('#rate-limit', () => {
let uut
let sandbox
beforeEach(() => {
sandbox = sinon.createSandbox()
uut = new RateLimit()
})
afterEach(() => sandbox.restore())
describe('#constructor', () => {
it('should use the options provided', async () => {
try {
const options = {
interval: { min: 10 },
delayAfter: 1,
timeWait: { sec: 5 },
max: 2,
onLimitReached: () => {
throw new Error('custom message error')
}
}
const _uut = new RateLimit(options)
// Assert options
assert.equal(_uut.rateLimitOptions.interval.min, options.interval.min)
assert.equal(_uut.rateLimitOptions.delayAfter, options.delayAfter)
assert.equal(_uut.rateLimitOptions.timeWait.sec, options.timeWait.sec)
const from = 'constructor test'
const firstRequest = await _uut.limiter(from)
assert.isTrue(firstRequest)
const secondRequest = await _uut.limiter(from)
assert.isTrue(secondRequest)
await _uut.limiter(from)
assert.fail('unexpected error')
} catch (error) {
assert.include(error.message, 'custom message error')
}
})
})
describe('#onLimitReached', () => {
it('should throw error', async () => {
try {
uut.onLimitReached()
assert.fail('unexpected error')
} catch (error) {
assert.equal(error.status, 429)
assert.include(
error.message,
'Too many requests, please try again later.'
)
}
})
})
describe('#limiter', () => {
it('should throw error if "from" input is not provider', async () => {
try {
await uut.limiter()
assert.fail('unexpected error')
} catch (error) {
assert.include(error.message, 'from must be a string')
}
})
it('should throw error 429', async () => {
try {
const _uut = new RateLimit({ max: 1 })
const from = 'Origin request'
const firstRequest = await _uut.limiter(from)
assert.isTrue(firstRequest)
const secondRequest = await _uut.limiter(from)
assert.isTrue(secondRequest)
await _uut.limiter(from)
assert.fail('unexpected error')
} catch (error) {
assert.include(
error.message,
'Too many requests, please try again later.'
)
}
})
})
})
@@ -0,0 +1,38 @@
/*
Unit tests for the json-rpc/about/index.js file.
*/
// Public npm libraries
const sinon = require('sinon')
const assert = require('chai').assert
// Local libraries
const AboutRPC = require('../../../../src/controllers/json-rpc/about')
describe('#AboutRPC', () => {
let uut
let sandbox
beforeEach(() => {
sandbox = sinon.createSandbox()
uut = new AboutRPC()
})
afterEach(() => sandbox.restore())
describe('#aboutRouter', () => {
it('should return information about the service', async () => {
const result = await uut.aboutRouter()
// console.log('result: ', result)
assert.property(result, 'success')
assert.equal(result.success, true)
assert.property(result, 'status')
assert.equal(result.status, 200)
assert.property(result, 'message')
assert.property(result, 'endpoint')
assert.equal(result.endpoint, 'about')
})
})
})
@@ -0,0 +1,194 @@
/*
Unit tests for the json-rpc/auth/index.js file.
*/
// Public npm libraries
const jsonrpc = require('jsonrpc-lite')
const sinon = require('sinon')
const assert = require('chai').assert
const { v4: uid } = require('uuid')
// Set the environment variable to signal this is a test.
process.env.SVC_ENV = 'test'
// Local libraries
const AuthRPC = require('../../../../src/controllers/json-rpc/auth')
const RateLimit = require('../../../../src/controllers/json-rpc/rate-limit')
const adapters = require('../../mocks/adapters')
const UseCasesMock = require('../../mocks/use-cases')
describe('#AuthRPC', () => {
let uut
let sandbox
beforeEach(() => {
sandbox = sinon.createSandbox()
const useCases = new UseCasesMock()
uut = new AuthRPC({ adapters, useCases })
uut.rateLimit = new RateLimit({ max: 100 })
})
afterEach(() => sandbox.restore())
describe('#constructor', () => {
it('should throw an error if adapters are not passed in', () => {
try {
uut = new AuthRPC()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Adapters library required when instantiating Auth JSON RPC Controller.'
)
}
})
it('should throw an error if useCases are not passed in', () => {
try {
uut = new AuthRPC({ adapters })
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Use Cases library required when instantiating Auth JSON RPC Controller.'
)
}
})
})
describe('#authRouter', () => {
it('should route to the authUser method', async () => {
// Mock dependencies
sandbox.stub(uut, 'authUser').resolves(true)
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const authCall = jsonrpc.request(id, 'auth', { endpoint: 'authUser' })
const jsonStr = JSON.stringify(authCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
rpcData.from = 'Origin request'
const result = await uut.authRouter(rpcData)
assert.equal(result, true)
})
it('should return 500 status on routing issue', async () => {
// Mock dependencies
sandbox.stub(uut, 'authUser').rejects(new Error('test error'))
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const authCall = jsonrpc.request(id, 'auth', { endpoint: 'authUser' })
const jsonStr = JSON.stringify(authCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
rpcData.from = 'Origin request'
const result = await uut.authRouter(rpcData)
assert.equal(result.success, false)
assert.equal(result.status, 500)
assert.equal(result.message, 'test error')
assert.equal(result.endpoint, 'authUser')
})
})
describe('#authUser', () => {
it('should return a JWT token if user successfully authenticates', async () => {
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const authCall = jsonrpc.request(id, 'auth', {
endpoint: 'authUser',
login: 'test543@test.com',
password: 'password'
})
const jsonStr = JSON.stringify(authCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
const response = await uut.authUser(rpcData)
// console.log('response: ', response)
assert.equal(response.endpoint, 'authUser')
assert.property(response, 'userId')
// assert.equal(response.userType, 'user')
assert.property(response, 'userName')
assert.property(response, 'userEmail')
assert.property(response, 'apiToken')
assert.equal(response.status, 200)
assert.equal(response.success, true)
assert.property(response, 'message')
})
it('should return an error for invalid credentials', async () => {
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const authCall = jsonrpc.request(id, 'auth', {
endpoint: 'authUser',
login: 'test543@test.com',
password: 'badpassword'
})
const jsonStr = JSON.stringify(authCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
// Force an error.
sandbox
.stub(uut.userLib, 'authUser')
.rejects(new Error('Login credential do not match'))
const response = await uut.authUser(rpcData)
// console.log('response: ', response)
assert.equal(response.success, false)
assert.equal(response.status, 422)
assert.equal(response.message, 'Login credential do not match')
assert.equal(response.endpoint, 'authUser')
})
it('should throw an error if login is not provided', async () => {
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const authCall = jsonrpc.request(id, 'auth', {
endpoint: 'authUser'
})
const jsonStr = JSON.stringify(authCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
const response = await uut.authUser(rpcData)
// console.log('response: ', response)
assert.equal(response.success, false)
assert.equal(response.status, 422)
assert.equal(response.message, 'login must be specified')
assert.equal(response.endpoint, 'authUser')
})
it('should throw an error if password is not provided', async () => {
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const authCall = jsonrpc.request(id, 'auth', {
endpoint: 'authUser',
login: 'test543@test.com'
})
const jsonStr = JSON.stringify(authCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
const response = await uut.authUser(rpcData)
// console.log('response: ', response)
assert.equal(response.success, false)
assert.equal(response.status, 422)
assert.equal(response.message, 'password must be specified')
assert.equal(response.endpoint, 'authUser')
})
})
})
@@ -0,0 +1,412 @@
/*
Unit tests for the rpc/users/index.js file.
*/
// Public npm libraries
const jsonrpc = require('jsonrpc-lite')
const sinon = require('sinon')
const assert = require('chai').assert
const { v4: uid } = require('uuid')
// Set the environment variable to signal this is a test.
process.env.SVC_ENV = 'test'
// Local libraries
const UserRPC = require('../../../../src/controllers/json-rpc/users')
const adapters = require('../../mocks/adapters')
const UseCasesMock = require('../../mocks/use-cases')
describe('#UserRPC', () => {
let uut
let sandbox
let testUser
beforeEach(() => {
sandbox = sinon.createSandbox()
const useCases = new UseCasesMock()
uut = new UserRPC({ adapters, useCases })
})
afterEach(() => sandbox.restore())
describe('#constructor', () => {
it('should throw an error if adapters are not passed in', () => {
try {
uut = new UserRPC()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Adapters library required when instantiating User JSON RPC Controller.'
)
}
})
it('should throw an error if useCases are not passed in', () => {
try {
uut = new UserRPC({ adapters })
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Use Cases library required when instantiating User JSON RPC Controller.'
)
}
})
})
describe('#createUser', () => {
it('should create a new user', async () => {
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const userCall = jsonrpc.request(id, 'users', {
endpoint: 'createUser',
email: 'test973@test.com',
name: 'test973',
password: 'password'
})
const jsonStr = JSON.stringify(userCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
const result = await uut.createUser(rpcData)
// console.log('result: ', result)
// CreateUser() specific return values.
// assert.equal(result.userData.type, 'user')
// assert.equal(result.userData.email, 'test973@test.com')
// assert.equal(result.userData.name, 'test973')
// assert.property(result.userData, '_id')
// assert.property(result, 'token')
// Generic JSON RPC return values
assert.equal(result.endpoint, 'createUser')
assert.equal(result.success, true)
assert.equal(result.status, 200)
assert.equal(result.message, '')
// Save the user ID for future tests.
testUser = result
})
it('should return error data if biz logic throws an error', async () => {
// Force an error
sandbox.stub(uut.userLib, 'createUser').rejects(new Error('test error'))
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const userCall = jsonrpc.request(id, 'users', {
endpoint: 'createUser',
email: 'test973@test.com',
name: 'test973',
password: 'password'
})
const jsonStr = JSON.stringify(userCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
const result = await uut.createUser(rpcData)
// console.log('result: ', result)
// Generic JSON RPC return values
assert.equal(result.endpoint, 'createUser')
assert.equal(result.success, false)
assert.equal(result.status, 422)
assert.equal(result.message, 'test error')
})
})
describe('#userRouter', () => {
it('should route to the createUser method', async () => {
// Mock dependencies
sandbox.stub(uut, 'createUser').resolves(true)
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const userCall = jsonrpc.request(id, 'users', { endpoint: 'createUser' })
const jsonStr = JSON.stringify(userCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
rpcData.from = 'Origin request'
const result = await uut.userRouter(rpcData)
assert.equal(result, true)
})
it('should route to the getAllUsers method', async () => {
// Mock dependencies
sandbox.stub(uut, 'getAll').resolves(true)
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const userCall = jsonrpc.request(id, 'users', {
endpoint: 'getAllUsers',
apiToken: testUser.token
})
const jsonStr = JSON.stringify(userCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
rpcData.from = 'Origin request'
// Force middleware to pass.
sandbox.stub(uut.validators, 'ensureUser').resolves(true)
const result = await uut.userRouter(rpcData)
// console.log('result', result)
assert.equal(result, true)
})
it('should route to the updateUser method', async () => {
// Mock dependencies
sandbox.stub(uut, 'updateUser').resolves(true)
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const userCall = jsonrpc.request(id, 'users', {
endpoint: 'updateUser',
apiToken: 'fakeJWTToken',
userId: 'abc123'
})
const jsonStr = JSON.stringify(userCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
rpcData.from = 'Origin request'
// Force middleware to pass.
sandbox.stub(uut.validators, 'ensureTargetUserOrAdmin').resolves(true)
const result = await uut.userRouter(rpcData)
// console.log('result: ', result)
assert.equal(result, true)
})
it('should route to the getUser method', async () => {
// Mock dependencies
sandbox.stub(uut, 'getUser').resolves(true)
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const userCall = jsonrpc.request(id, 'users', {
endpoint: 'getUser',
apiToken: testUser.token
})
const jsonStr = JSON.stringify(userCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
rpcData.from = 'Origin request'
// Force middleware to pass.
sandbox.stub(uut.validators, 'ensureUser').resolves(true)
const result = await uut.userRouter(rpcData)
// console.log('result: ', result)
assert.equal(result, true)
})
it('should route to the deleteUsers method', async () => {
// Mock dependencies
sandbox.stub(uut, 'deleteUser').resolves(true)
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const userCall = jsonrpc.request(id, 'users', {
endpoint: 'deleteUser',
apiToken: 'fakeJWTToken',
userId: 'abc123'
})
const jsonStr = JSON.stringify(userCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
rpcData.from = 'Origin request'
// Force middleware to pass.
sandbox.stub(uut.validators, 'ensureTargetUserOrAdmin').resolves(true)
const result = await uut.userRouter(rpcData)
// console.log('result: ', result)
assert.equal(result, true)
})
it('should return 500 status on routing issue', async () => {
// Force an error
sandbox.stub(uut, 'createUser').rejects(new Error('test error'))
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const userCall = jsonrpc.request(id, 'users', { endpoint: 'createUser' })
const jsonStr = JSON.stringify(userCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
rpcData.from = 'Origin request'
const result = await uut.userRouter(rpcData)
// console.log('result: ', result)
assert.equal(result.success, false)
assert.equal(result.status, 500)
assert.equal(result.message, 'test error')
assert.equal(result.endpoint, 'createUser')
})
})
describe('#getAllUsers', () => {
it('should return all users', async () => {
const result = await uut.getAll()
// console.log('getAll result: ', result)
// Endpoint specific properties
assert.property(result, 'users')
// Generic JSON RPC return values
assert.equal(result.endpoint, 'getAllUsers')
assert.equal(result.success, true)
assert.equal(result.status, 200)
assert.equal(result.message, '')
})
it('should return error data if biz logic throws an error', async () => {
// Force an error
sandbox.stub(uut.userLib, 'getAllUsers').rejects(new Error('test error'))
const result = await uut.getAll()
// console.log('result: ', result)
// Generic JSON RPC return values
assert.equal(result.endpoint, 'getAllUsers')
assert.equal(result.success, false)
assert.equal(result.status, 422)
assert.equal(result.message, 'test error')
})
})
describe('#updateUser', () => {
it('should update a user', async () => {
// Get the user model for the test user.
// const testUserModel = await UserModel.findById(
// testUser.userData._id,
// '-password'
// )
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const userCall = jsonrpc.request(id, 'users', {
endpoint: 'updateUser',
// userId: testUser.userData._id.toString(),
userId: 'abc123',
name: 'test777'
})
const jsonStr = JSON.stringify(userCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
const result = await uut.updateUser(rpcData, {})
// console.log('updateUser result: ', result)
// Endpoint specific properties
assert.property(result, 'user')
// assert.property(result.user, 'type')
// assert.property(result.user, '_id')
// assert.property(result.user, 'email')
// assert.property(result.user, 'name')
// Generic JSON RPC return values
assert.equal(result.endpoint, 'updateUser')
assert.equal(result.success, true)
assert.equal(result.status, 200)
assert.equal(result.message, '')
})
it('should return error data if biz logic throws an error', async () => {
// Force an error by not specifying an user ID.
const result = await uut.updateUser()
// console.log('result: ', result)
// Generic JSON RPC return values
assert.equal(result.endpoint, 'updateUser')
assert.equal(result.success, false)
assert.equal(result.status, 422)
assert.include(result.message, 'Cannot read property')
})
})
describe('#getUser', () => {
it('should return a specific user', async () => {
// Generate the parsed data that the main router would pass to this
// endpoint.
const id = uid()
const userCall = jsonrpc.request(id, 'users', {
endpoint: 'getUser',
userId: 'abc123'
})
const jsonStr = JSON.stringify(userCall, null, 2)
const rpcData = jsonrpc.parse(jsonStr)
const result = await uut.getUser(rpcData)
// console.log('getUser result: ', result)
// Endpoint specific properties
assert.property(result, 'user')
// assert.property(result.user, 'type')
// assert.property(result.user, '_id')
// assert.property(result.user, 'email')
// assert.property(result.user, 'name')
// Generic JSON RPC return values
assert.equal(result.endpoint, 'getUser')
assert.equal(result.success, true)
assert.equal(result.status, 200)
assert.equal(result.message, '')
})
it('should return error data if biz logic throws an error', async () => {
// Force an error by not specifying an user ID.
const result = await uut.getUser()
// console.log('result: ', result)
// Generic JSON RPC return values
assert.equal(result.endpoint, 'getUser')
assert.equal(result.success, false)
assert.equal(result.status, 422)
assert.include(result.message, 'Cannot read property')
})
})
describe('#deleteUser', () => {
it('should delete a user', async () => {
// Get the user model for the test user.
// const testUserModel = await UserModel.findById(
// testUser.userData._id,
// '-password'
// )
await uut.deleteUser({}, {})
// console.log(result)
assert.isOk('Not throwing an error is a success')
})
it('should return error data if biz logic throws an error', async () => {
// Force an error:
sandbox
.stub(uut.userLib, 'deleteUser')
.rejects(new Error('Cannot read property'))
const result = await uut.deleteUser()
// console.log('result: ', result)
// Generic JSON RPC return values
assert.equal(result.endpoint, 'deleteUser')
assert.equal(result.success, false)
assert.equal(result.status, 422)
assert.include(result.message, 'Cannot read property')
})
})
})
+9
View File
@@ -0,0 +1,9 @@
# REST API Unit Tests
The tests in this directory are unit tests of REST API. These tests are not
concerned with the business logic behind the endpoints. They are only concerned
with the handling of the REST API endpoint. These tests answer questions like:
- Is the endpoint responding properly when the business logic throws an error?
- When returning an error, is it returning the proper HTTP response?
- When returning success, is it returning the correct payload?
@@ -0,0 +1,90 @@
/*
Unit tests for the REST API handler for the /users endpoints.
*/
// Public npm libraries
const assert = require('chai').assert
const sinon = require('sinon')
// Local support libraries
const adapters = require('../../../mocks/adapters')
const UseCasesMock = require('../../../mocks/use-cases')
// const app = require('../../../mocks/app-mock')
const AuthRESTController = require('../../../../../src/controllers/rest-api/auth/controller')
let uut
let sandbox
let ctx
const mockContext = require('../../../../unit/mocks/ctx-mock').context
describe('#Auth-REST-Router', () => {
// const testUser = {}
beforeEach(() => {
const useCases = new UseCasesMock()
uut = new AuthRESTController({ 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 AuthRESTController()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Adapters library required when instantiating Auth REST Controller.'
)
}
})
it('should throw an error if useCases are not passed in', () => {
try {
uut = new AuthRESTController({ adapters })
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Use Cases library required when instantiating Auth REST Controller.'
)
}
})
})
describe('#authUser', () => {
it('should authorize a user', async () => {
// Mock dependencies
const user = {
toJSON: () => {
return { password: 'password' }
},
generateToken: () => {}
}
sandbox.stub(uut.passport, 'authUser').resolves(user)
await uut.authUser(ctx)
})
it('should catch and throw an error', async () => {
try {
// Force an error
sandbox.stub(uut.passport, 'authUser').rejects('test error')
await uut.authUser(ctx)
} catch (err) {
// console.log('err: ', err)
assert.include(err.message, 'Unauthorized')
}
})
})
})
@@ -0,0 +1,78 @@
/*
Unit tests for the REST API handler for the /users endpoints.
*/
// Public npm libraries
const assert = require('chai').assert
const sinon = require('sinon')
// Local support libraries
const adapters = require('../../../mocks/adapters')
const UseCasesMock = require('../../../mocks/use-cases')
// const app = require('../../../mocks/app-mock')
const AuthRouter = require('../../../../../src/controllers/rest-api/auth')
let uut
let sandbox
// let ctx
// const mockContext = require('../../../../unit/mocks/ctx-mock').context
describe('#Auth-REST-Router', () => {
// const testUser = {}
beforeEach(() => {
const useCases = new UseCasesMock()
uut = new AuthRouter({ 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 AuthRouter()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Adapters library required when instantiating PostEntry REST Controller.'
)
}
})
it('should throw an error if useCases are not passed in', () => {
try {
uut = new AuthRouter({ adapters })
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Use Cases library required when instantiating PostEntry 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 attached REST API controllers.'
)
}
})
})
})
@@ -0,0 +1,88 @@
/*
Unit tests for the REST API handler for the /users endpoints.
*/
// Public npm libraries
const assert = require('chai').assert
const sinon = require('sinon')
const ContactController = require('../../../../../src/controllers/rest-api/contact/controller')
let uut
let sandbox
let ctx
const mockContext = require('../../../../unit/mocks/ctx-mock').context
describe('Contact', () => {
before(async () => {})
beforeEach(() => {
uut = new ContactController()
sandbox = sinon.createSandbox()
// Mock the context object.
ctx = mockContext()
})
afterEach(() => sandbox.restore())
describe('#POST /contact', () => {
it('should return 422 status on biz logic error', async () => {
try {
await uut.email(ctx)
assert.fail('Unexpected result')
} catch (err) {
// console.log(err)
assert.equal(err.status, 422)
assert.include(err.message, 'Cannot read property')
}
})
it('should return 200 status on success', async () => {
sandbox.stub(uut.contactLib, 'sendEmail').resolves(true)
ctx.request.body = {
email: 'test02@test.com',
formMessage: 'test'
}
await uut.email(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, 'success')
assert.isTrue(ctx.response.body.success)
})
})
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,78 @@
/*
Unit tests for the REST API handler for the /users endpoints.
*/
// Public npm libraries
const assert = require('chai').assert
const sinon = require('sinon')
// Local support libraries
const adapters = require('../../../mocks/adapters')
const UseCasesMock = require('../../../mocks/use-cases')
// const app = require('../../../mocks/app-mock')
const ContactRouter = require('../../../../../src/controllers/rest-api/contact')
let uut
let sandbox
// let ctx
// const mockContext = require('../../../../unit/mocks/ctx-mock').context
describe('#Contact-REST-Router', () => {
// const testUser = {}
beforeEach(() => {
const useCases = new UseCasesMock()
uut = new ContactRouter({ 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 ContactRouter()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Adapters library required when instantiating Contact REST Controller.'
)
}
})
it('should throw an error if useCases are not passed in', () => {
try {
uut = new ContactRouter({ adapters })
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Use Cases library required when instantiating Contact 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.'
)
}
})
})
})
@@ -0,0 +1,75 @@
/*
Unit tests for the REST API handler for the /users endpoints.
*/
// Public npm libraries
const assert = require('chai').assert
const sinon = require('sinon')
const LogsApiController = require('../../../../../src/controllers/rest-api/logs/controller')
let uut
let sandbox
let ctx
const mockContext = require('../../../../unit/mocks/ctx-mock').context
describe('Logapi', () => {
before(async () => {})
beforeEach(() => {
uut = new LogsApiController()
sandbox = sinon.createSandbox()
// Mock the context object.
ctx = mockContext()
})
afterEach(() => sandbox.restore())
describe('#POST /logapi', () => {
it('should return 422 status on biz logic error', async () => {
try {
await uut.getLogs(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(err.status, 422)
assert.include(err.message, 'Cannot read property')
}
})
it('should return 500 status on biz logic Unhandled error', async () => {
try {
// eslint-disable
sandbox
.stub(uut.logsApiLib, 'getLogs')
.returns(Promise.reject(new Error()))
ctx.request.body = {
password: 'test'
}
await uut.getLogs(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(err.status, 500)
assert.include(err.message, 'Unhandled error')
}
})
it('should return 200 status on success', async () => {
// Mock dependencies
sandbox.stub(uut.logsApiLib, 'getLogs').resolves({})
ctx.request.body = {
password: 'test'
}
await uut.getLogs(ctx)
assert.isOk(ctx.body)
})
})
})
@@ -0,0 +1,78 @@
/*
Unit tests for the REST API handler for the /users endpoints.
*/
// Public npm libraries
const assert = require('chai').assert
const sinon = require('sinon')
// Local support libraries
const adapters = require('../../../mocks/adapters')
const UseCasesMock = require('../../../mocks/use-cases')
// const app = require('../../../mocks/app-mock')
const LogsRouter = require('../../../../../src/controllers/rest-api/logs')
let uut
let sandbox
// let ctx
// const mockContext = require('../../../../unit/mocks/ctx-mock').context
describe('#Contact-REST-Router', () => {
// const testUser = {}
beforeEach(() => {
const useCases = new UseCasesMock()
uut = new LogsRouter({ 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 LogsRouter()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Adapters library required when instantiating Logs REST Controller.'
)
}
})
it('should throw an error if useCases are not passed in', () => {
try {
uut = new LogsRouter({ adapters })
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Use Cases library required when instantiating Logs 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.'
)
}
})
})
})
@@ -0,0 +1,64 @@
/*
Unit tests for the REST API controllers/rest-api/index.js library.
*/
// Public npm libraries
const assert = require('chai').assert
const sinon = require('sinon')
// Local libraries
const RESTControllers = require('../../../../src/controllers/rest-api/')
// const mockContext = require('../../../unit/mocks/ctx-mock').context
const adapters = require('../../mocks/adapters')
const UseCasesMock = require('../../mocks/use-cases')
describe('#RESTControllers', () => {
let uut
let sandbox
// let ctx
before(async () => {})
beforeEach(() => {
const useCases = new UseCasesMock()
uut = new RESTControllers({ 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 RESTControllers()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Adapters library required when instantiating REST Controller libraries.'
)
}
})
it('should throw an error if useCases are not passed in', () => {
try {
uut = new RESTControllers({ adapters })
assert.fail('Unexpected code path')
// use to prevent complaints from linter.
console.log('uut: ', uut)
} catch (err) {
assert.include(
err.message,
'Instance of Use Cases library required when instantiating REST Controller libraries.'
)
}
})
})
})
@@ -0,0 +1,258 @@
/*
Unit tests for the REST API handler for the /users endpoints.
*/
// Public npm libraries
const assert = require('chai').assert
const sinon = require('sinon')
// Local support libraries
const adapters = require('../../../mocks/adapters')
const UseCasesMock = require('../../../mocks/use-cases')
const UserController = require('../../../../../src/controllers/rest-api/users/controller')
let uut
let sandbox
let ctx
const mockContext = require('../../../../unit/mocks/ctx-mock').context
describe('#Users-REST-Controller', () => {
// const testUser = {}
beforeEach(() => {
const useCases = new UseCasesMock()
uut = new UserController({ 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 UserController()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Adapters library required when instantiating /users REST Controller.'
)
}
})
it('should throw an error if useCases are not passed in', () => {
try {
uut = new UserController({ adapters })
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Use Cases library required when instantiating /users REST Controller.'
)
}
})
})
describe('#POST /users', () => {
it('should return 422 status on biz logic error', async () => {
try {
await uut.createUser(ctx)
assert.fail('Unexpected result')
} catch (err) {
// console.log(err)
assert.equal(err.status, 422)
assert.include(err.message, 'Cannot read property')
}
})
it('should return 200 status on success', async () => {
ctx.request.body = {
user: {
email: 'test02@test.com',
password: 'test',
name: 'test02'
}
}
await uut.createUser(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, 'user')
assert.property(ctx.response.body, 'token')
// Used by downstream tests.
// testUser = ctx.response.body.user
// console.log('testUser: ', testUser)
})
})
describe('GET /users', () => {
it('should return 422 status on arbitrary biz logic error', async () => {
try {
// Force an error
sandbox
.stub(uut.useCases.user, 'getAllUsers')
.rejects(new Error('test error'))
await uut.getUsers(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 () => {
await uut.getUsers(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, 'users')
})
})
describe('GET /users/:id', () => {
it('should return 422 status on arbitrary biz logic error', async () => {
try {
// Force an error
sandbox
.stub(uut.useCases.user, 'getUser')
.rejects(new Error('test error'))
await uut.getUser(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.user, 'getUser').resolves({ _id: '123' })
await uut.getUser(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, 'user')
})
it('should return other error status passed by biz logic', async () => {
try {
// Mock dependencies
const testErr = new Error('test error')
testErr.status = 404
sandbox.stub(uut.useCases.user, 'getUser').rejects(testErr)
await uut.getUser(ctx)
assert.fail('Unexpected result')
} catch (err) {
assert.equal(err.status, 404)
assert.include(err.message, 'test error')
}
})
})
describe('PUT /users/:id', () => {
it('should return 422 if no input data given', async () => {
try {
await uut.updateUser(ctx)
assert.fail('Unexpected result')
} catch (err) {
// console.log(err)
assert.equal(err.status, 422)
assert.include(err.message, 'Cannot read property')
}
})
it('should return 200 on success', async () => {
// Prep the testUser data.
// console.log('testUser: ', testUser)
// testUser.password = 'password'
// delete testUser.type
// Replace the testUser variable with an actual model from the DB.
// const existingUser = await User.findById(testUser._id)
ctx.body = {
user: {}
}
ctx.request.body = {
user: {}
}
// Mock dependencies
sandbox.stub(uut.useCases.user, 'updateUser').resolves({})
await uut.updateUser(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, 'user')
})
})
describe('DELETE /users/:id', () => {
it('should return 422 if no input data given', async () => {
try {
await uut.deleteUser(ctx)
assert.fail('Unexpected result')
} catch (err) {
// console.log(err)
assert.equal(err.status, 422)
assert.include(err.message, 'Cannot read property')
}
})
it('should return 200 status on success', async () => {
// Replace the testUser variable with an actual model from the DB.
const existingUser = {}
ctx.body = {
user: existingUser
}
await uut.deleteUser(ctx)
// Assert the expected HTTP response
assert.equal(ctx.status, 200)
})
})
describe('#handleError', () => {
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,78 @@
/*
Unit tests for the REST API handler for the /users endpoints.
*/
// Public npm libraries
const assert = require('chai').assert
const sinon = require('sinon')
// Local support libraries
const adapters = require('../../../mocks/adapters')
const UseCasesMock = require('../../../mocks/use-cases')
// const app = require('../../../mocks/app-mock')
const UserRouter = require('../../../../../src/controllers/rest-api/users')
let uut
let sandbox
// let ctx
// const mockContext = require('../../../../unit/mocks/ctx-mock').context
describe('#Users-REST-Router', () => {
// const testUser = {}
beforeEach(() => {
const useCases = new UseCasesMock()
uut = new UserRouter({ 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 UserRouter()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Adapters library required when instantiating PostEntry REST Controller.'
)
}
})
it('should throw an error if useCases are not passed in', () => {
try {
uut = new UserRouter({ adapters })
assert.fail('Unexpected code path')
} catch (err) {
assert.include(
err.message,
'Instance of Use Cases library required when instantiating PostEntry 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.'
)
}
})
})
})