-
-
Notifications
You must be signed in to change notification settings - Fork 33
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
Calculation Endpoints: Adds Validation and Tests #1521
Changes from 5 commits
477aed8
3294ba2
b00f932
a7d28bf
2a85e69
c153a88
94e9032
7f1deff
f1dcd6d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,7 @@ on: | |
branches: | ||
- develop | ||
jobs: | ||
build: | ||
test: | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
|
@@ -39,6 +39,8 @@ jobs: | |
echo "APPLICATIONINSIGHTS_CONNECTION_STRING=InstrumentationKey=00000000-0000-0000-0000-000000000000;IngestionEndpoint=https://westus-0.in.applicationinsights.azure.com/;LiveEndpoint=https://westus.livediagnostics.monitor.azure.com/" >> .env | ||
echo "[email protected]" >> .env | ||
echo "SECURITY_ADMIN_PASSWORD=Dogfood1!" >> .env | ||
echo "[email protected]" >> .env | ||
echo "ADMIN_PASSWORD=Dogfood1!" >> .env | ||
echo "SQL_SERVER_NAME=localhost" >> .env | ||
echo "SQL_SERVER_PORT=1434" >> .env | ||
echo "SQL_DATABASE_NAME=tdmtestdb" >> .env | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -67,31 +67,33 @@ APPLICATIONINSIGHTS_CONNECTION_STRING= | |
############################################################### | ||
## Testing (Comment out ALL the above variables) ## | ||
############################################################### | ||
# TEST_ENV= | ||
# TEST_ENV=true | ||
|
||
# PORT= | ||
# NODE_OPTIONS= | ||
# PORT=5002 | ||
# NODE_OPTIONS=--trace-deprecation | ||
|
||
# JWT_SECRET_KEY= | ||
# JWT_SECRET_KEY=testingSecretKey | ||
|
||
# CLIENT_URL= | ||
# SERVER_URL= | ||
# CLIENT_URL=http://localhost:3001 | ||
# SERVER_URL=http://localhost:5002 | ||
|
||
# SENDGRID_API_KEY= | ||
# EMAIL_SENDER= | ||
# EMAIL_PUBLIC_COMMENT_LA_CITY= | ||
# EMAIL_PUBLIC_COMMENT_WEB_TEAM= | ||
# SENDGRID_API_KEY=SG.testAPIkey | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since the testing environment is local and isolated, I added the dummy variables with fake API keys into the example .env file since we already have them in the CI file There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this mean tdm-server-tests can run with .env.example? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nope! I just changed it because VSCode has better highlighting with the new name. Also included the variables so new members could run the tests easily without the formal variables. I tried to get this to work unsuccessfully with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wouldn't edit the .env file. Instead, switch between files. That way the server tests can have one. Local dev can have another, prod, etc. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was looking into that but had some concerns. Since the container has a single database that is restored before every test suite (i.e. before every test file), the logic that handles this will call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It means everywhere we use dotenv should probably go through a tdmenv wrapper. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so new issue for refactoring codebase with this ^ |
||
# EMAIL_SENDER=[email protected] | ||
# EMAIL_PUBLIC_COMMENT_LA_CITY=[email protected] | ||
# EMAIL_PUBLIC_COMMENT_WEB_TEAM=[email protected] | ||
|
||
# APPLICATIONINSIGHTS_CONNECTION_STRING= | ||
# APPLICATIONINSIGHTS_CONNECTION_STRING=InstrumentationKey=00000000-0000-0000-0000-000000000000;IngestionEndpoint=https://westus-0.in.applicationinsights.azure.com/;LiveEndpoint=https://westus.livediagnostics.monitor.azure.com/ | ||
|
||
## Server Test Accounts | ||
# SECURITY_ADMIN_EMAIL= | ||
# SECURITY_ADMIN_PASSWORD= | ||
# # Server Test Accounts | ||
# [email protected] | ||
# SECURITY_ADMIN_PASSWORD=Dogfood1! | ||
# [email protected] | ||
# ADMIN_PASSWORD=Dogfood1! | ||
|
||
## testingcontainer Environment Variables | ||
# SQL_SERVER_NAME= | ||
# SQL_SERVER_PORT= | ||
# SQL_DATABASE_NAME= | ||
# SQL_USER_NAME= | ||
# SQL_PASSWORD= | ||
# SQL_ENCRYPT = | ||
# # testingcontainer Environment Variables | ||
# SQL_SERVER_NAME=localhost | ||
# SQL_SERVER_PORT=1434 | ||
# SQL_DATABASE_NAME=tdmtestdb | ||
# SQL_USER_NAME=sa | ||
# SQL_PASSWORD=TestPassw0rd | ||
# SQL_ENCRYPT = false |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
const request = require("supertest"); | ||
const { | ||
setupServer, | ||
teardownServer | ||
} = require("../_jest-setup_/utils/server-setup"); | ||
|
||
let server; | ||
|
||
beforeAll(async () => { | ||
server = await setupServer(); | ||
}); | ||
|
||
afterAll(async () => { | ||
await teardownServer(); | ||
}); | ||
|
||
describe("Calculation API Endpoints", () => { | ||
////////////////////////////// | ||
// general // | ||
////////////////////////////// | ||
let calcId; // id of the calculation | ||
|
||
// GET "/" all calculations | ||
it("should get all calculations", async () => { | ||
const res = await request(server).get("/api/calculations"); | ||
expect(Array.isArray(res.body)).toBeTruthy(); | ||
res.body.forEach(calc => { | ||
expect(calc).toHaveProperty("id"); | ||
expect(calc).toHaveProperty("name"); | ||
expect(calc).toHaveProperty("description"); | ||
expect(calc).toHaveProperty("deprecated"); | ||
expect(calc).toHaveProperty("dateCreated"); | ||
expect(calc).toHaveProperty("dateModified"); | ||
}); | ||
calcId = res.body[0].id; | ||
}); | ||
|
||
// GET "/:calcId" calculation by id | ||
it("should get calculation by id", async () => { | ||
const res = await request(server).get(`/api/calculations/${calcId}`); | ||
expect(res.statusCode).toEqual(200); | ||
expect(res.body.id).toEqual(calcId); | ||
}); | ||
|
||
// GET "/:calcId" calculation by inexistent id | ||
it("should get calculation by id", async () => { | ||
const res = await request(server).get(`/api/calculations/9999999`); | ||
expect(res.statusCode).toEqual(404); | ||
}); | ||
|
||
// GET "/:calcId/rules" all rules for a calculation | ||
it("should get all rules for a calculation", async () => { | ||
const res = await request(server).get(`/api/calculations/${calcId}/rules`); | ||
expect(res.statusCode).toEqual(200); | ||
expect(Array.isArray(res.body)).toBeTruthy(); | ||
res.body.forEach(calc => { | ||
expect(calc).toHaveProperty("id"); | ||
expect(calc).toHaveProperty("calculationId"); | ||
expect(calc.calculationId).toEqual(calcId); | ||
}); | ||
}); | ||
|
||
////////////////////////////// | ||
// admin endpoints // | ||
////////////////////////////// | ||
let adminToken; // token of the admin | ||
let newCalcId; // id of the new calculation | ||
|
||
// POST "accounts/login/:email?" Login as admin to get token | ||
it("should login as a security admin", async () => { | ||
const res = await request(server).post("/api/accounts/login").send({ | ||
email: process.env.ADMIN_EMAIL, | ||
password: process.env.ADMIN_PASSWORD | ||
}); | ||
expect(res.statusCode).toEqual(200); | ||
expect(res.body).toHaveProperty("token"); | ||
adminToken = res.body.token; | ||
}); | ||
|
||
// POST "/" Create a calculation (Admin only) | ||
it("should create a calculation", async () => { | ||
const res = await request(server) | ||
.post("/api/calculations") | ||
.set("Authorization", `Bearer ${adminToken}`) | ||
.send({ | ||
name: "Test Name", | ||
description: "Test Description", | ||
deprecated: false | ||
}); | ||
expect(res.statusCode).toEqual(201); | ||
expect(res.body).toHaveProperty("id"); | ||
newCalcId = res.body.id; | ||
ZekeAranyLucas marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}); | ||
|
||
// PUT "/:id" Update calculation (Admin only) | ||
it("should update a calculation", async () => { | ||
const res = await request(server) | ||
.put(`/api/calculations/${newCalcId}`) | ||
.set("Authorization", `Bearer ${adminToken}`) | ||
.send({ | ||
name: "New Test Name", | ||
description: "New Test Description", | ||
deprecated: true, | ||
id: newCalcId | ||
}); | ||
expect(res.statusCode).toEqual(200); | ||
}); | ||
|
||
// PUT "/:id" Update calculation using inexistent id (Admin only) | ||
//TODO: this endpoint logic needs error handling for inexistent ids | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding issue - inexistent id's also returned 200 status codes There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed |
||
|
||
// DELETE "/:id" Delete a calculation (Admin only) | ||
it("should delete a calculation", async () => { | ||
const res = await request(server) | ||
.delete(`/api/calculations/${newCalcId}`) | ||
.set("Authorization", `Bearer ${adminToken}`); | ||
expect(res.statusCode).toEqual(200); | ||
}); | ||
|
||
// DELETE "/:id" Delete a calculation using inexistent id (Admin only) | ||
//TODO: this endpoint needs error handling for inexistent ids | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider adding issue - inexistent id's also returned 200 status codes There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed. |
||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
module.exports = { | ||
type: "object", | ||
required: ["name", "description", "deprecated"], | ||
properties: { | ||
name: { | ||
type: "string", | ||
minLength: 1, | ||
pattern: "^[a-zA-Z '.-]+$" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What will happen in the UX if the user breaks validation? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the create, update , delete, calculation endpoints are not used by the client currently - future implementation. I think we mostly use the GET endpoints. Also, there is only '1' calculation in the db currently. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then how do you know if this validation is right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The answer could be you are making stubs, but then the code needs a comment that the regex are not spec'ed. I am looking for answers to why in the code. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And unused endpoints are risky. I am inclined to delete dead code as a security risk. Is there a plan for when to add it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. excellent question. Ill have to refer to @entrotech not to mention validation logic may even require numbers to be valid (not included currently) |
||
}, | ||
description: { | ||
type: "string", | ||
minLength: 1, | ||
pattern: "^[a-zA-Z '.-]+$" | ||
}, | ||
deprecated: { | ||
type: "boolean" | ||
} | ||
} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
module.exports = { | ||
type: "object", | ||
required: ["name", "description", "deprecated", "id"], | ||
properties: { | ||
name: { | ||
type: "string", | ||
minLength: 1, | ||
pattern: "^[a-zA-Z '.-]+$" | ||
}, | ||
description: { | ||
type: "string", | ||
minLength: 1, | ||
pattern: "^[a-zA-Z '.-]+$" | ||
}, | ||
deprecated: { | ||
type: "boolean" | ||
}, | ||
id: { | ||
type: "integer" | ||
} | ||
} | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,7 +38,7 @@ const post = async item => { | |
request.output("id", mssql.Int, null); | ||
const response = await request.execute("Calculation_Insert"); | ||
|
||
return response.outputParameters; | ||
return response.output; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was returning an undefined variable so I used the VSCode debugger to check what the property was supposed to be - it's been rectified now and actually returns the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice! |
||
} catch (err) { | ||
return Promise.reject(err); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed the file name from
.example.env -> .env.example as it was before (better naming)