Skip to content

Commit

Permalink
build real unit and integration tests
Browse files Browse the repository at this point in the history
  • Loading branch information
devksingh4 committed Aug 10, 2024
1 parent 408d3d2 commit 8c3d53c
Show file tree
Hide file tree
Showing 15 changed files with 925 additions and 81 deletions.
10 changes: 9 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,13 @@
"alwaysTryTypes": true
}
}
}
},
"overrides": [
{
"files": ["*.test.ts", "*.testdata.ts"],
"rules": {
"@typescript-eslint/no-explicit-any": "off"
}
}
]
}
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,15 @@ deploy_dev: check_account_dev build

install_test_deps:
yarn -D
pip install -r $(integration_test_directory_root)/requirements.txt

test_live_integration: install_test_deps
APPLICATION_KEY=$(application_key) pytest -rP $(integration_test_directory_root)
yarn test:live

test_unit: install_test_deps
yarn typecheck
yarn lint
yarn prettier
yarn test:unit

dev_health_check:
curl -f https://$(application_key).aws.qa.acmuiuc.org/api/v1/healthz
Expand Down
14 changes: 12 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,21 @@
"prettier": "prettier --check src/*.ts src/**/*.ts",
"prettier:write": "prettier --write src/*.ts src/**/*.ts",
"prepare": "node .husky/install.mjs || true",
"lint-staged": "lint-staged"
"lint-staged": "lint-staged",
"test:unit": "cross-env APPLICATION_KEY=infra-events-api vitest run tests/unit",
"test:unit-ui": "yarn test:unit --ui",
"test:unit-watch": "cross-env APPLICATION_KEY=infra-events-api vitest tests/unit",
"test:live": "cross-env APPLICATION_KEY=infra-events-api vitest run tests/live",
"test:live-ui": "yarn test:live --ui"
},
"devDependencies": {
"@tsconfig/node20": "^20.1.4",
"@types/node": "^22.1.0",
"@types/supertest": "^6.0.2",
"@typescript-eslint/eslint-plugin": "^8.0.1",
"@typescript-eslint/parser": "^8.0.1",
"aws-sdk-client-mock": "^4.0.1",
"cross-env": "^7.0.3",
"esbuild": "^0.23.0",
"eslint": "^8.57.0",
"eslint-config-esnext": "^4.1.0",
Expand All @@ -34,9 +42,11 @@
"lint-staged": "^15.2.8",
"prettier": "^3.3.3",
"request": "^2.88.2",
"supertest": "^7.0.0",
"synp": "^1.9.13",
"tsx": "^4.16.5",
"typescript": "^5.5.4"
"typescript": "^5.5.4",
"vitest": "^2.0.5"
},
"dependencies": {
"@aws-sdk/client-dynamodb": "^3.624.0",
Expand Down
8 changes: 8 additions & 0 deletions src/errors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ export abstract class BaseError<T extends string> extends Error {
toString() {
return `Error ${this.id} (${this.name}): ${this.message}\n\n${this.stack}`;
}
toJson() {
return {
error: true,
name: this.name,
id: this.id,
message: this.message,
};
}
}

export class UnauthorizedError extends BaseError<"UnauthorizedError"> {
Expand Down
1 change: 1 addition & 0 deletions src/routes/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const responseJsonSchema = zodToJsonSchema(
// GET
const getResponseBodySchema = z.array(requestBodySchema);
const getResponseJsonSchema = zodToJsonSchema(getResponseBodySchema);
export type EventGetResponse = z.infer<typeof getResponseBodySchema>;
type EventsGetQueryParams = { upcomingOnly?: boolean };

const dynamoClient = new DynamoDBClient({
Expand Down
17 changes: 17 additions & 0 deletions tests/live/events.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { expect, test } from "vitest";
import { InternalServerError } from "../../src/errors/index.js";
import { EventGetResponse } from "../../src/routes/events.js";

const appKey = process.env.APPLICATION_KEY;
if (!appKey) {
throw new InternalServerError({ message: "No application key found" });
}

const baseEndpoint = `https://${appKey}.aws.qa.acmuiuc.org`;

test("getting events", async () => {
const response = await fetch(`${baseEndpoint}/api/v1/events`);
expect(response.status).toBe(200);

Check failure on line 14 in tests/live/events.test.ts

View workflow job for this annotation

GitHub Actions / Run Live Integration Tests

tests/live/events.test.ts > getting events

AssertionError: expected 502 to be 200 // Object.is equality - Expected + Received - 200 + 502 ❯ tests/live/events.test.ts:14:27
const responseJson = (await response.json()) as EventGetResponse;
expect(responseJson.length).greaterThan(0);
});
16 changes: 16 additions & 0 deletions tests/live/healthz.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { expect, test } from "vitest";
import { InternalServerError } from "../../src/errors/index.js";

const appKey = process.env.APPLICATION_KEY;
if (!appKey) {
throw new InternalServerError({ message: "No application key found" });
}

const baseEndpoint = `https://${appKey}.aws.qa.acmuiuc.org`;

test("healthz", async () => {
const response = await fetch(`${baseEndpoint}/api/v1/healthz`);
expect(response.status).toBe(200);

Check failure on line 13 in tests/live/healthz.test.ts

View workflow job for this annotation

GitHub Actions / Run Live Integration Tests

tests/live/healthz.test.ts > healthz

AssertionError: expected 502 to be 200 // Object.is equality - Expected + Received - 200 + 502 ❯ tests/live/healthz.test.ts:13:27
const responseJson = await response.json();
expect(responseJson).toEqual({ message: "UP" });
});
21 changes: 0 additions & 21 deletions tests/live_integration/conftest.py

This file was deleted.

2 changes: 0 additions & 2 deletions tests/live_integration/requirements.txt

This file was deleted.

9 changes: 0 additions & 9 deletions tests/live_integration/test_events.py

This file was deleted.

3 changes: 0 additions & 3 deletions tests/live_integration/test_healthz.py

This file was deleted.

66 changes: 66 additions & 0 deletions tests/unit/events.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { afterAll, expect, test, beforeEach, vi } from "vitest";
import { ScanCommand, DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { mockClient } from "aws-sdk-client-mock";
import init from "../../src/index.js";
import { EventGetResponse } from "../../src/routes/events.js";
import {
dynamoTableData,
dynamoTableDataUnmarshalled,
dynamoTableDataUnmarshalledUpcomingOnly,
} from "./mockEventData.testdata.js";

const ddbMock = mockClient(DynamoDBClient);

const app = await init();
test("Test getting events", async () => {
ddbMock.on(ScanCommand).resolves({
Items: dynamoTableData as any,
});
const response = await app.inject({
method: "GET",
url: "/api/v1/events",
});
expect(response.statusCode).toBe(200);
const responseDataJson = (await response.json()) as EventGetResponse;
expect(responseDataJson).toEqual(dynamoTableDataUnmarshalled);
});

test("Test dynamodb error handling", async () => {
ddbMock.on(ScanCommand).rejects("Could not get data.");
const response = await app.inject({
method: "GET",
url: "/api/v1/events",
});
expect(response.statusCode).toBe(500);
const responseDataJson = await response.json();
expect(responseDataJson).toEqual({
error: true,
name: "DatabaseFetchError",
id: 106,
message: "Failed to get events from Dynamo table.",
});
});

test("Test upcoming only", async () => {
const date = new Date(2024, 7, 10, 13, 0, 0); // 2024-08-10T17:00:00.000Z, don't ask me why its off a month
vi.setSystemTime(date);
ddbMock.on(ScanCommand).resolves({
Items: dynamoTableData as any,
});
const response = await app.inject({
method: "GET",
url: "/api/v1/events?upcomingOnly=true",
});
expect(response.statusCode).toBe(200);
const responseDataJson = (await response.json()) as EventGetResponse;
expect(responseDataJson).toEqual(dynamoTableDataUnmarshalledUpcomingOnly);
});

afterAll(async () => {
await app.close();
vi.useRealTimers();
});
beforeEach(() => {
ddbMock.reset();
vi.useFakeTimers();
});
17 changes: 17 additions & 0 deletions tests/unit/health.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { afterAll, expect, test } from "vitest";
import init from "../../src/index.js";
import { EventGetResponse } from "../../src/routes/events.js";

const app = await init();
test("Test getting events", async () => {
const response = await app.inject({
method: "GET",
url: "/api/v1/healthz",
});
expect(response.statusCode).toBe(200);
const responseDataJson = (await response.json()) as EventGetResponse;
expect(responseDataJson).toEqual({ message: "UP" });
});
afterAll(async () => {
await app.close();
});
Loading

0 comments on commit 8c3d53c

Please sign in to comment.