@application-kit/testing 0.4.0 → 0.6.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 (75) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +82 -4
  3. package/dist/cjs/index.d.ts +1 -0
  4. package/dist/cjs/index.js +1 -0
  5. package/dist/cjs/postgres/index.d.ts +1 -0
  6. package/dist/cjs/postgres/index.js +1 -0
  7. package/dist/cjs/postgres/start-postgres-web-application.d.ts +15 -0
  8. package/dist/cjs/postgres/start-postgres-web-application.js +36 -0
  9. package/dist/cjs/postgres/test-data-source.d.ts +2 -1
  10. package/dist/cjs/postgres/test-data-source.js +17 -15
  11. package/dist/cjs/replace-process-environment.d.ts +1 -0
  12. package/dist/cjs/replace-process-environment.js +60 -0
  13. package/dist/cjs/stryker/bin/application-mutation-layer.d.ts +2 -0
  14. package/dist/cjs/stryker/bin/application-mutation-layer.js +64 -0
  15. package/dist/cjs/stryker/bin/index.d.ts +1 -0
  16. package/dist/cjs/stryker/bin/index.js +17 -0
  17. package/dist/cjs/stryker/code-without-logic.d.ts +1 -0
  18. package/dist/cjs/stryker/code-without-logic.js +31 -0
  19. package/dist/cjs/stryker/decide-layer-run.d.ts +12 -0
  20. package/dist/cjs/stryker/decide-layer-run.js +40 -0
  21. package/dist/cjs/stryker/define-stryker-config.d.ts +2 -0
  22. package/dist/cjs/stryker/define-stryker-config.js +24 -0
  23. package/dist/cjs/stryker/define-stryker-layers.d.ts +2 -0
  24. package/dist/cjs/stryker/define-stryker-layers.js +113 -0
  25. package/dist/cjs/stryker/index.d.ts +7 -0
  26. package/dist/cjs/stryker/index.js +23 -0
  27. package/dist/cjs/stryker/mutation-layer-cli.d.ts +9 -0
  28. package/dist/cjs/stryker/mutation-layer-cli.js +64 -0
  29. package/dist/cjs/stryker/stryker-config.interface.d.ts +23 -0
  30. package/dist/cjs/stryker/stryker-config.interface.js +2 -0
  31. package/dist/cjs/stryker/stryker-layers.interface.d.ts +31 -0
  32. package/dist/cjs/stryker/stryker-layers.interface.js +2 -0
  33. package/dist/cjs/vitest/setup/fast-bcrypt-cost.d.ts +5 -0
  34. package/dist/cjs/vitest/setup/fast-bcrypt-cost.js +17 -0
  35. package/dist/cjs/vitest/setup/fast-bcrypt.d.ts +1 -0
  36. package/dist/cjs/vitest/setup/fast-bcrypt.js +9 -0
  37. package/dist/cjs/vitest/setup/index.d.ts +2 -0
  38. package/dist/cjs/vitest/setup/index.js +18 -0
  39. package/dist/esm/index.d.ts +1 -0
  40. package/dist/esm/index.js +1 -0
  41. package/dist/esm/postgres/index.d.ts +1 -0
  42. package/dist/esm/postgres/index.js +1 -0
  43. package/dist/esm/postgres/start-postgres-web-application.d.ts +15 -0
  44. package/dist/esm/postgres/start-postgres-web-application.js +32 -0
  45. package/dist/esm/postgres/test-data-source.d.ts +2 -1
  46. package/dist/esm/postgres/test-data-source.js +15 -14
  47. package/dist/esm/replace-process-environment.d.ts +1 -0
  48. package/dist/esm/replace-process-environment.js +53 -0
  49. package/dist/esm/stryker/bin/application-mutation-layer.d.ts +2 -0
  50. package/dist/esm/stryker/bin/application-mutation-layer.js +26 -0
  51. package/dist/esm/stryker/bin/index.d.ts +1 -0
  52. package/dist/esm/stryker/bin/index.js +1 -0
  53. package/dist/esm/stryker/code-without-logic.d.ts +1 -0
  54. package/dist/esm/stryker/code-without-logic.js +28 -0
  55. package/dist/esm/stryker/decide-layer-run.d.ts +12 -0
  56. package/dist/esm/stryker/decide-layer-run.js +36 -0
  57. package/dist/esm/stryker/define-stryker-config.d.ts +2 -0
  58. package/dist/esm/stryker/define-stryker-config.js +20 -0
  59. package/dist/esm/stryker/define-stryker-layers.d.ts +2 -0
  60. package/dist/esm/stryker/define-stryker-layers.js +106 -0
  61. package/dist/esm/stryker/index.d.ts +7 -0
  62. package/dist/esm/stryker/index.js +7 -0
  63. package/dist/esm/stryker/mutation-layer-cli.d.ts +9 -0
  64. package/dist/esm/stryker/mutation-layer-cli.js +60 -0
  65. package/dist/esm/stryker/stryker-config.interface.d.ts +23 -0
  66. package/dist/esm/stryker/stryker-config.interface.js +1 -0
  67. package/dist/esm/stryker/stryker-layers.interface.d.ts +31 -0
  68. package/dist/esm/stryker/stryker-layers.interface.js +1 -0
  69. package/dist/esm/vitest/setup/fast-bcrypt-cost.d.ts +5 -0
  70. package/dist/esm/vitest/setup/fast-bcrypt-cost.js +13 -0
  71. package/dist/esm/vitest/setup/fast-bcrypt.d.ts +1 -0
  72. package/dist/esm/vitest/setup/fast-bcrypt.js +7 -0
  73. package/dist/esm/vitest/setup/index.d.ts +2 -0
  74. package/dist/esm/vitest/setup/index.js +2 -0
  75. package/package.json +32 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # @application-kit/testing
2
2
 
3
+ ## 0.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - d790ac9: Обвязка e2e и unit-прогона приложения: `replaceProcessEnvironment(environment)` задаёт окружение прогона целиком (снимает
8
+ всё, кроме переменных инструментария, и выставляет набор приложения), `startPostgresWebApplication(options)` поднимает
9
+ настоящий web-граф на временном Postgres с подменой конфига БД по токену, `postgresContainerDataSourceOptions` — общие
10
+ опции подключения к контейнеру для `testDataSource` и запуска. Setup-файл `@application-kit/testing/vitest/fast-bcrypt`
11
+ подменяет `bcrypt` дешёвой стоимостью с сохранением запрошенной в строке хеша; `bcrypt` — optional peer.
12
+
13
+ ## 0.5.0
14
+
15
+ ### Minor Changes
16
+
17
+ - ba67414: Вход `@application-kit/testing/stryker`: `defineStrykerConfig` (база Stryker приложения), `defineStrykerLayers`
18
+ (мутации по группам каталогов с порогом на группу, проверкой «каталог ровно в одной группе», сверкой с матрицей CI и
19
+ чисткой incremental-отчёта), `CODE_WITHOUT_LOGIC` — общий список файлов без логики для `coverage.exclude` vitest и
20
+ `mutate` Stryker. Bin `application-mutation-layer` (`names`, `paths`, `run`) пропускает ячейку CI, если дифф MR группу не трогает.
21
+
22
+ lint-config: подпуть `@application-kit/testing/stryker` добавлен в allow-list импортов приложения.
23
+
3
24
  ## 0.4.0
4
25
 
5
26
  ### Minor Changes
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # @application-kit/testing
2
2
 
3
3
  Тестовая инфраструктура NestJS-приложений: стабы шин CQRS, логгер-накопитель, фейковый microservice-транспорт,
4
- сборка web-приложения для e2e, Postgres в testcontainers, конфиги vitest приложения и раскладка ячеек CI по проектам
5
- vitest (вход `/vitest`).
4
+ сборка и запуск web-приложения для e2e, Postgres в testcontainers, изоляция окружения прогона, конфиги vitest приложения,
5
+ дешёвый bcrypt для unit-прогона и раскладка ячеек CI по проектам vitest (входы `/vitest`, `/vitest/fast-bcrypt`).
6
6
  Для `devDependencies` приложения, в прод не попадает.
7
7
 
8
8
  ## Установка
@@ -14,7 +14,7 @@ yarn add --dev @application-kit/testing
14
14
  Peer-зависимости: `@nestjs/common`, `@nestjs/core`, `@nestjs/cqrs`,
15
15
  `@nestjs/microservices`, `@nestjs/platform-fastify`, `@nestjs/testing`, `nestjs-pino`, `oxide.ts`, `rxjs`,
16
16
  `testcontainers`, `typeorm` — обязательные для корневого входа. Вход `/vitest` несёт свои optional peers `vitest` и
17
- `vite` (`mergeConfig` фабрик конфигов).
17
+ `vite` (`mergeConfig` фабрик конфигов), `/vitest/fast-bcrypt` — `bcrypt`.
18
18
 
19
19
  ## Состав
20
20
 
@@ -26,8 +26,18 @@ Peer-зависимости: `@nestjs/common`, `@nestjs/core`, `@nestjs/cqrs`,
26
26
  `testQueryRunner` закрепляет `QueryRunner` (отключается `pinQueryRunner: false`).
27
27
  - `startPostgresContainer(image)`, `testDataSource({ image, migrations, entities, … })` — Postgres в контейнере и
28
28
  инициализированный `DataSource`. Образ и стратегию имён передаёт приложение: версия БД и именование — его решение, у пакета их нет.
29
+ Опции подключения к контейнеру — `postgresContainerDataSourceOptions(container, namingStrategy)`.
30
+ - `startPostgresWebApplication({ databaseConfigToken, image, imports, namingStrategy, …buildOptions })` — настоящий web-граф
31
+ на временном Postgres: конфиг БД по токену подменяется объектом с `createDataSourceOptions()` поверх контейнера, схема —
32
+ `synchronize` из entities, `stop()` закрывает приложение, затем контейнер. Звать из `beforeAll` с таймаутом.
33
+ - `replaceProcessEnvironment(environment)` — окружение прогона целиком: из `process.env` снимается всё, кроме переменных
34
+ инструментария (node, yarn, vitest, Docker, прокси, локаль), и выставляется набор приложения. Звать из `setupFiles`
35
+ e2e-профиля: конфиг-модули снимают окружение на импорте.
29
36
 
30
37
  ```ts
38
+ // src/testing/e2e-environment.ts — setupFiles e2e-профиля
39
+ replaceProcessEnvironment({ DB_HOST: 'localhost', JWT_SECRET: randomUUID() })
40
+
31
41
  const { dataSource } = await testDataSource({
32
42
  entities: [UserOrmEntity],
33
43
  image: 'postgres:18.3-alpine3.23',
@@ -55,7 +65,7 @@ export default defineApplicationUnitConfig({
55
65
  coverage: { exclude: ['**/*.module.ts'], include: ['src'], thresholds: { lines: 95 } },
56
66
  excludeSpecs: E2E_SPECS,
57
67
  root: import.meta.dirname,
58
- setupFiles: ['./src/testing/fast-hashing.ts'],
68
+ setupFiles: ['@application-kit/testing/vitest/fast-bcrypt'],
59
69
  specs: ['src/**/*.spec.ts'],
60
70
  viteConfig,
61
71
  })
@@ -82,6 +92,15 @@ export default defineApplicationEndToEndConfig({
82
92
  - e2e — окружение `node` и алиас `graphql` на CJS-вход `node_modules/graphql/index.js` под `root`: Vite для SSR берёт
83
93
  ESM-сборку `graphql`, внешний `mercurius` — CJS, и без алиаса схема и валидация запроса живут в разных realm.
84
94
 
95
+ ## Вход `/vitest/fast-bcrypt`: дешёвый bcrypt
96
+
97
+ Setup-файл unit-профиля (`setupFiles: ['@application-kit/testing/vitest/fast-bcrypt']`): `vi.mock('bcrypt')` считает хеш
98
+ с cost 4 (≈ 1 мс против ≈ 300 мс у cost 12), а поле стоимости в строке переписывает на запрошенную — `getRounds` и
99
+ решение о перехешировании видят настоящую стоимость; `compare` перед сверкой возвращает поле на 4. Сверяются только хеши,
100
+ выпущенные подменой: литерал хеша с настоящим cost 12 не сойдётся. Подмена видна коду, прошедшему трансформ vitest
101
+ (исходники приложения); внешний пакет, импортирующий `bcrypt`, ускоряется только через `inlineDependencies`. E2e-профилю
102
+ файл не нужен.
103
+
85
104
  ## Ячейки CI по проектам
86
105
 
87
106
  `vitest --shard=i/n` сам режет спеки хешем, и при двух проектах разных трансформов (`server` в `node`, `client` в
@@ -94,3 +113,62 @@ web-карту того же файла, и statements/functions задваив
94
113
  `defineApplicationUnitConfig` выбирает ячейку по `process.argv` в момент вызова: 1 — только проект `client` и покрытие
95
114
  по `clientSources`, 2..n — только проект `server` и покрытие без `clientSources`, без флага — оба проекта. Свой конфиг
96
115
  без фабрики собирается из `parseShard` и `ProjectShardSequencer` напрямую.
116
+
117
+ ## Вход `/stryker`: мутации по группам каталогов
118
+
119
+ Вход не тянет `vitest`: конфиг Stryker — обычный объект. `stryker.config.mjs` приложения собирается из трёх частей.
120
+
121
+ `CODE_WITHOUT_LOGIC` — глобы файлов без логики (команды, DTO, модули, мапперы, манифесты, сущности и миграции TypeORM,
122
+ навигация, виджет-плагины). Список один на оба инструмента: `defineStrykerConfig` добавляет к нему `!` для `mutate`, а
123
+ приложение кладёт его в `coverage.exclude` vitest. Расхождение допустимо только в явных добавках приложения:
124
+ `*.spec.ts` и `tests/**` для мутаций, `*.html` для покрытия.
125
+
126
+ ```js
127
+ import { CODE_WITHOUT_LOGIC, defineStrykerConfig } from '@application-kit/testing/stryker'
128
+
129
+ // stryker.conf.mjs — база
130
+ export default defineStrykerConfig({
131
+ ignorePatterns: ['docs', 'tasks'],
132
+ mutate: { exclude: ['src/**/*.spec.ts', 'src/**/*.vue', 'src/**/tests/**'], include: ['src/modules/**/*.ts', 'src/libs/**/*.ts'] },
133
+ thresholds: { break: 94, high: 100, low: 95 },
134
+ vitestConfigFile: 'vitest.stryker.config.mts',
135
+ })
136
+
137
+ // vitest.config.mts
138
+ coverage: { exclude: [...CODE_WITHOUT_LOGIC, '**/*.html'], include: ['src'], thresholds: { lines: 95 } }
139
+ ```
140
+
141
+ `defineStrykerLayers` режет прогон на группы каталогов: ячейка CI мутирует одну группу со своим порогом `break` (общий
142
+ порог разбавлял бы требование к сильной группе слабой). Группу называет `TYPESCRIPT_MUTATION_LAYER` (имя меняется
143
+ `layerVariable`); без переменной берётся `defaultLayer`. Фабрика сама проверяет при загрузке: каждый корень (`scope`)
144
+ лежит ровно в одной группе — новый модуль без группы падает с его именем, а не выпадает молча из мутаций; список ячеек
145
+ `parallel: matrix` в файле CI совпадает с таблицей групп; из incremental-отчёта перед прогоном Stryker убираются файлы
146
+ вне области группы. Каталог внутри чужого (слой модуля) принадлежит своей группе и у объемлющей снимается сам.
147
+
148
+ ```js
149
+ // stryker.layer.conf.mjs
150
+ export const layers = defineStrykerLayers({
151
+ base,
152
+ ciFile: path.join(import.meta.dirname, '.gitlab-ci.yml'),
153
+ defaultLayer: 'modules',
154
+ layers: {
155
+ libs: { break: 80, paths: ['src/libs'] },
156
+ modules: { break: 94, paths: ['src/modules/client', 'src/modules/common'] },
157
+ },
158
+ root: import.meta.dirname,
159
+ scope: { directories: ['src/libs'], parents: ['src/modules'] },
160
+ skip: { configFiles: /^(\.gitlab-ci\.yml|vitest\.config[^/]*\.mts)$/, sharedDirectories: ['src/libs/testing-module'], sourceRoot: 'src' },
161
+ })
162
+
163
+ export default layers.config
164
+ ```
165
+
166
+ Bin `application-mutation-layer` читает экспорт `layers` (`--config=`, по умолчанию `stryker.layer.conf.mjs`):
167
+
168
+ - `names` — группы через пробел (`for layer in $(application-mutation-layer names)`);
169
+ - `paths` — каталоги группы из переменной окружения, вырезанные каталоги других групп — с `!`;
170
+ - `run [base]` — в MR (`base` или `CI_MERGE_REQUEST_DIFF_BASE_SHA`) пропускает ячейку, если дифф не трогает её группу,
171
+ иначе запускает `yarn stryker run <config> --logLevel info --concurrency 2` (`MUTATION_LAYER_COMMAND` подменяет
172
+ команду). Причину решения печатает всегда: зелёная ячейка без прогона и с прогоном в логе различимы. Прогон идёт при
173
+ любом сомнении — нет базы, дифф не читается или пуст, тронут файл из `skip.configFiles`, файл под `sourceRoot` вне
174
+ групп, каталог из `skip.sharedDirectories`. Образу без `git` его ставит приложение до вызова.
@@ -1,6 +1,7 @@
1
1
  export * from './fake-microservice/index.js';
2
2
  export * from './postgres/index.js';
3
3
  export * from './build-web-application.js';
4
+ export * from './replace-process-environment.js';
4
5
  export * from './test-command-bus.js';
5
6
  export * from './test-event-bus.js';
6
7
  export * from './test-logger.js';
package/dist/cjs/index.js CHANGED
@@ -17,6 +17,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./fake-microservice/index.js"), exports);
18
18
  __exportStar(require("./postgres/index.js"), exports);
19
19
  __exportStar(require("./build-web-application.js"), exports);
20
+ __exportStar(require("./replace-process-environment.js"), exports);
20
21
  __exportStar(require("./test-command-bus.js"), exports);
21
22
  __exportStar(require("./test-event-bus.js"), exports);
22
23
  __exportStar(require("./test-logger.js"), exports);
@@ -1 +1,2 @@
1
+ export * from './start-postgres-web-application.js';
1
2
  export * from './test-data-source.js';
@@ -14,4 +14,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./start-postgres-web-application.js"), exports);
17
18
  __exportStar(require("./test-data-source.js"), exports);
@@ -0,0 +1,15 @@
1
+ import type { InjectionToken, ModuleMetadata } from '@nestjs/common';
2
+ import type { NestFastifyApplication } from '@nestjs/platform-fastify';
3
+ import type { NamingStrategyInterface } from 'typeorm';
4
+ import type { BuildWebApplicationOptionsInterface } from '../build-web-application.js';
5
+ export interface PostgresWebApplicationOptionsInterface extends BuildWebApplicationOptionsInterface {
6
+ databaseConfigToken: InjectionToken;
7
+ image: string;
8
+ imports: NonNullable<ModuleMetadata['imports']>;
9
+ namingStrategy: NamingStrategyInterface;
10
+ }
11
+ export interface StartedWebApplicationInterface {
12
+ app: NestFastifyApplication;
13
+ stop(): Promise<void>;
14
+ }
15
+ export declare const startPostgresWebApplication: (options: PostgresWebApplicationOptionsInterface) => Promise<StartedWebApplicationInterface>;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.startPostgresWebApplication = void 0;
4
+ const build_web_application_js_1 = require("../build-web-application.js");
5
+ const test_data_source_js_1 = require("./test-data-source.js");
6
+ const startPostgresWebApplication = async (options) => {
7
+ const { databaseConfigToken, image, imports, namingStrategy, ...buildOptions } = options;
8
+ const container = await (0, test_data_source_js_1.startPostgresContainer)(image);
9
+ const dataSourceOptions = {
10
+ ...(0, test_data_source_js_1.postgresContainerDataSourceOptions)(container, namingStrategy),
11
+ migrationsRun: false,
12
+ synchronize: true,
13
+ };
14
+ try {
15
+ const app = await (0, build_web_application_js_1.buildWebApplication)(imports, [
16
+ {
17
+ provide: databaseConfigToken,
18
+ useValue: { createDataSourceOptions: () => dataSourceOptions },
19
+ },
20
+ ], buildOptions);
21
+ await app.getHttpAdapter().getInstance()
22
+ .ready();
23
+ return {
24
+ app,
25
+ stop: async () => {
26
+ await app.close();
27
+ await container.stop();
28
+ },
29
+ };
30
+ }
31
+ catch (error) {
32
+ await container.stop();
33
+ throw error;
34
+ }
35
+ };
36
+ exports.startPostgresWebApplication = startPostgresWebApplication;
@@ -1,9 +1,10 @@
1
1
  import type { StartedTestContainer } from 'testcontainers';
2
- import type { EntitySchema, MixedList, NamingStrategyInterface, QueryRunner } from 'typeorm';
2
+ import type { DataSourceOptions, EntitySchema, MixedList, NamingStrategyInterface, QueryRunner } from 'typeorm';
3
3
  import { DataSource } from 'typeorm';
4
4
  export declare const TEST_DATABASE_NAME = "db";
5
5
  export declare const TEST_DATABASE_PORT = 5432;
6
6
  export declare const startPostgresContainer: (image: string) => Promise<StartedTestContainer>;
7
+ export declare const postgresContainerDataSourceOptions: (container: StartedTestContainer, namingStrategy: NamingStrategyInterface) => DataSourceOptions;
7
8
  export interface TestDataSourceOptionsInterface {
8
9
  entities: MixedList<EntitySchema | Function | string>;
9
10
  image: string;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.testDataSource = exports.startPostgresContainer = exports.TEST_DATABASE_PORT = exports.TEST_DATABASE_NAME = void 0;
3
+ exports.testDataSource = exports.postgresContainerDataSourceOptions = exports.startPostgresContainer = exports.TEST_DATABASE_PORT = exports.TEST_DATABASE_NAME = void 0;
4
4
  const testcontainers_1 = require("testcontainers");
5
5
  const typeorm_1 = require("typeorm");
6
6
  exports.TEST_DATABASE_NAME = 'db';
@@ -16,28 +16,30 @@ const startPostgresContainer = async (image) => new testcontainers_1.GenericCont
16
16
  .withExposedPorts(exports.TEST_DATABASE_PORT)
17
17
  .start();
18
18
  exports.startPostgresContainer = startPostgresContainer;
19
+ const postgresContainerDataSourceOptions = (container, namingStrategy) => ({
20
+ database: exports.TEST_DATABASE_NAME,
21
+ host: container.getHost(),
22
+ logging: false,
23
+ namingStrategy,
24
+ password: exports.TEST_DATABASE_NAME,
25
+ port: container.getMappedPort(exports.TEST_DATABASE_PORT),
26
+ ssl: false,
27
+ subscribers: [],
28
+ type: 'postgres',
29
+ username: exports.TEST_DATABASE_NAME,
30
+ });
31
+ exports.postgresContainerDataSourceOptions = postgresContainerDataSourceOptions;
19
32
  const testDataSource = async ({ entities, image, isLogging = false, migrations, namingStrategy, shouldSynchronize = false, }) => {
20
- const databaseName = exports.TEST_DATABASE_NAME;
21
- const databasePort = exports.TEST_DATABASE_PORT;
22
33
  const container = await (0, exports.startPostgresContainer)(image);
23
- const databaseConfig = {
24
- database: databaseName,
34
+ const dataSource = new typeorm_1.DataSource({
35
+ ...(0, exports.postgresContainerDataSourceOptions)(container, namingStrategy),
25
36
  entities,
26
- host: container.getHost(),
27
37
  logging: isLogging,
28
38
  maxQueryExecutionTime: 1000,
29
39
  migrations,
30
40
  migrationsRun: !shouldSynchronize,
31
- namingStrategy,
32
- password: databaseName,
33
- port: container.getMappedPort(databasePort),
34
- ssl: false,
35
- subscribers: [],
36
41
  synchronize: shouldSynchronize,
37
- type: 'postgres',
38
- username: databaseName,
39
- };
40
- const dataSource = new typeorm_1.DataSource(databaseConfig);
42
+ });
41
43
  await dataSource.initialize();
42
44
  const queryRunner = dataSource.createQueryRunner();
43
45
  Object.assign(dataSource.manager, { queryRunner });
@@ -0,0 +1 @@
1
+ export declare const replaceProcessEnvironment: (environment: Readonly<Record<string, string>>) => void;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.replaceProcessEnvironment = void 0;
7
+ const node_process_1 = __importDefault(require("node:process"));
8
+ const TOOLCHAIN_NAMES = new Set([
9
+ '_',
10
+ 'CI',
11
+ 'COLORTERM',
12
+ 'FORCE_COLOR',
13
+ 'HOME',
14
+ 'HOSTNAME',
15
+ 'INIT_CWD',
16
+ 'LANG',
17
+ 'OLDPWD',
18
+ 'PATH',
19
+ 'PROJECT_CWD',
20
+ 'PWD',
21
+ 'SHELL',
22
+ 'SHLVL',
23
+ 'TEMP',
24
+ 'TERM',
25
+ 'TMP',
26
+ 'TMPDIR',
27
+ 'TZ',
28
+ 'USER',
29
+ ]);
30
+ const TOOLCHAIN_PREFIXES = [
31
+ 'BERRY_',
32
+ 'DOCKER_',
33
+ 'HTTP_PROXY',
34
+ 'HTTPS_PROXY',
35
+ 'LC_',
36
+ 'NODE_',
37
+ 'NO_PROXY',
38
+ 'NPM_',
39
+ 'npm_',
40
+ 'TESTCONTAINERS_',
41
+ 'VITE_',
42
+ 'VITEST_',
43
+ 'XDG_',
44
+ 'YARN_',
45
+ 'http_proxy',
46
+ 'https_proxy',
47
+ 'no_proxy',
48
+ ];
49
+ const isToolchainVariable = (name) => TOOLCHAIN_NAMES.has(name)
50
+ || TOOLCHAIN_PREFIXES.some((prefix) => name.startsWith(prefix));
51
+ const replaceProcessEnvironment = (environment) => {
52
+ const runtimeEnvironment = node_process_1.default.env;
53
+ for (const name of Object.keys(runtimeEnvironment)) {
54
+ if (!isToolchainVariable(name)) {
55
+ Reflect.deleteProperty(runtimeEnvironment, name);
56
+ }
57
+ }
58
+ Object.assign(runtimeEnvironment, environment);
59
+ };
60
+ exports.replaceProcessEnvironment = replaceProcessEnvironment;
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ var __importDefault = (this && this.__importDefault) || function (mod) {
37
+ return (mod && mod.__esModule) ? mod : { "default": mod };
38
+ };
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ const node_child_process_1 = require("node:child_process");
41
+ const node_path_1 = __importDefault(require("node:path"));
42
+ const node_process_1 = __importDefault(require("node:process"));
43
+ const node_url_1 = require("node:url");
44
+ const mutation_layer_cli_js_1 = require("../mutation-layer-cli.js");
45
+ const changedFiles = (base) => (0, node_child_process_1.execFileSync)('git', ['diff', '--name-only', `${base}...HEAD`], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] })
46
+ .split('\n')
47
+ .filter(Boolean);
48
+ const loadLayers = async (config) => {
49
+ const loaded = (await Promise.resolve(`${(0, node_url_1.pathToFileURL)(node_path_1.default.resolve(config)).href}`).then(s => __importStar(require(s))));
50
+ return loaded.layers;
51
+ };
52
+ const main = async () => {
53
+ const code = await (0, mutation_layer_cli_js_1.executeMutationLayerCli)(node_process_1.default.argv.slice(2), {
54
+ changedFiles,
55
+ env: node_process_1.default.env,
56
+ loadLayers,
57
+ print: (line) => {
58
+ console.log(line);
59
+ },
60
+ run: (command) => (0, node_child_process_1.spawnSync)(command, { shell: true, stdio: 'inherit' }).status ?? 1,
61
+ });
62
+ node_process_1.default.exitCode = code;
63
+ };
64
+ void main();
@@ -0,0 +1 @@
1
+ export * from './application-mutation-layer.js';
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./application-mutation-layer.js"), exports);
@@ -0,0 +1 @@
1
+ export declare const CODE_WITHOUT_LOGIC: readonly string[];
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CODE_WITHOUT_LOGIC = void 0;
4
+ exports.CODE_WITHOUT_LOGIC = [
5
+ 'src/**/*.command.ts',
6
+ 'src/**/*.config.ts',
7
+ 'src/**/*.d.ts',
8
+ 'src/**/*.dto.ts',
9
+ 'src/**/*.environment.ts',
10
+ 'src/**/*.error.ts',
11
+ 'src/**/*.fake.ts',
12
+ 'src/**/*.interface.ts',
13
+ 'src/**/*.mock.ts',
14
+ 'src/**/*.module.ts',
15
+ 'src/**/*.query.ts',
16
+ 'src/**/*.type.ts',
17
+ 'src/**/*constants.ts',
18
+ 'src/**/entry.*.ts',
19
+ 'src/**/errors/*',
20
+ 'src/**/graphql/**/*.input.ts',
21
+ 'src/**/graphql/**/*.output.ts',
22
+ 'src/**/mappers/*',
23
+ 'src/**/module.cli.ts',
24
+ 'src/**/module.consumers.ts',
25
+ 'src/**/module.manifest.ts',
26
+ 'src/**/typeorm/entities/*.ts',
27
+ 'src/**/typeorm/migrations/*.ts',
28
+ 'src/**/user-interface/client/app/navigation.ts',
29
+ 'src/**/user-interface/client/app/plugins/*-widget.plugin.ts',
30
+ 'src/**/user-interface/client/app/plugins/container-plugin.ts',
31
+ ];
@@ -0,0 +1,12 @@
1
+ import type { StrykerLayerSkipOptionsInterface } from './stryker-layers.interface.js';
2
+ export interface LayerRunDecisionInterface {
3
+ readonly reason: string;
4
+ readonly run: boolean;
5
+ readonly touched: readonly string[];
6
+ }
7
+ export interface LayerRunInputInterface extends StrykerLayerSkipOptionsInterface {
8
+ changed: readonly string[];
9
+ layerPaths: readonly string[];
10
+ roots: readonly string[];
11
+ }
12
+ export declare const decideLayerRun: (input: LayerRunInputInterface) => LayerRunDecisionInterface;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.decideLayerRun = void 0;
4
+ const isUnder = (file, directory) => file.startsWith(`${directory}/`);
5
+ const isInLayer = (file, layerPaths) => {
6
+ let isHit = false;
7
+ for (const entry of layerPaths) {
8
+ if (entry.startsWith('!')) {
9
+ if (isUnder(file, entry.slice(1))) {
10
+ return false;
11
+ }
12
+ }
13
+ else if (isUnder(file, entry)) {
14
+ isHit = true;
15
+ }
16
+ }
17
+ return isHit;
18
+ };
19
+ const runBecause = (reason) => ({ reason, run: true, touched: [] });
20
+ const decideLayerRun = (input) => {
21
+ if (input.changed.length === 0) {
22
+ return runBecause('дифф пуст');
23
+ }
24
+ if (input.changed.some((file) => input.configFiles.test(file))) {
25
+ return runBecause('тронут конфиг прогона');
26
+ }
27
+ const source = input.changed.filter((file) => isUnder(file, input.sourceRoot));
28
+ if (source.some((file) => input.roots.every((root) => !isUnder(file, root)))) {
29
+ return runBecause(`тронут ${input.sourceRoot}/ вне групп`);
30
+ }
31
+ const shared = input.sharedDirectories.find((directory) => input.changed.some((file) => isUnder(file, directory)));
32
+ if (shared !== undefined) {
33
+ return runBecause(`тронута общая тестовая инфраструктура ${shared}`);
34
+ }
35
+ const touched = input.changed.filter((file) => isInLayer(file, input.layerPaths));
36
+ return touched.length === 0
37
+ ? { reason: 'дифф группу не трогает', run: false, touched }
38
+ : { reason: 'группа тронута', run: true, touched };
39
+ };
40
+ exports.decideLayerRun = decideLayerRun;
@@ -0,0 +1,2 @@
1
+ import type { StrykerConfigInterface, StrykerConfigOptionsInterface } from './stryker-config.interface.js';
2
+ export declare const defineStrykerConfig: (options: StrykerConfigOptionsInterface) => StrykerConfigInterface;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defineStrykerConfig = void 0;
4
+ const code_without_logic_js_1 = require("./code-without-logic.js");
5
+ const DEFAULT_IGNORE_PATTERNS = ['coverage', 'dist', 'node_modules', 'reports'];
6
+ const compare = (left, right) => left.localeCompare(right);
7
+ const defineStrykerConfig = (options) => ({
8
+ clearTextReporter: { reportTests: false },
9
+ dryRunTimeoutMinutes: 10,
10
+ ignorePatterns: [...DEFAULT_IGNORE_PATTERNS, ...(options.ignorePatterns ?? [])].toSorted(compare),
11
+ ignoreStatic: true,
12
+ incremental: true,
13
+ incrementalFile: options.incrementalFile ?? '.stryker-cache/incremental.json',
14
+ logLevel: 'warn',
15
+ mutate: [
16
+ ...options.mutate.include,
17
+ ...[...code_without_logic_js_1.CODE_WITHOUT_LOGIC, ...(options.mutate.exclude ?? [])].map((pattern) => `!${pattern}`),
18
+ ],
19
+ tempDirName: options.tempDirName ?? '/tmp/.stryker-tmp',
20
+ testRunner: 'vitest',
21
+ thresholds: options.thresholds,
22
+ vitest: { configFile: options.vitestConfigFile, dir: './' },
23
+ });
24
+ exports.defineStrykerConfig = defineStrykerConfig;
@@ -0,0 +1,2 @@
1
+ import type { StrykerLayersInterface, StrykerLayersOptionsInterface } from './stryker-layers.interface.js';
2
+ export declare const defineStrykerLayers: (options: StrykerLayersOptionsInterface) => StrykerLayersInterface;