@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/package.json CHANGED
@@ -1,76 +1,89 @@
1
1
  {
2
- "name": "@danceroutine/tango-testing",
3
- "version": "0.1.0",
4
- "description": "Testing utilities for Tango",
5
- "type": "module",
6
- "main": "./dist/index.js",
7
- "types": "./dist/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./dist/index.d.ts",
11
- "import": "./dist/index.js"
12
- },
13
- "./integration": {
14
- "types": "./dist/integration/index.d.ts",
15
- "import": "./dist/integration/index.js"
16
- },
17
- "./vitest": {
18
- "types": "./dist/vitest/index.d.ts",
19
- "import": "./dist/vitest/index.js"
20
- },
21
- "./mocks": {
22
- "types": "./dist/mocks/index.d.ts",
23
- "import": "./dist/mocks/index.js"
24
- },
25
- "./factories": {
26
- "types": "./dist/factories/index.d.ts",
27
- "import": "./dist/factories/index.js"
28
- },
29
- "./assertions": {
30
- "types": "./dist/assertions/index.d.ts",
31
- "import": "./dist/assertions/index.js"
32
- }
2
+ "name": "@danceroutine/tango-testing",
3
+ "version": "1.0.0",
4
+ "description": "Testing utilities for Tango",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
33
12
  },
34
- "files": [
35
- "dist"
36
- ],
37
- "scripts": {
38
- "build": "tsdown",
39
- "test": "vitest run --coverage",
40
- "test:watch": "vitest",
41
- "typecheck": "tsc --noEmit"
13
+ "./integration": {
14
+ "types": "./dist/integration/index.d.ts",
15
+ "import": "./dist/integration/index.js"
42
16
  },
43
- "keywords": [
44
- "tango",
45
- "testing",
46
- "test",
47
- "utilities"
48
- ],
49
- "author": "Pedro Del Moral Lopez",
50
- "license": "MIT",
51
- "repository": {
52
- "type": "git",
53
- "url": "https://github.com/danceroutine/tango.git",
54
- "directory": "packages/testing"
17
+ "./vitest": {
18
+ "types": "./dist/vitest/index.d.ts",
19
+ "import": "./dist/vitest/index.js"
55
20
  },
56
- "dependencies": {
57
- "@danceroutine/tango-config": "workspace:*",
58
- "@danceroutine/tango-migrations": "workspace:*",
59
- "@danceroutine/tango-orm": "workspace:*"
21
+ "./mocks": {
22
+ "types": "./dist/mocks/index.d.ts",
23
+ "import": "./dist/mocks/index.js"
60
24
  },
61
- "peerDependencies": {
62
- "vitest": "^4.0.0"
25
+ "./factories": {
26
+ "types": "./dist/factories/index.d.ts",
27
+ "import": "./dist/factories/index.js"
63
28
  },
64
- "peerDependenciesMeta": {
65
- "vitest": {
66
- "optional": true
67
- }
29
+ "./assertions": {
30
+ "types": "./dist/assertions/index.d.ts",
31
+ "import": "./dist/assertions/index.js"
68
32
  },
69
- "devDependencies": {
70
- "@types/node": "^22.9.0",
71
- "tsdown": "^0.4.0",
72
- "typescript": "^5.6.3",
73
- "vitest": "^4.0.6",
74
- "zod": "^4.0.0"
33
+ "./express": {
34
+ "types": "./dist/express/index.d.ts",
35
+ "import": "./dist/express/index.js"
75
36
  }
76
- }
37
+ },
38
+ "files": [
39
+ "dist"
40
+ ],
41
+ "keywords": [
42
+ "tango",
43
+ "testing",
44
+ "test",
45
+ "utilities"
46
+ ],
47
+ "author": "Pedro Del Moral Lopez",
48
+ "license": "MIT",
49
+ "repository": {
50
+ "type": "git",
51
+ "url": "https://github.com/danceroutine/tango.git",
52
+ "directory": "packages/testing"
53
+ },
54
+ "dependencies": {
55
+ "@danceroutine/tango-core": "1.0.0",
56
+ "@danceroutine/tango-orm": "1.0.0",
57
+ "@danceroutine/tango-config": "1.0.0",
58
+ "@danceroutine/tango-resources": "1.0.0",
59
+ "@danceroutine/tango-migrations": "1.0.0"
60
+ },
61
+ "peerDependencies": {
62
+ "@types/express": "^5.0.0",
63
+ "vitest": "^4.0.0"
64
+ },
65
+ "peerDependenciesMeta": {
66
+ "@types/express": {
67
+ "optional": true
68
+ },
69
+ "vitest": {
70
+ "optional": true
71
+ }
72
+ },
73
+ "devDependencies": {
74
+ "@types/express": "^5.0.0",
75
+ "@types/node": "^22.9.0",
76
+ "tsdown": "^0.4.0",
77
+ "typescript": "^5.6.3",
78
+ "vitest": "^4.0.6",
79
+ "zod": "^4.0.0"
80
+ },
81
+ "scripts": {
82
+ "build": "tsdown",
83
+ "test": "vitest run --coverage",
84
+ "test:watch": "vitest",
85
+ "typecheck": "pnpm run typecheck:prod && pnpm run typecheck:test",
86
+ "typecheck:prod": "tsc --noEmit -p tsconfig.json",
87
+ "typecheck:test": "tsc --noEmit -p tsconfig.tests.json"
88
+ }
89
+ }
@@ -1,8 +0,0 @@
1
- /**
2
- * Assertion helpers for Tango models.
3
- */
4
- export const assertions = {
5
- matchesSchema(model, data) {
6
- model.parse(data);
7
- },
8
- };
@@ -1 +0,0 @@
1
- {"version":3,"file":"assertions-CN6KxXhH.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<any, any>>(\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"}
@@ -1,33 +0,0 @@
1
- /**
2
- * Factory class for generating test data with sequences and defaults.
3
- */
4
- export class ModelDataFactory {
5
- model;
6
- defaults;
7
- sequence = 0;
8
- constructor(model, defaults = {}) {
9
- this.model = model;
10
- this.defaults = defaults;
11
- }
12
- build(overrides = {}) {
13
- this.sequence++;
14
- const data = {
15
- ...this.defaults,
16
- ...this.sequenceDefaults(),
17
- ...overrides,
18
- };
19
- return this.model.create(data);
20
- }
21
- buildList(count, overrides = {}) {
22
- return Array.from({ length: count }, () => this.build(overrides));
23
- }
24
- sequenceDefaults() {
25
- return {};
26
- }
27
- resetSequence() {
28
- this.sequence = 0;
29
- }
30
- getSequence() {
31
- return this.sequence;
32
- }
33
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"factories-CCAZ6E-g.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<any, any>> {\n private sequence = 0;\n\n constructor(\n private model: TModel,\n private defaults: Partial<Parameters<TModel['create']>[0]> = {}\n ) {}\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 any);\n }\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 protected sequenceDefaults(): Partial<Parameters<TModel['create']>[0]> {\n return {} as any;\n }\n\n public resetSequence(): void {\n this.sequence = 0;\n }\n\n public getSequence(): number {\n return this.sequence;\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,MAAqE;CACxE,WAAmB;CAEnB,YACYA,OACAC,WAAqD,CAAE,GACjE;AAAA,OAFU,QAAA;AAAA,OACA,WAAA;CACR;CAEJ,MAAaC,YAAsD,CAAE,GAAgC;AACjG,OAAK;EACL,MAAM,OAAO;GACT,GAAG,KAAK;GACR,GAAG,KAAK,kBAAkB;GAC1B,GAAG;EACN;AACD,SAAO,KAAK,MAAM,OAAO,KAAY;CACxC;CAED,UACIC,OACAD,YAAsD,CAAE,GAC1B;AAC9B,SAAO,MAAM,KAAK,EAAE,QAAQ,MAAO,GAAE,MAAM,KAAK,MAAM,UAAU,CAAC;CACpE;CAED,mBAAuE;AACnE,SAAO,CAAE;CACZ;CAED,gBAA6B;AACzB,OAAK,WAAW;CACnB;CAED,cAA6B;AACzB,SAAO,KAAK;CACf;AACJ"}
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/version.ts"],"sourcesContent":["export const VERSION = '0.1.0';\n"],"mappings":";;;;;;;MAAa,UAAU"}
@@ -1,9 +0,0 @@
1
- import { Repository } from '@danceroutine/tango-orm';
2
- import type { RepoMeta } from '@danceroutine/tango-orm/query';
3
- import { type IntegrationHarness } from './domain';
4
- export declare function seedTable<T extends Record<string, unknown>>(harness: IntegrationHarness, table: string, rows: T[]): Promise<void>;
5
- export declare function createRepositoryFixture<TModel extends Record<string, unknown>>(input: {
6
- harness: IntegrationHarness;
7
- meta: RepoMeta;
8
- }): Repository<TModel>;
9
- export declare function expectQueryResult<T>(actual: Promise<T> | T, expected: T): Promise<void>;
@@ -1,39 +0,0 @@
1
- import { Repository } from '@danceroutine/tango-orm';
2
- import { Dialect } from './domain';
3
- export async function seedTable(harness, table, rows) {
4
- if (rows.length === 0) {
5
- return;
6
- }
7
- const columns = Object.keys(rows[0] ?? {});
8
- if (columns.length === 0) {
9
- return;
10
- }
11
- for (const row of rows) {
12
- const values = columns.map((column) => {
13
- const value = row[column];
14
- if (harness.dialect === Dialect.Sqlite && typeof value === 'boolean') {
15
- return value ? 1 : 0;
16
- }
17
- return value;
18
- });
19
- const placeholders = harness.dialect === Dialect.Postgres
20
- ? columns.map((_, index) => `$${index + 1}`).join(', ')
21
- : columns.map(() => '?').join(', ');
22
- await harness.dbClient.query(`INSERT INTO ${table} (${columns.join(', ')}) VALUES (${placeholders})`, values);
23
- }
24
- }
25
- export function createRepositoryFixture(input) {
26
- class RepositoryFixture extends Repository {
27
- meta = input.meta;
28
- constructor() {
29
- super(input.harness.dbClient, input.harness.dialect);
30
- }
31
- }
32
- return new RepositoryFixture();
33
- }
34
- export async function expectQueryResult(actual, expected) {
35
- const resolved = await actual;
36
- if (JSON.stringify(resolved) !== JSON.stringify(expected)) {
37
- throw new Error(`Expected query result ${JSON.stringify(expected)}, got ${JSON.stringify(resolved)}`);
38
- }
39
- }
@@ -1,95 +0,0 @@
1
- import { MigrationRunner } from '@danceroutine/tango-migrations';
2
- import { PostgresAdapter } from '@danceroutine/tango-orm/connection';
3
- import { resolveAdapterConfig } from '../config';
4
- import { Dialect, ResetMode, } from '../domain';
5
- export class PostgresHarnessStrategy {
6
- static BRAND = 'tango.testing.postgres_harness_strategy';
7
- __tangoBrand = PostgresHarnessStrategy.BRAND;
8
- dialect = Dialect.Postgres;
9
- capabilities = {
10
- transactionalDDL: true,
11
- supportsSchemas: true,
12
- supportsConcurrentIndex: true,
13
- supportsDeferredFkValidation: true,
14
- supportsJsonb: true,
15
- };
16
- static isPostgresHarnessStrategy(value) {
17
- return (typeof value === 'object' &&
18
- value !== null &&
19
- value.__tangoBrand === PostgresHarnessStrategy.BRAND);
20
- }
21
- static buildSchemaName(explicitSchema) {
22
- if (explicitSchema)
23
- return explicitSchema;
24
- const random = Math.random().toString(36).slice(2, 8);
25
- return `tango_test_${Date.now()}_${random}`;
26
- }
27
- async create(options = {}) {
28
- const config = resolveAdapterConfig(Dialect.Postgres, {
29
- config: options.config,
30
- tangoConfigLoader: options.tangoConfigLoader,
31
- });
32
- const adapter = new PostgresAdapter();
33
- const schemaName = PostgresHarnessStrategy.buildSchemaName(options.schema);
34
- const resetMode = options.resetMode ?? ResetMode.DropSchema;
35
- let client = null;
36
- const ensureSearchPath = async () => {
37
- const dbClient = client;
38
- await dbClient.query(`CREATE SCHEMA IF NOT EXISTS \"${schemaName}\"`);
39
- await dbClient.query(`SET search_path TO \"${schemaName}\"`);
40
- };
41
- const recreateSchema = async () => {
42
- const dbClient = client;
43
- await dbClient.query(`DROP SCHEMA IF EXISTS \"${schemaName}\" CASCADE`);
44
- await dbClient.query(`CREATE SCHEMA \"${schemaName}\"`);
45
- await dbClient.query(`SET search_path TO \"${schemaName}\"`);
46
- };
47
- const harness = {
48
- dialect: Dialect.Postgres,
49
- capabilities: this.capabilities,
50
- resetMode,
51
- get dbClient() {
52
- if (!client) {
53
- throw new Error('Postgres harness not initialized. Call setup() first.');
54
- }
55
- return client;
56
- },
57
- async setup() {
58
- client = await adapter.connect(config);
59
- await ensureSearchPath();
60
- },
61
- async reset() {
62
- if (!client) {
63
- throw new Error('Postgres harness not initialized. Call setup() first.');
64
- }
65
- if (resetMode === ResetMode.DropSchema || resetMode === ResetMode.Transaction) {
66
- await recreateSchema();
67
- return;
68
- }
69
- const { rows } = await client.query(`SELECT table_name FROM information_schema.tables WHERE table_schema = $1 AND table_type = 'BASE TABLE'`, [schemaName]);
70
- for (const row of rows) {
71
- await client.query(`TRUNCATE TABLE \"${schemaName}\".\"${row.table_name}\" RESTART IDENTITY CASCADE`);
72
- }
73
- await client.query(`SET search_path TO \"${schemaName}\"`);
74
- },
75
- async teardown() {
76
- if (!client)
77
- return;
78
- try {
79
- await client.query(`DROP SCHEMA IF EXISTS \"${schemaName}\" CASCADE`);
80
- }
81
- finally {
82
- await client.close();
83
- client = null;
84
- }
85
- },
86
- migrationRunner(migrationsDir) {
87
- if (!client) {
88
- throw new Error('Postgres harness not initialized. Call setup() first.');
89
- }
90
- return new MigrationRunner(client, 'postgres', migrationsDir);
91
- },
92
- };
93
- return harness;
94
- }
95
- }