Skip to content

Commit aa307de

Browse files
committed
test(native): cover prototypeless namedQueries
The unit coverage added with the fix exercises `Connection.parsedStatements` only. The native client keeps its own cache in `namedQueries` with the same inherited-name lookup in `native/query.js`, so it needs its own test. Runs under `make test-native`. Covers a first use of an inherited name being prepared rather than rejected, a text-less second use resolving from the cache the first populated, and a genuine duplicate name still erroring.
1 parent 5accfc5 commit aa307de

1 file changed

Lines changed: 77 additions & 0 deletions

File tree

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
'use strict'
2+
const helper = require('./../test-helper')
3+
const Client = require('./../../lib/native')
4+
const suite = new helper.Suite()
5+
const assert = require('assert')
6+
7+
// `namedQueries` is keyed by user-supplied statement name. Before it was made prototypeless a name
8+
// matching a property of Object.prototype was truthy on a client that had prepared nothing, so the
9+
// query was either rejected as a duplicate or handed to PQexecPrepared without ever being prepared.
10+
// This mirrors the pure-JS coverage in test/unit/client/parsed-statements-prototype-tests.js.
11+
const inheritedNames = ['constructor', 'toString', 'valueOf', 'hasOwnProperty', 'isPrototypeOf', '__proto__']
12+
13+
suite.test('namedQueries does not inherit from Object.prototype', function (done) {
14+
const client = new Client(helper.config)
15+
client.connect()
16+
17+
for (const name of inheritedNames) {
18+
assert.strictEqual(client.namedQueries[name], undefined, `'${name}' should not resolve on a fresh client`)
19+
}
20+
assert.strictEqual(Object.getPrototypeOf(client.namedQueries), null)
21+
22+
client.end(done)
23+
})
24+
25+
// Queries run one at a time: issuing them concurrently would queue them on the client and trip the
26+
// pg@9.0 deprecation notice for querying while another query is in flight.
27+
suite.test('a statement named after an inherited property is prepared and executed', function (done) {
28+
const client = new Client(helper.config)
29+
client.connect()
30+
31+
const runNext = function (i) {
32+
if (i === inheritedNames.length) {
33+
return client.end(done)
34+
}
35+
const name = inheritedNames[i]
36+
37+
// first use: nothing is cached under this name, so it must be prepared before it can run
38+
client.query(
39+
{ name: name, text: 'SELECT $1::int as num', values: [i] },
40+
assert.calls(function (err, result) {
41+
assert(!err, `preparing '${name}' failed: ${err && err.message}`)
42+
assert.equal(result.rows[0].num, i)
43+
assert.strictEqual(client.namedQueries[name], 'SELECT $1::int as num')
44+
45+
// second use supplies no text, so it can only work if the first call cached the statement
46+
client.query(
47+
{ name: name, values: [i + 1] },
48+
assert.calls(function (err, result) {
49+
assert(!err, `re-executing '${name}' failed: ${err && err.message}`)
50+
assert.equal(result.rows[0].num, i + 1)
51+
runNext(i + 1)
52+
})
53+
)
54+
})
55+
)
56+
}
57+
58+
runNext(0)
59+
})
60+
61+
suite.test('reusing a statement name for different text is still an error', function (done) {
62+
const client = new Client(helper.config)
63+
client.connect()
64+
65+
client.query(
66+
{ name: 'constructor', text: 'SELECT 1 as num' },
67+
assert.calls(function (err) {
68+
assert(!err, `preparing 'constructor' failed: ${err && err.message}`)
69+
70+
client.query({ name: 'constructor', text: 'SELECT 2 as num' }, function (err) {
71+
assert(err instanceof Error, 'expected a duplicate statement name to be rejected')
72+
assert.ok(/must be unique/.test(err.message), `unexpected message: ${err.message}`)
73+
client.end(done)
74+
})
75+
})
76+
)
77+
})

0 commit comments

Comments
 (0)