@danceroutine/tango-testing 1.1.2 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -3
- package/dist/mocks/MockQuerySetResult.d.ts +2 -1
- package/dist/mocks/aQuerySet.d.ts +1 -1
- package/dist/mocks/index.js +1 -1
- package/dist/{mocks-BkwkXQQt.js → mocks-JCG0Cdwf.js} +3 -2
- package/dist/mocks-JCG0Cdwf.js.map +1 -0
- package/dist/vitest/index.d.ts +1 -0
- package/dist/vitest/index.js +3 -1
- package/dist/vitest/withGlobalTestApi.d.ts +7 -0
- package/dist/{vitest-37qN8D93.js → vitest-DmamRJ18.js} +19 -2
- package/dist/vitest-DmamRJ18.js.map +1 -0
- package/package.json +6 -6
- package/dist/mocks-BkwkXQQt.js.map +0 -1
- package/dist/vitest-37qN8D93.js.map +0 -1
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { aDBClient } from "./aDBClient-W6eXsK3X.js";
|
|
2
|
-
import { aManager, aQueryExecutor, aQueryResult, aQuerySet, aRequestContext, mocks_exports } from "./mocks-
|
|
2
|
+
import { aManager, aQueryExecutor, aQueryResult, aQuerySet, aRequestContext, mocks_exports } from "./mocks-JCG0Cdwf.js";
|
|
3
3
|
import { ModelDataFactory, factories_exports } from "./factories-Cl_CAzbj.js";
|
|
4
4
|
import { assertions, assertions_exports } from "./assertions-CCFZ53Y-.js";
|
|
5
5
|
import { AppProcessHarness, Dialect, HarnessStrategyRegistry, ResetMode, TestHarness, aTangoConfig, anIntegrationHarness, applyAndVerifyMigrations, assertMigrationPlan, conformance_exports, createQuerySetFixture, domain_exports, expectQueryResult, integration_exports, introspectSchema, migrations_exports, runDialectConformanceSuite, runtime_exports, seedTable, setupTestTangoRuntime, smoke_exports } from "./integration-BrJw6NzG.js";
|
|
6
|
-
import { vitest_exports } from "./vitest-
|
|
6
|
+
import { vitest_exports, withGlobalTestApi } from "./vitest-DmamRJ18.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, aRequestContext, aTangoConfig, 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 };
|
|
9
|
+
export { AppProcessHarness, Dialect, HarnessStrategyRegistry, ModelDataFactory, ResetMode, TestHarness, aDBClient, aManager, aQueryExecutor, aQueryResult, aQuerySet, aRequestContext, aTangoConfig, 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 };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { QuerySet } from '@danceroutine/tango-orm';
|
|
2
2
|
/**
|
|
3
|
+
* @deprecated Use QuerySet instead.
|
|
3
4
|
* Legacy alias for query-set test doubles.
|
|
4
5
|
*/
|
|
5
|
-
export type MockQuerySetResult<TModel extends Record<string, unknown
|
|
6
|
+
export type MockQuerySetResult<TModel extends Record<string, unknown>, TResult extends Record<string, unknown> = TModel> = QuerySet<TModel, TResult>;
|
|
@@ -5,4 +5,4 @@ import type { QuerySet } from '@danceroutine/tango-orm';
|
|
|
5
5
|
* All methods are wrapped in `vi.fn()` so they can be asserted on directly
|
|
6
6
|
* without an additional `vi.mocked()` call.
|
|
7
7
|
*/
|
|
8
|
-
export declare function aQuerySet<TModel extends Record<string, unknown
|
|
8
|
+
export declare function aQuerySet<TModel extends Record<string, unknown>, TResult extends Record<string, unknown> = TModel>(overrides?: Partial<QuerySet<TModel, TResult>>): QuerySet<TModel, TResult>;
|
package/dist/mocks/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { aDBClient } from "../aDBClient-W6eXsK3X.js";
|
|
2
|
-
import { aManager, aQueryExecutor, aQueryResult, aQuerySet, aRequestContext } from "../mocks-
|
|
2
|
+
import { aManager, aQueryExecutor, aQueryResult, aQuerySet, aRequestContext } from "../mocks-JCG0Cdwf.js";
|
|
3
3
|
|
|
4
4
|
export { aDBClient, aManager, aQueryExecutor, aQueryResult, aQuerySet, aRequestContext };
|
|
@@ -41,7 +41,8 @@ function aQuerySet(overrides = {}) {
|
|
|
41
41
|
const orderByImpl = overrides.orderBy ?? ((..._tokens) => queryset);
|
|
42
42
|
const limitImpl = overrides.limit ?? ((_n) => queryset);
|
|
43
43
|
const offsetImpl = overrides.offset ?? ((_n) => queryset);
|
|
44
|
-
const
|
|
44
|
+
const defaultSelect = (_cols) => queryset;
|
|
45
|
+
const selectImpl = overrides.select ?? defaultSelect;
|
|
45
46
|
const selectRelatedImpl = overrides.selectRelated ?? ((..._rels) => queryset);
|
|
46
47
|
const prefetchRelatedImpl = overrides.prefetchRelated ?? ((..._rels) => queryset);
|
|
47
48
|
const fetchImpl = overrides.fetch ?? (async (_shape) => aQueryResult());
|
|
@@ -133,4 +134,4 @@ __export(mocks_exports, {
|
|
|
133
134
|
|
|
134
135
|
//#endregion
|
|
135
136
|
export { aManager, aQueryExecutor, aQueryResult, aQuerySet, aRequestContext, mocks_exports };
|
|
136
|
-
//# sourceMappingURL=mocks-
|
|
137
|
+
//# sourceMappingURL=mocks-JCG0Cdwf.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mocks-JCG0Cdwf.js","names":["overrides: Partial<QueryResult<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)[]","selectRelatedImpl: QuerySet<TModel, TResult>['selectRelated']","prefetchRelatedImpl: QuerySet<TModel, TResult>['prefetchRelated']","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>[]","optionsOrMethod: RequestContextFixtureOptions<TUser, TContext> | string","urlArg?: string","bodyArg?: unknown","resolvedOptions: RequestContextFixtureOptions<TUser, TContext>","resolvedHeaders: HeadersInit | undefined","req: Request","currentUser: TUser | null"],"sources":["../src/mocks/aQueryResult.ts","../src/mocks/aQueryExecutor.ts","../src/mocks/aQuerySet.ts","../src/mocks/aManager.ts","../src/mocks/aRequestContext.ts","../src/mocks/index.ts"],"sourcesContent":["import type { QueryResult } from '@danceroutine/tango-orm/query';\n\n/**\n * Create a query-result test value with optional overrides.\n */\nexport function aQueryResult<TModel>(overrides: Partial<QueryResult<TModel>> = {}): QueryResult<TModel> {\n return {\n results: [] as TModel[],\n nextCursor: null,\n ...overrides,\n };\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: QuerySet<TModel, TResult>['selectRelated'] =\n overrides.selectRelated ?? ((..._rels) => queryset);\n const prefetchRelatedImpl: QuerySet<TModel, TResult>['prefetchRelated'] =\n overrides.prefetchRelated ?? ((..._rels) => 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: Parameters<QuerySet<TModel, TResult>['selectRelated']>) =>\n selectRelatedImpl(...rels)\n ) as QuerySet<TModel, TResult>['selectRelated'];\n queryset.prefetchRelated = vi.fn((...rels: Parameters<QuerySet<TModel, TResult>['prefetchRelated']>) =>\n prefetchRelatedImpl(...rels)\n ) 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 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 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\n return {\n meta,\n query: vi.fn(() => queryImpl()),\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","/**\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 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":"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"}
|
package/dist/vitest/index.d.ts
CHANGED
package/dist/vitest/index.js
CHANGED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install a temporary global test API for the duration of one callback.
|
|
3
|
+
*
|
|
4
|
+
* This is useful for module-loading tests that need a stable escape hatch into
|
|
5
|
+
* already-imported symbols without introducing a second copy of the same package.
|
|
6
|
+
*/
|
|
7
|
+
export declare function withGlobalTestApi<TValue, TResult>(key: string, value: TValue, work: () => TResult | Promise<TResult>): Promise<TResult>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { __export } from "./chunk-BkvOhyD0.js";
|
|
1
2
|
import { TestHarness, applyAndVerifyMigrations, assertMigrationPlan, createQuerySetFixture, expectQueryResult, introspectSchema, seedTable } from "./integration-BrJw6NzG.js";
|
|
2
3
|
import { expect, vi } from "vitest";
|
|
3
4
|
|
|
@@ -72,10 +73,26 @@ expect.extend({ toMatchSchema(received, schema) {
|
|
|
72
73
|
} });
|
|
73
74
|
vi.tango = tangoHelpers;
|
|
74
75
|
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/vitest/withGlobalTestApi.ts
|
|
78
|
+
async function withGlobalTestApi(key, value, work) {
|
|
79
|
+
const globalRecord = globalThis;
|
|
80
|
+
const previousValue = globalRecord[key];
|
|
81
|
+
const hadPreviousValue = Object.prototype.hasOwnProperty.call(globalRecord, key);
|
|
82
|
+
globalRecord[key] = value;
|
|
83
|
+
try {
|
|
84
|
+
return await work();
|
|
85
|
+
} finally {
|
|
86
|
+
if (hadPreviousValue) globalRecord[key] = previousValue;
|
|
87
|
+
else delete globalRecord[key];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
75
91
|
//#endregion
|
|
76
92
|
//#region src/vitest/index.ts
|
|
77
93
|
var vitest_exports = {};
|
|
94
|
+
__export(vitest_exports, { withGlobalTestApi: () => withGlobalTestApi });
|
|
78
95
|
|
|
79
96
|
//#endregion
|
|
80
|
-
export { vitest_exports };
|
|
81
|
-
//# sourceMappingURL=vitest-
|
|
97
|
+
export { vitest_exports, withGlobalTestApi };
|
|
98
|
+
//# sourceMappingURL=vitest-DmamRJ18.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danceroutine/tango-testing",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
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-core": "1.
|
|
57
|
-
"@danceroutine/tango-migrations": "1.
|
|
58
|
-
"@danceroutine/tango-orm": "1.
|
|
59
|
-
"@danceroutine/tango-resources": "1.
|
|
55
|
+
"@danceroutine/tango-config": "1.2.0",
|
|
56
|
+
"@danceroutine/tango-core": "1.2.0",
|
|
57
|
+
"@danceroutine/tango-migrations": "1.2.0",
|
|
58
|
+
"@danceroutine/tango-orm": "1.2.0",
|
|
59
|
+
"@danceroutine/tango-resources": "1.2.0"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
62
62
|
"@types/express": "^5.0.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mocks-BkwkXQQt.js","names":["overrides: Partial<QueryResult<TModel>>","overrides: QueryExecutorOverrides<TModel>","meta: TableMeta","overrides: Partial<QuerySet<TModel>>","filterImpl: QuerySet<TModel>['filter']","excludeImpl: QuerySet<TModel>['exclude']","orderByImpl: QuerySet<TModel>['orderBy']","limitImpl: QuerySet<TModel>['limit']","offsetImpl: QuerySet<TModel>['offset']","selectImpl: QuerySet<TModel>['select']","selectRelatedImpl: QuerySet<TModel>['selectRelated']","prefetchRelatedImpl: QuerySet<TModel>['prefetchRelated']","fetchImpl: QuerySet<TModel>['fetch']","_shape?: ((r: TModel) => Out) | { parse: (r: TModel) => Out }","fetchOneImpl: QuerySet<TModel>['fetchOne']","countImpl: QuerySet<TModel>['count']","existsImpl: QuerySet<TModel>['exists']","input: Parameters<QuerySet<TModel>['filter']>[0]","input: Parameters<QuerySet<TModel>['exclude']>[0]","n: number","cols: Parameters<QuerySet<TModel>['select']>[0]","overrides: ManagerOverrides<TModel>","id: ModelId","input: Partial<TModel>","_id: ModelId","patch: Partial<TModel>","inputs: Partial<TModel>[]","optionsOrMethod: RequestContextFixtureOptions<TUser, TContext> | string","urlArg?: string","bodyArg?: unknown","resolvedOptions: RequestContextFixtureOptions<TUser, TContext>","resolvedHeaders: HeadersInit | undefined","req: Request","currentUser: TUser | null"],"sources":["../src/mocks/aQueryResult.ts","../src/mocks/aQueryExecutor.ts","../src/mocks/aQuerySet.ts","../src/mocks/aManager.ts","../src/mocks/aRequestContext.ts","../src/mocks/index.ts"],"sourcesContent":["import type { QueryResult } from '@danceroutine/tango-orm/query';\n\n/**\n * Create a query-result test value with optional overrides.\n */\nexport function aQueryResult<TModel>(overrides: Partial<QueryResult<TModel>> = {}): QueryResult<TModel> {\n return {\n results: [] as TModel[],\n nextCursor: null,\n ...overrides,\n };\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>>(\n overrides: Partial<QuerySet<TModel>> = {}\n): QuerySet<TModel> {\n const queryset = new QuerySetClass<TModel>(aQueryExecutor<TModel>());\n const filterImpl: QuerySet<TModel>['filter'] = overrides.filter ?? ((_input) => queryset);\n const excludeImpl: QuerySet<TModel>['exclude'] = overrides.exclude ?? ((_input) => queryset);\n const orderByImpl: QuerySet<TModel>['orderBy'] = overrides.orderBy ?? ((..._tokens) => queryset);\n const limitImpl: QuerySet<TModel>['limit'] = overrides.limit ?? ((_n) => queryset);\n const offsetImpl: QuerySet<TModel>['offset'] = overrides.offset ?? ((_n) => queryset);\n const selectImpl: QuerySet<TModel>['select'] = overrides.select ?? ((_cols) => queryset);\n const selectRelatedImpl: QuerySet<TModel>['selectRelated'] = overrides.selectRelated ?? ((..._rels) => queryset);\n const prefetchRelatedImpl: QuerySet<TModel>['prefetchRelated'] =\n overrides.prefetchRelated ?? ((..._rels) => queryset);\n const fetchImpl: QuerySet<TModel>['fetch'] =\n overrides.fetch ??\n (async <Out = TModel>(_shape?: ((r: TModel) => Out) | { parse: (r: TModel) => Out }) => aQueryResult<Out>());\n const fetchOneImpl: QuerySet<TModel>['fetchOne'] =\n overrides.fetchOne ??\n (async <Out = TModel>(_shape?: ((r: TModel) => Out) | { parse: (r: TModel) => Out }) => null as Out | null);\n const countImpl: QuerySet<TModel>['count'] = overrides.count ?? (async () => 0);\n const existsImpl: QuerySet<TModel>['exists'] = overrides.exists ?? (async () => false);\n\n queryset.filter = vi.fn((input: Parameters<QuerySet<TModel>['filter']>[0]) =>\n filterImpl(input)\n ) as QuerySet<TModel>['filter'];\n queryset.exclude = vi.fn((input: Parameters<QuerySet<TModel>['exclude']>[0]) =>\n excludeImpl(input)\n ) as QuerySet<TModel>['exclude'];\n queryset.orderBy = vi.fn((...tokens: Parameters<QuerySet<TModel>['orderBy']>) =>\n orderByImpl(...tokens)\n ) as QuerySet<TModel>['orderBy'];\n queryset.limit = vi.fn((n: number) => limitImpl(n)) as QuerySet<TModel>['limit'];\n queryset.offset = vi.fn((n: number) => offsetImpl(n)) as QuerySet<TModel>['offset'];\n queryset.select = vi.fn((cols: Parameters<QuerySet<TModel>['select']>[0]) =>\n selectImpl(cols)\n ) as QuerySet<TModel>['select'];\n queryset.selectRelated = vi.fn((...rels: Parameters<QuerySet<TModel>['selectRelated']>) =>\n selectRelatedImpl(...rels)\n ) as QuerySet<TModel>['selectRelated'];\n queryset.prefetchRelated = vi.fn((...rels: Parameters<QuerySet<TModel>['prefetchRelated']>) =>\n prefetchRelatedImpl(...rels)\n ) as QuerySet<TModel>['prefetchRelated'];\n queryset.fetch = vi.fn(fetchImpl) as QuerySet<TModel>['fetch'];\n queryset.fetchOne = vi.fn(fetchOneImpl) as QuerySet<TModel>['fetchOne'];\n queryset.count = vi.fn(() => countImpl()) as QuerySet<TModel>['count'];\n queryset.exists = vi.fn(() => existsImpl()) as QuerySet<TModel>['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 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 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\n return {\n meta,\n query: vi.fn(() => queryImpl()),\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","/**\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 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":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"vitest-37qN8D93.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"],"sources":["../src/vitest/registerVitestTango.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 * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport * from './registerVitestTango';\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"}
|