@danceroutine/tango-testing 0.1.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +85 -0
  3. package/dist/aDBClient-W6eXsK3X.js +21 -0
  4. package/dist/aDBClient-W6eXsK3X.js.map +1 -0
  5. package/dist/assertions/index.js +1 -1
  6. package/dist/{assertions-CN6KxXhH.js → assertions-CCFZ53Y-.js} +1 -1
  7. package/dist/assertions-CCFZ53Y-.js.map +1 -0
  8. package/dist/express/anExpressRequest.d.ts +24 -0
  9. package/dist/express/anExpressResponse.d.ts +9 -0
  10. package/dist/express/index.d.ts +3 -0
  11. package/dist/express/index.js +3 -0
  12. package/dist/express-Czpfz_Ay.js +68 -0
  13. package/dist/express-Czpfz_Ay.js.map +1 -0
  14. package/dist/factories/ModelDataFactory.d.ts +16 -1
  15. package/dist/factories/index.js +1 -1
  16. package/dist/{factories-CCAZ6E-g.js → factories-Cl_CAzbj.js} +19 -4
  17. package/dist/factories-Cl_CAzbj.js.map +1 -0
  18. package/dist/index.d.ts +5 -3
  19. package/dist/index.js +8 -11
  20. package/dist/integration/HarnessStrategyRegistry.d.ts +15 -0
  21. package/dist/integration/TestHarness.d.ts +23 -2
  22. package/dist/integration/anIntegrationHarness.d.ts +5 -0
  23. package/dist/integration/config.d.ts +4 -0
  24. package/dist/integration/conformance/index.d.ts +1 -0
  25. package/dist/integration/conformance/runDialectConformanceSuite.d.ts +11 -0
  26. package/dist/integration/domain/Dialect.d.ts +5 -4
  27. package/dist/integration/domain/ResetMode.d.ts +6 -5
  28. package/dist/integration/index.d.ts +8 -1
  29. package/dist/integration/index.js +3 -2
  30. package/dist/integration/migrations/ApplyAndVerifyMigrations.d.ts +3 -0
  31. package/dist/integration/migrations/AssertMigrationPlan.d.ts +3 -0
  32. package/dist/integration/migrations/IntrospectSchema.d.ts +3 -0
  33. package/dist/integration/orm/createQuerySetFixture.d.ts +10 -0
  34. package/dist/integration/orm/expectQueryResult.d.ts +4 -0
  35. package/dist/integration/orm/index.d.ts +6 -0
  36. package/dist/integration/orm/seedTable.d.ts +5 -0
  37. package/dist/integration/runtime/aTangoConfig.d.ts +8 -0
  38. package/dist/integration/runtime/index.d.ts +6 -0
  39. package/dist/integration/runtime/setupTestTangoRuntime.d.ts +6 -0
  40. package/dist/integration/smoke/AppProcessHarness.d.ts +83 -0
  41. package/dist/integration/smoke/index.d.ts +4 -0
  42. package/dist/integration/strategies/PostgresHarnessStrategy.d.ts +9 -0
  43. package/dist/integration/strategies/SqliteHarnessStrategy.d.ts +9 -0
  44. package/dist/integration-BrJw6NzG.js +747 -0
  45. package/dist/integration-BrJw6NzG.js.map +1 -0
  46. package/dist/mocks/DBClient.d.ts +1 -9
  47. package/dist/mocks/MockQuerySetResult.d.ts +5 -12
  48. package/dist/mocks/aDBClient.d.ts +21 -0
  49. package/dist/mocks/aManager.d.ts +17 -0
  50. package/dist/mocks/aQueryExecutor.d.ts +14 -0
  51. package/dist/mocks/aQueryResult.d.ts +5 -0
  52. package/dist/mocks/aQuerySet.d.ts +8 -0
  53. package/dist/mocks/aRequestContext.d.ts +22 -0
  54. package/dist/mocks/index.d.ts +9 -4
  55. package/dist/mocks/index.js +4 -6
  56. package/dist/mocks-BkwkXQQt.js +136 -0
  57. package/dist/mocks-BkwkXQQt.js.map +1 -0
  58. package/dist/vitest/index.js +3 -2
  59. package/dist/vitest/registerVitestTango.d.ts +3 -3
  60. package/dist/{vitest-PxMJue7R.js → vitest-37qN8D93.js} +4 -4
  61. package/dist/vitest-37qN8D93.js.map +1 -0
  62. package/package.json +81 -68
  63. package/dist/assertions/assertions.js +0 -8
  64. package/dist/assertions-CN6KxXhH.js.map +0 -1
  65. package/dist/factories/ModelDataFactory.js +0 -33
  66. package/dist/factories-CCAZ6E-g.js.map +0 -1
  67. package/dist/index.js.map +0 -1
  68. package/dist/integration/orm.d.ts +0 -9
  69. package/dist/integration/orm.js +0 -39
  70. package/dist/integration/strategies/PostgresHarnessStrategy.js +0 -95
  71. package/dist/integration-CDdpboYz.js +0 -378
  72. package/dist/integration-CDdpboYz.js.map +0 -1
  73. package/dist/mocks/DBClient.js +0 -1
  74. package/dist/mocks/MockQuerySetResult.js +0 -1
  75. package/dist/mocks/RepositoryLike.d.ts +0 -12
  76. package/dist/mocks/RepositoryLike.js +0 -1
  77. package/dist/mocks/aMockDBClient.d.ts +0 -2
  78. package/dist/mocks/aMockDBClient.js +0 -13
  79. package/dist/mocks/aMockQuerySet.d.ts +0 -2
  80. package/dist/mocks/aMockQuerySet.js +0 -15
  81. package/dist/mocks/aMockRepository.d.ts +0 -2
  82. package/dist/mocks/aMockRepository.js +0 -20
  83. package/dist/mocks/types.d.ts +0 -33
  84. package/dist/mocks-qo-1vCez.js +0 -72
  85. package/dist/mocks-qo-1vCez.js.map +0 -1
  86. package/dist/version.d.ts +0 -1
  87. package/dist/vitest/registerVitestTango.js +0 -90
  88. package/dist/vitest-PxMJue7R.js.map +0 -1
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Pedro Del Moral Lopez
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # @danceroutine/tango-testing
2
+
3
+ `@danceroutine/tango-testing` provides Tango's testing helpers for unit tests, integration tests, and smoke-test workflows.
4
+
5
+ This package exists because framework-level testing needs more than a handful of mocks. Once a codebase has managers, query composition, migrations, dialect differences, and real application processes, test support has to cover several levels of confidence at once. Tango keeps those helpers together so that application and framework code can move from fast isolated tests to realistic integration coverage without rebuilding the same harness machinery for every project.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ pnpm add -D @danceroutine/tango-testing vitest
11
+ ```
12
+
13
+ Depending on your tests, you may also need the database driver or Tango packages your application already uses.
14
+
15
+ ## What kinds of testing it supports
16
+
17
+ The package spans three common testing layers:
18
+
19
+ - lightweight mocks for fast unit tests
20
+ - integration harnesses for database-backed query, manager, and migration tests
21
+ - Vitest helpers and smoke-test utilities for higher-level workflows
22
+
23
+ Those layers let one package support fast unit tests, dialect-backed integration tests, and full process-level smoke tests without changing mental models between them.
24
+
25
+ ## Quick start
26
+
27
+ Register the Vitest helper surface:
28
+
29
+ ```ts
30
+ import { beforeAll } from 'vitest';
31
+ import { registerVitestTango } from '@danceroutine/tango-testing/vitest';
32
+
33
+ beforeAll(() => {
34
+ registerVitestTango();
35
+ });
36
+ ```
37
+
38
+ For unit tests, you can use the mock helpers directly:
39
+
40
+ ```ts
41
+ import { aManager } from '@danceroutine/tango-testing';
42
+
43
+ const manager = aManager();
44
+ ```
45
+
46
+ Runtime-focused tests can also start from a shared config and runtime setup helper instead of rebuilding Tango config in every file:
47
+
48
+ ```ts
49
+ import { aTangoConfig, setupTestTangoRuntime } from '@danceroutine/tango-testing';
50
+
51
+ const config = aTangoConfig({ adapter: 'sqlite' });
52
+ const runtime = await setupTestTangoRuntime();
53
+ ```
54
+
55
+ `aTangoConfig` gives tests a stable config fixture, and `setupTestTangoRuntime` resets and initializes the process-default Tango runtime in one call.
56
+
57
+ For integration tests, the package exposes the `TestHarness` and dialect strategies that create real database-backed workflows.
58
+
59
+ ## Public API
60
+
61
+ The root export covers three main jobs. Mock helpers such as `aDBClient`, `aManager`, `aQueryExecutor`, and `aQuerySet` keep unit tests lightweight. Runtime and integration helpers such as `aTangoConfig`, `setupTestTangoRuntime`, `TestHarness`, and `createQuerySetFixture` 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
+
63
+ The package also exposes subpaths such as `mocks`, `factories`, `assertions`, `integration`, and `vitest` when you want a more explicit import boundary.
64
+
65
+ ## Documentation
66
+
67
+ - Official documentation: <https://tangowebframework.dev>
68
+ - Testing topic: <https://tangowebframework.dev/topics/testing>
69
+ - New dialect onboarding: <https://tangowebframework.dev/contributors/how-to/new-dialect-onboarding>
70
+
71
+ ## Development
72
+
73
+ ```bash
74
+ pnpm --filter @danceroutine/tango-testing build
75
+ pnpm --filter @danceroutine/tango-testing typecheck
76
+ pnpm --filter @danceroutine/tango-testing test
77
+ ```
78
+
79
+ For the wider contributor workflow, use:
80
+
81
+ - <https://tangowebframework.dev/contributing>
82
+
83
+ ## License
84
+
85
+ MIT
@@ -0,0 +1,21 @@
1
+ import { vi } from "vitest";
2
+
3
+ //#region src/mocks/aDBClient.ts
4
+ function aDBClient(overrides = {}) {
5
+ const queryImpl = overrides.query ?? (async (_sql, _params) => ({ rows: [] }));
6
+ const beginImpl = overrides.begin ?? (async () => {});
7
+ const commitImpl = overrides.commit ?? (async () => {});
8
+ const rollbackImpl = overrides.rollback ?? (async () => {});
9
+ const closeImpl = overrides.close ?? (async () => {});
10
+ return {
11
+ query: vi.fn((sql, params) => queryImpl(sql, params)),
12
+ begin: vi.fn(() => beginImpl()),
13
+ commit: vi.fn(() => commitImpl()),
14
+ rollback: vi.fn(() => rollbackImpl()),
15
+ close: vi.fn(() => closeImpl())
16
+ };
17
+ }
18
+
19
+ //#endregion
20
+ export { aDBClient };
21
+ //# sourceMappingURL=aDBClient-W6eXsK3X.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aDBClient-W6eXsK3X.js","names":["overrides: DBClientOverrides","_sql: string","_params?: readonly unknown[]","sql: string","params?: readonly unknown[]"],"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};\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\n return {\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 };\n}\n"],"mappings":";;;AAqBO,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;AAEpD,QAAO;EACH,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;CAClC;AACJ"}
@@ -1,3 +1,3 @@
1
- import { assertions } from "../assertions-CN6KxXhH.js";
1
+ import { assertions } from "../assertions-CCFZ53Y-.js";
2
2
 
3
3
  export { assertions };
@@ -12,4 +12,4 @@ __export(assertions_exports, { assertions: () => assertions });
12
12
 
13
13
  //#endregion
14
14
  export { assertions, assertions_exports };
15
- //# sourceMappingURL=assertions-CN6KxXhH.js.map
15
+ //# sourceMappingURL=assertions-CCFZ53Y-.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assertions-CCFZ53Y-.js","names":["model: TModel","data: unknown"],"sources":["../src/assertions/assertions.ts","../src/assertions/index.ts"],"sourcesContent":["import type { GenericModelFactory } from '../factories';\n\n/**\n * Assertion helpers for Tango models.\n */\nexport const assertions = {\n matchesSchema<TModel extends GenericModelFactory<Record<string, unknown>, unknown>>(\n model: TModel,\n data: unknown\n ): asserts data is ReturnType<TModel['parse']> {\n model.parse(data);\n },\n};\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport { assertions } from './assertions';\n"],"mappings":";;;MAKa,aAAa,EACtB,cACIA,OACAC,MAC2C;AAC3C,OAAM,MAAM,KAAK;AACpB,EACJ"}
@@ -0,0 +1,24 @@
1
+ import type { Request } from 'express';
2
+ type ExpressRequestParams = Request['params'] | Record<string, string | string[]>;
3
+ /**
4
+ * Overrides accepted by `anExpressRequest`. All fields are optional;
5
+ * sensible defaults are provided for a minimal GET request.
6
+ */
7
+ export type ExpressRequestOverrides = {
8
+ protocol?: string;
9
+ method?: string;
10
+ originalUrl?: string;
11
+ url?: string;
12
+ headers?: Record<string, any>;
13
+ body?: any;
14
+ params?: ExpressRequestParams;
15
+ get?: (name: string) => string | undefined;
16
+ };
17
+ /**
18
+ * Create a minimal Express `Request` test double.
19
+ * The `get` method is a Vitest mock that returns `'localhost:3000'` for the
20
+ * `host` header and `undefined` for everything else, matching the most common
21
+ * test pattern. Override any field via the `overrides` argument.
22
+ */
23
+ export declare function anExpressRequest(overrides?: ExpressRequestOverrides): Request;
24
+ export {};
@@ -0,0 +1,9 @@
1
+ import type { Response } from 'express';
2
+ /**
3
+ * Create an Express `Response` test double.
4
+ *
5
+ * Every Express `Response` method is backed by a `vi.fn()`.
6
+ * Chainable methods (`status`, `sendStatus`, `links`, etc.) return `res`.
7
+ * Use `vi.mocked()` to access the mock API for assertions.
8
+ */
9
+ export declare function anExpressResponse(): Response;
@@ -0,0 +1,3 @@
1
+ export { anExpressRequest } from './anExpressRequest';
2
+ export type { ExpressRequestOverrides } from './anExpressRequest';
3
+ export { anExpressResponse } from './anExpressResponse';
@@ -0,0 +1,3 @@
1
+ import { anExpressRequest, anExpressResponse } from "../express-Czpfz_Ay.js";
2
+
3
+ export { anExpressRequest, anExpressResponse };
@@ -0,0 +1,68 @@
1
+ import { __export } from "./chunk-BkvOhyD0.js";
2
+ import { vi, vi as vi$1 } from "vitest";
3
+
4
+ //#region src/express/anExpressRequest.ts
5
+ function anExpressRequest(overrides = {}) {
6
+ return {
7
+ protocol: "http",
8
+ method: "GET",
9
+ originalUrl: "/",
10
+ url: "/",
11
+ headers: {},
12
+ body: undefined,
13
+ params: {},
14
+ get: vi$1.fn((name) => name.toLowerCase() === "host" ? "localhost:3000" : undefined),
15
+ ...overrides
16
+ };
17
+ }
18
+
19
+ //#endregion
20
+ //#region src/express/anExpressResponse.ts
21
+ function anExpressResponse() {
22
+ const res = {};
23
+ const chainable = [
24
+ "status",
25
+ "sendStatus",
26
+ "links",
27
+ "contentType",
28
+ "type",
29
+ "format",
30
+ "attachment",
31
+ "set",
32
+ "header",
33
+ "clearCookie",
34
+ "cookie",
35
+ "location",
36
+ "vary",
37
+ "append"
38
+ ];
39
+ for (const method of chainable) res[method] = vi.fn().mockReturnValue(res);
40
+ res.send = vi.fn().mockReturnValue(res);
41
+ res.json = vi.fn().mockReturnValue(res);
42
+ res.jsonp = vi.fn().mockReturnValue(res);
43
+ res.end = vi.fn().mockReturnValue(res);
44
+ res.sendFile = vi.fn();
45
+ res.download = vi.fn();
46
+ res.redirect = vi.fn();
47
+ res.render = vi.fn();
48
+ res.get = vi.fn();
49
+ res.setHeader = vi.fn().mockReturnValue(res);
50
+ res.headersSent = false;
51
+ res.locals = {};
52
+ res.charset = "utf-8";
53
+ res.app = {};
54
+ res.req = {};
55
+ return res;
56
+ }
57
+
58
+ //#endregion
59
+ //#region src/express/index.ts
60
+ var express_exports = {};
61
+ __export(express_exports, {
62
+ anExpressRequest: () => anExpressRequest,
63
+ anExpressResponse: () => anExpressResponse
64
+ });
65
+
66
+ //#endregion
67
+ export { anExpressRequest, anExpressResponse, express_exports };
68
+ //# sourceMappingURL=express-Czpfz_Ay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"express-Czpfz_Ay.js","names":["overrides: ExpressRequestOverrides","name: string","res: Record<string, unknown>"],"sources":["../src/express/anExpressRequest.ts","../src/express/anExpressResponse.ts","../src/express/index.ts"],"sourcesContent":["import { vi } from 'vitest';\nimport type { Request } from 'express';\n\ntype ExpressRequestParams = Request['params'] | Record<string, string | string[]>;\n\n/**\n * Overrides accepted by `anExpressRequest`. All fields are optional;\n * sensible defaults are provided for a minimal GET request.\n */\nexport type ExpressRequestOverrides = {\n protocol?: string;\n method?: string;\n originalUrl?: string;\n url?: string;\n // oxlint-disable-next-line typescript/no-explicit-any\n headers?: Record<string, any>;\n // oxlint-disable-next-line typescript/no-explicit-any\n body?: any;\n params?: ExpressRequestParams;\n get?: (name: string) => string | undefined;\n};\n\n/**\n * Create a minimal Express `Request` test double.\n * The `get` method is a Vitest mock that returns `'localhost:3000'` for the\n * `host` header and `undefined` for everything else, matching the most common\n * test pattern. Override any field via the `overrides` argument.\n */\nexport function anExpressRequest(overrides: ExpressRequestOverrides = {}): Request {\n return {\n protocol: 'http',\n method: 'GET',\n originalUrl: '/',\n url: '/',\n headers: {},\n body: undefined,\n params: {},\n get: vi.fn((name: string) => (name.toLowerCase() === 'host' ? 'localhost:3000' : undefined)),\n ...overrides,\n } as unknown as Request;\n}\n","import { vi } from 'vitest';\nimport type { Response } from 'express';\n\n/**\n * Create an Express `Response` test double.\n *\n * Every Express `Response` method is backed by a `vi.fn()`.\n * Chainable methods (`status`, `sendStatus`, `links`, etc.) return `res`.\n * Use `vi.mocked()` to access the mock API for assertions.\n */\nexport function anExpressResponse(): Response {\n const res: Record<string, unknown> = {};\n\n const chainable = [\n 'status',\n 'sendStatus',\n 'links',\n 'contentType',\n 'type',\n 'format',\n 'attachment',\n 'set',\n 'header',\n 'clearCookie',\n 'cookie',\n 'location',\n 'vary',\n 'append',\n ];\n\n for (const method of chainable) {\n res[method] = vi.fn().mockReturnValue(res);\n }\n\n res.send = vi.fn().mockReturnValue(res);\n res.json = vi.fn().mockReturnValue(res);\n res.jsonp = vi.fn().mockReturnValue(res);\n res.end = vi.fn().mockReturnValue(res);\n res.sendFile = vi.fn();\n res.download = vi.fn();\n res.redirect = vi.fn();\n res.render = vi.fn();\n res.get = vi.fn();\n res.setHeader = vi.fn().mockReturnValue(res);\n\n res.headersSent = false;\n res.locals = {};\n res.charset = 'utf-8';\n res.app = {};\n res.req = {};\n\n return res as unknown as Response;\n}\n","export { anExpressRequest } from './anExpressRequest';\nexport type { ExpressRequestOverrides } from './anExpressRequest';\nexport { anExpressResponse } from './anExpressResponse';\n"],"mappings":";;;;AA4BO,SAAS,iBAAiBA,YAAqC,CAAE,GAAW;AAC/E,QAAO;EACH,UAAU;EACV,QAAQ;EACR,aAAa;EACb,KAAK;EACL,SAAS,CAAE;EACX,MAAM;EACN,QAAQ,CAAE;EACV,KAAK,KAAG,GAAG,CAACC,SAAkB,KAAK,aAAa,KAAK,SAAS,mBAAmB,UAAW;EAC5F,GAAG;CACN;AACJ;;;;AC9BM,SAAS,oBAA8B;CAC1C,MAAMC,MAA+B,CAAE;CAEvC,MAAM,YAAY;EACd;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACH;AAED,MAAK,MAAM,UAAU,UACjB,KAAI,UAAU,GAAG,IAAI,CAAC,gBAAgB,IAAI;AAG9C,KAAI,OAAO,GAAG,IAAI,CAAC,gBAAgB,IAAI;AACvC,KAAI,OAAO,GAAG,IAAI,CAAC,gBAAgB,IAAI;AACvC,KAAI,QAAQ,GAAG,IAAI,CAAC,gBAAgB,IAAI;AACxC,KAAI,MAAM,GAAG,IAAI,CAAC,gBAAgB,IAAI;AACtC,KAAI,WAAW,GAAG,IAAI;AACtB,KAAI,WAAW,GAAG,IAAI;AACtB,KAAI,WAAW,GAAG,IAAI;AACtB,KAAI,SAAS,GAAG,IAAI;AACpB,KAAI,MAAM,GAAG,IAAI;AACjB,KAAI,YAAY,GAAG,IAAI,CAAC,gBAAgB,IAAI;AAE5C,KAAI,cAAc;AAClB,KAAI,SAAS,CAAE;AACf,KAAI,UAAU;AACd,KAAI,MAAM,CAAE;AACZ,KAAI,MAAM,CAAE;AAEZ,QAAO;AACV"}
@@ -10,9 +10,24 @@ export declare class ModelDataFactory<TModel extends GenericModelFactory<Record<
10
10
  private defaults;
11
11
  private sequence;
12
12
  constructor(model: TModel, defaults?: Partial<Parameters<TModel['create']>[0]>);
13
+ /**
14
+ * Build one model instance by merging defaults, sequence defaults, and overrides.
15
+ */
13
16
  build(overrides?: Partial<Parameters<TModel['create']>[0]>): ReturnType<TModel['create']>;
17
+ /**
18
+ * Build `count` model instances using shared overrides.
19
+ */
14
20
  buildList(count: number, overrides?: Partial<Parameters<TModel['create']>[0]>): ReturnType<TModel['create']>[];
15
- protected sequenceDefaults(): Partial<Parameters<TModel['create']>[0]>;
21
+ /**
22
+ * Reset the internal sequence counter to zero.
23
+ */
16
24
  resetSequence(): void;
25
+ /**
26
+ * Return the current sequence counter value.
27
+ */
17
28
  getSequence(): number;
29
+ /**
30
+ * Hook for subclasses to provide per-sequence default values.
31
+ */
32
+ protected sequenceDefaults(): Partial<Parameters<TModel['create']>[0]>;
18
33
  }
@@ -1,3 +1,3 @@
1
- import { ModelDataFactory } from "../factories-CCAZ6E-g.js";
1
+ import { ModelDataFactory } from "../factories-Cl_CAzbj.js";
2
2
 
3
3
  export { ModelDataFactory };
@@ -7,6 +7,9 @@ var ModelDataFactory = class {
7
7
  this.model = model;
8
8
  this.defaults = defaults;
9
9
  }
10
+ /**
11
+ * Build one model instance by merging defaults, sequence defaults, and overrides.
12
+ */
10
13
  build(overrides = {}) {
11
14
  this.sequence++;
12
15
  const data = {
@@ -16,18 +19,30 @@ var ModelDataFactory = class {
16
19
  };
17
20
  return this.model.create(data);
18
21
  }
22
+ /**
23
+ * Build `count` model instances using shared overrides.
24
+ */
19
25
  buildList(count, overrides = {}) {
20
26
  return Array.from({ length: count }, () => this.build(overrides));
21
27
  }
22
- sequenceDefaults() {
23
- return {};
24
- }
28
+ /**
29
+ * Reset the internal sequence counter to zero.
30
+ */
25
31
  resetSequence() {
26
32
  this.sequence = 0;
27
33
  }
34
+ /**
35
+ * Return the current sequence counter value.
36
+ */
28
37
  getSequence() {
29
38
  return this.sequence;
30
39
  }
40
+ /**
41
+ * Hook for subclasses to provide per-sequence default values.
42
+ */
43
+ sequenceDefaults() {
44
+ return {};
45
+ }
31
46
  };
32
47
 
33
48
  //#endregion
@@ -37,4 +52,4 @@ __export(factories_exports, { ModelDataFactory: () => ModelDataFactory });
37
52
 
38
53
  //#endregion
39
54
  export { ModelDataFactory, factories_exports };
40
- //# sourceMappingURL=factories-CCAZ6E-g.js.map
55
+ //# sourceMappingURL=factories-Cl_CAzbj.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factories-Cl_CAzbj.js","names":["model: TModel","defaults: Partial<Parameters<TModel['create']>[0]>","overrides: Partial<Parameters<TModel['create']>[0]>","count: number"],"sources":["../src/factories/ModelDataFactory.ts","../src/factories/index.ts"],"sourcesContent":["export type GenericModelFactory<TInput extends Record<string, unknown> = Record<string, unknown>, TOutput = TInput> = {\n create(data: TInput): TOutput;\n parse(data: unknown): TOutput;\n};\n\n/**\n * Factory class for generating test data with sequences and defaults.\n */\nexport class ModelDataFactory<TModel extends GenericModelFactory<Record<string, unknown>, unknown>> {\n private sequence = 0;\n\n constructor(\n private model: TModel,\n private defaults: Partial<Parameters<TModel['create']>[0]> = {}\n ) {}\n\n /**\n * Build one model instance by merging defaults, sequence defaults, and overrides.\n */\n public build(overrides: Partial<Parameters<TModel['create']>[0]> = {}): ReturnType<TModel['create']> {\n this.sequence++;\n const data = {\n ...this.defaults,\n ...this.sequenceDefaults(),\n ...overrides,\n };\n return this.model.create(data as Parameters<TModel['create']>[0]) as ReturnType<TModel['create']>;\n }\n\n /**\n * Build `count` model instances using shared overrides.\n */\n public buildList(\n count: number,\n overrides: Partial<Parameters<TModel['create']>[0]> = {}\n ): ReturnType<TModel['create']>[] {\n return Array.from({ length: count }, () => this.build(overrides));\n }\n\n /**\n * Reset the internal sequence counter to zero.\n */\n public resetSequence(): void {\n this.sequence = 0;\n }\n\n /**\n * Return the current sequence counter value.\n */\n public getSequence(): number {\n return this.sequence;\n }\n\n /**\n * Hook for subclasses to provide per-sequence default values.\n */\n protected sequenceDefaults(): Partial<Parameters<TModel['create']>[0]> {\n return {};\n }\n}\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport { ModelDataFactory, type GenericModelFactory } from './ModelDataFactory';\n"],"mappings":";;;IAQa,mBAAN,MAA6F;CAChG,WAAmB;CAEnB,YACYA,OACAC,WAAqD,CAAE,GACjE;AAAA,OAFU,QAAA;AAAA,OACA,WAAA;CACR;;;;CAKJ,MAAaC,YAAsD,CAAE,GAAgC;AACjG,OAAK;EACL,MAAM,OAAO;GACT,GAAG,KAAK;GACR,GAAG,KAAK,kBAAkB;GAC1B,GAAG;EACN;AACD,SAAO,KAAK,MAAM,OAAO,KAAwC;CACpE;;;;CAKD,UACIC,OACAD,YAAsD,CAAE,GAC1B;AAC9B,SAAO,MAAM,KAAK,EAAE,QAAQ,MAAO,GAAE,MAAM,KAAK,MAAM,UAAU,CAAC;CACpE;;;;CAKD,gBAA6B;AACzB,OAAK,WAAW;CACnB;;;;CAKD,cAA6B;AACzB,SAAO,KAAK;CACf;;;;CAKD,mBAAuE;AACnE,SAAO,CAAE;CACZ;AACJ"}
package/dist/index.d.ts CHANGED
@@ -7,9 +7,11 @@ export * as factories from './factories/index';
7
7
  export * as assertionsDomain from './assertions/index';
8
8
  export * as integration from './integration/index';
9
9
  export * as vitest from './vitest/index';
10
- export { VERSION as version } from './version';
11
- export { aMockDBClient, aMockQuerySet, aMockRepository } from './mocks/index';
12
- export type { DBClient, MockQuerySetResult, RepositoryLike } from './mocks/index';
10
+ export * as express from './express/index';
11
+ export { aDBClient, aManager, aQueryResult, aQuerySet, aRequestContext, aQueryExecutor } from './mocks/index';
12
+ export { anExpressRequest, anExpressResponse } from './express/index';
13
+ export type { DBClient, ManagerOverrides, MockQuerySetResult, QueryExecutorOverrides, RequestContextFixtureOptions, } from './mocks/index';
14
+ export type { ExpressRequestOverrides } from './express/index';
13
15
  export { ModelDataFactory } from './factories/index';
14
16
  export type { GenericModelFactory } from './factories/index';
15
17
  export { assertions } from './assertions/index';
package/dist/index.js CHANGED
@@ -1,12 +1,9 @@
1
- import { aMockDBClient, aMockQuerySet, aMockRepository, mocks_exports } from "./mocks-qo-1vCez.js";
2
- import { ModelDataFactory, factories_exports } from "./factories-CCAZ6E-g.js";
3
- import { assertions, assertions_exports } from "./assertions-CN6KxXhH.js";
4
- import { Dialect, HarnessStrategyRegistry, ResetMode, TestHarness, applyAndVerifyMigrations, assertMigrationPlan, createRepositoryFixture, domain_exports, expectQueryResult, integration_exports, introspectSchema, migrations_exports, seedTable } from "./integration-CDdpboYz.js";
5
- import { vitest_exports } from "./vitest-PxMJue7R.js";
1
+ import { aDBClient } from "./aDBClient-W6eXsK3X.js";
2
+ import { aManager, aQueryExecutor, aQueryResult, aQuerySet, aRequestContext, mocks_exports } from "./mocks-BkwkXQQt.js";
3
+ import { ModelDataFactory, factories_exports } from "./factories-Cl_CAzbj.js";
4
+ import { assertions, assertions_exports } from "./assertions-CCFZ53Y-.js";
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-37qN8D93.js";
7
+ import { anExpressRequest, anExpressResponse, express_exports } from "./express-Czpfz_Ay.js";
6
8
 
7
- //#region src/version.ts
8
- const VERSION = "0.1.0";
9
-
10
- //#endregion
11
- export { Dialect, HarnessStrategyRegistry, ModelDataFactory, ResetMode, TestHarness, aMockDBClient, aMockQuerySet, aMockRepository, applyAndVerifyMigrations, assertMigrationPlan, assertions, assertions_exports as assertionsDomain, createRepositoryFixture, domain_exports as domain, expectQueryResult, factories_exports as factories, integration_exports as integration, introspectSchema, migrations_exports as migrations, mocks_exports as mocks, seedTable, VERSION as version, vitest_exports as vitest };
12
- //# sourceMappingURL=index.js.map
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 };
@@ -1,10 +1,25 @@
1
1
  import type { Dialect, HarnessStrategy } from './domain';
2
+ /**
3
+ * Registry of test harness strategies keyed by dialect.
4
+ */
2
5
  export declare class HarnessStrategyRegistry {
3
6
  static readonly BRAND: "tango.testing.harness_strategy_registry";
4
7
  readonly __tangoBrand: typeof HarnessStrategyRegistry.BRAND;
5
8
  private readonly strategies;
9
+ /**
10
+ * Narrow an unknown value to `HarnessStrategyRegistry`.
11
+ */
6
12
  static isHarnessStrategyRegistry(value: unknown): value is HarnessStrategyRegistry;
13
+ /**
14
+ * Register or replace a dialect strategy.
15
+ */
7
16
  register(strategy: HarnessStrategy): this;
17
+ /**
18
+ * Resolve a strategy for a dialect, or throw if none is registered.
19
+ */
8
20
  get(dialect: Dialect | string): HarnessStrategy;
21
+ /**
22
+ * List all registered strategies.
23
+ */
9
24
  list(): readonly HarnessStrategy[];
10
25
  }
@@ -1,17 +1,38 @@
1
1
  import { HarnessStrategyRegistry } from './HarnessStrategyRegistry';
2
2
  import { Dialect, type HarnessOptions, type HarnessStrategy, type IntegrationHarness } from './domain';
3
+ /**
4
+ * Facade for creating integration test harnesses by dialect.
5
+ */
3
6
  export declare class TestHarness {
4
7
  static readonly BRAND: "tango.testing.test_harness";
5
- readonly __tangoBrand: typeof TestHarness.BRAND;
6
8
  private static defaultRegistry;
9
+ readonly __tangoBrand: typeof TestHarness.BRAND;
10
+ /**
11
+ * Narrow an unknown value to `TestHarness`.
12
+ */
7
13
  static isTestHarness(value: unknown): value is TestHarness;
8
- private static ensureRegistry;
14
+ /**
15
+ * Register a harness strategy on the shared default registry.
16
+ */
9
17
  static registerStrategy(strategy: HarnessStrategy): void;
18
+ /**
19
+ * Return the shared harness strategy registry.
20
+ */
10
21
  static getRegistry(): HarnessStrategyRegistry;
22
+ /**
23
+ * Create a dialect-specific harness from the registry.
24
+ */
11
25
  static forDialect(args: {
12
26
  dialect: Dialect | string;
13
27
  options?: HarnessOptions;
14
28
  }, registry?: HarnessStrategyRegistry): Promise<IntegrationHarness>;
29
+ /**
30
+ * Convenience helper for a SQLite test harness.
31
+ */
15
32
  static sqlite(options?: HarnessOptions): Promise<IntegrationHarness>;
33
+ /**
34
+ * Convenience helper for a Postgres test harness.
35
+ */
16
36
  static postgres(options?: HarnessOptions): Promise<IntegrationHarness>;
37
+ private static ensureRegistry;
17
38
  }
@@ -0,0 +1,5 @@
1
+ import type { IntegrationHarness } from './domain/IntegrationHarness';
2
+ /**
3
+ * Create an integration-harness fixture with optional overrides.
4
+ */
5
+ export declare function anIntegrationHarness(overrides?: Partial<IntegrationHarness>): IntegrationHarness;
@@ -1,5 +1,9 @@
1
1
  import type { AdapterConfig } from '@danceroutine/tango-orm';
2
2
  import { Dialect } from './domain';
3
+ /**
4
+ * Resolve adapter configuration for a test harness from explicit options,
5
+ * typed Tango config, and environment variables in that order.
6
+ */
3
7
  export declare function resolveAdapterConfig(dialect: Dialect, opts: {
4
8
  config?: Partial<AdapterConfig>;
5
9
  tangoConfigLoader?: () => unknown;
@@ -0,0 +1 @@
1
+ export { runDialectConformanceSuite } from './runDialectConformanceSuite';
@@ -0,0 +1,11 @@
1
+ import type { HarnessOptions, HarnessStrategy } from '../domain';
2
+ /**
3
+ * Shared conformance checks every harness strategy must satisfy.
4
+ *
5
+ * This is intentionally framework-agnostic so first-party and third-party
6
+ * strategies can run the same lifecycle validation.
7
+ */
8
+ export declare function runDialectConformanceSuite(strategy: HarnessStrategy, options?: {
9
+ createOptions?: HarnessOptions;
10
+ migrationsDir?: string;
11
+ }): Promise<void>;
@@ -1,4 +1,5 @@
1
- export declare enum Dialect {
2
- Sqlite = "sqlite",
3
- Postgres = "postgres"
4
- }
1
+ export declare const Dialect: {
2
+ readonly Sqlite: "sqlite";
3
+ readonly Postgres: "postgres";
4
+ };
5
+ export type Dialect = (typeof Dialect)[keyof typeof Dialect];
@@ -1,5 +1,6 @@
1
- export declare enum ResetMode {
2
- Transaction = "transaction",
3
- Truncate = "truncate",
4
- DropSchema = "drop-schema"
5
- }
1
+ export declare const ResetMode: {
2
+ readonly Transaction: "transaction";
3
+ readonly Truncate: "truncate";
4
+ readonly DropSchema: "drop-schema";
5
+ };
6
+ export type ResetMode = (typeof ResetMode)[keyof typeof ResetMode];
@@ -4,8 +4,15 @@
4
4
  */
5
5
  export * as domain from './domain/index';
6
6
  export * as migrations from './migrations/index';
7
+ export * as conformance from './conformance/index';
8
+ export * as runtime from './runtime/index';
9
+ export * as smoke from './smoke/index';
7
10
  export * from './domain/index';
11
+ export * from './anIntegrationHarness';
8
12
  export * from './HarnessStrategyRegistry';
9
13
  export * from './TestHarness';
10
14
  export * from './migrations/index';
11
- export * from './orm';
15
+ export * from './conformance/index';
16
+ export * from './orm/index';
17
+ export * from './runtime/index';
18
+ export * from './smoke/index';
@@ -1,3 +1,4 @@
1
- import { Dialect, HarnessStrategyRegistry, ResetMode, TestHarness, applyAndVerifyMigrations, assertMigrationPlan, createRepositoryFixture, domain_exports, expectQueryResult, introspectSchema, migrations_exports, seedTable } from "../integration-CDdpboYz.js";
1
+ import "../aDBClient-W6eXsK3X.js";
2
+ import { AppProcessHarness, Dialect, HarnessStrategyRegistry, ResetMode, TestHarness, aTangoConfig, anIntegrationHarness, applyAndVerifyMigrations, assertMigrationPlan, conformance_exports, createQuerySetFixture, domain_exports, expectQueryResult, introspectSchema, migrations_exports, runDialectConformanceSuite, runtime_exports, seedTable, setupTestTangoRuntime, smoke_exports } from "../integration-BrJw6NzG.js";
2
3
 
3
- export { Dialect, HarnessStrategyRegistry, ResetMode, TestHarness, applyAndVerifyMigrations, assertMigrationPlan, createRepositoryFixture, domain_exports as domain, expectQueryResult, introspectSchema, migrations_exports as migrations, seedTable };
4
+ export { AppProcessHarness, Dialect, HarnessStrategyRegistry, ResetMode, TestHarness, aTangoConfig, 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 };
@@ -8,6 +8,9 @@ export type MigrationStatus = {
8
8
  id: string;
9
9
  applied: boolean;
10
10
  };
11
+ /**
12
+ * Apply migrations through a harness and optionally verify that specific ids were applied.
13
+ */
11
14
  export declare function applyAndVerifyMigrations(harness: IntegrationHarness, options: ApplyAndVerifyMigrationsOptions): Promise<{
12
15
  statuses: MigrationStatus[];
13
16
  }>;
@@ -3,4 +3,7 @@ export type AssertMigrationPlanOptions = {
3
3
  migrationsDir: string;
4
4
  expectSqlContains?: string[];
5
5
  };
6
+ /**
7
+ * Generate a migration plan through a harness and assert that required SQL fragments appear.
8
+ */
6
9
  export declare function assertMigrationPlan(harness: IntegrationHarness, options: AssertMigrationPlanOptions): Promise<string>;
@@ -1,2 +1,5 @@
1
1
  import { type IntegrationHarness } from '../domain';
2
+ /**
3
+ * Introspect the schema visible to a harness using Tango's built-in introspectors.
4
+ */
2
5
  export declare function introspectSchema(harness: IntegrationHarness): Promise<unknown>;
@@ -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 `QuerySet` fixture backed by a real integration harness and supplied table metadata.
6
+ */
7
+ export declare function createQuerySetFixture<TModel extends Record<string, unknown>>(input: {
8
+ harness: IntegrationHarness;
9
+ meta: TableMeta;
10
+ }): QuerySet<TModel>;
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Assert that a query result matches an expected value using structural equality.
3
+ */
4
+ export declare function expectQueryResult<T>(actual: Promise<T> | T, expected: T): Promise<void>;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Domain boundary barrel: centralizes this subdomain's public contract.
3
+ */
4
+ export { seedTable } from './seedTable';
5
+ export { createQuerySetFixture } from './createQuerySetFixture';
6
+ export { expectQueryResult } from './expectQueryResult';
@@ -0,0 +1,5 @@
1
+ import { type IntegrationHarness } from '../domain/index';
2
+ /**
3
+ * Seed rows directly into a table for integration tests that need known fixtures.
4
+ */
5
+ export declare function seedTable<T extends Record<string, unknown>>(harness: IntegrationHarness, table: string, rows: T[]): Promise<void>;
@@ -0,0 +1,8 @@
1
+ import { type TangoConfig } from '@danceroutine/tango-config';
2
+ export type TestTangoConfigOptions = {
3
+ adapter?: 'sqlite' | 'postgres';
4
+ };
5
+ /**
6
+ * Create a stable Tango config fixture for runtime-oriented tests.
7
+ */
8
+ export declare function aTangoConfig(options?: TestTangoConfigOptions): TangoConfig;