Skip to content

Commit

Permalink
feat: add test (#1)
Browse files Browse the repository at this point in the history
* feat: add test

* fix: update path

* Rename test.yaml to test.yml

* Update test.yaml

* Create test.yaml

* fix: update test

* feat: update env

* Update test.yml

* Update test.yml
  • Loading branch information
berviantoleo authored Aug 11, 2024
1 parent f07beb6 commit ea1b3a8
Show file tree
Hide file tree
Showing 9 changed files with 4,773 additions and 766 deletions.
36 changes: 36 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Test

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

env:
MONGO_CONNECTION_STRING: mongodb://root:example@localhost:27017/invitation?authSource=admin

jobs:
build:

runs-on: ubuntu-latest
strategy:
matrix:
node-version: ['20.x', '22.x']
services:
# Label used to access the service container
mongo:
image: mongo:7
ports:
- 27017:27017
env:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: example
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: corepack enable && yarn --immutable
- run: yarn build
- run: yarn test
6 changes: 6 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
presets: [
['@babel/preset-env', {targets: {node: 'current'}}],
'@babel/preset-typescript',
],
};
11 changes: 10 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
"dev": "nodemon src/index.ts"
"dev": "nodemon src/index.ts",
"test": "jest"
},
"dependencies": {
"body-parser": "^1.20.2",
Expand All @@ -15,10 +16,18 @@
"winston": "^3.14.1"
},
"devDependencies": {
"@babel/preset-env": "^7.25.3",
"@babel/preset-typescript": "^7.24.7",
"@types/babel__preset-env": "^7",
"@types/body-parser": "^1",
"@types/express": "^4.17.21",
"@types/jest": "^29.5.12",
"@types/node": "^22.2.0",
"@types/supertest": "^6.0.2",
"jest": "^29.7.0",
"nodemon": "^3.1.4",
"supertest": "^7.0.0",
"ts-jest": "^29.2.4",
"ts-node": "^10.9.2",
"typescript": "^5.5.4"
}
Expand Down
25 changes: 25 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import express, { Express, Request, Response } from 'express';
import { rateLimit } from 'express-rate-limit';
import bodyParser from 'body-parser';
import invitation from './invitation';

const app: Express = express();

const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
limit: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes).
standardHeaders: 'draft-7', // draft-6: `RateLimit-*` headers; draft-7: combined `RateLimit` header
legacyHeaders: false, // Disable the `X-RateLimit-*` headers.
// store: ... , // Redis, Memcached, etc. See below.
});

app.use(limiter);
app.use(bodyParser.json());

app.get('/', (req: Request, res: Response) => {
res.send('Express + TypeScript Server');
});

app.use('/invitation', invitation);

export default app;
25 changes: 2 additions & 23 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,12 @@
import express, { Express, Request, Response } from 'express';
import { rateLimit } from 'express-rate-limit';
import mongoose from 'mongoose';
import bodyParser from 'body-parser';
import dotenv from 'dotenv';
import logger from './logger';
dotenv.config();

import invitation from './invitation';
import logger from './logger';
import app from './app';

const app: Express = express();
const port = process.env.PORT || 3000;

const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
limit: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes).
standardHeaders: 'draft-7', // draft-6: `RateLimit-*` headers; draft-7: combined `RateLimit` header
legacyHeaders: false, // Disable the `X-RateLimit-*` headers.
// store: ... , // Redis, Memcached, etc. See below.
});

app.use(limiter);
app.use(bodyParser.json());

app.get('/', (req: Request, res: Response) => {
res.send('Express + TypeScript Server');
});

app.use('/invitation', invitation);

mongoose.connect(process.env.MONGO_CONNECTION_STRING || '').then(() => {
logger.info("MongoDB Connected!");
app.listen(port, () => {
Expand Down
10 changes: 10 additions & 0 deletions test/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import request from "supertest";
import app from "../src/app";

describe("Test the root path", () => {
test("It should response the GET method", async () => {
const response = await request(app).get("/");
expect(response.statusCode).toBe(200);
expect(response.text).toBe("Express + TypeScript Server");
});
});
66 changes: 66 additions & 0 deletions test/invitation.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import request from "supertest";
import mongoose from "mongoose";
import app from "../src/app";
import Invitation from "../src/models/invitation";

describe("Test the invitation path", () => {
beforeAll(async () => {
await mongoose.connect(process.env.MONGO_CONNECTION_STRING || '');
});

beforeEach(async () => {
await Invitation.deleteMany();
})

afterAll(async () => {
await mongoose.disconnect();
});

test("It should response the GET method", async () => {
let dateNow = Date.now();
let objectItem = {
invitee: 'every where',
inviter: 'berviantoleo',
message: 'Hello!',
createdAt: dateNow
};
await Invitation.create(objectItem);
const response = await request(app).get("/invitation/berviantoleo");
expect(response.statusCode).toBe(200);
expect(Array.isArray(response.body)).toBe(true);
expect(response.body.length).toBe(1)
let responseItem = response.body[0];
expect(responseItem.invitee).toBe(objectItem.invitee);
expect(responseItem.inviter).toBe(objectItem.inviter);
expect(responseItem.message).toBe(objectItem.message);
expect(responseItem.createdAt).toBe(new Date(objectItem.createdAt).toISOString());
});

test("It should response the POST method", async () => {
let objectItem = {
invitee: 'every where',
inviter: 'berviantoleo',
message: 'Hello!'
};
const response = await request(app).post("/invitation").send(objectItem);
expect(response.statusCode).toBe(200);
expect(response.body).toBeTruthy();
let responseItem = response.body;
expect(responseItem.invitee).toBe(objectItem.invitee);
expect(responseItem.inviter).toBe(objectItem.inviter);
expect(responseItem.message).toBe(objectItem.message);
expect(responseItem.createdAt).toBeTruthy();
expect(responseItem._id).toBeTruthy();
expect(responseItem.__v).toBe(0);

const dataDb = await Invitation.findById(responseItem._id);

expect(dataDb).toBeTruthy();
expect(dataDb?.invitee).toBe(objectItem.invitee);
expect(dataDb?.inviter).toBe(objectItem.inviter);
expect(dataDb?.message).toBe(objectItem.message);
expect(dataDb?.createdAt?.toISOString()).toBe(responseItem.createdAt);
expect(dataDb?._id?.toString()).toBe(responseItem._id);
expect(dataDb?.__v).toBe(0);
});
});
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,5 +104,6 @@
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
},
"exclude": ["./test/"]
}
Loading

0 comments on commit ea1b3a8

Please sign in to comment.