Initial commit

This commit is contained in:
Mohit Nagar
2025-09-27 12:08:31 +05:30
committed by GitHub
commit f283f6043f
47 changed files with 9418 additions and 0 deletions

77
spec/index.ts Normal file
View File

@@ -0,0 +1,77 @@
import dotenv from 'dotenv';
import find from 'find';
import Jasmine from 'jasmine';
import { parse } from 'ts-command-line-args';
import logger from 'jet-logger';
// **** Types **** //
interface IArgs {
testFile: string;
}
// **** Setup **** //
// ** Init ** //
// NOTE: MUST BE FIRST!! Load env vars
const result2 = dotenv.config({
path: './env/test.env',
});
if (result2.error) {
throw result2.error;
}
// Setup command line options.
const args = parse<IArgs>({
testFile: {
type: String,
defaultValue: '',
},
});
// ** Start Jasmine ** //
// Init Jasmine
const jasmine = new Jasmine();
jasmine.exitOnCompletion = false;
// Set location of test files
jasmine.loadConfig({
random: true,
spec_dir: 'spec',
spec_files: [
'./tests/**/*.spec.ts',
],
stopSpecOnExpectationFailure: false,
});
// Run all or a single unit-test
let execResp: Promise<jasmine.JasmineDoneInfo> | undefined;
if (args.testFile) {
const testFile = args.testFile;
find.file(testFile + '.spec.ts', './spec', (files: string[]) => {
if (files.length === 1) {
jasmine.execute([files[0]]);
} else {
logger.err('Test file not found!');
}
});
} else {
execResp = jasmine.execute();
}
// Wait for tests to finish
(async () => {
if (!!execResp) {
const info = await execResp;
if (info.overallStatus === 'passed') {
logger.info('All tests have passed :)');
} else {
logger.err('At least one test has failed :(');
}
}
})();

6
spec/nodemon.json Normal file
View File

@@ -0,0 +1,6 @@
{
"watch": ["spec"],
"ext": "spec.ts",
"ignore": ["spec/support"],
"exec": "./node_modules/.bin/ts-node --files -r tsconfig-paths/register ./spec"
}

11
spec/support/jasmine.json Normal file
View File

@@ -0,0 +1,11 @@
{
"spec_dir": "spec",
"spec_files": [
"**/*[sS]pec.ts"
],
"helpers": [
"helpers/**/*.js"
],
"stopSpecOnExpectationFailure": false,
"random": true
}

1
spec/support/types.ts Normal file
View File

@@ -0,0 +1 @@
export type TReqBody = string | object | undefined;

213
spec/tests/users.spec.ts Normal file
View File

@@ -0,0 +1,213 @@
import supertest, { SuperTest, Test, Response } from 'supertest';
import { defaultErrMsg as ValidatorErr } from 'jet-validator';
import insertUrlParams from 'inserturlparams';
import app from '@src/server';
import UserRepo from '@src/repos/UserRepo';
import User from '@src/models/User';
import HttpStatusCodes from '@src/constants/HttpStatusCodes';
import { USER_NOT_FOUND_ERR } from '@src/services/UserService';
import FullPaths from '@src/routes/constants/FullPaths';
import { TReqBody } from 'spec/support/types';
// **** Variables **** //
// Paths
const {
Get,
Add,
Update,
Delete,
} = FullPaths.Users;
// StatusCodes
const {
OK,
CREATED,
NOT_FOUND,
BAD_REQUEST,
} = HttpStatusCodes;
// Dummy users for GET req
const DummyGetAllUsers = [
User.new('Sean Maxwell', 'sean.maxwell@gmail.com'),
User.new('John Smith', 'john.smith@gmail.com'),
User.new('Gordan Freeman', 'gordan.freeman@gmail.com'),
] as const;
// Dummy update user
const DummyUserData = {
user: User.new('Gordan Freeman', 'gordan.freeman@gmail.com'),
} as const;
// **** Tests **** //
describe('UserRouter', () => {
let agent: SuperTest<Test>;
// Run before all tests
beforeAll((done) => {
agent = supertest.agent(app);
done();
});
// ** Get all users ** //
describe(`"GET:${Get}"`, () => {
const callApi = () => agent.get(Get);
// Success
it('should return a JSON object with all the users and a status code ' +
`of "${OK}" if the request was successful.`, (done) => {
// Add spy
spyOn(UserRepo, 'getAll').and.resolveTo([...DummyGetAllUsers]);
// Call API
callApi()
.end((_: Error, res: Response) => {
expect(res.status).toBe(OK);
for (let i = 0; i < res.body.users.length; i++) {
const user = res.body.users[i];
expect(user).toEqual(DummyGetAllUsers[i]);
}
done();
});
});
});
// Test add user
describe(`"POST:${Add}"`, () => {
const callApi = (reqBody: TReqBody) =>
agent
.post(Add)
.type('form').send(reqBody);
// Test add user success
it(`should return a status code of "${CREATED}" if the request was ` +
'successful.', (done) => {
// Spy
spyOn(UserRepo, 'add').and.resolveTo();
// Call api
callApi(DummyUserData)
.end((_: Error, res: Response) => {
expect(res.status).toBe(CREATED);
expect(res.body.error).toBeUndefined();
done();
});
});
// Missing param
it('should return a JSON object with an error message of ' +
`"${ValidatorErr}" and a status code of "${BAD_REQUEST}" if the user ` +
'param was missing.', (done) => {
// Call api
callApi({})
.end((_: Error, res: Response) => {
expect(res.status).toBe(BAD_REQUEST);
expect(res.body.error).toBe(ValidatorErr);
done();
});
});
});
// ** Update users ** //
describe(`"PUT:${Update}"`, () => {
const callApi = (reqBody: TReqBody) =>
agent
.put(Update)
.type('form').send(reqBody);
// Success
it(`should return a status code of "${OK}" if the request was successful.`,
(done) => {
// Setup spies
spyOn(UserRepo, 'update').and.resolveTo();
spyOn(UserRepo, 'persists').and.resolveTo(true);
// Call api
callApi(DummyUserData)
.end((_: Error, res: Response) => {
expect(res.status).toBe(OK);
expect(res.body.error).toBeUndefined();
done();
});
});
// Param missing
it('should return a JSON object with an error message of ' +
`"${ValidatorErr}" and a status code of "${BAD_REQUEST}" if the user ` +
'param was missing.', (done) => {
// Call api
callApi({})
.end((_: Error, res: Response) => {
expect(res.status).toBe(BAD_REQUEST);
expect(res.body.error).toBe(ValidatorErr);
done();
});
});
// User not found
it('should return a JSON object with the error message of ' +
`"${USER_NOT_FOUND_ERR}" and a status code of "${NOT_FOUND}" if the id ` +
'was not found.', (done) => {
// Call api
callApi(DummyUserData)
.end((_: Error, res: Response) => {
expect(res.status).toBe(NOT_FOUND);
expect(res.body.error).toBe(USER_NOT_FOUND_ERR);
done();
});
});
});
// ** Delete user ** //
describe(`"DELETE:${Delete}"`, () => {
const callApi = (id: number) =>
agent
.delete(insertUrlParams(Delete, { id }));
// Success
it(`should return a status code of "${OK}" if the request was successful.`,
(done) => {
// Setup spies
spyOn(UserRepo, 'delete').and.resolveTo();
spyOn(UserRepo, 'persists').and.resolveTo(true);
// Call api
callApi(5)
.end((_: Error, res: Response) => {
expect(res.status).toBe(OK);
expect(res.body.error).toBeUndefined();
done();
});
});
// User not found
it('should return a JSON object with the error message of ' +
`"${USER_NOT_FOUND_ERR}" and a status code of "${NOT_FOUND}" if the id ` +
'was not found.', (done) => {
callApi(-1)
.end((_: Error, res: Response) => {
expect(res.status).toBe(NOT_FOUND);
expect(res.body.error).toBe(USER_NOT_FOUND_ERR);
done();
});
});
// Invalid param
it(`should return a status code of "${BAD_REQUEST}" and return an error ` +
`message of "${ValidatorErr}" if the id was not a valid number`, (done) => {
callApi('horse' as unknown as number)
.end((_: Error, res: Response) => {
expect(res.status).toBe(BAD_REQUEST);
expect(res.body.error).toBe(ValidatorErr);
done();
});
});
});
});

14
spec/types/supertest/index.d.ts vendored Normal file
View File

@@ -0,0 +1,14 @@
import { IUser } from '@src/models/User';
import 'supertest';
declare module 'supertest' {
export interface Response {
headers: Record<string, string[]>;
body: {
error: string;
users: IUser[];
};
}
}