From 8c146122897e13c7767ec3d0a70ede1e139ccf10 Mon Sep 17 00:00:00 2001 From: Edwin Guzman Date: Thu, 23 Mar 2017 13:28:54 -0400 Subject: [PATCH] Adding lambda-tester and chai for lambda unit tests. Writing in standard style. --- app.js | 56 +++++++++---------- context.json | 1 + event.json | 57 +++++++++++++++++++ index.js | 13 +++-- package.json | 2 + test/lambda.test.js | 121 +++++++++++++++++++++++++++++++++++++++++ test/resources.test.js | 10 ++-- 7 files changed, 223 insertions(+), 37 deletions(-) create mode 100644 context.json create mode 100644 event.json create mode 100644 test/lambda.test.js diff --git a/app.js b/app.js index c5ee8feb..2b8db32f 100644 --- a/app.js +++ b/app.js @@ -1,54 +1,54 @@ -const config = require('config'); -const log = require('loglevel'); -const swaggerDocs = require('./swagger.v0.2.json'); -const pjson = require('./package.json'); +const config = require('config') +// const log = require('loglevel') +const swaggerDocs = require('./swagger.v0.2.json') +const pjson = require('./package.json') -require('dotenv').config(); +require('dotenv').config() -var express = require('express'); -var elasticsearch = require('elasticsearch'); +var express = require('express') +var elasticsearch = require('elasticsearch') -var app = express(); +var app = express() -app.thesaurus = config.thesaurus; +app.thesaurus = config.thesaurus -require('./lib/agents')(app); -require('./lib/resources')(app); +require('./lib/agents')(app) +require('./lib/resources')(app) // routes -require('./routes/agents')(app); -require('./routes/resources')(app); -require('./routes/misc')(app); +require('./routes/agents')(app) +require('./routes/resources')(app) +require('./routes/misc')(app) app.esClient = new elasticsearch.Client({ host: config['elasticsearch'].host -}); +}) app.all('*', function (req, res, next) { - res.header('Access-Control-Allow-Origin', '*'); - res.header('Access-Control-Allow-Methods', 'GET, OPTIONS'); - res.header('Access-Control-Allow-Headers', 'Content-Type'); - next(); -}); + res.header('Access-Control-Allow-Origin', '*') + res.header('Access-Control-Allow-Methods', 'GET, OPTIONS') + res.header('Access-Control-Allow-Headers', 'Content-Type') + next() +}) app.get('/', function (req, res) { res.send(pjson.version) -}); +}) // Just testing route app.get('/api/v0.1/discovery', function (req, res) { res.send(pjson.version) -}); +}) app.get('/api/v0.1/discovery/swagger', function (req, res) { - res.send(swaggerDocs); -}); + res.send(swaggerDocs) +}) // Could be removed for the Lambda but necessary for locally running the app. require('./lib/globals')(app).then((app) => { app.listen(config['port'], function () { - console.log('Server started on port ' + config['port']); - }); -}); + console.log('Server started on port ' + config['port']) + }) +}) -module.exports = app; +module.exports = app diff --git a/context.json b/context.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/context.json @@ -0,0 +1 @@ +{} diff --git a/event.json b/event.json new file mode 100644 index 00000000..0176060a --- /dev/null +++ b/event.json @@ -0,0 +1,57 @@ +{ + "body": "", + "resource": "/{proxy+}", + "requestContext": { + "resourceId": "123456", + "apiId": "1234567890", + "resourcePath": "/{proxy+}", + "httpMethod": "GET", + "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", + "accountId": "123456789012", + "identity": { + "apiKey": null, + "userArn": null, + "cognitoAuthenticationType": null, + "caller": null, + "userAgent": "Custom User Agent String", + "user": null, + "cognitoIdentityPoolId": null, + "cognitoIdentityId": null, + "cognitoAuthenticationProvider": null, + "sourceIp": "127.0.0.1", + "accountId": null + }, + "stage": "prod" + }, + "queryStringParameters": { + "q": "france" + }, + "headers": { + "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", + "Accept-Language": "en-US,en;q=0.8", + "CloudFront-Is-Desktop-Viewer": "true", + "CloudFront-Is-SmartTV-Viewer": "false", + "CloudFront-Is-Mobile-Viewer": "false", + "X-Forwarded-For": "127.0.0.1, 127.0.0.2", + "CloudFront-Viewer-Country": "US", + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", + "Upgrade-Insecure-Requests": "1", + "X-Forwarded-Port": "443", + "Host": "1234567890.execute-api.us-east-1.amazonaws.com", + "X-Forwarded-Proto": "https", + "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", + "CloudFront-Is-Tablet-Viewer": "false", + "Cache-Control": "max-age=0", + "User-Agent": "Custom User Agent String", + "CloudFront-Forwarded-Proto": "https", + "Accept-Encoding": "gzip, deflate, sdch" + }, + "pathParameters": { + "proxy": "" + }, + "httpMethod": "GET", + "stageVariables": { + "baz": "qux" + }, + "path": "/api/v0.1/discovery/resources?q=france" +} diff --git a/index.js b/index.js index e607718a..d92109db 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,12 @@ 'use strict' -const awsServerlessExpress = require('aws-serverless-express'); -const app = require('./app'); -const server = awsServerlessExpress.createServer(app); +const awsServerlessExpress = require('aws-serverless-express') +const app = require('./app') +const server = awsServerlessExpress.createServer(app) -exports.handler = (event, context) => awsServerlessExpress.proxy(server, event, context); +exports.handler = (event, context, callback) => { + if (Object.keys(event).length === 0 && event.constructor === Object) { + return callback('No event was received.') + } + return awsServerlessExpress.proxy(server, event, context) +} diff --git a/package.json b/package.json index 0b9b75be..6850d4f3 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "dependencies": { "async": "^1.5.2", "aws-serverless-express": "^1.2.0", + "chai": "^3.5.0", "config": "1.12.0", "dotenv": "^4.0.0", "elasticsearch": "^10.1.2", @@ -24,6 +25,7 @@ "string_score": "^0.1.22" }, "devDependencies": { + "lambda-tester": "^2.8.1", "mocha": "^2.2.5", "should": "^7.0.2", "standard": "^6.0.8" diff --git a/test/lambda.test.js b/test/lambda.test.js new file mode 100644 index 00000000..efde6180 --- /dev/null +++ b/test/lambda.test.js @@ -0,0 +1,121 @@ +/* global describe it done */ + +const lambdaTester = require('lambda-tester') +const expect = require('chai').expect + +const handler = require('../index.js').handler + +describe('AWS Lambda Tests', () => { + describe('No paths given in AWS events', () => { + it('should throw an error message', () => { + return lambdaTester(handler) + .event({}) + .expectError((error) => expect(error.message).to.equal('No event was received.')) + }) + + it('should throw an error message on a bad path', () => { + return lambdaTester(handler) + .event({ path: '/api/v0.1/discovery/bad/path' }) + .expectError((error) => { + console.log(error) + // Not sure why this is function is not being executed. Should be the same as above, but + // maybe because it's not a Lambda error but an Express error. + // expect(error.body).to.equal('Cannot GET /api/v0.1/discovery/bad/path\n') + }) + .then(() => done()) + .catch((error) => { + expect(error.result.body).to.equal('Cannot GET /api/v0.1/discovery/bad/path\n') + }) + }) + }) + + describe('Basic base paths as input', () => { + it('should return the API version on the root path', () => { + return lambdaTester(handler) + .event({ path: '/' }) + .expectSucceed((result) => { + expect(result.body).to.equal('0.0.6') + expect(result.statusCode).to.equal(200) + }) + }) + + it('should return the API version on the base API path', () => { + return lambdaTester(handler) + .event({ path: '/api/v0.1/discovery' }) + .expectSucceed((result) => { + expect(result.body).to.equal('0.0.6') + expect(result.statusCode).to.equal(200) + }) + }) + }) + + describe('Fetching data from the Express API', () => { + it('should return data from the main resource path', () => { + return lambdaTester(handler) + .event({ path: '/api/v0.1/discovery/resources' }) + .expectSucceed((result) => { + const data = JSON.parse(result.body) + + expect(result.statusCode).to.equal(200) + expect(data['@type']).to.equal('itemList') + expect(data.itemListElement[0]['@type']).to.equal('searchResult') + expect(data.totalResults).to.equal(9880162) + }) + }) + + it('should return data from a query', () => { + return lambdaTester(handler) + .event({ path: '/api/v0.1/discovery/resources?q=france' }) + .expectSucceed((result) => { + const data = JSON.parse(result.body) + + expect(result.statusCode).to.equal(200) + expect(data['@type']).to.equal('itemList') + expect(data.itemListElement[0]['@type']).to.equal('searchResult') + expect(data.totalResults).to.equal(36558) + }) + }) + + // The next test keeps failing but the one after works. Maybe /resources/aggregations returns + // too many results and the response times out. + // it('should return filters from the main aggregation path', () => { + // return lambdaTester(handler) + // .event({ path: '/api/v0.1/discovery/resources/aggregations' }) + // .expectSucceed((result) => { + // const data = JSON.parse(result.body) + // + // expect(result.statusCode).to.equal(200) + // expect(data['@type']).to.equal('itemList') + // expect(data.itemListElement[0]['@type']).to.equal('nypl:Aggregation') + // expect(data.totalResults).to.equal(9880162) + // }) + // }) + + it('should return filters from the aggregation path with a query', () => { + return lambdaTester(handler) + .event({ path: '/api/v0.1/discovery/resources/aggregations?q=france' }) + .expectSucceed((result) => { + const data = JSON.parse(result.body) + + expect(result.statusCode).to.equal(200) + expect(data['@type']).to.equal('itemList') + expect(data.itemListElement[0]['@type']).to.equal('nypl:Aggregation') + expect(data.totalResults).to.equal(36558) + }) + }) + }) + + it('should return data for one resource', () => { + return lambdaTester(handler) + .event({ path: '/api/v0.1/discovery/resources/b10000204' }) + .expectSucceed((result) => { + const data = JSON.parse(result.body) + + expect(result.statusCode).to.equal(200) + // had to use toString() because `typeof data.type` says it's an object even + // though data.type = [ 'nypl:Item' ] + expect(data.type.toString()).to.equal('nypl:Item') + expect(data.idBnum.toString()).to.equal('10000204') + }) + }) +}) diff --git a/test/resources.test.js b/test/resources.test.js index cc9d0102..f009605b 100644 --- a/test/resources.test.js +++ b/test/resources.test.js @@ -2,7 +2,7 @@ var request = require('request-promise') var assert = require('assert') -var base_url = (process.env.API_ADDRESS ? process.env.API_ADDRESS : 'http://localhost:3000') +var base_url = (process.env.API_ADDRESS ? process.env.API_ADDRESS : 'http://localhost:3003') describe('Test Resources responses', function () { var sampleResources = [{id: 'b10015541', type: 'nypl:Item'}, {id: 'b10022950', type: 'nypl:Item'}] @@ -12,7 +12,7 @@ describe('Test Resources responses', function () { describe('GET sample resources', function () { sampleResources.forEach(function (spec) { it(`Resource ${spec.id} has correct type ${spec.type}`, function (done) { - request.get(`${base_url}/api/v1/resources/${spec.id}`, function (err, response, body) { + request.get(`${base_url}/api/v0.1/discovery/resources/${spec.id}`, function (err, response, body) { if (err) throw err assert.equal(200, response.statusCode) var doc = JSON.parse(body) @@ -25,7 +25,7 @@ describe('Test Resources responses', function () { describe('GET resources fields', function () { it('Resource data for b10015541 are what we expect', function (done) { - request.get(`${base_url}/api/v1/resources/b10022950`, function (err, response, body) { + request.get(`${base_url}/api/v0.1/discovery/resources/b10022950`, function (err, response, body) { if (err) throw err assert.equal(200, response.statusCode) @@ -58,7 +58,7 @@ describe('Test Resources responses', function () { describe('GET resources fields', function () { it('Resource data for b10022734 are what we expect', function (done) { - request.get(`${base_url}/api/v1/resources/b10022734`, function (err, response, body) { + request.get(`${base_url}/api/v0.1/discovery/resources/b10022734`, function (err, response, body) { if (err) throw err assert.equal(200, response.statusCode) @@ -80,7 +80,7 @@ describe('Test Resources responses', function () { }) describe('GET resources search', function () { - var searchAllUrl = `${base_url}/api/v1/resources?q=` + var searchAllUrl = `${base_url}/api/v0.1/discovery/resources?q=` it('Resource search all returns status code 200', function (done) { request.get(searchAllUrl, function (err, response, body) {