Skip to content
Draft
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
3 changes: 1 addition & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,5 @@ jobs:
command: npm test
- store_artifacts:
path: coverage
prefix: coverage
- codecov/upload:
file: coverage/coverage-final.json
file: coverage/coverage-final.json
18 changes: 12 additions & 6 deletions db-init/init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,20 @@ CREATE TABLE IF NOT EXISTS tanks (
name VARCHAR(255) NOT NULL,
status VARCHAR(255) NOT NULL,
in_use BOOLEAN NOT NULL,
disabled BOOLEAN NOT NULL,
update_user INTEGER NULL
);

CREATE TABLE IF NOT EXISTS tanks_audit (
id SERIAL NOT NULL PRIMARY KEY,
operation VARCHAR(6) NOT NULL,
time_stamp TIMESTAMPTZ NOT NULL,

tanks_id INTEGER NOT NULL,
name VARCHAR(255) NOT NULL,
status VARCHAR(255) NOT NULL,
in_use BOOLEAN NOT NULL,
disabled BOOLEAN NOT NULL,
update_user INTEGER NULL
);

Expand Down Expand Up @@ -191,10 +193,14 @@ CREATE TABLE IF NOT EXISTS tasks_audit (

-- the batch ID and action name of all open tasks
CREATE VIEW open_tasks AS
SELECT actions.name AS action_name,
tasks.batch_id
FROM actions, tasks
WHERE tasks.action_id=actions.id AND
SELECT
actions.name AS action_name,
actions.classname,
tasks.batch_id,
tasks.action_id
FROM actions,
tasks
WHERE tasks.action_id = actions.id AND
tasks.completed_on IS NULL;
-- EXAMPLE:
-- action_name | batch_id
Expand Down Expand Up @@ -222,7 +228,7 @@ WHERE batches.tank_id=tanks.id AND

-- Gets the most recent info for each batch
CREATE VIEW most_recent_batch_info AS
SELECT pressure, temperature, SG, PH, ABV, batch_id
SELECT DISTINCT pressure, temperature, SG, PH, ABV, batch_id
FROM versions
INNER JOIN (
SELECT Max(measured_on)
Expand Down
44 changes: 44 additions & 0 deletions package-lock.json

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

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"husky": {
"hooks": {
"pre-commit": "lint-staged",
"pre-push": "npm run build && npm run test",
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
},
Expand All @@ -61,7 +62,7 @@
},
"globals": {
"ts-jest": {
"tsConfigFile": "tsconfig.json"
"tsConfig": "tsconfig.json"
}
},
"testMatch": [
Expand All @@ -85,6 +86,7 @@
"jest": "^24.5.0",
"joi": "^13.4.0",
"jsonwebtoken": "^8.3.0",
"morgan": "^1.9.1",
"papaparse": "^4.6.3",
"pg": "^7.4.3",
"ts-jest": "^24.0.0",
Expand All @@ -103,6 +105,7 @@
"@types/is": "0.0.21",
"@types/joi": "^14.0.0",
"@types/jsonwebtoken": "^8.3.0",
"@types/morgan": "^1.7.37",
"@types/node": "^10.12.9",
"@types/pg": "^7.4.11",
"chai": "^4.1.2",
Expand Down
183 changes: 183 additions & 0 deletions src/components/tanks/__tests__/controller.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import Boom from "boom";
import { NextFunction, Request, RequestHandler, Response } from "express";
import { TankController } from "../controller";
import { Tank } from "../types";

describe("Tank controller", () => {
let tableName: string;
let controller: TankController;
let tank: Tank;
let rows: Tank[];
let keys: string[] = ["keys"];
let values: string[] = ["values"];
let escapes: string[] = ["escapes"];
let error: string;
let request: any = {};
let response: any = {};
const nextFunction: any = {};
let json: any;
let send: any;

beforeEach(() => {
tableName = "tanks";
controller = new TankController(tableName);
tank = {
disabled: false,
id: 1,
in_use: true,
name: "rad-tank",
status: "brewing",
};
rows = [tank];

keys = ["keys"];
values = ["values"];
escapes = ["escapes"];

error = "ERROR";

json = jest.fn();
send = jest.fn();

request = {
body: {
escapes, keys, values,
},
params: {
id: tank.id,
},
};
response = {
status: jest.fn().mockImplementation(() => ({
json,
send,
})),
};
});

describe("getTanks", () => {
it("success", async () => {
controller.read = jest.fn().mockResolvedValueOnce({ rows });
await controller.getTanks(request as Request, response as Response);
expect(response.status).toHaveBeenCalledWith(200);
expect(json).toHaveBeenCalledWith(rows);
});

it("error", async () => {
controller.read = jest.fn().mockRejectedValueOnce(error);
await controller.getTanks(request as Request, response as Response);
expect(response.status).toHaveBeenCalledWith(500);
expect(send).toHaveBeenCalledWith(Boom.badImplementation(error));
});
});

describe("getTank", () => {
it("success", async () => {
controller.readById = jest.fn().mockResolvedValueOnce({ rows });
await controller.getTank(request as Request, response as Response, nextFunction as NextFunction);
expect(response.status).toHaveBeenCalledWith(200);
expect(json).toHaveBeenCalledWith(tank);
});

it("error", async () => {
controller.readById = jest.fn().mockRejectedValueOnce(error);
await controller.getTank(request as Request, response as Response, nextFunction as NextFunction);
expect(response.status).toHaveBeenCalledWith(400);
expect(send).toHaveBeenCalledWith(Boom.badImplementation(error));
});
});

describe("getTankMonitoring", () => {
it("success", async () => {
controller.pool.query = jest.fn().mockResolvedValueOnce({ rows });
await controller.getTankMonitoring(request as Request, response as Response, nextFunction as NextFunction);
expect(response.status).toHaveBeenCalledWith(200);
expect(json).toHaveBeenCalledWith(rows);
});

it("error", async () => {
controller.pool.query = jest.fn().mockRejectedValueOnce(error);
await controller.getTankMonitoring(request as Request, response as Response, nextFunction as NextFunction);
expect(response.status).toHaveBeenCalledWith(500);
expect(send).toHaveBeenCalledWith(Boom.badImplementation(error));
});
});

describe("createTank", () => {
beforeAll(() => {
controller.splitObjectKeyVals = jest.fn().mockReturnValue({ keys, values, escapes });
});

it("success", async () => {
controller.create = jest.fn().mockResolvedValueOnce({ rows });
await controller.createTank(request as Request, response as Response);
expect(response.status).toHaveBeenCalledWith(201);
expect(json).toHaveBeenCalledWith(tank);
});

it("error", async () => {
controller.create = jest.fn().mockRejectedValueOnce(error);
await controller.createTank(request as Request, response as Response);
expect(response.status).toHaveBeenCalledWith(400);
expect(send).toHaveBeenCalledWith(Boom.badRequest(error));
});
});

describe("updateTank", () => {
let query: string;
let idx: string;

beforeAll(() => {
query = "SELECT * FROm table";
idx = "2";

controller.splitObjectKeyVals = jest.fn().mockReturnValue({ keys, values });
controller.buildUpdateString = jest.fn().mockReturnValue({ query, idx });
});

it("success", async () => {
controller.update = jest.fn().mockResolvedValueOnce({ rows });
await controller.updateTank(request as Request, response as Response, nextFunction as NextFunction);
expect(response.status).toHaveBeenCalledWith(200);
expect(json).toHaveBeenCalledWith(rows);
});

it("error", async () => {
controller.update = jest.fn().mockRejectedValueOnce(error);
await controller.updateTank(request as Request, response as Response, nextFunction as NextFunction);
expect(response.status).toHaveBeenCalledWith(500);
expect(send).toHaveBeenCalledWith(Boom.badImplementation(error));
});

it("empty request body error", async () => {
request.body = null;

controller.update = jest.fn().mockRejectedValueOnce(error);
await controller.updateTank(request as Request, response as Response, nextFunction as NextFunction);
expect(response.status).toHaveBeenCalledWith(400);
expect(send).toHaveBeenCalledWith(Boom.badRequest("Request does not match valid form"));
});
});

describe("deleteTank", () => {
let rowCount: number;

beforeEach(() => {
rowCount = 4;
});

it("success", async () => {
controller.deleteById = jest.fn().mockResolvedValueOnce({rowCount});
await controller.deleteTank(request as Request, response as Response, nextFunction as NextFunction);
expect(response.status).toHaveBeenCalledWith(200);
expect(json).toHaveBeenCalledWith(`Successfully deleted tank (id=${tank.id})`);
});

it("error", async () => {
controller.deleteById = jest.fn().mockRejectedValueOnce(error);
await controller.deleteTank(request as Request, response as Response, nextFunction as NextFunction);
expect(response.status).toHaveBeenCalledWith(500);
expect(send).toHaveBeenCalledWith(Boom.badImplementation(error));
});
});
});
38 changes: 26 additions & 12 deletions src/components/tanks/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,20 +82,34 @@ export class TankController extends PostgresController implements ITankControlle
* temperature
*/
const query = `
SELECT action_name, open_tasks.batch_id, batch_name,
tank_name, tank_id, beer_name, pressure, temperature
FROM (
SELECT DISTINCT ON (tanks.id)
tanks.id,
tanks.status,
tanks.name,
action_name,
action_id,
classname,
open_tasks.batch_id,
batch_name,
beer_name,
pressure,
temperature
FROM
(
(
most_recent_batch_info RIGHT JOIN
open_tasks
ON open_tasks.batch_id = most_recent_batch_info.batch_id
)
RIGHT JOIN tank_open_batch
ON open_tasks.batch_id = tank_open_batch.batch_id
)`;
(
most_recent_batch_info RIGHT JOIN open_tasks ON
open_tasks.batch_id = most_recent_batch_info.batch_id
)
RIGHT JOIN tank_open_batch ON
open_tasks.batch_id = tank_open_batch.batch_id
) RIGHT JOIN tanks ON
tanks.id=tank_id
) WHERE tanks.disabled=false
`;
try {
const results = await this.pool.query(query);
res.status(200).json(results.rows);
const { rows } = await this.pool.query(query);
res.status(200).json(rows);
} catch (err) {
res.status(500).send(Boom.badImplementation(err));
}
Expand Down
Loading