Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
"build": "tsc",
"build:test": "tsc -p tsconfig.json",
"test": "mocha \"test/**/*.test.js\"",
"record-corpus": "node test/contract/record.js",
"test:improved": "mocha test/connection-improved.test.js --reporter spec",
"test:webstorm": "mocha test/connection-webstorm.test.js --config test/.mocharc.webstorm.js",
"test:connection": "mocha test/connect*.js --reporter spec --timeout 30000",
Expand Down
14 changes: 8 additions & 6 deletions test/common/fake-native.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,12 +191,14 @@ function fakeSql (native, { version = 18 } = {}) {
conn.setDriverVersion(version)
native.open(connectionString, 0, err => setImmediate(() => cb(Array.isArray(err) ? err[0] : err, conn)))
}
return {
open,
promises: {
open: connectionString => new Promise((resolve, reject) => open(connectionString, (e, c) => e ? reject(e) : resolve(c)))
}
}
// everything else (PollingQuery, the types, ...) is the real module's
const real = require(root)
const fake = Object.create(real)
fake.open = open
fake.promises = Object.assign(Object.create(real.promises ?? {}), {
open: connectionString => new Promise((resolve, reject) => open(connectionString, (e, c) => e ? reject(e) : resolve(c)))
})
return fake
}

module.exports = { ReplayNative, fakeSql }
5 changes: 3 additions & 2 deletions test/contract/HANDOVER.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,9 @@ at execute, the error comes with `more=false` and the rest of the batch is lost:
`rowcount`, no following result set, no `done`. Windows gives `more=true`, then the rest. So
the #475 fix in 5.6.0 (`odbc_statement_legacy.cpp`, the look-ahead after a failed execute)
does not hold on Linux. The same 4 scenarios differ with the base commit's `lib/`, so it is
older than the queue work. Pinned as `errors/{raiserror-then-result,dup-key-d}.*.linux.txt`,
`# status: known-bug`, so that CI is green and the fix shows up as a diff.
older than the queue work. Pinned as `errors/{raiserror-then-result,dup-key-d}.*.linux.txt`
and `scripts/issue-478.linux.txt` (its case D), `# status: known-bug`, so that CI is green
and the fix shows up as a diff. Filed as #483.

To run the corpus on WSL: a `git worktree` under `/tmp/<x>/node_modules/msnodesqlv8` (the
tests `require('msnodesqlv8')`), `npx node-gyp rebuild`, copy `.env-cmdrc`, symlink
Expand Down
2 changes: 2 additions & 0 deletions test/contract/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ event order shows up as a diff in review, and the contract can be discussed line
| File | What it is |
|---|---|
| `scenarios.js` | the scenarios: query, setup, actions (pause / resume / cancel at row n, at start, after n ms, after free), follow-up queries |
| `scripts/` | scripted scenarios, for what one query can't say: the issue repros (`issue-478.js` ...), several cases in sequence. See `scripts/README.md` |
| `record.js` | `npm run record-corpus -- <tester.js>`: copies a script into `scripts/` and records its transcript and native log |
| `recorder.js` | runs a scenario and records the transcript; line format documented at the top |
| `corpus.js` | reads / writes / diffs corpus files |
| `capture.js` | CLI to record and compare |
Expand Down
7 changes: 5 additions & 2 deletions test/contract/capture.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
// node test/contract/capture.js --repeat 10 [filter] run each scenario 10 times (default 3)
// node test/contract/capture.js --root <dir> use the msnodesqlv8 in <dir>, e.g. an
// older release, to see how it differs
// node test/contract/capture.js --exact <name> only the scenario with exactly that name
// node test/contract/capture.js --native [filter] also record each scenario's native calls
// into native/<name>.json, for the fake
// driver to replay (a run whose transcript
Expand All @@ -24,12 +25,13 @@ const { captureScenario, runIsolated } = require('./recorder')
const corpus = require('./corpus')

function parseArgs (argv) {
const opts = { update: false, print: false, native: false, repeat: 3, root: path.join(__dirname, '../..'), filters: [] }
const opts = { update: false, print: false, native: false, exact: false, repeat: 3, root: path.join(__dirname, '../..'), filters: [] }
for (let i = 0; i < argv.length; i++) {
const a = argv[i]
if (a === '--update') opts.update = true
else if (a === '--print') opts.print = true
else if (a === '--native') opts.native = true
else if (a === '--exact') opts.exact = true
else if (a === '--repeat') opts.repeat = Number(argv[++i])
else if (a === '--root') opts.root = path.resolve(argv[++i])
else opts.filters.push(a)
Expand All @@ -41,7 +43,8 @@ async function main () {
const opts = parseArgs(process.argv.slice(2))
const version = require(path.join(opts.root, 'package.json')).version
const connectionString = new TestEnv().connectionString
const selected = scenarios.filter(s => opts.filters.length === 0 || opts.filters.some(f => s.name.includes(f)))
const chosen = s => opts.exact ? opts.filters.includes(s.name) : opts.filters.some(f => s.name.includes(f))
const selected = scenarios.filter(s => opts.filters.length === 0 || chosen(s))
console.log(`msnodesqlv8 ${version} (${opts.root}) - ${selected.length} scenarios, ${opts.repeat} runs each`)

const summary = { same: 0, different: 0, missing: 0, nondeterministic: [] }
Expand Down
3 changes: 2 additions & 1 deletion test/contract/corpus.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ function read (name, platform = process.platform) {
function header (scenario, status) {
const lines = [`# scenario: ${scenario.name}`, `# status: ${status}`]
for (const statement of scenario.setup ?? []) lines.push(`# setup: ${statement}`)
lines.push(`# sql: ${scenario.sql}`)
if (scenario.script) lines.push(`# script: test/contract/scripts/${path.basename(scenario.script)}`)
else lines.push(`# sql: ${scenario.sql}`)
if (scenario.params) lines.push(`# params: ${JSON.stringify(scenario.params, (k, v) => typeof v === 'number' && !isFinite(v) ? String(v) : v)}`)
if (scenario.at) lines.push(`# at: ${JSON.stringify(scenario.at)}`)
for (const f of scenario.followUps ?? []) {
Expand Down
37 changes: 37 additions & 0 deletions test/contract/corpus/scripts/issue-478.linux.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# scenario: scripts/issue-478
# status: known-bug
# script: test/contract/scripts/issue-478.js
setup: submitted
setup: rowcount 1
setup: cb ok rows=0 more=false
setup: done
setup: free
== A insert #t values (1)
A: submitted
A: info 01000 3621 The statement has been terminated.
A: error 23000 2627 Violation of PRIMARY KEY constraint 'pk_t'. Cannot insert duplicate key in object 'dbo.#t'. The duplicate key value is (1). more=false
A: free
== B select 1 as n; insert #t values (1)
B: submitted
B: meta n
B: row
B: info 01000 3621 The statement has been terminated.
B: error 23000 2627 Violation of PRIMARY KEY constraint 'pk_t'. Cannot insert duplicate key in object 'dbo.#t'. The duplicate key value is (1). more=false
B: free
== C select 1 as n; insert #t values (1); select 2 as m
C: submitted
C: meta n
C: row
C: info 01000 3621 The statement has been terminated.
C: error 23000 2627 Violation of PRIMARY KEY constraint 'pk_t'. Cannot insert duplicate key in object 'dbo.#t'. The duplicate key value is (1). more=true
C: rowcount 0
C: meta m
C: row
C: done
C: free
== D insert #t values (1); select 2 as m
D: submitted
D: info 01000 3621 The statement has been terminated.
D: error 23000 2627 Violation of PRIMARY KEY constraint 'pk_t'. Cannot insert duplicate key in object 'dbo.#t'. The duplicate key value is (1). more=false
D: free
check ok
41 changes: 41 additions & 0 deletions test/contract/corpus/scripts/issue-478.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# scenario: scripts/issue-478
# status: unreviewed
# script: test/contract/scripts/issue-478.js
setup: submitted
setup: rowcount 1
setup: cb ok rows=0 more=false
setup: done
setup: free
== A insert #t values (1)
A: submitted
A: info 01000 3621 The statement has been terminated.
A: error 23000 2627 Violation of PRIMARY KEY constraint 'pk_t'. Cannot insert duplicate key in object 'dbo.#t'. The duplicate key value is (1). more=false
A: free
== B select 1 as n; insert #t values (1)
B: submitted
B: meta n
B: row
B: info 01000 3621 The statement has been terminated.
B: error 23000 2627 Violation of PRIMARY KEY constraint 'pk_t'. Cannot insert duplicate key in object 'dbo.#t'. The duplicate key value is (1). more=false
B: free
== C select 1 as n; insert #t values (1); select 2 as m
C: submitted
C: meta n
C: row
C: info 01000 3621 The statement has been terminated.
C: error 23000 2627 Violation of PRIMARY KEY constraint 'pk_t'. Cannot insert duplicate key in object 'dbo.#t'. The duplicate key value is (1). more=true
C: rowcount 0
C: meta m
C: row
C: done
C: free
== D insert #t values (1); select 2 as m
D: submitted
D: error 23000 2627 Violation of PRIMARY KEY constraint 'pk_t'. Cannot insert duplicate key in object 'dbo.#t'. The duplicate key value is (1). more=true
D: info 01000 3621 The statement has been terminated.
D: rowcount 0
D: meta m
D: row
D: done
D: free
check ok
82 changes: 82 additions & 0 deletions test/contract/corpus/scripts/issue-479.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# scenario: scripts/issue-479
# status: unreviewed
# script: test/contract/scripts/issue-479.js
== A running cancel, at row 100
A: submitted
A: meta name
A: row x100
A: action cancel
A: done
A: cancel-cb ok
A: error HY008 0 Operation canceled more=false
A: free
A.check: submitted
A.check: meta n
A.check: row
A.check: cb ok rows=1 more=false
A.check: done
A.check: free
== B late cancel, after free
B: submitted
B: meta n
B: row
B: done
B: free
B: action cancel
B: done
B: cancel-cb error Error: [msnodesql] cannot cancel query (empty queue) id 2
B.check: submitted
B.check: meta n
B.check: row
B.check: cb ok rows=1 more=false
B.check: done
B.check: free
== C running cancel after a print, at row 100
C: submitted
C: info 01000 0 start
C: rowcount 0
C: meta name
C: row x100
C: action cancel
C: done
C: cancel-cb ok
C: row x4900
C: done
C: free
C.check: submitted
C.check: meta n
C.check: row
C.check: cb ok rows=1 more=false
C.check: done
C.check: free
== 4 a polling query, paused and cancelled at row 100
P4: submitted
P4: meta name
P4: row x100
P4: action pause
P4: action cancel
P4: done
P4: cancel-cb ok
P4: error HY008 0 Operation canceled more=false
P4: free
P4.check: submitted
P4.check: meta n
P4.check: row
P4.check: cb ok rows=1 more=false
P4.check: done
P4.check: free
== 5 paused and cancelled before it executes
P5: action pause
P5: action cancel
P5: submitted
P5: done
P5: cancel-cb ok
P5: error HY008 0 Operation canceled more=false
P5: free
P5.check: submitted
P5.check: meta n
P5.check: row
P5.check: cb ok rows=1 more=false
P5.check: done
P5.check: free
check ok
36 changes: 36 additions & 0 deletions test/contract/corpus/scripts/issue-480.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# scenario: scripts/issue-480
# status: unreviewed
# script: test/contract/scripts/issue-480.js
== pause at row 50, resume 50 ms later
at50: submitted
at50: meta a
at50: row x50
at50: action pause
at50: action resume
at50: row
at50: meta b
at50: row x51
at50: done
at50: free
== pause at row 51, resume 50 ms later
at51: submitted
at51: meta a
at51: row x51
at51: action pause
at51: action resume
at51: meta b
at51: row x51
at51: done
at51: free
== pause at row 52, resume 50 ms later
at52: submitted
at52: meta a
at52: row x51
at52: meta b
at52: row
at52: action pause
at52: action resume
at52: row x50
at52: done
at52: free
check ok
Loading
Loading