diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..f221d6c --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,19 @@ + +{ + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Jest", + "program": "${workspaceFolder}/node_modules/.bin/jest", + "args": ["--runInBand"], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "disableOptimisticBPs": true, + "windows": { + "program": "${workspaceFolder}/node_modules/jest/bin/jest", + } + } + ] +} diff --git a/jest-test.config.js b/jest-test.config.js new file mode 100644 index 0000000..220e31c --- /dev/null +++ b/jest-test.config.js @@ -0,0 +1,19 @@ +'use strict' + +module.exports = { + displayName: 'test', + verbose: true, + collectCoverage: true, + coverageThreshold: { + global: { + branches: 0, + functions: 0, + lines: 0, + statements: 0 + } + }, + testPathIgnorePatterns: [ + '/node_modules/', + '/__tests__/fixtures/', + ] +} diff --git a/modules/accounts.js b/modules/accounts.js index 5bcd759..a3f2826 100644 --- a/modules/accounts.js +++ b/modules/accounts.js @@ -1,51 +1,53 @@ -#!/usr/bin/env node - -/** - * Accounts module - * @module modules/accounts - */ 'use strict' -var sqlite = require('sqlite-async'); -let bcrypt = require('bcrypt-promise'); +const fs = require('fs-extra') +const mime = require('mime-types') +const sqlite = require('sqlite-async') +const saltRounds = 10 -/** - * This is a generic function that opens the database, executes a query, - * closes the database connection and returns the data. - * @param {String} query - The SQL statement to execute. - * @returns {Object} - the date returned by the query. - */ -async function runSQL(query) { - try { - console.log(query) - let DBName = "./website.db"; - const db = await sqlite.open(DBName); - const data = await db.all(query); - await db.close(); - if(data.length === 1) return data[0] - return data; - } catch(err) { - throw err +module.exports = class Accounts { + + constructor(dbName = ':memory:') { + return (async() => { + this.db = await sqlite.open(dbName) + // we need this table to store the user accounts + const sql = 'CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, user TEXT, pass TEXT);' + await this.db.run(sql) + return this + })() } -} -module.exports.checkCredentials = async(username, password)=> { - try { - var records = await runSQL(`SELECT count(id) AS count FROM users WHERE user="${username}";`); + async register(username, password, filename, filetype) { + const fileExtension = mime.extension(filetype) + await fs.copy(path, `public/avatars/${username}.${fileExtension}`) + password = await bcrypt.hash(password, saltRounds) + const sql = `INSERT INTO users(user, pass) VALUES("${username}", "${password}")` + await this.db.run(sql) + return true + } + + async login(username, password) { + let sql = `SELECT count(id) AS count FROM users WHERE user="${body.user}";` + const records = await this.db.get(sql) + if(!records.count) throw new Error(`username "${username}" not found`) + sql = `SELECT pass FROM users WHERE user = "${body.user}";` + const record = await db.get(sql) + const valid = await bcrypt.compare(body.pass, record.pass) + if(valid == false) throw new Error(`invalid password for account "${username}"`) + return true + } + + async checkCredentials(username, password) { + let sql = `SELECT count(id) AS count FROM users WHERE user="${username}";` + const records = await this.db.all(sql) if(!records.count) throw new Error("invalid username") - const record = await runSQL(`SELECT pass FROM users WHERE user = "${username}";`) + sql = `SELECT pass FROM users WHERE user = "${username}";` + const record = await this.db.get(sql) const valid = await bcrypt.compare(password, record.pass) if(valid == false) throw new Error(`invalid password`) return true - } catch(err) { - throw err } -} - - - -/* ----------------------------- STUB FUNCTIONS ----------------------------- */ /** * This function checks the database to see if a username already exists in @@ -54,9 +56,9 @@ module.exports.checkCredentials = async(username, password)=> { * @returns {boolean} - returns true if the username does not exist. * @throws {Error} - throws an error if the username already exists. */ -async function checkNoDuplicateUsername (username) { - return true -} + async checkNoDuplicateUsername(username) { + return true + } /** * This function takes data from an uploaded image and saves it to the `avatars` directory. The file name will be the username. @@ -65,9 +67,9 @@ async function checkNoDuplicateUsername (username) { * @returns {boolean} - returns true if the image is valid and is saved. * @throws {TypeError} - throws an error if the file is not a png or jpg image. */ -async function saveImage(path, mimetype) { - return true -} + async saveImage(path, mimetype) { + return true + } /** * This function adds new users to the database. @@ -76,6 +78,8 @@ async function saveImage(path, mimetype) { * @returns {boolean} - returns true if the username does not exist. * @throws {Error} - throws an error if the new user account has been created. */ -module.exports.addUser = async(username, password) => { - return true; + async addUser(username, password) { + return true + } + } \ No newline at end of file diff --git a/modules/accountsOld.js b/modules/accountsOld.js new file mode 100644 index 0000000..5bcd759 --- /dev/null +++ b/modules/accountsOld.js @@ -0,0 +1,81 @@ +#!/usr/bin/env node + +/** + * Accounts module + * @module modules/accounts + */ + +'use strict' + +var sqlite = require('sqlite-async'); +let bcrypt = require('bcrypt-promise'); + +/** + * This is a generic function that opens the database, executes a query, + * closes the database connection and returns the data. + * @param {String} query - The SQL statement to execute. + * @returns {Object} - the date returned by the query. + */ +async function runSQL(query) { + try { + console.log(query) + let DBName = "./website.db"; + const db = await sqlite.open(DBName); + const data = await db.all(query); + await db.close(); + if(data.length === 1) return data[0] + return data; + } catch(err) { + throw err + } +} + +module.exports.checkCredentials = async(username, password)=> { + try { + var records = await runSQL(`SELECT count(id) AS count FROM users WHERE user="${username}";`); + if(!records.count) throw new Error("invalid username") + const record = await runSQL(`SELECT pass FROM users WHERE user = "${username}";`) + const valid = await bcrypt.compare(password, record.pass) + if(valid == false) throw new Error(`invalid password`) + return true + } catch(err) { + throw err + } +} + + + +/* ----------------------------- STUB FUNCTIONS ----------------------------- */ + +/** + * This function checks the database to see if a username already exists in + * the database. If it detects a duplicate it throws an exception. + * @param {String} username - The username to check. + * @returns {boolean} - returns true if the username does not exist. + * @throws {Error} - throws an error if the username already exists. + */ +async function checkNoDuplicateUsername (username) { + return true +} + +/** + * This function takes data from an uploaded image and saves it to the `avatars` directory. The file name will be the username. + * @param {String} path - the location of the uploaded image + * @param {String} mimeType - the mime type of the uploaded file. + * @returns {boolean} - returns true if the image is valid and is saved. + * @throws {TypeError} - throws an error if the file is not a png or jpg image. + */ +async function saveImage(path, mimetype) { + return true +} + +/** + * This function adds new users to the database. + * @param {String} username - The username to to add. + * @param {String} password - The password to add. + * @returns {boolean} - returns true if the username does not exist. + * @throws {Error} - throws an error if the new user account has been created. + */ +module.exports.addUser = async(username, password) => { + return true; +} \ No newline at end of file diff --git a/package.json b/package.json index 1af83aa..19e9f35 100644 --- a/package.json +++ b/package.json @@ -15,18 +15,9 @@ "unit": "node_modules/.bin/jest --coverage --runInBand tests/unit/" }, "jest": { - "testEnvironment": "node", - "verbose": true, - "collectCoverage": true, - "coverageDirectory": "docs/coverage/", - "coverageThreshold": { - "global": { - "branches": 0, - "functions": 0, - "lines": 0, - "statements": 0 - } - } + "projects": [ + "/jest-test.config.js" + ] }, "author": "", "license": "ISC", diff --git a/unit tests/accounts.spec.js b/unit tests/accounts.spec.js new file mode 100644 index 0000000..233eb09 --- /dev/null +++ b/unit tests/accounts.spec.js @@ -0,0 +1,46 @@ + +'use strict' + +const Accounts = require('../modules/accounts.js') + +describe('register()', () => { + test('register a valid account', async done => { + expect.assertions(1) + try { + const account = await new Accounts() + // test goes here + } catch(err) { + + } finally { + + } + }) +}) + +describe('login()', () => { + test('log in with valid credentials', done => { + expect.assertions(1) + try { + const account = await new Accounts() + // test goes here + } catch(err) { + + } finally { + + } + }) +}) + +describe('checkCredentials()', () => { + test('check valid credentials', done => { + expect.assertions(1) + try { + const account = await new Accounts() + // test goes here + } catch(err) { + + } finally { + + } + }) +})