Skip to content
Permalink
Browse files
initial groundwork
  • Loading branch information
soperd committed Oct 6, 2019
0 parents commit a4b0cd0391fab056e2d1fbe203f53de7e7391b9e
Show file tree
Hide file tree
Showing 19 changed files with 7,696 additions and 0 deletions.
@@ -0,0 +1 @@
*.test.js
@@ -0,0 +1,6 @@
{
"extends": "standard",
"rules": {
"space-before-function-paren": "off"
}
}
@@ -0,0 +1,4 @@
node_modules

# ignore sqlite databases
*.db
@@ -0,0 +1,3 @@
{
"editor.tabSize": 2
}
26 app.js
@@ -0,0 +1,26 @@
/**
* Creates a common Koa app
*/

'use strict'

const path = require('path')

const Koa = require('koa')
const Views = require('koa-views')

const app = new Koa()
const handlebars = new Views(
path.join(__dirname, '/views'),
{
map: { hbs: 'handlebars' },
extension: 'hbs'
}
)

const login = require('./controllers/login')

app.use(handlebars)
app.use(login.routes())

module.exports = app
@@ -0,0 +1,20 @@
#!/usr/bin/env node

'use strict'

const fs = require('fs')
const path = require('path')

const sqlite = require('sqlite')

sqlite.open('app.db').then(db => {
const sqlPath = path.join(__dirname, 'build_db.sql')
fs.readFile(sqlPath, 'utf8', async (err, sql) => {
if (err) {
console.error(err)
process.exit(1)
}

await db.exec(sql)
})
})
@@ -0,0 +1,6 @@
CREATE TABLE `users`
(
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`username` TEXT UNIQUE,
`hash` TEXT
);
@@ -0,0 +1,7 @@
'use strict'

const Router = require('koa-router')

const apiRouter = new Router({ prefix: '/api' })

module.export = apiRouter
@@ -0,0 +1,16 @@
'use strict'

const Router = require('koa-router')

const login = new Router({ prefix: '/login' })

login.get('/', async ctx => {
await ctx.render('login.hbs')
})

login.post('/', async ctx => {
console.log(ctx.query)
console.log(ctx.querystring)
})

module.exports = login
93 db.js
@@ -0,0 +1,93 @@
/*
* Database controller
*/

const sqlite = require('sqlite')

const User = require('./models/user')

class DbContext {
async getUsers() { }
async getUser(id) { }
async deleteUser(id) { }
async createUser(user) { }
async updateUser(user) { }

async execute(query) { }
}

class SqliteDbContext extends DbContext {
constructor(filename) {
super()

this.sqlitePromise = sqlite.open(filename, { Promise })
}

async getUser(id) {
const sqlite = await this.sqlitePromise

let query

if (typeof id === 'number') {
query = 'SELECT * FROM `users` WHERE `id` = ?;'
} else if (typeof id === 'string') {
query = 'SELECT * FROM `users` WHERE `username` = ?;'
} else {
throw new TypeError('id must be number or string')
}

const user = await sqlite.get(query, id)
return Object.assign(new User(), user)
}

async getUsers() {
const sqlite = await this.sqlitePromise

const users = await sqlite.all('SELECT * FROM `users`;')
return users.map(x => Object.assign(new User(), x))
}

async deleteUser(id) {
const sqlite = await this.sqlitePromise

let query

if (typeof id === 'number') {
query = 'DELETE FROM `users` WHERE `id` = ?;'
} else if (typeof id === 'string') {
query = 'DELETE FROM `users` WHERE `username` = ?;'
} else {
throw new TypeError('id must be number or string')
}

await sqlite.run(query, id)
}

async createUser(user) {
const sqlite = await this.sqlitePromise

const { lastID } = await sqlite.run(
'INSERT INTO `users` (`username`, `hash`) VALUES (?, ?);',
user.username,
user.hash
)
return this.getUser(lastID)
}

async updateUser(user) {
const sqlite = await this.sqlitePromise

await sqlite.run(
'UPDATE `users` SET `username` = ?, `hash` = ? WHERE `id` = ?;',
user.username,
user.hash,
user.id
)
return this.getUser(user.id)
}
}

module.exports = {
DbContext,
SqliteDbContext
}
@@ -0,0 +1,68 @@
const db = require('./db')

const User = require('./models/user')

// create an in memory db
const sqliteContext = new db.SqliteDbContext(':memory:')

describe('user database', () => {
beforeEach(async () => {
await sqliteContext.sqlitePromise.then(async db => {
// clear table
await db.exec('DROP TABLE IF EXISTS `users`;')

// create user table
await db.exec('CREATE TABLE `users` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `username` TEXT UNIQUE, `hash` TEXT);')

// insert test users
await db.exec('INSERT INTO `users` VALUES (10, \'hakasec\', \'test\'), (11, \'hello\', \'world\');')
})
})

test('should get a user by id', async () => {
expect(await sqliteContext.getUser(10))
.toEqual({ id: 10, username: 'hakasec', hash: 'test' })
});

test('should get a user by username', async () => {
expect(await sqliteContext.getUser('hakasec'))
.toEqual({ id: 10, username: 'hakasec', hash: 'test' })
})

test('should get all users', async () => {
expect(await sqliteContext.getUsers())
.toContainEqual({ id: 11, username: 'hello', hash: 'world' })
})

test('should create a user', async () => {
const user = await sqliteContext.createUser(new User('hello1', 'world'))
expect(user.id).not.toEqual(-1)
})

test('should delete a user by id', async () => {
await sqliteContext.deleteUser(10)

const userCheck = await sqliteContext.sqlitePromise.then(db => {
return db.get('SELECT * FROM `users` WHERE `id` = 10;')
})

expect(userCheck).toBe(undefined)
})

test('should delete a user by username', async () => {
await sqliteContext.deleteUser('hakasec')

const userCheck = await sqliteContext.sqlitePromise.then(db => {
return db.get('SELECT * FROM `users` WHERE `id` = \'hakasec\';')
})

expect(userCheck).toBe(undefined)
})

test('should update a user', async () => {
let user = await sqliteContext.getUser('hakasec')
user.hash = 'test1'

expect(await sqliteContext.updateUser(user)).toEqual(user)
})
})
@@ -0,0 +1,14 @@
#!/usr/bin/env node

const path = require('path')

const db = require('./db')

const dbcontext = new db.SqliteDbContext(path.join(__dirname, 'app.db'))

const app = require('./app')

// inject the db context
app.context.db = dbcontext

app.listen(8080)

0 comments on commit a4b0cd0

Please sign in to comment.