Skip to content

Commit d94a444

Browse files
we are now at 100/100/100 stmt/branch/line coverage
1 parent 185f249 commit d94a444

File tree

3 files changed

+243
-6
lines changed

3 files changed

+243
-6
lines changed

db-session.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -195,18 +195,20 @@ function Session$RunWrapped (createSession,
195195
})
196196

197197
return runBefore.then(() => {
198-
const opArgs = args.slice()
199-
opArgs.unshift(operation)
200198
const getResult = Promise.resolve(
201-
subdomain.run.apply(subdomain, opArgs)
199+
subdomain.run(() => Promise.try(() => {
200+
return operation.apply(null, args)
201+
}))
202202
)
203203

204+
const reflectedResult = getResult.reflect()
205+
204206
const waitOperation = Promise.join(
205-
getResult,
206-
getResult.then(() => session.operation)
207+
reflectedResult,
208+
reflectedResult.then(() => session.operation)
207209
)
208210
.finally(markInactive(subdomain))
209-
.return(getResult.reflect())
211+
.return(reflectedResult)
210212

211213
const runCommitStep = waitOperation.then(result => {
212214
return new Promise((resolve, reject) => {

test/basic-atomic-error-test.js

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
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+
// what happens if there's an error in the previous query?
10+
// - a failed query should not automatically end the atomic
11+
// - only returning a promise will end the atomic
12+
test('test error in previous query', assert => {
13+
const domain1 = domain.create()
14+
15+
db.install(domain1, getConnection, {maxConcurrency: 0})
16+
17+
domain1.run(() => {
18+
return db.atomic(() => {
19+
const first = db.getConnection().then(conn => {
20+
return Promise.promisify(conn.connection.query)('ONE')
21+
.then(() => conn.release())
22+
.catch(err => conn.release(err))
23+
})
24+
25+
const second = first.then(() => {
26+
return db.getConnection()
27+
}).then(conn => {
28+
return Promise.promisify(conn.connection.query)('TWO')
29+
.then(() => conn.release())
30+
.catch(err => conn.release(err))
31+
})
32+
33+
return second.then(() => 'expect this value')
34+
})()
35+
})
36+
.then(value => assert.equal(value, 'expect this value'))
37+
.catch(err => assert.fail(err.stack))
38+
.finally(() => domain1.exit())
39+
.finally(assert.end)
40+
41+
function getConnection () {
42+
return {
43+
connection: {query (sql, ready) {
44+
if (sql === 'ONE') {
45+
return ready(new Error('failed'))
46+
}
47+
return ready()
48+
}},
49+
release (err) {
50+
}
51+
}
52+
}
53+
})
54+
55+
// what happens if BEGIN fails
56+
test('test error in BEGIN', assert => {
57+
const domain1 = domain.create()
58+
class BeginError extends Error {}
59+
60+
db.install(domain1, getConnection, {maxConcurrency: 0})
61+
62+
domain1.run(() => {
63+
return db.atomic(() => {
64+
assert.fail('should not reach here.')
65+
})()
66+
})
67+
.catch(BeginError, err => assert.ok(1, 'caught expected err'))
68+
.catch(err => assert.fail(err))
69+
.finally(() => domain1.exit())
70+
.finally(assert.end)
71+
72+
function getConnection () {
73+
var trippedBegin = false
74+
return {
75+
connection: {query (sql, ready) {
76+
if (trippedBegin) {
77+
assert.fail('should not run subsequent queries')
78+
}
79+
if (sql === 'BEGIN') {
80+
trippedBegin = true
81+
return ready(new BeginError('failed BEGIN'))
82+
}
83+
return ready()
84+
}},
85+
release (err) {
86+
}
87+
}
88+
}
89+
})
90+
91+
// what happens if COMMIT / ROLLBACK fails
92+
test('test error in COMMIT', assert => {
93+
const domain1 = domain.create()
94+
class CommitError extends Error {}
95+
96+
db.install(domain1, getConnection, {maxConcurrency: 0})
97+
98+
domain1.run(() => {
99+
return db.atomic(() => {
100+
return db.getConnection().then(pair => pair.release())
101+
})()
102+
})
103+
.catch(CommitError, () => assert.ok(1, 'caught expected error'))
104+
.catch(err => assert.fail(err))
105+
.finally(() => domain1.exit())
106+
.finally(assert.end)
107+
108+
function getConnection () {
109+
return {
110+
connection: {query (sql, ready) {
111+
if (sql === 'COMMIT') {
112+
return ready(new CommitError('failed COMMIT'))
113+
}
114+
return ready()
115+
}},
116+
release (err) {
117+
}
118+
}
119+
}
120+
})
121+
122+
test('test error in ROLLBACK: does not reuse connection', assert => {
123+
const domain1 = domain.create()
124+
class RollbackError extends Error {}
125+
126+
db.install(domain1, getConnection, {maxConcurrency: 1})
127+
128+
var connectionPair = null
129+
domain1.run(() => {
130+
const first = db.atomic(() => {
131+
return db.getConnection().then(pair => {
132+
connectionPair = pair.pair
133+
pair.release()
134+
throw new Error('any kind of error, really')
135+
})
136+
})().reflect()
137+
138+
const second = db.getConnection().then(pair => {
139+
// with concurrency=1, we will try to re-use
140+
// the connection if we can. since we had an error,
141+
// it's best not to use the connection!
142+
assert.notEqual(connectionPair, pair)
143+
pair.release()
144+
})
145+
146+
return Promise.join(first, second)
147+
})
148+
.catch(err => assert.fail(err))
149+
.finally(() => domain1.exit())
150+
.finally(assert.end)
151+
152+
function getConnection () {
153+
return {
154+
connection: {query (sql, ready) {
155+
if (sql === 'ROLLBACK') {
156+
return ready(new RollbackError('failed ROLLBACK'))
157+
}
158+
return ready()
159+
}},
160+
release (err) {
161+
}
162+
}
163+
}
164+
})

test/basic-rollback-test.js

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
'use strict'
2+
const Promise = require('bluebird')
3+
const domain = require('domain')
4+
const test = require('tap').test
5+
6+
const db = require('../db-session.js')
7+
8+
const LOGS = []
9+
10+
test('rolling back transaction calls ROLLBACK', assert => {
11+
const domain1 = domain.create()
12+
13+
LOGS.length = 0
14+
db.install(domain1, getConnection, {maxConcurrency: 0})
15+
16+
domain1.run(() => {
17+
return db.transaction(() => {
18+
throw new Error('no thanks')
19+
})().reflect()
20+
})
21+
.then(() => assert.equal(LOGS.join(' '), 'BEGIN ROLLBACK'))
22+
.catch(err => assert.fail(err))
23+
.finally(() => domain1.exit())
24+
.finally(assert.end)
25+
26+
function getConnection () {
27+
return {
28+
connection: {query (sql, ready) {
29+
LOGS.push(sql)
30+
return ready()
31+
}},
32+
release (err) {
33+
}
34+
}
35+
}
36+
})
37+
38+
test('rolling back atomic calls ROLLBACK', assert => {
39+
const domain1 = domain.create()
40+
41+
LOGS.length = 0
42+
db.install(domain1, getConnection, {maxConcurrency: 0})
43+
44+
domain1.run(() => {
45+
return db.atomic(() => {
46+
throw new Error('no thanks')
47+
})().reflect()
48+
})
49+
.then(() => {
50+
assert.equal(LOGS.join('\n').replace(/_[\d_]+$/gm, '_TS'), `
51+
BEGIN
52+
SAVEPOINT save_0_anon_TS
53+
ROLLBACK TO SAVEPOINT save_0_anon_TS
54+
ROLLBACK
55+
`.trim())
56+
})
57+
.catch(err => assert.fail(err))
58+
.finally(() => domain1.exit())
59+
.finally(assert.end)
60+
61+
function getConnection () {
62+
return {
63+
connection: {query (sql, ready) {
64+
LOGS.push(sql)
65+
return ready()
66+
}},
67+
release (err) {
68+
}
69+
}
70+
}
71+
})

0 commit comments

Comments
 (0)