Skip to content

Commit 981f308

Browse files
authored
Merge pull request #2 from node-modli/fix/connections
Fix/connections
2 parents 7fee810 + 55400b1 commit 981f308

File tree

7 files changed

+1449
-655
lines changed

7 files changed

+1449
-655
lines changed

binci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ tasks:
2626
test: |
2727
node ./scripts/postgres_conn.js
2828
yarn run test
29+
test:watch: |
30+
node ./scripts/postgres_conn.js
31+
yarn run test:watch
2932
cover: |
3033
node ./scripts/postgres_conn.js
3134
yarn run cover

package.json

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,45 @@
1818
"author": "TechnologyAdvice <devteam@technologyadvice.com>",
1919
"scripts": {
2020
"test": "yarn run lint && yarn run cover",
21+
"test:watch": "nodemon --exec \"./node_modules/.bin/mocha ./test/src || exit 1\"",
2122
"clean": "rm -rf ./node_modules rm -rf ./build",
2223
"mocha": "mocha ./test/src --recursive",
2324
"cover": "istanbul cover ./node_modules/.bin/_mocha ./test/src",
2425
"lint": "standard --fix --verbose"
2526
},
2627
"license": "MIT",
2728
"devDependencies": {
28-
"assert": "^1.3.0",
2929
"chai": "^4.1.2",
30-
"express": "^4.13.3",
30+
"chai-as-promised": "^7.1.1",
31+
"chai-subset": "^1.6.0",
32+
"dirty-chai": "^2.0.1",
33+
"fixd": "^1.0.1",
3134
"istanbul": "^0.4.5",
3235
"mocha": "^3.5.3",
36+
"nodemon": "^1.12.1",
37+
"require-dir": "^0.3.2",
3338
"should": "^13.0.1",
39+
"sinon": "^3.3.0",
40+
"sinon-chai": "^2.13.0",
3441
"standard": "^10.0.3"
3542
},
3643
"dependencies": {
3744
"bluebird": "^3.5.0",
3845
"pg": "^7.3.0"
46+
},
47+
"standard": {
48+
"ignore": [
49+
"coverage"
50+
],
51+
"env": [
52+
"node",
53+
"mocha"
54+
],
55+
"globals": [
56+
"sinon",
57+
"fixd",
58+
"expect",
59+
"sandbox"
60+
]
3961
}
4062
}

src/index.js

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const pg = require('pg')
1+
const { Pool } = require('pg')
22

33
/**
44
* @class postgres
@@ -13,7 +13,7 @@ module.exports = class {
1313
* @param {String} config.database The connection database
1414
*/
1515
constructor (config) {
16-
this.pg = new pg.Client(config)
16+
this.pg = new Pool(config)
1717
}
1818

1919
/**
@@ -22,18 +22,19 @@ module.exports = class {
2222
* @returns {Object} promise
2323
*/
2424
query (query) {
25-
let res
26-
// TODO: overlapping connections
25+
let client
2726
return this.pg.connect()
28-
.then(() => this.pg.query(query))
29-
.then(data => {
30-
res = data
31-
return this.pg.end()
27+
.then((cli) => {
28+
client = cli
29+
return client.query(query)
30+
})
31+
.then(res => {
32+
client.release()
33+
return res
3234
})
33-
.then(() => res)
3435
.catch((err) => {
35-
return this.pg.end()
36-
.then(() => { throw err })
36+
client.release()
37+
throw err
3738
})
3839
}
3940

@@ -67,14 +68,22 @@ module.exports = class {
6768
create (body, version = false) {
6869
return this.validate(body)
6970
.then(data => {
71+
const res = Object.assign({}, data)
7072
const vals = []
7173
const cols = Object.keys(data).reduce((acc, key) => {
74+
if (typeof data[key] === 'object' && data[key] !== null) {
75+
data[key] = JSON.stringify(data[key])
76+
}
7277
vals.push(`'${data[key]}'`)
7378
acc.push(key)
7479
return acc
7580
}, [])
7681
const query = `INSERT INTO ${this.tableName} (${cols.join(',')}) VALUES (${vals.join(',')})`
7782
return this.query(query)
83+
.then(data => {
84+
if (data.rowCount) return res
85+
throw new Error('Unable to create record')
86+
})
7887
})
7988
}
8089

@@ -103,17 +112,26 @@ module.exports = class {
103112
update (query, body, version = false) {
104113
return this.validate(body)
105114
.then(data => {
115+
const res = Object.assign({}, data)
106116
let i = 1
107117
let changes = ''
108-
let len = Object.keys(body).length
109-
for (let prop in body) {
110-
if ({}.hasOwnProperty.call(body, prop)) {
118+
let len = Object.keys(data).length
119+
for (let key in data) {
120+
/* istanbul ignore else: should not happen */
121+
if ({}.hasOwnProperty.call(data, key)) {
122+
if (typeof data[key] === 'object' && data[key] !== null) {
123+
data[key] = JSON.stringify(data[key])
124+
}
111125
let comma = (i !== len) ? ', ' : ''
112-
changes += `${prop}='${body[prop]}'${comma}`
126+
changes += `${key}='${data[key]}'${comma}`
113127
i++
114128
}
115129
}
116130
return this.query(`UPDATE ${this.tableName} SET ${changes} WHERE ${query}`)
131+
.then(data => {
132+
if (data.rowCount) return res
133+
throw new Error('Unable to update record(s)')
134+
})
117135
})
118136
}
119137

test/fixtures/postgres.fixture.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
fixd.postgres = {
2+
config: {
3+
host: process.env.MODLI_POSTGRES_HOST,
4+
user: process.env.POSTGRES_USER,
5+
password: process.env.POSTGRES_PASSWORD,
6+
database: process.env.POSTGRES_DB
7+
},
8+
testData: {
9+
fname: 'John',
10+
lname: 'Smith',
11+
email: 'jsmith@gmail.com',
12+
address: {
13+
street: '123 Fake St',
14+
city: 'Nashville'
15+
}
16+
},
17+
createTable: {
18+
id: [ 'serial', 'NOT NULL', 'PRIMARY KEY' ],
19+
fname: [ 'varchar(255)' ],
20+
lname: [ 'varchar(255)' ],
21+
email: [ 'varchar(255)' ],
22+
address: [ 'jsonb' ]
23+
}
24+
}

test/setup.js

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,37 @@
11
const chai = require('chai')
2-
global.assert = require('assert')
3-
global.should = require('should')
2+
const dirtyChai = require('dirty-chai')
3+
const chaiAsPromised = require('chai-as-promised')
4+
const chaiSubset = require('chai-subset')
5+
const mod = require('module')
6+
const path = require('path')
7+
const sinon = require('sinon')
8+
const sinonChai = require('sinon-chai')
9+
const fixd = require('fixd')
10+
11+
// Chai
12+
// ----------------------------------------
413
global.expect = chai.expect
14+
chai.use(chaiAsPromised)
15+
chai.use(dirtyChai)
16+
chai.use(chaiSubset)
17+
chai.use(sinonChai)
18+
19+
// Sinon
20+
// ----------------------------------------
21+
global.sinon = sinon
22+
global.sandbox = sinon.sandbox.create()
23+
24+
if (global.afterEach) {
25+
afterEach(() => global.sandbox.restore())
26+
}
27+
28+
// Fixd
29+
// ----------------------------------------
30+
global.fixd = fixd
31+
require('require-dir')(path.resolve(__dirname, 'fixtures'))
32+
33+
// Node
34+
// ----------------------------------------
35+
// allow require() from project root
36+
process.env.NODE_PATH = path.join(process.cwd(), process.env.NODE_PATH || '')
37+
mod._initPaths()

test/src/index.spec.js

Lines changed: 52 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,110 +1,102 @@
11
/* eslint no-unused-expressions: 0 */
2-
/* global expect, describe, it, beforeEach, afterEach */
32
require('../setup')
4-
const PostgresAdapter = require('../../src/index')
5-
6-
const config = {
7-
host: process.env.MODLI_POSTGRES_HOST,
8-
user: process.env.POSTGRES_USER,
9-
password: process.env.POSTGRES_PASSWORD,
10-
database: process.env.POSTGRES_DB
11-
}
12-
13-
// Test record
14-
const testData = {
15-
fname: 'John',
16-
lname: 'Smith',
17-
email: 'jsmith@gmail.com'
18-
}
3+
const PostgresAdapter = require('src')
194

205
describe('postgres', () => {
216
let inst
22-
beforeEach(() => {
23-
inst = new PostgresAdapter(config)
7+
before(() => {
8+
inst = new PostgresAdapter(fixd.postgres.config)
249
inst.tableName = 'foo'
2510
// Mock validation method, this is automatically done by the model
2611
inst.validate = (body) => Promise.resolve(body)
2712
// Mock sanitize method, this is automatically done by the model
2813
inst.sanitize = (body) => body
2914
})
30-
afterEach(() => {
31-
inst.query(`DROP TABLE ${inst.tableName}`)
32-
})
15+
after(() => inst.pg.end())
3316
describe('query', () => {
34-
it('fails when invalid query is run', () => {
35-
return inst.query('`')
36-
.catch((err) => {
37-
expect(err).to.be.an.instanceof(Error)
38-
})
39-
})
4017
it('runs a query against the database when connection is good', () => {
4118
return inst.query('SELECT 1 + 1 AS number')
42-
.then((result) => {
43-
expect(result.rows[0].number).to.equal(2)
19+
.then((res) => {
20+
expect(res.rows[0].number).to.equal(2)
4421
})
4522
})
23+
it('fails when invalid query is run', () => {
24+
return expect(inst.query('`')).to.be.rejectedWith(Error)
25+
})
4626
})
4727
describe('createTable', () => {
4828
it('creates a new table based on object passed (if not exists)', () => {
49-
return inst.createTable({
50-
'id': [ 'serial', 'NOT NULL', 'PRIMARY KEY' ],
51-
'fname': [ 'varchar(255)' ],
52-
'lname': [ 'varchar(255)' ],
53-
'email': [ 'varchar(255)' ]
54-
})
55-
.then((result) => {
56-
expect(result.command).to.equal('CREATE')
57-
})
29+
return inst.createTable(fixd.postgres.createTable)
30+
.then((res) => {
31+
expect(res.command).to.equal('CREATE')
32+
})
5833
})
5934
})
6035
describe('create', () => {
36+
let data
37+
beforeEach(() => {
38+
data = Object.assign({}, fixd.postgres.testData)
39+
})
6140
it('creates a new record based on object passed', () => {
62-
return inst.create(testData)
63-
.then((result) => {
64-
expect(result.command).to.equal('INSERT')
65-
expect(result.rowCount).to.equal(1)
41+
return inst.create(data)
42+
.then((res) => {
43+
expect(res).to.containSubset(fixd.postgres.testData)
6644
})
6745
})
46+
it('rejects if record is not created', () => {
47+
sandbox.stub(inst, 'query').resolves({ rowCount: 0 })
48+
return expect(inst.create(data))
49+
.to.be.rejectedWith('Unable to create record')
50+
})
6851
})
6952
describe('read', () => {
7053
it('reads all when no query specified', () => {
7154
return inst.read()
72-
.then((result) => {
73-
expect(result.length).to.equal(1)
74-
expect(result[0].email).to.equal(testData.email)
55+
.then((res) => {
56+
expect(res.length).to.equal(1)
57+
expect(res[0].email).to.equal(fixd.postgres.testData.email)
7558
})
7659
})
7760
it('reads specific records when query supplied', () => {
7861
return inst.read('fname=\'John\'', 1)
79-
.then((result) => {
80-
expect(result.length).to.equal(1)
81-
expect(result[0].email).to.equal(testData.email)
62+
.then((res) => {
63+
expect(res.length).to.equal(1)
64+
expect(res[0].email).to.equal(fixd.postgres.testData.email)
8265
})
8366
})
8467
it('fails when a bad query is provided', () => {
85-
return inst.read('`fart=`knocker')
86-
.catch((err) => {
87-
expect(err).to.be.an.instanceof(Error)
88-
})
68+
return expect(inst.read('`')).to.be.rejectedWith(Error)
8969
})
9070
})
9171
describe('update', () => {
9272
it('updates record(s) based on query and body', () => {
93-
return inst.update('fname=\'John\'', {
73+
const query = 'fname=\'John\''
74+
const body = {
9475
fname: 'Bob',
9576
email: 'bsmith@gmail.com'
96-
}, 1)
97-
.then((result) => {
98-
expect(result.command).to.equal('UPDATE')
99-
expect(result.rowCount).to.equal(1)
100-
})
77+
}
78+
return inst.update(query, body)
79+
.then((res) => {
80+
expect(res.fname).to.equal('Bob')
81+
})
82+
})
83+
it('rejects if records are not updated', () => {
84+
sandbox.stub(inst, 'query').resolves({ rowCount: 0 })
85+
const query = 'fname=\'John\''
86+
const body = {
87+
fname: 'Bob',
88+
email: 'bsmith@gmail.com',
89+
address: { city: 'Fakeville' }
90+
}
91+
return expect(inst.update(query, body))
92+
.to.be.rejectedWith('Unable to update record(s)')
10193
})
10294
})
10395
describe('delete', () => {
10496
it('deletes record(s) based on query', () => {
10597
return inst.delete('fname=\'Bob\'')
106-
.then((result) => {
107-
expect(result.rowCount).to.equal(1)
98+
.then((res) => {
99+
expect(res.rowCount).to.equal(1)
108100
})
109101
})
110102
})

0 commit comments

Comments
 (0)