Skip to content

Commit

Permalink
feat: add db cleanup for integration testing
Browse files Browse the repository at this point in the history
  • Loading branch information
darraghoriordan committed Mar 13, 2023
1 parent 29aad81 commit dbecd24
Show file tree
Hide file tree
Showing 30 changed files with 558 additions and 297 deletions.
9 changes: 5 additions & 4 deletions apps/backend-e2e/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ module.exports = {
settings: {
["import/parsers"]: { "@typescript-eslint/parser": [".ts", ".tsx"] },
["import/resolver"]: {
typescript: {},
node: {
extensions: [".ts", ".tsx"],
extensions: [".ts", ".tsx", ".js"],
},
},
},
Expand All @@ -28,16 +29,16 @@ module.exports = {
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"plugin:unicorn/recommended",
"plugin:import/errors",
"plugin:import/warnings",
"plugin:import/typescript",
"plugin:unicorn/recommended",
"plugin:eslint-comments/recommended",
"plugin:sonarjs/recommended",
"prettier",
"plugin:promise/recommended",
"plugin:jest/recommended",
"plugin:@darraghor/nestjs-typed/recommended",
"plugin:import/errors",
"plugin:import/warnings",
"plugin:import/typescript",
],
// tsconfigRootDir: __dirname,
root: true,
Expand Down
2 changes: 1 addition & 1 deletion apps/backend-e2e/basic-user-api-test-token.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Il9RUGlhWnJaOVMxSDNpNmhLQXFQeCJ9.eyJpc3MiOiJodHRwczovL3VzZW1pbGxlcmRldi5hdS5hdXRoMC5jb20vIiwic3ViIjoiYXV0aDB8NjNlMGVlNDc0OGNjNmE3ZDQzMmRlMjUwIiwiYXVkIjpbImJhY2tlbmQtYXBpLWF1ZGllbmNlIiwiaHR0cHM6Ly91c2VtaWxsZXJkZXYuYXUuYXV0aDAuY29tL3VzZXJpbmZvIl0sImlhdCI6MTY3ODI1ODI2MiwiZXhwIjoxNjc4MzQ0NjYyLCJhenAiOiJVbzNjZHp3bVpUOWtMenF4TGtJbnZ5cVFZbzRsVWVqMiIsInNjb3BlIjoib3BlbmlkIGVtYWlsIHByb2ZpbGUgb2ZmbGluZV9hY2Nlc3MiLCJndHkiOiJwYXNzd29yZCIsInBlcm1pc3Npb25zIjpbXX0.mUbibCSOGWGdadKBKf8ARYbT9-z3J4KwKMpjMYz0v0rwA4rnZax0-xNHPmWZTUOTWZZMXijyT6uevyPygs9BWzXXKItc5qOQYdIUjRLaeRsUdOewYVWiiA8u7yBug7VloQhHXrUK307fPdtZZ4qJdwzLoNqhvpbaLumWMlO8v8SmzddzHrkggqSsQiMVGvAif4YnfM_v7nR8VH0CaMKM_JxzHULC2swBtpEiSGXBaj331PE7Lsds5eF9fMXvEWRzn6uwa-JVQyFaJN09eAqgb9wpUn0x9wejbkDQb47iddmyJpWq_4IHEbD_d9KAbnIzehPbBmuISJI0Bx6VnLgKuQ","date_received":"2023-03-08T06:51:02.152Z","expires_in":86400,"token_type":"Bearer"}
{"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Il9RUGlhWnJaOVMxSDNpNmhLQXFQeCJ9.eyJpc3MiOiJodHRwczovL3VzZW1pbGxlcmRldi5hdS5hdXRoMC5jb20vIiwic3ViIjoiYXV0aDB8NjNlMGVlNDc0OGNjNmE3ZDQzMmRlMjUwIiwiYXVkIjpbImJhY2tlbmQtYXBpLWF1ZGllbmNlIiwiaHR0cHM6Ly91c2VtaWxsZXJkZXYuYXUuYXV0aDAuY29tL3VzZXJpbmZvIl0sImlhdCI6MTY3ODYyMDgyMCwiZXhwIjoxNjc4NzA3MjIwLCJhenAiOiJVbzNjZHp3bVpUOWtMenF4TGtJbnZ5cVFZbzRsVWVqMiIsInNjb3BlIjoib3BlbmlkIGVtYWlsIHByb2ZpbGUgb2ZmbGluZV9hY2Nlc3MiLCJndHkiOiJwYXNzd29yZCIsInBlcm1pc3Npb25zIjpbXX0.D7SWSfqCKEsfXezd9paIkiNUgi1nm6oaB9ZmEnFbi1FbocbDZojahF2YHyt3C7XIygJx6LP0Bk67KOYxcuDzYBuk-HWAgXBCKM_nipqkyDdUS8fpha6fOdmcBrdYucZsqrvdfFVTmuHtcgqYgYyUpZGT7033-JB2CZYiGnoYIPQaqq00e_QAHAF0X-We8TvCdOUujRmKo0phCHlBxiFJ6HFIf9uhapnfpXU5BJH41Qx0L3A2MNaTNWktOjD2yqm-9SCtESUceIw6zqRzB1kBIsYfk1Wa11ixGDuntIcCHajrU_mbAtAKODfThZPmKrgIjcQk9ci75AhvDVY0P8flpA","date_received":"2023-03-12T11:33:40.353Z","expires_in":86400,"token_type":"Bearer"}
20 changes: 10 additions & 10 deletions apps/backend-e2e/jest-e2e.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"preset": "ts-jest",
"roots": ["<rootDir>/src"],
"testEnvironment": "jsdom",
"extensionsToTreatAsEsm": [".ts"],
"testRegex": ".e2e-spec.ts$",
"setupFiles": ["<rootDir>/src/preRun.ts"],
"globals": {
"ts-jest": {
"useESM": true
"preset": "ts-jest",
"roots": ["<rootDir>/src"],
"testEnvironment": "jsdom",
"extensionsToTreatAsEsm": [".ts"],
"testRegex": ".e2e-spec.ts$",
"setupFiles": ["dotenv/config", "<rootDir>/src/preRun.ts"],
"globals": {
"ts-jest": {
"useESM": true
}
}
}
}
2 changes: 1 addition & 1 deletion apps/backend-e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"@nrwl/node": "15.8.6",
"@types/express": "4.17.17",
"@types/jest": "29.4.0",
"@types/node": "18.15.0",
"@types/node": "18.15.1",
"@types/node-fetch": "2.6.2",
"@types/supertest": "2.0.12",
"@typescript-eslint/eslint-plugin": "5.54.1",
Expand Down
79 changes: 79 additions & 0 deletions apps/backend-e2e/src/miller-tests/basic-security.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/* eslint-disable sonarjs/no-duplicate-string */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { ApplicationSupportApi } from "@use-miller/shared-api-client";
import { ApiClientFactory } from "./commonDataModels/ApiClientFactory";
import { TestUserAccounts } from "./commonDataModels/AuthenticationTokenManager";

describe("For unauthenticated users", () => {
const applicationSupportApi =
ApiClientFactory.getUnAuthenticatedApiInstance(ApplicationSupportApi);

it("unsecure endpoints are reachable", async () => {
const response = await applicationSupportApi.appControllerGetHello();
expect(response).toEqual("Healthy and running");
});

it("secure endpoints are blocked", async () => {
await expect(() =>
applicationSupportApi.appControllerGetHelloAuthorized()
).rejects.toMatchObject({ status: 401 });
});

it("super secure endpoints are blocked", async () => {
await expect(() =>
applicationSupportApi.appControllerGetHelloSuperAdmin()
).rejects.toMatchObject({ status: 401 });
});
});

describe("For normal authorized users", () => {
const applicationSupportApi = ApiClientFactory.getAuthenticatedApiInstance(
ApplicationSupportApi
);

it("unsecure endpoints are reachable", async () => {
const response = await applicationSupportApi.appControllerGetHello();
expect(response).toEqual("Healthy and running");
});

it("secures endpoints are reachable", async () => {
const response =
await applicationSupportApi.appControllerGetHelloAuthorized();
expect(response).toEqual("Healthy and running");
});

it("super secure endpoints are blocked", async () => {
await expect(() =>
applicationSupportApi.appControllerGetHelloSuperAdmin()
).rejects.toMatchObject({ status: 403 });
});
});

describe("For super admin authorized users", () => {
const applicationSupportApi = ApiClientFactory.getAuthenticatedApiInstance(
ApplicationSupportApi,
TestUserAccounts.SUPER_USER
);

it("unsecure endpoints are reachable", async () => {
const response = await applicationSupportApi.appControllerGetHello();
expect(response).toEqual("Healthy and running");
});

it("secures endpoints are reachable", async () => {
const response =
await applicationSupportApi.appControllerGetHelloAuthorized();
expect(response).toEqual("Healthy and running");
});

it("super secure endpoints are reachable", async () => {
const response =
await applicationSupportApi.appControllerGetHelloSuperAdmin();
expect(response).toEqual("Healthy and running");
});

afterAll(async () => {
await applicationSupportApi.superPowersControllerResetDatabase();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,46 +5,41 @@ import {
UsersApi,
ApplicationSupportApi,
} from "@use-miller/shared-api-client";
import { AuthenticationTokenManager } from "./AuthenticationTokenManager";
import {
AuthenticationTokenManager,
TestUserAccounts,
} from "./AuthenticationTokenManager";
import fetch from "node-fetch";

export class ApiClientFactory {
static contentType = "content-type";
static jsonType = "application/json";
static validToken = "";

public static getAllAuthenticated(elevateToSuperUser = false): {
public static getAllAuthenticated(): {
applicationSupportApi: ApplicationSupportApi;
userApi: UsersApi;
emailClientApi: EmailClientApi;
} {
return {
applicationSupportApi: ApiClientFactory.getAuthenticatedApiInstance(
ApplicationSupportApi,
elevateToSuperUser
),
userApi: ApiClientFactory.getAuthenticatedApiInstance(
UsersApi,
elevateToSuperUser
ApplicationSupportApi
),
userApi: ApiClientFactory.getAuthenticatedApiInstance(UsersApi),
emailClientApi:
ApiClientFactory.getAuthenticatedApiInstance<EmailClientApi>(
EmailClientApi,
elevateToSuperUser
EmailClientApi
),
};
}
public static getAuthenticatedApiInstance<T extends BaseAPI>(
apiService: {
new (apiConfig: Configuration): T;
},
elevateToSuperUser = false
userType: TestUserAccounts = TestUserAccounts.BASIC_USER
) {
const apiConfig = new Configuration({
basePath: process.env.TEST_API_URL,
accessToken: elevateToSuperUser
? AuthenticationTokenManager.validSuperUserToken
: AuthenticationTokenManager.validBasicUserToken,
accessToken: AuthenticationTokenManager.getAccessToken(userType),
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
fetchApi: fetch as any,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,63 @@ import LocalApiTestToken from "./LocalApiTestToken";
import fs from "fs";
import axios from "axios";

export enum TestUserAccounts {
SUPER_USER = "SuperUser",
BASIC_USER = "BasicUser",
EMAIL_NOT_VERIFIED_USER = "EmailNotVerifiedUser",
}
export type TestUserConfiguration = {
tokenPath: string;
username: string;
password: string;
accountType: TestUserAccounts;
token: string;
};
export class AuthenticationTokenManager {
static validSuperUserToken = "";
static validBasicUserToken = "";

public static async init(): Promise<void> {
const basicUserTokenParameters = {
tokenPath: "./basic-user-api-test-token.json",
username: process.env.AUTH0_TEST_ACCOUNT_BASIC_USERNAME!,
password: process.env.AUTH0_TEST_ACCOUNT_BASIC_PASSWORD!,
};
static getAccessToken(userType: TestUserAccounts): string {
return this.userConfiguration.find((x) => x.accountType === userType)!
.token;
}

const superUserTokenParameters = {
tokenPath: "./local-api-test-token.json",
private static userConfiguration = [
{
accountType: TestUserAccounts.SUPER_USER,
tokenPath: "./tmp-tokens/super-user-local-api-test-token.json",
username: process.env.AUTH0_TEST_ACCOUNT_USERNAME!,
password: process.env.AUTH0_TEST_ACCOUNT_PASSWORD!,
};
if (
AuthenticationTokenManager.validSuperUserToken === undefined ||
AuthenticationTokenManager.validSuperUserToken === ""
) {
AuthenticationTokenManager.validSuperUserToken =
await AuthenticationTokenManager.initSingleToken(
superUserTokenParameters
);
}
token: "",
},
{
accountType: TestUserAccounts.BASIC_USER,
tokenPath: "./tmp-tokens/basic-user-api-test-token.json",
username: process.env.AUTH0_TEST_ACCOUNT_BASIC_USERNAME!,
password: process.env.AUTH0_TEST_ACCOUNT_BASIC_PASSWORD!,
token: "",
},
{
accountType: TestUserAccounts.EMAIL_NOT_VERIFIED_USER,
tokenPath:
"./tmp-tokens/email-not-verified-user-api-test-token.json",
username: process.env.AUTH0_TEST_ACCOUNT_NO_EMAILV_USERNAME!,
password: process.env.AUTH0_TEST_ACCOUNT_NO_EMAILV_PASSWORD!,
token: "",
},
];

if (
AuthenticationTokenManager.validBasicUserToken === undefined ||
AuthenticationTokenManager.validBasicUserToken === ""
) {
AuthenticationTokenManager.validBasicUserToken =
await AuthenticationTokenManager.initSingleToken(
basicUserTokenParameters
);
public static async init(): Promise<void> {
for (const userConfig of AuthenticationTokenManager.userConfiguration) {
if (userConfig.token === "") {
userConfig.token =
await AuthenticationTokenManager.initSingleToken(
userConfig
);
}
}
}

private static async initSingleToken(parameters: {
tokenPath: string;
username: string;
password: string;
}): Promise<string> {
private static async initSingleToken(
parameters: TestUserConfiguration
): Promise<string> {
try {
console.log("Getting new token", parameters);
if (!parameters.username) {
Expand All @@ -54,6 +69,9 @@ export class AuthenticationTokenManager {

let localToken: LocalApiTestToken;

if (!fs.existsSync("./tmp-tokens")) {
fs.mkdirSync("./tmp-tokens");
}
// eslint-disable-next-line prefer-const
localToken = fs.existsSync(parameters.tokenPath)
? new LocalApiTestToken(
Expand All @@ -64,7 +82,7 @@ export class AuthenticationTokenManager {
)
: new LocalApiTestToken();

if (localToken.needNewToken()) {
if (localToken.mustRefreshToken()) {
console.log("Getting new token...");
const options = {
method: "POST",
Expand Down Expand Up @@ -115,7 +133,7 @@ export class AuthenticationTokenManager {
JSON.stringify(localToken)
);

console.log("New token written to ./local-api-test-token.json");
console.log(`New token written to ${parameters.tokenPath}`);
}

return localToken.access_token!;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default class LocalApiTestToken {
public token_type: string;
public date_received: Date;

public needNewToken(): boolean {
public mustRefreshToken(): boolean {
const noExistingAccessToken = this.access_token === undefined;
// now - date received / 1000 > expires in
const yesterday = new Date(Date.now() - 24 * 3600 * 1000);
Expand Down

This file was deleted.

3 changes: 2 additions & 1 deletion apps/backend-e2e/src/miller-tests/new-user-flow.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
UsersApi,
} from "@use-miller/shared-api-client";
import { ApiClientFactory } from "./commonDataModels/ApiClientFactory";
import { TestUserAccounts } from "./commonDataModels/AuthenticationTokenManager.js";

// This follows a user through the first steps when they hit
// the api for the first time.
Expand All @@ -28,7 +29,7 @@ describe("When getting a user the first time", () => {
const superUserSubscriptionsApi =
ApiClientFactory.getAuthenticatedApiInstance(
OrganisationSubscriptionsApi,
true
TestUserAccounts.SUPER_USER
);

let foundUser: User | undefined;
Expand Down
20 changes: 0 additions & 20 deletions apps/backend-e2e/src/miller-tests/root.e2e-spec.ts

This file was deleted.

Loading

0 comments on commit dbecd24

Please sign in to comment.