diff --git a/.travis.yml b/.travis.yml index fbfd6f9..5244158 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,13 +16,6 @@ addons: packages: - postgresql-10 - postgresql-client-10 -before_script: - - psql -c 'create database testdb;' -U postgres - - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter - - chmod +x ./cc-test-reporter - - ./cc-test-reporter before-build -after_script: - - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT before_install: - sudo sed -i -e '/local.*peer/s/postgres/all/' -e 's/peer\|md5/trust/g' /etc/postgresql/*/main/pg_hba.conf - sudo service postgresql restart @@ -40,3 +33,13 @@ deploy: app: qcredit on: develop after_success: yarn coverage + +before_script: + - psql -c 'create database testdb;' -U postgres + - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter + - chmod +x ./cc-test-reporter + - ./cc-test-reporter before-build +script: + - yarn test +after_script: + - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT diff --git a/README.md b/README.md index 9da3e11..f65a90b 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![Build Status](https://travis-ci.com/chidimo/Quick-Credit.svg?branch=develop)](https://travis-ci.com/chidimo/Quick-Credit) [![Coverage Status](https://coveralls.io/repos/github/chidimo/Quick-Credit/badge.svg?branch=develop)](https://coveralls.io/github/chidimo/Quick-Credit?branch=develop) [![Maintainability](https://api.codeclimate.com/v1/badges/38d7483bb05e2777f391/maintainability)](https://codeclimate.com/github/chidimo/Quick-Credit/maintainability) +[![Test Coverage](https://api.codeclimate.com/v1/badges/38d7483bb05e2777f391/test_coverage)](https://codeclimate.com/github/chidimo/Quick-Credit/test_coverage) ## Website diff --git a/UI/js/loan.js b/UI/js/loan.js index 1daba60..b22451f 100644 --- a/UI/js/loan.js +++ b/UI/js/loan.js @@ -5,31 +5,30 @@ const reject_loan_application = document.getElementById( 'reject_loan_application' ); -approve_loan_application.addEventListener('click', e => { - e.preventDefault(); - const cont = prompt( - 'Confirm loan approval', - 'Click okay to approve this loan request'); - +const promptUser = (text, defaultText, msg) => { + const cont = prompt(text, defaultText); if (cont !== null) { - alert('Loan application was approved'); - // send email notification + alert(msg); } +}; +approve_loan_application.addEventListener('click', e => { + e.preventDefault(); + promptUser( + 'Confirm loan approval', + 'Click okay to approve this loan request', + 'Loan application was approved' + ); return; }); reject_loan_application.addEventListener('click', e => { e.preventDefault(); - const cont = prompt( + promptUser( 'Confirm loan rejection', - 'Click okay to reject this loan request'); - - if (cont !== null) { - alert('Loan application was rejected'); - // send email notification - } - + 'Click okay to reject this loan request', + 'Loan application was rejected' + ); return; }); diff --git a/UI/js/user.js b/UI/js/user.js index c205f29..8b0794f 100644 --- a/UI/js/user.js +++ b/UI/js/user.js @@ -1,32 +1,31 @@ const verify_user = document.getElementById('verify_user'); const un_verify_user = document.getElementById('un_verify_user'); -verify_user.addEventListener('click', e => { - e.preventDefault(); - const cont = prompt( - 'Please confirm if you want to verify this user', - 'Click okay to reject this loan request'); - +const promptUser = (text, defaultText, msg) => { + const cont = prompt(text, defaultText); if (cont !== null) { - // verify user - alert('User was verified.'); - // Send email notification + alert(msg); } +}; +verify_user.addEventListener('click', e => { + e.preventDefault(); + + promptUser( + 'Confirm user verification', + 'Click okay to verify this user', + 'User was verified.' + ); return; }); un_verify_user.addEventListener('click', e => { e.preventDefault(); - const cont = prompt( - "Please confirm if you want to verify revoke this user's verification", - "Click okay to revoke this user's verification"); - - if (cont !== null) { - // revoke verification - alert("User's verification was revoked."); - // Send email notification - } + promptUser( + 'Confirm revoking user verification', + "Click okay to revoke this user's verification", + "User's verification was revoked." + ); return; }); diff --git a/models/Model.js b/models/Model.js index 21c0808..18aadb3 100644 --- a/models/Model.js +++ b/models/Model.js @@ -1,25 +1,10 @@ -import { Pool } from 'pg'; -import dotenv from 'dotenv'; - -import Settings from '../settings'; -import { dev_logger, test_logger } from '../utils/loggers'; - -dotenv.config(); - -const config = { - user: Settings.dbSettings().dbUser, - host: Settings.dbSettings().dbHost, - port: Settings.dbSettings().dbPort, - database: Settings.dbSettings().dbName, - password: Settings.dbSettings().dbPassword -}; - -test_logger(`test db config ${JSON.stringify(config)}`); +import { dev_logger } from '../utils/loggers'; +import pool from './pool'; class Model { constructor(table) { this.table = table; - this.pool = new Pool(config); + this.pool = pool; this.pool.on('error', (err, client) => { dev_logger(`****Unexpected error on idle client, ${err}`); process.exit(-1); @@ -50,12 +35,6 @@ class Model { return await this.pool.query(query); } - // async insert(columns, values) { - // const query = `INSERT INTO ${this.table} ${columns} VALUES(${values})`; - // dev_logger(`\nINSERT QUERY: ${query}\n`); - // return await this.pool.query(query); - // } - async insert_with_return(columns, values) { const query = ` INSERT INTO ${this.table} ${columns} diff --git a/models/pool.js b/models/pool.js new file mode 100644 index 0000000..5086176 --- /dev/null +++ b/models/pool.js @@ -0,0 +1,18 @@ +import { Pool } from 'pg'; +import dotenv from 'dotenv'; + +import Settings from '../settings'; + +dotenv.config(); + +const config = { + user: Settings.dbSettings().dbUser, + host: Settings.dbSettings().dbHost, + port: Settings.dbSettings().dbPort, + database: Settings.dbSettings().dbName, + password: Settings.dbSettings().dbPassword +}; + +const pool = new Pool(config); + +export default pool; diff --git a/package.json b/package.json index 895109b..81d9e10 100644 --- a/package.json +++ b/package.json @@ -15,9 +15,9 @@ "start": "set NODE_ENV=production&node --require @babel/register ./bin/www ", "devstart": "set NODE_ENV=development&set DEBUG=dev&nodemon --exec babel-node ./bin/www", "lint": "./node_modules/.bin/eslint ./", - "test": "set NODE_ENV=test&set DEBUG=test&set DBNAME=testdb&nyc --reporter=html --reporter=text mocha -r @babel/register -r should", + "test": "set NODE_ENV=test&set DEBUG=test&set DBNAME=testdb&nyc --reporter=html --reporter=text --reporter=lcov mocha -r @babel/register -r should", "cover": "nyc report --reporter=text-lcov | coveralls", - "coverage": "nyc report --reporter=text-lcov | coveralls && rm -rf ./.nyc_output && rm -rf coverage/" + "coverage": "nyc report --reporter=text-lcov | coveralls" }, "bugs": { "url": "https://github.com/chidimo/Quick-Credit/issues" diff --git a/test/loans-spec.js b/test/loans-spec.js index 74d6a97..2660e1a 100644 --- a/test/loans-spec.js +++ b/test/loans-spec.js @@ -1,39 +1,25 @@ // should is not used directly in the file but is added as a mocha requirement -import { exec } from 'child_process'; + import supertest from 'supertest'; import assert from 'assert'; import app from '../app'; + import { test_logger } from '../utils/loggers'; +import createDB from '../utils/createDB'; +import clearDB from '../utils/clearDB'; const server = supertest.agent(app); describe('/loans', () => { - const dump = 'psql -h localhost -d testdb -U postgres -f test/testdb.sql'; - const connect = 'psql -h localhost -d testdb -U postgres -c'; - const query = 'delete from loans;delete from repayments'; - const clear = `${connect} "${query}"`; - - before(done => { - - exec(dump, err => { - if (err) { - test_logger(`dump error: ${ err }`); - } - test_logger('****Database populated successfully.****'); - done(); - }); + before(async () => { + test_logger('Creating DB in loans-spec'); + await createDB(); }); - - after(done => { - test_logger('After hook start'); - exec(clear, err => { - if (err) { - test_logger(`Error clearing db ${err}`); - } - }); - test_logger('****Database cleared successfully.****'); - done(); + + after(async () => { + test_logger('Clearing DB in loans-spec'); + await clearDB(); }); describe('/loans: Get all loans', () => { diff --git a/test/users-spec.js b/test/users-spec.js index 12df0d0..520c180 100644 --- a/test/users-spec.js +++ b/test/users-spec.js @@ -4,35 +4,22 @@ import supertest from 'supertest'; import app from '../app'; -import { exec } from 'child_process'; import { test_logger } from '../utils/loggers'; +import createDB from '../utils/createDB'; +import clearDB from '../utils/clearDB'; const server = supertest.agent(app); describe('/users', () => { - const dump = 'psql -h localhost -d testdb -U postgres -f test/testdb.sql'; - const connect = 'psql -h localhost -d testdb -U postgres -c'; - const query = 'delete from users'; - const clear = `${connect} "${query}"`; - before(done => { - exec(dump, err => { - if (err) { - test_logger(`dump error: ${ err }`); - } - test_logger('****Database populated successfully.****'); - done(); - }); + before(async () => { + test_logger('Creating DB in users-spec'); + await createDB(); }); - - after(done => { - exec(clear, err => { - if (err) { - test_logger(`Error clearing db ${err}`); - } - }); - test_logger('****Database cleared successfully.****'); - done(); + + after(async () => { + test_logger('Clearing DB in users-spec'); + await clearDB(); }); describe('/auth/signup', () => { diff --git a/utils/clearDB.js b/utils/clearDB.js new file mode 100644 index 0000000..31b843f --- /dev/null +++ b/utils/clearDB.js @@ -0,0 +1,21 @@ +import pool from '../models/pool'; +import { dev_logger } from './loggers'; + +const dropUsers = ` + DROP TABLE users +`; +const dropLoans = ` + DROP TABLE loans +`; +const dropRepayments = ` + DROP TABLE repayments +`; +const queries = [ dropUsers, dropLoans, dropRepayments ]; +const clearDB = async () => { + for (const query of queries) { + dev_logger(query); + await pool.query(query); + } +}; + +export default clearDB; diff --git a/utils/createDB.js b/utils/createDB.js new file mode 100644 index 0000000..ef4203d --- /dev/null +++ b/utils/createDB.js @@ -0,0 +1,101 @@ +import bcrypt from 'bcrypt'; +import pool from '../models/pool'; +import { dev_logger, test_logger } from './loggers'; + +const hashPassword = password => (bcrypt.hashSync(password, 8)); + +const createUserTable = ` + CREATE TABLE IF NOT EXISTS users ( + id SERIAL PRIMARY KEY, + email VARCHAR NOT NULL UNIQUE, + password VARCHAR NOT NULL, + firstname VARCHAR DEFAULT '', + lastname VARCHAR DEFAULT '', + phone VARCHAR DEFAULT '', + photo VARCHAR NULL, + address jsonb DEFAULT '{"home": "", "office": ""}', + status VARCHAR DEFAULT 'unverified', + isAdmin BOOLEAN DEFAULT false + ) +`; + +const populateUserTable = ` + INSERT INTO users(email, password, firstname, lastname, phone, address) + VALUES ('a@b.com', '${hashPassword('password')}', 'first', 'men', '080121515', '{"home": "iyaba", "office": "ring road"}'), + ('c@d.go', '${hashPassword('password')}', 'name', 'cat', '08151584151', '{"home": "london", "office": "NYC"}'), + ('me@yahoo.com', '${hashPassword('password')}', 'tayo', 'dele', '08012345678', '{"home": "ijebu","office": "ijegun"}'), + ('abc@gmail.com', '${hashPassword('password')}', 'what', 'is', '08012345678','{"home": "must","office": "not"}'), + ('name@chat.co', '${hashPassword('password')}', 'niger', 'tornadoes', '08012345678', '{"home": "niger","office": "niger"}'), + ('bcc@gmail.com', '${hashPassword('password')}', 'bcc', 'lions', '08012345678', '{"home": "gboko","office": "gboko"}'), + ('bbc@bbc.uk', '${hashPassword('password')}', 'bbc', 'broadcast', '08012345678', '{"home": "london","office": "uk"}'), + ('c@g.move', '${hashPassword('password')}', 'abc', 'def', '08012345678', '{"home": "shop","office": "home"}'), + ('an@dela.ng', '${hashPassword('password')}', 'and', 'ela', '08012345678', '{"home": "ikorodu","office": "lagos"}'), + ('soft@ware.eng', '${hashPassword('password')}', 'soft', 'eng', '08012345678', '{"home": "remote","office": "on-site"}'); +`; + +const createLoansTable = ` + CREATE TABLE IF NOT EXISTS loans ( + id SERIAL PRIMARY KEY, + userid INT NOT NULL, + createdon TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + status VARCHAR DEFAULT 'pending', + repaid BOOLEAN DEFAULT false, + amount FLOAT NOT NULL, + tenor INT NOT NULL, + interest FLOAT NOT NULL, + balance FLOAT NOT NULL, + paymentinstallment FLOAT NOT NULL + ); +`; + +const populateLoansTable = ` + INSERT INTO loans(userid, status, repaid, amount, tenor, interest, balance, paymentinstallment) + VALUES (1, 'approved', false, 50000, 12, 2500, 36999.35, 4375), + (2, 'approved', true, 100000, 12, 5000, 0, 8750), + (3, 'approved', false, 200000, 8, 10000, 200000, 26250), + (4, 'approved', false, 25000, 12, 1250, 24500, 2187.5), + (5, 'approved', false, 45000, 6, 2250, 26250, 7875), + (6, 'pending', false, 80000, 12, 4000, 8000, 7000), + (7, 'rejected', false, 60000, 6, 3000, 6000, 10500), + (8, 'approved', false, 125000, 12, 6250, 20000, 10937.5), + (9, 'rejected', false, 190000, 12, 9500, 19000, 16625), + (10, 'pending', false, 1000000, 12, 50000, 0, 87500); + `; + +const createRepaymentsTable = ` + CREATE TABLE IF NOT EXISTS repayments ( + id SERIAL PRIMARY KEY, + loanid INT NOT NULL, + adminid INT NOT NULL, + createdon TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP, + amount FLOAT NOT NULL + ); +`; +const populateRepaymentsTable = ` + INSERT INTO repayments(loanid, adminid, amount) + VALUES (1, 3, 4375), + (1, 3, 4375), + (2, 1, 26250), + (1, 2, 4375), + (3, 4, 2875), + (5, 8, 10500), + (4, 3, 4375), + (8, 1, 4375), + (8, 4, 4375), + (10, 8, 4375) + `; + +const queries = [ + createUserTable, createLoansTable, createRepaymentsTable, + populateUserTable, populateLoansTable, populateRepaymentsTable +]; + +const createDB = async () => { + for (const query of queries) { + dev_logger(query); + test_logger(query); + await pool.query(query); + } +}; + +export default createDB;