neon-testing 2.0.1-beta.9 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,8 +1,10 @@
1
- # Neon testing
1
+ # Neon Testing
2
2
 
3
3
  [![Integration tests](https://github.com/starmode-base/neon-testing/actions/workflows/test.yml/badge.svg)](https://github.com/starmode-base/neon-testing/actions/workflows/test.yml)
4
+ [![npm version](https://img.shields.io/npm/v/neon-testing)](https://www.npmjs.com/package/neon-testing)
5
+ [![GitHub release](https://img.shields.io/github/v/release/starmode-base/neon-testing)](https://github.com/starmode-base/neon-testing/releases)
4
6
 
5
- A [Vitest](https://vitest.dev/) utility for seamless integration tests with [Neon Postgres](https://neon.com/).
7
+ A [Vitest](https://vitest.dev/) utility for seamless integration tests with [Neon Postgres](https://neon.com/). <!-- A [STΛR MODΞ](https://starmode.dev) open-source project. -->
6
8
 
7
9
  Each test file runs against its own isolated PostgreSQL database (Neon branch), ensuring clean, parallel, and reproducible testing of code that interacts with a database. Because it uses a real, isolated clone of your production database, you can test code logic that depends on database features, such as transaction rollbacks, unique constraints, and more.
8
10
 
@@ -13,7 +15,7 @@ Each test file runs against its own isolated PostgreSQL database (Neon branch),
13
15
  - 🔄 **Isolated test environments** - Each test file runs against its own Postgres database with your actual schema and constraints
14
16
  - 🧹 **Automatic cleanup** - Neon test branches are created and destroyed automatically
15
17
  - 🐛 **Debug friendly** - Option to preserve test branches for debugging failed tests
16
- - 🛡️ **TypeScript native** - No JavaScript support
18
+ - 🛡️ **TypeScript native** - With JavaScript support
17
19
  - 🎯 **ESM only** - No CommonJS support
18
20
 
19
21
  ## How it works
@@ -41,12 +43,12 @@ If you prefer individual tests to be isolated, you can [reset the database](exam
41
43
  ### Install
42
44
 
43
45
  ```sh
44
- bun add -d neon-testing
46
+ bun add -d neon-testing vitest
45
47
  ```
46
48
 
47
49
  ### Minimal example
48
50
 
49
- ```typescript
51
+ ```ts
50
52
  // minimal.test.ts
51
53
  import { expect, test } from "vitest";
52
54
  import { makeNeonTesting } from "neon-testing";
@@ -73,11 +75,27 @@ test("database operations", async () => {
73
75
 
74
76
  ### Recommended usage
75
77
 
76
- #### 1. Configuration
78
+ #### 1. Plugin setup
79
+
80
+ First, add the Vite plugin to clear any existing `DATABASE_URL` environment variable before tests run, ensuring tests use isolated test databases.
81
+
82
+ ```ts
83
+ // vitest.config.ts or vite.config.ts
84
+ import { defineConfig } from "vitest/config";
85
+ import { neonTesting } from "neon-testing/utils";
86
+
87
+ export default defineConfig({
88
+ plugins: [neonTesting()],
89
+ });
90
+ ```
91
+
92
+ This plugin is recommended but not required. Without it, tests might accidentally use your existing `DATABASE_URL` (from `.env` files or environment variables) instead of the isolated test databases that Neon Testing creates. This can happen if you forget to call `withNeonTestBranch()` in a test file where database writes happen.
93
+
94
+ #### 2. Configuration
77
95
 
78
96
  Use the `makeNeonTesting` factory to generate a lifecycle function for your tests.
79
97
 
80
- ```typescript
98
+ ```ts
81
99
  // test-setup.ts
82
100
  import { makeNeonTesting } from "neon-testing";
83
101
 
@@ -88,11 +106,11 @@ export const withNeonTestBranch = makeNeonTesting({
88
106
  });
89
107
  ```
90
108
 
91
- #### 2. Enable database testing
109
+ #### 3. Enable database testing
92
110
 
93
111
  Then call the exported test lifecycle function in the test files where you need database access.
94
112
 
95
- ```typescript
113
+ ```ts
96
114
  // recommended.test.ts
97
115
  import { expect, test } from "vitest";
98
116
  import { withNeonTestBranch } from "./test-setup";
@@ -134,18 +152,66 @@ This library works with any database driver that supports Neon Postgres and Vite
134
152
 
135
153
  ## Configuration
136
154
 
137
- You configure neon-testing in two places:
155
+ You configure Neon Testing in two places:
138
156
 
139
157
  - **Base settings** in `makeNeonTesting()`
140
158
  - **Optional overrides** in `withNeonTestBranch()`
141
159
 
142
- See all available options in [NeonTestingOptions](https://github.com/starmode-base/neon-testing/blob/main/index.ts#L33-L75).
160
+ Configure these in `makeNeonTesting()` and optionally override per test file via `withNeonTestBranch()`.
161
+
162
+ ```ts
163
+ export interface NeonTestingOptions {
164
+ /**
165
+ * The Neon API key, this is used to create and teardown test branches
166
+ *
167
+ * https://neon.com/docs/manage/api-keys#creating-api-keys
168
+ */
169
+ apiKey: string;
170
+ /**
171
+ * The Neon project ID to operate on
172
+ *
173
+ * https://console.neon.tech/app/projects
174
+ */
175
+ projectId: string;
176
+ /**
177
+ * The parent branch ID for the new branch. If omitted or empty, the branch
178
+ * will be created from the project's default branch.
179
+ */
180
+ parentBranchId?: string;
181
+ /**
182
+ * Whether to create a schema-only branch (default: false)
183
+ */
184
+ schemaOnly?: boolean;
185
+ /**
186
+ * The type of connection to create (pooler is recommended)
187
+ */
188
+ endpoint?: "pooler" | "direct";
189
+ /**
190
+ * Delete the test branch in afterAll (default: true)
191
+ *
192
+ * Disabling this will leave each test branch in the Neon project after the
193
+ * test suite runs
194
+ */
195
+ deleteBranch?: boolean;
196
+ /**
197
+ * Automatically close Neon WebSocket connections opened during tests before
198
+ * deleting the branch (default: false)
199
+ *
200
+ * Suppresses the specific Neon WebSocket "Connection terminated unexpectedly"
201
+ * error that may surface when deleting a branch with open WebSocket
202
+ * connections
203
+ */
204
+ autoCloseWebSockets?: boolean;
205
+ }
206
+ ```
207
+
208
+ See all available options in [NeonTestingOptions](index.ts#L31-L73).
143
209
 
144
210
  ### Base configuration
145
211
 
146
212
  Configure the base settings in `makeNeonTesting()`:
147
213
 
148
- ```typescript
214
+ ```ts
149
215
  import { makeNeonTesting } from "neon-testing";
150
216
 
151
217
  export const withNeonTestBranch = makeNeonTesting({
@@ -158,32 +224,41 @@ export const withNeonTestBranch = makeNeonTesting({
158
224
 
159
225
  Override the base configuration in specific test files with `withNeonTestBranch()`:
160
226
 
161
- ```typescript
227
+ ```ts
162
228
  import { withNeonTestBranch } from "./test-setup";
163
229
 
164
230
  withNeonTestBranch({ parentBranchId: "br-staging-123" });
165
231
  ```
166
232
 
233
+ ## Continuous integration
234
+
235
+ It's easy to run Neon integration tests in CI/CD pipelines:
236
+
237
+ - **GitHub Actions** — see the [example workflow](.github/workflows/test.yml)
238
+ - **Vercel** — either
239
+ - add `vitest run` to the `build` script in [package.json](https://github.com/starmode-base/template-tanstack-start/blob/83c784e164b55fd8d59c5b57b907251e5eb03de1/app/package.json#L11), or
240
+ - add `vitest run` to the _Build Command_ in the Vercel dashboard
241
+
167
242
  ## Utilities
168
243
 
169
244
  ### deleteAllTestBranches()
170
245
 
171
246
  The `deleteAllTestBranches()` function is a utility that deletes all test branches from your Neon project. This is useful for cleanup when tests fail unexpectedly and leave orphaned test branches.
172
247
 
173
- ```typescript
248
+ ```ts
174
249
  import { withNeonTestBranch } from "./test-setup";
175
250
 
176
251
  // Access the cleanup utility
177
252
  await withNeonTestBranch.deleteAllTestBranches();
178
253
  ```
179
254
 
180
- The function identifies test branches by looking for the `integration-test: true` annotation that neon-testing automatically adds to all test branches it creates.
255
+ The function identifies test branches by looking for the `integration-test: true` annotation that Neon Testing automatically adds to all test branches it creates.
181
256
 
182
257
  ### lazySingleton()
183
258
 
184
259
  The `lazySingleton()` function creates a lazy singleton from a factory function. This is useful for managing database connections efficiently:
185
260
 
186
- ```typescript
261
+ ```ts
187
262
  import { lazySingleton } from "neon-testing/utils";
188
263
  import { neon } from "@neondatabase/serverless";
189
264
 
@@ -219,12 +294,8 @@ bun run release
219
294
 
220
295
  The command will abort if there are uncommitted changes in the working tree, or if the `version` in [package.json](package.json) has not been incremented.
221
296
 
222
- ## License
223
-
224
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
225
-
226
- ## Need help?
297
+ ## Author
227
298
 
228
- Hi, Im [Mikael Lirbank](https://www.lirbank.com/). I help teams build reliable AI systems. I care about quality—AI evals, robust test suites, clean data models, and clean architecture. Sometimes I draw user interfaces.
299
+ Hi, I'm [Mikael Lirbank](https://www.lirbank.com/). I build robust, reliable, high-quality AI systems. I care deeply about quality—AI evals, robust test suites, clean data models, and clean architecture.
229
300
 
230
- Want to ship faster without breaking things? Let’s talk.
301
+ Need help building elegant systems? [I'm happy to help](https://www.lirbank.com/).
package/dist/index.d.ts CHANGED
@@ -64,3 +64,4 @@ export declare function makeNeonTesting(factoryOptions: NeonTestingOptions): {
64
64
  (overrides?: NeonTestingOverrides): void;
65
65
  deleteAllTestBranches: () => Promise<void>;
66
66
  };
67
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AA8BA,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,QAAQ,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC/B;;;;;OAKG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;;OAOG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,mEAAmE;AACnE,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,EAAE,QAAQ,CAAC,CAAC;AAE/E;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAAC,cAAc,EAAE,kBAAkB;iBA0BlD,oBAAoB;;EAmGnC"}
@@ -2,3 +2,4 @@
2
2
  * Create a lazy singleton from a factory function
3
3
  */
4
4
  export declare function lazySingleton<T>(factory: () => T): () => T;
5
+ //# sourceMappingURL=singleton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"singleton.d.ts","sourceRoot":"","sources":["../singleton.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,CAM1D"}
package/dist/utils.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export { lazySingleton } from "./singleton.js";
2
2
  export { neonTesting } from "./vite-plugin.js";
3
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC"}
@@ -1,2 +1,10 @@
1
1
  import type { Plugin } from "vite";
2
- export declare function neonTesting(): Plugin;
2
+ export declare function neonTesting(options?: {
3
+ /**
4
+ * Enable debug logging.
5
+ *
6
+ * @default false
7
+ */
8
+ debug?: boolean;
9
+ }): Plugin;
10
+ //# sourceMappingURL=vite-plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite-plugin.d.ts","sourceRoot":"","sources":["../vite-plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC,wBAAgB,WAAW,CACzB,OAAO,GAAE;IACP;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACZ,GACL,MAAM,CAyBR"}
@@ -1,14 +1,20 @@
1
1
  import { fileURLToPath } from "node:url";
2
- export function neonTesting() {
2
+ export function neonTesting(options = {}) {
3
3
  return {
4
4
  name: "neon-testing-plugin",
5
- enforce: "pre",
5
+ // Run as late as possible to reduce the risk of other plugins restoring
6
+ // DATABASE_URL after we clear it
7
+ enforce: "post",
6
8
  config(user) {
7
9
  const setupPath = fileURLToPath(new URL("./vitest-setup.js", import.meta.url));
8
- const setup = new Set([...(user.test?.setupFiles ?? []), setupPath]);
9
10
  return {
10
11
  test: {
11
- setupFiles: Array.from(setup),
12
+ // Register the vitest-setup.js file to run after other setup files
13
+ setupFiles: Array.from(new Set([...(user.test?.setupFiles ?? []), setupPath])),
14
+ env: {
15
+ ...user.test?.env,
16
+ NEON_TESTING_DEBUG: options.debug ? "true" : "false",
17
+ },
12
18
  },
13
19
  };
14
20
  },
@@ -1 +1 @@
1
- {"version":3,"file":"vite-plugin.js","sourceRoot":"","sources":["../vite-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC,MAAM,UAAU,WAAW;IACzB,OAAO;QACL,IAAI,EAAE,qBAAqB;QAC3B,OAAO,EAAE,KAAK;QACd,MAAM,CAAC,IAAI;YACT,MAAM,SAAS,GAAG,aAAa,CAC7B,IAAI,GAAG,CAAC,mBAAmB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAC9C,CAAC;YAEF,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,IAAI,EAAE,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC;YAErE,OAAO;gBACL,IAAI,EAAE;oBACJ,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;iBAC9B;aACF,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"vite-plugin.js","sourceRoot":"","sources":["../vite-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC,MAAM,UAAU,WAAW,CACzB,UAOI,EAAE;IAEN,OAAO;QACL,IAAI,EAAE,qBAAqB;QAC3B,wEAAwE;QACxE,iCAAiC;QACjC,OAAO,EAAE,MAAM;QACf,MAAM,CAAC,IAAI;YACT,MAAM,SAAS,GAAG,aAAa,CAC7B,IAAI,GAAG,CAAC,mBAAmB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAC9C,CAAC;YAEF,OAAO;gBACL,IAAI,EAAE;oBACJ,mEAAmE;oBACnE,UAAU,EAAE,KAAK,CAAC,IAAI,CACpB,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,IAAI,EAAE,CAAC,EAAE,SAAS,CAAC,CAAC,CACvD;oBACD,GAAG,EAAE;wBACH,GAAG,IAAI,CAAC,IAAI,EAAE,GAAG;wBACjB,kBAAkB,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;qBACrD;iBACF;aACF,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ declare const isVitest: string | undefined;
2
+ declare const isDebug: boolean;
3
+ //# sourceMappingURL=vitest-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vitest-setup.d.ts","sourceRoot":"","sources":["../vitest-setup.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,QAAQ,oBAAqB,CAAC;AACpC,QAAA,MAAM,OAAO,SAA4C,CAAC"}
@@ -1,11 +1,10 @@
1
+ "use strict";
1
2
  const isVitest = process.env.VITEST;
2
-
3
+ const isDebug = process.env.NEON_TESTING_DEBUG === "true";
3
4
  if (isVitest) {
4
- if (process.env.DATABASE_URL) {
5
- console.warn(
6
- "[neon-testing] Clearing existing DATABASE_URL in test environment",
7
- );
8
- }
9
-
10
- delete process.env.DATABASE_URL;
5
+ if (process.env.DATABASE_URL && isDebug) {
6
+ console.debug("[neon-testing] Clearing existing DATABASE_URL in test environment");
7
+ }
8
+ delete process.env.DATABASE_URL;
11
9
  }
10
+ //# sourceMappingURL=vitest-setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vitest-setup.js","sourceRoot":"","sources":["../vitest-setup.ts"],"names":[],"mappings":";AAAA,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;AACpC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,KAAK,MAAM,CAAC;AAE1D,IAAI,QAAQ,EAAE,CAAC;IACb,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,OAAO,EAAE,CAAC;QACxC,OAAO,CAAC,KAAK,CACX,mEAAmE,CACpE,CAAC;IACJ,CAAC;IAED,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;AAClC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neon-testing",
3
- "version": "2.0.1-beta.9",
3
+ "version": "2.1.1",
4
4
  "description": "A Vitest utility for seamless integration tests with Neon Postgres",
5
5
  "keywords": [
6
6
  "neon",
@@ -22,26 +22,20 @@
22
22
  "./utils": {
23
23
  "types": "./dist/utils.d.ts",
24
24
  "import": "./dist/utils.js"
25
- },
26
- "./vite-plugin": {
27
- "types": "./dist/vite-plugin.d.ts",
28
- "import": "./dist/vite-plugin.js"
29
25
  }
30
26
  },
31
- "types": "./dist/index.d.ts",
32
27
  "files": [
33
- "dist",
34
- "vitest-setup.js"
28
+ "dist"
35
29
  ],
36
30
  "scripts": {
31
+ "build": "rm -rf dist && tsc",
37
32
  "test": "vitest",
38
33
  "format": "prettier --write .",
39
- "build": "rm -rf dist && tsc -p tsconfig.build.json && cp vitest-setup.js dist/",
40
- "release:patch": "bun pm version patch && bun publish --tag latest",
41
- "release:minor": "bun pm version minor && bun publish --tag latest",
42
- "release:major": "bun pm version major && bun publish --tag latest",
43
- "release:beta": "bun pm version prerelease --preid=beta && bun publish --tag beta",
44
- "prepublishOnly": "bun run build && vitest run && prettier --check .",
34
+ "check": "bun run build && bunx vitest run && bunx prettier --check .",
35
+ "release:patch": "bun run check && bun pm version patch && bun publish --tag latest",
36
+ "release:minor": "bun run check && bun pm version minor && bun publish --tag latest",
37
+ "release:major": "bun run check && bun pm version major && bun publish --tag latest",
38
+ "release:beta": "bun run check && bun pm version prerelease --preid=beta && bun publish --tag beta",
45
39
  "postpublish": "git push --follow-tags"
46
40
  },
47
41
  "dependencies": {
package/vitest-setup.js DELETED
@@ -1,11 +0,0 @@
1
- const isVitest = process.env.VITEST;
2
-
3
- if (isVitest) {
4
- if (process.env.DATABASE_URL) {
5
- console.warn(
6
- "[neon-testing] Clearing existing DATABASE_URL in test environment",
7
- );
8
- }
9
-
10
- delete process.env.DATABASE_URL;
11
- }