Skip to content

Commit e7f3ace

Browse files
start adding tests
1 parent 44fc3a7 commit e7f3ace

File tree

5 files changed

+291
-16
lines changed

5 files changed

+291
-16
lines changed

db-session.js

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ const AtomicSessionConnectionPair = require('./lib/atomic-session-connpair.js')
88
const TxSessionConnectionPair = require('./lib/tx-session-connpair.js')
99
const SessionConnectionPair = require('./lib/session-connpair.js')
1010

11+
class NoSessionAvailable extends Error {
12+
constructor () {
13+
super('No session available')
14+
Error.captureStackTrace(this, NoSessionAvailable)
15+
}
16+
}
17+
1118
const api = module.exports = {
1219
install (domain, getConnection, opts) {
1320
opts = opts || {}
@@ -19,15 +26,19 @@ const api = module.exports = {
1926

2027
atomic (operation) {
2128
return function atomic$operation () {
22-
const args = [].slice.call(arguments)
23-
return api.session.atomic(operation, args)
29+
return Promise.try(() => {
30+
const args = [].slice.call(arguments)
31+
return api.session.atomic(operation, args)
32+
})
2433
}
2534
},
2635

2736
transaction (operation) {
2837
return function transaction$operation () {
29-
const args = [].slice.call(arguments)
30-
return api.session.transaction(operation, args)
38+
return Promise.try(() => {
39+
const args = [].slice.call(arguments)
40+
return api.session.transaction(operation, args)
41+
})
3142
}
3243
},
3344

@@ -37,14 +48,16 @@ const api = module.exports = {
3748

3849
get session () {
3950
var current = DOMAIN_TO_SESSION.get(process.domain)
40-
if (!current) {
41-
throw new Error('no session active')
51+
if (!current || !process.domain) {
52+
throw new NoSessionAvailable()
4253
}
4354
while (current.inactive && current.parent) {
4455
current = current.parent
4556
}
4657
return current
47-
}
58+
},
59+
60+
NoSessionAvailable
4861
}
4962

5063
// how does this nest:
@@ -70,7 +83,7 @@ class Session {
7083
return pending.promise
7184
}
7285

73-
const connPair = this._getConnection()
86+
const connPair = Promise.resolve(this._getConnection())
7487
++this._activeConnections
7588

7689
return connPair.then(
@@ -210,12 +223,12 @@ function getSavepointName (operation) {
210223
getSavepointName.ID = 0
211224

212225
function markInactive (session) {
213-
// XXX(chrisdickinson): is this a good idea? this
214-
// means that DB requests after the operation completes
215-
// will still work, but will operate outside of the
216-
// transaction.
217226
return domain => {
227+
domain.exit()
218228
DOMAIN_TO_SESSION.get(domain).inactive = true
229+
230+
// if, somehow, we get a reference to this domain again, point
231+
// it at the parent session.
219232
DOMAIN_TO_SESSION.set(domain, session)
220233
}
221234
}

package.json

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
"description": "domain-attached database sessions",
55
"main": "db-session.js",
66
"scripts": {
7-
"test": "tape test/*-test.js && standard",
8-
"test-cov": "nyc tape test/*-test.js && standard"
7+
"test": "tap test/*-test.js && standard",
8+
"test-cov": "nyc tap test/*-test.js && standard"
99
},
1010
"repository": {
1111
"type": "git",
@@ -24,11 +24,10 @@
2424
},
2525
"homepage": "https://github.com/npm/pg-db-session#readme",
2626
"devDependencies": {
27-
"faucet": "0.0.1",
2827
"nyc": "^5.3.0",
2928
"pg": "^4.4.3",
3029
"standard": "^5.4.1",
31-
"tape": "^4.4.0"
30+
"tap": "^5.0.0"
3231
},
3332
"dependencies": {
3433
"bluebird": "^3.1.1"

test/basic-api-errors-test.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
'use strict'
2+
3+
const Promise = require('bluebird')
4+
const domain = require('domain')
5+
const test = require('tap').test
6+
7+
const db = require('../db-session.js')
8+
9+
test('test transaction outside of session', assert => {
10+
const testTransaction = db.transaction(function testTransaction () {
11+
return
12+
})
13+
14+
testTransaction()
15+
.then(() => { throw new Error('expected error') })
16+
.catch(db.NoSessionAvailable, () => assert.end())
17+
.catch(err => assert.end(err))
18+
})
19+
20+
test('test atomic outside of session', assert => {
21+
const testAtomic = db.atomic(function testAtomic () {
22+
return
23+
})
24+
25+
testAtomic()
26+
.then(() => { throw new Error('expected error') })
27+
.catch(db.NoSessionAvailable, () => assert.end())
28+
.catch(err => assert.end(err))
29+
})
30+
31+
if (false)
32+
test('test atomic after release', assert => {
33+
})
34+
35+
if (false)
36+
test('test transaction after release', assert => {
37+
})

test/basic-interleaved-test.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
'use strict'
2+
3+
const domain = require('domain')
4+
const test = require('tap').test
5+
const fs = require('fs')
6+
7+
const db = require('../db-session.js')
8+
9+
// this is pretty much just testing domains
10+
test('test of interleaved requests', assert => {
11+
const domain1 = domain.create()
12+
const domain2 = domain.create()
13+
14+
db.install(domain1, getFakeConnection)
15+
db.install(domain2, getFakeConnection)
16+
17+
var pending = 3
18+
19+
domain1.run(() => {
20+
const firstSession = db.session
21+
fs.readFile(__filename, () => {
22+
assert.equal(firstSession, db.session)
23+
domain2.run(() => {
24+
assert.ok(firstSession !== db.session)
25+
const secondSession = db.session
26+
setTimeout(() => {
27+
assert.equal(secondSession, db.session)
28+
!--pending && end()
29+
}, 100)
30+
fs.readFile(__filename, () => {
31+
assert.equal(secondSession, db.session)
32+
!--pending && end()
33+
})
34+
})
35+
setTimeout(() => {
36+
assert.equal(firstSession, db.session)
37+
!--pending && end()
38+
}, 100)
39+
})
40+
})
41+
42+
function end () {
43+
process.domain.exit()
44+
assert.end()
45+
}
46+
47+
function getFakeConnection () {
48+
return {
49+
connection: {query () {
50+
}},
51+
release () {
52+
}
53+
}
54+
}
55+
})
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
'use strict'
2+
3+
const Promise = require('bluebird')
4+
const domain = require('domain')
5+
const test = require('tap').test
6+
7+
const db = require('../db-session.js')
8+
9+
const LOGS = []
10+
11+
test('test root session concurrency=0', assert => {
12+
const domain1 = domain.create()
13+
db.install(domain1, innerGetConnection, {maxConcurrency: 0})
14+
domain1.run(() => {
15+
return runOperations()
16+
}).then(() => {
17+
domain1.exit()
18+
}).then(() => {
19+
assert.equal(LOGS.join('\n'), `
20+
load 0
21+
load 1
22+
load 2
23+
load 3
24+
load 4
25+
load 5
26+
load 6
27+
load 7
28+
release 0
29+
release
30+
release 1
31+
release
32+
release 2
33+
release
34+
release 3
35+
release
36+
release 4
37+
release
38+
release 5
39+
release
40+
release 6
41+
release
42+
release 7
43+
release
44+
`.trim())
45+
assert.equal(process.domain, undefined)
46+
}).then(() => assert.end())
47+
.catch(assert.end)
48+
})
49+
50+
test('test root session concurrency=1', assert => {
51+
const domain1 = domain.create()
52+
db.install(domain1, innerGetConnection, {maxConcurrency: 1})
53+
domain1.run(() => {
54+
return runOperations()
55+
}).then(() => {
56+
domain1.exit()
57+
}).then(() => {
58+
assert.equal(LOGS.join('\n'), `
59+
load 0
60+
release 0
61+
load 1
62+
release 1
63+
load 2
64+
release 2
65+
load 3
66+
release 3
67+
load 4
68+
release 4
69+
load 5
70+
release 5
71+
load 6
72+
release 6
73+
load 7
74+
release 7
75+
release
76+
`.trim())
77+
assert.equal(process.domain, undefined)
78+
}).then(() => assert.end())
79+
.catch(assert.end)
80+
})
81+
82+
test('test root session concurrency=2', assert => {
83+
const domain1 = domain.create()
84+
db.install(domain1, innerGetConnection, {maxConcurrency: 2})
85+
domain1.run(() => {
86+
return runOperations()
87+
}).then(() => {
88+
domain1.exit()
89+
}).then(() => {
90+
assert.equal(LOGS.join('\n'), `
91+
load 0
92+
load 1
93+
release 0
94+
release 1
95+
load 2
96+
load 3
97+
release 2
98+
release 3
99+
load 4
100+
load 5
101+
release 4
102+
release 5
103+
load 6
104+
load 7
105+
release 6
106+
release
107+
release 7
108+
release
109+
`.trim())
110+
assert.equal(process.domain, undefined)
111+
}).then(() => assert.end())
112+
.catch(assert.end)
113+
})
114+
115+
test('test root session concurrency=4', assert => {
116+
const domain1 = domain.create()
117+
db.install(domain1, innerGetConnection, {maxConcurrency: 4})
118+
domain1.run(() => {
119+
return runOperations()
120+
}).then(() => {
121+
domain1.exit()
122+
}).then(() => {
123+
assert.equal(LOGS.join('\n'), `
124+
load 0
125+
load 1
126+
load 2
127+
load 3
128+
release 0
129+
release 1
130+
release 2
131+
release 3
132+
load 4
133+
load 5
134+
load 6
135+
load 7
136+
release 4
137+
release
138+
release 5
139+
release
140+
release 6
141+
release
142+
release 7
143+
release
144+
`.trim())
145+
assert.equal(process.domain, undefined)
146+
}).then(() => assert.end())
147+
.catch(assert.end)
148+
})
149+
150+
function innerGetConnection () {
151+
return {
152+
connection: {query () {
153+
}},
154+
release () {
155+
LOGS.push(`release`)
156+
}
157+
}
158+
}
159+
160+
function runOperations () {
161+
LOGS.length = 0
162+
return Promise.all(Array.from(Array(8)).map((_, idx) => {
163+
return db.getConnection().then(connPair => {
164+
LOGS.push(`load ${idx}`)
165+
return Promise.delay(5).then(() => {
166+
LOGS.push(`release ${idx}`)
167+
connPair.release()
168+
})
169+
})
170+
}))
171+
}

0 commit comments

Comments
 (0)