Skip to content

Commit

Permalink
Merge pull request #1 from bardarz/refactor-with-test
Browse files Browse the repository at this point in the history
Refactor with test
  • Loading branch information
Deepzima authored Jan 27, 2020
2 parents 57e4c7b + 2cc3186 commit de71470
Show file tree
Hide file tree
Showing 16 changed files with 221 additions and 78 deletions.
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ ecosystem and it's good to archieve a proof-of concepts.

* `Webpack` and `webpack-serverless` if you want to use the latest ecma version, use loaders and manage the bundle of your code.

* `Mocha` testing framework

* `Chai` Our TDD/BDD assertion library
* `Jest` Our TDD/BDD assertion library

* `SuperTest` HTTP server testing library

Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"main": "index.js",
"scripts": {
"start": "nodemon --exec babel-node server.js",
"test": "DEBUG=server:debug NODE_ENV=test mocha --require @babel/register --reporter spec --exit tests/ --exec babel-node",
"test": "DEBUG=server:debug NODE_ENV=test serverless-bundle test",
"cover": "nyc npm run test"
},
"repository": {
Expand All @@ -29,6 +29,8 @@
"dynamoose": "^1.11.1",
"express": "^4.17.1",
"express-joi-validation": "^4.0.1",
"http-status-codes": "^1.4.0",
"lodash": "^4.17.15",
"source-map-support": "^0.5.13"
},
"devDependencies": {
Expand All @@ -42,9 +44,8 @@
"babel-loader": "^8.0.6",
"babel-plugin-source-map-support": "^2.1.1",
"babel-watch": "^7.0.0",
"chai": "^4.2.0",
"debug": "^4.1.1",
"mocha": "^6.2.1",
"jest": "^25.1.0",
"nodemon": "^1.19.4",
"nyc": "^14.1.1",
"serverless-bundle": "^1.2.5",
Expand Down
9 changes: 5 additions & 4 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ const server = http.createServer(app);
const PORT = process.env.NODE_PORT || 5000;

const listen = server.listen(PORT, () => {
debug(`server is running on port ${PORT}`);
console.log(`Listening to port ${PORT}`);
if (process.env.NODE_ENV !== 'test') {
debug(`server is running on port ${PORT}`);
console.log(`Listening to port ${PORT}`);
}
});


module.exports.port = listen.address().port;
module.exports.port = listen.address().port;
22 changes: 9 additions & 13 deletions src/app.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@

// app.js
/** app.js **/
const express = require('express');
const app = express();

//require('./config/dynamodb.config');

const corsOptions = {
origin: '*',
methods: 'DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT',
allowedHeaders: 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'
};
/** CORS option if not using the express ones **/
// const corsOptions = {
// origin: '*',
// methods: 'DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT',
// allowedHeaders: 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'
// };

// Integrate CORS options for all routes
app.use(express.json());


// Injecting routes
require('./routes/route.index.js')(app);
require('./routes/index.js')(app);

app.get('/', (req, res) => {
return res.status(200).send({'message': 'YAY! Congratulations! Your first endpoint is working'});
});


module.exports = app;
module.exports = app;
22 changes: 10 additions & 12 deletions src/config/dynamodb.config.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
'use strict';

import { ENV } from './env.config'


// External libraries
const dynamoose = require('dynamoose');

dynamoose.setDefaults(
{
create: false,
waitForActive: false
});
// Config
import { ENV } from './env.config'

dynamoose.setDefaults({
create: false,
waitForActive: false
});

dynamoose.AWS.config.update({
region: "eu-west-1"
});

if(!ENV.DYNAMO_DB.IS_OFFLINE) {
if (!ENV.DYNAMO_DB.IS_OFFLINE) {
console.log("is local");
dynamoose.local('http://localhost:4569')
}



module.exports = dynamoose;
export default dynamoose;
35 changes: 25 additions & 10 deletions src/controllers/item.controller.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,30 @@
import * as Item from '../models/item.model'
/** External libraries **/
import { INTERNAL_SERVER_ERROR, NOT_FOUND, OK } from 'http-status-codes';
// import * as _ from 'lodash';

const Get = async (req, res) => {
console.log(req.params);
import { isEmpty } from 'lodash';

await Item.get(req.params.ID).then((data) => {
console.log(data);
res.json(data);
});
/** Item model **/
import Item from '../models/item.model'
import { ErrItemNotFound } from '../errors';

};
const ItemController = {
Get: async (req, res) => {
const { ID } = req.params;

try {
const result = await Item.Get(ID);

module.exports = {
Get
if (isEmpty(result)) {
return res.status(NOT_FOUND).json(ErrItemNotFound);
}

return res.status(OK).json(result);
} catch (e) {
console.error(e);
return res.status(INTERNAL_SERVER_ERROR).json(e);
}
}
};

export default ItemController;
5 changes: 5 additions & 0 deletions src/errors/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/** ID param not defined **/
export const ErrIdNotDefined = 'ErrorIdNotDefined';

/** Item not found**/
export const ErrItemNotFound = 'ErrItemNotFound';
32 changes: 13 additions & 19 deletions src/models/item.model.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,17 @@
/** Third Party **/
const dynamoose = require('../config/dynamodb.config');
/** Item Dynamoose Schema **/
import ItemSchema from './schemas/item.schema';

/** Env **/
import { ENV } from '../config/env.config';
/** Errors **/
import { ErrIdNotDefined } from '../errors';

const Schema = dynamoose.Schema;
const ItemModel = {
Get: async (ID) => {
if (!ID) {
throw Error(ErrIdNotDefined);
}

const ItemSchema = new Schema({
set_ID: {
type: String,
hashKey: true
},
created_at: String,
updated_at: String,
data: String
return await ItemSchema.get(ID);
}
};

}, {
useNativeBooleans: true,
useDocumentTypes: true,
});

module.exports = dynamoose.model(ENV.DYNAMO_DB.ITEM_TABLE, ItemSchema);
export default ItemModel;
20 changes: 20 additions & 0 deletions src/models/schemas/item.schema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/** Dynamoose config **/
import dynamoose from '../../config/dynamodb.config';

/** ENV config **/
import { ENV } from '../../config/env.config';

const ItemSchema = new dynamoose.Schema({
ID: {
type: String,
hashKey: true
},
createdAt: String,
updatedAt: String,
data: String
}, {
useNativeBooleans: true,
useDocumentTypes: true,
});

export default dynamoose.model(ENV.DYNAMO_DB.ITEM_TABLE, ItemSchema);
13 changes: 13 additions & 0 deletions src/routes/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/** External libraries **/
import { createValidator } from 'express-joi-validation';
const validator = createValidator({});

/** Controllers **/
import ItemController from '../controllers/item.controller';

/** Validators **/
import { itemParamsSchema } from '../validators/item.validators';

module.exports = (app) => {
app.get('/api/item/:ID', validator.params(itemParamsSchema), ItemController.Get);
};
9 changes: 0 additions & 9 deletions src/routes/route.index.js

This file was deleted.

5 changes: 5 additions & 0 deletions src/validators/item.validators.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const Joi = require('@hapi/joi');

export const itemParamsSchema = Joi.object({
ID: Joi.string().guid().required()
});
49 changes: 49 additions & 0 deletions tests/item.controller.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import ItemController from '../src/controllers/item.controller';

jest.mock('../src/models/item.model', () => ({
Get: jest.fn()
}));

import ItemModel from '../src/models/item.model';
import { Item, ItemID } from './mocks';
import { ErrItemNotFound } from '../src/errors';

describe('Item Controller', () => {
const params = { ID: ItemID };
let mockRequest, mockResponse;

beforeEach(() => {
mockRequest = require('./mocks').mockRequest;
mockResponse = require('./mocks').mockResponse;
});

afterEach(() => {
jest.resetAllMocks();
});

it('[GET] Should return the item', async () => {
ItemModel.Get.mockImplementation(() => Item);

const req = mockRequest({ params });
const res = mockResponse();

await ItemController.Get(req, res);

expect(ItemModel.Get).toBeCalledTimes(1);
expect(res.status).toHaveBeenCalledWith(200);
expect(res.json).toHaveBeenCalledWith(Item);
});

it('[GET] Should return 404 if the item does not exist', async () => {
ItemModel.Get.mockImplementation(() => {});

const req = mockRequest({ params });
const res = mockResponse();

await ItemController.Get(req, res);

expect(ItemModel.Get).toBeCalledTimes(1);
expect(res.status).toHaveBeenCalledWith(404);
expect(res.json).toHaveBeenCalledWith(ErrItemNotFound);
});
});
25 changes: 25 additions & 0 deletions tests/mocks/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const ItemID = 'b9894650-f038-46e5-94e2-1686e755ebb9';

const Item = {
ID: ItemID,
data: 'This is a mocked Item',
createdAt: "2020-01-20T10:00:00.000Z",
updatedAt: "2020-01-20T10:00:00.000Z"
};

const mockRequest = (data) => ({ ...data });
const mockResponse = (data) => {
const res = data || {};

res.status = jest.fn().mockReturnValue(res);
res.json = jest.fn().mockReturnValue(res);

return res;
};

export {
Item,
ItemID,
mockRequest,
mockResponse
};
8 changes: 3 additions & 5 deletions tests/server.test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { expect } from 'chai';
import server from '../server';

describe('Server', ()=>{
it('tests that server is running current port', async () => {
expect(server.port).to.equal(5000)

it('Is running in current port', () => {
expect(server.port).toEqual(5000)
})
});
});
34 changes: 34 additions & 0 deletions tests/validators.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { itemParamsSchema } from '../src/validators/item.validators';
import { ItemID } from './mocks';

describe('Item Joi validators', () => {
const allowed = { ID: ItemID };
const notAllowed = { ID: '1' };
const empty = {};

describe('Params validation', () => {
it('Should validate the UUID', () => {
const { error } = itemParamsSchema.validate(allowed);

expect(error).toBeUndefined();
});

it('Should invalidate with UUID not valid', () => {
const { error } = itemParamsSchema.validate(notAllowed);
const errorTypes = error.details.map(el => el.type);

expect(error).toBeDefined();
expect(errorTypes.length).toBeGreaterThan(0);
expect(errorTypes.includes('string.guid')).toBe(true);
});

it('Should invalidate if empty', () => {
const { error } = itemParamsSchema.validate(empty);
const errorTypes = error.details.map(el => el.type);

expect(error).toBeDefined();
expect(errorTypes.length).toBeGreaterThan(0);
expect(errorTypes.includes('any.required')).toBe(true);
})
})
});

0 comments on commit de71470

Please sign in to comment.