Skip to content

Commit b934d7b

Browse files
authored
feat: extend Node mock with connections and statements (#420)
1 parent 3dfe21e commit b934d7b

12 files changed

Lines changed: 817 additions & 252 deletions

File tree

‎docs/api-package-intros.mjs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ db.close()
3131
\`\`\`
3232
3333
Use [Getting Started](/docs) for native setup and a fuller first query. The [concepts](/docs/concepts/databases-and-connections) and [guides](/docs/guides/parameters-and-results) explain database lifetime, query parameters, and results. For the Node-only test export and its API, see [Test SQLite in Node](/docs/guides/node-test-mock).
34+
`,
35+
'react-native-nitro-sqlite/mock': `This Node-only export runs SQLite tests through \`better-sqlite3\` without loading the React Native module. Install \`better-sqlite3\` as a development dependency and import this module in your test setup. See [Test SQLite in Node](/docs/guides/node-test-mock) for Jest setup, shared connections, prepared statements, transactions, and cleanup.
3436
`,
3537
'react-native-nitro-sqlite-vec': `This optional package adds [sqlite-vec](https://github.com/asg017/sqlite-vec) vector search to Nitro SQLite. Its native code is compiled into the core package's SQLite build. The functions and types below cover availability checks, vector tables, and nearest-neighbor searches.
3638

‎docs/content/docs/guides/node-test-mock.mdx‎

Lines changed: 78 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
---
22
title: Test SQLite in Node
3-
description: Run SQL in Jest tests with the in-memory SQLite mock.
3+
description: Test shared SQLite connections, statements, and transactions in Node.
44
---
55

6-
Use `react-native-nitro-sqlite/mock` to run queries and batches in Node tests without loading the React Native module. Install `better-sqlite3` as a development dependency in your test project. The mock keeps each named database in memory, so close connections or reset the databases between tests. Use device tests for file storage or native scheduling.
6+
SQLite connections are handles to a database file. Separate handles can share committed data, and a transaction can keep a consistent snapshot while another connection writes under [WAL](/docs/guides/multiple-connections).
7+
8+
Use `react-native-nitro-sqlite/mock` to test these behaviors in Node without loading the React Native module. Install `better-sqlite3` as a development dependency in your test project. The mock creates databases in a temporary directory and supports queries, atomic batches, prepared statements, and callback transactions.
79

810
## Set up Jest
911

@@ -38,20 +40,83 @@ test('stores a note', () => {
3840
})
3941
```
4042

41-
The mock returns query rows through both `results` and `rows`, and supports synchronous and asynchronous queries and [atomic batches](/docs/guides/batch-operations). Bound `undefined` becomes SQL NULL. A query that fails throws from `execute()` or rejects from `executeAsync()`.
43+
The mock returns query rows through both `results` and `rows`, and supports [atomic batches](/docs/guides/batch-operations). Bound `undefined` becomes SQL NULL. A query that fails throws from `execute()` or rejects from `executeAsync()`.
44+
45+
## Share a database between connections
46+
47+
Connections with the same `name` and `location` open the same temporary file. A default connection reserves its name until you close it. Set `connection: 'independent'` to open another handle, and `readOnly: true` to prevent writes. Read-only opens fail if the file does not exist.
48+
49+
```ts
50+
const writer = open({ name: 'notes.sqlite' })
51+
writer.execute('PRAGMA journal_mode = WAL')
52+
writer.execute('CREATE TABLE notes (title TEXT)')
53+
writer.execute('INSERT INTO notes VALUES (?)', ['Draft'])
54+
55+
const reader = open({
56+
name: 'notes.sqlite',
57+
connection: 'independent',
58+
readOnly: true,
59+
})
60+
expect(reader.execute('SELECT title FROM notes').rows.item(0)?.title).toBe(
61+
'Draft',
62+
)
63+
64+
reader.close()
65+
writer.close()
66+
```
67+
68+
`location` selects a relative directory inside the mock's temporary directory. A name must be a file name; paths that escape the temporary directory throw.
69+
70+
Closing a connection preserves its data for reopening. `delete()` closes the connection and removes its database and sidecar files. It throws for a read-only connection or if another open connection uses the file. Close those connections before deleting it.
71+
72+
## Reuse a prepared statement
73+
74+
Preparing SQL once lets you execute it repeatedly with new parameter values. Each execution replaces the previous bindings. Omitted values bind as SQL NULL. Finalize statements before closing the connection.
75+
76+
```ts
77+
const db = open({ name: 'notes.sqlite' })
78+
db.execute('CREATE TABLE notes (title TEXT)')
79+
const insert = db.prepare('INSERT INTO notes VALUES (?)')
80+
81+
await insert.executeAsync(['Draft'])
82+
await insert.executeAsync(['Published'])
83+
insert.finalize()
84+
expect(insert.isFinalized).toBe(true)
85+
86+
db.close()
87+
```
88+
89+
`finalize()` is safe to repeat while the connection is open and idle. Executing a finalized statement or using a statement after its connection closes throws or rejects.
90+
91+
## Commit or roll back a transaction
92+
93+
A transaction groups operations into one commit. The mock keeps a callback transaction exclusive on its connection, including across `await`. Use the supplied `tx` for all database work inside the callback:
94+
95+
```ts
96+
const db = open({ name: 'notes.sqlite' })
97+
db.execute('CREATE TABLE notes (title TEXT)')
98+
99+
const count = await db.transaction(async (tx) => {
100+
await tx.executeAsync('INSERT INTO notes VALUES (?)', ['Draft'])
101+
return tx.execute('SELECT title FROM notes').rows.length
102+
})
103+
expect(count).toBe(1)
104+
105+
db.close()
106+
```
107+
108+
The callback's result resolves after commit. A callback error rolls back its changes. You can call `tx.commit()` or `tx.rollback()` explicitly; subsequent operations on that transaction fail. Await all `tx.executeAsync()` calls before a synchronous transaction operation or before returning from the callback.
109+
110+
Async connection calls run in call order and wait for callback transactions. Synchronous queries, preparation, statement finalization, close, and delete throw while their connection is busy. Awaiting a queued connection call inside its transaction callback deadlocks; use `tx` instead. Independent connections have separate queues.
42111

43-
## Mock API
112+
## Clean up between tests
44113

45-
Import these exports from `react-native-nitro-sqlite/mock`:
114+
Await all pending operations before calling `resetAllDatabases()`. It closes every handle and removes all temporary databases and sidecar files, including files whose connections you already closed. Connections and statements from before the reset cannot execute again.
46115

47-
| Export | Behavior |
48-
| --- | --- |
49-
| `open({ name })` | Opens a named in-memory database and returns a connection. Opening the same name twice before closing it throws. |
50-
| `NitroSQLite.open({ name })` | Calls the same `open()` function. |
51-
| `resetAllDatabases()` | Closes every open mock database and removes its data. |
116+
The reset throws if any connection is busy. Await that work and retry. No connections close when this check fails.
52117

53-
The returned connection implements `execute()`, `executeAsync()`, `executeBatch()`, `executeBatchAsync()`, `close()`, and `delete()`. `delete()` closes the in-memory database. Query methods return the public data of [`QueryResult`](/api/react-native-nitro-sqlite/type-aliases/QueryResult); batch methods return a `rowsAffected` count. Batch commands use the [`BatchQueryCommand`](/api/react-native-nitro-sqlite/interfaces/BatchQueryCommand) shape.
118+
## API reference and limits
54119

55-
## Limits
120+
The generated [mock API reference](/api/react-native-nitro-sqlite/mock) documents `open()`, `NitroSQLite.open()`, `MockConnection`, and `resetAllDatabases()`. Connection methods use the same parameter and result types as the native API.
56121

57-
The mock does not implement independent or read-only connections, files, attachments, prepared statements, callback transactions, or other native methods. Its async methods return promises but execute SQL synchronously in Node.
122+
The mock does not implement attachments, SQL file imports, or direct `NitroSQLite.native` methods. Its SQL runs synchronously on Node's thread; async methods schedule that work and return promises. It uses the SQLite version bundled with `better-sqlite3`, which can differ from the library's native SQLite build. Use device tests for native integration, thread behavior, lock contention, and performance.

‎docs/mock-api/package.json‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"name": "react-native-nitro-sqlite/mock",
3+
"private": true,
4+
"typedocOptions": {
5+
"entryPoints": ["../../packages/react-native-nitro-sqlite/src/mock.ts"],
6+
"tsconfig": "../tsconfig.typedoc.json"
7+
}
8+
}

‎docs/scripts/check-api.mjs‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ const requiredExports = {
2525
'TypeOrmNitroSQLiteConnection',
2626
'typeORMDriver',
2727
],
28+
'react-native-nitro-sqlite/mock': [
29+
'open',
30+
'NitroSQLite',
31+
'MockConnection',
32+
'resetAllDatabases',
33+
],
2834
'react-native-nitro-sqlite-vec': [
2935
'VectorColumnType',
3036
'VectorDistanceMetric',

‎docs/tsconfig.typedoc.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
},
1212
"files": [
1313
"../packages/react-native-nitro-sqlite/src/index.ts",
14+
"../packages/react-native-nitro-sqlite/src/mock.ts",
1415
"../packages/react-native-nitro-sqlite-vec/src/index.ts"
1516
]
1617
}

‎docs/typedoc.json‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
{
22
"entryPoints": [
33
"../packages/react-native-nitro-sqlite",
4-
"../packages/react-native-nitro-sqlite-vec"
4+
"../packages/react-native-nitro-sqlite-vec",
5+
"./mock-api"
56
],
67
"entryPointStrategy": "packages",
78
"name": "API Reference",

‎packages/react-native-nitro-sqlite/package.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@
117117
"!src/**/__tests__/**",
118118
"!src/**/__mocks__/**",
119119
"!src/mock.ts",
120+
"!src/mock/**",
120121
"!src/specs/**",
121122
"!src/types.ts"
122123
],

0 commit comments

Comments
 (0)