From 2b60bc78cfb58c06872b6f58f5fb8e1b3ac228df Mon Sep 17 00:00:00 2001 From: Martin Donnelly Date: Tue, 15 Mar 2016 14:49:39 +0000 Subject: [PATCH] Init --- .editorconfig | 32 ++++ .gitignore | 177 ++++++++++++++++++++++ .jshintrc | 32 ++++ .pgpass | 1 + connect.js | 29 ++++ md-validator.js | 262 ++++++++++++++++++++++++++++++++ mock.js | 38 +++++ package.json | 33 ++++ rebuild.sql | 127 ++++++++++++++++ test/db-account.js | 321 +++++++++++++++++++++++++++++++++++++++ test/db-company.js | 27 ++++ test/jshint.spec.js | 8 + test/md-validator.js | 341 ++++++++++++++++++++++++++++++++++++++++++ test/mocha.opts | 4 + units/db-accounts.js | 173 +++++++++++++++++++++ units/db-company.js | 17 +++ units/db-connector.js | 28 ++++ units/md-errors.js | 32 ++++ 18 files changed, 1682 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 .jshintrc create mode 100644 .pgpass create mode 100644 connect.js create mode 100644 md-validator.js create mode 100644 mock.js create mode 100644 package.json create mode 100644 rebuild.sql create mode 100644 test/db-account.js create mode 100644 test/db-company.js create mode 100644 test/jshint.spec.js create mode 100644 test/md-validator.js create mode 100644 test/mocha.opts create mode 100644 units/db-accounts.js create mode 100644 units/db-company.js create mode 100644 units/db-connector.js create mode 100644 units/md-errors.js diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e86f5fa --- /dev/null +++ b/.editorconfig @@ -0,0 +1,32 @@ +; http://editorconfig.org + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 2 + +[*.txt] +insert_final_newline = false +trim_trailing_whitespace = false + +[*.py] +indent_size = 4 + +[*.m] +indent_size = 4 + +[Makefile] +indent_style = tab +indent_size = 8 + +[*.{js,json}] +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..90e76ee --- /dev/null +++ b/.gitignore @@ -0,0 +1,177 @@ +# Created by .ignore support plugin (hsz.mobi) +### Archives template +# It's better to unpack these files and commit the raw source because +# git has its own built in compression methods. +*.7z +*.jar +*.rar +*.zip +*.gz +*.bzip +*.bz2 +*.xz +*.lzma +*.cab + +#packing-only formats +*.iso +*.tar + +#package management formats +*.dmg +*.xpi +*.gem +*.egg +*.deb +*.rpm +*.msi +*.msm +*.msp +### Windows template +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk +### OSX template +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio + +*.iml + +## Directory-based project format: +.idea/ +# if you remove the above rule, at least ignore the following: + +# User-specific stuff: +# .idea/workspace.xml +# .idea/tasks.xml +# .idea/dictionaries + +# Sensitive or high-churn files: +# .idea/dataSources.ids +# .idea/dataSources.xml +# .idea/sqlDataSources.xml +# .idea/dynamic.xml +# .idea/uiDesigner.xml + +# Gradle: +# .idea/gradle.xml +# .idea/libraries + +# Mongo Explorer plugin: +# .idea/mongoSettings.xml + +## File-based project format: +*.ipr +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +### Node template +# Logs +logs +*.log +npm-debug.log* + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directory +# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git +node_modules +### VisualStudioCode template +.settings + +### Xcode template +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## Build generated +build/ +DerivedData + +## Various settings +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata + +## Other +*.xccheckout +*.moved-aside +*.xcuserstate + diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..10d1327 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,32 @@ +{ + "predef": [ + "Promise" + ], + "node":true, + "browser": false, + "boss": true, + "curly": true, + "debug": false, + "devel": true, + "eqeqeq": true, + "evil": true, + "forin": false, + "immed": false, + "laxbreak": false, + "newcap": true, + "noarg": true, + "noempty": false, + "nonew": false, + "nomen": false, + "onevar": false, + "plusplus": false, + "regexp": false, + "undef": true, + "sub": true, + "strict": false, + "white": false, + "eqnull": true, + "esnext": true, + "unused": true, + "supernew":true +} diff --git a/.pgpass b/.pgpass new file mode 100644 index 0000000..c0fd1f6 --- /dev/null +++ b/.pgpass @@ -0,0 +1 @@ +localhost:5432:oBrand:obrand:obrand \ No newline at end of file diff --git a/connect.js b/connect.js new file mode 100644 index 0000000..d975e82 --- /dev/null +++ b/connect.js @@ -0,0 +1,29 @@ +/** + * + * User: Martin Donnelly + * Date: 2016-03-09 + * Time: 11:44 + * + */ +'use strict'; +var pg = require('pg'); + +var conString = 'postgres://postgres:MPReoa43@localhost/oBrand'; + +pg.connect(conString, function(err, client, done) { + + if(err) { + return console.error('error fetching client from pool',err); + } + client.query('select $1::int as number',[1], function(err,result) { + done(); + + if (err) { + return console.error('error running query', err); + + } + + console.log(result.rows[0].number); + + }) +}); \ No newline at end of file diff --git a/md-validator.js b/md-validator.js new file mode 100644 index 0000000..dedaf2b --- /dev/null +++ b/md-validator.js @@ -0,0 +1,262 @@ +/** + * + * User: Martin Donnelly + * Date: 2016-03-09 + * Time: 14:26 + * + */ +var VALIDATE = new function() { + + this.dateBuilder = function(d, m, y) { + return new Date(Date.UTC(y, m, d,0,0,0)); + }; + + /** + * Check to see if a date is actually a valid date (e.g. FF browser converts 31 Feb to an invalid date) + * @param {number} d + * @param {number} m + * @param {number} y + * @returns {boolean} + */ + this.isDateValid = function(d, m, y) { + var enteredDate = new Date(y, m, d); + if (enteredDate.getFullYear() == y && enteredDate.getMonth() == m && enteredDate.getDate() == d) { + return true; + } + return false; + }; + + /** + * Validates emails + * @return {boolean} + */ + this.Email = function(email) { + var flag = false, b; + if (/^\w+([\+\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(email)) { + + flag = true; + } + else + flag = false; + + b = email.split("@"); + if (b.length > 2) flag = false; + if (b[0].length == 0) flag = false; + if (email.charAt(0) == '.' || email.charAt(email.length - 1) == '.') flag = false; + return flag; + }; + + /** + * + * @param inval + * @returns {boolean} + */ + + this.time = function(inval) { + "use strict"; + var rtrn = false, exp = /^([0-9]|0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$/; + + if (exp.test(inval)) { + rtrn = true; + } + + return rtrn; + }; + + /** + * + * @param inval + * @returns {boolean} + */ + this.money = function(inval) { + "use strict"; + var rtrn = '', exp = /^\xA3?\d{1,3}?([,]\d{3}|\d)*?([.]\d{1,2})?$/; + + if (exp.test(inval)) { + rtrn = exp.exec(inval); + } + + return rtrn[0]; + + }; + + /** + * Validates UK postcodes + */ + + this.checkPostCode = function(toCheck) { + + // Permitted letters depend upon their position in the postcode. + var alpha1 = '[abcdefghijklmnoprstuwyz]'; // Character 1 + var alpha2 = '[abcdefghklmnopqrstuvwxy]'; // Character 2 + var alpha3 = '[abcdefghjkpmnrstuvwxy]'; // Character 3 + var alpha4 = '[abehmnprvwxy]'; // Character 4 + var alpha5 = '[abdefghjlnpqrstuwxyz]'; // Character 5 + var BFPOa5 = '[abdefghjlnpqrst]'; // BFPO alpha5 + var BFPOa6 = '[abdefghjlnpqrstuwzyz]'; // BFPO alpha6 + + var invalidW1 = /^(W1)([eilmnopqrvwxyz]{1})(\s*)([0-9]{1}[abdefghjlnpqrstuwxyz]{2})$/i; + + // Array holds the regular expressions for the valid postcodes + var pcexp = []; + + // BFPO postcodes + pcexp.push(new RegExp('^(bf1)(\\s*)([0-6]{1}' + BFPOa5 + '{1}' + BFPOa6 + '{1})$', + 'i')); + + // Expression for postcodes: AN NAA, ANN NAA, AAN NAA, and AANN NAA + pcexp.push(new RegExp('^(' + alpha1 + '{1}' + alpha2 + '?[0-9]{1,2})(\\s*)([0-9]{1}' + alpha5 + '{2})$', + 'i')); + + // Expression for postcodes: ANA NAA + pcexp.push(new RegExp('^(' + alpha1 + '{1}[0-9]{1}' + alpha3 + '{1})(\\s*)([0-9]{1}' + alpha5 + '{2})$', + 'i')); + + // Expression for postcodes: AANA NAA + pcexp.push(new RegExp('^(' + alpha1 + '{1}' + alpha2 + '{1}' + '?[0-9]{1}' + alpha4 + '{1})(\\s*)([0-9]{1}' + alpha5 + '{2})$', + 'i')); + + // Exception for the special postcode GIR 0AA + pcexp.push(/^(GIR)(\s*)(0AA)$/i); + + // Standard BFPO numbers + pcexp.push(/^(bfpo)(\s*)([0-9]{1,4})$/i); + + // c/o BFPO numbers + pcexp.push(/^(bfpo)(\s*)(c\/o\s*[0-9]{1,3})$/i); + + // Overseas Territories + pcexp.push(/^([A-Z]{4})(\s*)(1ZZ)$/i); + + // Anguilla + pcexp.push(/^(ai-2640)$/i); + + // Load up the string to check + var postCode = toCheck; + + // Assume we're not going to find a valid postcode + var valid = false; + + // Check the string against the types of post codes + for (var i = 0; i < pcexp.length; i++) { + + if (pcexp[i].test(postCode)) { + + // The post code is valid - split the post code into component parts + pcexp[i].exec(postCode); + + // Copy it back into the original string, converting it to uppercase + // and inserting a space between the inward and outward codes + postCode = RegExp.$1.toUpperCase() + ' ' + RegExp.$3.toUpperCase(); + + // If it is a BFPO c/o type postcode, tidy up the "c/o" part + postCode = postCode.replace(/C\/O\s*/, 'c/o '); + + // If it is the Anguilla overseas territory postcode, we need to + // treat it specially + if (toCheck.toUpperCase() == 'AI-2640') {postCode = 'AI-2640'} + // Load new postcode back into the form element + valid = true; + + // Remember that we have found that the code is valid and break + // from loop + break; + } + } + + if (invalidW1.test(postCode)) { + valid = false; + } + + // Return with either the reformatted valid postcode or the original + // invalid postcode + //if (valid) {return postCode;} else return false; + if (valid) {return postCode;} + else return ''; + }; + + /** + * Validates UK Telephone numbers + * @param {string} telephoneNumber + * @returns {*} + */ + this.checkUKTelephone = function(telephoneNumber) { + + // Convert into a string and check that we were provided with something + var telNumberErrorNo, telnum = telephoneNumber + ' '; + if (telnum.length == 1) { + // telNumberErrorNo = 1; + return false; + } + telnum = (telnum).trim(); + + // Don't allow country codes to be included (assumes a leading "+") + var exp = /^(\+)[\s]*(.*)$/; + if (exp.test(telnum) == true) { + //telNumberErrorNo = 2; + return false; + } + + // Remove spaces from the telephone number to help validation + while (telnum.indexOf(' ') != -1) { + telnum = telnum.slice(0, + telnum.indexOf(' ')) + telnum.slice(telnum.indexOf(' ') + 1); + } + + // Remove hyphens from the telephone number to help validation + while (telnum.indexOf('-') != -1) { + telnum = telnum.slice(0, + telnum.indexOf('-')) + telnum.slice(telnum.indexOf('-') + 1); + } + + // Now check that all the characters are digits + + exp = /^[0-9]{10,11}$/; + if (exp.test(telnum) != true) { + // telNumberErrorNo = 3; + return false; + } + + // Now check that the first digit is 0 + exp = /^0[0-9]{9,10}$/; + if (exp.test(telnum) != true) { + // telNumberErrorNo = 4; + return false; + } + + // Disallow numbers allocated for dramas. + + // Array holds the regular expressions for the drama telephone numbers + var tnexp = []; + tnexp.push(/^(0113|0114|0115|0116|0117|0118|0121|0131|0141|0151|0161)(4960)[0-9]{3}$/); + tnexp.push(/^02079460[0-9]{3}$/); + tnexp.push(/^01914980[0-9]{3}$/); + tnexp.push(/^02890180[0-9]{3}$/); + tnexp.push(/^02920180[0-9]{3}$/); + tnexp.push(/^01632960[0-9]{3}$/); + tnexp.push(/^07700900[0-9]{3}$/); + tnexp.push(/^08081570[0-9]{3}$/); + tnexp.push(/^09098790[0-9]{3}$/); + tnexp.push(/^03069990[0-9]{3}$/); + + for (var i = 0; i < tnexp.length; i++) { + if (tnexp[i].test(telnum)) { + telNumberErrorNo = 5; + return false; + } + } + + // Finally check that the telephone number is appropriate. + exp = (/^(01|02|03|05|070|071|072|073|074|075|07624|077|078|079)[0-9]+$/); + if (exp.test(telnum) != true) { + // telNumberErrorNo = 5; + return false; + } + + // Telephone number seems to be valid - return the stripped telehone number + return telnum; + }; +}; + + +module.exports = VALIDATE; diff --git a/mock.js b/mock.js new file mode 100644 index 0000000..cb569f0 --- /dev/null +++ b/mock.js @@ -0,0 +1,38 @@ +/** + * + * User: Martin Donnelly + * Date: 2016-03-09 + * Time: 15:18 + * + */ + + +var dbAccounts = require('units/db-accounts'); +var log4js = require('log4js'); +var logger = log4js.getLogger(); + +var dbAccount = dbAccounts; + +var profileData = { + forename:'Tim', + surname:'Timmington', + gender:0, + dob:'2000-01-01', + bio:'He is forbidden from walking through cemetaries, because of that one incident where he raised the dead' + }; + return dbAccount.findAccount({ + password: 'b0b5p455w0rd', + email: 'bob@tim.com' + }) + .then(function(data) { + profileData.uid = data.uid; + return dbAccount.addInsertProfile(profileData) + .then(function(sqlResponse){ + logger.debug(sqlResponse); + }); + }) + .catch(function(err) { + console.log(err); + //err.should.not.be.ok(); + }); + diff --git a/package.json b/package.json new file mode 100644 index 0000000..87773c8 --- /dev/null +++ b/package.json @@ -0,0 +1,33 @@ +{ + "name": "dbwork", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "mocha --recursive --reporter spec --bail --check-leaks --timeout 3000" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "after": "^0.8.1", + "assert": "^1.3.0", + "bcrypt-as-promised": "^1.1.0", + "chai": "^3.5.0", + "exec": "^0.2.1", + "expect": "^1.15.1", + "log4js": "^0.6.33", + "mocha": "^2.4.5", + "mocha-jshint": "^2.3.1", + "moment": "^2.12.0", + "node-validator": "http://gitlab.silvrtree.co.uk/martind2000/node-validator.git", + "pg-promise": "^3.3.0", + "should": "^8.2.2", + "superagent": "^1.8.0-beta.2", + "supertest": "^1.2.0", + "uuid-pure": "^1.0.10" + }, + "dependencies": { + "bcrypt": "^0.8.5", + "pg": "^4.5.1" + } +} diff --git a/rebuild.sql b/rebuild.sql new file mode 100644 index 0000000..92ebbd6 --- /dev/null +++ b/rebuild.sql @@ -0,0 +1,127 @@ +ALTER TABLE "profile" DROP CONSTRAINT IF EXISTS "profile_fk0"; + +ALTER TABLE "profile" DROP CONSTRAINT IF EXISTS "profile_fk1"; + +ALTER TABLE "venue" DROP CONSTRAINT IF EXISTS "venue_fk0"; + +ALTER TABLE "billing" DROP CONSTRAINT IF EXISTS "billing_fk0"; + +DROP TABLE IF EXISTS "logins"; + +DROP TABLE IF EXISTS "profile"; + +DROP TABLE IF EXISTS "company"; + +DROP TABLE IF EXISTS "venue"; + +DROP TABLE IF EXISTS "billing"; + +DROP TABLE IF EXISTS "master_beacons"; + + +CREATE TABLE "logins" ( + "id" serial NOT NULL, + "username" varchar(100) NOT NULL UNIQUE, + "email" varchar(150) NOT NULL UNIQUE, + "password_hash" varchar(78) NOT NULL, + "password_reset_token" varchar(128) UNIQUE, + "uid" varchar(22) UNIQUE, + CONSTRAINT logins_pk PRIMARY KEY ("id") +) WITH ( + OIDS=FALSE +); + + + +CREATE TABLE "profile" ( + "id" serial NOT NULL, + "uid" varchar(22) NOT NULL, + "forename" varchar(75) NOT NULL, + "surname" varchar(75) NOT NULL, + "gender" int NOT NULL, + "dob" DATE NOT NULL, + "bio" TEXT NOT NULL, + "member_of" int, + CONSTRAINT profile_pk PRIMARY KEY ("id") +) WITH ( + OIDS=FALSE +); + + + +CREATE TABLE "company" ( + "id" serial NOT NULL, + "company_name" varchar(100) NOT NULL, + "address1" varchar(150) NOT NULL, + "address2" varchar(150) NOT NULL, + "address3" varchar(150) NOT NULL, + "town" varchar(150) NOT NULL, + "county" varchar(150) NOT NULL, + "postcode" varchar(12) NOT NULL, + "country" int NOT NULL, + "pcontact" varchar(20) NOT NULL, + "ocontact" varchar(20) NOT NULL, + "mobile" varchar(20) NOT NULL, + "email" varchar(150) NOT NULL, + CONSTRAINT company_pk PRIMARY KEY ("id") +) WITH ( + OIDS=FALSE +); + + + +CREATE TABLE "venue" ( + "id" serial NOT NULL, + "venue_name" varchar(100) NOT NULL, + "address1" varchar(150) NOT NULL, + "address2" varchar(150) NOT NULL, + "address3" varchar(150) NOT NULL, + "town" varchar(150) NOT NULL, + "county" varchar(150) NOT NULL, + "postcode" varchar(12) NOT NULL, + "country" int NOT NULL, + "pcontact" varchar(20) NOT NULL, + "ocontact" varchar(20) NOT NULL, + "mobile" varchar(20) NOT NULL, + "email" varchar(150) NOT NULL, + "company_id" int NOT NULL, + CONSTRAINT venue_pk PRIMARY KEY ("id") +) WITH ( + OIDS=FALSE +); + + + +CREATE TABLE "billing" ( + "id" serial NOT NULL, + "company_id" int NOT NULL, + CONSTRAINT billing_pk PRIMARY KEY ("id") +) WITH ( + OIDS=FALSE +); + + + +CREATE TABLE "master_beacons" ( + "id" bigint NOT NULL, + "uid" uuid NOT NULL, + CONSTRAINT master_beacons_pk PRIMARY KEY ("id") +) WITH ( + OIDS=FALSE +); + + + + +ALTER TABLE "profile" ADD CONSTRAINT "profile_fk0" FOREIGN KEY ("user_id") REFERENCES "logins"("id"); +ALTER TABLE "profile" ADD CONSTRAINT "profile_fk1" FOREIGN KEY ("member_of") REFERENCES "company"("id"); + + +ALTER TABLE "venue" ADD CONSTRAINT "venue_fk0" FOREIGN KEY ("company_id") REFERENCES "company"("id"); + +ALTER TABLE "billing" ADD CONSTRAINT "billing_fk0" FOREIGN KEY ("company_id") REFERENCES "company"("id"); + +grant connect on database "oBrand" to obrand; +GRANT SELECT, UPDATE, INSERT, DELETE, TRIGGER ON TABLE public.logins TO obrand; +grant select, update on logins_id_seq to obrand; +grant select, update on profile_id_seq to obrand; diff --git a/test/db-account.js b/test/db-account.js new file mode 100644 index 0000000..c4fb69d --- /dev/null +++ b/test/db-account.js @@ -0,0 +1,321 @@ +"use strict"; + +var should = require('should'); +var assert = require('assert'); +var db = require('../units/db-connector').dbConnection; +var dbAccount = require('../units/db-accounts')(db); +var exec = require('child_process').exec; + +function prepare_db(next) { + exec('psql -Upostgres -d oBrand -f ./rebuild.sql', function(err) { + if (err !== null) { + console.log('exec error: ' + err); + } + }); +} + +describe('Accounts DB', function() { + before(function() { + console.log('Prepare database'); + + prepare_db(function(err) { + if (err) { + console.log('Problem setting up database'); + } + }); + }); + + + it('should connect to Postgres using promises', function() { + + return dbAccount.connectPGP().then(function(data) { + + assert.equal(data[0].number, 1); + }); + + }); + + it('should not add user with no data', function() { + + return dbAccount.addNewAccount({}).then(function() { + + }) + .catch(function(err) { + assert.notEqual(err, null); + }); + + }); + + it('should not add user with no details', function() { + + return dbAccount.addNewAccount({ + username: '', password: '', email: '' + }).then(function(data) { + + }) + .catch(function(err) { + assert.notEqual(err, null); + err.should.have.property('code', 1000); + }); + + }); + + it('should not add an empty name', function() { + return dbAccount.addNewAccount({ + username: '', password: 'b0b5p455w0rd', email: 'bob@tim.com' + }).then(function(data) { + + }) + .catch(function(err) { + err.should.have.property('code', 1000); + }); + + }); + + it('should not add user with missing password', function() { + return dbAccount.addNewAccount({ + username: 'bob', password: '', email: 'bob@tim.com' + }).then(function(data) { + + }) + .catch(function(err) { + err.should.have.property('code', 1000); + }); + + }); + + it('should not add user with missing email', function() { + return dbAccount.addNewAccount({ + username: 'bob', password: 'b0b5p455w0rd', email: '' + }).then(function(data) { + + }) + .catch(function(err) { + err.should.have.property('code', 1000); + }); + + }); + + it('should not add user with invalid email', function() { + return dbAccount.addNewAccount({ + username: 'bob', password: 'b0b5p455w0rd', email: 'tim' + }).then(function(data) { + + }) + .catch(function(err) { + err.should.have.property('code', 1001); + }); + + }); + + it('should add a new user', function() { + return dbAccount.addNewAccount({ + username: 'bob', + password: 'b0b5p455w0rd', + email: 'bob@tim.com' + }) + .then(function(data) { + + should(data.reply).equal('user added'); + }) + .catch(function(err) { + console.log(err); + err.should.not.be.ok(); + }); + + }); + + it('should not add a duplicate user', function() { + return dbAccount.addNewAccount({ + username: 'bob', + password: 'b0b5p455w0rd', + email: 'bob@tim.com' + }) + .then(function(data) { + + should(data.reply).equal('user added'); + }) + .catch(function(err) { + console.log(err); + err.should.have.property('detail', + 'Key (username)=(bob) already exists.'); + }); + + }); + + it('should find a valid account', function() { + return dbAccount.findAccount({ + password: 'b0b5p455w0rd', + email: 'bob@tim.com' + }) + .then(function(data) { + + data.should.have.property('username','bob'); + }) + .catch(function(err) { + console.log(err); + err.should.be.ok(); + }); + + }); + + it('should not allow login with incorrect password', function() { + return dbAccount.findAccount({password: 'bobspassword', email: 'bob@tim.com'}) + .then(function(data) { + + data.should.have.property('username','bob'); + }) + .catch(function(err) { + err.should.have.property('code', 1004); + }); + + }); + + it('should not allow login with incorrect email', function() { + return dbAccount.findAccount({password: 'b0b5p455w0rd', email: 'tim@bob.com'}) + .then(function(data) { + + data.should.have.property('username','bob'); + }) + .catch(function(err) { + err.should.have.property('code', 1004); + }); + + }); + + it('should return account details for a valid account', function() { + return dbAccount.findAccount({ + password: 'b0b5p455w0rd', + email: 'bob@tim.com' + }) + .then(function(data) { + return dbAccount.sqlGetAccountDetails(data.uid) + .then(function(wantedData){ + console.log(wantedData); + wantedData.should.have.property('username','bob'); + }); + }) + .catch(function(err) { + console.log(err); + err.should.be.ok(); + }); + + }); + it('should return account details for a valid account', function() { + return dbAccount.sqlGetAccountDetails('DEADBEEFDEADBEEF') + .then(function(wantedData){ + wantedData.should.have.property('username','bob'); + }).catch(function(err) { + console.log(err); + err.should.be.ok(); + }); + }); + + it('should insert profile details', function() { + let profileData = { + forename:'Bob', + surname:'Bobbington', + gender:0, + dob:'1974-10-24', + bio:'Sharks have a week dedicated to him' + }; + + return dbAccount.findAccount({ + password: 'b0b5p455w0rd', + email: 'bob@tim.com' + }) + .then(function(data) { + profileData.uid = data.uid; + return dbAccount.addInsertProfile(profileData) + .then(function(sqlResponse){ + sqlResponse.should.be.ok(); + }); + }) + .catch(function(err) { + console.log(err); + err.should.not.be.ok(); + }); + }); + + it('should insert update details', function() { + let profileData = { + forename:'Tim', + surname:'Timmington', + gender:0, + dob:'2000-01-01', + bio:'He is forbidden from walking through cemetaries, because of that one incident where he raised the dead' + }; + return dbAccount.findAccount({ + password: 'b0b5p455w0rd', + email: 'bob@tim.com' + }) + .then(function(data) { + profileData.uid = data.uid; + return dbAccount.addInsertProfile(profileData) + .then(function(sqlResponse){ + sqlResponse.should.be.ok(); + }); + }) + .catch(function(err) { + console.log(err); + err.should.not.be.ok(); + }); + }); + + it('should return profile details', function() { + + return dbAccount.findAccount({ + password: 'b0b5p455w0rd', + email: 'bob@tim.com' + }) + .then(function(data) { + + return dbAccount.sqlGetProfile(data.uid) + .then(function(sqlResponse){ + console.log(sqlResponse); + sqlResponse.should.have.property('forename','Tim'); + }); + }) + .catch(function(err) { + console.log(err); + err.should.not.be.ok(); + }); + }); + + it('should add Martin', function() { + return dbAccount.addNewAccount({ + username: 'Martin ', + password: 'MPReoa43', + email: 'martind2000@gmail.com' + }) + .then(function(data) { + + should(data.reply).equal('user added'); + }) + .catch(function(err) { + console.log(err); + err.should.not.be.ok(); + }); + + }); + + it('should add Default', function() { + return dbAccount.addNewAccount({ + username: 'Default', + password: 'password', + email: 'm@g.com' + }) + .then(function(data) { + + should(data.reply).equal('user added'); + }) + .catch(function(err) { + console.log(err); + err.should.not.be.ok(); + }); + + }); + + // TEST END HERE +}); + diff --git a/test/db-company.js b/test/db-company.js new file mode 100644 index 0000000..86f98a9 --- /dev/null +++ b/test/db-company.js @@ -0,0 +1,27 @@ +/** + * + * User: Martin Donnelly + * Date: 2016-03-15 + * Time: 14:26 + * + */ +"use strict"; + +var should = require('should'); +var assert = require('assert'); +var db = require('../units/db-connector').dbConnection; +var dbCompany = require('../units/db-company')(db); + +describe('abc Setup DB', function() { + +it('should not add user with no data', function() { + + return dbCompany.addNewCompany({}).then(function() { + + }) + .catch(function(err) { + assert.notEqual(err, null); + }); + + }); +}); diff --git a/test/jshint.spec.js b/test/jshint.spec.js new file mode 100644 index 0000000..6bf87ce --- /dev/null +++ b/test/jshint.spec.js @@ -0,0 +1,8 @@ +/** + * + * User: Martin Donnelly + * Date: 2016-03-15 + * Time: 13:42 + * + */ +require('mocha-jshint')({paths:['units/']}); diff --git a/test/md-validator.js b/test/md-validator.js new file mode 100644 index 0000000..3bd8dea --- /dev/null +++ b/test/md-validator.js @@ -0,0 +1,341 @@ +/** + * + * User: Martin Donnelly + * Date: 2016-03-09 + * Time: 14:37 + * + */ +"use strict"; +var mdValidate = require('node-validator'); + +var should = require('should'); +var assert = require('assert'); +var expect = require('expect'); + +var phone = [ + 'A', + 'AA', + 'AAA', + 'AAAA', + 'AAAAA', + 'AAAAAA', + '0', + '01', + '012', + '0123', + '01234', + '012345', + '0123456', + '01234567', + '012345678', + '01134960000', + '01134960500', + '01134960999', + '01144960000', + '01144960500', + '01144960999', + '01154960000', + '01154960500', + '01154960999', + '01164960000', + '01164960500', + '01164960999', + '01174960000', + '01174960500', + '01174960999', + '01184960000', + '01184960500', + '01184960999', + '01214960000', + '01214960500', + '01214960999', + '01314960000', + '01314960500', + '01314960999', + '01414960000', + '01414960500', + '01414960999', + '01514960000', + '01514960500', + '01514960999', + '01614960000', + '01614960500', + '01614960999', + '02079460000', + '02079460500', + '02079460999', + '01914980000', + '01914980500', + '01914980999', + '02890180000', + '02890180500', + '02890180999', + '02920180000', + '02920180500', + '02920180999', + '01632960000', + '01632960500', + '01632960999', + '07700900000', + '07700900500', + '07700900999', + '08081570000', + '08081570555', + '08081570999', + '09098790000', + '09098790500', + '09098790999', + '03069990000', + '03069990500', + '03069990999', + '08002323636', + '09002323636', + '06002323636', + "00254745856", + "+254745856" +]; + +var validPhone = ['01389703002', '07944577934', '01412807000', '02071539996']; + +var postcodes = [ + 'A', + 'AA', + 'AAA', + 'AAAA', + 'AAAAA', + 'AAAAAA', + 'Q1 1AA', + 'V1 1AA', + 'X1 1AA', + 'M1 CAA', + 'M1 IAA', + 'M1 KAA', + 'M1 MAA', + 'M1 OAA', + 'M1 VAA', + + 'Q60 1NW', + 'V60 1NW', + 'X60 1NW', + 'M60 1CW', + 'M60 1IW', + 'M60 1KW', + 'M60 1MW', + 'M60 1OW', + 'M60 1VW', + 'M60 1NC', + 'M60 1NI', + 'M60 1NK', + 'M60 1NM', + 'M60 1NO', + 'M60 1NV', + + 'QR2 6XH', + 'VR2 6XH', + 'XR2 6XH', + 'CI2 6XH', + 'CJ2 6XH', + 'CZ2 6XH', + 'CR2 6CH', + 'CR2 6IH', + 'CR2 6KH', + 'CR2 6MH', + 'CR2 6OH', + 'CR2 6VH', + 'CR2 6XC', + 'CR2 6XI', + 'CR2 6XK', + 'CR2 6XM', + 'CR2 6XO', + 'CR2 6XV', + + 'QN55 1PT', + 'VN55 1PT', + 'XN55 1PT', + 'DI55 1PT', + 'DJ55 1PT', + 'DZ55 1PT', + 'DN55 1CT', + 'DN55 1IT', + 'DN55 1KT', + 'DN55 1MT', + 'DN55 1OT', + 'DN55 1VT', + 'DN55 1PC', + 'DN55 1PI', + 'DN55 1PK', + 'DN55 1PM', + 'DN55 1PO', + 'DN55 1PV', + + 'Q1A 1HQ', + 'V1A 1HQ', + 'X1A 1HQ', + 'W1I 1HQ', + 'W1L 1HQ', + 'W1M 1HQ', + 'W1N 1HQ', + 'W1O 1HQ', + 'W1P 1HQ', + 'W1Q 1HQ', + 'W1R 1HQ', + 'W1V 1HQ', + 'W1X 1HQ', + 'W1Y 1HQ', + 'W1Z 1HQ', + 'W1A 1CQ', + 'W1A 1IQ', + 'W1A 1KQ', + 'W1A 1MQ', + 'W1A 1OQ', + 'W1A 1VQ', + 'W1A 1HC', + 'W1A 1HI', + 'W1A 1HK', + 'W1A 1HM', + 'W1A 1HO', + 'W1A 1HV', + + 'QC1A 1BB', + 'VC1A 1BB', + 'XC1A 1BB', + 'EI1A 1BB', + 'EJ1A 1BB', + 'EZ1A 1BB', + 'EC1C 1BB', + 'EC1D 1BB', + 'EC1F 1BB', + 'EC1G 1BB', + 'EC1I 1BB', + 'EC1J 1BB', + 'EC1K 1BB', + 'EC1L 1BB', + 'EC1O 1BB', + 'EC1Q 1BB', + 'EC1S 1BB', + 'EC1T 1BB', + 'EC1U 1BB', + 'EC1Z 1BB', + 'EC1A 1IB', + 'EC1A 1MB', + 'EC1A 1OB', + 'EC1A 1VB', + 'EC1A 1BC', + 'EC1A 1BI', + 'EC1A 1BK', + 'EC1A 1BM', + 'EC1A 1BO', + 'EC1A 1BV' + +]; + +var validPostcodes = [ + 'M1 1AA', + 'M60 1NW', + 'CR2 6XH', + 'DN55 1PT', + 'W1A 1HQ', + 'EC1A 1BB', + 'BT9 7JL', + 'GIR 0AA' + +]; + +// taken from http://blogs.msdn.com/b/testing123/archive/2009/02/05/email-address-test-cases.aspx +// having difficulty invalidating /*'あいうえお@domain.com',*/ +// Several test cases for the email address have been removed as they should really be fixed by the mail system +// and they would be costly to fix in code at the moment + +var invalidEmail = [ + 'plainaddress', + '#@%^%#$@#$@#.com', + '@domain.com', + 'Joe Smith ', + 'email.domain.com', + 'email@domain@domain.com', + '.email@domain.com', + 'email.@domain.com', + 'email..email@domain.com', + 'email@domain.com (Joe Smith)', + 'email@domain', + 'email@-domain.com', + /*'email@domain.web',*/ + 'email@111.222.333.44444', + 'email@domain..com' +]; + +var validEmail = [ + 'email@domain.com', + 'firstname.lastname@domain.com', + 'email@subdomain.domain.com', + 'firstname+lastname@domain.com', + 'email@123.123.123.123', + /*'email@[123.123.123.123]',*/ + /*'"email"@domain.com',*/ + '1234567890@domain.com', + 'email@domain-one.com', + '_______@domain.com', + /*'email@domain.name',*/ + 'email@domain.co.uk', + 'firstname-lastname@domain.com' +]; + +var emailOne = ['m@g.com', 'm@g.co.uk', 'bob@g.com']; +var emailTwo = ['m@g.co.uk', 'bob@g.com', 'm@g.com']; + +describe('MD-Validator', function() { + it('should not validate an incorrect email address', function(done) { + + for (var i in invalidEmail) { + mdValidate.Email(invalidEmail[i]).should.not.be.ok(); + + } + done(); + }); + + it('should validate a correct email address', function(done) { + + for (var i in validEmail) { + mdValidate.Email(validEmail[i]).should.be.ok(); + + } + done(); + }); + + it('should not validate an incorrect phone number', function(done) { + + for (var i in phone) { + mdValidate.checkUKTelephone(phone[i]).should.not.be.ok(); + + } + done(); + }); + + it('should validate a correct phone number', function(done) { + + for (var i in validPhone) { + mdValidate.checkUKTelephone(validPhone[i]).should.be.ok(); + + } + done(); + }); + + it('should not validate an incorrect postcode', function(done) { + + for (var i in postcodes) { + mdValidate.checkPostCode(postcodes[i]).should.not.be.ok(); + + } + done(); + }); + + it('should validate a correct postcode', function(done) { + + for (var i in validPostcodes) { + mdValidate.checkPostCode(validPostcodes[i]).should.be.ok(); + + } + done(); + }); + +}); diff --git a/test/mocha.opts b/test/mocha.opts new file mode 100644 index 0000000..0781660 --- /dev/null +++ b/test/mocha.opts @@ -0,0 +1,4 @@ +--require should +--slow 200 +--growl +--sort diff --git a/units/db-accounts.js b/units/db-accounts.js new file mode 100644 index 0000000..9e57be6 --- /dev/null +++ b/units/db-accounts.js @@ -0,0 +1,173 @@ +'use strict'; + +//var logger = require('log4js').getLogger(); +var mdValidator = require('node-validator'), mdErrors = require('./md-errors'); +var bcrypt = require('bcrypt-as-promised'); +var newId = require('uuid-pure').newId; + + +//var db = require('./db-connector').dbConnection; + +module.exports = function(db) { + var module = {}; + + module.connectPGP= function() { + return new Promise(function(resolve, reject) { + db.query('select $1::int as number', [1]) + .then((data)=>{ + console.log(data); + resolve(data); + }) + .catch((error)=>{ + reject(error); + }); + }); + }; + module.sqlInsertAccount= function(data) { + let _data = data; + _data.uid = newId(); + return new Promise(function(resolve, reject) { + db.func('insert_user',[_data.username, _data.email, _data.hash, _data.uid]) + .then(()=> { + return resolve('ok'); + }) + .catch((err)=> { + return reject(err); + }); + }); + }; + module.sqlGetAccount= function(email) { + return new Promise(function(resolve, reject) { + db.oneOrNone("select * from logins where email=$1;",[email]) + .then(function(d) { + return resolve(d); + }) + .catch((err)=> { + return reject(err); + }); + }); + }; + module.sqlGetAccountDetails= function(uid) { + return new Promise(function(resolve, reject) { + db.one("select * from logins where uid=$1;",[uid]) + .then(function(d) { + return resolve(d); + }) + .catch((err)=> { + return reject(err); + }); + }); + }; + module.sqlUpsertProfile= function(data) { + var propArray=[data.uid ,data.forename, data.surname, data.gender, data.dob, data.bio]; + + return new Promise(function(resolve, reject) { + db.func('upsert_profile',propArray) + .then(()=> { + return resolve(true); + }) + .catch((err)=> { + return reject(err); + }); + }); + }; + module.sqlGetProfile= function(uid) { + return new Promise(function(resolve, reject) { + db.oneOrNone("select * from profile where uid=$1;",[uid]) + .then(function(d) { + return resolve(d); + }) + .catch((err)=> { + return reject(err); + }); + }); + }; + + module.addNewAccount= function(data) { +var self = this; + return new Promise((resolve, reject) => { + + let _data = {}; + _data.username = data.username.trim(); + _data.password = data.password.trim(); + _data.email = data.email.trim(); + + if (Object.keys(data).length === 3) { + + if (_data.username.length === 0 || _data.password.length === 0 || _data.email.length === 0) + { + return reject(mdErrors.error(1000)); + } + + if (mdValidator.Email(data.email) === false) + { + return reject(mdErrors.error(1001)); + } else + { + // It should be possible to insert the user now. + bcrypt.hash(data.password,10).then((d) => { + _data.hash = d; + self.sqlInsertAccount(_data) + .then(()=>{ + return resolve({reply:'user added'}); + }) + .catch((err)=>{ + return reject(err); + }); + }); + + } + } else + { + // error - required details missing + return reject(mdErrors.error(1002)); + + } + }); + }; + module.findAccount= function(data) { + + var _data = data; + return new Promise((resolve, reject) => { + this.sqlGetAccount(_data.email) + .then((row) => { + if (row === null) { + return reject(mdErrors.error(1004)); + } + // check password against hash held in db + bcrypt.compare(_data.password, row.password_hash) + .then(function() { + let loginDetails = { + id: row.id, + username: row.username, + email: row.email, + uid: row.uid + }; + return resolve(loginDetails); + }) + // failure, reject + .catch(function() { + return reject(mdErrors.error(1004)); + }); + + }) + .catch(function(err){ + return reject(err); + }); + }); + }; + module.addInsertProfile= function(data) { + return new Promise((resolve, reject) => { + this.sqlUpsertProfile(data) + .then((d)=>{ + return resolve(d); + }) + .catch((err)=>{ + return reject(err); + }); + }); + }; + + return module; +}; + diff --git a/units/db-company.js b/units/db-company.js new file mode 100644 index 0000000..0dd7fcb --- /dev/null +++ b/units/db-company.js @@ -0,0 +1,17 @@ +/** + * + * User: Martin Donnelly + * Date: 2016-03-15 + * Time: 14:04 + * + */ +'use strict'; +var mdValidator = require('node-validator'), mdErrors = require('./md-errors'); +var newId = require('uuid-pure').newId; + +module.exports = function(db) { + var module = {}; + + + return module; +}; diff --git a/units/db-connector.js b/units/db-connector.js new file mode 100644 index 0000000..0589c91 --- /dev/null +++ b/units/db-connector.js @@ -0,0 +1,28 @@ +'uses strict'; +/** + * + * User: Martin Donnelly + * Date: 2016-03-11 + * Time: 10:22 + * + */ + +var pgp = require('pg-promise')(); +var monitor = require('pg-monitor'); + +var options = {}; + +monitor.attach(options); + +var cn = { + host:'localhost', + port: 5432, + database: 'oBrand', + user:'obrand', + password:'obrand' +}; + +//exports pgp(cn); + +exports.dbConnection = pgp(cn); + diff --git a/units/md-errors.js b/units/md-errors.js new file mode 100644 index 0000000..3f00e36 --- /dev/null +++ b/units/md-errors.js @@ -0,0 +1,32 @@ +/** + * + * User: Martin Donnelly + * Date: 2016-03-10 + * Time: 11:31 + * + */ +var logger = require('log4js').getLogger(); + +var MDERRORS = new function() { + "use strict"; + + var errors = { + 1000: {name:'Account error', title:'Signup data missing'}, + 1001: {name:'Account error', title:'Email address is not in the correct format'}, + 1002: {name:'Account error', title:'Required details missing'}, + 1003: {name:'Account error', title:'Email does not exist in login table'}, + 1004: {name:'Account error', title:'Email address or password are incorrect'} + }; + + this.error = function(code) + { + var estring = ''; + estring = errors[code].name + ': ' + errors[code].title + '\nCode: ' + code + '\n'; + + logger.error(estring); + + return({code:code, name:errors[code].name, title:errors[code].title, string:estring}); + }; +}(); + +module.exports = MDERRORS;