@danceroutine/tango-testing 0.1.0 → 1.0.0
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/LICENSE +21 -0
- package/README.md +85 -0
- package/dist/aDBClient-W6eXsK3X.js +21 -0
- package/dist/aDBClient-W6eXsK3X.js.map +1 -0
- package/dist/assertions/index.js +1 -1
- package/dist/{assertions-CN6KxXhH.js → assertions-CCFZ53Y-.js} +1 -1
- package/dist/assertions-CCFZ53Y-.js.map +1 -0
- package/dist/express/anExpressRequest.d.ts +24 -0
- package/dist/express/anExpressResponse.d.ts +9 -0
- package/dist/express/index.d.ts +3 -0
- package/dist/express/index.js +3 -0
- package/dist/express-Czpfz_Ay.js +68 -0
- package/dist/express-Czpfz_Ay.js.map +1 -0
- package/dist/factories/ModelDataFactory.d.ts +16 -1
- package/dist/factories/index.js +1 -1
- package/dist/{factories-CCAZ6E-g.js → factories-Cl_CAzbj.js} +19 -4
- package/dist/factories-Cl_CAzbj.js.map +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +8 -11
- package/dist/integration/HarnessStrategyRegistry.d.ts +15 -0
- package/dist/integration/TestHarness.d.ts +23 -2
- package/dist/integration/anIntegrationHarness.d.ts +5 -0
- package/dist/integration/config.d.ts +4 -0
- package/dist/integration/conformance/index.d.ts +1 -0
- package/dist/integration/conformance/runDialectConformanceSuite.d.ts +11 -0
- package/dist/integration/domain/Dialect.d.ts +5 -4
- package/dist/integration/domain/ResetMode.d.ts +6 -5
- package/dist/integration/index.d.ts +8 -1
- package/dist/integration/index.js +3 -2
- package/dist/integration/migrations/ApplyAndVerifyMigrations.d.ts +3 -0
- package/dist/integration/migrations/AssertMigrationPlan.d.ts +3 -0
- package/dist/integration/migrations/IntrospectSchema.d.ts +3 -0
- package/dist/integration/orm/createQuerySetFixture.d.ts +10 -0
- package/dist/integration/orm/expectQueryResult.d.ts +4 -0
- package/dist/integration/orm/index.d.ts +6 -0
- package/dist/integration/orm/seedTable.d.ts +5 -0
- package/dist/integration/runtime/aTangoConfig.d.ts +8 -0
- package/dist/integration/runtime/index.d.ts +6 -0
- package/dist/integration/runtime/setupTestTangoRuntime.d.ts +6 -0
- package/dist/integration/smoke/AppProcessHarness.d.ts +83 -0
- package/dist/integration/smoke/index.d.ts +4 -0
- package/dist/integration/strategies/PostgresHarnessStrategy.d.ts +9 -0
- package/dist/integration/strategies/SqliteHarnessStrategy.d.ts +9 -0
- package/dist/integration-BrJw6NzG.js +747 -0
- package/dist/integration-BrJw6NzG.js.map +1 -0
- package/dist/mocks/DBClient.d.ts +1 -9
- package/dist/mocks/MockQuerySetResult.d.ts +5 -12
- package/dist/mocks/aDBClient.d.ts +21 -0
- package/dist/mocks/aManager.d.ts +17 -0
- package/dist/mocks/aQueryExecutor.d.ts +14 -0
- package/dist/mocks/aQueryResult.d.ts +5 -0
- package/dist/mocks/aQuerySet.d.ts +8 -0
- package/dist/mocks/aRequestContext.d.ts +22 -0
- package/dist/mocks/index.d.ts +9 -4
- package/dist/mocks/index.js +4 -6
- package/dist/mocks-BkwkXQQt.js +136 -0
- package/dist/mocks-BkwkXQQt.js.map +1 -0
- package/dist/vitest/index.js +3 -2
- package/dist/vitest/registerVitestTango.d.ts +3 -3
- package/dist/{vitest-PxMJue7R.js → vitest-37qN8D93.js} +4 -4
- package/dist/vitest-37qN8D93.js.map +1 -0
- package/package.json +81 -68
- package/dist/assertions/assertions.js +0 -8
- package/dist/assertions-CN6KxXhH.js.map +0 -1
- package/dist/factories/ModelDataFactory.js +0 -33
- package/dist/factories-CCAZ6E-g.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/integration/orm.d.ts +0 -9
- package/dist/integration/orm.js +0 -39
- package/dist/integration/strategies/PostgresHarnessStrategy.js +0 -95
- package/dist/integration-CDdpboYz.js +0 -378
- package/dist/integration-CDdpboYz.js.map +0 -1
- package/dist/mocks/DBClient.js +0 -1
- package/dist/mocks/MockQuerySetResult.js +0 -1
- package/dist/mocks/RepositoryLike.d.ts +0 -12
- package/dist/mocks/RepositoryLike.js +0 -1
- package/dist/mocks/aMockDBClient.d.ts +0 -2
- package/dist/mocks/aMockDBClient.js +0 -13
- package/dist/mocks/aMockQuerySet.d.ts +0 -2
- package/dist/mocks/aMockQuerySet.js +0 -15
- package/dist/mocks/aMockRepository.d.ts +0 -2
- package/dist/mocks/aMockRepository.js +0 -20
- package/dist/mocks/types.d.ts +0 -33
- package/dist/mocks-qo-1vCez.js +0 -72
- package/dist/mocks-qo-1vCez.js.map +0 -1
- package/dist/version.d.ts +0 -1
- package/dist/vitest/registerVitestTango.js +0 -90
- package/dist/vitest-PxMJue7R.js.map +0 -1
|
@@ -0,0 +1,747 @@
|
|
|
1
|
+
import { __export } from "./chunk-BkvOhyD0.js";
|
|
2
|
+
import { aDBClient } from "./aDBClient-W6eXsK3X.js";
|
|
3
|
+
import { vi } from "vitest";
|
|
4
|
+
import { QuerySet, initializeTangoRuntime, resetTangoRuntime } from "@danceroutine/tango-orm";
|
|
5
|
+
import { MigrationRunner, MigrationRunner as MigrationRunner$1, createDefaultIntrospectorStrategy } from "@danceroutine/tango-migrations";
|
|
6
|
+
import { defineConfig, loadConfig } from "@danceroutine/tango-config";
|
|
7
|
+
import { spawn } from "node:child_process";
|
|
8
|
+
import { quoteSqlIdentifier, quoteSqlIdentifier as quoteSqlIdentifier$1, quoteSqlIdentifier as quoteSqlIdentifier$2, validateSqlIdentifier, validateSqlIdentifier as validateSqlIdentifier$1, validateSqlIdentifier as validateSqlIdentifier$2 } from "@danceroutine/tango-core";
|
|
9
|
+
import { PostgresAdapter, SqliteAdapter } from "@danceroutine/tango-orm/connection";
|
|
10
|
+
import { rm } from "node:fs/promises";
|
|
11
|
+
|
|
12
|
+
//#region src/integration/domain/Dialect.ts
|
|
13
|
+
const Dialect = {
|
|
14
|
+
Sqlite: "sqlite",
|
|
15
|
+
Postgres: "postgres"
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/integration/domain/ResetMode.ts
|
|
20
|
+
const ResetMode = {
|
|
21
|
+
Transaction: "transaction",
|
|
22
|
+
Truncate: "truncate",
|
|
23
|
+
DropSchema: "drop-schema"
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/integration/domain/index.ts
|
|
28
|
+
var domain_exports = {};
|
|
29
|
+
__export(domain_exports, {
|
|
30
|
+
Dialect: () => Dialect,
|
|
31
|
+
ResetMode: () => ResetMode
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region src/integration/migrations/AssertMigrationPlan.ts
|
|
36
|
+
async function assertMigrationPlan(harness, options) {
|
|
37
|
+
const runner = harness.migrationRunner(options.migrationsDir);
|
|
38
|
+
const plan = await runner.plan();
|
|
39
|
+
for (const snippet of options.expectSqlContains ?? []) if (!plan.includes(snippet)) throw new Error(`Expected migration plan to contain: ${snippet}`);
|
|
40
|
+
return plan;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
//#endregion
|
|
44
|
+
//#region src/integration/migrations/ApplyAndVerifyMigrations.ts
|
|
45
|
+
async function applyAndVerifyMigrations(harness, options) {
|
|
46
|
+
const runner = harness.migrationRunner(options.migrationsDir);
|
|
47
|
+
await runner.apply(options.toId);
|
|
48
|
+
const statuses = await runner.status();
|
|
49
|
+
for (const id of options.expectedAppliedIds ?? []) {
|
|
50
|
+
const row = statuses.find((status) => status.id === id);
|
|
51
|
+
if (!row || !row.applied) throw new Error(`Expected migration ${id} to be applied`);
|
|
52
|
+
}
|
|
53
|
+
return { statuses };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region src/integration/migrations/IntrospectSchema.ts
|
|
58
|
+
const introspectorStrategy = createDefaultIntrospectorStrategy();
|
|
59
|
+
async function introspectSchema(harness) {
|
|
60
|
+
if (harness.dialect !== Dialect.Postgres && harness.dialect !== Dialect.Sqlite) throw new Error(`No introspector registered for dialect: ${String(harness.dialect)}`);
|
|
61
|
+
const dialect = harness.dialect === Dialect.Postgres ? "postgres" : "sqlite";
|
|
62
|
+
return introspectorStrategy.introspect(dialect, harness.dbClient);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
//#endregion
|
|
66
|
+
//#region src/integration/migrations/index.ts
|
|
67
|
+
var migrations_exports = {};
|
|
68
|
+
__export(migrations_exports, {
|
|
69
|
+
applyAndVerifyMigrations: () => applyAndVerifyMigrations,
|
|
70
|
+
assertMigrationPlan: () => assertMigrationPlan,
|
|
71
|
+
introspectSchema: () => introspectSchema
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region src/integration/conformance/runDialectConformanceSuite.ts
|
|
76
|
+
async function runDialectConformanceSuite(strategy, options = {}) {
|
|
77
|
+
const harness = await strategy.create(options.createOptions);
|
|
78
|
+
if (harness.dialect !== strategy.dialect) throw new Error(`Conformance failed: harness dialect '${String(harness.dialect)}' does not match strategy dialect '${String(strategy.dialect)}'`);
|
|
79
|
+
if (harness.capabilities !== strategy.capabilities) throw new Error("Conformance failed: harness capabilities must be strategy capabilities reference");
|
|
80
|
+
let resetBeforeSetupThrew = false;
|
|
81
|
+
try {
|
|
82
|
+
await harness.reset();
|
|
83
|
+
} catch {
|
|
84
|
+
resetBeforeSetupThrew = true;
|
|
85
|
+
}
|
|
86
|
+
if (!resetBeforeSetupThrew) throw new Error("Conformance failed: reset() must throw before setup()");
|
|
87
|
+
await harness.setup();
|
|
88
|
+
await harness.reset();
|
|
89
|
+
harness.migrationRunner(options.migrationsDir ?? "/tmp/migrations");
|
|
90
|
+
await harness.teardown();
|
|
91
|
+
let dbClientAfterTeardownThrew = false;
|
|
92
|
+
try {
|
|
93
|
+
harness.dbClient;
|
|
94
|
+
} catch {
|
|
95
|
+
dbClientAfterTeardownThrew = true;
|
|
96
|
+
}
|
|
97
|
+
if (!dbClientAfterTeardownThrew) throw new Error("Conformance failed: dbClient getter must throw after teardown()");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
//#endregion
|
|
101
|
+
//#region src/integration/conformance/index.ts
|
|
102
|
+
var conformance_exports = {};
|
|
103
|
+
__export(conformance_exports, { runDialectConformanceSuite: () => runDialectConformanceSuite });
|
|
104
|
+
|
|
105
|
+
//#endregion
|
|
106
|
+
//#region src/integration/runtime/aTangoConfig.ts
|
|
107
|
+
function aTangoConfig(options = {}) {
|
|
108
|
+
const adapter = options.adapter ?? "sqlite";
|
|
109
|
+
return defineConfig({
|
|
110
|
+
current: "test",
|
|
111
|
+
environments: {
|
|
112
|
+
development: {
|
|
113
|
+
name: "development",
|
|
114
|
+
db: adapter === "sqlite" ? {
|
|
115
|
+
adapter: "sqlite",
|
|
116
|
+
filename: ":memory:",
|
|
117
|
+
maxConnections: 1
|
|
118
|
+
} : {
|
|
119
|
+
adapter: "postgres",
|
|
120
|
+
url: "postgres://postgres:postgres@localhost:5432/tango",
|
|
121
|
+
maxConnections: 1
|
|
122
|
+
},
|
|
123
|
+
migrations: {
|
|
124
|
+
dir: "migrations",
|
|
125
|
+
online: adapter === "postgres"
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
test: {
|
|
129
|
+
name: "test",
|
|
130
|
+
db: adapter === "sqlite" ? {
|
|
131
|
+
adapter: "sqlite",
|
|
132
|
+
filename: ":memory:",
|
|
133
|
+
maxConnections: 1
|
|
134
|
+
} : {
|
|
135
|
+
adapter: "postgres",
|
|
136
|
+
url: "postgres://postgres:postgres@localhost:5432/tango_test",
|
|
137
|
+
maxConnections: 1
|
|
138
|
+
},
|
|
139
|
+
migrations: {
|
|
140
|
+
dir: "migrations",
|
|
141
|
+
online: adapter === "postgres"
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
production: {
|
|
145
|
+
name: "production",
|
|
146
|
+
db: adapter === "sqlite" ? {
|
|
147
|
+
adapter: "sqlite",
|
|
148
|
+
filename: ":memory:",
|
|
149
|
+
maxConnections: 1
|
|
150
|
+
} : {
|
|
151
|
+
adapter: "postgres",
|
|
152
|
+
url: "postgres://postgres:postgres@localhost:5432/tango",
|
|
153
|
+
maxConnections: 1
|
|
154
|
+
},
|
|
155
|
+
migrations: {
|
|
156
|
+
dir: "migrations",
|
|
157
|
+
online: adapter === "postgres"
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
//#endregion
|
|
165
|
+
//#region src/integration/runtime/setupTestTangoRuntime.ts
|
|
166
|
+
async function setupTestTangoRuntime(options = {}) {
|
|
167
|
+
await resetTangoRuntime();
|
|
168
|
+
return initializeTangoRuntime(() => aTangoConfig(options));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
//#endregion
|
|
172
|
+
//#region src/integration/runtime/index.ts
|
|
173
|
+
var runtime_exports = {};
|
|
174
|
+
__export(runtime_exports, {
|
|
175
|
+
aTangoConfig: () => aTangoConfig,
|
|
176
|
+
setupTestTangoRuntime: () => setupTestTangoRuntime
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
//#endregion
|
|
180
|
+
//#region src/integration/smoke/AppProcessHarness.ts
|
|
181
|
+
const DEFAULT_READY_TIMEOUT_MS = 3e4;
|
|
182
|
+
const DEFAULT_READY_INTERVAL_MS = 250;
|
|
183
|
+
const DEFAULT_STOP_TIMEOUT_MS = 1e4;
|
|
184
|
+
const MAX_LOG_BUFFER_CHARS = 2e4;
|
|
185
|
+
const defaultDeps = {
|
|
186
|
+
spawnProcess: (command, args, options) => spawn(command, args, options),
|
|
187
|
+
fetchImpl: fetch,
|
|
188
|
+
sleep: (ms) => new Promise((resolve) => {
|
|
189
|
+
setTimeout(resolve, ms);
|
|
190
|
+
})
|
|
191
|
+
};
|
|
192
|
+
var AppProcessHarness = class AppProcessHarness {
|
|
193
|
+
static BRAND = "tango.testing.app_process_harness";
|
|
194
|
+
__tangoBrand = AppProcessHarness.BRAND;
|
|
195
|
+
child;
|
|
196
|
+
baseUrl;
|
|
197
|
+
readyUrl;
|
|
198
|
+
readyTimeoutMs;
|
|
199
|
+
readyIntervalMs;
|
|
200
|
+
stopTimeoutMs;
|
|
201
|
+
deps;
|
|
202
|
+
stopped = false;
|
|
203
|
+
stdoutBuffer = "";
|
|
204
|
+
stderrBuffer = "";
|
|
205
|
+
constructor(options, deps) {
|
|
206
|
+
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
207
|
+
this.readyUrl = `${this.baseUrl}${normalizePath(options.readyPath ?? "/health")}`;
|
|
208
|
+
this.readyTimeoutMs = options.readyTimeoutMs ?? DEFAULT_READY_TIMEOUT_MS;
|
|
209
|
+
this.readyIntervalMs = options.readyIntervalMs ?? DEFAULT_READY_INTERVAL_MS;
|
|
210
|
+
this.stopTimeoutMs = options.stopTimeoutMs ?? DEFAULT_STOP_TIMEOUT_MS;
|
|
211
|
+
this.deps = deps;
|
|
212
|
+
this.child = this.deps.spawnProcess(options.command, options.args ?? [], {
|
|
213
|
+
cwd: options.cwd,
|
|
214
|
+
env: {
|
|
215
|
+
...process.env,
|
|
216
|
+
...options.env
|
|
217
|
+
},
|
|
218
|
+
stdio: "pipe"
|
|
219
|
+
});
|
|
220
|
+
this.child.stdout?.on("data", (chunk) => {
|
|
221
|
+
this.stdoutBuffer = appendBuffer(this.stdoutBuffer, String(chunk));
|
|
222
|
+
});
|
|
223
|
+
this.child.stderr?.on("data", (chunk) => {
|
|
224
|
+
this.stderrBuffer = appendBuffer(this.stderrBuffer, String(chunk));
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Narrow an unknown value to the smoke-test harness that owns a child process.
|
|
229
|
+
*/
|
|
230
|
+
static isAppProcessHarness(value) {
|
|
231
|
+
return typeof value === "object" && value !== null && value.__tangoBrand === AppProcessHarness.BRAND;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Spawn the target process and wait until its readiness endpoint responds successfully.
|
|
235
|
+
*/
|
|
236
|
+
static async start(options, deps = {}) {
|
|
237
|
+
const mergedDeps = {
|
|
238
|
+
...defaultDeps,
|
|
239
|
+
...deps
|
|
240
|
+
};
|
|
241
|
+
const harness = new AppProcessHarness(options, mergedDeps);
|
|
242
|
+
await harness.waitForReady();
|
|
243
|
+
return harness;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Return the buffered stdout log for recent process output.
|
|
247
|
+
*/
|
|
248
|
+
getStdoutLog() {
|
|
249
|
+
return this.stdoutBuffer;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Return the buffered stderr log for recent process output.
|
|
253
|
+
*/
|
|
254
|
+
getStderrLog() {
|
|
255
|
+
return this.stderrBuffer;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Return stdout and stderr in a single formatted string for debugging failures.
|
|
259
|
+
*/
|
|
260
|
+
getCombinedLog() {
|
|
261
|
+
const stdout = this.stdoutBuffer.trim();
|
|
262
|
+
const stderr = this.stderrBuffer.trim();
|
|
263
|
+
if (!stdout && !stderr) return "";
|
|
264
|
+
return [`[stdout]\n${stdout}`, `[stderr]\n${stderr}`].join("\n\n").trim();
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Issue an HTTP request against the managed application process.
|
|
268
|
+
*/
|
|
269
|
+
async request(path, init) {
|
|
270
|
+
const target = path.startsWith("http") ? path : `${this.baseUrl}${normalizePath(path)}`;
|
|
271
|
+
return this.deps.fetchImpl(target, init);
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Assert an HTTP response status and include process logs when it mismatches.
|
|
275
|
+
*/
|
|
276
|
+
async assertResponseStatus(response, expectedStatus, label) {
|
|
277
|
+
if (response.status === expectedStatus) return;
|
|
278
|
+
let bodyText;
|
|
279
|
+
try {
|
|
280
|
+
bodyText = await response.text();
|
|
281
|
+
} catch (error) {
|
|
282
|
+
bodyText = `failed to read response body: ${String(error)}`;
|
|
283
|
+
}
|
|
284
|
+
throw new Error([
|
|
285
|
+
`${label}. expected ${String(expectedStatus)} got ${String(response.status)}`,
|
|
286
|
+
`response body: ${bodyText}`,
|
|
287
|
+
`process logs:\n${this.getCombinedLog()}`
|
|
288
|
+
].join("\n"));
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Stop the managed process, escalating from SIGTERM to SIGKILL when necessary.
|
|
292
|
+
*/
|
|
293
|
+
async stop() {
|
|
294
|
+
if (this.stopped) return;
|
|
295
|
+
this.stopped = true;
|
|
296
|
+
if (this.child.exitCode !== null || this.child.killed) return;
|
|
297
|
+
this.child.kill("SIGTERM");
|
|
298
|
+
const exited = await this.waitForExit(this.stopTimeoutMs);
|
|
299
|
+
if (!exited && !this.child.killed) {
|
|
300
|
+
this.child.kill("SIGKILL");
|
|
301
|
+
await this.waitForExit(this.stopTimeoutMs);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
async waitForReady() {
|
|
305
|
+
const deadline = Date.now() + this.readyTimeoutMs;
|
|
306
|
+
while (Date.now() < deadline) {
|
|
307
|
+
if (this.child.exitCode !== null) throw new Error(`Process exited before ready check succeeded (exitCode=${String(this.child.exitCode)}).\n${this.getCombinedLog()}`);
|
|
308
|
+
try {
|
|
309
|
+
const response = await this.deps.fetchImpl(this.readyUrl);
|
|
310
|
+
if (response.ok) return;
|
|
311
|
+
} catch {}
|
|
312
|
+
await this.deps.sleep(this.readyIntervalMs);
|
|
313
|
+
}
|
|
314
|
+
await this.stop();
|
|
315
|
+
throw new Error(`Timed out waiting for readiness at ${this.readyUrl}.\n${this.getCombinedLog()}`);
|
|
316
|
+
}
|
|
317
|
+
async waitForExit(timeoutMs) {
|
|
318
|
+
if (this.child.exitCode !== null) return true;
|
|
319
|
+
return await new Promise((resolve) => {
|
|
320
|
+
const timer = setTimeout(() => {
|
|
321
|
+
this.child.off("exit", onExit);
|
|
322
|
+
resolve(false);
|
|
323
|
+
}, timeoutMs);
|
|
324
|
+
const onExit = () => {
|
|
325
|
+
clearTimeout(timer);
|
|
326
|
+
resolve(true);
|
|
327
|
+
};
|
|
328
|
+
this.child.once("exit", onExit);
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
function appendBuffer(current, chunk) {
|
|
333
|
+
const next = current + chunk;
|
|
334
|
+
if (next.length <= MAX_LOG_BUFFER_CHARS) return next;
|
|
335
|
+
return next.slice(next.length - MAX_LOG_BUFFER_CHARS);
|
|
336
|
+
}
|
|
337
|
+
function normalizePath(path) {
|
|
338
|
+
if (!path) return "/";
|
|
339
|
+
return path.startsWith("/") ? path : `/${path}`;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
//#endregion
|
|
343
|
+
//#region src/integration/smoke/index.ts
|
|
344
|
+
var smoke_exports = {};
|
|
345
|
+
__export(smoke_exports, { AppProcessHarness: () => AppProcessHarness });
|
|
346
|
+
|
|
347
|
+
//#endregion
|
|
348
|
+
//#region src/integration/anIntegrationHarness.ts
|
|
349
|
+
const defaultCapabilities = {
|
|
350
|
+
transactionalDDL: true,
|
|
351
|
+
supportsSchemas: false,
|
|
352
|
+
supportsConcurrentIndex: false,
|
|
353
|
+
supportsDeferredFkValidation: false,
|
|
354
|
+
supportsJsonb: false
|
|
355
|
+
};
|
|
356
|
+
function anIntegrationHarness(overrides = {}) {
|
|
357
|
+
return {
|
|
358
|
+
dialect: Dialect.Sqlite,
|
|
359
|
+
capabilities: defaultCapabilities,
|
|
360
|
+
resetMode: ResetMode.DropSchema,
|
|
361
|
+
dbClient: aDBClient(),
|
|
362
|
+
setup: vi.fn(async () => {}),
|
|
363
|
+
reset: vi.fn(async () => {}),
|
|
364
|
+
teardown: vi.fn(async () => {}),
|
|
365
|
+
migrationRunner: vi.fn(() => ({})),
|
|
366
|
+
...overrides
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
//#endregion
|
|
371
|
+
//#region src/integration/HarnessStrategyRegistry.ts
|
|
372
|
+
var HarnessStrategyRegistry = class HarnessStrategyRegistry {
|
|
373
|
+
static BRAND = "tango.testing.harness_strategy_registry";
|
|
374
|
+
__tangoBrand = HarnessStrategyRegistry.BRAND;
|
|
375
|
+
strategies = new Map();
|
|
376
|
+
/**
|
|
377
|
+
* Narrow an unknown value to `HarnessStrategyRegistry`.
|
|
378
|
+
*/
|
|
379
|
+
static isHarnessStrategyRegistry(value) {
|
|
380
|
+
return typeof value === "object" && value !== null && value.__tangoBrand === HarnessStrategyRegistry.BRAND;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Register or replace a dialect strategy.
|
|
384
|
+
*/
|
|
385
|
+
register(strategy) {
|
|
386
|
+
this.strategies.set(String(strategy.dialect), strategy);
|
|
387
|
+
return this;
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Resolve a strategy for a dialect, or throw if none is registered.
|
|
391
|
+
*/
|
|
392
|
+
get(dialect) {
|
|
393
|
+
const strategy = this.strategies.get(String(dialect));
|
|
394
|
+
if (!strategy) throw new Error(`No harness strategy registered for dialect: ${String(dialect)}`);
|
|
395
|
+
return strategy;
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* List all registered strategies.
|
|
399
|
+
*/
|
|
400
|
+
list() {
|
|
401
|
+
return [...this.strategies.values()];
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
//#endregion
|
|
406
|
+
//#region src/integration/config.ts
|
|
407
|
+
function readNumber(value) {
|
|
408
|
+
if (!value) return undefined;
|
|
409
|
+
const parsed = Number(value);
|
|
410
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
411
|
+
}
|
|
412
|
+
function resolveAdapterConfig(dialect, opts) {
|
|
413
|
+
const fromOptions = opts.config ?? {};
|
|
414
|
+
if (opts.tangoConfigLoader) {
|
|
415
|
+
const loaded = loadConfig(opts.tangoConfigLoader);
|
|
416
|
+
const current = loaded.current.db;
|
|
417
|
+
const merged = {
|
|
418
|
+
url: fromOptions.url ?? current.url,
|
|
419
|
+
host: fromOptions.host ?? current.host,
|
|
420
|
+
port: fromOptions.port ?? current.port,
|
|
421
|
+
database: fromOptions.database ?? current.database,
|
|
422
|
+
user: fromOptions.user ?? current.user,
|
|
423
|
+
password: fromOptions.password ?? current.password,
|
|
424
|
+
filename: fromOptions.filename ?? current.filename,
|
|
425
|
+
maxConnections: fromOptions.maxConnections ?? current.maxConnections
|
|
426
|
+
};
|
|
427
|
+
if (dialect === Dialect.Sqlite) merged.filename = opts.sqliteFile ?? merged.filename ?? ":memory:";
|
|
428
|
+
return merged;
|
|
429
|
+
}
|
|
430
|
+
if (dialect === Dialect.Postgres) return {
|
|
431
|
+
url: fromOptions.url ?? process.env.TANGO_DATABASE_URL ?? process.env.DATABASE_URL,
|
|
432
|
+
host: fromOptions.host ?? process.env.TANGO_DB_HOST,
|
|
433
|
+
port: fromOptions.port ?? readNumber(process.env.TANGO_DB_PORT),
|
|
434
|
+
database: fromOptions.database ?? process.env.TANGO_DB_NAME,
|
|
435
|
+
user: fromOptions.user ?? process.env.TANGO_DB_USER,
|
|
436
|
+
password: fromOptions.password ?? process.env.TANGO_DB_PASSWORD,
|
|
437
|
+
maxConnections: fromOptions.maxConnections ?? 10
|
|
438
|
+
};
|
|
439
|
+
return {
|
|
440
|
+
filename: opts.sqliteFile ?? fromOptions.filename ?? process.env.TANGO_SQLITE_FILENAME ?? ":memory:",
|
|
441
|
+
maxConnections: fromOptions.maxConnections ?? 1
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
//#endregion
|
|
446
|
+
//#region src/integration/strategies/PostgresHarnessStrategy.ts
|
|
447
|
+
var PostgresHarnessStrategy = class PostgresHarnessStrategy {
|
|
448
|
+
static BRAND = "tango.testing.postgres_harness_strategy";
|
|
449
|
+
__tangoBrand = PostgresHarnessStrategy.BRAND;
|
|
450
|
+
dialect = Dialect.Postgres;
|
|
451
|
+
capabilities = {
|
|
452
|
+
transactionalDDL: true,
|
|
453
|
+
supportsSchemas: true,
|
|
454
|
+
supportsConcurrentIndex: true,
|
|
455
|
+
supportsDeferredFkValidation: true,
|
|
456
|
+
supportsJsonb: true
|
|
457
|
+
};
|
|
458
|
+
/**
|
|
459
|
+
* Narrow an unknown value to the PostgreSQL integration harness strategy.
|
|
460
|
+
*/
|
|
461
|
+
static isPostgresHarnessStrategy(value) {
|
|
462
|
+
return typeof value === "object" && value !== null && value.__tangoBrand === PostgresHarnessStrategy.BRAND;
|
|
463
|
+
}
|
|
464
|
+
static buildSchemaName(explicitSchema) {
|
|
465
|
+
if (explicitSchema) return explicitSchema;
|
|
466
|
+
const random = Math.random().toString(36).slice(2, 8);
|
|
467
|
+
return `tango_test_${Date.now()}_${random}`;
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Create a configured Postgres integration harness instance.
|
|
471
|
+
*/
|
|
472
|
+
async create(options = {}) {
|
|
473
|
+
const config = resolveAdapterConfig(Dialect.Postgres, {
|
|
474
|
+
config: options.config,
|
|
475
|
+
tangoConfigLoader: options.tangoConfigLoader
|
|
476
|
+
});
|
|
477
|
+
const adapter = new PostgresAdapter();
|
|
478
|
+
const schemaName = PostgresHarnessStrategy.buildSchemaName(options.schema);
|
|
479
|
+
const resetMode = options.resetMode ?? ResetMode.DropSchema;
|
|
480
|
+
let client = null;
|
|
481
|
+
const ensureSearchPath = async () => {
|
|
482
|
+
const dbClient = client;
|
|
483
|
+
const schema = quoteSqlIdentifier$2(validateSqlIdentifier$2(schemaName, "schema"), "postgres");
|
|
484
|
+
await dbClient.query(`CREATE SCHEMA IF NOT EXISTS ${schema}`);
|
|
485
|
+
await dbClient.query(`SET search_path TO ${schema}`);
|
|
486
|
+
};
|
|
487
|
+
const recreateSchema = async () => {
|
|
488
|
+
const dbClient = client;
|
|
489
|
+
const schema = quoteSqlIdentifier$2(validateSqlIdentifier$2(schemaName, "schema"), "postgres");
|
|
490
|
+
await dbClient.query(`DROP SCHEMA IF EXISTS ${schema} CASCADE`);
|
|
491
|
+
await dbClient.query(`CREATE SCHEMA ${schema}`);
|
|
492
|
+
await dbClient.query(`SET search_path TO ${schema}`);
|
|
493
|
+
};
|
|
494
|
+
const harness = {
|
|
495
|
+
dialect: Dialect.Postgres,
|
|
496
|
+
capabilities: this.capabilities,
|
|
497
|
+
resetMode,
|
|
498
|
+
get dbClient() {
|
|
499
|
+
if (!client) throw new Error("Postgres harness not initialized. Call setup() first.");
|
|
500
|
+
return client;
|
|
501
|
+
},
|
|
502
|
+
async setup() {
|
|
503
|
+
client = await adapter.connect(config);
|
|
504
|
+
await ensureSearchPath();
|
|
505
|
+
},
|
|
506
|
+
async reset() {
|
|
507
|
+
if (!client) throw new Error("Postgres harness not initialized. Call setup() first.");
|
|
508
|
+
if (resetMode === ResetMode.DropSchema || resetMode === ResetMode.Transaction) {
|
|
509
|
+
await recreateSchema();
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
const { rows } = await client.query(`SELECT table_name FROM information_schema.tables WHERE table_schema = $1 AND table_type = 'BASE TABLE'`, [schemaName]);
|
|
513
|
+
for (const row of rows) {
|
|
514
|
+
const schema = quoteSqlIdentifier$2(validateSqlIdentifier$2(schemaName, "schema"), "postgres");
|
|
515
|
+
const table = quoteSqlIdentifier$2(validateSqlIdentifier$2(String(row.table_name), "table"), "postgres");
|
|
516
|
+
await client.query(`TRUNCATE TABLE ${schema}.${table} RESTART IDENTITY CASCADE`);
|
|
517
|
+
}
|
|
518
|
+
await client.query(`SET search_path TO ${quoteSqlIdentifier$2(validateSqlIdentifier$2(schemaName, "schema"), "postgres")}`);
|
|
519
|
+
},
|
|
520
|
+
async teardown() {
|
|
521
|
+
if (!client) return;
|
|
522
|
+
try {
|
|
523
|
+
await client.query(`DROP SCHEMA IF EXISTS ${quoteSqlIdentifier$2(validateSqlIdentifier$2(schemaName, "schema"), "postgres")} CASCADE`);
|
|
524
|
+
} finally {
|
|
525
|
+
await client.close();
|
|
526
|
+
client = null;
|
|
527
|
+
}
|
|
528
|
+
},
|
|
529
|
+
migrationRunner(migrationsDir) {
|
|
530
|
+
if (!client) throw new Error("Postgres harness not initialized. Call setup() first.");
|
|
531
|
+
return new MigrationRunner$1(client, "postgres", migrationsDir);
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
return harness;
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
//#endregion
|
|
539
|
+
//#region src/integration/strategies/SqliteHarnessStrategy.ts
|
|
540
|
+
var SqliteHarnessStrategy = class SqliteHarnessStrategy {
|
|
541
|
+
static BRAND = "tango.testing.sqlite_harness_strategy";
|
|
542
|
+
__tangoBrand = SqliteHarnessStrategy.BRAND;
|
|
543
|
+
dialect = Dialect.Sqlite;
|
|
544
|
+
capabilities = {
|
|
545
|
+
transactionalDDL: true,
|
|
546
|
+
supportsSchemas: false,
|
|
547
|
+
supportsConcurrentIndex: false,
|
|
548
|
+
supportsDeferredFkValidation: false,
|
|
549
|
+
supportsJsonb: false
|
|
550
|
+
};
|
|
551
|
+
/**
|
|
552
|
+
* Narrow an unknown value to the SQLite integration harness strategy.
|
|
553
|
+
*/
|
|
554
|
+
static isSqliteHarnessStrategy(value) {
|
|
555
|
+
return typeof value === "object" && value !== null && value.__tangoBrand === SqliteHarnessStrategy.BRAND;
|
|
556
|
+
}
|
|
557
|
+
static async dropAllTables(client) {
|
|
558
|
+
const { rows } = await client.query("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'");
|
|
559
|
+
for (const row of rows) {
|
|
560
|
+
const table = quoteSqlIdentifier$1(validateSqlIdentifier$1(String(row.name), "table"), "sqlite");
|
|
561
|
+
await client.query(`DROP TABLE IF EXISTS ${table}`);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Create a configured SQLite integration harness instance.
|
|
566
|
+
*/
|
|
567
|
+
async create(options = {}) {
|
|
568
|
+
const config = resolveAdapterConfig(Dialect.Sqlite, {
|
|
569
|
+
config: options.config,
|
|
570
|
+
tangoConfigLoader: options.tangoConfigLoader,
|
|
571
|
+
sqliteFile: options.sqliteFile
|
|
572
|
+
});
|
|
573
|
+
const adapter = new SqliteAdapter();
|
|
574
|
+
const resetMode = options.resetMode ?? ResetMode.DropSchema;
|
|
575
|
+
let client = null;
|
|
576
|
+
const reconnect = async () => {
|
|
577
|
+
client = await adapter.connect(config);
|
|
578
|
+
return client;
|
|
579
|
+
};
|
|
580
|
+
const harness = {
|
|
581
|
+
dialect: Dialect.Sqlite,
|
|
582
|
+
capabilities: this.capabilities,
|
|
583
|
+
resetMode,
|
|
584
|
+
get dbClient() {
|
|
585
|
+
if (!client) throw new Error("Sqlite harness not initialized. Call setup() first.");
|
|
586
|
+
return client;
|
|
587
|
+
},
|
|
588
|
+
async setup() {
|
|
589
|
+
await reconnect();
|
|
590
|
+
},
|
|
591
|
+
async reset() {
|
|
592
|
+
if (!client) throw new Error("Sqlite harness not initialized. Call setup() first.");
|
|
593
|
+
if (resetMode === ResetMode.DropSchema && config.filename && config.filename !== ":memory:") {
|
|
594
|
+
await client.close();
|
|
595
|
+
await rm(config.filename, { force: true });
|
|
596
|
+
await reconnect();
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
await SqliteHarnessStrategy.dropAllTables(client);
|
|
600
|
+
},
|
|
601
|
+
async teardown() {
|
|
602
|
+
if (client) {
|
|
603
|
+
await client.close();
|
|
604
|
+
client = null;
|
|
605
|
+
}
|
|
606
|
+
if (config.filename && config.filename !== ":memory:") await rm(config.filename, { force: true });
|
|
607
|
+
},
|
|
608
|
+
migrationRunner(migrationsDir) {
|
|
609
|
+
if (!client) throw new Error("Sqlite harness not initialized. Call setup() first.");
|
|
610
|
+
return new MigrationRunner(client, "sqlite", migrationsDir);
|
|
611
|
+
}
|
|
612
|
+
};
|
|
613
|
+
return harness;
|
|
614
|
+
}
|
|
615
|
+
};
|
|
616
|
+
|
|
617
|
+
//#endregion
|
|
618
|
+
//#region src/integration/TestHarness.ts
|
|
619
|
+
var TestHarness = class TestHarness {
|
|
620
|
+
static BRAND = "tango.testing.test_harness";
|
|
621
|
+
static defaultRegistry = null;
|
|
622
|
+
__tangoBrand = TestHarness.BRAND;
|
|
623
|
+
/**
|
|
624
|
+
* Narrow an unknown value to `TestHarness`.
|
|
625
|
+
*/
|
|
626
|
+
static isTestHarness(value) {
|
|
627
|
+
return typeof value === "object" && value !== null && value.__tangoBrand === TestHarness.BRAND;
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Register a harness strategy on the shared default registry.
|
|
631
|
+
*/
|
|
632
|
+
static registerStrategy(strategy) {
|
|
633
|
+
this.ensureRegistry().register(strategy);
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Return the shared harness strategy registry.
|
|
637
|
+
*/
|
|
638
|
+
static getRegistry() {
|
|
639
|
+
return this.ensureRegistry();
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* Create a dialect-specific harness from the registry.
|
|
643
|
+
*/
|
|
644
|
+
static async forDialect(args, registry) {
|
|
645
|
+
const selectedRegistry = registry ?? this.ensureRegistry();
|
|
646
|
+
const strategy = selectedRegistry.get(args.dialect);
|
|
647
|
+
return strategy.create(args.options);
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* Convenience helper for a SQLite test harness.
|
|
651
|
+
*/
|
|
652
|
+
static async sqlite(options) {
|
|
653
|
+
return this.forDialect({
|
|
654
|
+
dialect: Dialect.Sqlite,
|
|
655
|
+
options
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* Convenience helper for a Postgres test harness.
|
|
660
|
+
*/
|
|
661
|
+
static async postgres(options) {
|
|
662
|
+
return this.forDialect({
|
|
663
|
+
dialect: Dialect.Postgres,
|
|
664
|
+
options
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
static ensureRegistry() {
|
|
668
|
+
if (this.defaultRegistry) return this.defaultRegistry;
|
|
669
|
+
const registry = new HarnessStrategyRegistry();
|
|
670
|
+
registry.register(new SqliteHarnessStrategy());
|
|
671
|
+
registry.register(new PostgresHarnessStrategy());
|
|
672
|
+
this.defaultRegistry = registry;
|
|
673
|
+
return registry;
|
|
674
|
+
}
|
|
675
|
+
};
|
|
676
|
+
|
|
677
|
+
//#endregion
|
|
678
|
+
//#region src/integration/orm/seedTable.ts
|
|
679
|
+
async function seedTable(harness, table, rows) {
|
|
680
|
+
if (rows.length === 0) return;
|
|
681
|
+
const columns = Object.keys(rows[0] ?? {});
|
|
682
|
+
if (columns.length === 0) return;
|
|
683
|
+
const dialect = harness.dialect;
|
|
684
|
+
const safeTable = quoteSqlIdentifier(validateSqlIdentifier(table, "table"), dialect);
|
|
685
|
+
const safeColumns = columns.map((column) => quoteSqlIdentifier(validateSqlIdentifier(column, "column", columns), dialect));
|
|
686
|
+
for (const row of rows) {
|
|
687
|
+
const values = columns.map((column) => {
|
|
688
|
+
const value = row[column];
|
|
689
|
+
if (harness.dialect === Dialect.Sqlite && typeof value === "boolean") return value ? 1 : 0;
|
|
690
|
+
return value;
|
|
691
|
+
});
|
|
692
|
+
const placeholders = harness.dialect === Dialect.Postgres ? columns.map((_, index) => `$${index + 1}`).join(", ") : columns.map(() => "?").join(", ");
|
|
693
|
+
await harness.dbClient.query(`INSERT INTO ${safeTable} (${safeColumns.join(", ")}) VALUES (${placeholders})`, values);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
//#endregion
|
|
698
|
+
//#region src/integration/orm/createQuerySetFixture.ts
|
|
699
|
+
function createQuerySetFixture(input) {
|
|
700
|
+
const executor = {
|
|
701
|
+
meta: input.meta,
|
|
702
|
+
client: input.harness.dbClient,
|
|
703
|
+
dialect: input.harness.dialect,
|
|
704
|
+
run: async (compiled) => {
|
|
705
|
+
const result = await input.harness.dbClient.query(compiled.sql, compiled.params);
|
|
706
|
+
return result.rows;
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
return new QuerySet(executor);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
//#endregion
|
|
713
|
+
//#region src/integration/orm/expectQueryResult.ts
|
|
714
|
+
async function expectQueryResult(actual, expected) {
|
|
715
|
+
const resolved = await actual;
|
|
716
|
+
if (JSON.stringify(resolved) !== JSON.stringify(expected)) throw new Error(`Expected query result ${JSON.stringify(expected)}, got ${JSON.stringify(resolved)}`);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
//#endregion
|
|
720
|
+
//#region src/integration/index.ts
|
|
721
|
+
var integration_exports = {};
|
|
722
|
+
__export(integration_exports, {
|
|
723
|
+
AppProcessHarness: () => AppProcessHarness,
|
|
724
|
+
Dialect: () => Dialect,
|
|
725
|
+
HarnessStrategyRegistry: () => HarnessStrategyRegistry,
|
|
726
|
+
ResetMode: () => ResetMode,
|
|
727
|
+
TestHarness: () => TestHarness,
|
|
728
|
+
aTangoConfig: () => aTangoConfig,
|
|
729
|
+
anIntegrationHarness: () => anIntegrationHarness,
|
|
730
|
+
applyAndVerifyMigrations: () => applyAndVerifyMigrations,
|
|
731
|
+
assertMigrationPlan: () => assertMigrationPlan,
|
|
732
|
+
conformance: () => conformance_exports,
|
|
733
|
+
createQuerySetFixture: () => createQuerySetFixture,
|
|
734
|
+
domain: () => domain_exports,
|
|
735
|
+
expectQueryResult: () => expectQueryResult,
|
|
736
|
+
introspectSchema: () => introspectSchema,
|
|
737
|
+
migrations: () => migrations_exports,
|
|
738
|
+
runDialectConformanceSuite: () => runDialectConformanceSuite,
|
|
739
|
+
runtime: () => runtime_exports,
|
|
740
|
+
seedTable: () => seedTable,
|
|
741
|
+
setupTestTangoRuntime: () => setupTestTangoRuntime,
|
|
742
|
+
smoke: () => smoke_exports
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
//#endregion
|
|
746
|
+
export { AppProcessHarness, Dialect, HarnessStrategyRegistry, ResetMode, TestHarness, aTangoConfig, anIntegrationHarness, applyAndVerifyMigrations, assertMigrationPlan, conformance_exports, createQuerySetFixture, domain_exports, expectQueryResult, integration_exports, introspectSchema, migrations_exports, runDialectConformanceSuite, runtime_exports, seedTable, setupTestTangoRuntime, smoke_exports };
|
|
747
|
+
//# sourceMappingURL=integration-BrJw6NzG.js.map
|