Skip to content

Commit

Permalink
Merge effad93 into dd9ed09
Browse files Browse the repository at this point in the history
  • Loading branch information
macfarlandian committed Dec 1, 2020
2 parents dd9ed09 + effad93 commit fba1671
Show file tree
Hide file tree
Showing 10 changed files with 726 additions and 61 deletions.
23 changes: 22 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,31 @@ jobs:
- uses: c-hive/gha-yarn-cache@v1
- run: yarn install --frozen-lockfile
- run: yarn lint
test-spotlight-api:
name: Test Spotlight API
runs-on: ubuntu-latest
defaults:
run:
working-directory: spotlight-api
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: "12.x"
- uses: c-hive/gha-yarn-cache@v1
- run: yarn install --frozen-lockfile
- run: yarn test --coverage
- name: Coveralls
uses: coverallsapp/github-action@master
with:
flag-name: spotlight-api
github-token: ${{ secrets.GITHUB_TOKEN }}
parallel: true
path-to-lcov: spotlight-api/coverage/lcov.info
finish-coveralls:
name: Finish Coveralls
runs-on: ubuntu-latest
needs: [test-public-dashboard, test-spotlight-client]
needs: [test-public-dashboard, test-spotlight-client, test-spotlight-api]
steps:
- name: Coveralls Parallel Finished
uses: coverallsapp/github-action@master
Expand Down
51 changes: 51 additions & 0 deletions spotlight-api/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Recidiviz - a data platform for criminal justice reform
// Copyright (C) 2020 Recidiviz, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =============================================================================

const express = require("express");
const cors = require("cors");
const morgan = require("morgan");
const helmet = require("helmet");
const zip = require("express-easy-zip");
const api = require("./routes/api");

const app = express();

app.use(cors());

const port = process.env.PORT || 3001;
app.set("port", port);

app.use(morgan("dev"));
app.use(helmet());
app.use(zip());

app.get("/api/:tenantId/download", api.download);
app.get("/api/:tenantId/parole", api.parole);
app.get("/api/:tenantId/prison", api.prison);
app.get("/api/:tenantId/probation", api.probation);
app.get("/api/:tenantId/race", api.race);
app.get("/api/:tenantId/sentencing", api.sentencing);
app.post("/api/:tenantId/public", express.json(), api.metricsByName);

// An App Engine-specific API for handling warmup requests on new instance initialization
app.get("/_ah/warmup", () => {
// The server automatically launches initialization of the metric cache, so nothing is needed here
// eslint-disable-next-line no-console
console.log("Responding to warmup request...");
});

module.exports = { app, port };
97 changes: 97 additions & 0 deletions spotlight-api/app.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Recidiviz - a data platform for criminal justice reform
// Copyright (C) 2020 Recidiviz, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =============================================================================

const request = require("supertest");
const {
getFirstRecordFromFixture,
expectedMetricsByGroup,
} = require("./testUtils");
const { app } = require("./app");

jest.mock("./utils/demoMode", () => {
return {
// use our filesystem data fixtures rather than hitting GCS
isDemoMode: jest.fn().mockReturnValue(true),
};
});

afterEach(() => {
jest.resetAllMocks();
});

test.each([
["parole", expectedMetricsByGroup.parole],
["prison", expectedMetricsByGroup.prison],
["probation", expectedMetricsByGroup.probation],
["race", expectedMetricsByGroup.race],
["sentencing", expectedMetricsByGroup.sentencing],
])("/%s endpoint", async (endpoint, expectedMetrics) => {
const response = await request(app).get(`/api/test_id/${endpoint}`);

expect(response.status).toBe(200);
expect(response.get("Content-Type")).toMatch("json");
expectedMetrics.forEach((metricName) => {
expect(response.body[metricName]).toContainEqual(
getFirstRecordFromFixture(`${metricName}.json`)
);
});
});

test("/public endpoint", async () => {
const response = await request(app)
.post("/api/test_id/public")
.send({
metrics: ["racial_disparities", "incarceration_lengths_by_demographics"],
});
expect(response.status).toBe(200);
expect(response.get("Content-Type")).toMatch("json");
expect(response.body.racial_disparities).toContainEqual(
getFirstRecordFromFixture("racial_disparities.json")
);
expect(response.body.incarceration_lengths_by_demographics).toContainEqual(
getFirstRecordFromFixture("incarceration_lengths_by_demographics.json")
);
});

test("cannot GET /public", async () => {
const response = await request(app).get("/api/test_id/public");
expect(response.status).toBe(404);
});

test("/public requires request format", async () => {
// metrics must be included in body
const responseMissing = await request(app).post("/api/test_id/public");
expect(responseMissing.status).toBe(400);
expect(responseMissing.body.error).toMatch("missing metrics");

// metrics must be an array
const responseWrongFormat = await request(app)
.post("/api/test_id/public")
.send({ metrics: "racial_disparities" });
expect(responseWrongFormat.status).toBe(400);
expect(responseWrongFormat.body.error).toMatch("missing metrics");
});

test("/public errors on nonexistent files", async () => {
const response = await request(app)
.post("/api/test_id/public")
.send({
metrics: ["racial_disparities", "this_file_does_not_exist"],
});
expect(response.status).toBe(500);
expect(response.body.error).toMatch("not registered");
});
Loading

0 comments on commit fba1671

Please sign in to comment.