@danceroutine/tango-testing 1.7.0 → 1.8.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 +1 -1
- package/dist/{aDBClient-Di5g6_hl.js → aDBClient-CH-ZcOaP.js} +14 -2
- package/dist/aDBClient-CH-ZcOaP.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +5 -5
- package/dist/integration/domain/IntegrationHarness.d.ts +2 -1
- package/dist/integration/index.js +3 -3
- package/dist/integration/orm/createModelQuerySetFixture.d.ts +10 -0
- package/dist/integration/orm/createQuerySetFixture.d.ts +3 -3
- package/dist/integration/orm/index.d.ts +1 -0
- package/dist/{integration-3tpk5c5V.js → integration-CaRF_lF_.js} +25 -9
- package/dist/integration-CaRF_lF_.js.map +1 -0
- package/dist/mocks/aManyToManyRelatedManager.d.ts +48 -0
- package/dist/mocks/aModelQuerySet.d.ts +8 -0
- package/dist/mocks/aQueryExecutor.d.ts +3 -1
- package/dist/mocks/aQuerySet.d.ts +1 -4
- package/dist/mocks/anAdapter.d.ts +13 -0
- package/dist/mocks/index.d.ts +5 -0
- package/dist/mocks/index.js +3 -3
- package/dist/{mocks-vvHjWn5S.js → mocks-iKVkzHJZ.js} +89 -35
- package/dist/mocks-iKVkzHJZ.js.map +1 -0
- package/dist/vitest/index.js +3 -3
- package/dist/vitest/registerVitestTango.d.ts +7 -0
- package/dist/{vitest-DmamRJ18.js → vitest-BqZmULOa.js} +16 -3
- package/dist/vitest-BqZmULOa.js.map +1 -0
- package/package.json +6 -6
- package/dist/aDBClient-Di5g6_hl.js.map +0 -1
- package/dist/integration-3tpk5c5V.js.map +0 -1
- package/dist/mocks-vvHjWn5S.js.map +0 -1
- package/dist/vitest-DmamRJ18.js.map +0 -1
package/README.md
CHANGED
|
@@ -58,7 +58,7 @@ For integration tests, the package exposes the `TestHarness` and dialect strateg
|
|
|
58
58
|
|
|
59
59
|
## Public API
|
|
60
60
|
|
|
61
|
-
The root export covers three main jobs. Mock helpers such as `aDBClient`, `aManager`, `aQueryExecutor`, and `
|
|
61
|
+
The root export covers three main jobs. Mock helpers such as `aDBClient`, `aManager`, `aQueryExecutor`, and `aModelQuerySet` keep unit tests lightweight. Runtime and integration helpers such as `aTangoConfig`, `setupTestTangoRuntime`, `TestHarness`, and `createModelQuerySetFixture` support database-backed and framework-backed workflows. `ModelDataFactory` and the Vitest integration surface round out the package for data fixtures and test-runner setup.
|
|
62
62
|
|
|
63
63
|
The package also exposes subpaths such as `mocks`, `factories`, `assertions`, `integration`, and `vitest` when you want a more explicit import boundary.
|
|
64
64
|
|
|
@@ -1,5 +1,17 @@
|
|
|
1
|
+
import { PostgresAdapter, SqliteAdapter } from "@danceroutine/tango-orm";
|
|
1
2
|
import { vi } from "vitest";
|
|
2
3
|
|
|
4
|
+
//#region src/mocks/anAdapter.ts
|
|
5
|
+
function anAdapter(overrides = {}) {
|
|
6
|
+
const dialect = overrides.dialect ?? "postgres";
|
|
7
|
+
switch (dialect) {
|
|
8
|
+
case "postgres": return new PostgresAdapter();
|
|
9
|
+
case "sqlite": return new SqliteAdapter();
|
|
10
|
+
default: throw new Error(`Unsupported dialect: ${dialect}`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
//#endregion
|
|
3
15
|
//#region src/mocks/aDBClient.ts
|
|
4
16
|
function aDBClient(overrides = {}) {
|
|
5
17
|
const queryImpl = overrides.query ?? (async (_sql, _params) => ({ rows: [] }));
|
|
@@ -24,5 +36,5 @@ function aDBClient(overrides = {}) {
|
|
|
24
36
|
}
|
|
25
37
|
|
|
26
38
|
//#endregion
|
|
27
|
-
export { aDBClient };
|
|
28
|
-
//# sourceMappingURL=aDBClient-
|
|
39
|
+
export { aDBClient, anAdapter };
|
|
40
|
+
//# sourceMappingURL=aDBClient-CH-ZcOaP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aDBClient-CH-ZcOaP.js","names":["overrides: AdapterOverrides","dialect: Dialect","overrides: DBClientOverrides","_sql: string","_params?: readonly unknown[]","_name: string","client: DBClient","sql: string","params?: readonly unknown[]","name: string"],"sources":["../src/mocks/anAdapter.ts","../src/mocks/aDBClient.ts"],"sourcesContent":["import { PostgresAdapter, SqliteAdapter, type Adapter } from '@danceroutine/tango-orm';\nimport type { Dialect } from '@danceroutine/tango-orm/query';\n\nexport type AdapterOverrides = {\n dialect?: Dialect;\n};\n\n/**\n * Create a real built-in `Adapter` instance for unit tests that need one.\n *\n * Defaults to a Postgres adapter; pass `{ dialect: 'sqlite' }` to obtain a\n * SQLite adapter. The returned adapter carries the matching placeholder\n * formatter so compilers exercised under test emit dialect-accurate SQL.\n */\nexport function anAdapter(overrides: AdapterOverrides = {}): Adapter {\n const dialect: Dialect = overrides.dialect ?? 'postgres';\n switch (dialect) {\n case 'postgres':\n return new PostgresAdapter();\n case 'sqlite':\n return new SqliteAdapter();\n default:\n throw new Error(`Unsupported dialect: ${dialect}`);\n }\n}\n","import { vi } from 'vitest';\nimport type { DBClient } from './DBClient';\n\n/**\n * Looser override type for `aDBClient` that accepts concrete-typed query mocks.\n * Consumers should not need to cast their mocks to satisfy `DBClient['query']`'s generic.\n */\ntype DBClientOverrides = {\n // oxlint-disable-next-line typescript/no-explicit-any\n query?: (sql: string, params?: readonly unknown[]) => Promise<{ rows: any[] }>;\n begin?: () => Promise<void>;\n commit?: () => Promise<void>;\n rollback?: () => Promise<void>;\n close?: () => Promise<void>;\n createSavepoint?: (name: string) => Promise<void>;\n releaseSavepoint?: (name: string) => Promise<void>;\n rollbackToSavepoint?: (name: string) => Promise<void>;\n};\n\n/**\n * Create a lightweight `DBClient` test double with optional behavior overrides.\n * The `query` override accepts any function returning `Promise<{ rows: any[] }>`,\n * so concrete-typed Vitest mocks do not require a cast at the call site.\n */\nexport function aDBClient(overrides: DBClientOverrides = {}): DBClient {\n const queryImpl =\n // oxlint-disable-next-line typescript/no-explicit-any\n overrides.query ?? (async (_sql: string, _params?: readonly unknown[]) => ({ rows: [] as any[] }));\n const beginImpl = overrides.begin ?? (async () => {});\n const commitImpl = overrides.commit ?? (async () => {});\n const rollbackImpl = overrides.rollback ?? (async () => {});\n const closeImpl = overrides.close ?? (async () => {});\n const createSavepointImpl = overrides.createSavepoint ?? (async (_name: string) => {});\n const releaseSavepointImpl = overrides.releaseSavepoint ?? (async (_name: string) => {});\n const rollbackToSavepointImpl = overrides.rollbackToSavepoint ?? (async (_name: string) => {});\n\n const client: DBClient = {\n query: vi.fn((sql: string, params?: readonly unknown[]) => queryImpl(sql, params)) as DBClient['query'],\n begin: vi.fn(() => beginImpl()),\n commit: vi.fn(() => commitImpl()),\n rollback: vi.fn(() => rollbackImpl()),\n close: vi.fn(() => closeImpl()),\n createSavepoint: vi.fn((name: string) => createSavepointImpl(name)),\n releaseSavepoint: vi.fn((name: string) => releaseSavepointImpl(name)),\n rollbackToSavepoint: vi.fn((name: string) => rollbackToSavepointImpl(name)),\n };\n\n return client;\n}\n"],"mappings":";;;;AAcO,SAAS,UAAUA,YAA8B,CAAE,GAAW;CACjE,MAAMC,UAAmB,UAAU,WAAW;AAC9C,SAAQ,SAAR;AACI,OAAK,WACD,QAAO,IAAI;AACf,OAAK,SACD,QAAO,IAAI;AACf,UACI,OAAM,IAAI,OAAO,uBAAuB,QAAQ;CACvD;AACJ;;;;ACAM,SAAS,UAAUC,YAA+B,CAAE,GAAY;CACnE,MAAM,YAEF,UAAU,UAAU,OAAOC,MAAcC,aAAkC,EAAE,MAAM,CAAE,EAAW;CACpG,MAAM,YAAY,UAAU,UAAU,YAAY,CAAE;CACpD,MAAM,aAAa,UAAU,WAAW,YAAY,CAAE;CACtD,MAAM,eAAe,UAAU,aAAa,YAAY,CAAE;CAC1D,MAAM,YAAY,UAAU,UAAU,YAAY,CAAE;CACpD,MAAM,sBAAsB,UAAU,oBAAoB,OAAOC,UAAkB,CAAE;CACrF,MAAM,uBAAuB,UAAU,qBAAqB,OAAOA,UAAkB,CAAE;CACvF,MAAM,0BAA0B,UAAU,wBAAwB,OAAOA,UAAkB,CAAE;CAE7F,MAAMC,SAAmB;EACrB,OAAO,GAAG,GAAG,CAACC,KAAaC,WAAgC,UAAU,KAAK,OAAO,CAAC;EAClF,OAAO,GAAG,GAAG,MAAM,WAAW,CAAC;EAC/B,QAAQ,GAAG,GAAG,MAAM,YAAY,CAAC;EACjC,UAAU,GAAG,GAAG,MAAM,cAAc,CAAC;EACrC,OAAO,GAAG,GAAG,MAAM,WAAW,CAAC;EAC/B,iBAAiB,GAAG,GAAG,CAACC,SAAiB,oBAAoB,KAAK,CAAC;EACnE,kBAAkB,GAAG,GAAG,CAACA,SAAiB,qBAAqB,KAAK,CAAC;EACrE,qBAAqB,GAAG,GAAG,CAACA,SAAiB,wBAAwB,KAAK,CAAC;CAC9E;AAED,QAAO;AACV"}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,9 +8,9 @@ export * as assertionsDomain from './assertions/index';
|
|
|
8
8
|
export * as integration from './integration/index';
|
|
9
9
|
export * as vitest from './vitest/index';
|
|
10
10
|
export * as express from './express/index';
|
|
11
|
-
export { aDBClient, aManager, aQueryResult, aQuerySet, aRequestContext, aQueryExecutor, aRelationMeta, } from './mocks/index';
|
|
11
|
+
export { anAdapter, aDBClient, aManager, aManyToManyRelatedManager, aModelQuerySet, aQueryResult, aQuerySet, aRequestContext, aQueryExecutor, aRelationMeta, } from './mocks/index';
|
|
12
12
|
export { anExpressRequest, anExpressResponse } from './express/index';
|
|
13
|
-
export type { DBClient, ManagerOverrides, MockQuerySetResult, QueryExecutorOverrides, RequestContextFixtureOptions, } from './mocks/index';
|
|
13
|
+
export type { AdapterOverrides, DBClient, ManagerOverrides, ManyToManyRelatedManagerFixture, ManyToManyRelatedManagerFixtureOverrides, MockQuerySetResult, QueryExecutorOverrides, RequestContextFixtureOptions, } from './mocks/index';
|
|
14
14
|
export type { ExpressRequestOverrides } from './express/index';
|
|
15
15
|
export { ModelDataFactory } from './factories/index';
|
|
16
16
|
export type { GenericModelFactory } from './factories/index';
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { aDBClient } from "./aDBClient-
|
|
2
|
-
import { aManager, aQueryExecutor, aQueryResult, aQuerySet, aRelationMeta, aRequestContext, mocks_exports } from "./mocks-
|
|
1
|
+
import { aDBClient, anAdapter } from "./aDBClient-CH-ZcOaP.js";
|
|
2
|
+
import { aManager, aManyToManyRelatedManager, aModelQuerySet, aQueryExecutor, aQueryResult, aQuerySet, aRelationMeta, aRequestContext, mocks_exports } from "./mocks-iKVkzHJZ.js";
|
|
3
3
|
import { ModelDataFactory, factories_exports } from "./factories-Cl_CAzbj.js";
|
|
4
4
|
import { assertions, assertions_exports } from "./assertions-CCFZ53Y-.js";
|
|
5
|
-
import { AppProcessHarness, Dialect, HarnessStrategyRegistry, ResetMode, TestHarness, aTangoConfig, aTangoRuntime, anIntegrationHarness, applyAndVerifyMigrations, assertMigrationPlan, conformance_exports, createQuerySetFixture, domain_exports, expectQueryResult, integration_exports, introspectSchema, migrations_exports, runDialectConformanceSuite, runtime_exports, seedTable, setupTestTangoRuntime, smoke_exports } from "./integration-
|
|
6
|
-
import { vitest_exports, withGlobalTestApi } from "./vitest-
|
|
5
|
+
import { AppProcessHarness, Dialect, HarnessStrategyRegistry, ResetMode, TestHarness, aTangoConfig, aTangoRuntime, anIntegrationHarness, applyAndVerifyMigrations, assertMigrationPlan, conformance_exports, createModelQuerySetFixture, createQuerySetFixture, domain_exports, expectQueryResult, integration_exports, introspectSchema, migrations_exports, runDialectConformanceSuite, runtime_exports, seedTable, setupTestTangoRuntime, smoke_exports } from "./integration-CaRF_lF_.js";
|
|
6
|
+
import { vitest_exports, withGlobalTestApi } from "./vitest-BqZmULOa.js";
|
|
7
7
|
import { anExpressRequest, anExpressResponse, express_exports } from "./express-Czpfz_Ay.js";
|
|
8
8
|
|
|
9
|
-
export { AppProcessHarness, Dialect, HarnessStrategyRegistry, ModelDataFactory, ResetMode, TestHarness, aDBClient, aManager, aQueryExecutor, aQueryResult, aQuerySet, aRelationMeta, aRequestContext, aTangoConfig, aTangoRuntime, anExpressRequest, anExpressResponse, anIntegrationHarness, applyAndVerifyMigrations, assertMigrationPlan, assertions, assertions_exports as assertionsDomain, conformance_exports as conformance, createQuerySetFixture, domain_exports as domain, expectQueryResult, express_exports as express, factories_exports as factories, integration_exports as integration, introspectSchema, migrations_exports as migrations, mocks_exports as mocks, runDialectConformanceSuite, runtime_exports as runtime, seedTable, setupTestTangoRuntime, smoke_exports as smoke, vitest_exports as vitest, withGlobalTestApi };
|
|
9
|
+
export { AppProcessHarness, Dialect, HarnessStrategyRegistry, ModelDataFactory, ResetMode, TestHarness, aDBClient, aManager, aManyToManyRelatedManager, aModelQuerySet, aQueryExecutor, aQueryResult, aQuerySet, aRelationMeta, aRequestContext, aTangoConfig, aTangoRuntime, anAdapter, anExpressRequest, anExpressResponse, anIntegrationHarness, applyAndVerifyMigrations, assertMigrationPlan, assertions, assertions_exports as assertionsDomain, conformance_exports as conformance, createModelQuerySetFixture, createQuerySetFixture, domain_exports as domain, expectQueryResult, express_exports as express, factories_exports as factories, integration_exports as integration, introspectSchema, migrations_exports as migrations, mocks_exports as mocks, runDialectConformanceSuite, runtime_exports as runtime, seedTable, setupTestTangoRuntime, smoke_exports as smoke, vitest_exports as vitest, withGlobalTestApi };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DBClient } from '@danceroutine/tango-orm';
|
|
1
|
+
import type { Adapter, DBClient } from '@danceroutine/tango-orm';
|
|
2
2
|
import type { MigrationRunner } from '@danceroutine/tango-migrations';
|
|
3
3
|
import type { Dialect } from './Dialect';
|
|
4
4
|
import type { ResetMode } from './ResetMode';
|
|
@@ -11,6 +11,7 @@ export interface DialectTestCapabilities {
|
|
|
11
11
|
}
|
|
12
12
|
export interface IntegrationHarness {
|
|
13
13
|
readonly dialect: Dialect | string;
|
|
14
|
+
readonly adapter: Adapter;
|
|
14
15
|
readonly capabilities: DialectTestCapabilities;
|
|
15
16
|
readonly resetMode: ResetMode;
|
|
16
17
|
readonly dbClient: DBClient;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import "../aDBClient-
|
|
2
|
-
import { AppProcessHarness, Dialect, HarnessStrategyRegistry, ResetMode, TestHarness, aTangoConfig, aTangoRuntime, anIntegrationHarness, applyAndVerifyMigrations, assertMigrationPlan, conformance_exports, createQuerySetFixture, domain_exports, expectQueryResult, introspectSchema, migrations_exports, runDialectConformanceSuite, runtime_exports, seedTable, setupTestTangoRuntime, smoke_exports } from "../integration-
|
|
1
|
+
import "../aDBClient-CH-ZcOaP.js";
|
|
2
|
+
import { AppProcessHarness, Dialect, HarnessStrategyRegistry, ResetMode, TestHarness, aTangoConfig, aTangoRuntime, anIntegrationHarness, applyAndVerifyMigrations, assertMigrationPlan, conformance_exports, createModelQuerySetFixture, createQuerySetFixture, domain_exports, expectQueryResult, introspectSchema, migrations_exports, runDialectConformanceSuite, runtime_exports, seedTable, setupTestTangoRuntime, smoke_exports } from "../integration-CaRF_lF_.js";
|
|
3
3
|
|
|
4
|
-
export { AppProcessHarness, Dialect, HarnessStrategyRegistry, ResetMode, TestHarness, aTangoConfig, aTangoRuntime, anIntegrationHarness, applyAndVerifyMigrations, assertMigrationPlan, conformance_exports as conformance, createQuerySetFixture, domain_exports as domain, expectQueryResult, introspectSchema, migrations_exports as migrations, runDialectConformanceSuite, runtime_exports as runtime, seedTable, setupTestTangoRuntime, smoke_exports as smoke };
|
|
4
|
+
export { AppProcessHarness, Dialect, HarnessStrategyRegistry, ResetMode, TestHarness, aTangoConfig, aTangoRuntime, anIntegrationHarness, applyAndVerifyMigrations, assertMigrationPlan, conformance_exports as conformance, createModelQuerySetFixture, createQuerySetFixture, domain_exports as domain, expectQueryResult, introspectSchema, migrations_exports as migrations, runDialectConformanceSuite, runtime_exports as runtime, seedTable, setupTestTangoRuntime, smoke_exports as smoke };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { QuerySet } from '@danceroutine/tango-orm';
|
|
2
|
+
import type { TableMeta } from '@danceroutine/tango-orm/query';
|
|
3
|
+
import type { IntegrationHarness } from '../domain/index';
|
|
4
|
+
/**
|
|
5
|
+
* Create a `ModelQuerySet` fixture backed by a real integration harness and supplied table metadata.
|
|
6
|
+
*/
|
|
7
|
+
export declare function createModelQuerySetFixture<TModel extends Record<string, unknown>>(input: {
|
|
8
|
+
harness: IntegrationHarness;
|
|
9
|
+
meta: TableMeta;
|
|
10
|
+
}): QuerySet<TModel>;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { QuerySet } from '@danceroutine/tango-orm';
|
|
1
|
+
import type { QuerySet } from '@danceroutine/tango-orm';
|
|
2
2
|
import type { TableMeta } from '@danceroutine/tango-orm/query';
|
|
3
|
-
import {
|
|
3
|
+
import type { IntegrationHarness } from '../domain/index';
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* @deprecated Use `createModelQuerySetFixture(...)` instead.
|
|
6
6
|
*/
|
|
7
7
|
export declare function createQuerySetFixture<TModel extends Record<string, unknown>>(input: {
|
|
8
8
|
harness: IntegrationHarness;
|
|
@@ -2,5 +2,6 @@
|
|
|
2
2
|
* Domain boundary barrel: centralizes this subdomain's public contract.
|
|
3
3
|
*/
|
|
4
4
|
export { seedTable } from './seedTable';
|
|
5
|
+
export { createModelQuerySetFixture } from './createModelQuerySetFixture';
|
|
5
6
|
export { createQuerySetFixture } from './createQuerySetFixture';
|
|
6
7
|
export { expectQueryResult } from './expectQueryResult';
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { __export } from "./chunk-BkvOhyD0.js";
|
|
2
|
-
import { aDBClient } from "./aDBClient-
|
|
2
|
+
import { aDBClient, anAdapter } from "./aDBClient-CH-ZcOaP.js";
|
|
3
|
+
import { ModelQuerySet, TangoRuntime, initializeTangoRuntime, resetTangoRuntime } from "@danceroutine/tango-orm";
|
|
3
4
|
import { vi } from "vitest";
|
|
4
|
-
import {
|
|
5
|
+
import { getLogger, quoteSqlIdentifier, quoteSqlIdentifier as quoteSqlIdentifier$1, quoteSqlIdentifier as quoteSqlIdentifier$2, validateSqlIdentifier, validateSqlIdentifier as validateSqlIdentifier$1, validateSqlIdentifier as validateSqlIdentifier$2 } from "@danceroutine/tango-core";
|
|
5
6
|
import { MigrationRunner, MigrationRunner as MigrationRunner$1, createDefaultIntrospectorStrategy } from "@danceroutine/tango-migrations";
|
|
6
7
|
import { defineConfig, loadConfig, loadConfig as loadConfig$1 } from "@danceroutine/tango-config";
|
|
7
8
|
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
9
|
import { PostgresAdapter, SqliteAdapter } from "@danceroutine/tango-orm/connection";
|
|
10
10
|
import { rm } from "node:fs/promises";
|
|
11
11
|
|
|
@@ -364,6 +364,7 @@ const defaultCapabilities = {
|
|
|
364
364
|
function anIntegrationHarness(overrides = {}) {
|
|
365
365
|
return {
|
|
366
366
|
dialect: Dialect.Sqlite,
|
|
367
|
+
adapter: anAdapter({ dialect: "sqlite" }),
|
|
367
368
|
capabilities: defaultCapabilities,
|
|
368
369
|
resetMode: ResetMode.DropSchema,
|
|
369
370
|
dbClient: aDBClient(),
|
|
@@ -501,6 +502,7 @@ var PostgresHarnessStrategy = class PostgresHarnessStrategy {
|
|
|
501
502
|
};
|
|
502
503
|
const harness = {
|
|
503
504
|
dialect: Dialect.Postgres,
|
|
505
|
+
adapter,
|
|
504
506
|
capabilities: this.capabilities,
|
|
505
507
|
resetMode,
|
|
506
508
|
get dbClient() {
|
|
@@ -587,6 +589,7 @@ var SqliteHarnessStrategy = class SqliteHarnessStrategy {
|
|
|
587
589
|
};
|
|
588
590
|
const harness = {
|
|
589
591
|
dialect: Dialect.Sqlite,
|
|
592
|
+
adapter,
|
|
590
593
|
capabilities: this.capabilities,
|
|
591
594
|
resetMode,
|
|
592
595
|
get dbClient() {
|
|
@@ -703,18 +706,30 @@ async function seedTable(harness, table, rows) {
|
|
|
703
706
|
}
|
|
704
707
|
|
|
705
708
|
//#endregion
|
|
706
|
-
//#region src/integration/orm/
|
|
707
|
-
function
|
|
709
|
+
//#region src/integration/orm/createModelQuerySetFixture.ts
|
|
710
|
+
function createModelQuerySetFixture(input) {
|
|
708
711
|
const executor = {
|
|
709
712
|
meta: input.meta,
|
|
710
713
|
client: input.harness.dbClient,
|
|
711
|
-
|
|
714
|
+
adapter: input.harness.adapter,
|
|
712
715
|
run: async (compiled) => {
|
|
713
716
|
const result = await input.harness.dbClient.query(compiled.sql, compiled.params);
|
|
714
717
|
return result.rows;
|
|
715
718
|
}
|
|
716
719
|
};
|
|
717
|
-
return new
|
|
720
|
+
return new ModelQuerySet(executor);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
//#endregion
|
|
724
|
+
//#region src/integration/orm/createQuerySetFixture.ts
|
|
725
|
+
const logger = getLogger("tango.testing.integration");
|
|
726
|
+
let hasWarned = false;
|
|
727
|
+
function createQuerySetFixture(input) {
|
|
728
|
+
if (!hasWarned) {
|
|
729
|
+
hasWarned = true;
|
|
730
|
+
logger.warn("`createQuerySetFixture(...)` is deprecated. Use `createModelQuerySetFixture(...)` instead.");
|
|
731
|
+
}
|
|
732
|
+
return createModelQuerySetFixture(input);
|
|
718
733
|
}
|
|
719
734
|
|
|
720
735
|
//#endregion
|
|
@@ -741,6 +756,7 @@ __export(integration_exports, {
|
|
|
741
756
|
applyAndVerifyMigrations: () => applyAndVerifyMigrations,
|
|
742
757
|
assertMigrationPlan: () => assertMigrationPlan,
|
|
743
758
|
conformance: () => conformance_exports,
|
|
759
|
+
createModelQuerySetFixture: () => createModelQuerySetFixture,
|
|
744
760
|
createQuerySetFixture: () => createQuerySetFixture,
|
|
745
761
|
domain: () => domain_exports,
|
|
746
762
|
expectQueryResult: () => expectQueryResult,
|
|
@@ -754,5 +770,5 @@ __export(integration_exports, {
|
|
|
754
770
|
});
|
|
755
771
|
|
|
756
772
|
//#endregion
|
|
757
|
-
export { AppProcessHarness, Dialect, HarnessStrategyRegistry, ResetMode, TestHarness, aTangoConfig, aTangoRuntime, anIntegrationHarness, applyAndVerifyMigrations, assertMigrationPlan, conformance_exports, createQuerySetFixture, domain_exports, expectQueryResult, integration_exports, introspectSchema, migrations_exports, runDialectConformanceSuite, runtime_exports, seedTable, setupTestTangoRuntime, smoke_exports };
|
|
758
|
-
//# sourceMappingURL=integration-
|
|
773
|
+
export { AppProcessHarness, Dialect, HarnessStrategyRegistry, ResetMode, TestHarness, aTangoConfig, aTangoRuntime, anIntegrationHarness, applyAndVerifyMigrations, assertMigrationPlan, conformance_exports, createModelQuerySetFixture, createQuerySetFixture, domain_exports, expectQueryResult, integration_exports, introspectSchema, migrations_exports, runDialectConformanceSuite, runtime_exports, seedTable, setupTestTangoRuntime, smoke_exports };
|
|
774
|
+
//# sourceMappingURL=integration-CaRF_lF_.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integration-CaRF_lF_.js","names":["harness: IntegrationHarness","options: AssertMigrationPlanOptions","harness: IntegrationHarness","options: ApplyAndVerifyMigrationsOptions","status: MigrationStatus","harness: IntegrationHarness","strategy: HarnessStrategy","options: {\n createOptions?: HarnessOptions;\n migrationsDir?: string;\n }","options: TestTangoConfigOptions","options: TestTangoConfigOptions","options: TestTangoConfigOptions","defaultDeps: AppProcessHarnessDeps","ms: number","options: AppProcessHarnessOptions","deps: AppProcessHarnessDeps","chunk: unknown","value: unknown","deps: Partial<AppProcessHarnessDeps>","mergedDeps: AppProcessHarnessDeps","path: string","init?: RequestInit","response: Response","expectedStatus: number","label: string","bodyText: string","timeoutMs: number","current: string","chunk: string","defaultCapabilities: DialectTestCapabilities","overrides: Partial<IntegrationHarness>","value: unknown","strategy: HarnessStrategy","dialect: Dialect | string","value: string | undefined","dialect: Dialect","opts: {\n config?: Partial<AdapterConfig>;\n tangoConfigLoader?: () => unknown;\n sqliteFile?: string;\n }","merged: AdapterConfig","value: unknown","explicitSchema?: string","options: HarnessOptions","client: DBClient | null","harness: IntegrationHarness","migrationsDir: string","value: unknown","client: DBClient","options: HarnessOptions","client: DBClient | null","harness: IntegrationHarness","migrationsDir: string","value: unknown","strategy: HarnessStrategy","args: { dialect: Dialect | string; options?: HarnessOptions }","registry?: HarnessStrategyRegistry","options?: HarnessOptions","harness: IntegrationHarness","table: string","rows: T[]","input: {\n harness: IntegrationHarness;\n meta: TableMeta;\n}","executor: QueryExecutor<TModel>","input: {\n harness: IntegrationHarness;\n meta: TableMeta;\n}","actual: Promise<T> | T","expected: T"],"sources":["../src/integration/domain/Dialect.ts","../src/integration/domain/ResetMode.ts","../src/integration/domain/index.ts","../src/integration/migrations/AssertMigrationPlan.ts","../src/integration/migrations/ApplyAndVerifyMigrations.ts","../src/integration/migrations/IntrospectSchema.ts","../src/integration/migrations/index.ts","../src/integration/conformance/runDialectConformanceSuite.ts","../src/integration/conformance/index.ts","../src/integration/runtime/aTangoConfig.ts","../src/integration/runtime/aTangoRuntime.ts","../src/integration/runtime/setupTestTangoRuntime.ts","../src/integration/runtime/index.ts","../src/integration/smoke/AppProcessHarness.ts","../src/integration/smoke/index.ts","../src/integration/anIntegrationHarness.ts","../src/integration/HarnessStrategyRegistry.ts","../src/integration/config.ts","../src/integration/strategies/PostgresHarnessStrategy.ts","../src/integration/strategies/SqliteHarnessStrategy.ts","../src/integration/TestHarness.ts","../src/integration/orm/seedTable.ts","../src/integration/orm/createModelQuerySetFixture.ts","../src/integration/orm/createQuerySetFixture.ts","../src/integration/orm/expectQueryResult.ts","../src/integration/index.ts"],"sourcesContent":["export const Dialect = {\n Sqlite: 'sqlite',\n Postgres: 'postgres',\n} as const;\n\nexport type Dialect = (typeof Dialect)[keyof typeof Dialect];\n","export const ResetMode = {\n Transaction: 'transaction',\n Truncate: 'truncate',\n DropSchema: 'drop-schema',\n} as const;\n\nexport type ResetMode = (typeof ResetMode)[keyof typeof ResetMode];\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport { Dialect } from './Dialect';\nexport type { HarnessOptions, HarnessStrategy } from './HarnessStrategy';\nexport type { DialectTestCapabilities, IntegrationHarness } from './IntegrationHarness';\nexport { ResetMode } from './ResetMode';\n","import type { IntegrationHarness } from '../domain';\n\nexport type AssertMigrationPlanOptions = {\n migrationsDir: string;\n expectSqlContains?: string[];\n};\n\n/**\n * Generate a migration plan through a harness and assert that required SQL fragments appear.\n */\nexport async function assertMigrationPlan(\n harness: IntegrationHarness,\n options: AssertMigrationPlanOptions\n): Promise<string> {\n const runner = harness.migrationRunner(options.migrationsDir);\n const plan = await runner.plan();\n\n for (const snippet of options.expectSqlContains ?? []) {\n if (!plan.includes(snippet)) {\n throw new Error(`Expected migration plan to contain: ${snippet}`);\n }\n }\n\n return plan;\n}\n","import type { IntegrationHarness } from '../domain';\n\nexport type ApplyAndVerifyMigrationsOptions = {\n migrationsDir: string;\n toId?: string;\n expectedAppliedIds?: string[];\n};\n\nexport type MigrationStatus = { id: string; applied: boolean };\n\n/**\n * Apply migrations through a harness and optionally verify that specific ids were applied.\n */\nexport async function applyAndVerifyMigrations(\n harness: IntegrationHarness,\n options: ApplyAndVerifyMigrationsOptions\n): Promise<{ statuses: MigrationStatus[] }> {\n const runner = harness.migrationRunner(options.migrationsDir);\n await runner.apply(options.toId);\n const statuses = await runner.status();\n\n for (const id of options.expectedAppliedIds ?? []) {\n const row = statuses.find((status: MigrationStatus) => status.id === id);\n if (!row || !row.applied) {\n throw new Error(`Expected migration ${id} to be applied`);\n }\n }\n\n return { statuses };\n}\n","import { createDefaultIntrospectorStrategy } from '@danceroutine/tango-migrations';\nimport type { Dialect as MigrationDialect } from '@danceroutine/tango-migrations';\nimport { Dialect, type IntegrationHarness } from '../domain';\n\nconst introspectorStrategy = createDefaultIntrospectorStrategy();\n\n/**\n * Introspect the schema visible to a harness using Tango's built-in introspectors.\n */\nexport async function introspectSchema(harness: IntegrationHarness): Promise<unknown> {\n if (harness.dialect !== Dialect.Postgres && harness.dialect !== Dialect.Sqlite) {\n throw new Error(`No introspector registered for dialect: ${String(harness.dialect)}`);\n }\n const dialect = harness.dialect === Dialect.Postgres ? 'postgres' : 'sqlite';\n return introspectorStrategy.introspect(dialect as unknown as MigrationDialect, harness.dbClient);\n}\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport { assertMigrationPlan, type AssertMigrationPlanOptions } from './AssertMigrationPlan';\nexport {\n applyAndVerifyMigrations,\n type ApplyAndVerifyMigrationsOptions,\n type MigrationStatus,\n} from './ApplyAndVerifyMigrations';\nexport { introspectSchema } from './IntrospectSchema';\n","import type { HarnessOptions, HarnessStrategy } from '../domain';\n\n/**\n * Shared conformance checks every harness strategy must satisfy.\n *\n * This is intentionally framework-agnostic so first-party and third-party\n * strategies can run the same lifecycle validation.\n */\nexport async function runDialectConformanceSuite(\n strategy: HarnessStrategy,\n options: {\n createOptions?: HarnessOptions;\n migrationsDir?: string;\n } = {}\n): Promise<void> {\n const harness = await strategy.create(options.createOptions);\n\n if (harness.dialect !== strategy.dialect) {\n throw new Error(\n `Conformance failed: harness dialect '${String(harness.dialect)}' does not match strategy dialect '${String(strategy.dialect)}'`\n );\n }\n\n if (harness.capabilities !== strategy.capabilities) {\n throw new Error('Conformance failed: harness capabilities must be strategy capabilities reference');\n }\n\n let resetBeforeSetupThrew = false;\n try {\n await harness.reset();\n } catch {\n resetBeforeSetupThrew = true;\n }\n if (!resetBeforeSetupThrew) {\n throw new Error('Conformance failed: reset() must throw before setup()');\n }\n\n await harness.setup();\n await harness.reset();\n harness.migrationRunner(options.migrationsDir ?? '/tmp/migrations');\n await harness.teardown();\n\n let dbClientAfterTeardownThrew = false;\n try {\n // Access after teardown should fail to prevent stale client usage.\n\n // oxlint-disable-next-line no-unused-expressions\n harness.dbClient;\n } catch {\n dbClientAfterTeardownThrew = true;\n }\n if (!dbClientAfterTeardownThrew) {\n throw new Error('Conformance failed: dbClient getter must throw after teardown()');\n }\n}\n","export { runDialectConformanceSuite } from './runDialectConformanceSuite';\n","import { defineConfig, type TangoConfig } from '@danceroutine/tango-config';\n\nexport type TestTangoConfigOptions = {\n adapter?: 'sqlite' | 'postgres';\n sqliteFilename?: string;\n};\n\n/**\n * Create a stable Tango config fixture for runtime-oriented tests.\n */\nexport function aTangoConfig(options: TestTangoConfigOptions = {}): TangoConfig {\n const adapter = options.adapter ?? 'sqlite';\n const sqliteFilename = options.sqliteFilename ?? ':memory:';\n\n return defineConfig({\n current: 'test',\n environments: {\n development: {\n name: 'development',\n db:\n adapter === 'sqlite'\n ? { adapter: 'sqlite', filename: sqliteFilename, maxConnections: 1 }\n : {\n adapter: 'postgres',\n url: 'postgres://postgres:postgres@localhost:5432/tango',\n maxConnections: 1,\n },\n migrations: { dir: 'migrations', online: adapter === 'postgres' },\n },\n test: {\n name: 'test',\n db:\n adapter === 'sqlite'\n ? { adapter: 'sqlite', filename: sqliteFilename, maxConnections: 1 }\n : {\n adapter: 'postgres',\n url: 'postgres://postgres:postgres@localhost:5432/tango_test',\n maxConnections: 1,\n },\n migrations: { dir: 'migrations', online: adapter === 'postgres' },\n },\n production: {\n name: 'production',\n db:\n adapter === 'sqlite'\n ? { adapter: 'sqlite', filename: sqliteFilename, maxConnections: 1 }\n : {\n adapter: 'postgres',\n url: 'postgres://postgres:postgres@localhost:5432/tango',\n maxConnections: 1,\n },\n migrations: { dir: 'migrations', online: adapter === 'postgres' },\n },\n },\n });\n}\n","import { loadConfig } from '@danceroutine/tango-config';\nimport { TangoRuntime } from '@danceroutine/tango-orm';\nimport { aTangoConfig, type TestTangoConfigOptions } from './aTangoConfig';\n\n/**\n * Create a standalone Tango runtime for tests without mutating the process-default runtime.\n */\nexport function aTangoRuntime(options: TestTangoConfigOptions = {}): TangoRuntime {\n return new TangoRuntime(() => loadConfig(() => aTangoConfig(options)));\n}\n","import { initializeTangoRuntime, resetTangoRuntime, type TangoRuntime } from '@danceroutine/tango-orm';\nimport { aTangoConfig, type TestTangoConfigOptions } from './aTangoConfig';\n\n/**\n * Reset and initialize the process-default Tango runtime for tests.\n */\nexport async function setupTestTangoRuntime(options: TestTangoConfigOptions = {}): Promise<TangoRuntime> {\n await resetTangoRuntime();\n return initializeTangoRuntime(() => aTangoConfig(options));\n}\n","/**\n * Domain boundary barrel: centralizes runtime-oriented integration fixtures.\n */\n\nexport { aTangoConfig } from './aTangoConfig';\nexport type { TestTangoConfigOptions } from './aTangoConfig';\nexport { aTangoRuntime } from './aTangoRuntime';\nexport { setupTestTangoRuntime } from './setupTestTangoRuntime';\n","import { spawn } from 'node:child_process';\n\nexport interface AppProcessHarnessOptions {\n command: string;\n args?: string[];\n cwd?: string;\n env?: Record<string, string | undefined>;\n baseUrl: string;\n readyPath?: string;\n readyTimeoutMs?: number;\n readyIntervalMs?: number;\n stopTimeoutMs?: number;\n}\n\ntype FetchLike = typeof fetch;\ntype ProcessHarnessOutputStream = {\n on(eventName: 'data', listener: (chunk: unknown) => void): unknown;\n};\ntype ProcessHarnessChild = {\n exitCode: number | null;\n killed: boolean;\n kill(signal?: NodeJS.Signals): boolean;\n off(eventName: 'exit', listener: () => void): unknown;\n once(eventName: 'exit', listener: () => void): unknown;\n stdout?: ProcessHarnessOutputStream | null;\n stderr?: ProcessHarnessOutputStream | null;\n};\n\ninterface AppProcessHarnessDeps {\n spawnProcess: (\n command: string,\n args?: readonly string[],\n options?: Parameters<typeof spawn>[2]\n ) => ProcessHarnessChild;\n fetchImpl: FetchLike;\n sleep: (ms: number) => Promise<void>;\n}\n\nconst DEFAULT_READY_TIMEOUT_MS = 30_000;\nconst DEFAULT_READY_INTERVAL_MS = 250;\nconst DEFAULT_STOP_TIMEOUT_MS = 10_000;\nconst MAX_LOG_BUFFER_CHARS = 20_000;\n\nconst defaultDeps: AppProcessHarnessDeps = {\n spawnProcess: (command, args, options) => spawn(command, args as string[], options as Parameters<typeof spawn>[2]),\n fetchImpl: fetch,\n sleep: (ms: number) =>\n new Promise((resolve) => {\n setTimeout(resolve, ms);\n }),\n};\n\n/**\n * Lightweight process harness for end-to-end smoke tests that need a real app process.\n */\nexport class AppProcessHarness {\n static readonly BRAND = 'tango.testing.app_process_harness' as const;\n readonly __tangoBrand: typeof AppProcessHarness.BRAND = AppProcessHarness.BRAND;\n private readonly child: ProcessHarnessChild;\n private readonly baseUrl: string;\n private readonly readyUrl: string;\n private readonly readyTimeoutMs: number;\n private readonly readyIntervalMs: number;\n private readonly stopTimeoutMs: number;\n private readonly deps: AppProcessHarnessDeps;\n private stopped = false;\n private stdoutBuffer = '';\n private stderrBuffer = '';\n\n private constructor(options: AppProcessHarnessOptions, deps: AppProcessHarnessDeps) {\n this.baseUrl = options.baseUrl.replace(/\\/+$/, '');\n this.readyUrl = `${this.baseUrl}${normalizePath(options.readyPath ?? '/health')}`;\n this.readyTimeoutMs = options.readyTimeoutMs ?? DEFAULT_READY_TIMEOUT_MS;\n this.readyIntervalMs = options.readyIntervalMs ?? DEFAULT_READY_INTERVAL_MS;\n this.stopTimeoutMs = options.stopTimeoutMs ?? DEFAULT_STOP_TIMEOUT_MS;\n this.deps = deps;\n\n this.child = this.deps.spawnProcess(options.command, options.args ?? [], {\n cwd: options.cwd,\n env: { ...process.env, ...options.env },\n stdio: 'pipe',\n });\n this.child.stdout?.on('data', (chunk: unknown) => {\n this.stdoutBuffer = appendBuffer(this.stdoutBuffer, String(chunk));\n });\n this.child.stderr?.on('data', (chunk: unknown) => {\n this.stderrBuffer = appendBuffer(this.stderrBuffer, String(chunk));\n });\n }\n\n /**\n * Narrow an unknown value to the smoke-test harness that owns a child process.\n */\n static isAppProcessHarness(value: unknown): value is AppProcessHarness {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === AppProcessHarness.BRAND\n );\n }\n\n /**\n * Spawn the target process and wait until its readiness endpoint responds successfully.\n */\n static async start(\n options: AppProcessHarnessOptions,\n deps: Partial<AppProcessHarnessDeps> = {}\n ): Promise<AppProcessHarness> {\n const mergedDeps: AppProcessHarnessDeps = {\n ...defaultDeps,\n ...deps,\n };\n const harness = new AppProcessHarness(options, mergedDeps);\n await harness.waitForReady();\n return harness;\n }\n\n /**\n * Return the buffered stdout log for recent process output.\n */\n getStdoutLog(): string {\n return this.stdoutBuffer;\n }\n\n /**\n * Return the buffered stderr log for recent process output.\n */\n getStderrLog(): string {\n return this.stderrBuffer;\n }\n\n /**\n * Return stdout and stderr in a single formatted string for debugging failures.\n */\n getCombinedLog(): string {\n const stdout = this.stdoutBuffer.trim();\n const stderr = this.stderrBuffer.trim();\n if (!stdout && !stderr) {\n return '';\n }\n return [`[stdout]\\n${stdout}`, `[stderr]\\n${stderr}`].join('\\n\\n').trim();\n }\n\n /**\n * Issue an HTTP request against the managed application process.\n */\n async request(path: string, init?: RequestInit): Promise<Response> {\n const target = path.startsWith('http') ? path : `${this.baseUrl}${normalizePath(path)}`;\n return this.deps.fetchImpl(target, init);\n }\n\n /**\n * Assert an HTTP response status and include process logs when it mismatches.\n */\n async assertResponseStatus(response: Response, expectedStatus: number, label: string): Promise<void> {\n if (response.status === expectedStatus) {\n return;\n }\n\n let bodyText: string;\n try {\n bodyText = await response.text();\n } catch (error) {\n bodyText = `failed to read response body: ${String(error)}`;\n }\n\n throw new Error(\n [\n `${label}. expected ${String(expectedStatus)} got ${String(response.status)}`,\n `response body: ${bodyText}`,\n `process logs:\\n${this.getCombinedLog()}`,\n ].join('\\n')\n );\n }\n\n /**\n * Stop the managed process, escalating from SIGTERM to SIGKILL when necessary.\n */\n async stop(): Promise<void> {\n if (this.stopped) {\n return;\n }\n this.stopped = true;\n\n if (this.child.exitCode !== null || this.child.killed) {\n return;\n }\n\n this.child.kill('SIGTERM');\n const exited = await this.waitForExit(this.stopTimeoutMs);\n if (!exited && !this.child.killed) {\n this.child.kill('SIGKILL');\n await this.waitForExit(this.stopTimeoutMs);\n }\n }\n\n private async waitForReady(): Promise<void> {\n const deadline = Date.now() + this.readyTimeoutMs;\n\n while (Date.now() < deadline) {\n if (this.child.exitCode !== null) {\n throw new Error(\n `Process exited before ready check succeeded (exitCode=${String(this.child.exitCode)}).\\n${this.getCombinedLog()}`\n );\n }\n try {\n const response = await this.deps.fetchImpl(this.readyUrl);\n if (response.ok) {\n return;\n }\n } catch {\n // Retry until timeout.\n }\n await this.deps.sleep(this.readyIntervalMs);\n }\n\n await this.stop();\n throw new Error(`Timed out waiting for readiness at ${this.readyUrl}.\\n${this.getCombinedLog()}`);\n }\n\n private async waitForExit(timeoutMs: number): Promise<boolean> {\n if (this.child.exitCode !== null) {\n return true;\n }\n\n return await new Promise<boolean>((resolve) => {\n const timer = setTimeout(() => {\n this.child.off('exit', onExit);\n resolve(false);\n }, timeoutMs);\n\n const onExit = () => {\n clearTimeout(timer);\n resolve(true);\n };\n\n this.child.once('exit', onExit);\n });\n }\n}\n\nfunction appendBuffer(current: string, chunk: string): string {\n const next = current + chunk;\n if (next.length <= MAX_LOG_BUFFER_CHARS) {\n return next;\n }\n return next.slice(next.length - MAX_LOG_BUFFER_CHARS);\n}\n\nfunction normalizePath(path: string): string {\n if (!path) {\n return '/';\n }\n return path.startsWith('/') ? path : `/${path}`;\n}\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport * from './AppProcessHarness';\n","import { vi } from 'vitest';\nimport { anAdapter } from '../mocks/anAdapter';\nimport { aDBClient } from '../mocks/aDBClient';\nimport { Dialect } from './domain/Dialect';\nimport { ResetMode } from './domain/ResetMode';\nimport type { DialectTestCapabilities, IntegrationHarness } from './domain/IntegrationHarness';\n\nconst defaultCapabilities: DialectTestCapabilities = {\n transactionalDDL: true,\n supportsSchemas: false,\n supportsConcurrentIndex: false,\n supportsDeferredFkValidation: false,\n supportsJsonb: false,\n};\n\n/**\n * Create an integration-harness fixture with optional overrides.\n */\nexport function anIntegrationHarness(overrides: Partial<IntegrationHarness> = {}): IntegrationHarness {\n return {\n dialect: Dialect.Sqlite,\n adapter: anAdapter({ dialect: 'sqlite' }),\n capabilities: defaultCapabilities,\n resetMode: ResetMode.DropSchema,\n dbClient: aDBClient(),\n setup: vi.fn(async () => {}),\n reset: vi.fn(async () => {}),\n teardown: vi.fn(async () => {}),\n migrationRunner: vi.fn(() => ({}) as never),\n ...overrides,\n };\n}\n","import type { Dialect, HarnessStrategy } from './domain';\n\n/**\n * Registry of test harness strategies keyed by dialect.\n */\nexport class HarnessStrategyRegistry {\n static readonly BRAND = 'tango.testing.harness_strategy_registry' as const;\n readonly __tangoBrand: typeof HarnessStrategyRegistry.BRAND = HarnessStrategyRegistry.BRAND;\n private readonly strategies = new Map<string, HarnessStrategy>();\n\n /**\n * Narrow an unknown value to `HarnessStrategyRegistry`.\n */\n static isHarnessStrategyRegistry(value: unknown): value is HarnessStrategyRegistry {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === HarnessStrategyRegistry.BRAND\n );\n }\n\n /**\n * Register or replace a dialect strategy.\n */\n register(strategy: HarnessStrategy): this {\n this.strategies.set(String(strategy.dialect), strategy);\n return this;\n }\n\n /**\n * Resolve a strategy for a dialect, or throw if none is registered.\n */\n get(dialect: Dialect | string): HarnessStrategy {\n const strategy = this.strategies.get(String(dialect));\n if (!strategy) {\n throw new Error(`No harness strategy registered for dialect: ${String(dialect)}`);\n }\n return strategy;\n }\n\n /**\n * List all registered strategies.\n */\n list(): readonly HarnessStrategy[] {\n return [...this.strategies.values()];\n }\n}\n","import type { AdapterConfig } from '@danceroutine/tango-orm';\nimport { loadConfig } from '@danceroutine/tango-config';\nimport { Dialect } from './domain';\n\nfunction readNumber(value: string | undefined): number | undefined {\n if (!value) return undefined;\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n\n/**\n * Resolve adapter configuration for a test harness from explicit options,\n * typed Tango config, and environment variables in that order.\n */\nexport function resolveAdapterConfig(\n dialect: Dialect,\n opts: {\n config?: Partial<AdapterConfig>;\n tangoConfigLoader?: () => unknown;\n sqliteFile?: string;\n }\n): AdapterConfig {\n const fromOptions = opts.config ?? {};\n\n if (opts.tangoConfigLoader) {\n const loaded = loadConfig(opts.tangoConfigLoader);\n const current = loaded.current.db;\n const merged: AdapterConfig = {\n url: fromOptions.url ?? current.url,\n host: fromOptions.host ?? current.host,\n port: fromOptions.port ?? current.port,\n database: fromOptions.database ?? current.database,\n user: fromOptions.user ?? current.user,\n password: fromOptions.password ?? current.password,\n filename: fromOptions.filename ?? current.filename,\n maxConnections: fromOptions.maxConnections ?? current.maxConnections,\n };\n if (dialect === Dialect.Sqlite) {\n merged.filename = opts.sqliteFile ?? merged.filename ?? ':memory:';\n }\n return merged;\n }\n\n if (dialect === Dialect.Postgres) {\n return {\n url: fromOptions.url ?? process.env.TANGO_DATABASE_URL ?? process.env.DATABASE_URL,\n host: fromOptions.host ?? process.env.TANGO_DB_HOST,\n port: fromOptions.port ?? readNumber(process.env.TANGO_DB_PORT),\n database: fromOptions.database ?? process.env.TANGO_DB_NAME,\n user: fromOptions.user ?? process.env.TANGO_DB_USER,\n password: fromOptions.password ?? process.env.TANGO_DB_PASSWORD,\n maxConnections: fromOptions.maxConnections ?? 10,\n };\n }\n\n return {\n filename: opts.sqliteFile ?? fromOptions.filename ?? process.env.TANGO_SQLITE_FILENAME ?? ':memory:',\n maxConnections: fromOptions.maxConnections ?? 1,\n };\n}\n","import { quoteSqlIdentifier, validateSqlIdentifier } from '@danceroutine/tango-core';\nimport { MigrationRunner } from '@danceroutine/tango-migrations';\nimport type { Dialect as MigrationDialect } from '@danceroutine/tango-migrations';\nimport { PostgresAdapter } from '@danceroutine/tango-orm/connection';\nimport type { DBClient } from '@danceroutine/tango-orm';\nimport { resolveAdapterConfig } from '../config';\nimport {\n Dialect,\n ResetMode,\n type DialectTestCapabilities,\n type HarnessOptions,\n type HarnessStrategy,\n type IntegrationHarness,\n} from '../domain';\n\n/**\n * Harness strategy for PostgreSQL-backed integration tests.\n */\nexport class PostgresHarnessStrategy implements HarnessStrategy {\n static readonly BRAND = 'tango.testing.postgres_harness_strategy' as const;\n readonly __tangoBrand: typeof PostgresHarnessStrategy.BRAND = PostgresHarnessStrategy.BRAND;\n readonly dialect: Dialect = Dialect.Postgres;\n readonly capabilities: DialectTestCapabilities = {\n transactionalDDL: true,\n supportsSchemas: true,\n supportsConcurrentIndex: true,\n supportsDeferredFkValidation: true,\n supportsJsonb: true,\n };\n\n /**\n * Narrow an unknown value to the PostgreSQL integration harness strategy.\n */\n static isPostgresHarnessStrategy(value: unknown): value is PostgresHarnessStrategy {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === PostgresHarnessStrategy.BRAND\n );\n }\n\n private static buildSchemaName(explicitSchema?: string): string {\n if (explicitSchema) return explicitSchema;\n const random = Math.random().toString(36).slice(2, 8);\n return `tango_test_${Date.now()}_${random}`;\n }\n\n /**\n * Create a configured Postgres integration harness instance.\n */\n async create(options: HarnessOptions = {}): Promise<IntegrationHarness> {\n const config = resolveAdapterConfig(Dialect.Postgres, {\n config: options.config,\n tangoConfigLoader: options.tangoConfigLoader,\n });\n\n const adapter = new PostgresAdapter();\n const schemaName = PostgresHarnessStrategy.buildSchemaName(options.schema);\n const resetMode = options.resetMode ?? ResetMode.DropSchema;\n let client: DBClient | null = null;\n\n const ensureSearchPath = async (): Promise<void> => {\n const dbClient = client as DBClient;\n const schema = quoteSqlIdentifier(validateSqlIdentifier(schemaName, 'schema'), 'postgres');\n await dbClient.query(`CREATE SCHEMA IF NOT EXISTS ${schema}`);\n await dbClient.query(`SET search_path TO ${schema}`);\n };\n\n const recreateSchema = async (): Promise<void> => {\n const dbClient = client as DBClient;\n const schema = quoteSqlIdentifier(validateSqlIdentifier(schemaName, 'schema'), 'postgres');\n await dbClient.query(`DROP SCHEMA IF EXISTS ${schema} CASCADE`);\n await dbClient.query(`CREATE SCHEMA ${schema}`);\n await dbClient.query(`SET search_path TO ${schema}`);\n };\n\n const harness: IntegrationHarness = {\n dialect: Dialect.Postgres,\n adapter,\n capabilities: this.capabilities,\n resetMode,\n get dbClient(): DBClient {\n if (!client) {\n throw new Error('Postgres harness not initialized. Call setup() first.');\n }\n return client;\n },\n async setup(): Promise<void> {\n client = await adapter.connect(config);\n await ensureSearchPath();\n },\n async reset(): Promise<void> {\n if (!client) {\n throw new Error('Postgres harness not initialized. Call setup() first.');\n }\n if (resetMode === ResetMode.DropSchema || resetMode === ResetMode.Transaction) {\n await recreateSchema();\n return;\n }\n\n const { rows } = await client.query<{ table_name: string }>(\n `SELECT table_name FROM information_schema.tables WHERE table_schema = $1 AND table_type = 'BASE TABLE'`,\n [schemaName]\n );\n for (const row of rows) {\n const schema = quoteSqlIdentifier(validateSqlIdentifier(schemaName, 'schema'), 'postgres');\n const table = quoteSqlIdentifier(\n validateSqlIdentifier(String(row.table_name), 'table'),\n 'postgres'\n );\n await client.query(`TRUNCATE TABLE ${schema}.${table} RESTART IDENTITY CASCADE`);\n }\n await client.query(\n `SET search_path TO ${quoteSqlIdentifier(validateSqlIdentifier(schemaName, 'schema'), 'postgres')}`\n );\n },\n async teardown(): Promise<void> {\n if (!client) return;\n try {\n await client.query(\n `DROP SCHEMA IF EXISTS ${quoteSqlIdentifier(validateSqlIdentifier(schemaName, 'schema'), 'postgres')} CASCADE`\n );\n } finally {\n await client.close();\n client = null;\n }\n },\n migrationRunner(migrationsDir: string): MigrationRunner {\n if (!client) {\n throw new Error('Postgres harness not initialized. Call setup() first.');\n }\n return new MigrationRunner(client, 'postgres' as MigrationDialect, migrationsDir);\n },\n };\n\n return harness;\n }\n}\n","import { rm } from 'node:fs/promises';\nimport { quoteSqlIdentifier, validateSqlIdentifier } from '@danceroutine/tango-core';\nimport { MigrationRunner } from '@danceroutine/tango-migrations';\nimport type { Dialect as MigrationDialect } from '@danceroutine/tango-migrations';\nimport { SqliteAdapter } from '@danceroutine/tango-orm/connection';\nimport type { DBClient } from '@danceroutine/tango-orm';\nimport { resolveAdapterConfig } from '../config';\nimport {\n Dialect,\n ResetMode,\n type DialectTestCapabilities,\n type HarnessOptions,\n type HarnessStrategy,\n type IntegrationHarness,\n} from '../domain';\n\n/**\n * Harness strategy for SQLite-backed integration tests.\n */\nexport class SqliteHarnessStrategy implements HarnessStrategy {\n static readonly BRAND = 'tango.testing.sqlite_harness_strategy' as const;\n readonly __tangoBrand: typeof SqliteHarnessStrategy.BRAND = SqliteHarnessStrategy.BRAND;\n readonly dialect: Dialect = Dialect.Sqlite;\n readonly capabilities: DialectTestCapabilities = {\n transactionalDDL: true,\n supportsSchemas: false,\n supportsConcurrentIndex: false,\n supportsDeferredFkValidation: false,\n supportsJsonb: false,\n };\n\n /**\n * Narrow an unknown value to the SQLite integration harness strategy.\n */\n static isSqliteHarnessStrategy(value: unknown): value is SqliteHarnessStrategy {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === SqliteHarnessStrategy.BRAND\n );\n }\n\n private static async dropAllTables(client: DBClient): Promise<void> {\n const { rows } = await client.query<{ name: string }>(\n \"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'\"\n );\n for (const row of rows) {\n const table = quoteSqlIdentifier(validateSqlIdentifier(String(row.name), 'table'), 'sqlite');\n await client.query(`DROP TABLE IF EXISTS ${table}`);\n }\n }\n\n /**\n * Create a configured SQLite integration harness instance.\n */\n async create(options: HarnessOptions = {}): Promise<IntegrationHarness> {\n const config = resolveAdapterConfig(Dialect.Sqlite, {\n config: options.config,\n tangoConfigLoader: options.tangoConfigLoader,\n sqliteFile: options.sqliteFile,\n });\n\n const adapter = new SqliteAdapter();\n const resetMode = options.resetMode ?? ResetMode.DropSchema;\n let client: DBClient | null = null;\n\n const reconnect = async (): Promise<DBClient> => {\n client = await adapter.connect(config);\n return client;\n };\n\n const harness: IntegrationHarness = {\n dialect: Dialect.Sqlite,\n adapter,\n capabilities: this.capabilities,\n resetMode,\n get dbClient(): DBClient {\n if (!client) {\n throw new Error('Sqlite harness not initialized. Call setup() first.');\n }\n return client;\n },\n async setup(): Promise<void> {\n await reconnect();\n },\n async reset(): Promise<void> {\n if (!client) {\n throw new Error('Sqlite harness not initialized. Call setup() first.');\n }\n\n if (resetMode === ResetMode.DropSchema && config.filename && config.filename !== ':memory:') {\n await client.close();\n await rm(config.filename, { force: true });\n await reconnect();\n return;\n }\n\n await SqliteHarnessStrategy.dropAllTables(client);\n },\n async teardown(): Promise<void> {\n if (client) {\n await client.close();\n client = null;\n }\n if (config.filename && config.filename !== ':memory:') {\n await rm(config.filename, { force: true });\n }\n },\n migrationRunner(migrationsDir: string): MigrationRunner {\n if (!client) {\n throw new Error('Sqlite harness not initialized. Call setup() first.');\n }\n return new MigrationRunner(client, 'sqlite' as MigrationDialect, migrationsDir);\n },\n };\n\n return harness;\n }\n}\n","import { HarnessStrategyRegistry } from './HarnessStrategyRegistry';\nimport { Dialect, type HarnessOptions, type HarnessStrategy, type IntegrationHarness } from './domain';\nimport { PostgresHarnessStrategy } from './strategies/PostgresHarnessStrategy';\nimport { SqliteHarnessStrategy } from './strategies/SqliteHarnessStrategy';\n\n/**\n * Facade for creating integration test harnesses by dialect.\n */\nexport class TestHarness {\n static readonly BRAND = 'tango.testing.test_harness' as const;\n private static defaultRegistry: HarnessStrategyRegistry | null = null;\n readonly __tangoBrand: typeof TestHarness.BRAND = TestHarness.BRAND;\n\n /**\n * Narrow an unknown value to `TestHarness`.\n */\n static isTestHarness(value: unknown): value is TestHarness {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === TestHarness.BRAND\n );\n }\n\n /**\n * Register a harness strategy on the shared default registry.\n */\n static registerStrategy(strategy: HarnessStrategy): void {\n this.ensureRegistry().register(strategy);\n }\n\n /**\n * Return the shared harness strategy registry.\n */\n static getRegistry(): HarnessStrategyRegistry {\n return this.ensureRegistry();\n }\n\n /**\n * Create a dialect-specific harness from the registry.\n */\n static async forDialect(\n args: { dialect: Dialect | string; options?: HarnessOptions },\n registry?: HarnessStrategyRegistry\n ): Promise<IntegrationHarness> {\n const selectedRegistry = registry ?? this.ensureRegistry();\n const strategy = selectedRegistry.get(args.dialect);\n return strategy.create(args.options);\n }\n\n /**\n * Convenience helper for a SQLite test harness.\n */\n static async sqlite(options?: HarnessOptions): Promise<IntegrationHarness> {\n return this.forDialect({ dialect: Dialect.Sqlite, options });\n }\n\n /**\n * Convenience helper for a Postgres test harness.\n */\n static async postgres(options?: HarnessOptions): Promise<IntegrationHarness> {\n return this.forDialect({ dialect: Dialect.Postgres, options });\n }\n\n private static ensureRegistry(): HarnessStrategyRegistry {\n if (this.defaultRegistry) return this.defaultRegistry;\n\n const registry = new HarnessStrategyRegistry();\n registry.register(new SqliteHarnessStrategy());\n registry.register(new PostgresHarnessStrategy());\n this.defaultRegistry = registry;\n return registry;\n }\n}\n","import { quoteSqlIdentifier, validateSqlIdentifier, type SqlDialect } from '@danceroutine/tango-core';\nimport { Dialect, type IntegrationHarness } from '../domain/index';\n\n/**\n * Seed rows directly into a table for integration tests that need known fixtures.\n */\nexport async function seedTable<T extends Record<string, unknown>>(\n harness: IntegrationHarness,\n table: string,\n rows: T[]\n): Promise<void> {\n if (rows.length === 0) {\n return;\n }\n\n const columns = Object.keys(rows[0] ?? {});\n if (columns.length === 0) {\n return;\n }\n\n const dialect = harness.dialect as SqlDialect;\n const safeTable = quoteSqlIdentifier(validateSqlIdentifier(table, 'table'), dialect);\n const safeColumns = columns.map((column) =>\n quoteSqlIdentifier(validateSqlIdentifier(column, 'column', columns), dialect)\n );\n\n for (const row of rows) {\n const values = columns.map((column) => {\n const value = row[column];\n if (harness.dialect === Dialect.Sqlite && typeof value === 'boolean') {\n return value ? 1 : 0;\n }\n return value;\n });\n const placeholders =\n harness.dialect === Dialect.Postgres\n ? columns.map((_, index) => `$${index + 1}`).join(', ')\n : columns.map(() => '?').join(', ');\n\n await harness.dbClient.query(\n `INSERT INTO ${safeTable} (${safeColumns.join(', ')}) VALUES (${placeholders})`,\n values as unknown[]\n );\n }\n}\n","import { ModelQuerySet, QuerySet, type QueryExecutor } from '@danceroutine/tango-orm';\nimport type { TableMeta } from '@danceroutine/tango-orm/query';\nimport type { IntegrationHarness } from '../domain/index';\n\n/**\n * Create a `ModelQuerySet` fixture backed by a real integration harness and supplied table metadata.\n */\nexport function createModelQuerySetFixture<TModel extends Record<string, unknown>>(input: {\n harness: IntegrationHarness;\n meta: TableMeta;\n}): QuerySet<TModel> {\n const executor: QueryExecutor<TModel> = {\n meta: input.meta,\n client: input.harness.dbClient,\n adapter: input.harness.adapter,\n run: async (compiled) => {\n const result = await input.harness.dbClient.query<TModel>(compiled.sql, compiled.params);\n return result.rows;\n },\n };\n\n return new ModelQuerySet<TModel>(executor);\n}\n","import { getLogger } from '@danceroutine/tango-core';\nimport type { QuerySet } from '@danceroutine/tango-orm';\nimport type { TableMeta } from '@danceroutine/tango-orm/query';\nimport type { IntegrationHarness } from '../domain/index';\nimport { createModelQuerySetFixture } from './createModelQuerySetFixture';\n\nconst logger = getLogger('tango.testing.integration');\nlet hasWarned = false;\n\n/**\n * @deprecated Use `createModelQuerySetFixture(...)` instead.\n */\nexport function createQuerySetFixture<TModel extends Record<string, unknown>>(input: {\n harness: IntegrationHarness;\n meta: TableMeta;\n}): QuerySet<TModel> {\n if (!hasWarned) {\n hasWarned = true;\n logger.warn('`createQuerySetFixture(...)` is deprecated. Use `createModelQuerySetFixture(...)` instead.');\n }\n\n return createModelQuerySetFixture(input);\n}\n","/**\n * Assert that a query result matches an expected value using structural equality.\n */\nexport async function expectQueryResult<T>(actual: Promise<T> | T, expected: T): Promise<void> {\n const resolved = await actual;\n\n const resolvedJson = JSON.stringify(resolved);\n const expectedJson = JSON.stringify(expected);\n\n if (resolvedJson !== expectedJson) {\n throw new Error(`Expected query result ${expectedJson}, got ${resolvedJson}`);\n }\n}\n","/**\n * Domain boundary barrel: exposes namespaced exports for Django-style drill-down\n * imports and curated flat exports for TS-native ergonomics.\n */\n\nexport * as domain from './domain/index';\nexport * as migrations from './migrations/index';\nexport * as conformance from './conformance/index';\nexport * as runtime from './runtime/index';\nexport * as smoke from './smoke/index';\n\nexport * from './domain/index';\nexport * from './anIntegrationHarness';\nexport * from './HarnessStrategyRegistry';\nexport * from './TestHarness';\nexport * from './migrations/index';\nexport * from './conformance/index';\nexport * from './orm/index';\nexport * from './runtime/index';\nexport * from './smoke/index';\n"],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { vi } from 'vitest';
|
|
2
|
+
import type { QueryExecutor } from '@danceroutine/tango-orm';
|
|
3
|
+
import { ManyToManyRelatedManager } from '@danceroutine/tango-orm';
|
|
4
|
+
/**
|
|
5
|
+
* Overrides accepted by the {@link aManyToManyRelatedManager} fixture. All
|
|
6
|
+
* fields are optional; defaults produce a manager bound to owner primary key
|
|
7
|
+
* `7`, relation name `'tags'`, owner label `'Post'`, and target primary-key
|
|
8
|
+
* field `'id'`.
|
|
9
|
+
*/
|
|
10
|
+
export interface ManyToManyRelatedManagerFixtureOverrides<TTarget extends Record<string, unknown>> {
|
|
11
|
+
ownerPrimaryKey?: unknown;
|
|
12
|
+
relationName?: string;
|
|
13
|
+
ownerModelLabel?: string;
|
|
14
|
+
targetPrimaryKeyField?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Resolver returned by the manager's `targetExecutorProvider`. Pass `null`
|
|
17
|
+
* to simulate the registry not yet knowing about the target model; omit to
|
|
18
|
+
* return a minimal executor stub.
|
|
19
|
+
*/
|
|
20
|
+
targetExecutor?: QueryExecutor<TTarget> | null;
|
|
21
|
+
insertLink?: (ownerPrimaryKey: unknown, targetPrimaryKey: unknown) => Promise<void>;
|
|
22
|
+
insertLinks?: (ownerPrimaryKey: unknown, targetPrimaryKeys: readonly unknown[]) => Promise<void>;
|
|
23
|
+
deleteLink?: (ownerPrimaryKey: unknown, targetPrimaryKey: unknown) => Promise<void>;
|
|
24
|
+
deleteLinks?: (ownerPrimaryKey: unknown, targetPrimaryKeys: readonly unknown[]) => Promise<void>;
|
|
25
|
+
selectTargetIdsForOwner?: (ownerPrimaryKey: unknown) => Promise<readonly (string | number)[]>;
|
|
26
|
+
runAtomic?: <T>(work: () => Promise<T>) => Promise<T>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Return shape of the {@link aManyToManyRelatedManager} fixture. The spy
|
|
30
|
+
* references are returned alongside the manager so tests can assert against
|
|
31
|
+
* the join-table calls without reaching into private fields.
|
|
32
|
+
*/
|
|
33
|
+
export interface ManyToManyRelatedManagerFixture<TTarget extends Record<string, unknown>> {
|
|
34
|
+
manager: ManyToManyRelatedManager<TTarget>;
|
|
35
|
+
insertLink: ReturnType<typeof vi.fn>;
|
|
36
|
+
insertLinks: ReturnType<typeof vi.fn>;
|
|
37
|
+
deleteLink: ReturnType<typeof vi.fn>;
|
|
38
|
+
deleteLinks: ReturnType<typeof vi.fn>;
|
|
39
|
+
selectTargetIdsForOwner: ReturnType<typeof vi.fn>;
|
|
40
|
+
runAtomic: ReturnType<typeof vi.fn>;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Build a {@link ManyToManyRelatedManager} instance wired to lightweight spies
|
|
44
|
+
* for its underlying join-table operations. The fixture skips the
|
|
45
|
+
* relation-metadata and registry plumbing exercised by `ModelManager`, so
|
|
46
|
+
* tests can focus on manager behavior in isolation.
|
|
47
|
+
*/
|
|
48
|
+
export declare function aManyToManyRelatedManager<TTarget extends Record<string, unknown>>(overrides?: ManyToManyRelatedManagerFixtureOverrides<TTarget>): ManyToManyRelatedManagerFixture<TTarget>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { QuerySet } from '@danceroutine/tango-orm';
|
|
2
|
+
/**
|
|
3
|
+
* Create a chainable model-queryset test double with optional behavior overrides.
|
|
4
|
+
*
|
|
5
|
+
* All methods are wrapped in `vi.fn()` so they can be asserted on directly
|
|
6
|
+
* without an additional `vi.mocked()` call.
|
|
7
|
+
*/
|
|
8
|
+
export declare function aModelQuerySet<TModel extends Record<string, unknown>, TResult extends Record<string, unknown> = TModel>(overrides?: Partial<QuerySet<TModel, TResult>>): QuerySet<TModel, TResult>;
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import type { QueryExecutor } from '@danceroutine/tango-orm';
|
|
1
|
+
import type { Adapter, QueryExecutor } from '@danceroutine/tango-orm';
|
|
2
2
|
import type { Dialect, TableMeta } from '@danceroutine/tango-orm/query';
|
|
3
3
|
export type QueryExecutorOverrides<TModel extends Record<string, unknown>> = {
|
|
4
4
|
dialect?: Dialect;
|
|
5
|
+
adapter?: Adapter;
|
|
5
6
|
meta?: TableMeta;
|
|
6
7
|
query?: (sql: string, params?: readonly unknown[]) => Promise<{
|
|
7
8
|
rows: unknown[];
|
|
8
9
|
}>;
|
|
9
10
|
run?: QueryExecutor<TModel>['run'];
|
|
11
|
+
attachPersistedRecordAccessors?: QueryExecutor<TModel>['attachPersistedRecordAccessors'];
|
|
10
12
|
};
|
|
11
13
|
/**
|
|
12
14
|
* Create a minimal `QueryExecutor` test double for `QuerySet` tests.
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import type { QuerySet } from '@danceroutine/tango-orm';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* All methods are wrapped in `vi.fn()` so they can be asserted on directly
|
|
6
|
-
* without an additional `vi.mocked()` call.
|
|
3
|
+
* @deprecated Use `aModelQuerySet(...)` instead.
|
|
7
4
|
*/
|
|
8
5
|
export declare function aQuerySet<TModel extends Record<string, unknown>, TResult extends Record<string, unknown> = TModel>(overrides?: Partial<QuerySet<TModel, TResult>>): QuerySet<TModel, TResult>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type Adapter } from '@danceroutine/tango-orm';
|
|
2
|
+
import type { Dialect } from '@danceroutine/tango-orm/query';
|
|
3
|
+
export type AdapterOverrides = {
|
|
4
|
+
dialect?: Dialect;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Create a real built-in `Adapter` instance for unit tests that need one.
|
|
8
|
+
*
|
|
9
|
+
* Defaults to a Postgres adapter; pass `{ dialect: 'sqlite' }` to obtain a
|
|
10
|
+
* SQLite adapter. The returned adapter carries the matching placeholder
|
|
11
|
+
* formatter so compilers exercised under test emit dialect-accurate SQL.
|
|
12
|
+
*/
|
|
13
|
+
export declare function anAdapter(overrides?: AdapterOverrides): Adapter;
|
package/dist/mocks/index.d.ts
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Domain boundary barrel: centralizes this subdomain's public contract.
|
|
3
3
|
*/
|
|
4
|
+
export { anAdapter } from './anAdapter';
|
|
4
5
|
export { aDBClient } from './aDBClient';
|
|
5
6
|
export { aManager } from './aManager';
|
|
7
|
+
export { aManyToManyRelatedManager } from './aManyToManyRelatedManager';
|
|
8
|
+
export { aModelQuerySet } from './aModelQuerySet';
|
|
6
9
|
export { aQueryResult } from './aQueryResult';
|
|
7
10
|
export { aQuerySet } from './aQuerySet';
|
|
8
11
|
export { aRequestContext } from './aRequestContext';
|
|
9
12
|
export { aQueryExecutor } from './aQueryExecutor';
|
|
10
13
|
export { aRelationMeta } from './aRelationMeta';
|
|
14
|
+
export type { AdapterOverrides } from './anAdapter';
|
|
11
15
|
export type { QueryExecutorOverrides } from './aQueryExecutor';
|
|
12
16
|
export type { ManagerOverrides } from './aManager';
|
|
17
|
+
export type { ManyToManyRelatedManagerFixture, ManyToManyRelatedManagerFixtureOverrides, } from './aManyToManyRelatedManager';
|
|
13
18
|
export type { DBClient } from './DBClient';
|
|
14
19
|
export type { MockQuerySetResult } from './MockQuerySetResult';
|
|
15
20
|
export type { RequestContextFixtureOptions } from './aRequestContext';
|
package/dist/mocks/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { aDBClient } from "../aDBClient-
|
|
2
|
-
import { aManager, aQueryExecutor, aQueryResult, aQuerySet, aRelationMeta, aRequestContext } from "../mocks-
|
|
1
|
+
import { aDBClient, anAdapter } from "../aDBClient-CH-ZcOaP.js";
|
|
2
|
+
import { aManager, aManyToManyRelatedManager, aModelQuerySet, aQueryExecutor, aQueryResult, aQuerySet, aRelationMeta, aRequestContext } from "../mocks-iKVkzHJZ.js";
|
|
3
3
|
|
|
4
|
-
export { aDBClient, aManager, aQueryExecutor, aQueryResult, aQuerySet, aRelationMeta, aRequestContext };
|
|
4
|
+
export { aDBClient, aManager, aManyToManyRelatedManager, aModelQuerySet, aQueryExecutor, aQueryResult, aQuerySet, aRelationMeta, aRequestContext, anAdapter };
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { __export } from "./chunk-BkvOhyD0.js";
|
|
2
|
-
import { aDBClient } from "./aDBClient-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { aDBClient, anAdapter } from "./aDBClient-CH-ZcOaP.js";
|
|
3
|
+
import { ManyToManyRelatedManager, ModelQuerySet as QuerySetClass } from "@danceroutine/tango-orm";
|
|
4
|
+
import { vi, vi as vi$1, vi as vi$2, vi as vi$3 } from "vitest";
|
|
5
5
|
import { QueryResult as QueryResultClass } from "@danceroutine/tango-orm/query";
|
|
6
|
+
import { getLogger } from "@danceroutine/tango-core";
|
|
6
7
|
import { RequestContext } from "@danceroutine/tango-resources/context";
|
|
7
8
|
|
|
8
9
|
//#region src/mocks/aQueryResult.ts
|
|
@@ -14,25 +15,26 @@ function aQueryResult(overrides = {}) {
|
|
|
14
15
|
//#endregion
|
|
15
16
|
//#region src/mocks/aQueryExecutor.ts
|
|
16
17
|
function aQueryExecutor(overrides = {}) {
|
|
17
|
-
const
|
|
18
|
+
const adapter = overrides.adapter ?? anAdapter({ dialect: overrides.dialect ?? "postgres" });
|
|
18
19
|
const meta = overrides.meta ?? {
|
|
19
20
|
table: "mock_table",
|
|
20
21
|
pk: "id",
|
|
21
22
|
columns: {}
|
|
22
23
|
};
|
|
23
|
-
const run = overrides.run ?? vi$
|
|
24
|
+
const run = overrides.run ?? vi$3.fn(async () => []);
|
|
24
25
|
const client = aDBClient(overrides.query ? { query: overrides.query } : {});
|
|
25
26
|
return {
|
|
26
27
|
meta,
|
|
27
28
|
client,
|
|
28
|
-
|
|
29
|
-
run
|
|
29
|
+
adapter,
|
|
30
|
+
run,
|
|
31
|
+
...overrides.attachPersistedRecordAccessors ? { attachPersistedRecordAccessors: overrides.attachPersistedRecordAccessors } : {}
|
|
30
32
|
};
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
//#endregion
|
|
34
|
-
//#region src/mocks/
|
|
35
|
-
function
|
|
36
|
+
//#region src/mocks/aModelQuerySet.ts
|
|
37
|
+
function aModelQuerySet(overrides = {}) {
|
|
36
38
|
const queryset = new QuerySetClass(aQueryExecutor());
|
|
37
39
|
const filterImpl = overrides.filter ?? ((_input) => queryset);
|
|
38
40
|
const excludeImpl = overrides.exclude ?? ((_input) => queryset);
|
|
@@ -47,18 +49,18 @@ function aQuerySet(overrides = {}) {
|
|
|
47
49
|
const fetchOneImpl = overrides.fetchOne ?? (async (_shape) => null);
|
|
48
50
|
const countImpl = overrides.count ?? (async () => 0);
|
|
49
51
|
const existsImpl = overrides.exists ?? (async () => false);
|
|
50
|
-
queryset.filter = vi$
|
|
51
|
-
queryset.exclude = vi$
|
|
52
|
-
queryset.orderBy = vi$
|
|
53
|
-
queryset.limit = vi$
|
|
54
|
-
queryset.offset = vi$
|
|
55
|
-
queryset.select = vi$
|
|
56
|
-
queryset.selectRelated = vi$
|
|
57
|
-
queryset.prefetchRelated = vi$
|
|
58
|
-
queryset.fetch = vi$
|
|
59
|
-
queryset.fetchOne = vi$
|
|
60
|
-
queryset.count = vi$
|
|
61
|
-
queryset.exists = vi$
|
|
52
|
+
queryset.filter = vi$2.fn((input) => filterImpl(input));
|
|
53
|
+
queryset.exclude = vi$2.fn((input) => excludeImpl(input));
|
|
54
|
+
queryset.orderBy = vi$2.fn((...tokens) => orderByImpl(...tokens));
|
|
55
|
+
queryset.limit = vi$2.fn((n) => limitImpl(n));
|
|
56
|
+
queryset.offset = vi$2.fn((n) => offsetImpl(n));
|
|
57
|
+
queryset.select = vi$2.fn((cols) => selectImpl(cols));
|
|
58
|
+
queryset.selectRelated = vi$2.fn((...rels) => selectRelatedImpl(...rels));
|
|
59
|
+
queryset.prefetchRelated = vi$2.fn((...rels) => prefetchRelatedImpl(...rels));
|
|
60
|
+
queryset.fetch = vi$2.fn(fetchImpl);
|
|
61
|
+
queryset.fetchOne = vi$2.fn(fetchOneImpl);
|
|
62
|
+
queryset.count = vi$2.fn(() => countImpl());
|
|
63
|
+
queryset.exists = vi$2.fn(() => existsImpl());
|
|
62
64
|
return queryset;
|
|
63
65
|
}
|
|
64
66
|
|
|
@@ -70,7 +72,7 @@ function aManager(overrides = {}) {
|
|
|
70
72
|
pk: "id",
|
|
71
73
|
columns: {}
|
|
72
74
|
};
|
|
73
|
-
const querySet = overrides.querySet ??
|
|
75
|
+
const querySet = overrides.querySet ?? aModelQuerySet();
|
|
74
76
|
const queryImpl = overrides.query ?? (() => querySet);
|
|
75
77
|
const allImpl = overrides.all ?? (() => queryImpl());
|
|
76
78
|
const findByIdImpl = overrides.findById ?? (async () => null);
|
|
@@ -94,19 +96,68 @@ function aManager(overrides = {}) {
|
|
|
94
96
|
}));
|
|
95
97
|
return {
|
|
96
98
|
meta,
|
|
97
|
-
query: vi.fn(() => queryImpl()),
|
|
98
|
-
all: vi.fn(() => allImpl()),
|
|
99
|
-
getOrCreate: vi.fn((args) => getOrCreateImpl(args)),
|
|
100
|
-
updateOrCreate: vi.fn((args) => updateOrCreateImpl(args)),
|
|
101
|
-
findById: vi.fn((id) => findByIdImpl(id)),
|
|
102
|
-
getOrThrow: vi.fn((id) => getOrThrowImpl(id)),
|
|
103
|
-
create: vi.fn((input) => createImpl(input)),
|
|
104
|
-
update: vi.fn((id, patch) => updateImpl(id, patch)),
|
|
105
|
-
delete: vi.fn((id) => deleteImpl(id)),
|
|
106
|
-
bulkCreate: vi.fn((inputs) => bulkCreateImpl(inputs))
|
|
99
|
+
query: vi$1.fn(() => queryImpl()),
|
|
100
|
+
all: vi$1.fn(() => allImpl()),
|
|
101
|
+
getOrCreate: vi$1.fn((args) => getOrCreateImpl(args)),
|
|
102
|
+
updateOrCreate: vi$1.fn((args) => updateOrCreateImpl(args)),
|
|
103
|
+
findById: vi$1.fn((id) => findByIdImpl(id)),
|
|
104
|
+
getOrThrow: vi$1.fn((id) => getOrThrowImpl(id)),
|
|
105
|
+
create: vi$1.fn((input) => createImpl(input)),
|
|
106
|
+
update: vi$1.fn((id, patch) => updateImpl(id, patch)),
|
|
107
|
+
delete: vi$1.fn((id) => deleteImpl(id)),
|
|
108
|
+
bulkCreate: vi$1.fn((inputs) => bulkCreateImpl(inputs))
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
//#endregion
|
|
113
|
+
//#region src/mocks/aManyToManyRelatedManager.ts
|
|
114
|
+
function aManyToManyRelatedManager(overrides = {}) {
|
|
115
|
+
const insertLink = vi.fn(overrides.insertLink ?? (async () => {}));
|
|
116
|
+
const insertLinks = vi.fn(overrides.insertLinks ?? (async () => {}));
|
|
117
|
+
const deleteLink = vi.fn(overrides.deleteLink ?? (async () => {}));
|
|
118
|
+
const deleteLinks = vi.fn(overrides.deleteLinks ?? (async () => {}));
|
|
119
|
+
const selectTargetIdsForOwner = vi.fn(overrides.selectTargetIdsForOwner ?? (async () => []));
|
|
120
|
+
const runAtomicImpl = overrides.runAtomic ?? (async (work) => work());
|
|
121
|
+
const runAtomicSpy = vi.fn(async (work) => runAtomicImpl(work));
|
|
122
|
+
const throughTableManager = {
|
|
123
|
+
insertLink,
|
|
124
|
+
insertLinks,
|
|
125
|
+
deleteLink,
|
|
126
|
+
deleteLinks,
|
|
127
|
+
selectTargetIdsForOwner
|
|
128
|
+
};
|
|
129
|
+
const manager = new ManyToManyRelatedManager({
|
|
130
|
+
ownerPrimaryKey: overrides.ownerPrimaryKey ?? 7,
|
|
131
|
+
relationName: overrides.relationName ?? "tags",
|
|
132
|
+
ownerModelLabel: overrides.ownerModelLabel ?? "Post",
|
|
133
|
+
targetPrimaryKeyField: overrides.targetPrimaryKeyField ?? "id",
|
|
134
|
+
throughTableManager,
|
|
135
|
+
targetExecutorProvider: () => overrides.targetExecutor === undefined ? {} : overrides.targetExecutor,
|
|
136
|
+
runAtomic: runAtomicSpy
|
|
137
|
+
});
|
|
138
|
+
return {
|
|
139
|
+
manager,
|
|
140
|
+
insertLink,
|
|
141
|
+
insertLinks,
|
|
142
|
+
deleteLink,
|
|
143
|
+
deleteLinks,
|
|
144
|
+
selectTargetIdsForOwner,
|
|
145
|
+
runAtomic: runAtomicSpy
|
|
107
146
|
};
|
|
108
147
|
}
|
|
109
148
|
|
|
149
|
+
//#endregion
|
|
150
|
+
//#region src/mocks/aQuerySet.ts
|
|
151
|
+
const logger = getLogger("tango.testing.mocks");
|
|
152
|
+
let hasWarned = false;
|
|
153
|
+
function aQuerySet(overrides = {}) {
|
|
154
|
+
if (!hasWarned) {
|
|
155
|
+
hasWarned = true;
|
|
156
|
+
logger.warn("`aQuerySet(...)` is deprecated. Use `aModelQuerySet(...)` instead.");
|
|
157
|
+
}
|
|
158
|
+
return aModelQuerySet(overrides);
|
|
159
|
+
}
|
|
160
|
+
|
|
110
161
|
//#endregion
|
|
111
162
|
//#region src/mocks/aRequestContext.ts
|
|
112
163
|
function aRequestContext(optionsOrMethod = {}, urlArg, bodyArg) {
|
|
@@ -171,13 +222,16 @@ var mocks_exports = {};
|
|
|
171
222
|
__export(mocks_exports, {
|
|
172
223
|
aDBClient: () => aDBClient,
|
|
173
224
|
aManager: () => aManager,
|
|
225
|
+
aManyToManyRelatedManager: () => aManyToManyRelatedManager,
|
|
226
|
+
aModelQuerySet: () => aModelQuerySet,
|
|
174
227
|
aQueryExecutor: () => aQueryExecutor,
|
|
175
228
|
aQueryResult: () => aQueryResult,
|
|
176
229
|
aQuerySet: () => aQuerySet,
|
|
177
230
|
aRelationMeta: () => aRelationMeta,
|
|
178
|
-
aRequestContext: () => aRequestContext
|
|
231
|
+
aRequestContext: () => aRequestContext,
|
|
232
|
+
anAdapter: () => anAdapter
|
|
179
233
|
});
|
|
180
234
|
|
|
181
235
|
//#endregion
|
|
182
|
-
export { aManager, aQueryExecutor, aQueryResult, aQuerySet, aRelationMeta, aRequestContext, mocks_exports };
|
|
183
|
-
//# sourceMappingURL=mocks-
|
|
236
|
+
export { aManager, aManyToManyRelatedManager, aModelQuerySet, aQueryExecutor, aQueryResult, aQuerySet, aRelationMeta, aRequestContext, mocks_exports };
|
|
237
|
+
//# sourceMappingURL=mocks-iKVkzHJZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mocks-iKVkzHJZ.js","names":["overrides: AQueryResultOverrides<TModel>","overrides: QueryExecutorOverrides<TModel>","meta: TableMeta","overrides: Partial<QuerySet<TModel, TResult>>","filterImpl: QuerySet<TModel, TResult>['filter']","excludeImpl: QuerySet<TModel, TResult>['exclude']","orderByImpl: QuerySet<TModel, TResult>['orderBy']","limitImpl: QuerySet<TModel, TResult>['limit']","offsetImpl: QuerySet<TModel, TResult>['offset']","_cols: readonly (keyof TModel)[]","fetchImpl: QuerySet<TModel, TResult>['fetch']","_shape?: ((r: TResult) => Out) | { parse: (r: TResult) => Out }","fetchOneImpl: QuerySet<TModel, TResult>['fetchOne']","countImpl: QuerySet<TModel, TResult>['count']","existsImpl: QuerySet<TModel, TResult>['exists']","input: Parameters<QuerySet<TModel, TResult>['filter']>[0]","input: Parameters<QuerySet<TModel, TResult>['exclude']>[0]","n: number","cols: Parameters<QuerySet<TModel, TResult>['select']>[0]","overrides: ManagerOverrides<TModel>","id: ModelId","input: Partial<TModel>","_id: ModelId","patch: Partial<TModel>","inputs: Partial<TModel>[]","args: Parameters<ManagerLike<TModel>['getOrCreate']>[0]","args: Parameters<ManagerLike<TModel>['updateOrCreate']>[0]","overrides: ManyToManyRelatedManagerFixtureOverrides<TTarget>","runAtomicImpl: ManagerConstructorInputs['runAtomic']","work: () => Promise<T>","overrides: Partial<QuerySet<TModel, TResult>>","optionsOrMethod: RequestContextFixtureOptions<TUser, TContext> | string","urlArg?: string","bodyArg?: unknown","resolvedOptions: RequestContextFixtureOptions<TUser, TContext>","resolvedHeaders: HeadersInit | undefined","req: Request","currentUser: TUser | null","input: RelationMetaInput"],"sources":["../src/mocks/aQueryResult.ts","../src/mocks/aQueryExecutor.ts","../src/mocks/aModelQuerySet.ts","../src/mocks/aManager.ts","../src/mocks/aManyToManyRelatedManager.ts","../src/mocks/aQuerySet.ts","../src/mocks/aRequestContext.ts","../src/mocks/aRelationMeta.ts","../src/mocks/index.ts"],"sourcesContent":["import type { QueryResult } from '@danceroutine/tango-orm/query';\nimport { QueryResult as QueryResultClass } from '@danceroutine/tango-orm/query';\n\nexport type AQueryResultOverrides<TModel> = {\n items?: readonly TModel[];\n results?: readonly TModel[];\n};\n\n/**\n * Create a query-result test value with optional overrides.\n */\nexport function aQueryResult<TModel>(overrides: AQueryResultOverrides<TModel> = {}): QueryResult<TModel> {\n const items = overrides.items ?? overrides.results ?? ([] as TModel[]);\n return new QueryResultClass(items);\n}\n","import { vi } from 'vitest';\nimport type { Adapter, QueryExecutor } from '@danceroutine/tango-orm';\nimport type { Dialect, TableMeta } from '@danceroutine/tango-orm/query';\nimport { anAdapter } from './anAdapter';\nimport { aDBClient } from './aDBClient';\n\nexport type QueryExecutorOverrides<TModel extends Record<string, unknown>> = {\n dialect?: Dialect;\n adapter?: Adapter;\n meta?: TableMeta;\n query?: (sql: string, params?: readonly unknown[]) => Promise<{ rows: unknown[] }>;\n run?: QueryExecutor<TModel>['run'];\n attachPersistedRecordAccessors?: QueryExecutor<TModel>['attachPersistedRecordAccessors'];\n};\n\n/**\n * Create a minimal `QueryExecutor` test double for `QuerySet` tests.\n */\nexport function aQueryExecutor<TModel extends Record<string, unknown>>(\n overrides: QueryExecutorOverrides<TModel> = {}\n): QueryExecutor<TModel> {\n const adapter = overrides.adapter ?? anAdapter({ dialect: overrides.dialect ?? 'postgres' });\n const meta: TableMeta = overrides.meta ?? { table: 'mock_table', pk: 'id', columns: {} };\n const run = overrides.run ?? vi.fn(async () => [] as TModel[]);\n const client = aDBClient(overrides.query ? { query: overrides.query } : {});\n\n return {\n meta,\n client,\n adapter,\n run,\n ...(overrides.attachPersistedRecordAccessors\n ? { attachPersistedRecordAccessors: overrides.attachPersistedRecordAccessors }\n : {}),\n };\n}\n","import { vi } from 'vitest';\nimport type { QuerySet } from '@danceroutine/tango-orm';\nimport { ModelQuerySet as QuerySetClass } from '@danceroutine/tango-orm';\nimport { aQueryResult } from './aQueryResult';\nimport { aQueryExecutor } from './aQueryExecutor';\n\n/**\n * Create a chainable model-queryset test double with optional behavior overrides.\n *\n * All methods are wrapped in `vi.fn()` so they can be asserted on directly\n * without an additional `vi.mocked()` call.\n */\nexport function aModelQuerySet<\n TModel extends Record<string, unknown>,\n TResult extends Record<string, unknown> = TModel,\n>(overrides: Partial<QuerySet<TModel, TResult>> = {}): QuerySet<TModel, TResult> {\n const queryset = new QuerySetClass<TModel, TResult>(aQueryExecutor<TModel>());\n const filterImpl: QuerySet<TModel, TResult>['filter'] = overrides.filter ?? ((_input) => queryset);\n const excludeImpl: QuerySet<TModel, TResult>['exclude'] = overrides.exclude ?? ((_input) => queryset);\n const orderByImpl: QuerySet<TModel, TResult>['orderBy'] = overrides.orderBy ?? ((..._tokens) => queryset);\n const limitImpl: QuerySet<TModel, TResult>['limit'] = overrides.limit ?? ((_n) => queryset);\n const offsetImpl: QuerySet<TModel, TResult>['offset'] = overrides.offset ?? ((_n) => queryset);\n const defaultSelect = ((_cols: readonly (keyof TModel)[]) => queryset) as unknown as QuerySet<\n TModel,\n TResult\n >['select'];\n const selectImpl = overrides.select ?? defaultSelect;\n const selectRelatedImpl =\n (overrides.selectRelated as ((...rels: readonly string[]) => QuerySet<TModel, TResult>) | undefined) ??\n ((..._rels: readonly string[]) => queryset);\n const prefetchRelatedImpl =\n (overrides.prefetchRelated as ((...rels: readonly string[]) => QuerySet<TModel, TResult>) | undefined) ??\n ((..._rels: readonly string[]) => queryset);\n const fetchImpl: QuerySet<TModel, TResult>['fetch'] =\n overrides.fetch ??\n (async <Out = TResult>(_shape?: ((r: TResult) => Out) | { parse: (r: TResult) => Out }) => aQueryResult<Out>());\n const fetchOneImpl: QuerySet<TModel, TResult>['fetchOne'] =\n overrides.fetchOne ??\n (async <Out = TResult>(_shape?: ((r: TResult) => Out) | { parse: (r: TResult) => Out }) => null as Out | null);\n const countImpl: QuerySet<TModel, TResult>['count'] = overrides.count ?? (async () => 0);\n const existsImpl: QuerySet<TModel, TResult>['exists'] = overrides.exists ?? (async () => false);\n\n queryset.filter = vi.fn((input: Parameters<QuerySet<TModel, TResult>['filter']>[0]) =>\n filterImpl(input)\n ) as QuerySet<TModel, TResult>['filter'];\n queryset.exclude = vi.fn((input: Parameters<QuerySet<TModel, TResult>['exclude']>[0]) =>\n excludeImpl(input)\n ) as QuerySet<TModel, TResult>['exclude'];\n queryset.orderBy = vi.fn((...tokens: Parameters<QuerySet<TModel, TResult>['orderBy']>) =>\n orderByImpl(...tokens)\n ) as QuerySet<TModel, TResult>['orderBy'];\n queryset.limit = vi.fn((n: number) => limitImpl(n)) as QuerySet<TModel, TResult>['limit'];\n queryset.offset = vi.fn((n: number) => offsetImpl(n)) as QuerySet<TModel, TResult>['offset'];\n queryset.select = vi.fn((cols: Parameters<QuerySet<TModel, TResult>['select']>[0]) =>\n selectImpl(cols)\n ) as unknown as QuerySet<TModel, TResult>['select'];\n queryset.selectRelated = vi.fn((...rels: readonly string[]) => selectRelatedImpl(...rels)) as unknown as QuerySet<\n TModel,\n TResult\n >['selectRelated'];\n queryset.prefetchRelated = vi.fn((...rels: readonly string[]) =>\n prefetchRelatedImpl(...rels)\n ) as unknown as QuerySet<TModel, TResult>['prefetchRelated'];\n queryset.fetch = vi.fn(fetchImpl) as unknown as QuerySet<TModel, TResult>['fetch'];\n queryset.fetchOne = vi.fn(fetchOneImpl) as unknown as QuerySet<TModel, TResult>['fetchOne'];\n queryset.count = vi.fn(() => countImpl()) as QuerySet<TModel, TResult>['count'];\n queryset.exists = vi.fn(() => existsImpl()) as QuerySet<TModel, TResult>['exists'];\n\n return queryset;\n}\n","import { vi } from 'vitest';\nimport type { ManagerLike, QuerySet } from '@danceroutine/tango-orm';\nimport type { TableMeta } from '@danceroutine/tango-orm/query';\nimport { aModelQuerySet } from './aModelQuerySet';\n\nexport type ManagerOverrides<TModel extends Record<string, unknown>> = {\n meta?: TableMeta;\n querySet?: QuerySet<TModel>;\n query?: ManagerLike<TModel>['query'];\n all?: ManagerLike<TModel>['all'];\n getOrCreate?: ManagerLike<TModel>['getOrCreate'];\n updateOrCreate?: ManagerLike<TModel>['updateOrCreate'];\n findById?: ManagerLike<TModel>['findById'];\n getOrThrow?: ManagerLike<TModel>['getOrThrow'];\n create?: ManagerLike<TModel>['create'];\n update?: ManagerLike<TModel>['update'];\n delete?: ManagerLike<TModel>['delete'];\n bulkCreate?: ManagerLike<TModel>['bulkCreate'];\n};\n\n/**\n * Create a manager-shaped test double for resource and service tests.\n */\nexport function aManager<TModel extends Record<string, unknown>>(\n overrides: ManagerOverrides<TModel> = {}\n): ManagerLike<TModel> {\n const meta = overrides.meta ?? { table: 'mock_table', pk: 'id', columns: {} };\n const querySet = overrides.querySet ?? aModelQuerySet<TModel>();\n type ModelId = TModel[keyof TModel];\n\n const queryImpl = overrides.query ?? (() => querySet);\n const allImpl = overrides.all ?? (() => queryImpl());\n const findByIdImpl = overrides.findById ?? (async () => null as TModel | null);\n const getOrThrowImpl =\n overrides.getOrThrow ??\n (async (id: ModelId) => {\n const record = await findByIdImpl(id);\n if (!record) {\n throw new Error(`No ${meta.table} record found for ${String(meta.pk)}=${String(id)}.`);\n }\n return record;\n });\n const createImpl = overrides.create ?? (async (input: Partial<TModel>) => input as TModel);\n const updateImpl = overrides.update ?? (async (_id: ModelId, patch: Partial<TModel>) => patch as TModel);\n const deleteImpl = overrides.delete ?? (async (_id: ModelId) => {});\n const bulkCreateImpl = overrides.bulkCreate ?? (async (inputs: Partial<TModel>[]) => inputs as TModel[]);\n const getOrCreateImpl =\n overrides.getOrCreate ??\n (async ({ defaults }: Parameters<ManagerLike<TModel>['getOrCreate']>[0]) => ({\n record: { ...defaults } as TModel,\n created: true,\n }));\n const updateOrCreateImpl =\n overrides.updateOrCreate ??\n (async ({ defaults }: Parameters<ManagerLike<TModel>['updateOrCreate']>[0]) => ({\n record: { ...defaults } as TModel,\n created: true,\n updated: false,\n }));\n\n return {\n meta,\n query: vi.fn(() => queryImpl()),\n all: vi.fn(() => allImpl()),\n getOrCreate: vi.fn((args: Parameters<ManagerLike<TModel>['getOrCreate']>[0]) => getOrCreateImpl(args)),\n updateOrCreate: vi.fn((args: Parameters<ManagerLike<TModel>['updateOrCreate']>[0]) => updateOrCreateImpl(args)),\n findById: vi.fn((id: ModelId) => findByIdImpl(id)),\n getOrThrow: vi.fn((id: ModelId) => getOrThrowImpl(id)),\n create: vi.fn((input: Partial<TModel>) => createImpl(input)),\n update: vi.fn((id: ModelId, patch: Partial<TModel>) => updateImpl(id, patch)),\n delete: vi.fn((id: ModelId) => deleteImpl(id)),\n bulkCreate: vi.fn((inputs: Partial<TModel>[]) => bulkCreateImpl(inputs)),\n };\n}\n","import { vi } from 'vitest';\nimport type { QueryExecutor } from '@danceroutine/tango-orm';\nimport { ManyToManyRelatedManager } from '@danceroutine/tango-orm';\n\ntype ManagerConstructorInputs = ConstructorParameters<typeof ManyToManyRelatedManager>[0];\n\n/**\n * Overrides accepted by the {@link aManyToManyRelatedManager} fixture. All\n * fields are optional; defaults produce a manager bound to owner primary key\n * `7`, relation name `'tags'`, owner label `'Post'`, and target primary-key\n * field `'id'`.\n */\nexport interface ManyToManyRelatedManagerFixtureOverrides<TTarget extends Record<string, unknown>> {\n ownerPrimaryKey?: unknown;\n relationName?: string;\n ownerModelLabel?: string;\n targetPrimaryKeyField?: string;\n /**\n * Resolver returned by the manager's `targetExecutorProvider`. Pass `null`\n * to simulate the registry not yet knowing about the target model; omit to\n * return a minimal executor stub.\n */\n targetExecutor?: QueryExecutor<TTarget> | null;\n insertLink?: (ownerPrimaryKey: unknown, targetPrimaryKey: unknown) => Promise<void>;\n insertLinks?: (ownerPrimaryKey: unknown, targetPrimaryKeys: readonly unknown[]) => Promise<void>;\n deleteLink?: (ownerPrimaryKey: unknown, targetPrimaryKey: unknown) => Promise<void>;\n deleteLinks?: (ownerPrimaryKey: unknown, targetPrimaryKeys: readonly unknown[]) => Promise<void>;\n selectTargetIdsForOwner?: (ownerPrimaryKey: unknown) => Promise<readonly (string | number)[]>;\n runAtomic?: <T>(work: () => Promise<T>) => Promise<T>;\n}\n\n/**\n * Return shape of the {@link aManyToManyRelatedManager} fixture. The spy\n * references are returned alongside the manager so tests can assert against\n * the join-table calls without reaching into private fields.\n */\nexport interface ManyToManyRelatedManagerFixture<TTarget extends Record<string, unknown>> {\n manager: ManyToManyRelatedManager<TTarget>;\n insertLink: ReturnType<typeof vi.fn>;\n insertLinks: ReturnType<typeof vi.fn>;\n deleteLink: ReturnType<typeof vi.fn>;\n deleteLinks: ReturnType<typeof vi.fn>;\n selectTargetIdsForOwner: ReturnType<typeof vi.fn>;\n runAtomic: ReturnType<typeof vi.fn>;\n}\n\n/**\n * Build a {@link ManyToManyRelatedManager} instance wired to lightweight spies\n * for its underlying join-table operations. The fixture skips the\n * relation-metadata and registry plumbing exercised by `ModelManager`, so\n * tests can focus on manager behavior in isolation.\n */\nexport function aManyToManyRelatedManager<TTarget extends Record<string, unknown>>(\n overrides: ManyToManyRelatedManagerFixtureOverrides<TTarget> = {}\n): ManyToManyRelatedManagerFixture<TTarget> {\n const insertLink = vi.fn(overrides.insertLink ?? (async () => {}));\n const insertLinks = vi.fn(overrides.insertLinks ?? (async () => {}));\n const deleteLink = vi.fn(overrides.deleteLink ?? (async () => {}));\n const deleteLinks = vi.fn(overrides.deleteLinks ?? (async () => {}));\n const selectTargetIdsForOwner = vi.fn(\n overrides.selectTargetIdsForOwner ?? (async (): Promise<readonly (string | number)[]> => [])\n );\n const runAtomicImpl: ManagerConstructorInputs['runAtomic'] =\n overrides.runAtomic ?? (async <T>(work: () => Promise<T>) => work());\n const runAtomicSpy = vi.fn(async <T>(work: () => Promise<T>) => runAtomicImpl(work));\n\n const throughTableManager = {\n insertLink,\n insertLinks,\n deleteLink,\n deleteLinks,\n selectTargetIdsForOwner,\n } as unknown as ManagerConstructorInputs['throughTableManager'];\n\n const manager = new ManyToManyRelatedManager<TTarget>({\n ownerPrimaryKey: overrides.ownerPrimaryKey ?? 7,\n relationName: overrides.relationName ?? 'tags',\n ownerModelLabel: overrides.ownerModelLabel ?? 'Post',\n targetPrimaryKeyField: overrides.targetPrimaryKeyField ?? 'id',\n throughTableManager,\n targetExecutorProvider: () =>\n overrides.targetExecutor === undefined ? ({} as QueryExecutor<TTarget>) : overrides.targetExecutor,\n runAtomic: runAtomicSpy as ManagerConstructorInputs['runAtomic'],\n });\n\n return {\n manager,\n insertLink,\n insertLinks,\n deleteLink,\n deleteLinks,\n selectTargetIdsForOwner,\n runAtomic: runAtomicSpy,\n };\n}\n","import { getLogger } from '@danceroutine/tango-core';\nimport type { QuerySet } from '@danceroutine/tango-orm';\nimport { aModelQuerySet } from './aModelQuerySet';\n\nconst logger = getLogger('tango.testing.mocks');\n// TODO this pattern keeps showing up, perhaps we should implement logger.warnOnce\nlet hasWarned = false;\n\n/**\n * @deprecated Use `aModelQuerySet(...)` instead.\n */\nexport function aQuerySet<TModel extends Record<string, unknown>, TResult extends Record<string, unknown> = TModel>(\n overrides: Partial<QuerySet<TModel, TResult>> = {}\n): QuerySet<TModel, TResult> {\n if (!hasWarned) {\n hasWarned = true;\n logger.warn('`aQuerySet(...)` is deprecated. Use `aModelQuerySet(...)` instead.');\n }\n\n return aModelQuerySet(overrides);\n}\n","// Import through the package subpath so fixtures stay aligned with the public nominal type.\nimport { RequestContext, type BaseUser } from '@danceroutine/tango-resources/context';\n\ntype RequestContextFactory<TUser, TContext extends RequestContextLike<TUser>> = (\n request: Request,\n user: TUser | null\n) => TContext;\n\ntype RequestContextLike<TUser> = {\n request: Request;\n user: TUser | null;\n params: Record<string, string>;\n};\n\nexport type RequestContextFixtureOptions<\n TUser = BaseUser,\n TContext extends RequestContextLike<TUser> = RequestContext<TUser>,\n> = {\n method?: string;\n url?: string;\n body?: unknown;\n user?: TUser | null;\n params?: Record<string, string>;\n headers?: HeadersInit;\n contextFactory?: RequestContextFactory<TUser, TContext>;\n};\n\n/**\n * Create a RequestContext fixture with optional method/url/body/user/params.\n */\nexport function aRequestContext<TUser = BaseUser, TContext extends RequestContextLike<TUser> = RequestContext<TUser>>(\n method: string,\n url: string,\n body?: unknown\n): TContext;\nexport function aRequestContext<TUser = BaseUser, TContext extends RequestContextLike<TUser> = RequestContext<TUser>>(\n options?: RequestContextFixtureOptions<TUser, TContext>\n): TContext;\nexport function aRequestContext<TUser = BaseUser, TContext extends RequestContextLike<TUser> = RequestContext<TUser>>(\n optionsOrMethod: RequestContextFixtureOptions<TUser, TContext> | string = {},\n urlArg?: string,\n bodyArg?: unknown\n): TContext {\n const resolvedOptions: RequestContextFixtureOptions<TUser, TContext> =\n typeof optionsOrMethod === 'string'\n ? {\n method: optionsOrMethod,\n url: urlArg,\n body: bodyArg,\n }\n : optionsOrMethod;\n const {\n method = 'GET',\n url = 'https://example.test',\n body,\n user = null,\n params = {},\n headers,\n contextFactory,\n } = resolvedOptions;\n\n const resolvedHeaders: HeadersInit | undefined =\n body === undefined ? headers : { 'content-type': 'application/json', ...headers };\n\n const request = new Request(url, {\n method,\n headers: resolvedHeaders,\n body: body === undefined ? undefined : JSON.stringify(body),\n });\n const createContext =\n contextFactory ?? ((req: Request, currentUser: TUser | null) => RequestContext.create<TUser>(req, currentUser));\n const context = createContext(request, user) as TContext;\n context.params = params;\n return context;\n}\n","import type { RelationMeta, TableMeta } from '@danceroutine/tango-orm/query';\n\ntype RelationMetaInput = {\n kind: RelationMeta['kind'];\n table: string;\n sourceKey: string;\n targetKey: string;\n targetColumns: Record<string, string>;\n alias: string;\n edgeId?: string;\n sourceModelKey?: string;\n targetModelKey?: string;\n targetPrimaryKey?: string;\n targetMeta?: TableMeta;\n capabilities?: Partial<RelationMeta['capabilities']>;\n};\n\n/**\n * Build recursive relation metadata fixtures for planner/compiler/query tests.\n */\nexport function aRelationMeta(input: RelationMetaInput): RelationMeta {\n const cardinality = input.kind === 'belongsTo' || input.kind === 'hasOne' ? 'single' : 'many';\n const defaultHydratable = input.kind !== ('manyToMany' as RelationMeta['kind']);\n\n const targetMeta =\n input.targetMeta ??\n ({\n modelKey: input.targetModelKey ?? `${input.alias}:target`,\n table: input.table,\n pk: input.targetPrimaryKey ?? 'id',\n columns: input.targetColumns,\n } satisfies TableMeta);\n\n return {\n edgeId: input.edgeId ?? `${input.alias}:${input.sourceKey}:${input.targetKey}`,\n sourceModelKey: input.sourceModelKey ?? `${input.alias}:source`,\n targetModelKey: input.targetModelKey ?? `${input.alias}:target`,\n kind: input.kind,\n cardinality,\n capabilities: {\n queryable: true,\n hydratable: defaultHydratable,\n joinable: cardinality === 'single' && defaultHydratable,\n prefetchable: defaultHydratable,\n ...input.capabilities,\n },\n table: input.table,\n sourceKey: input.sourceKey,\n targetKey: input.targetKey,\n targetPrimaryKey: input.targetPrimaryKey ?? 'id',\n targetColumns: input.targetColumns,\n alias: input.alias,\n targetMeta,\n };\n}\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport { anAdapter } from './anAdapter';\nexport { aDBClient } from './aDBClient';\nexport { aManager } from './aManager';\nexport { aManyToManyRelatedManager } from './aManyToManyRelatedManager';\nexport { aModelQuerySet } from './aModelQuerySet';\nexport { aQueryResult } from './aQueryResult';\nexport { aQuerySet } from './aQuerySet';\nexport { aRequestContext } from './aRequestContext';\nexport { aQueryExecutor } from './aQueryExecutor';\nexport { aRelationMeta } from './aRelationMeta';\nexport type { AdapterOverrides } from './anAdapter';\nexport type { QueryExecutorOverrides } from './aQueryExecutor';\nexport type { ManagerOverrides } from './aManager';\nexport type {\n ManyToManyRelatedManagerFixture,\n ManyToManyRelatedManagerFixtureOverrides,\n} from './aManyToManyRelatedManager';\nexport type { DBClient } from './DBClient';\nexport type { MockQuerySetResult } from './MockQuerySetResult';\nexport type { RequestContextFixtureOptions } from './aRequestContext';\n"],"mappings":"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"}
|
package/dist/vitest/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import "../aDBClient-
|
|
2
|
-
import "../integration-
|
|
3
|
-
import { withGlobalTestApi } from "../vitest-
|
|
1
|
+
import "../aDBClient-CH-ZcOaP.js";
|
|
2
|
+
import "../integration-CaRF_lF_.js";
|
|
3
|
+
import { withGlobalTestApi } from "../vitest-BqZmULOa.js";
|
|
4
4
|
|
|
5
5
|
export { withGlobalTestApi };
|
|
@@ -19,6 +19,13 @@ export interface TangoVitestHelpers {
|
|
|
19
19
|
}>;
|
|
20
20
|
introspectSchema(harness?: IntegrationHarness): Promise<unknown>;
|
|
21
21
|
seedTable<T extends Record<string, unknown>>(table: string, rows: T[], harness?: IntegrationHarness): Promise<void>;
|
|
22
|
+
createModelQuerySetFixture<TModel extends Record<string, unknown>>(options: {
|
|
23
|
+
meta: import('@danceroutine/tango-orm/query').TableMeta;
|
|
24
|
+
harness?: IntegrationHarness;
|
|
25
|
+
}): import('@danceroutine/tango-orm').QuerySet<TModel>;
|
|
26
|
+
/**
|
|
27
|
+
* @deprecated Use `vi.tango.createModelQuerySetFixture(...)` instead.
|
|
28
|
+
*/
|
|
22
29
|
createQuerySetFixture<TModel extends Record<string, unknown>>(options: {
|
|
23
30
|
meta: import('@danceroutine/tango-orm/query').TableMeta;
|
|
24
31
|
harness?: IntegrationHarness;
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { __export } from "./chunk-BkvOhyD0.js";
|
|
2
|
-
import { TestHarness, applyAndVerifyMigrations, assertMigrationPlan,
|
|
2
|
+
import { TestHarness, applyAndVerifyMigrations, assertMigrationPlan, createModelQuerySetFixture, expectQueryResult, introspectSchema, seedTable } from "./integration-CaRF_lF_.js";
|
|
3
3
|
import { expect, vi } from "vitest";
|
|
4
|
+
import { getLogger } from "@danceroutine/tango-core";
|
|
4
5
|
|
|
5
6
|
//#region src/vitest/registerVitestTango.ts
|
|
6
7
|
function isError(value) {
|
|
7
8
|
return typeof value === "object" && value !== null && typeof value.name === "string" && typeof value.message === "string";
|
|
8
9
|
}
|
|
9
10
|
let activeHarness = null;
|
|
11
|
+
const logger = getLogger("tango.testing.vitest");
|
|
12
|
+
let hasWarnedForCreateQuerySetFixture = false;
|
|
10
13
|
async function resolveHarness(input) {
|
|
11
14
|
if (typeof input === "function") return input();
|
|
12
15
|
return input;
|
|
@@ -46,8 +49,18 @@ const tangoHelpers = {
|
|
|
46
49
|
async seedTable(table, rows, harness) {
|
|
47
50
|
await seedTable(harness ?? tangoHelpers.getTestHarness(), table, rows);
|
|
48
51
|
},
|
|
52
|
+
createModelQuerySetFixture(options) {
|
|
53
|
+
return createModelQuerySetFixture({
|
|
54
|
+
harness: options.harness ?? tangoHelpers.getTestHarness(),
|
|
55
|
+
meta: options.meta
|
|
56
|
+
});
|
|
57
|
+
},
|
|
49
58
|
createQuerySetFixture(options) {
|
|
50
|
-
|
|
59
|
+
if (!hasWarnedForCreateQuerySetFixture) {
|
|
60
|
+
hasWarnedForCreateQuerySetFixture = true;
|
|
61
|
+
logger.warn("`vi.tango.createQuerySetFixture(...)` is deprecated. Use `vi.tango.createModelQuerySetFixture(...)` instead.");
|
|
62
|
+
}
|
|
63
|
+
return createModelQuerySetFixture({
|
|
51
64
|
harness: options.harness ?? tangoHelpers.getTestHarness(),
|
|
52
65
|
meta: options.meta
|
|
53
66
|
});
|
|
@@ -95,4 +108,4 @@ __export(vitest_exports, { withGlobalTestApi: () => withGlobalTestApi });
|
|
|
95
108
|
|
|
96
109
|
//#endregion
|
|
97
110
|
export { vitest_exports, withGlobalTestApi };
|
|
98
|
-
//# sourceMappingURL=vitest-
|
|
111
|
+
//# sourceMappingURL=vitest-BqZmULOa.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vitest-BqZmULOa.js","names":["value: unknown","activeHarness: IntegrationHarness | null","input: IntegrationHarness | (() => IntegrationHarness | Promise<IntegrationHarness>)","tangoHelpers: TangoVitestHelpers","harness?: IntegrationHarness","table: string","rows: T[]","options: {\n meta: import('@danceroutine/tango-orm/query').TableMeta;\n harness?: IntegrationHarness;\n }","actual: Promise<T> | T","expected: T","received: unknown","schema: Parseable","key: string","value: TValue","work: () => TResult | Promise<TResult>"],"sources":["../src/vitest/registerVitestTango.ts","../src/vitest/withGlobalTestApi.ts","../src/vitest/index.ts"],"sourcesContent":["/**\n * Vitest custom matchers and helpers for Tango.\n *\n * Import this module in your Vitest setup file:\n *\n * ```typescript\n * import '@danceroutine/tango-testing/vitest';\n * ```\n */\nimport { expect, vi } from 'vitest';\nimport { getLogger } from '@danceroutine/tango-core';\nimport {\n TestHarness,\n applyAndVerifyMigrations as applyAndVerifyMigrationsFn,\n assertMigrationPlan as assertMigrationPlanFn,\n createModelQuerySetFixture as createModelQuerySetFixtureFn,\n expectQueryResult as expectQueryResultFn,\n introspectSchema as introspectSchemaFn,\n seedTable as seedTableFn,\n type ApplyAndVerifyMigrationsOptions,\n type AssertMigrationPlanOptions,\n type HarnessStrategyRegistry,\n type IntegrationHarness,\n} from '../integration';\n\ninterface Parseable {\n parse(data: unknown): unknown;\n}\n\nfunction isError(value: unknown): value is Error {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as { name?: unknown }).name === 'string' &&\n typeof (value as { message?: unknown }).message === 'string'\n );\n}\n\nlet activeHarness: IntegrationHarness | null = null;\nconst logger = getLogger('tango.testing.vitest');\nlet hasWarnedForCreateQuerySetFixture = false;\n\nasync function resolveHarness(\n input: IntegrationHarness | (() => IntegrationHarness | Promise<IntegrationHarness>)\n): Promise<IntegrationHarness> {\n if (typeof input === 'function') {\n return input();\n }\n return input;\n}\n\nexport interface TangoVitestHelpers {\n useHarness(\n input: IntegrationHarness | (() => IntegrationHarness | Promise<IntegrationHarness>)\n ): Promise<IntegrationHarness>;\n getTestHarness(): IntegrationHarness;\n getRegistry(): HarnessStrategyRegistry;\n assertMigrationPlan(options: AssertMigrationPlanOptions & { harness?: IntegrationHarness }): Promise<string>;\n applyAndVerifyMigrations(\n options: ApplyAndVerifyMigrationsOptions & { harness?: IntegrationHarness }\n ): Promise<{ statuses: { id: string; applied: boolean }[] }>;\n introspectSchema(harness?: IntegrationHarness): Promise<unknown>;\n seedTable<T extends Record<string, unknown>>(table: string, rows: T[], harness?: IntegrationHarness): Promise<void>;\n createModelQuerySetFixture<TModel extends Record<string, unknown>>(options: {\n meta: import('@danceroutine/tango-orm/query').TableMeta;\n harness?: IntegrationHarness;\n }): import('@danceroutine/tango-orm').QuerySet<TModel>;\n /**\n * @deprecated Use `vi.tango.createModelQuerySetFixture(...)` instead.\n */\n createQuerySetFixture<TModel extends Record<string, unknown>>(options: {\n meta: import('@danceroutine/tango-orm/query').TableMeta;\n harness?: IntegrationHarness;\n }): import('@danceroutine/tango-orm').QuerySet<TModel>;\n expectQueryResult<T>(actual: Promise<T> | T, expected: T): Promise<void>;\n}\n\nconst tangoHelpers: TangoVitestHelpers = {\n async useHarness(input): Promise<IntegrationHarness> {\n const harness = await resolveHarness(input);\n await harness.setup();\n activeHarness = harness;\n return harness;\n },\n getTestHarness(): IntegrationHarness {\n if (!activeHarness) {\n throw new Error('No active test harness. Call vi.tango.useHarness(...) in beforeAll first.');\n }\n return activeHarness;\n },\n getRegistry(): HarnessStrategyRegistry {\n return TestHarness.getRegistry();\n },\n async assertMigrationPlan(options): Promise<string> {\n const harness = options.harness ?? tangoHelpers.getTestHarness();\n return assertMigrationPlanFn(harness, {\n migrationsDir: options.migrationsDir,\n expectSqlContains: options.expectSqlContains,\n });\n },\n async applyAndVerifyMigrations(options): Promise<{ statuses: { id: string; applied: boolean }[] }> {\n const harness = options.harness ?? tangoHelpers.getTestHarness();\n return applyAndVerifyMigrationsFn(harness, {\n migrationsDir: options.migrationsDir,\n toId: options.toId,\n expectedAppliedIds: options.expectedAppliedIds,\n });\n },\n async introspectSchema(harness?: IntegrationHarness): Promise<unknown> {\n return introspectSchemaFn(harness ?? tangoHelpers.getTestHarness());\n },\n async seedTable<T extends Record<string, unknown>>(\n table: string,\n rows: T[],\n harness?: IntegrationHarness\n ): Promise<void> {\n await seedTableFn(harness ?? tangoHelpers.getTestHarness(), table, rows);\n },\n createModelQuerySetFixture<TModel extends Record<string, unknown>>(options: {\n meta: import('@danceroutine/tango-orm/query').TableMeta;\n harness?: IntegrationHarness;\n }): import('@danceroutine/tango-orm').QuerySet<TModel> {\n return createModelQuerySetFixtureFn<TModel>({\n harness: options.harness ?? tangoHelpers.getTestHarness(),\n meta: options.meta,\n });\n },\n createQuerySetFixture<TModel extends Record<string, unknown>>(options: {\n meta: import('@danceroutine/tango-orm/query').TableMeta;\n harness?: IntegrationHarness;\n }): import('@danceroutine/tango-orm').QuerySet<TModel> {\n if (!hasWarnedForCreateQuerySetFixture) {\n hasWarnedForCreateQuerySetFixture = true;\n logger.warn(\n '`vi.tango.createQuerySetFixture(...)` is deprecated. Use `vi.tango.createModelQuerySetFixture(...)` instead.'\n );\n }\n return createModelQuerySetFixtureFn<TModel>({\n harness: options.harness ?? tangoHelpers.getTestHarness(),\n meta: options.meta,\n });\n },\n async expectQueryResult<T>(actual: Promise<T> | T, expected: T): Promise<void> {\n await expectQueryResultFn(actual, expected);\n },\n};\n\nexpect.extend({\n toMatchSchema(received: unknown, schema: Parseable) {\n try {\n schema.parse(received);\n return {\n pass: true,\n message: () => 'expected data not to match schema',\n };\n } catch (error) {\n const detail = isError(error) ? error.message : String(error);\n return {\n pass: false,\n message: () => `expected data to match schema\\n\\n${detail}`,\n };\n }\n },\n});\n\n(vi as unknown as { tango?: TangoVitestHelpers }).tango = tangoHelpers;\n\ndeclare module 'vitest' {\n // oxlint-disable-next-line no-unused-vars\n interface Assertion<T> {\n toMatchSchema(schema: Parseable): void;\n }\n\n interface AsymmetricMatchersContaining {\n toMatchSchema(schema: Parseable): void;\n }\n\n interface VitestUtils {\n tango: TangoVitestHelpers;\n }\n}\n","/**\n * Install a temporary global test API for the duration of one callback.\n *\n * This is useful for module-loading tests that need a stable escape hatch into\n * already-imported symbols without introducing a second copy of the same package.\n */\nexport async function withGlobalTestApi<TValue, TResult>(\n key: string,\n value: TValue,\n work: () => TResult | Promise<TResult>\n): Promise<TResult> {\n const globalRecord = globalThis as Record<string, unknown>;\n const previousValue = globalRecord[key];\n const hadPreviousValue = Object.prototype.hasOwnProperty.call(globalRecord, key);\n\n globalRecord[key] = value;\n try {\n return await work();\n } finally {\n if (hadPreviousValue) {\n globalRecord[key] = previousValue;\n } else {\n delete globalRecord[key];\n }\n }\n}\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport * from './registerVitestTango';\nexport * from './withGlobalTestApi';\n"],"mappings":";;;;;;AA6BA,SAAS,QAAQA,OAAgC;AAC7C,eACW,UAAU,YACjB,UAAU,eACF,MAA6B,SAAS,mBACtC,MAAgC,YAAY;AAE3D;AAED,IAAIC,gBAA2C;AAC/C,MAAM,SAAS,UAAU,uBAAuB;AAChD,IAAI,oCAAoC;AAExC,eAAe,eACXC,OAC2B;AAC3B,YAAW,UAAU,WACjB,QAAO,OAAO;AAElB,QAAO;AACV;AA4BD,MAAMC,eAAmC;CACrC,MAAM,WAAW,OAAoC;EACjD,MAAM,UAAU,MAAM,eAAe,MAAM;AAC3C,QAAM,QAAQ,OAAO;AACrB,kBAAgB;AAChB,SAAO;CACV;CACD,iBAAqC;AACjC,OAAK,cACD,OAAM,IAAI,MAAM;AAEpB,SAAO;CACV;CACD,cAAuC;AACnC,SAAO,YAAY,aAAa;CACnC;CACD,MAAM,oBAAoB,SAA0B;EAChD,MAAM,UAAU,QAAQ,WAAW,aAAa,gBAAgB;AAChE,SAAO,oBAAsB,SAAS;GAClC,eAAe,QAAQ;GACvB,mBAAmB,QAAQ;EAC9B,EAAC;CACL;CACD,MAAM,yBAAyB,SAAoE;EAC/F,MAAM,UAAU,QAAQ,WAAW,aAAa,gBAAgB;AAChE,SAAO,yBAA2B,SAAS;GACvC,eAAe,QAAQ;GACvB,MAAM,QAAQ;GACd,oBAAoB,QAAQ;EAC/B,EAAC;CACL;CACD,MAAM,iBAAiBC,SAAgD;AACnE,SAAO,iBAAmB,WAAW,aAAa,gBAAgB,CAAC;CACtE;CACD,MAAM,UACFC,OACAC,MACAF,SACa;AACb,QAAM,UAAY,WAAW,aAAa,gBAAgB,EAAE,OAAO,KAAK;CAC3E;CACD,2BAAmEG,SAGZ;AACnD,SAAO,2BAAqC;GACxC,SAAS,QAAQ,WAAW,aAAa,gBAAgB;GACzD,MAAM,QAAQ;EACjB,EAAC;CACL;CACD,sBAA8DA,SAGP;AACnD,OAAK,mCAAmC;AACpC,uCAAoC;AACpC,UAAO,KACH,+GACH;EACJ;AACD,SAAO,2BAAqC;GACxC,SAAS,QAAQ,WAAW,aAAa,gBAAgB;GACzD,MAAM,QAAQ;EACjB,EAAC;CACL;CACD,MAAM,kBAAqBC,QAAwBC,UAA4B;AAC3E,QAAM,kBAAoB,QAAQ,SAAS;CAC9C;AACJ;AAED,OAAO,OAAO,EACV,cAAcC,UAAmBC,QAAmB;AAChD,KAAI;AACA,SAAO,MAAM,SAAS;AACtB,SAAO;GACH,MAAM;GACN,SAAS,MAAM;EAClB;CACJ,SAAQ,OAAO;EACZ,MAAM,SAAS,QAAQ,MAAM,GAAG,MAAM,UAAU,OAAO,MAAM;AAC7D,SAAO;GACH,MAAM;GACN,SAAS,OAAO,mCAAmC,OAAO;EAC7D;CACJ;AACJ,EACJ,EAAC;AAEF,GAAkD,QAAQ;;;;AC/JnD,eAAe,kBAClBC,KACAC,OACAC,MACgB;CAChB,MAAM,eAAe;CACrB,MAAM,gBAAgB,aAAa;CACnC,MAAM,mBAAmB,OAAO,UAAU,eAAe,KAAK,cAAc,IAAI;AAEhF,cAAa,OAAO;AACpB,KAAI;AACA,SAAO,MAAM,MAAM;CACtB,UAAS;AACN,MAAI,iBACA,cAAa,OAAO;IAEpB,QAAO,aAAa;CAE3B;AACJ"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danceroutine/tango-testing",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.1",
|
|
4
4
|
"description": "Testing utilities for Tango",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -52,11 +52,11 @@
|
|
|
52
52
|
"directory": "packages/testing"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@danceroutine/tango-config": "1.
|
|
56
|
-
"@danceroutine/tango-
|
|
57
|
-
"@danceroutine/tango-
|
|
58
|
-
"@danceroutine/tango-orm": "1.
|
|
59
|
-
"@danceroutine/tango-resources": "1.
|
|
55
|
+
"@danceroutine/tango-config": "1.8.1",
|
|
56
|
+
"@danceroutine/tango-migrations": "1.8.1",
|
|
57
|
+
"@danceroutine/tango-core": "1.8.1",
|
|
58
|
+
"@danceroutine/tango-orm": "1.8.1",
|
|
59
|
+
"@danceroutine/tango-resources": "1.8.1"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
62
62
|
"@types/express": "^5.0.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"aDBClient-Di5g6_hl.js","names":["overrides: DBClientOverrides","_sql: string","_params?: readonly unknown[]","_name: string","client: DBClient","sql: string","params?: readonly unknown[]","name: string"],"sources":["../src/mocks/aDBClient.ts"],"sourcesContent":["import { vi } from 'vitest';\nimport type { DBClient } from './DBClient';\n\n/**\n * Looser override type for `aDBClient` that accepts concrete-typed query mocks.\n * Consumers should not need to cast their mocks to satisfy `DBClient['query']`'s generic.\n */\ntype DBClientOverrides = {\n // oxlint-disable-next-line typescript/no-explicit-any\n query?: (sql: string, params?: readonly unknown[]) => Promise<{ rows: any[] }>;\n begin?: () => Promise<void>;\n commit?: () => Promise<void>;\n rollback?: () => Promise<void>;\n close?: () => Promise<void>;\n createSavepoint?: (name: string) => Promise<void>;\n releaseSavepoint?: (name: string) => Promise<void>;\n rollbackToSavepoint?: (name: string) => Promise<void>;\n};\n\n/**\n * Create a lightweight `DBClient` test double with optional behavior overrides.\n * The `query` override accepts any function returning `Promise<{ rows: any[] }>`,\n * so concrete-typed Vitest mocks do not require a cast at the call site.\n */\nexport function aDBClient(overrides: DBClientOverrides = {}): DBClient {\n const queryImpl =\n // oxlint-disable-next-line typescript/no-explicit-any\n overrides.query ?? (async (_sql: string, _params?: readonly unknown[]) => ({ rows: [] as any[] }));\n const beginImpl = overrides.begin ?? (async () => {});\n const commitImpl = overrides.commit ?? (async () => {});\n const rollbackImpl = overrides.rollback ?? (async () => {});\n const closeImpl = overrides.close ?? (async () => {});\n const createSavepointImpl = overrides.createSavepoint ?? (async (_name: string) => {});\n const releaseSavepointImpl = overrides.releaseSavepoint ?? (async (_name: string) => {});\n const rollbackToSavepointImpl = overrides.rollbackToSavepoint ?? (async (_name: string) => {});\n\n const client: DBClient = {\n query: vi.fn((sql: string, params?: readonly unknown[]) => queryImpl(sql, params)) as DBClient['query'],\n begin: vi.fn(() => beginImpl()),\n commit: vi.fn(() => commitImpl()),\n rollback: vi.fn(() => rollbackImpl()),\n close: vi.fn(() => closeImpl()),\n createSavepoint: vi.fn((name: string) => createSavepointImpl(name)),\n releaseSavepoint: vi.fn((name: string) => releaseSavepointImpl(name)),\n rollbackToSavepoint: vi.fn((name: string) => rollbackToSavepointImpl(name)),\n };\n\n return client;\n}\n"],"mappings":";;;AAwBO,SAAS,UAAUA,YAA+B,CAAE,GAAY;CACnE,MAAM,YAEF,UAAU,UAAU,OAAOC,MAAcC,aAAkC,EAAE,MAAM,CAAE,EAAW;CACpG,MAAM,YAAY,UAAU,UAAU,YAAY,CAAE;CACpD,MAAM,aAAa,UAAU,WAAW,YAAY,CAAE;CACtD,MAAM,eAAe,UAAU,aAAa,YAAY,CAAE;CAC1D,MAAM,YAAY,UAAU,UAAU,YAAY,CAAE;CACpD,MAAM,sBAAsB,UAAU,oBAAoB,OAAOC,UAAkB,CAAE;CACrF,MAAM,uBAAuB,UAAU,qBAAqB,OAAOA,UAAkB,CAAE;CACvF,MAAM,0BAA0B,UAAU,wBAAwB,OAAOA,UAAkB,CAAE;CAE7F,MAAMC,SAAmB;EACrB,OAAO,GAAG,GAAG,CAACC,KAAaC,WAAgC,UAAU,KAAK,OAAO,CAAC;EAClF,OAAO,GAAG,GAAG,MAAM,WAAW,CAAC;EAC/B,QAAQ,GAAG,GAAG,MAAM,YAAY,CAAC;EACjC,UAAU,GAAG,GAAG,MAAM,cAAc,CAAC;EACrC,OAAO,GAAG,GAAG,MAAM,WAAW,CAAC;EAC/B,iBAAiB,GAAG,GAAG,CAACC,SAAiB,oBAAoB,KAAK,CAAC;EACnE,kBAAkB,GAAG,GAAG,CAACA,SAAiB,qBAAqB,KAAK,CAAC;EACrE,qBAAqB,GAAG,GAAG,CAACA,SAAiB,wBAAwB,KAAK,CAAC;CAC9E;AAED,QAAO;AACV"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"integration-3tpk5c5V.js","names":["harness: IntegrationHarness","options: AssertMigrationPlanOptions","harness: IntegrationHarness","options: ApplyAndVerifyMigrationsOptions","status: MigrationStatus","harness: IntegrationHarness","strategy: HarnessStrategy","options: {\n createOptions?: HarnessOptions;\n migrationsDir?: string;\n }","options: TestTangoConfigOptions","options: TestTangoConfigOptions","options: TestTangoConfigOptions","defaultDeps: AppProcessHarnessDeps","ms: number","options: AppProcessHarnessOptions","deps: AppProcessHarnessDeps","chunk: unknown","value: unknown","deps: Partial<AppProcessHarnessDeps>","mergedDeps: AppProcessHarnessDeps","path: string","init?: RequestInit","response: Response","expectedStatus: number","label: string","bodyText: string","timeoutMs: number","current: string","chunk: string","defaultCapabilities: DialectTestCapabilities","overrides: Partial<IntegrationHarness>","value: unknown","strategy: HarnessStrategy","dialect: Dialect | string","value: string | undefined","dialect: Dialect","opts: {\n config?: Partial<AdapterConfig>;\n tangoConfigLoader?: () => unknown;\n sqliteFile?: string;\n }","merged: AdapterConfig","value: unknown","explicitSchema?: string","options: HarnessOptions","client: DBClient | null","harness: IntegrationHarness","migrationsDir: string","value: unknown","client: DBClient","options: HarnessOptions","client: DBClient | null","harness: IntegrationHarness","migrationsDir: string","value: unknown","strategy: HarnessStrategy","args: { dialect: Dialect | string; options?: HarnessOptions }","registry?: HarnessStrategyRegistry","options?: HarnessOptions","harness: IntegrationHarness","table: string","rows: T[]","input: {\n harness: IntegrationHarness;\n meta: TableMeta;\n}","executor: QueryExecutor<TModel>","actual: Promise<T> | T","expected: T"],"sources":["../src/integration/domain/Dialect.ts","../src/integration/domain/ResetMode.ts","../src/integration/domain/index.ts","../src/integration/migrations/AssertMigrationPlan.ts","../src/integration/migrations/ApplyAndVerifyMigrations.ts","../src/integration/migrations/IntrospectSchema.ts","../src/integration/migrations/index.ts","../src/integration/conformance/runDialectConformanceSuite.ts","../src/integration/conformance/index.ts","../src/integration/runtime/aTangoConfig.ts","../src/integration/runtime/aTangoRuntime.ts","../src/integration/runtime/setupTestTangoRuntime.ts","../src/integration/runtime/index.ts","../src/integration/smoke/AppProcessHarness.ts","../src/integration/smoke/index.ts","../src/integration/anIntegrationHarness.ts","../src/integration/HarnessStrategyRegistry.ts","../src/integration/config.ts","../src/integration/strategies/PostgresHarnessStrategy.ts","../src/integration/strategies/SqliteHarnessStrategy.ts","../src/integration/TestHarness.ts","../src/integration/orm/seedTable.ts","../src/integration/orm/createQuerySetFixture.ts","../src/integration/orm/expectQueryResult.ts","../src/integration/index.ts"],"sourcesContent":["export const Dialect = {\n Sqlite: 'sqlite',\n Postgres: 'postgres',\n} as const;\n\nexport type Dialect = (typeof Dialect)[keyof typeof Dialect];\n","export const ResetMode = {\n Transaction: 'transaction',\n Truncate: 'truncate',\n DropSchema: 'drop-schema',\n} as const;\n\nexport type ResetMode = (typeof ResetMode)[keyof typeof ResetMode];\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport { Dialect } from './Dialect';\nexport type { HarnessOptions, HarnessStrategy } from './HarnessStrategy';\nexport type { DialectTestCapabilities, IntegrationHarness } from './IntegrationHarness';\nexport { ResetMode } from './ResetMode';\n","import type { IntegrationHarness } from '../domain';\n\nexport type AssertMigrationPlanOptions = {\n migrationsDir: string;\n expectSqlContains?: string[];\n};\n\n/**\n * Generate a migration plan through a harness and assert that required SQL fragments appear.\n */\nexport async function assertMigrationPlan(\n harness: IntegrationHarness,\n options: AssertMigrationPlanOptions\n): Promise<string> {\n const runner = harness.migrationRunner(options.migrationsDir);\n const plan = await runner.plan();\n\n for (const snippet of options.expectSqlContains ?? []) {\n if (!plan.includes(snippet)) {\n throw new Error(`Expected migration plan to contain: ${snippet}`);\n }\n }\n\n return plan;\n}\n","import type { IntegrationHarness } from '../domain';\n\nexport type ApplyAndVerifyMigrationsOptions = {\n migrationsDir: string;\n toId?: string;\n expectedAppliedIds?: string[];\n};\n\nexport type MigrationStatus = { id: string; applied: boolean };\n\n/**\n * Apply migrations through a harness and optionally verify that specific ids were applied.\n */\nexport async function applyAndVerifyMigrations(\n harness: IntegrationHarness,\n options: ApplyAndVerifyMigrationsOptions\n): Promise<{ statuses: MigrationStatus[] }> {\n const runner = harness.migrationRunner(options.migrationsDir);\n await runner.apply(options.toId);\n const statuses = await runner.status();\n\n for (const id of options.expectedAppliedIds ?? []) {\n const row = statuses.find((status: MigrationStatus) => status.id === id);\n if (!row || !row.applied) {\n throw new Error(`Expected migration ${id} to be applied`);\n }\n }\n\n return { statuses };\n}\n","import { createDefaultIntrospectorStrategy } from '@danceroutine/tango-migrations';\nimport type { Dialect as MigrationDialect } from '@danceroutine/tango-migrations';\nimport { Dialect, type IntegrationHarness } from '../domain';\n\nconst introspectorStrategy = createDefaultIntrospectorStrategy();\n\n/**\n * Introspect the schema visible to a harness using Tango's built-in introspectors.\n */\nexport async function introspectSchema(harness: IntegrationHarness): Promise<unknown> {\n if (harness.dialect !== Dialect.Postgres && harness.dialect !== Dialect.Sqlite) {\n throw new Error(`No introspector registered for dialect: ${String(harness.dialect)}`);\n }\n const dialect = harness.dialect === Dialect.Postgres ? 'postgres' : 'sqlite';\n return introspectorStrategy.introspect(dialect as unknown as MigrationDialect, harness.dbClient);\n}\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport { assertMigrationPlan, type AssertMigrationPlanOptions } from './AssertMigrationPlan';\nexport {\n applyAndVerifyMigrations,\n type ApplyAndVerifyMigrationsOptions,\n type MigrationStatus,\n} from './ApplyAndVerifyMigrations';\nexport { introspectSchema } from './IntrospectSchema';\n","import type { HarnessOptions, HarnessStrategy } from '../domain';\n\n/**\n * Shared conformance checks every harness strategy must satisfy.\n *\n * This is intentionally framework-agnostic so first-party and third-party\n * strategies can run the same lifecycle validation.\n */\nexport async function runDialectConformanceSuite(\n strategy: HarnessStrategy,\n options: {\n createOptions?: HarnessOptions;\n migrationsDir?: string;\n } = {}\n): Promise<void> {\n const harness = await strategy.create(options.createOptions);\n\n if (harness.dialect !== strategy.dialect) {\n throw new Error(\n `Conformance failed: harness dialect '${String(harness.dialect)}' does not match strategy dialect '${String(strategy.dialect)}'`\n );\n }\n\n if (harness.capabilities !== strategy.capabilities) {\n throw new Error('Conformance failed: harness capabilities must be strategy capabilities reference');\n }\n\n let resetBeforeSetupThrew = false;\n try {\n await harness.reset();\n } catch {\n resetBeforeSetupThrew = true;\n }\n if (!resetBeforeSetupThrew) {\n throw new Error('Conformance failed: reset() must throw before setup()');\n }\n\n await harness.setup();\n await harness.reset();\n harness.migrationRunner(options.migrationsDir ?? '/tmp/migrations');\n await harness.teardown();\n\n let dbClientAfterTeardownThrew = false;\n try {\n // Access after teardown should fail to prevent stale client usage.\n\n // oxlint-disable-next-line no-unused-expressions\n harness.dbClient;\n } catch {\n dbClientAfterTeardownThrew = true;\n }\n if (!dbClientAfterTeardownThrew) {\n throw new Error('Conformance failed: dbClient getter must throw after teardown()');\n }\n}\n","export { runDialectConformanceSuite } from './runDialectConformanceSuite';\n","import { defineConfig, type TangoConfig } from '@danceroutine/tango-config';\n\nexport type TestTangoConfigOptions = {\n adapter?: 'sqlite' | 'postgres';\n sqliteFilename?: string;\n};\n\n/**\n * Create a stable Tango config fixture for runtime-oriented tests.\n */\nexport function aTangoConfig(options: TestTangoConfigOptions = {}): TangoConfig {\n const adapter = options.adapter ?? 'sqlite';\n const sqliteFilename = options.sqliteFilename ?? ':memory:';\n\n return defineConfig({\n current: 'test',\n environments: {\n development: {\n name: 'development',\n db:\n adapter === 'sqlite'\n ? { adapter: 'sqlite', filename: sqliteFilename, maxConnections: 1 }\n : {\n adapter: 'postgres',\n url: 'postgres://postgres:postgres@localhost:5432/tango',\n maxConnections: 1,\n },\n migrations: { dir: 'migrations', online: adapter === 'postgres' },\n },\n test: {\n name: 'test',\n db:\n adapter === 'sqlite'\n ? { adapter: 'sqlite', filename: sqliteFilename, maxConnections: 1 }\n : {\n adapter: 'postgres',\n url: 'postgres://postgres:postgres@localhost:5432/tango_test',\n maxConnections: 1,\n },\n migrations: { dir: 'migrations', online: adapter === 'postgres' },\n },\n production: {\n name: 'production',\n db:\n adapter === 'sqlite'\n ? { adapter: 'sqlite', filename: sqliteFilename, maxConnections: 1 }\n : {\n adapter: 'postgres',\n url: 'postgres://postgres:postgres@localhost:5432/tango',\n maxConnections: 1,\n },\n migrations: { dir: 'migrations', online: adapter === 'postgres' },\n },\n },\n });\n}\n","import { loadConfig } from '@danceroutine/tango-config';\nimport { TangoRuntime } from '@danceroutine/tango-orm';\nimport { aTangoConfig, type TestTangoConfigOptions } from './aTangoConfig';\n\n/**\n * Create a standalone Tango runtime for tests without mutating the process-default runtime.\n */\nexport function aTangoRuntime(options: TestTangoConfigOptions = {}): TangoRuntime {\n return new TangoRuntime(() => loadConfig(() => aTangoConfig(options)));\n}\n","import { initializeTangoRuntime, resetTangoRuntime, type TangoRuntime } from '@danceroutine/tango-orm';\nimport { aTangoConfig, type TestTangoConfigOptions } from './aTangoConfig';\n\n/**\n * Reset and initialize the process-default Tango runtime for tests.\n */\nexport async function setupTestTangoRuntime(options: TestTangoConfigOptions = {}): Promise<TangoRuntime> {\n await resetTangoRuntime();\n return initializeTangoRuntime(() => aTangoConfig(options));\n}\n","/**\n * Domain boundary barrel: centralizes runtime-oriented integration fixtures.\n */\n\nexport { aTangoConfig } from './aTangoConfig';\nexport type { TestTangoConfigOptions } from './aTangoConfig';\nexport { aTangoRuntime } from './aTangoRuntime';\nexport { setupTestTangoRuntime } from './setupTestTangoRuntime';\n","import { spawn } from 'node:child_process';\n\nexport interface AppProcessHarnessOptions {\n command: string;\n args?: string[];\n cwd?: string;\n env?: Record<string, string | undefined>;\n baseUrl: string;\n readyPath?: string;\n readyTimeoutMs?: number;\n readyIntervalMs?: number;\n stopTimeoutMs?: number;\n}\n\ntype FetchLike = typeof fetch;\ntype ProcessHarnessOutputStream = {\n on(eventName: 'data', listener: (chunk: unknown) => void): unknown;\n};\ntype ProcessHarnessChild = {\n exitCode: number | null;\n killed: boolean;\n kill(signal?: NodeJS.Signals): boolean;\n off(eventName: 'exit', listener: () => void): unknown;\n once(eventName: 'exit', listener: () => void): unknown;\n stdout?: ProcessHarnessOutputStream | null;\n stderr?: ProcessHarnessOutputStream | null;\n};\n\ninterface AppProcessHarnessDeps {\n spawnProcess: (\n command: string,\n args?: readonly string[],\n options?: Parameters<typeof spawn>[2]\n ) => ProcessHarnessChild;\n fetchImpl: FetchLike;\n sleep: (ms: number) => Promise<void>;\n}\n\nconst DEFAULT_READY_TIMEOUT_MS = 30_000;\nconst DEFAULT_READY_INTERVAL_MS = 250;\nconst DEFAULT_STOP_TIMEOUT_MS = 10_000;\nconst MAX_LOG_BUFFER_CHARS = 20_000;\n\nconst defaultDeps: AppProcessHarnessDeps = {\n spawnProcess: (command, args, options) => spawn(command, args as string[], options as Parameters<typeof spawn>[2]),\n fetchImpl: fetch,\n sleep: (ms: number) =>\n new Promise((resolve) => {\n setTimeout(resolve, ms);\n }),\n};\n\n/**\n * Lightweight process harness for end-to-end smoke tests that need a real app process.\n */\nexport class AppProcessHarness {\n static readonly BRAND = 'tango.testing.app_process_harness' as const;\n readonly __tangoBrand: typeof AppProcessHarness.BRAND = AppProcessHarness.BRAND;\n private readonly child: ProcessHarnessChild;\n private readonly baseUrl: string;\n private readonly readyUrl: string;\n private readonly readyTimeoutMs: number;\n private readonly readyIntervalMs: number;\n private readonly stopTimeoutMs: number;\n private readonly deps: AppProcessHarnessDeps;\n private stopped = false;\n private stdoutBuffer = '';\n private stderrBuffer = '';\n\n private constructor(options: AppProcessHarnessOptions, deps: AppProcessHarnessDeps) {\n this.baseUrl = options.baseUrl.replace(/\\/+$/, '');\n this.readyUrl = `${this.baseUrl}${normalizePath(options.readyPath ?? '/health')}`;\n this.readyTimeoutMs = options.readyTimeoutMs ?? DEFAULT_READY_TIMEOUT_MS;\n this.readyIntervalMs = options.readyIntervalMs ?? DEFAULT_READY_INTERVAL_MS;\n this.stopTimeoutMs = options.stopTimeoutMs ?? DEFAULT_STOP_TIMEOUT_MS;\n this.deps = deps;\n\n this.child = this.deps.spawnProcess(options.command, options.args ?? [], {\n cwd: options.cwd,\n env: { ...process.env, ...options.env },\n stdio: 'pipe',\n });\n this.child.stdout?.on('data', (chunk: unknown) => {\n this.stdoutBuffer = appendBuffer(this.stdoutBuffer, String(chunk));\n });\n this.child.stderr?.on('data', (chunk: unknown) => {\n this.stderrBuffer = appendBuffer(this.stderrBuffer, String(chunk));\n });\n }\n\n /**\n * Narrow an unknown value to the smoke-test harness that owns a child process.\n */\n static isAppProcessHarness(value: unknown): value is AppProcessHarness {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === AppProcessHarness.BRAND\n );\n }\n\n /**\n * Spawn the target process and wait until its readiness endpoint responds successfully.\n */\n static async start(\n options: AppProcessHarnessOptions,\n deps: Partial<AppProcessHarnessDeps> = {}\n ): Promise<AppProcessHarness> {\n const mergedDeps: AppProcessHarnessDeps = {\n ...defaultDeps,\n ...deps,\n };\n const harness = new AppProcessHarness(options, mergedDeps);\n await harness.waitForReady();\n return harness;\n }\n\n /**\n * Return the buffered stdout log for recent process output.\n */\n getStdoutLog(): string {\n return this.stdoutBuffer;\n }\n\n /**\n * Return the buffered stderr log for recent process output.\n */\n getStderrLog(): string {\n return this.stderrBuffer;\n }\n\n /**\n * Return stdout and stderr in a single formatted string for debugging failures.\n */\n getCombinedLog(): string {\n const stdout = this.stdoutBuffer.trim();\n const stderr = this.stderrBuffer.trim();\n if (!stdout && !stderr) {\n return '';\n }\n return [`[stdout]\\n${stdout}`, `[stderr]\\n${stderr}`].join('\\n\\n').trim();\n }\n\n /**\n * Issue an HTTP request against the managed application process.\n */\n async request(path: string, init?: RequestInit): Promise<Response> {\n const target = path.startsWith('http') ? path : `${this.baseUrl}${normalizePath(path)}`;\n return this.deps.fetchImpl(target, init);\n }\n\n /**\n * Assert an HTTP response status and include process logs when it mismatches.\n */\n async assertResponseStatus(response: Response, expectedStatus: number, label: string): Promise<void> {\n if (response.status === expectedStatus) {\n return;\n }\n\n let bodyText: string;\n try {\n bodyText = await response.text();\n } catch (error) {\n bodyText = `failed to read response body: ${String(error)}`;\n }\n\n throw new Error(\n [\n `${label}. expected ${String(expectedStatus)} got ${String(response.status)}`,\n `response body: ${bodyText}`,\n `process logs:\\n${this.getCombinedLog()}`,\n ].join('\\n')\n );\n }\n\n /**\n * Stop the managed process, escalating from SIGTERM to SIGKILL when necessary.\n */\n async stop(): Promise<void> {\n if (this.stopped) {\n return;\n }\n this.stopped = true;\n\n if (this.child.exitCode !== null || this.child.killed) {\n return;\n }\n\n this.child.kill('SIGTERM');\n const exited = await this.waitForExit(this.stopTimeoutMs);\n if (!exited && !this.child.killed) {\n this.child.kill('SIGKILL');\n await this.waitForExit(this.stopTimeoutMs);\n }\n }\n\n private async waitForReady(): Promise<void> {\n const deadline = Date.now() + this.readyTimeoutMs;\n\n while (Date.now() < deadline) {\n if (this.child.exitCode !== null) {\n throw new Error(\n `Process exited before ready check succeeded (exitCode=${String(this.child.exitCode)}).\\n${this.getCombinedLog()}`\n );\n }\n try {\n const response = await this.deps.fetchImpl(this.readyUrl);\n if (response.ok) {\n return;\n }\n } catch {\n // Retry until timeout.\n }\n await this.deps.sleep(this.readyIntervalMs);\n }\n\n await this.stop();\n throw new Error(`Timed out waiting for readiness at ${this.readyUrl}.\\n${this.getCombinedLog()}`);\n }\n\n private async waitForExit(timeoutMs: number): Promise<boolean> {\n if (this.child.exitCode !== null) {\n return true;\n }\n\n return await new Promise<boolean>((resolve) => {\n const timer = setTimeout(() => {\n this.child.off('exit', onExit);\n resolve(false);\n }, timeoutMs);\n\n const onExit = () => {\n clearTimeout(timer);\n resolve(true);\n };\n\n this.child.once('exit', onExit);\n });\n }\n}\n\nfunction appendBuffer(current: string, chunk: string): string {\n const next = current + chunk;\n if (next.length <= MAX_LOG_BUFFER_CHARS) {\n return next;\n }\n return next.slice(next.length - MAX_LOG_BUFFER_CHARS);\n}\n\nfunction normalizePath(path: string): string {\n if (!path) {\n return '/';\n }\n return path.startsWith('/') ? path : `/${path}`;\n}\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport * from './AppProcessHarness';\n","import { vi } from 'vitest';\nimport { aDBClient } from '../mocks/aDBClient';\nimport { Dialect } from './domain/Dialect';\nimport { ResetMode } from './domain/ResetMode';\nimport type { DialectTestCapabilities, IntegrationHarness } from './domain/IntegrationHarness';\n\nconst defaultCapabilities: DialectTestCapabilities = {\n transactionalDDL: true,\n supportsSchemas: false,\n supportsConcurrentIndex: false,\n supportsDeferredFkValidation: false,\n supportsJsonb: false,\n};\n\n/**\n * Create an integration-harness fixture with optional overrides.\n */\nexport function anIntegrationHarness(overrides: Partial<IntegrationHarness> = {}): IntegrationHarness {\n return {\n dialect: Dialect.Sqlite,\n capabilities: defaultCapabilities,\n resetMode: ResetMode.DropSchema,\n dbClient: aDBClient(),\n setup: vi.fn(async () => {}),\n reset: vi.fn(async () => {}),\n teardown: vi.fn(async () => {}),\n migrationRunner: vi.fn(() => ({}) as never),\n ...overrides,\n };\n}\n","import type { Dialect, HarnessStrategy } from './domain';\n\n/**\n * Registry of test harness strategies keyed by dialect.\n */\nexport class HarnessStrategyRegistry {\n static readonly BRAND = 'tango.testing.harness_strategy_registry' as const;\n readonly __tangoBrand: typeof HarnessStrategyRegistry.BRAND = HarnessStrategyRegistry.BRAND;\n private readonly strategies = new Map<string, HarnessStrategy>();\n\n /**\n * Narrow an unknown value to `HarnessStrategyRegistry`.\n */\n static isHarnessStrategyRegistry(value: unknown): value is HarnessStrategyRegistry {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === HarnessStrategyRegistry.BRAND\n );\n }\n\n /**\n * Register or replace a dialect strategy.\n */\n register(strategy: HarnessStrategy): this {\n this.strategies.set(String(strategy.dialect), strategy);\n return this;\n }\n\n /**\n * Resolve a strategy for a dialect, or throw if none is registered.\n */\n get(dialect: Dialect | string): HarnessStrategy {\n const strategy = this.strategies.get(String(dialect));\n if (!strategy) {\n throw new Error(`No harness strategy registered for dialect: ${String(dialect)}`);\n }\n return strategy;\n }\n\n /**\n * List all registered strategies.\n */\n list(): readonly HarnessStrategy[] {\n return [...this.strategies.values()];\n }\n}\n","import type { AdapterConfig } from '@danceroutine/tango-orm';\nimport { loadConfig } from '@danceroutine/tango-config';\nimport { Dialect } from './domain';\n\nfunction readNumber(value: string | undefined): number | undefined {\n if (!value) return undefined;\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n\n/**\n * Resolve adapter configuration for a test harness from explicit options,\n * typed Tango config, and environment variables in that order.\n */\nexport function resolveAdapterConfig(\n dialect: Dialect,\n opts: {\n config?: Partial<AdapterConfig>;\n tangoConfigLoader?: () => unknown;\n sqliteFile?: string;\n }\n): AdapterConfig {\n const fromOptions = opts.config ?? {};\n\n if (opts.tangoConfigLoader) {\n const loaded = loadConfig(opts.tangoConfigLoader);\n const current = loaded.current.db;\n const merged: AdapterConfig = {\n url: fromOptions.url ?? current.url,\n host: fromOptions.host ?? current.host,\n port: fromOptions.port ?? current.port,\n database: fromOptions.database ?? current.database,\n user: fromOptions.user ?? current.user,\n password: fromOptions.password ?? current.password,\n filename: fromOptions.filename ?? current.filename,\n maxConnections: fromOptions.maxConnections ?? current.maxConnections,\n };\n if (dialect === Dialect.Sqlite) {\n merged.filename = opts.sqliteFile ?? merged.filename ?? ':memory:';\n }\n return merged;\n }\n\n if (dialect === Dialect.Postgres) {\n return {\n url: fromOptions.url ?? process.env.TANGO_DATABASE_URL ?? process.env.DATABASE_URL,\n host: fromOptions.host ?? process.env.TANGO_DB_HOST,\n port: fromOptions.port ?? readNumber(process.env.TANGO_DB_PORT),\n database: fromOptions.database ?? process.env.TANGO_DB_NAME,\n user: fromOptions.user ?? process.env.TANGO_DB_USER,\n password: fromOptions.password ?? process.env.TANGO_DB_PASSWORD,\n maxConnections: fromOptions.maxConnections ?? 10,\n };\n }\n\n return {\n filename: opts.sqliteFile ?? fromOptions.filename ?? process.env.TANGO_SQLITE_FILENAME ?? ':memory:',\n maxConnections: fromOptions.maxConnections ?? 1,\n };\n}\n","import { quoteSqlIdentifier, validateSqlIdentifier } from '@danceroutine/tango-core';\nimport { MigrationRunner } from '@danceroutine/tango-migrations';\nimport type { Dialect as MigrationDialect } from '@danceroutine/tango-migrations';\nimport { PostgresAdapter } from '@danceroutine/tango-orm/connection';\nimport type { DBClient } from '@danceroutine/tango-orm';\nimport { resolveAdapterConfig } from '../config';\nimport {\n Dialect,\n ResetMode,\n type DialectTestCapabilities,\n type HarnessOptions,\n type HarnessStrategy,\n type IntegrationHarness,\n} from '../domain';\n\n/**\n * Harness strategy for PostgreSQL-backed integration tests.\n */\nexport class PostgresHarnessStrategy implements HarnessStrategy {\n static readonly BRAND = 'tango.testing.postgres_harness_strategy' as const;\n readonly __tangoBrand: typeof PostgresHarnessStrategy.BRAND = PostgresHarnessStrategy.BRAND;\n readonly dialect: Dialect = Dialect.Postgres;\n readonly capabilities: DialectTestCapabilities = {\n transactionalDDL: true,\n supportsSchemas: true,\n supportsConcurrentIndex: true,\n supportsDeferredFkValidation: true,\n supportsJsonb: true,\n };\n\n /**\n * Narrow an unknown value to the PostgreSQL integration harness strategy.\n */\n static isPostgresHarnessStrategy(value: unknown): value is PostgresHarnessStrategy {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === PostgresHarnessStrategy.BRAND\n );\n }\n\n private static buildSchemaName(explicitSchema?: string): string {\n if (explicitSchema) return explicitSchema;\n const random = Math.random().toString(36).slice(2, 8);\n return `tango_test_${Date.now()}_${random}`;\n }\n\n /**\n * Create a configured Postgres integration harness instance.\n */\n async create(options: HarnessOptions = {}): Promise<IntegrationHarness> {\n const config = resolveAdapterConfig(Dialect.Postgres, {\n config: options.config,\n tangoConfigLoader: options.tangoConfigLoader,\n });\n\n const adapter = new PostgresAdapter();\n const schemaName = PostgresHarnessStrategy.buildSchemaName(options.schema);\n const resetMode = options.resetMode ?? ResetMode.DropSchema;\n let client: DBClient | null = null;\n\n const ensureSearchPath = async (): Promise<void> => {\n const dbClient = client as DBClient;\n const schema = quoteSqlIdentifier(validateSqlIdentifier(schemaName, 'schema'), 'postgres');\n await dbClient.query(`CREATE SCHEMA IF NOT EXISTS ${schema}`);\n await dbClient.query(`SET search_path TO ${schema}`);\n };\n\n const recreateSchema = async (): Promise<void> => {\n const dbClient = client as DBClient;\n const schema = quoteSqlIdentifier(validateSqlIdentifier(schemaName, 'schema'), 'postgres');\n await dbClient.query(`DROP SCHEMA IF EXISTS ${schema} CASCADE`);\n await dbClient.query(`CREATE SCHEMA ${schema}`);\n await dbClient.query(`SET search_path TO ${schema}`);\n };\n\n const harness: IntegrationHarness = {\n dialect: Dialect.Postgres,\n capabilities: this.capabilities,\n resetMode,\n get dbClient(): DBClient {\n if (!client) {\n throw new Error('Postgres harness not initialized. Call setup() first.');\n }\n return client;\n },\n async setup(): Promise<void> {\n client = await adapter.connect(config);\n await ensureSearchPath();\n },\n async reset(): Promise<void> {\n if (!client) {\n throw new Error('Postgres harness not initialized. Call setup() first.');\n }\n if (resetMode === ResetMode.DropSchema || resetMode === ResetMode.Transaction) {\n await recreateSchema();\n return;\n }\n\n const { rows } = await client.query<{ table_name: string }>(\n `SELECT table_name FROM information_schema.tables WHERE table_schema = $1 AND table_type = 'BASE TABLE'`,\n [schemaName]\n );\n for (const row of rows) {\n const schema = quoteSqlIdentifier(validateSqlIdentifier(schemaName, 'schema'), 'postgres');\n const table = quoteSqlIdentifier(\n validateSqlIdentifier(String(row.table_name), 'table'),\n 'postgres'\n );\n await client.query(`TRUNCATE TABLE ${schema}.${table} RESTART IDENTITY CASCADE`);\n }\n await client.query(\n `SET search_path TO ${quoteSqlIdentifier(validateSqlIdentifier(schemaName, 'schema'), 'postgres')}`\n );\n },\n async teardown(): Promise<void> {\n if (!client) return;\n try {\n await client.query(\n `DROP SCHEMA IF EXISTS ${quoteSqlIdentifier(validateSqlIdentifier(schemaName, 'schema'), 'postgres')} CASCADE`\n );\n } finally {\n await client.close();\n client = null;\n }\n },\n migrationRunner(migrationsDir: string): MigrationRunner {\n if (!client) {\n throw new Error('Postgres harness not initialized. Call setup() first.');\n }\n return new MigrationRunner(client, 'postgres' as MigrationDialect, migrationsDir);\n },\n };\n\n return harness;\n }\n}\n","import { rm } from 'node:fs/promises';\nimport { quoteSqlIdentifier, validateSqlIdentifier } from '@danceroutine/tango-core';\nimport { MigrationRunner } from '@danceroutine/tango-migrations';\nimport type { Dialect as MigrationDialect } from '@danceroutine/tango-migrations';\nimport { SqliteAdapter } from '@danceroutine/tango-orm/connection';\nimport type { DBClient } from '@danceroutine/tango-orm';\nimport { resolveAdapterConfig } from '../config';\nimport {\n Dialect,\n ResetMode,\n type DialectTestCapabilities,\n type HarnessOptions,\n type HarnessStrategy,\n type IntegrationHarness,\n} from '../domain';\n\n/**\n * Harness strategy for SQLite-backed integration tests.\n */\nexport class SqliteHarnessStrategy implements HarnessStrategy {\n static readonly BRAND = 'tango.testing.sqlite_harness_strategy' as const;\n readonly __tangoBrand: typeof SqliteHarnessStrategy.BRAND = SqliteHarnessStrategy.BRAND;\n readonly dialect: Dialect = Dialect.Sqlite;\n readonly capabilities: DialectTestCapabilities = {\n transactionalDDL: true,\n supportsSchemas: false,\n supportsConcurrentIndex: false,\n supportsDeferredFkValidation: false,\n supportsJsonb: false,\n };\n\n /**\n * Narrow an unknown value to the SQLite integration harness strategy.\n */\n static isSqliteHarnessStrategy(value: unknown): value is SqliteHarnessStrategy {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === SqliteHarnessStrategy.BRAND\n );\n }\n\n private static async dropAllTables(client: DBClient): Promise<void> {\n const { rows } = await client.query<{ name: string }>(\n \"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'\"\n );\n for (const row of rows) {\n const table = quoteSqlIdentifier(validateSqlIdentifier(String(row.name), 'table'), 'sqlite');\n await client.query(`DROP TABLE IF EXISTS ${table}`);\n }\n }\n\n /**\n * Create a configured SQLite integration harness instance.\n */\n async create(options: HarnessOptions = {}): Promise<IntegrationHarness> {\n const config = resolveAdapterConfig(Dialect.Sqlite, {\n config: options.config,\n tangoConfigLoader: options.tangoConfigLoader,\n sqliteFile: options.sqliteFile,\n });\n\n const adapter = new SqliteAdapter();\n const resetMode = options.resetMode ?? ResetMode.DropSchema;\n let client: DBClient | null = null;\n\n const reconnect = async (): Promise<DBClient> => {\n client = await adapter.connect(config);\n return client;\n };\n\n const harness: IntegrationHarness = {\n dialect: Dialect.Sqlite,\n capabilities: this.capabilities,\n resetMode,\n get dbClient(): DBClient {\n if (!client) {\n throw new Error('Sqlite harness not initialized. Call setup() first.');\n }\n return client;\n },\n async setup(): Promise<void> {\n await reconnect();\n },\n async reset(): Promise<void> {\n if (!client) {\n throw new Error('Sqlite harness not initialized. Call setup() first.');\n }\n\n if (resetMode === ResetMode.DropSchema && config.filename && config.filename !== ':memory:') {\n await client.close();\n await rm(config.filename, { force: true });\n await reconnect();\n return;\n }\n\n await SqliteHarnessStrategy.dropAllTables(client);\n },\n async teardown(): Promise<void> {\n if (client) {\n await client.close();\n client = null;\n }\n if (config.filename && config.filename !== ':memory:') {\n await rm(config.filename, { force: true });\n }\n },\n migrationRunner(migrationsDir: string): MigrationRunner {\n if (!client) {\n throw new Error('Sqlite harness not initialized. Call setup() first.');\n }\n return new MigrationRunner(client, 'sqlite' as MigrationDialect, migrationsDir);\n },\n };\n\n return harness;\n }\n}\n","import { HarnessStrategyRegistry } from './HarnessStrategyRegistry';\nimport { Dialect, type HarnessOptions, type HarnessStrategy, type IntegrationHarness } from './domain';\nimport { PostgresHarnessStrategy } from './strategies/PostgresHarnessStrategy';\nimport { SqliteHarnessStrategy } from './strategies/SqliteHarnessStrategy';\n\n/**\n * Facade for creating integration test harnesses by dialect.\n */\nexport class TestHarness {\n static readonly BRAND = 'tango.testing.test_harness' as const;\n private static defaultRegistry: HarnessStrategyRegistry | null = null;\n readonly __tangoBrand: typeof TestHarness.BRAND = TestHarness.BRAND;\n\n /**\n * Narrow an unknown value to `TestHarness`.\n */\n static isTestHarness(value: unknown): value is TestHarness {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === TestHarness.BRAND\n );\n }\n\n /**\n * Register a harness strategy on the shared default registry.\n */\n static registerStrategy(strategy: HarnessStrategy): void {\n this.ensureRegistry().register(strategy);\n }\n\n /**\n * Return the shared harness strategy registry.\n */\n static getRegistry(): HarnessStrategyRegistry {\n return this.ensureRegistry();\n }\n\n /**\n * Create a dialect-specific harness from the registry.\n */\n static async forDialect(\n args: { dialect: Dialect | string; options?: HarnessOptions },\n registry?: HarnessStrategyRegistry\n ): Promise<IntegrationHarness> {\n const selectedRegistry = registry ?? this.ensureRegistry();\n const strategy = selectedRegistry.get(args.dialect);\n return strategy.create(args.options);\n }\n\n /**\n * Convenience helper for a SQLite test harness.\n */\n static async sqlite(options?: HarnessOptions): Promise<IntegrationHarness> {\n return this.forDialect({ dialect: Dialect.Sqlite, options });\n }\n\n /**\n * Convenience helper for a Postgres test harness.\n */\n static async postgres(options?: HarnessOptions): Promise<IntegrationHarness> {\n return this.forDialect({ dialect: Dialect.Postgres, options });\n }\n\n private static ensureRegistry(): HarnessStrategyRegistry {\n if (this.defaultRegistry) return this.defaultRegistry;\n\n const registry = new HarnessStrategyRegistry();\n registry.register(new SqliteHarnessStrategy());\n registry.register(new PostgresHarnessStrategy());\n this.defaultRegistry = registry;\n return registry;\n }\n}\n","import { quoteSqlIdentifier, validateSqlIdentifier, type SqlDialect } from '@danceroutine/tango-core';\nimport { Dialect, type IntegrationHarness } from '../domain/index';\n\n/**\n * Seed rows directly into a table for integration tests that need known fixtures.\n */\nexport async function seedTable<T extends Record<string, unknown>>(\n harness: IntegrationHarness,\n table: string,\n rows: T[]\n): Promise<void> {\n if (rows.length === 0) {\n return;\n }\n\n const columns = Object.keys(rows[0] ?? {});\n if (columns.length === 0) {\n return;\n }\n\n const dialect = harness.dialect as SqlDialect;\n const safeTable = quoteSqlIdentifier(validateSqlIdentifier(table, 'table'), dialect);\n const safeColumns = columns.map((column) =>\n quoteSqlIdentifier(validateSqlIdentifier(column, 'column', columns), dialect)\n );\n\n for (const row of rows) {\n const values = columns.map((column) => {\n const value = row[column];\n if (harness.dialect === Dialect.Sqlite && typeof value === 'boolean') {\n return value ? 1 : 0;\n }\n return value;\n });\n const placeholders =\n harness.dialect === Dialect.Postgres\n ? columns.map((_, index) => `$${index + 1}`).join(', ')\n : columns.map(() => '?').join(', ');\n\n await harness.dbClient.query(\n `INSERT INTO ${safeTable} (${safeColumns.join(', ')}) VALUES (${placeholders})`,\n values as unknown[]\n );\n }\n}\n","import { QuerySet, type QueryExecutor } from '@danceroutine/tango-orm';\nimport type { TableMeta } from '@danceroutine/tango-orm/query';\nimport { Dialect, type IntegrationHarness } from '../domain/index';\n\n/**\n * Create a `QuerySet` fixture backed by a real integration harness and supplied table metadata.\n */\nexport function createQuerySetFixture<TModel extends Record<string, unknown>>(input: {\n harness: IntegrationHarness;\n meta: TableMeta;\n}): QuerySet<TModel> {\n const executor: QueryExecutor<TModel> = {\n meta: input.meta,\n client: input.harness.dbClient,\n dialect: input.harness.dialect as Dialect,\n run: async (compiled) => {\n const result = await input.harness.dbClient.query<TModel>(compiled.sql, compiled.params);\n return result.rows;\n },\n };\n\n return new QuerySet<TModel>(executor);\n}\n","/**\n * Assert that a query result matches an expected value using structural equality.\n */\nexport async function expectQueryResult<T>(actual: Promise<T> | T, expected: T): Promise<void> {\n const resolved = await actual;\n\n const resolvedJson = JSON.stringify(resolved);\n const expectedJson = JSON.stringify(expected);\n\n if (resolvedJson !== expectedJson) {\n throw new Error(`Expected query result ${expectedJson}, got ${resolvedJson}`);\n }\n}\n","/**\n * Domain boundary barrel: exposes namespaced exports for Django-style drill-down\n * imports and curated flat exports for TS-native ergonomics.\n */\n\nexport * as domain from './domain/index';\nexport * as migrations from './migrations/index';\nexport * as conformance from './conformance/index';\nexport * as runtime from './runtime/index';\nexport * as smoke from './smoke/index';\n\nexport * from './domain/index';\nexport * from './anIntegrationHarness';\nexport * from './HarnessStrategyRegistry';\nexport * from './TestHarness';\nexport * from './migrations/index';\nexport * from './conformance/index';\nexport * from './orm/index';\nexport * from './runtime/index';\nexport * from './smoke/index';\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mocks-vvHjWn5S.js","names":["overrides: AQueryResultOverrides<TModel>","overrides: QueryExecutorOverrides<TModel>","meta: TableMeta","overrides: Partial<QuerySet<TModel, TResult>>","filterImpl: QuerySet<TModel, TResult>['filter']","excludeImpl: QuerySet<TModel, TResult>['exclude']","orderByImpl: QuerySet<TModel, TResult>['orderBy']","limitImpl: QuerySet<TModel, TResult>['limit']","offsetImpl: QuerySet<TModel, TResult>['offset']","_cols: readonly (keyof TModel)[]","fetchImpl: QuerySet<TModel, TResult>['fetch']","_shape?: ((r: TResult) => Out) | { parse: (r: TResult) => Out }","fetchOneImpl: QuerySet<TModel, TResult>['fetchOne']","countImpl: QuerySet<TModel, TResult>['count']","existsImpl: QuerySet<TModel, TResult>['exists']","input: Parameters<QuerySet<TModel, TResult>['filter']>[0]","input: Parameters<QuerySet<TModel, TResult>['exclude']>[0]","n: number","cols: Parameters<QuerySet<TModel, TResult>['select']>[0]","overrides: ManagerOverrides<TModel>","id: ModelId","input: Partial<TModel>","_id: ModelId","patch: Partial<TModel>","inputs: Partial<TModel>[]","args: Parameters<ManagerLike<TModel>['getOrCreate']>[0]","args: Parameters<ManagerLike<TModel>['updateOrCreate']>[0]","optionsOrMethod: RequestContextFixtureOptions<TUser, TContext> | string","urlArg?: string","bodyArg?: unknown","resolvedOptions: RequestContextFixtureOptions<TUser, TContext>","resolvedHeaders: HeadersInit | undefined","req: Request","currentUser: TUser | null","input: RelationMetaInput"],"sources":["../src/mocks/aQueryResult.ts","../src/mocks/aQueryExecutor.ts","../src/mocks/aQuerySet.ts","../src/mocks/aManager.ts","../src/mocks/aRequestContext.ts","../src/mocks/aRelationMeta.ts","../src/mocks/index.ts"],"sourcesContent":["import type { QueryResult } from '@danceroutine/tango-orm/query';\nimport { QueryResult as QueryResultClass } from '@danceroutine/tango-orm/query';\n\nexport type AQueryResultOverrides<TModel> = {\n items?: readonly TModel[];\n results?: readonly TModel[];\n};\n\n/**\n * Create a query-result test value with optional overrides.\n */\nexport function aQueryResult<TModel>(overrides: AQueryResultOverrides<TModel> = {}): QueryResult<TModel> {\n const items = overrides.items ?? overrides.results ?? ([] as TModel[]);\n return new QueryResultClass(items);\n}\n","import { vi } from 'vitest';\nimport type { QueryExecutor } from '@danceroutine/tango-orm';\nimport type { Dialect, TableMeta } from '@danceroutine/tango-orm/query';\nimport { aDBClient } from './aDBClient';\n\nexport type QueryExecutorOverrides<TModel extends Record<string, unknown>> = {\n dialect?: Dialect;\n meta?: TableMeta;\n query?: (sql: string, params?: readonly unknown[]) => Promise<{ rows: unknown[] }>;\n run?: QueryExecutor<TModel>['run'];\n};\n\n/**\n * Create a minimal `QueryExecutor` test double for `QuerySet` tests.\n */\nexport function aQueryExecutor<TModel extends Record<string, unknown>>(\n overrides: QueryExecutorOverrides<TModel> = {}\n): QueryExecutor<TModel> {\n const dialect = overrides.dialect ?? 'postgres';\n const meta: TableMeta = overrides.meta ?? { table: 'mock_table', pk: 'id', columns: {} };\n const run = overrides.run ?? vi.fn(async () => [] as TModel[]);\n const client = aDBClient(overrides.query ? { query: overrides.query } : {});\n\n return { meta, client, dialect, run };\n}\n","import { vi } from 'vitest';\nimport type { QuerySet } from '@danceroutine/tango-orm';\nimport { QuerySet as QuerySetClass } from '@danceroutine/tango-orm';\nimport { aQueryResult } from './aQueryResult';\nimport { aQueryExecutor } from './aQueryExecutor';\n\n/**\n * Create a chainable query-set test double with optional behavior overrides.\n *\n * All methods are wrapped in `vi.fn()` so they can be asserted on directly\n * without an additional `vi.mocked()` call.\n */\nexport function aQuerySet<TModel extends Record<string, unknown>, TResult extends Record<string, unknown> = TModel>(\n overrides: Partial<QuerySet<TModel, TResult>> = {}\n): QuerySet<TModel, TResult> {\n const queryset = new QuerySetClass<TModel, TResult>(aQueryExecutor<TModel>());\n const filterImpl: QuerySet<TModel, TResult>['filter'] = overrides.filter ?? ((_input) => queryset);\n const excludeImpl: QuerySet<TModel, TResult>['exclude'] = overrides.exclude ?? ((_input) => queryset);\n const orderByImpl: QuerySet<TModel, TResult>['orderBy'] = overrides.orderBy ?? ((..._tokens) => queryset);\n const limitImpl: QuerySet<TModel, TResult>['limit'] = overrides.limit ?? ((_n) => queryset);\n const offsetImpl: QuerySet<TModel, TResult>['offset'] = overrides.offset ?? ((_n) => queryset);\n const defaultSelect = ((_cols: readonly (keyof TModel)[]) => queryset) as unknown as QuerySet<\n TModel,\n TResult\n >['select'];\n const selectImpl = overrides.select ?? defaultSelect;\n const selectRelatedImpl =\n (overrides.selectRelated as ((...rels: readonly string[]) => QuerySet<TModel, TResult>) | undefined) ??\n ((..._rels: readonly string[]) => queryset);\n const prefetchRelatedImpl =\n (overrides.prefetchRelated as ((...rels: readonly string[]) => QuerySet<TModel, TResult>) | undefined) ??\n ((..._rels: readonly string[]) => queryset);\n const fetchImpl: QuerySet<TModel, TResult>['fetch'] =\n overrides.fetch ??\n (async <Out = TResult>(_shape?: ((r: TResult) => Out) | { parse: (r: TResult) => Out }) => aQueryResult<Out>());\n const fetchOneImpl: QuerySet<TModel, TResult>['fetchOne'] =\n overrides.fetchOne ??\n (async <Out = TResult>(_shape?: ((r: TResult) => Out) | { parse: (r: TResult) => Out }) => null as Out | null);\n const countImpl: QuerySet<TModel, TResult>['count'] = overrides.count ?? (async () => 0);\n const existsImpl: QuerySet<TModel, TResult>['exists'] = overrides.exists ?? (async () => false);\n\n queryset.filter = vi.fn((input: Parameters<QuerySet<TModel, TResult>['filter']>[0]) =>\n filterImpl(input)\n ) as QuerySet<TModel, TResult>['filter'];\n queryset.exclude = vi.fn((input: Parameters<QuerySet<TModel, TResult>['exclude']>[0]) =>\n excludeImpl(input)\n ) as QuerySet<TModel, TResult>['exclude'];\n queryset.orderBy = vi.fn((...tokens: Parameters<QuerySet<TModel, TResult>['orderBy']>) =>\n orderByImpl(...tokens)\n ) as QuerySet<TModel, TResult>['orderBy'];\n queryset.limit = vi.fn((n: number) => limitImpl(n)) as QuerySet<TModel, TResult>['limit'];\n queryset.offset = vi.fn((n: number) => offsetImpl(n)) as QuerySet<TModel, TResult>['offset'];\n queryset.select = vi.fn((cols: Parameters<QuerySet<TModel, TResult>['select']>[0]) =>\n selectImpl(cols)\n ) as unknown as QuerySet<TModel, TResult>['select'];\n queryset.selectRelated = vi.fn((...rels: readonly string[]) => selectRelatedImpl(...rels)) as unknown as QuerySet<\n TModel,\n TResult\n >['selectRelated'];\n queryset.prefetchRelated = vi.fn((...rels: readonly string[]) =>\n prefetchRelatedImpl(...rels)\n ) as unknown as QuerySet<TModel, TResult>['prefetchRelated'];\n queryset.fetch = vi.fn(fetchImpl) as unknown as QuerySet<TModel, TResult>['fetch'];\n queryset.fetchOne = vi.fn(fetchOneImpl) as unknown as QuerySet<TModel, TResult>['fetchOne'];\n queryset.count = vi.fn(() => countImpl()) as QuerySet<TModel, TResult>['count'];\n queryset.exists = vi.fn(() => existsImpl()) as QuerySet<TModel, TResult>['exists'];\n\n return queryset;\n}\n","import { vi } from 'vitest';\nimport type { ManagerLike, QuerySet } from '@danceroutine/tango-orm';\nimport type { TableMeta } from '@danceroutine/tango-orm/query';\nimport { aQuerySet } from './aQuerySet';\n\nexport type ManagerOverrides<TModel extends Record<string, unknown>> = {\n meta?: TableMeta;\n querySet?: QuerySet<TModel>;\n query?: ManagerLike<TModel>['query'];\n all?: ManagerLike<TModel>['all'];\n getOrCreate?: ManagerLike<TModel>['getOrCreate'];\n updateOrCreate?: ManagerLike<TModel>['updateOrCreate'];\n findById?: ManagerLike<TModel>['findById'];\n getOrThrow?: ManagerLike<TModel>['getOrThrow'];\n create?: ManagerLike<TModel>['create'];\n update?: ManagerLike<TModel>['update'];\n delete?: ManagerLike<TModel>['delete'];\n bulkCreate?: ManagerLike<TModel>['bulkCreate'];\n};\n\n/**\n * Create a manager-shaped test double for resource and service tests.\n */\nexport function aManager<TModel extends Record<string, unknown>>(\n overrides: ManagerOverrides<TModel> = {}\n): ManagerLike<TModel> {\n const meta = overrides.meta ?? { table: 'mock_table', pk: 'id', columns: {} };\n const querySet = overrides.querySet ?? aQuerySet<TModel>();\n type ModelId = TModel[keyof TModel];\n\n const queryImpl = overrides.query ?? (() => querySet);\n const allImpl = overrides.all ?? (() => queryImpl());\n const findByIdImpl = overrides.findById ?? (async () => null as TModel | null);\n const getOrThrowImpl =\n overrides.getOrThrow ??\n (async (id: ModelId) => {\n const record = await findByIdImpl(id);\n if (!record) {\n throw new Error(`No ${meta.table} record found for ${String(meta.pk)}=${String(id)}.`);\n }\n return record;\n });\n const createImpl = overrides.create ?? (async (input: Partial<TModel>) => input as TModel);\n const updateImpl = overrides.update ?? (async (_id: ModelId, patch: Partial<TModel>) => patch as TModel);\n const deleteImpl = overrides.delete ?? (async (_id: ModelId) => {});\n const bulkCreateImpl = overrides.bulkCreate ?? (async (inputs: Partial<TModel>[]) => inputs as TModel[]);\n const getOrCreateImpl =\n overrides.getOrCreate ??\n (async ({ defaults }: Parameters<ManagerLike<TModel>['getOrCreate']>[0]) => ({\n record: { ...defaults } as TModel,\n created: true,\n }));\n const updateOrCreateImpl =\n overrides.updateOrCreate ??\n (async ({ defaults }: Parameters<ManagerLike<TModel>['updateOrCreate']>[0]) => ({\n record: { ...defaults } as TModel,\n created: true,\n updated: false,\n }));\n\n return {\n meta,\n query: vi.fn(() => queryImpl()),\n all: vi.fn(() => allImpl()),\n getOrCreate: vi.fn((args: Parameters<ManagerLike<TModel>['getOrCreate']>[0]) => getOrCreateImpl(args)),\n updateOrCreate: vi.fn((args: Parameters<ManagerLike<TModel>['updateOrCreate']>[0]) => updateOrCreateImpl(args)),\n findById: vi.fn((id: ModelId) => findByIdImpl(id)),\n getOrThrow: vi.fn((id: ModelId) => getOrThrowImpl(id)),\n create: vi.fn((input: Partial<TModel>) => createImpl(input)),\n update: vi.fn((id: ModelId, patch: Partial<TModel>) => updateImpl(id, patch)),\n delete: vi.fn((id: ModelId) => deleteImpl(id)),\n bulkCreate: vi.fn((inputs: Partial<TModel>[]) => bulkCreateImpl(inputs)),\n };\n}\n","// Import through the package subpath so fixtures stay aligned with the public nominal type.\nimport { RequestContext, type BaseUser } from '@danceroutine/tango-resources/context';\n\ntype RequestContextFactory<TUser, TContext extends RequestContextLike<TUser>> = (\n request: Request,\n user: TUser | null\n) => TContext;\n\ntype RequestContextLike<TUser> = {\n request: Request;\n user: TUser | null;\n params: Record<string, string>;\n};\n\nexport type RequestContextFixtureOptions<\n TUser = BaseUser,\n TContext extends RequestContextLike<TUser> = RequestContext<TUser>,\n> = {\n method?: string;\n url?: string;\n body?: unknown;\n user?: TUser | null;\n params?: Record<string, string>;\n headers?: HeadersInit;\n contextFactory?: RequestContextFactory<TUser, TContext>;\n};\n\n/**\n * Create a RequestContext fixture with optional method/url/body/user/params.\n */\nexport function aRequestContext<TUser = BaseUser, TContext extends RequestContextLike<TUser> = RequestContext<TUser>>(\n method: string,\n url: string,\n body?: unknown\n): TContext;\nexport function aRequestContext<TUser = BaseUser, TContext extends RequestContextLike<TUser> = RequestContext<TUser>>(\n options?: RequestContextFixtureOptions<TUser, TContext>\n): TContext;\nexport function aRequestContext<TUser = BaseUser, TContext extends RequestContextLike<TUser> = RequestContext<TUser>>(\n optionsOrMethod: RequestContextFixtureOptions<TUser, TContext> | string = {},\n urlArg?: string,\n bodyArg?: unknown\n): TContext {\n const resolvedOptions: RequestContextFixtureOptions<TUser, TContext> =\n typeof optionsOrMethod === 'string'\n ? {\n method: optionsOrMethod,\n url: urlArg,\n body: bodyArg,\n }\n : optionsOrMethod;\n const {\n method = 'GET',\n url = 'https://example.test',\n body,\n user = null,\n params = {},\n headers,\n contextFactory,\n } = resolvedOptions;\n\n const resolvedHeaders: HeadersInit | undefined =\n body === undefined ? headers : { 'content-type': 'application/json', ...headers };\n\n const request = new Request(url, {\n method,\n headers: resolvedHeaders,\n body: body === undefined ? undefined : JSON.stringify(body),\n });\n const createContext =\n contextFactory ?? ((req: Request, currentUser: TUser | null) => RequestContext.create<TUser>(req, currentUser));\n const context = createContext(request, user) as TContext;\n context.params = params;\n return context;\n}\n","import type { RelationMeta, TableMeta } from '@danceroutine/tango-orm/query';\n\ntype RelationMetaInput = {\n kind: RelationMeta['kind'];\n table: string;\n sourceKey: string;\n targetKey: string;\n targetColumns: Record<string, string>;\n alias: string;\n edgeId?: string;\n sourceModelKey?: string;\n targetModelKey?: string;\n targetPrimaryKey?: string;\n targetMeta?: TableMeta;\n capabilities?: Partial<RelationMeta['capabilities']>;\n};\n\n/**\n * Build recursive relation metadata fixtures for planner/compiler/query tests.\n */\nexport function aRelationMeta(input: RelationMetaInput): RelationMeta {\n const cardinality = input.kind === 'belongsTo' || input.kind === 'hasOne' ? 'single' : 'many';\n const defaultHydratable = input.kind !== ('manyToMany' as RelationMeta['kind']);\n\n const targetMeta =\n input.targetMeta ??\n ({\n modelKey: input.targetModelKey ?? `${input.alias}:target`,\n table: input.table,\n pk: input.targetPrimaryKey ?? 'id',\n columns: input.targetColumns,\n } satisfies TableMeta);\n\n return {\n edgeId: input.edgeId ?? `${input.alias}:${input.sourceKey}:${input.targetKey}`,\n sourceModelKey: input.sourceModelKey ?? `${input.alias}:source`,\n targetModelKey: input.targetModelKey ?? `${input.alias}:target`,\n kind: input.kind,\n cardinality,\n capabilities: {\n queryable: true,\n hydratable: defaultHydratable,\n joinable: cardinality === 'single' && defaultHydratable,\n prefetchable: defaultHydratable,\n ...input.capabilities,\n },\n table: input.table,\n sourceKey: input.sourceKey,\n targetKey: input.targetKey,\n targetPrimaryKey: input.targetPrimaryKey ?? 'id',\n targetColumns: input.targetColumns,\n alias: input.alias,\n targetMeta,\n };\n}\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport { aDBClient } from './aDBClient';\nexport { aManager } from './aManager';\nexport { aQueryResult } from './aQueryResult';\nexport { aQuerySet } from './aQuerySet';\nexport { aRequestContext } from './aRequestContext';\nexport { aQueryExecutor } from './aQueryExecutor';\nexport { aRelationMeta } from './aRelationMeta';\nexport type { QueryExecutorOverrides } from './aQueryExecutor';\nexport type { ManagerOverrides } from './aManager';\nexport type { DBClient } from './DBClient';\nexport type { MockQuerySetResult } from './MockQuerySetResult';\nexport type { RequestContextFixtureOptions } from './aRequestContext';\n"],"mappings":";;;;;;;;AAWO,SAAS,aAAqBA,YAA2C,CAAE,GAAuB;CACrG,MAAM,QAAQ,UAAU,SAAS,UAAU,WAAY,CAAE;AACzD,QAAO,IAAI,iBAAiB;AAC/B;;;;ACCM,SAAS,eACZC,YAA4C,CAAE,GACzB;CACrB,MAAM,UAAU,UAAU,WAAW;CACrC,MAAMC,OAAkB,UAAU,QAAQ;EAAE,OAAO;EAAc,IAAI;EAAM,SAAS,CAAE;CAAE;CACxF,MAAM,MAAM,UAAU,OAAO,KAAG,GAAG,YAAY,CAAE,EAAa;CAC9D,MAAM,SAAS,UAAU,UAAU,QAAQ,EAAE,OAAO,UAAU,MAAO,IAAG,CAAE,EAAC;AAE3E,QAAO;EAAE;EAAM;EAAQ;EAAS;CAAK;AACxC;;;;ACZM,SAAS,UACZC,YAAgD,CAAE,GACzB;CACzB,MAAM,WAAW,IAAI,cAA+B,gBAAwB;CAC5E,MAAMC,aAAkD,UAAU,WAAW,CAAC,WAAW;CACzF,MAAMC,cAAoD,UAAU,YAAY,CAAC,WAAW;CAC5F,MAAMC,cAAoD,UAAU,YAAY,CAAC,GAAG,YAAY;CAChG,MAAMC,YAAgD,UAAU,UAAU,CAAC,OAAO;CAClF,MAAMC,aAAkD,UAAU,WAAW,CAAC,OAAO;CACrF,MAAM,gBAAiB,CAACC,UAAqC;CAI7D,MAAM,aAAa,UAAU,UAAU;CACvC,MAAM,oBACD,UAAU,kBACV,CAAC,GAAG,UAA6B;CACtC,MAAM,sBACD,UAAU,oBACV,CAAC,GAAG,UAA6B;CACtC,MAAMC,YACF,UAAU,UACT,OAAsBC,WAAoE,cAAmB;CAClH,MAAMC,eACF,UAAU,aACT,OAAsBD,WAAoE;CAC/F,MAAME,YAAgD,UAAU,UAAU,YAAY;CACtF,MAAMC,aAAkD,UAAU,WAAW,YAAY;AAEzF,UAAS,SAAS,KAAG,GAAG,CAACC,UACrB,WAAW,MAAM,CACpB;AACD,UAAS,UAAU,KAAG,GAAG,CAACC,UACtB,YAAY,MAAM,CACrB;AACD,UAAS,UAAU,KAAG,GAAG,CAAC,GAAG,WACzB,YAAY,GAAG,OAAO,CACzB;AACD,UAAS,QAAQ,KAAG,GAAG,CAACC,MAAc,UAAU,EAAE,CAAC;AACnD,UAAS,SAAS,KAAG,GAAG,CAACA,MAAc,WAAW,EAAE,CAAC;AACrD,UAAS,SAAS,KAAG,GAAG,CAACC,SACrB,WAAW,KAAK,CACnB;AACD,UAAS,gBAAgB,KAAG,GAAG,CAAC,GAAG,SAA4B,kBAAkB,GAAG,KAAK,CAAC;AAI1F,UAAS,kBAAkB,KAAG,GAAG,CAAC,GAAG,SACjC,oBAAoB,GAAG,KAAK,CAC/B;AACD,UAAS,QAAQ,KAAG,GAAG,UAAU;AACjC,UAAS,WAAW,KAAG,GAAG,aAAa;AACvC,UAAS,QAAQ,KAAG,GAAG,MAAM,WAAW,CAAC;AACzC,UAAS,SAAS,KAAG,GAAG,MAAM,YAAY,CAAC;AAE3C,QAAO;AACV;;;;AC7CM,SAAS,SACZC,YAAsC,CAAE,GACrB;CACnB,MAAM,OAAO,UAAU,QAAQ;EAAE,OAAO;EAAc,IAAI;EAAM,SAAS,CAAE;CAAE;CAC7E,MAAM,WAAW,UAAU,YAAY,WAAmB;CAG1D,MAAM,YAAY,UAAU,UAAU,MAAM;CAC5C,MAAM,UAAU,UAAU,QAAQ,MAAM,WAAW;CACnD,MAAM,eAAe,UAAU,aAAa,YAAY;CACxD,MAAM,iBACF,UAAU,eACT,OAAOC,OAAgB;EACpB,MAAM,SAAS,MAAM,aAAa,GAAG;AACrC,OAAK,OACD,OAAM,IAAI,OAAO,KAAK,KAAK,MAAM,oBAAoB,OAAO,KAAK,GAAG,CAAC,GAAG,OAAO,GAAG,CAAC;AAEvF,SAAO;CACV;CACL,MAAM,aAAa,UAAU,WAAW,OAAOC,UAA2B;CAC1E,MAAM,aAAa,UAAU,WAAW,OAAOC,KAAcC,UAA2B;CACxF,MAAM,aAAa,UAAU,WAAW,OAAOD,QAAiB,CAAE;CAClE,MAAM,iBAAiB,UAAU,eAAe,OAAOE,WAA8B;CACrF,MAAM,kBACF,UAAU,gBACT,OAAO,EAAE,UAA6D,MAAM;EACzE,QAAQ,EAAE,GAAG,SAAU;EACvB,SAAS;CACZ;CACL,MAAM,qBACF,UAAU,mBACT,OAAO,EAAE,UAAgE,MAAM;EAC5E,QAAQ,EAAE,GAAG,SAAU;EACvB,SAAS;EACT,SAAS;CACZ;AAEL,QAAO;EACH;EACA,OAAO,GAAG,GAAG,MAAM,WAAW,CAAC;EAC/B,KAAK,GAAG,GAAG,MAAM,SAAS,CAAC;EAC3B,aAAa,GAAG,GAAG,CAACC,SAA4D,gBAAgB,KAAK,CAAC;EACtG,gBAAgB,GAAG,GAAG,CAACC,SAA+D,mBAAmB,KAAK,CAAC;EAC/G,UAAU,GAAG,GAAG,CAACN,OAAgB,aAAa,GAAG,CAAC;EAClD,YAAY,GAAG,GAAG,CAACA,OAAgB,eAAe,GAAG,CAAC;EACtD,QAAQ,GAAG,GAAG,CAACC,UAA2B,WAAW,MAAM,CAAC;EAC5D,QAAQ,GAAG,GAAG,CAACD,IAAaG,UAA2B,WAAW,IAAI,MAAM,CAAC;EAC7E,QAAQ,GAAG,GAAG,CAACH,OAAgB,WAAW,GAAG,CAAC;EAC9C,YAAY,GAAG,GAAG,CAACI,WAA8B,eAAe,OAAO,CAAC;CAC3E;AACJ;;;;ACnCM,SAAS,gBACZG,kBAA0E,CAAE,GAC5EC,QACAC,SACQ;CACR,MAAMC,yBACK,oBAAoB,WACrB;EACI,QAAQ;EACR,KAAK;EACL,MAAM;CACT,IACD;CACV,MAAM,EACF,SAAS,OACT,MAAM,wBACN,MACA,OAAO,MACP,SAAS,CAAE,GACX,SACA,gBACH,GAAG;CAEJ,MAAMC,kBACF,SAAS,YAAY,UAAU;EAAE,gBAAgB;EAAoB,GAAG;CAAS;CAErF,MAAM,UAAU,IAAI,QAAQ,KAAK;EAC7B;EACA,SAAS;EACT,MAAM,SAAS,YAAY,YAAY,KAAK,UAAU,KAAK;CAC9D;CACD,MAAM,gBACF,mBAAmB,CAACC,KAAcC,gBAA8B,eAAe,OAAc,KAAK,YAAY;CAClH,MAAM,UAAU,cAAc,SAAS,KAAK;AAC5C,SAAQ,SAAS;AACjB,QAAO;AACV;;;;ACtDM,SAAS,cAAcC,OAAwC;CAClE,MAAM,cAAc,MAAM,SAAS,eAAe,MAAM,SAAS,WAAW,WAAW;CACvF,MAAM,oBAAoB,MAAM,SAAU;CAE1C,MAAM,aACF,MAAM,cACL;EACG,UAAU,MAAM,mBAAmB,EAAE,MAAM,MAAM;EACjD,OAAO,MAAM;EACb,IAAI,MAAM,oBAAoB;EAC9B,SAAS,MAAM;CAClB;AAEL,QAAO;EACH,QAAQ,MAAM,WAAW,EAAE,MAAM,MAAM,GAAG,MAAM,UAAU,GAAG,MAAM,UAAU;EAC7E,gBAAgB,MAAM,mBAAmB,EAAE,MAAM,MAAM;EACvD,gBAAgB,MAAM,mBAAmB,EAAE,MAAM,MAAM;EACvD,MAAM,MAAM;EACZ;EACA,cAAc;GACV,WAAW;GACX,YAAY;GACZ,UAAU,gBAAgB,YAAY;GACtC,cAAc;GACd,GAAG,MAAM;EACZ;EACD,OAAO,MAAM;EACb,WAAW,MAAM;EACjB,WAAW,MAAM;EACjB,kBAAkB,MAAM,oBAAoB;EAC5C,eAAe,MAAM;EACrB,OAAO,MAAM;EACb;CACH;AACJ"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"vitest-DmamRJ18.js","names":["value: unknown","activeHarness: IntegrationHarness | null","input: IntegrationHarness | (() => IntegrationHarness | Promise<IntegrationHarness>)","tangoHelpers: TangoVitestHelpers","harness?: IntegrationHarness","table: string","rows: T[]","options: {\n meta: import('@danceroutine/tango-orm/query').TableMeta;\n harness?: IntegrationHarness;\n }","actual: Promise<T> | T","expected: T","received: unknown","schema: Parseable","key: string","value: TValue","work: () => TResult | Promise<TResult>"],"sources":["../src/vitest/registerVitestTango.ts","../src/vitest/withGlobalTestApi.ts","../src/vitest/index.ts"],"sourcesContent":["/**\n * Vitest custom matchers and helpers for Tango.\n *\n * Import this module in your Vitest setup file:\n *\n * ```typescript\n * import '@danceroutine/tango-testing/vitest';\n * ```\n */\nimport { expect, vi } from 'vitest';\nimport {\n TestHarness,\n applyAndVerifyMigrations as applyAndVerifyMigrationsFn,\n assertMigrationPlan as assertMigrationPlanFn,\n createQuerySetFixture as createQuerySetFixtureFn,\n expectQueryResult as expectQueryResultFn,\n introspectSchema as introspectSchemaFn,\n seedTable as seedTableFn,\n type ApplyAndVerifyMigrationsOptions,\n type AssertMigrationPlanOptions,\n type HarnessStrategyRegistry,\n type IntegrationHarness,\n} from '../integration';\n\ninterface Parseable {\n parse(data: unknown): unknown;\n}\n\nfunction isError(value: unknown): value is Error {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as { name?: unknown }).name === 'string' &&\n typeof (value as { message?: unknown }).message === 'string'\n );\n}\n\nlet activeHarness: IntegrationHarness | null = null;\n\nasync function resolveHarness(\n input: IntegrationHarness | (() => IntegrationHarness | Promise<IntegrationHarness>)\n): Promise<IntegrationHarness> {\n if (typeof input === 'function') {\n return input();\n }\n return input;\n}\n\nexport interface TangoVitestHelpers {\n useHarness(\n input: IntegrationHarness | (() => IntegrationHarness | Promise<IntegrationHarness>)\n ): Promise<IntegrationHarness>;\n getTestHarness(): IntegrationHarness;\n getRegistry(): HarnessStrategyRegistry;\n assertMigrationPlan(options: AssertMigrationPlanOptions & { harness?: IntegrationHarness }): Promise<string>;\n applyAndVerifyMigrations(\n options: ApplyAndVerifyMigrationsOptions & { harness?: IntegrationHarness }\n ): Promise<{ statuses: { id: string; applied: boolean }[] }>;\n introspectSchema(harness?: IntegrationHarness): Promise<unknown>;\n seedTable<T extends Record<string, unknown>>(table: string, rows: T[], harness?: IntegrationHarness): Promise<void>;\n createQuerySetFixture<TModel extends Record<string, unknown>>(options: {\n meta: import('@danceroutine/tango-orm/query').TableMeta;\n harness?: IntegrationHarness;\n }): import('@danceroutine/tango-orm').QuerySet<TModel>;\n expectQueryResult<T>(actual: Promise<T> | T, expected: T): Promise<void>;\n}\n\nconst tangoHelpers: TangoVitestHelpers = {\n async useHarness(input): Promise<IntegrationHarness> {\n const harness = await resolveHarness(input);\n await harness.setup();\n activeHarness = harness;\n return harness;\n },\n getTestHarness(): IntegrationHarness {\n if (!activeHarness) {\n throw new Error('No active test harness. Call vi.tango.useHarness(...) in beforeAll first.');\n }\n return activeHarness;\n },\n getRegistry(): HarnessStrategyRegistry {\n return TestHarness.getRegistry();\n },\n async assertMigrationPlan(options): Promise<string> {\n const harness = options.harness ?? tangoHelpers.getTestHarness();\n return assertMigrationPlanFn(harness, {\n migrationsDir: options.migrationsDir,\n expectSqlContains: options.expectSqlContains,\n });\n },\n async applyAndVerifyMigrations(options): Promise<{ statuses: { id: string; applied: boolean }[] }> {\n const harness = options.harness ?? tangoHelpers.getTestHarness();\n return applyAndVerifyMigrationsFn(harness, {\n migrationsDir: options.migrationsDir,\n toId: options.toId,\n expectedAppliedIds: options.expectedAppliedIds,\n });\n },\n async introspectSchema(harness?: IntegrationHarness): Promise<unknown> {\n return introspectSchemaFn(harness ?? tangoHelpers.getTestHarness());\n },\n async seedTable<T extends Record<string, unknown>>(\n table: string,\n rows: T[],\n harness?: IntegrationHarness\n ): Promise<void> {\n await seedTableFn(harness ?? tangoHelpers.getTestHarness(), table, rows);\n },\n createQuerySetFixture<TModel extends Record<string, unknown>>(options: {\n meta: import('@danceroutine/tango-orm/query').TableMeta;\n harness?: IntegrationHarness;\n }): import('@danceroutine/tango-orm').QuerySet<TModel> {\n return createQuerySetFixtureFn<TModel>({\n harness: options.harness ?? tangoHelpers.getTestHarness(),\n meta: options.meta,\n });\n },\n async expectQueryResult<T>(actual: Promise<T> | T, expected: T): Promise<void> {\n await expectQueryResultFn(actual, expected);\n },\n};\n\nexpect.extend({\n toMatchSchema(received: unknown, schema: Parseable) {\n try {\n schema.parse(received);\n return {\n pass: true,\n message: () => 'expected data not to match schema',\n };\n } catch (error) {\n const detail = isError(error) ? error.message : String(error);\n return {\n pass: false,\n message: () => `expected data to match schema\\n\\n${detail}`,\n };\n }\n },\n});\n\n(vi as unknown as { tango?: TangoVitestHelpers }).tango = tangoHelpers;\n\ndeclare module 'vitest' {\n // oxlint-disable-next-line no-unused-vars\n interface Assertion<T> {\n toMatchSchema(schema: Parseable): void;\n }\n\n interface AsymmetricMatchersContaining {\n toMatchSchema(schema: Parseable): void;\n }\n\n interface VitestUtils {\n tango: TangoVitestHelpers;\n }\n}\n","/**\n * Install a temporary global test API for the duration of one callback.\n *\n * This is useful for module-loading tests that need a stable escape hatch into\n * already-imported symbols without introducing a second copy of the same package.\n */\nexport async function withGlobalTestApi<TValue, TResult>(\n key: string,\n value: TValue,\n work: () => TResult | Promise<TResult>\n): Promise<TResult> {\n const globalRecord = globalThis as Record<string, unknown>;\n const previousValue = globalRecord[key];\n const hadPreviousValue = Object.prototype.hasOwnProperty.call(globalRecord, key);\n\n globalRecord[key] = value;\n try {\n return await work();\n } finally {\n if (hadPreviousValue) {\n globalRecord[key] = previousValue;\n } else {\n delete globalRecord[key];\n }\n }\n}\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport * from './registerVitestTango';\nexport * from './withGlobalTestApi';\n"],"mappings":";;;;;AA4BA,SAAS,QAAQA,OAAgC;AAC7C,eACW,UAAU,YACjB,UAAU,eACF,MAA6B,SAAS,mBACtC,MAAgC,YAAY;AAE3D;AAED,IAAIC,gBAA2C;AAE/C,eAAe,eACXC,OAC2B;AAC3B,YAAW,UAAU,WACjB,QAAO,OAAO;AAElB,QAAO;AACV;AAqBD,MAAMC,eAAmC;CACrC,MAAM,WAAW,OAAoC;EACjD,MAAM,UAAU,MAAM,eAAe,MAAM;AAC3C,QAAM,QAAQ,OAAO;AACrB,kBAAgB;AAChB,SAAO;CACV;CACD,iBAAqC;AACjC,OAAK,cACD,OAAM,IAAI,MAAM;AAEpB,SAAO;CACV;CACD,cAAuC;AACnC,SAAO,YAAY,aAAa;CACnC;CACD,MAAM,oBAAoB,SAA0B;EAChD,MAAM,UAAU,QAAQ,WAAW,aAAa,gBAAgB;AAChE,SAAO,oBAAsB,SAAS;GAClC,eAAe,QAAQ;GACvB,mBAAmB,QAAQ;EAC9B,EAAC;CACL;CACD,MAAM,yBAAyB,SAAoE;EAC/F,MAAM,UAAU,QAAQ,WAAW,aAAa,gBAAgB;AAChE,SAAO,yBAA2B,SAAS;GACvC,eAAe,QAAQ;GACvB,MAAM,QAAQ;GACd,oBAAoB,QAAQ;EAC/B,EAAC;CACL;CACD,MAAM,iBAAiBC,SAAgD;AACnE,SAAO,iBAAmB,WAAW,aAAa,gBAAgB,CAAC;CACtE;CACD,MAAM,UACFC,OACAC,MACAF,SACa;AACb,QAAM,UAAY,WAAW,aAAa,gBAAgB,EAAE,OAAO,KAAK;CAC3E;CACD,sBAA8DG,SAGP;AACnD,SAAO,sBAAgC;GACnC,SAAS,QAAQ,WAAW,aAAa,gBAAgB;GACzD,MAAM,QAAQ;EACjB,EAAC;CACL;CACD,MAAM,kBAAqBC,QAAwBC,UAA4B;AAC3E,QAAM,kBAAoB,QAAQ,SAAS;CAC9C;AACJ;AAED,OAAO,OAAO,EACV,cAAcC,UAAmBC,QAAmB;AAChD,KAAI;AACA,SAAO,MAAM,SAAS;AACtB,SAAO;GACH,MAAM;GACN,SAAS,MAAM;EAClB;CACJ,SAAQ,OAAO;EACZ,MAAM,SAAS,QAAQ,MAAM,GAAG,MAAM,UAAU,OAAO,MAAM;AAC7D,SAAO;GACH,MAAM;GACN,SAAS,OAAO,mCAAmC,OAAO;EAC7D;CACJ;AACJ,EACJ,EAAC;AAEF,GAAkD,QAAQ;;;;ACtInD,eAAe,kBAClBC,KACAC,OACAC,MACgB;CAChB,MAAM,eAAe;CACrB,MAAM,gBAAgB,aAAa;CACnC,MAAM,mBAAmB,OAAO,UAAU,eAAe,KAAK,cAAc,IAAI;AAEhF,cAAa,OAAO;AACpB,KAAI;AACA,SAAO,MAAM,MAAM;CACtB,UAAS;AACN,MAAI,iBACA,cAAa,OAAO;IAEpB,QAAO,aAAa;CAE3B;AACJ"}
|