Skip to content

Commit e2a0cae

Browse files
author
hirsch88
committed
Add black box testing and console commands
1 parent 55d59d4 commit e2a0cae

File tree

10 files changed

+150
-16
lines changed

10 files changed

+150
-16
lines changed

package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,16 @@
1010
"serve": "./node_modules/.bin/nodemon --watch 'src/**/*.ts'",
1111
"serve:debug": "npm run serve -- --debug=6666",
1212
"lint": "./node_modules/.bin/gulp lint",
13-
"test": "NODE_ENV=test ./node_modules/.bin/jest ./test/unit --coverage",
14-
"test:black-box": "NODE_ENV=test ./node_modules/.bin/jest ./test/black-box -i",
13+
"test": "NODE_ENV=test ./node_modules/.bin/jest ./test/unit --verbose --coverage",
14+
"test:black-box": "NODE_ENV=test ./node_modules/.bin/jest ./test/black-box -i --verbose",
1515
"build": "./node_modules/.bin/gulp build",
1616
"clean": "./node_modules/.bin/gulp clean",
1717
"zip": "./node_modules/.bin/gulp zip",
1818
"db:migrate": "./node_modules/.bin/knex migrate:latest",
1919
"db:migrate:rollback": "./node_modules/.bin/knex migrate:rollback",
2020
"db:seed": "./node_modules/.bin/knex seed:run",
21+
"db:reset": "npm run console db:reset",
22+
"console": "./node_modules/.bin/ts-node ./src/console/commander.ts",
2123
"start": "node src/index.js"
2224
},
2325
"repository": "git+ssh://git@github.com/w3tec/express-typescript-boilerplate.git",
@@ -51,6 +53,7 @@
5153
"@types/bluebird": "^3.5.2",
5254
"@types/body-parser": "^1.16.3",
5355
"@types/bookshelf": "^0.8.38",
56+
"@types/commander": "^2.9.0",
5457
"@types/cors": "^2.8.1",
5558
"@types/debug": "0.0.29",
5659
"@types/dotenv": "^4.0.0",
@@ -70,6 +73,7 @@
7073
"bookshelf": "^0.10.3",
7174
"bookshelf-camelcase": "^1.1.4",
7275
"class-validator": "^0.7.0",
76+
"commander": "^2.9.0",
7377
"compression": "^1.6.2",
7478
"cors": "^2.8.1",
7579
"debug": "^2.6.0",

src/api/repositories/UserRepository.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ export class UserRepository {
4646
public static async create(data: any): Promise<User> {
4747
const user = User.forge<User>(data);
4848
try {
49-
return await user.save();
49+
const createdUser = await user.save();
50+
return await User.fetchById(createdUser.id);
5051
} catch (error) {
5152
throw new DatabaseException('Could not create the user!', error);
5253
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import '../core';
2+
import { Log } from '../core/log';
3+
4+
import * as Knex from 'knex';
5+
const options = require('../../knexfile.ts');
6+
const log = new Log('app:console:DatabaseResetCommand');
7+
8+
9+
export class DatabaseResetCommand {
10+
11+
static command = 'db:reset';
12+
static description = 'Reverse all migrations and migrate to latest.';
13+
14+
static async action(): Promise<void> {
15+
try {
16+
await DatabaseResetCommand.run();
17+
process.exit(0);
18+
} catch (e) {
19+
process.exit(1);
20+
}
21+
}
22+
23+
static async run(): Promise<void> {
24+
const knex = Knex(options);
25+
26+
const migrate: any = knex.migrate;
27+
28+
// Force unlock in case of bad state
29+
await migrate.forceFreeMigrationsLock();
30+
31+
// Get completed migrations
32+
log.info('Get completed migrations');
33+
const completedMigrations = await migrate._listCompleted();
34+
35+
// Rollback migrations
36+
log.info('Rollback migrations');
37+
await migrate._waterfallBatch(0, completedMigrations.reverse(), 'down');
38+
39+
// Migrate to latest
40+
log.info('Migrate to latest');
41+
await migrate.latest();
42+
43+
// Close connection to the database
44+
await knex.destroy();
45+
log.info('Done');
46+
}
47+
48+
}
49+
50+

src/console/commander.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import * as commander from 'commander';
2+
import { DatabaseResetCommand } from './DatabaseResetCommand';
3+
4+
/**
5+
* DatabaseResetCommand
6+
*/
7+
commander
8+
.command(DatabaseResetCommand.command)
9+
.description(DatabaseResetCommand.description)
10+
.action(() => DatabaseResetCommand.action());
11+
12+
commander.parse(process.argv);

src/core/Knex.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ export const Knex: knex = knex({
88
pool: {
99
min: Environment.get<number>('DB_POOL_MIN'),
1010
max: Environment.get<number>('DB_POOL_MAX')
11-
},
12-
migrations: {
13-
tableName: Environment.get<string>('DB_MIGRATION_TABLE')
1411
}
12+
// migrations: {
13+
// tableName: Environment.get<string>('DB_MIGRATION_TABLE')
14+
// }
1515
});

test/black-box/home.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { api } from './lib/api';
22

33

4-
test('GET / - should return the api info as a json', async () => {
4+
test('GET [/] - should return the api info as a json', async () => {
55
const res = await api('GET', '/api');
66
res.expectJson();
77
res.expectStatusCode(200);

test/black-box/lib/ApiResponeTest.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ export class ApiResponeTest {
55
constructor(private res: any) { }
66

77
getBody<T>(): T {
8-
return JSON.parse(this.res['body']);
8+
return this.res['body'];
9+
}
10+
11+
getData<T>(): T {
12+
return this.getBody()['data'];
913
}
1014

1115
getHeaders<T>(): T {
@@ -24,8 +28,10 @@ export class ApiResponeTest {
2428

2529
expectData(keys: string[]): ApiResponeTest {
2630
const a = keys.sort();
27-
const b = Object.keys(this.getBody()).sort();
31+
const d = _.isArray(this.getData()) ? this.getData()[0] : this.getData();
32+
const b = Object.keys(d).sort();
2833
expect(_.isEqual(a, b)).toBeTruthy();
34+
expect(this.getBody()['success']).toBeTruthy();
2935
return this;
3036
}
3137
}

test/black-box/lib/api.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,23 @@
11
require('dotenv').config();
22
import * as request from 'request-promise';
3-
import { OptionsWithUri } from 'request-promise';
3+
import { Options } from 'request-promise';
44
import { ApiResponeTest } from './ApiResponeTest';
55

66

7-
export const api = async (method: string, path: string, options: OptionsWithUri = { uri: '' }) => {
8-
options.method = method;
9-
options.uri = `${process.env.APP_HOST}:${process.env.APP_PORT}${path}`;
10-
options.resolveWithFullResponse = true;
11-
return new ApiResponeTest(await request(options));
7+
export interface ApiOptions<T> {
8+
body?: T;
9+
headers?: any;
10+
}
11+
12+
13+
export const api = async <T>(method: string, path: string, options: ApiOptions<T> = {}) => {
14+
let o: Options = {
15+
method: method,
16+
uri: `${process.env.APP_HOST}:${process.env.APP_PORT}${path}`,
17+
resolveWithFullResponse: true,
18+
headers: options.headers,
19+
json: true,
20+
body: options.body
21+
};
22+
return new ApiResponeTest(await request(o));
1223
};

test/black-box/user.test.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { api } from './lib/api';
2+
import { DatabaseResetCommand } from '../../src/console/DatabaseResetCommand';
3+
4+
let createdId;
5+
const userKeys = ['id', 'firstName', 'lastName', 'email', 'updatedAt', 'createdAt'];
6+
7+
beforeAll(async () => {
8+
await DatabaseResetCommand.run();
9+
});
10+
11+
test('POST [/v1/user] - should create a new user', async () => {
12+
const res = await api('POST', '/api/v1/user', {
13+
body: {
14+
firstName: 'Hans',
15+
lastName: 'Muster',
16+
email: 'hans@muster.ch'
17+
}
18+
});
19+
res.expectJson();
20+
res.expectStatusCode(201);
21+
res.expectData(userKeys);
22+
createdId = res.getData()['id'];
23+
});
24+
25+
test('GET [/v1/user] - should return the api info as a json', async () => {
26+
const res = await api('GET', '/api/v1/user');
27+
res.expectJson();
28+
res.expectStatusCode(200);
29+
res.expectData(userKeys);
30+
});
31+
32+
test('GET [/v1/user/:id] - should return the api info as a json', async () => {
33+
const res = await api('GET', `/api/v1/user/${createdId}`);
34+
res.expectJson();
35+
res.expectStatusCode(200);
36+
res.expectData(userKeys);
37+
38+
const user: any = res.getData();
39+
expect(user.firstName).toBe('Hans');
40+
expect(user.lastName).toBe('Muster');
41+
expect(user.email).toBe('hans@muster.ch');
42+
});
43+
44+

yarn.lock

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@
2929
"@types/knex" "*"
3030
"@types/lodash" "*"
3131

32+
"@types/commander@^2.9.0":
33+
version "2.9.0"
34+
resolved "https://registry.yarnpkg.com/@types/commander/-/commander-2.9.0.tgz#dd07af1fc35d76833e0da26ea67a2be088b5fafc"
35+
dependencies:
36+
"@types/node" "*"
37+
3238
"@types/cors@^2.8.1":
3339
version "2.8.1"
3440
resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.1.tgz#54073caf3b7a741e67fb82483f83a6cadfba7501"
@@ -1150,7 +1156,7 @@ combined-stream@^1.0.5, combined-stream@~1.0.5:
11501156
dependencies:
11511157
delayed-stream "~1.0.0"
11521158

1153-
commander@^2.2.0:
1159+
commander@^2.2.0, commander@^2.9.0:
11541160
version "2.9.0"
11551161
resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4"
11561162
dependencies:

0 commit comments

Comments
 (0)