Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add integration tests and improve mocha config #12

Merged
merged 1 commit into from
Aug 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .mocharc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
ignore: ["test/test-helpers/**/*"],
recursive: true,
require: "test/test-helpers"
}
2 changes: 1 addition & 1 deletion .npmignore
Original file line number Diff line number Diff line change
@@ -1 +1 @@
tests/*
test/*
2 changes: 1 addition & 1 deletion nyc.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';

const defaultExclude = require('@istanbuljs/schema/default-exclude');
const localExclude = ["src/handlers/**"];
const localExclude = [".aws-sam/**/*"];
module.exports = {
all: true,
"check-coverage": true,
Expand Down
25 changes: 25 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@
"@aws-sdk/protocol-http": "^3.127.0",
"@aws-sdk/signature-v4": "^3.130.0",
"axios": ">=0.21.1",
"base64-js": "^1.5.1",
"lz-string": "^1.4.4"
},
"scripts": {
"prettier": "prettier -c src tests",
"prettier:fix": "prettier -cw src tests",
"test": "mocha --require tests/test-helpers --recursive tests/unit",
"prettier": "prettier -c src test",
"prettier:fix": "prettier -cw src test",
"test": "mocha",
"test:coverage": "nyc npm test"
},
"devDependencies": {
Expand Down
3 changes: 3 additions & 0 deletions src/api/opensearch.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ async function getDocument(index, id) {
}

function isVisible(doc) {
if (!doc?.found) {
return false;
}
if (doc?._source.api_model == "FileSet") {
return doc?._source?.visibility !== "Private";
} else {
Expand Down
10 changes: 10 additions & 0 deletions src/handlers/middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const base64 = require("base64-js");

function decodeEventBody(event) {
if (!event.isBase64Encoded) return event;
event.body = new Buffer.from(base64.toByteArray(event.body)).toString();
event.isBase64Encoded = false;
return event;
}

module.exports = { decodeEventBody };
2 changes: 2 additions & 0 deletions src/handlers/search.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const { decodeEventBody } = require("./middleware");
const { baseUrl } = require("../helpers");
const { modelsToTargets, validModels } = require("../api/request/models");
const { search } = require("../api/opensearch");
Expand Down Expand Up @@ -25,6 +26,7 @@ const getSearch = async (event) => {
};

const postSearch = async (event) => {
event = decodeEventBody(event);
const eventBody = JSON.parse(event.body);

const requestedModels =
Expand Down
12 changes: 8 additions & 4 deletions src/helpers.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
const gatewayRe = /execute-api.[a-z]+-[a-z]+-\d+.amazonaws.com/;

function getHeader(event, name) {
return event.headers[name] || event.headers[name.toLowerCase()];
}

function baseUrl(event) {
const scheme = event.headers["X-Forwarded-Proto"];
const scheme = getHeader(event, "X-Forwarded-Proto");

// The localhost check only matters in dev mode, but it's
// really inconvenient not to have it
const host =
event.requestContext.domainName === "localhost"
? event.headers["Host"].split(/:/)[0]
? getHeader(event, "Host").split(/:/)[0]
: event.requestContext.domainName;
const port = event.headers["X-Forwarded-Port"];
const port = getHeader(event, "X-Forwarded-Port");

let result = new URL(`${scheme}://${host}:${port}`);
const stage = event.requestContext?.stage;
Expand All @@ -20,4 +24,4 @@ function baseUrl(event) {
return result.toString();
}

module.exports = { baseUrl };
module.exports = { baseUrl, getHeader };
File renamed without changes.
File renamed without changes.
93 changes: 93 additions & 0 deletions test/integration/get-doc.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"use strict";

const chai = require("chai");
const expect = chai.expect;

describe("Doc retrieval routes", () => {
helpers.saveEnvironment();
const mock = helpers.mockIndex();

describe("GET /works/{id}", () => {
const { handler } = require("../../src/handlers/get-work-by-id");

it("retrieves a single work", async () => {
mock
.get("/dc-v2-work/_doc/1234")
.reply(200, helpers.testFixture("mocks/work-1234.json"));

const { event } = helpers
.mockEvent("GET", "/works/{id}")
.pathParams({ id: 1234 });
const result = await handler(event);
expect(result.statusCode).to.eq(200);

const resultBody = JSON.parse(result.body);
expect(resultBody.data.api_model).to.eq("Work");
expect(resultBody.data.id).to.eq("1234");
});

it("404s a missing work", async () => {
mock
.get("/dc-v2-work/_doc/1234")
.reply(200, helpers.testFixture("mocks/missing-work-1234.json"));

const { event } = helpers
.mockEvent("GET", "/works/{id}")
.pathParams({ id: 1234 });
const result = await handler(event);
expect(result.statusCode).to.eq(404);
});

it("404s an unpublished work", async () => {
mock
.get("/dc-v2-work/_doc/1234")
.reply(200, helpers.testFixture("mocks/unpublished-work-1234.json"));

const { event } = helpers
.mockEvent("GET", "/works/{id}")
.pathParams({ id: 1234 });
const result = await handler(event);
expect(result.statusCode).to.eq(404);
});
});

describe("GET /collections/{id}", () => {
const { handler } = require("../../src/handlers/get-collection-by-id");

it("retrieves a single collection", async () => {
mock
.get("/dc-v2-collection/_doc/1234")
.reply(200, helpers.testFixture("mocks/collection-1234.json"));

const { event } = helpers
.mockEvent("GET", "/collections/{id}")
.pathParams({ id: 1234 });
const result = await handler(event);
expect(result.statusCode).to.eq(200);

const resultBody = JSON.parse(result.body);
expect(resultBody.data.api_model).to.eq("Collection");
expect(resultBody.data.id).to.eq("1234");
});
});

describe("GET /file-sets/{id}", () => {
const { handler } = require("../../src/handlers/get-file-set-by-id");

it("retrieves a single file-set", async () => {
mock
.get("/dc-v2-file-set/_doc/1234")
.reply(200, helpers.testFixture("mocks/fileset-1234.json"));

const { event } = helpers
.mockEvent("GET", "/file-sets/{id}")
.pathParams({ id: 1234 });
const result = await handler(event);
expect(result.statusCode).to.eq(200);

const resultBody = JSON.parse(result.body);
expect(resultBody.data.api_model).to.eq("FileSet");
expect(resultBody.data.id).to.eq("1234");
});
});
});
113 changes: 113 additions & 0 deletions test/integration/search.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
"use strict";

const chai = require("chai");
const expect = chai.expect;
const searchHandlers = require("../../src/handlers/search");
const RequestPipeline = require("../../src/api/request/pipeline");

describe("Search routes", () => {
helpers.saveEnvironment();
const mock = helpers.mockIndex();

describe("POST /search/{targets}", () => {
const handler = searchHandlers.postSearch;
const originalQuery = { query: { match_all: {} } };
const authQuery = new RequestPipeline(originalQuery).authFilter().toJson();

it("performs a works search by default", async () => {
mock
.post("/dc-v2-work/_search", authQuery)
.reply(200, helpers.testFixture("mocks/search.json"));
const { event } = helpers
.mockEvent("POST", "/search")
.body(originalQuery);
const result = await handler(event);
expect(result.statusCode).to.eq(200);

const resultBody = JSON.parse(result.body);
expect(resultBody).to.include.keys(["data", "pagination"]);
expect(resultBody.data.length).to.eq(10);
});

it("performs a search on specified models", async () => {
mock
.post("/dc-v2-work,dc-v2-collection/_search", authQuery)
.reply(200, helpers.testFixture("mocks/search-multiple-targets.json"));
const { event } = helpers
.mockEvent("POST", "/search/{models}")
.pathParams({ models: "works,collections" })
.body(originalQuery)
.base64Encode();
const result = await handler(event);
expect(result.statusCode).to.eq(200);

const resultBody = JSON.parse(result.body);
expect(resultBody).to.include.keys(["data", "pagination"]);
expect(resultBody.data.length).to.eq(10);
});

it("errors if invalid models specified", async () => {
const { event } = helpers
.mockEvent("POST", "/search/{models}")
.pathParams({ models: "works,collections,blargh" })
.body(originalQuery);
const result = await handler(event);
expect(result.statusCode).to.eq(400);

const resultBody = JSON.parse(result.body);
expect(resultBody.message).to.eq(
"Invalid models requested: works,collections,blargh"
);
});
});

describe("GET /search", () => {
const handler = searchHandlers.getSearch;
const originalQuery = { query: { match_all: {} }, size: 10, from: 0 };
const authQuery = new RequestPipeline(originalQuery).authFilter().toJson();
const searchToken =
"N4IgRg9gJgniBcoCOBXApgJzokBbAhgC4DGAFgPr4A2VCwAvvQDQgDOAlgF5oICMADMzzQ0VVggDaIAO4QMAa3EBdekA";

it("requires a searchToken", async () => {
const { event } = helpers.mockEvent("GET", "/search");
const result = await handler(event);
expect(result.statusCode).to.eq(400);
const resultBody = JSON.parse(result.body);
expect(resultBody.message).to.eq("searchToken parameter is required");
});

it("requires a valid searchToken", async () => {
const { event } = helpers
.mockEvent("GET", "/search")
.queryParams({ searchToken: "Ceci n'est pas une searchToken" });
const result = await handler(event);
expect(result.statusCode).to.eq(400);
const resultBody = JSON.parse(result.body);
expect(resultBody.message).to.eq("searchToken is invalid");
});

it("performs a search using a searchToken and page number", async () => {
mock
.post("/dc-v2-work/_search", authQuery)
.reply(200, helpers.testFixture("mocks/search.json"));

const { event } = helpers
.mockEvent("GET", "/search")
.queryParams({ searchToken, page: 1 });
const result = await handler(event);
expect(result.statusCode).to.eq(200);
});

it("defaults to page 1", async () => {
mock
.post("/dc-v2-work/_search", authQuery)
.reply(200, helpers.testFixture("mocks/search.json"));

const { event } = helpers
.mockEvent("GET", "/search")
.queryParams({ searchToken });
const result = await handler(event);
expect(result.statusCode).to.eq(200);
});
});
});
Loading