Переносит архив `audits/2026-10-11/` (19 файлов) из codex/audit-remediation-2026-10-11 в develop и добавляет точечный allowlist в .gitignore.
74 KiB
Аудит 09 — Test Architecture & Infrastructure
1. Audit metadata
Дата: 2026-10-11. Режим: read-only аудит тестовой инфраструктуры; записан только этот отчёт в родительском workspace.
| Область | Путь относительно workspace | Точный HEAD | Состояние при начале аудита |
|---|---|---|---|
| Workspace | . |
dc030f84c979cedb2628ef4bd97c62d6c63a9256 |
Чистый, release/0.2.1 |
| Frontend | frontend/ |
c8821200ce699dab1ce65360d1968d5877a2e78b |
Чистый, codex/fix-stock-screener-race |
| Backend | backend/ |
0ad84ff4f0b4712abc7e69dc299856f14af6eadd |
Чистый, codex/add-stock-screener |
| Specs | specs/ |
fcdc805f6c1b3be6a20a7e858e3e80cbd83a9121 |
Чистый, codex/add-stock-screener |
| Docs | docs/ |
e6fd4f64b81dd73b33959b845a1babcfac1891c8 |
Чистый, codex/add-stock-screener |
Абсолютный корень: /Users/ksv741/Project/vibe/WealthLedger. Прочитаны локальные AGENTS.md, engineering/testing conventions и относящиеся к трассировке architecture/data источники. Conventions использованы для установления задуманного устройства, а не как доказательство полезности каждого правила.
Skill: установленный ponytail-audit загружен через Skill tool и применён к сложности test support. Его complexity-pass отделён от проверки isolation/lifecycle. Исправления не применялись. technical-audit-2026-10-11.md прочитан как список прежних гипотез; выводы ниже проверены по текущему checkout.
Выполненные проверки
-
В каждом репозитории:
git rev-parse HEAD,git status --porcelain=v1 --branch. -
Статический inventory через Glob/Read/Grep и
rg: runner configuration, imports test support, lifecycle hooks, mocks/timers, локальные factories, consumers fixtures и зависимости subprocess. -
Backend, после загрузки существующего NVM и активации местной
.nvmrc:source "/Users/ksv741/.nvm/nvm.sh" && nvm use --silent && npm test -- --runInBand --no-cache --runTestsByPath src/modules/banking/bank-cards/domain/rub-money.spec.tsPASS: 1 suite, 22 tests; Jest сообщил 0.379 s. Это время одного локального запуска pure unit suite, не оценка CI или всего набора. Проверка подтверждает работоспособность дешёвой unit boundary без Nest bootstrap, SQLite и внешней сети. Coverage и Jest cache не создавались.
-
Первая попытка той же команды остановилась на
nvm: command not found: NVM не был загружен в tool shell, сам тест не запускался. Послеsourceпроверка прошла. Это ограничение оболочки аудита, не finding проекта. -
Одна поисковая команда сначала включала отсутствующий frontend-каталог
test/; inventory затем выполнен по фактическимsrc/test/,src/,packages/. Данных эта ошибка не меняла.
Не запускались: install, build, codegen, миграции, full-suite, coverage suite, verify, SQLite/HTTP suites, browser tests, performance/durability/container/regression runners. Реальные банковские и брокерские API не вызывались. Изменения исходников, тестов, конфигурации, lockfiles и Git history отсутствуют.
Ограничение доказательств: дефекты организации и возможные failure modes выведены из исходников. Частота flaky-сбоев, время bootstrap/migrations и экономия CI не измерены. Не воспроизведённые runtime-сценарии помечены отдельно; зелёный unit test не подтверждает исправность HTTP/SQLite инфраструктуры.
2. Executive verdict
- Основа достаточно проста и оправданна. Backend разделяет colocated Jest unit, реальные SQLite suites и configured HTTP; frontend использует небольшие query/resource helpers, RTL/MSW и два browser layout-файла. Причин создавать общий test framework, builders DSL или cross-repo runtime не найдено.
- Подтверждена межтестовая зависимость банковской suite — TI-001, P2. Данные одного owner живут между
it; restart-case проверяет переименование категории, сделанное другим case. Это непосредственно мешает изолированному запуску и перестановке сценариев. - Подтверждён неконтролируемый фоновой lifecycle — TI-002, P2. В 10 функциональных HTTP suites зарегистрирован настоящий минутный scheduler. В части из них есть активные connection fixtures; длительный запуск способен начать работу вне Arrange/Act. Отдельный benchmark тоже запускает scheduler, что мешает изолированному измерению read budget.
- Синхронизация фоновых сценариев местами привязана к скорости машины — TI-003, P2. Sleeps на 5–10 ms используются как признак начала работы; для portfolio unit tests уже существует
work.done, но вместо него опрашиваются mock calls. Это подтверждённый механизм риска, а не измеренная flaky-rate. - Targeted SQLite path допускает смешение source и build — TI-004, P2. Crash producer загружается из
dist, основной driver — через ts-jest изsrc; прямой runner не собирает build. Штатныйtest:e2eзащищён предварительным build. Фактическое устаревание текущегоdist— UNVERIFIED. - Cleanup небольшой и необязательный. Opt-in regression самого runner выполняет пять полных функциональных проходов — TI-005, P3; в bond setup есть один option без consumer — TI-006, P3. Ни одна находка не оправдывает массовую перестройку тестов.
Всего: 6 findings — 4 P2 и 2 P3. P0/P1 не установлены. Самые важные изменения уменьшают скрытое состояние и делают выполнение управляемым, сохраняя реальные DB/HTTP guarantees.
3. Inventory & coverage
3.1. Фактический стек и размеры области
| Объект | Результат | Метод и граница подсчёта |
|---|---|---|
| Frontend test files | 63 | rg --files src packages -g '*.test.ts' -g '*.test.tsx' | wc -l; файлы, не expanded test cases |
| Frontend browser files | 2 | По найденным *.browser.test.tsx: EntryCard и position-layout; оба запускаются в двух viewport instances |
| Frontend JSDOM files | 61 | 63 минус 2 browser-файла; разграничение подтверждено vitest.config.ts:26–65 |
Consumers @/test/query |
27 test files | rg -l 'from "@/test/query"' src packages с теми же test globs; прямые imports, не число вызовов |
Consumers @/test/resource |
20 test files | Аналогичный подсчёт прямых imports; дополнительно helper использует src/test/handlers.ts:3 |
Consumers @/test/server |
27 test files | Аналогичный подсчёт прямых imports; lifecycle setup рассматривается отдельно |
| Backend unit files | 39 | rg --files src -g '*.spec.ts' | wc -l |
| Backend e2e/integration files | 20 | rg --files test -g '*.e2e-spec.ts' | wc -l |
Функциональные suites в --all |
19 | 20 файлов минус явно исключённый performance-file, test/sqlite.cjs:7–17 |
Suites с AppModule |
14, из них 13 функциональных | rg -l 'imports: \[AppModule\]' test -g '*.e2e-spec.ts' | wc -l; просмотр multiline imports и consumer каждого результата |
| Overrides hourly scheduler | 3 suites | portfolio-structure, tbank-value-history, upcoming-events; остальные 10 функциональных AppModule suites запускают lifecycle настоящего provider |
Backend локальные deferred<T> |
4 определения | По function deferred в test/: local-reads, operations-sync, metadata-persistence, catalog-discovery |
Frontend manifest: Vitest 5.0.0, RTL 16.3.3, MSW 2.15.0, Playwright 1.63.0, TanStack Query 5.102.8, JSDOM 30.0.1 (frontend/package.json:64–116). Backend: Jest 30.5.1, ts-jest 29.4.12, Nest testing 12.0.1, Supertest 7.2.2, Prisma 7.10.0, better-sqlite3 13.0.3 (backend/package.json:40–105). Это manifest versions; переустановка зависимостей не выполнялась.
3.2. Test Support Inventory
| Support / owner | Consumer / выполняемый поток | Назначение и оценка |
|---|---|---|
frontend/vitest.config.ts:26–65 |
Unit project; browser project | Разные environments и взаимоисключающие globs. Browser geometry не дублируется в JSDOM — KEEP |
frontend/src/test/setup.ts:1–48 |
Каждый JSDOM test file | jest-dom, i18n, strict MSW listener, RTL cleanup, auth reset, handler reset, browser API shims — KEEP |
frontend/src/test/server.ts:1–7, handlers.ts:10–82 |
Setup и 27 напрямую импортирующих suites | Один process-local MSW server и synthetic defaults. Нет browser worker или backend dependency — KEEP |
frontend/src/test/query.tsx:5–24 |
27 files | Fresh QueryClient, query/mutation retries off, wrapper сохраняет тот же client при rerender — KEEP |
frontend/src/test/resource.ts:1–27 |
20 files + handlers | Resource envelope, typed Meta и explicit overrides. Не ходит в сеть, не нормализует деньги — KEEP |
frontend/src/pages/account/model/positions.fixtures.ts:1–73 |
positions.test.ts:6, position-groups.test.tsx:7, AccountPage.vm.test.tsx:14 |
Три прямых consumers; typed data с null, mixed currencies и negative quantity — KEEP |
Page-local renderPage / setup |
Account, Card, OperationForm, Stock, Bond suites | Явная provider composition, локальные handlers/controls. Практически один suite-local уровень orchestration; не общий DSL — KEEP, TI-006 для одного flag |
Auth test wrapper features/auth/model/auth.test.tsx:20–28 |
AuthProvider suite | QueryClientProvider + настоящий AuthProvider. Сценарии изменения identity живут у auth owner — KEEP |
frontend/src/test/storybook.test.tsx:19–57 |
Composed stories из явно выбранного набора | Проверяет isolation от network/storage/cookies, а не работу factory как таковой — KEEP |
Browser-local afterEach(cleanup) |
Два browser-файла | Browser project не подключает JSDOM setup. Их cleanup обязателен, не дублирование global hook — KEEP |
backend/package.json:123–139 |
npm test, watch/cov/debug |
Unit Jest: rootDir=src, colocated specs, node environment — KEEP |
backend/test/jest-e2e.json:1–13 |
SQLite runner → Jest | E2E config, isolated-env gate, ts-jest, .js import mapper — KEEP |
backend/test/sqlite.cjs:6–95 |
test:e2e, test:sqlite, OpenAPI/performance entries |
Suite selection, свежий temp DB, штатные migrations, отдельный Jest process, signals и cleanup — KEEP; TI-004 для build prerequisite |
backend/test/e2e-env.cjs:1–17 |
Каждая e2e suite и crash subprocess | Проверка runner-owned URL/temp path до DB work; synthetic JWT/encryption config — KEEP |
| Local DB constructors | Пять service/repository suites: operations-persistence, operations-sync, metadata, catalog, candles | Реальная SQLite + конкретный service без Nest app и HTTP. Это уже реализованный меньший scope — KEEP |
| Suite-local DB seeds | Connections, memberships, portfolios, operations/coverage | Seed responsibility локальна; current revision, token и financial inputs доступны тесту. Глобального seed framework нет — KEEP |
| Local poll helpers | state, settled, requestPortfolio, load |
Разные смыслы: конкретный work state, dependency group, await existing handle. Нельзя объединять исключительно по похожему loop; TI-003 касается readiness sleeps |
backend/test/fixtures/tbank-sync-crash.cjs:1–86 |
tbank-operations-sync.e2e-spec.ts:424–499 |
Child process + IPC checkpoint + SIGKILL на durable boundaries. Обычный mock не заменяет crash — KEEP, TI-004 |
backend/test/sqlite-regression.cjs:20–96 |
Явный opt-in command в README.md:250–254 |
Уникальные DB, внешний sentinel, migration failure, SIGINT/SIGTERM cleanup — KEEP protections / SIMPLIFY workload, TI-005 |
backend/test/bank-cards-durability.cjs:10–23,120–215 |
test:durability -- <baseline> |
Независимая historical build, migration prefix, restart, rollback, backup/restore. Explicit external artifact, не shared helper dependency — KEEP |
backend/test/container-smoke.cjs:19–73, runtime-check.cjs:1–8 |
Container test / build-runtime проверка | Packaging/native modules/proto/read-only image boundaries; не дополнительный business test framework — KEEP |
backend/test/tbank-read-baseline.e2e-spec.ts |
Отдельный performance command | Большой synthetic dataset и read-budget measurement, исключён из --all — KEEP separation, TI-002 для scheduler |
backend/test/fixtures/tbank-read-baseline.json |
Benchmark, tbank-read-baseline.e2e-spec.ts:522–529 |
Historical RPC comparison input; не текущий CI timing — KEEP |
backend/test/fixtures/tbank-read-persistent.json:1–20 |
Runtime consumer в просмотренных test/config/docs roots не найден | Historical measurement evidence, не executable factory. KEEP как evidence; отсутствие import не доказывает ненужность исторического результата |
Custom builders/classes, собственные matchers/expect.extend, snapshot serializers и concurrent-case режим в просмотренной test architecture не найдены. Policy/tooling tests test/eslint-config.cjs, scripts/test-eslint.mjs, scripts/test-pre-commit.mjs отмечены в inventory, но их содержательная целесообразность относится к аудитам 10/11.
3.3. Representative cases и границы чтения
Карта имён/config/support охватывает все найденные test files. Углублённая выборка risk-based, а не построчное чтение всех assertions.
| Выборка | Реально рассмотренные файлы / участок |
|---|---|
| 5 сложных frontend suites | AccountPage.test.tsx — оба render helpers, fixtures, state/route setup; OperationForm.test.tsx — весь файл; CardPage.test.tsx — setup, pending-page/spy teardown и consumers options; StockPage.test.tsx, BondPage.test.tsx — options, handlers, controls и все consumers |
| Дополнительные RTL/MSW flows | StocksPage.test.tsx, resource-query.test.tsx, client.test.ts, auth.test.tsx, AccountPage.vm.test.tsx — async barriers, retry/time policy и wrapper ownership |
| 5 простых frontend suites | AuthForm.test.tsx, calendar.test.ts, period.test.ts, SourceSyncStatus.test.tsx, EntryCard.browser.test.tsx; дополнительно position-layout.browser.test.tsx |
| 5 сложных backend suites | bank-cards.e2e-spec.ts — helper closure, seeds, global counts, category dependency, restart; upcoming-events.e2e-spec.ts — bootstrap, cleanup, dependency polling, revision-aware seed; tbank-operations-sync.e2e-spec.ts — весь файл; metadata/catalog persistence — construction, hooks, producer/observer races и deferred closure |
| 5 простых backend suites | rub-money.spec.ts, bank-card.calculations.spec.ts, decimal.spec.ts, all-accounts.e2e-spec.ts, stocks.e2e-spec.ts — unit/HTTP boundary и setup scope |
| Дополнительный backend infrastructure | app.e2e-spec.ts, openapi-contract.e2e-spec.ts, portfolio.service.spec.ts, candle/local-reads/connection/history bootstrap, все собственные test runners и crash fixture |
| Public intent / supporting docs | Местные testing conventions/README; specs/.../frontend-ui-catalog/spec.md:40–88,97–105; docs/architecture/cross-cutting/data-freshness.md:63–83 |
Вне scope: правильность каждого assertion, полнота продуктовых regressions, общий coverage score и legacy сценарии как таковые — аудит 08; CI triggers — 11; production SRP — 01/06; non-test scripts — 10. Не исследовались реальные внешние API, performance profile всего приложения и архивная OpenSpec история.
3.4. Fixture Complexity Matrix
Arrange ниже — число явно видимых инфраструктурных операций в вызывающем тесте, не количество DB queries/framework internals. Глубина — последовательные project-owned helper calls; provider tree не считается цепочкой helper abstractions.
| Fixture/helper → consumers | Responsibility | Hidden side effects / сложность | Alternatives и решение |
|---|---|---|---|
createTestQueryClient / createQueryWrapper → 27 files |
Client lifecycle / wrapper | Один factory call; wrapper → factory — максимум два support calls. Fresh client, retries off; сетевых вызовов при construction нет | Сохранить; общий client вместо per-case создаст новую isolation проблему |
resource(data, meta) → 20 files + handlers |
Envelope только | Один call; generic data, fixed resource key/date/revision, shallow Meta override | Сохранить. Для stale/failure/connection-sensitive scenarios metadata задавать явно; не применять runtime validator ко всем fixtures |
positions.fixtures → 3 files |
Typed scenario data | Module-level objects, nested references при spread; consumers используют projections/overrides, общей мутации в рассмотренных путях не найдено | Сохранить; deep-freeze/clone framework без доказанной mutation не нужен |
OperationForm.setup → собственная suite |
Auth + categories handler + render | Три инфраструктурных шага; fresh client внутри render, close spy возвращается. Data-only operation передаётся отдельно; assertions в setup нет | Сохранить локально: category endpoint не перекрывает тестовые operation handlers |
CardPage.setup → собственная suite |
Auth + handler bundle + render + request capture | Четыре options, каждый имеет consumer; mutable request arrays принадлежат одному invocation. 1 helper, без builder layers | Сохранить; новые options добавлять по реальному сценарию |
StockPage.setup → собственная suite |
Несколько endpoint handlers + providers + failure controls | Девять options, examined consumers используют все; handlers содержат domain-specific payloads. Большой объём — данные/API sections, а не глубина DSL | Не объединять со BondPage: metric/rating/coupon contracts различаются |
BondPage.setup → собственная suite |
Аналогичная page composition | Семь options; шесть используются, failBond не имеет consumer; controls локальны |
Удалить только unused option — TI-006; общий instrument test framework не нужен |
Banking createCard → auth → token, op, createOp |
Happy-path HTTP arrange / payload creation | Максимум три локальных helper calls; createCard/createOp делают HTTP/DB write и ожидают успешный status. Проверки ошибок используют auth(...).expect(...) напрямую |
Сохранить короткие HTTP helpers: скрытая happy-path assertion здесь намеренная, negative path управляемый |
Banking module-level owner/DB → все it |
Shared suite state | До первого case users seed, после каждого case cleanup нет; category state пересекает границы сценариев | FIX TI-001: case-owned data и explicit prerequisites |
Upcoming portfolio / refresh / settled → собственная suite |
Seed / launch / await dependencies | Три разные обязанности уже разделены; portfolio явно повышает dataRevision и использует user-specific revision. settled включает quiet interval |
KEEP разделение; не превращать в givenPortfolioReadyAndAssert DSL |
Metadata/catalog createService, seedConnection, deferred, settled → по одной suite |
Composition / seed / RPC gate / completion | Четыре небольших помощника с разными ролями, не четыре обязательных layers; RPC readiness иногда заменён sleep | FIX TI-003 для sequencing. Небольшие duplicated constructors не требуют общего DI container |
| Portfolio unit polling → два launch cases | Ожидание publish mock | Два одинаковых loop по десять setImmediate; тест игнорирует доступный work.done |
Использовать existing completion promise — TI-003, без нового helper |
| Crash subprocess → три checkpoint cases | Настоящая потеря процесса | Контроль RPC/repository checkpoint, отдельный dist runtime, IPC, timeout и child cleanup |
KEEP; согласовать freshness build — TI-004 |
| Runner regression → 3 sequential + 2 parallel full runs и failure/signal cases | Isolation/lifecycle infrastructure | Полный product набор является payload каждого успешного infrastructure check | SIMPLIFY TI-005: маленький existing workload с нужными suite/process boundaries |
3.5. Проверка SRP / DRY / KISS / YAGNI / ISP / DIP
- SRP: в изученной выборке нет общего helper, который одновременно скрыто генерирует финансовый набор, мутирует DB, делает HTTP и проверяет весь business outcome. Banking happy-path helpers объединяют HTTP action и success status; negative cases обходят их напрямую, поэтому разбиение ради SRP увеличило бы ceremony.
- DRY: семантически одинаковые freshness/reset patterns важнее одинаковых object literals. Provider composition повторяется, но QueryClient factory уже общая. Backend auth/JWT seed wrappers короткие и локальные; общего runtime между frontend/backend нет.
- KISS: максимальная установленная project-owned цепочка arrange в рассмотренных support paths — три коротких banking helper calls. Цепочек по 5–10 factory layers не найдено. Большие stock/bond setup — плоские bundles handlers, не builders framework.
- YAGNI: подтверждён один unused option TI-006 и избыточный объём product workload в opt-in infrastructure regression TI-005. Оснований удалять основные factories, MSW server, crash helper или DB runner нет.
- ISP/DIP: mocks в основном структурные, ограничены используемыми boundary methods; interfaces/fake hierarchies не вводятся.
as neverослабляет compile-time проверку fake, но само по себе не доказывает runtime поломку и не оправдывает написание полного fake Prisma. DB guarantees проверяются на настоящей SQLite. - Lifecycle: frontend global hook очищает DOM/auth/handlers; fake timers обычно восстанавливаются локально. Backend per-suite fresh DB не равен per-case isolation — TI-001; настоящий background provider мешает deterministic setup — TI-002; producer gates требуют explicit readiness/release — TI-003.
- Financial defaults:
resourceне округляет деньги; fixtures и сценарии явно показывают null, zero, negative/fractional quantities, UTF-8 names, разные revision/user, signed64 и >2^53. Доказательств, что builder скрывает финансовый corner case, нет. Fixed metadata не доказывает fresh/stale policy: такие tests должны задавать значимые поля явно. - Heavy bootstrap: чистые money/calendar/domain tests используют imports напрямую, без AppModule. Пять persistence suites уже создают concrete services без HTTP. Stocks/bonds HTTP contracts стартуют весь AppModule; меньшая composition возможна, но сохранение настоящих guard/pipe/filter/route registration важнее немедленного сокращения setup. Это INVESTIGATE, не обязательный rewrite.
- Factory-internal tests: отдельные self-tests
resource,createQueryWrapper, builders не найдены. Storybook isolation и SQLite runner regression имеют реальные границы защиты: первый запрещает network/storage, второй защищает пользовательскую БД и child-process cleanup.
4. Findings
TI-001 — Банковская suite использует состояние предыдущих test cases
Decision = FIX · Severity = P2 · Confidence = High · Effort = M
Evidence: backend/test/bank-cards.e2e-spec.ts:23–39,77–105,106–145,371–390,710–753; consumers: все it в describe('Manual bank cards configured HTTP and real SQLite').
Claim. Per-suite fresh SQLite не обеспечивает самостоятельность банковских scenarios: часть cases требует данных/изменений, выполненных предыдущими it.
Current mechanism. Один module-level owner и stranger создаются в beforeAll; app/DB повторно используются, beforeEach/afterEach cleanup отсутствует. Case renames default category... меняет default category на «Остальное» (371–386). Case preserves history... across app restart сам создаёт карту/операции, но после restart ожидает ту же строку «Остальное» (728–730), не выполняя переименование. Case rename также читает default category до собственного createCard (372–373); её появление зависит от прежней записи. Constraint-case получает существующую category (743).
Impact / actual cost. Порядок тестов становится скрытым prerequisite. Targeted run одного rename/restart/constraint case на новой БД не воспроизводит исходные условия полной suite. Добавление нового раннего case меняет shared category/card state. Impact unmeasured: стоимость диагностики и частота падений не измерялись; runtime isolated run в этом аудите не выполнялся.
Why now. Дешёвые целевые проверки нужны для финансовых изменений; текущая организационная зависимость лишает их надёжности независимо от корректности business assertions.
Minimum change. Каждый независимый case создаёт owner/stranger или свой явно очищенный набор с FK-aware cleanup. Rename и constraint cases сами создают prerequisite card/categories. Restart-case сам переименовывает category до restart. App можно оставить suite-scoped; не нужна отдельная БД или migration на каждый it.
Guarantees at risk. Не удалять реальную SQLite, concurrent writes, replay fingerprints/tombstones, archive/version checks и restart внутри одного case. Смена owner требует обновить auth helpers, а cleanup — соблюдать FK order.
Counterarguments / trade-offs. Shared DB снижает bootstrap cost, случайные card IDs изолируют многие записи, некоторые global assertions сравнивают before/after вместо абсолютного значения. Это хорошие локальные меры, но они не устраняют найденную category dependency. Намеренный sequential journey допустим внутри одного it; здесь scenarios оформлены как независимые cases.
Verification or rollback. На disposable SQLite выполнить rename и restart отдельно через Jest --testNamePattern, а затем полную банковскую suite. Для filtered запуска сохранить runner-owned env: обычный runner сейчас не пересылает произвольные Jest flags. Проверить отсутствие зависимости от чужого case, затем real concurrency/replay outcomes. Rollback — вернуть только изменение test setup; production/schema не затрагивать.
Related finding IDs: TI-002, TI-003.
TI-002 — Настоящий scheduler запускается в suites, не управляющих его работой
Decision = FIX · Severity = P2 · Confidence = High · Effort = S
Evidence:
- Composition/lifecycle:
backend/src/app.module.ts:18–36,backend/src/modules/investments/tbank-sync/tbank-sync.module.ts:13–25,backend/src/modules/investments/tbank-sync/tbank-hourly.scheduler.ts:19–21,48–86,119–175. - Consumers:
backend/test/all-accounts.e2e-spec.ts:30–61,backend/test/tbank-connection.e2e-spec.ts:30–54,backend/test/tbank-local-reads.e2e-spec.ts:100–141,backend/test/tbank-income-local.e2e-spec.ts:58–100,backend/test/tbank-ledger-local.e2e-spec.ts:44–78. - Benchmark:
backend/test/tbank-read-baseline.e2e-spec.ts:199–247,355–357,410–485. - Контрпримеры:
backend/test/upcoming-events.e2e-spec.ts:140–152,backend/test/portfolio-structure.e2e-spec.ts:40–49,backend/test/tbank-value-history.e2e-spec.ts:47–56.
Claim. Тестовый app lifecycle не отделён от настоящего фонового scheduler в части HTTP/read suites и benchmark.
Current mechanism. AppModule включает TbankSyncModule, provider создаёт setInterval(..., 60_000) на app.init/listen. tick читает connections, резервирует hourly work и запускает membership/portfolio/operations/events. Из 13 функциональных AppModule suites только три override scheduler; в остальных 10 таймер активен. Пять перечисленных connection-oriented functional suites создают connection fixtures. Benchmark держит app во время большой DB population, допускает длительный setup/test и собирает upstream counters.
Impact / actual cost. При достижении минутного срока данные/metadata и counters могут меняться вне тестового Act; возможны неожиданные upstream mock calls и новые work rows. В benchmark фоновые writes/refresh конкурируют с измеряемыми reads. Impact unmeasured: длительность текущих suites, факт срабатывания tick и flaky frequency не измерены. Фактический внешний сетевой запрос не установлен.
Why now. Isolation не должна зависеть от того, уложился ли runner в минуту. Особенно это важно для утверждений «GET не запускает upstream» и измерения read-only budget.
Minimum change. В не-scheduler suites применить уже имеющийся .overrideProvider(TbankHourlyScheduler).useValue(...) без lifecycle таймера. Benchmark также отключает scheduler и явно запускает только необходимую подготовку. Для scheduler behavior нужен отдельный управляемый time/lifecycle case; не добавлять production env flag ради tests.
Guarantees at risk. Сохранить настоящие controllers/guards/pipes, migrations, DB publication fences и ручной запуск source owners в relevant scenarios. Scheduler tests должны сохранять реальный tick/reservation behavior; override не должен стать способом скрыть их ошибки.
Counterarguments / trade-offs. Большинство функциональных suites, вероятно, заканчивается раньше первого tick; auth/cards/stocks/bonds/OpenAPI обычно не создают connections, поэтому там интервал может быть inert. app.close очищает interval. Эти гарантии уменьшают вероятность, но не делают connection suites deterministic; cleanup timer не отменяет уже запущенную работу. Три существующих overrides показывают, что меньшая boundary уже принята.
Verification or rollback. После override отдельно проверить один local-read HTTP case и benchmark setup с временем за минутной границей: новых hourly work/upstream calls быть не должно. Затем relevant functional suites. Проверки самого scheduler — с controlled clock. Rollback — только provider override; production scheduler не меняется.
Related finding IDs: TI-001, TI-003, TI-005.
TI-003 — Readiness фоновой работы зависит от sleeps и polling вместо доступного completion handle
Decision = FIX · Severity = P2 · Confidence = High · Effort = M
Evidence:
- Consumers readiness sleeps:
backend/test/tbank-metadata-persistence.e2e-spec.ts:130–178,backend/test/tbank-catalog-discovery.e2e-spec.ts:179–225,backend/test/tbank-operations-sync.e2e-spec.ts:152–181,183–210,523–550. - Async launch mechanism:
backend/src/integrations/tbank/instrument-metadata.service.ts:75–108,111–129,backend/src/modules/investments/operations/application/brokerage-operations-sync.service.ts:157–185,233–275. - Consumers polling и существующий handle:
backend/src/modules/investments/portfolio/application/portfolio.service.spec.ts:100–110,150–154,backend/src/modules/investments/portfolio/application/portfolio.service.ts:234–306. - Контрпримеры:
backend/test/tbank-local-reads.e2e-spec.ts:69–77,backend/test/portfolio-structure.e2e-spec.ts:81–100.
Claim. Некоторые Arrange/Act sequences не имеют явного сигнала «producer дошёл до управляемого RPC», а часть tests не ждёт уже существующую completion promise.
Current mechanism. Metadata/catalog/operations services после reservation запускают async drain через setImmediate и затем выполняют DB/access awaits. Tests ждут delay(5) или delay(10) и считают producer начавшимся; после этого меняют connection или присоединяют другую работу. Два portfolio unit cases опрашивают repository.publish.mock.calls десять event-loop turns, хотя service.request возвращает {id, done}. В metadata coalescing-case assertions стоят перед blocked.resolve; при failure до release нет finally, который освободил бы producer.
Impact / actual cost. Sequencing зависит от CPU/SQLite scheduling. На медленном запуске возможен другой interleaving или падение mock call assertion. При раннем failure gate может остаться открытым до teardown и затруднить диагностику. Polling publish не выражает полный lifecycle задачи. Impact unmeasured: нет воспроизведённого flaky-run и оценки времени; речь о доказанном способе координации.
Why now. Race/connection scenarios должны контролировать точки начала/завершения, сохраняя реальный repository/CAS путь. Увеличение sleep не устанавливает ordering guarantee.
Minimum change. Mock upstream отмечает started и блокируется на существующем gate; тест ждёт этот сигнал перед replace/join. Release помещается в finally, когда case может выйти раньше. Где есть work.done, await именно его. Где completion представлен только durable work row, оставить bounded state polling и meaningful error; не переводить SQLite целиком на fake timers и не создавать универсальную race DSL.
Guarantees at risk. Барьер должен стоять на внешнем RPC или явной publication boundary, не заменять transaction/repository fake. Не сериализовать сами конкурирующие операции, не удалять проверки stale revision/attempt и final state.
Counterarguments / trade-offs. Настоящая SQLite может делать короткий polling самым простым reliable completion способом. Awaited service.read иногда даёт producer время на запуск; на обычной машине sleep может работать стабильно. Поэтому не удаляются все delay и settled: distinction между state polling и readiness sleep принципиален. Existing work.done consumers подтверждают более простой путь для portfolio без нового API.
Verification or rollback. Запустить только затронутые cases в среде с локальными портами и disposable DB. Продемонстрировать, что replace/join выполняется после started, а case заканчивается после release/completion; проверить failure-path cleanup. Для portfolio unit достаточно двух existing launch cases. Повторять тесты для оценки flaky-rate только при оставшемся сомнении, не заменять correctness многократным зелёным запуском.
Related finding IDs: TI-001, TI-002, TI-004.
TI-004 — Targeted runner не задаёт freshness prerequisite для crash subprocess из dist
Decision = FIX · Severity = P2 · Confidence = High · Effort = S
Evidence: backend/test/sqlite.cjs:7–19,71–85; backend/test/jest-e2e.json:7–12; backend/test/tbank-operations-sync.e2e-spec.ts:424–499; backend/test/fixtures/tbank-sync-crash.cjs:1–17,59–82; backend/package.json:28–34; backend/README.md:250–254.
Claim. Допустимый targeted launch может проверять исходники основного driver и другой build crash producer одновременно.
Current mechanism. Runner принимает любой existing e2e filename и запускает ts-jest. Operations-sync suite fork-ит fixture, которая требует services/repository/Prisma из dist. Прямой node test/sqlite.cjs tbank-operations-sync.e2e-spec.ts и передача такого filename в test:sqlite не выполняют build. test:e2e, performance и OpenAPI manifest commands предварительно собирают приложение.
Impact / actual cost. Если dist отсутствует, child не достигает checkpoint; если build старый, parent и producer могут иметь разную reservation/publication семантику. Возможны ложные failure или недостоверная green proof относительно текущего source. UNVERIFIED: текущий dist не проверялся на freshness; доказан разрешённый launch path без prerequisite, а не наличие устаревшего build сегодня. Impact unmeasured.
Why now. Самый дешёвый targeted SQLite path должен честно сообщать свои inputs; именно crash tests используют иной runtime artifact.
Minimum change. В existing README/test command guidance явно связать dist-dependent targeted suite с npm run build && node test/sqlite.cjs tbank-operations-sync.e2e-spec.ts. У runner сделать понятной build prerequisite для этой suite, если пользователи продолжают запускать её напрямую. Не добавлять checksum framework и не делать build обязательным для всех pure/DB-only tests; наличие файла само по себе не доказывает freshness.
Guarantees at risk. Сохранить реальный отдельный child process, SIGKILL, checkpoint semantics, migrated temporary DB и сравнение durable rows после recovery. Замена crash helper на mocked restart уменьшит доказательность.
Counterarguments / trade-offs. Штатный full e2e flow уже безопаснее: build выполняется перед всеми suites. Default test:sqlite выбирает только auth/cards и не использует crash fixture. Проблема локальна targeted/custom invocation. Повторный build стоит времени; достаточно explicit prerequisite, без усложнения общей схемы запусков.
Verification or rollback. Проверить две existing entry boundaries: targeted suite без build даёт понятное ограничение; после fresh build её три crash checkpoints работают с текущими services. Эта runtime проверка отложена, потому что требует build/migrations. Rollback — отменить только guidance/preflight change, не child protocol.
Related finding IDs: TI-003, TI-005.
TI-005 — Opt-in regression runner использует слишком большой product workload
Decision = SIMPLIFY · Severity = P3 · Confidence = High · Effort = M
Evidence: backend/test/sqlite-regression.cjs:20–76,77–91; backend/test/sqlite.cjs:7–17,58–89; consumer: backend/README.md:250–254.
Claim. Regression isolation/cleanup самого runner повторяет полный functional product набор там, где для механизма достаточно меньшего существующего workload.
Current mechanism. Каждый check вызывает test/sqlite.cjs --all; три последовательных и два параллельных normal passes проверяют allocated directories, uniqueness, cleanup и неизменность sentinel. Затем отдельно запускаются migration failure и signals. При прохождении всех normal checks текущий workload — 5 × 19 = 95 functional suite invocations, каждая со свежими migrations/Jest; failure/signal attempts в число 95 не включены.
Impact / actual cost. Maintenance/check cost связан со всеми business suites; чужая продуктовая регрессия может остановить проверку runner mechanics. Количество повторных suite invocations установлено статически. Impact unmeasured: wall time, CPU и CI minutes не измерялись; command opt-in, не утверждается его запуск на каждом PR.
Why now. Механизмы защиты DB и cleanup стоит проверять достаточно дешёво, чтобы ими действительно пользовались после изменения runner.
Minimum change. Использовать существующие небольшие SQLite suites как workload: один normal проход должен сохранить переход между минимум двумя suites; parallel checks должны сохранить минимум два одновременных runner process. Failure/signal/sentinel assertions оставить. Full product e2e уже имеет собственный entrypoint. Новая искусственная suite или новый test framework не требуются.
Guarantees at risk. Уникальность DB между suites и процессами, штатные migrations, реальная child failure, неизменность sentinel, SIGINT/SIGTERM nonzero exit и cleanup. Нельзя сократить workload до пустого mocked subprocess, который перестаёт обращаться к SQLite.
Counterarguments / trade-offs. Полные проходы дополнительно проверяют все suites на внешнее shared state и дают broad stress evidence. Поэтому сокращение не срочное; имеет смысл после маленького доказательства эквивалентности runner guarantees. Three identical sequential passes сами по себе не доказывают, что пять полных product проходов — минимально нужный объём.
Verification or rollback. Сопоставить assertions старого и малого workload: минимум две suite directories, два parallel runners, sentinel и failure/signal cleanup. Один измеренный запуск старого/нового workload даст baseline для экономии; пока не заявлять ускорение. Rollback — вернуть workload selection в opt-in runner.
Related finding IDs: TI-002, TI-004.
TI-006 — failBond — unused flexibility в локальном frontend setup
Decision = DELETE · Severity = P3 · Confidence = High · Effort = S
Evidence: frontend/src/pages/bond/ui/BondPage/BondPage.test.tsx:88–107,294–404; consumers setup — 296,337,345,353,364,371,383.
Claim. Option failBond и его endpoint branch существуют без выполняющего их scenario.
Current mechanism. Local setup не экспортируется. failBond определяется в options и читается только внутри default bond handler; ни один callsite не передаёт его. Остальные options имеют реальные consumers. В соседней stock suite failStock используется в recovery case (frontend/src/pages/stock/ui/StockPage/StockPage.test.tsx:473–481), поэтому удалять симметрично оба flags нельзя.
Impact / actual cost. Небольшая лишняя ветвь чтения и сопровождения helper, создающая видимость поддерживаемого scenario. Impact unmeasured; это необязательный cleanup, не дефект продукта и не требование добавить тест ради покрытия ветви.
Why now. Только при ближайшей правке этой suite; отдельная migration ради одного flag не оправданна.
Minimum change. Удалить option и conditional response, оставить happy-path handler. Если появляется настоящий unavailable-bond scenario, case может явно добавить соответствующий MSW handler.
Guarantees at risk. Не удалять ambiguity handler, failure coupons/rating и recovery UI production logic; находка относится только к невызванной test-support ветви.
Counterarguments / trade-offs. Flag мог готовиться под будущий сценарий; это не current consumer. Если такой scenario уже нужен, owner должен добавить его по продуктовому риску — решение о assertions относится к аудиту 08.
Verification or rollback. Static dependency closure уже прослежена в полном файле. При реализации достаточно existing targeted BondPage suite и typecheck; не писать test на отсутствие option. Rollback — вернуть локальный flag при появлении consumer.
Related finding IDs: —.
5. DELETE / SIMPLIFY / KEEP / INVESTIGATE
| Decision | Кандидат / механизм | Основание и граница |
|---|---|---|
| FIX | Banking per-case state — TI-001 | Самостоятельные prerequisites, app может оставаться общим |
| FIX | Hourly provider в не-scheduler suites — TI-002 | Existing override; scheduler behavior сохраняется в своей boundary |
| FIX | Readiness/completion — TI-003 | Explicit RPC start/release и existing done; не увеличивать sleeps |
| FIX | Dist-dependent targeted command — TI-004 | Согласовать build input; не менять все runner entrypoints |
| SIMPLIFY | Workload runner regression — TI-005 | Малый real SQLite payload при сохранённых safety assertions |
| DELETE | failBond test option — TI-006 |
Нулевой consumer подтверждён; production behavior не удаляется |
| KEEP | Real SQLite migrations + runner env gate + temp cleanup | sqlite.cjs:60–89, e2e-env.cjs:1–13; native constraint/transaction proof и защита пользовательской DB |
| KEEP | Refresh CAS, financial concurrency и replay tests | app.e2e-spec.ts:161–213, bank-cards.e2e-spec.ts:118–164,391–423,462–524,622–708; mocks не равносильны реальной записи |
| KEEP | Revision/attempt publication и crash process | tbank-migration.e2e-spec.ts:150–168,194–231, operations-sync crash cases; не заменять rollback/CAS обычными mocks |
| KEEP | Pure money/calendar boundary | rub-money.spec.ts, bank-card.calculations.spec.ts, decimal.spec.ts; никаких app/DB dependencies ради вычислений |
| KEEP | QueryClient factory + strict MSW + resource envelope | Shared technical policy уже имеет consumers; singleton client и cross-repo framework ухудшат isolation |
| KEEP | Explicit page providers и domain fixtures | Повторение короткого arrange делает local expectations видимыми |
| KEEP | Два browser viewport instances | Проверяют настоящую geometry, которой нет в JSDOM; это не suite duplication |
| KEEP | Storybook network/storage isolation | Прямое требование synthetic catalog; не «тест factory ради factory» |
| KEEP | Build/container/durability boundaries | Packaging/crash/backup risks не воспроизводятся pure assertions |
| KEEP | Локальные deferred сейчас |
Backend target=ES2023 (tsconfig.json:13) не предоставляет Promise.withResolvers в default lib. Не копировать frontend ES2024 решение, меняя TS config ради четырёх коротких helpers |
| INVESTIGATE | Узкий HTTP module для stocks/bonds | AppModule bootstrap/migrations time not measured; сначала сохранить реальные guards/pipes/filter/routes и проверить цену |
| INVESTIGATE | Explicit QueryClient teardown для незавершённых requests | Fresh clients и RTL cleanup уже есть; утечка/мешающий GC timer UNVERIFIED. Не вводить client registry или gcTime policy без воспроизведения |
Ponytail complexity-pass
shrink:уменьшить product workload opt-in runner regression; использовать existing small SQLite suites с теми же lifecycle/sentinel checks. [backend/test/sqlite-regression.cjs:20–91, TI-005]shrink:заменить два portfolio publish-poll loops на existingwork.done. [backend/src/modules/investments/portfolio/application/portfolio.service.spec.ts:100–110,150–154, часть TI-003]delete:убрать unusedfailBondoption и conditional handler branch; replacement — обычный success handler. [frontend/src/pages/bond/ui/BondPage/BondPage.test.tsx:88–107, TI-006]
net: -3 lines, -0 deps possible. Это консервативный подсчёт только конкретного удаления failBond: одна option-строка плюс замена трёх строк conditional expression одной (1 + (3 − 1)). Patch не применялся; сокращение polling/runner LOC не оценивалось. Кандидатов на удаление dependency не установлено.
6. Cost & benefit
| Действие | Ожидаемый класс пользы | Что измерено | Обязательные regressions / safety boundary |
|---|---|---|---|
| TI-001: case-owned data | Cognitive load, время targeted diagnosis, maintenance | Static dependency category rename → restart; время not measured | Money, FK order, concurrent refunds/refresh, replay/tombstones, restart |
| TI-002: controlled scheduler | Reliability, reproducibility; чистота benchmark measurement | 10 functional AppModule suites без override; runtime влияние not measured | HTTP/DB остаются настоящими; scheduler проверяется управляемо |
TI-003: readiness / done |
Flakiness risk, ясность Arrange/Act, меньше incidental polling | Concrete sleep/launch/completion paths; flaky frequency not measured | Не подменить CAS; awaited release и terminal state, failure cleanup |
| TI-004: fresh build для crash path | Достоверность proof, diagnostic clarity | Source/dist split; actual stale artifact UNVERIFIED | Actual separate process/SIGKILL/current services/temp DB |
| TI-005: меньший runner workload | Local verification time; CI minutes только если command используется в CI | 95 suite invocations в успешных normal checks; wall time not measured | Две suite boundaries, два parallel processes, sentinel, миграции, signals/failure |
| TI-006: unused flag | Очень небольшая maintenance/cognitive экономия | Dependency closure, минимальный подсчёт удаления выше | Existing BondPage behavior; новые tests ради флага не нужны |
Локальная pure unit проверка заняла сообщённые Jest 0.379 s 2026-10-11; её нельзя экстраполировать на HTTP/SQLite или приписывать предложенному cleanup.
Исторические benchmark JSON содержат application-read metrics и различный hardware/profile (test/fixtures/tbank-read-baseline.json:1–19, tbank-read-persistent.json:1–20). Это не CI timing; даты измерения в просмотренных headers не указаны. Их latency не использована для расчёта экономии инфраструктуры и не представлена как актуальное состояние.
Нет доказанных процентов ускорения, объёма agent-token экономии или необходимости убрать зависимости.
7. Top actions
Quick wins
- TI-002: применить existing scheduler override в connection/read HTTP suites и benchmark. Большая польза isolation при маленьком diff; real time остаётся только в scheduler-specific proof.
- TI-001: сделать rename/restart/constraint cases self-contained. Сначала добавить явные prerequisites, затем убрать shared owner state между независимыми cases. Полный финансовый suite остаётся необходимой regression boundary.
- TI-003: в двух portfolio unit launch cases ждать
work.done. Уже существующий handle короче и точнее нового polling helper. - TI-004: записать существующую build + targeted crash command как правильный путь. Понятное ограничение предпочтительнее проверки freshness через новый hash/manifest механизм.
- TI-006: удалить unused flag при ближайшей правке BondPage suite; самостоятельная cleanup-кампания не нужна.
Решения с тестовым подтверждением
- TI-003: убрать readiness sleeps в metadata/catalog/operations producer races; поставить gate на реальную upstream boundary, гарантировать release/settlement при выходе из case. Сохранить real SQLite.
- TI-005: сократить opt-in infrastructure workload после доказательства сохранности suite/process isolation, failure/signals и sentinel assertions. Измерить один before/after запуск; не объявлять неподтверждённое ускорение.
Новая ADR для этих локальных исправлений не нужна. ADR/отдельное решение потребуется только при смене базовой testing boundary, backend compiler lib ради новых APIs или принятии нового общего HTTP harness; таких изменений аудит не требует.
Minimal Test Infrastructure Baseline
| Граница | Минимально достаточный baseline | KEEP / REMOVE / CONSOLIDATE |
|---|---|---|
| Pure domain | Existing Jest/Vitest imports, explicit financial/calendar data | KEEP; не стартовать app/DB |
| Service orchestration | Concrete service + узкие external mocks + управляемые gates | KEEP real completion handles; REMOVE readiness sleeps там, где нужен ordering |
| Backend DB | Existing isolated SQLite runner, production migrations, case-owned state | KEEP per-suite DB и env gate; CONSOLIDATE launch guidance, не DB mocks |
| Backend HTTP | ConfigureApp + настоящие guard/pipe/filter + controlled external boundaries | KEEP; REMOVE автоматический scheduler lifecycle из не-scheduler suites |
| Crash/rollback/restore | Existing subprocess/baseline artifact/backup runners | KEEP независимые boundaries; CONSOLIDATE build prerequisite с explicit command |
| Frontend API/UI | Fresh test client + strict MSW + explicit providers + RTL cleanup | KEEP; page fixtures остаются локальными |
| Layout | Existing Vitest browser project на narrow/wide | KEEP; не имитировать dimensions в JSDOM |
| Catalog | Synthetic stories + network/storage isolation check | KEEP; отдельная story-fixture DSL не нужна |
| Runner regression | Small existing SQLite workload + sentinel/failure/signal assertions | CONSOLIDATE mechanics checks вокруг малого workload; full product e2e отдельно |
| Cross-repo reuse | Публичные contract artifacts, без runtime imports helpers | KEEP автономность frontend/backend/specs/docs |
8. Duplication vs Readability Trade-off
| Повторение | Что совпадает семантически | Решение |
|---|---|---|
| Query/Mantine/Router provider trees | Framework composition; маршруты/auth requirements различаются | Factory QueryClient уже общая. Не делать omnibus renderWithEverything с десятком switches |
| Stock/Bond handlers | Похожая topology; financial metrics/rating/coupons, identifiers и outcomes различаются | Local ownership важнее визуального DRY. Удалить только доказанный dead option |
| Backend JWT/connection seeds | Несколько строк config; revision/account/lifecycle цели различаются | Не делить между репозиториями и не строить users/builders framework |
settled loops |
Metadata/catalog ждут отсутствие running work; operations ждёт конкретное state; forecast ждёт dependency group и quiet period | Не унифицировать все loops по синтаксису. При изменении owner сделать условия/диагностику явными |
| Portfolio unit publish polling | Два одинаковых обхода вместо уже предоставленного completion | Сократить через work.done, не извлекать waitForPublish |
| Banking happy-path helpers | HTTP/auth/expected success повторяются реально | Сохранить helpers; negative status assertions оставить в test body |
Четыре deferred<T> |
Promise control похож; часть поддерживает reject | Цена мала, lib compatibility различается с frontend. Не менять compiler contract ради DRY; отдельно устранять sequencing недостатки |
| Restart/crash/durability scenarios | Слова «restart» совпадают, failure boundaries разные | Nest provider restart, SIGKILL процесса и old/new-build rollback не взаимозаменяемы |
| Runner regression full passes | Повторяются все product cases, а assertions проверяют runner directories/sentinel | Малый real workload может сохранить именно infrastructure proof — TI-005 |
9. Cross-audit signals
- 08: соответствие named race исходному data flow и ожидание позднего response —
backend/test/tbank-local-reads.e2e-spec.ts:489–515,frontend/src/pages/stocks/ui/StocksPage/StocksPage.test.tsx:175–237; assertions здесь не переаудировались. - 08: cache/error scenarios с изменением handler без следующего Act —
frontend/src/pages/bond/ui/BondPage/BondPage.test.tsx:363–369,src/pages/stock/ui/StockPage/StockPage.test.tsx:463–471; вопрос содержательной regression proof. - 08/12: coverage include захватывает
src/pages/account/model/positions.fixtures.ts, exclude ограничен test/stories/support globs —frontend/vitest.config.ts:15–24; оценить смысл coverage input, не добавлять tests на fixture. - 06/13: termination уже начавшейся hourly work при app shutdown —
backend/src/modules/investments/tbank-sync/tbank-hourly.scheduler.ts:48–54,84–86,197–218; production lifecycle не расследовался сверх необходимого для TI-002. - 10/11: policy-tooling tests и стоимость repeated package builds — frontend
package.json:18–26,scripts/test-eslint.mjs; backendtest/eslint-config.cjs; их обязательность/trigger budget вне этого аудита.
10. Open questions / verification plan
| Вопрос, реально меняющий решение | Текущее состояние | Самый дешёвый следующий шаг |
|---|---|---|
| Должна banking suite быть sequential journey или набором независимых cases? | Формат it и отдельные titles указывают на самостоятельные cases; category dependency доказана |
Запустить rename/restart отдельно на disposable DB. Если journey намеренный — держать dependent steps внутри одного scenario, а не скрывать dependency между it |
| Срабатывает ли hourly tick на обычном runner до конца connection suites? | UNVERIFIED, registration/connection path доказан | Один controlled-clock/instrumented targeted run. Отсутствие срабатывания сегодня не отменяет полезность deterministic override |
| Какие targeted SQLite команды реально используют разработчики для crash cases? | Full command собирает build; direct runner принимает dist-dependent suite | Проверить usage/guidance и выполнить один targeted launch после fresh build. Отдельная freshness registry не нужна |
| Достаточен ли маленький workload для safety regression runner? | Product повторение доказано; эквивалентность ещё не проверена | Малый prototype на existing suites; сверить directory/process/sentinel/failure/signal assertions и один before/after timing |
| Есть ли dangling frontend requests/GC timers после cleanup? | UNVERIFIED; fresh clients, auth generation fence и cleanup уменьшают риск | Только при воспроизведении pending-after-test симптома снять handles/requests для одной suite; затем локальный teardown. Не добавлять глобальный registry заранее |
| Стоит ли уменьшать stocks/bonds AppModule bootstrap? | Framework/DB bootstrap присутствует; цена not measured, pipeline coverage имеет ценность | Измерить setup одной existing HTTP suite; прототип narrow module только при значимой цене и с сохранёнными guard/pipe/filter outcomes |
Основания для library-specific решений
- Vitest lifecycle/file isolation и default stack hooks сверены с документацией через Context7
/vitest-dev/vitest/v5.0.3,docs/guide/lifecycle.md,docs/config/sequence.md. В проекте manifest5.0.0; документация ближайшей версии использована для общих lifecycle semantics, не как proof прогона текущего runner. File isolation не сбрасывает все resources после каждого case автоматически. - TanStack Query testing/cache guidance сверено через Context7
/tanstack/query, testing guide и core GC implementation. Fresh client/retry-off соответствует текущей задаче. СоветgcTime=Infinityотносится к определённому Jest handle symptom и не является основанием без воспроизведения добавлять его в frontend Vitest. - Decision о сохранении маленьких backend deferred helpers основан на local
tsconfig.json:13и реальных consumers, а не на недоступном для default lib API.
Финальная проверка отчёта
- Findings ограничены audit 09; проверены owner/consumer, контрпримеры, минимальная правка и сохраняемые guarantees.
- Числа — file/import counts, static workload selection и один explicitly logged targeted unit run; runtime экономия не приписывалась.
- Проверены source references и существование цитируемых путей; новый файл просмотрен отдельно, выполнен whitespace check.
- Финальная проверка HEAD/status четырёх репозиториев подтверждает исходные SHA и отсутствие staged/unstaged/untracked изменений. Отчёт находится вне них; root ignore/allowlist не менялись.
Таблица для консолидации
| ID | Decision | Severity | Confidence | Repo | Files | One-line impact | Effort | Related IDs |
|---|---|---|---|---|---|---|---|---|
| TI-001 | FIX | P2 | High | backend | test/bank-cards.e2e-spec.ts | Межтестовая category dependency мешает самостоятельному запуску финансовых scenarios | M | TI-002, TI-003 |
| TI-002 | FIX | P2 | High | backend | src/modules/investments/tbank-sync/tbank-hourly.scheduler.ts; test/all-accounts.e2e-spec.ts; test/tbank-connection.e2e-spec.ts; test/tbank-local-reads.e2e-spec.ts; test/tbank-income-local.e2e-spec.ts; test/tbank-ledger-local.e2e-spec.ts; test/tbank-read-baseline.e2e-spec.ts | Фоновый tick способен менять fixtures/counters вне Act и загрязнять read benchmark | S | TI-001, TI-003, TI-005 |
| TI-003 | FIX | P2 | High | backend | test/tbank-metadata-persistence.e2e-spec.ts; test/tbank-catalog-discovery.e2e-spec.ts; test/tbank-operations-sync.e2e-spec.ts; src/modules/investments/portfolio/application/portfolio.service.spec.ts | Readiness sleeps и ignored done делают lifecycle зависимым от scheduling | M | TI-001, TI-002, TI-004 |
| TI-004 | FIX | P2 | High | backend | test/sqlite.cjs; test/fixtures/tbank-sync-crash.cjs; test/tbank-operations-sync.e2e-spec.ts; package.json; README.md | Targeted crash path допускает source/dist mismatch без fresh-build prerequisite | S | TI-003, TI-005 |
| TI-005 | SIMPLIFY | P3 | High | backend | test/sqlite-regression.cjs; test/sqlite.cjs; README.md | Opt-in infrastructure regression выполняет 95 functional suite invocations в normal checks | M | TI-002, TI-004 |
| TI-006 | DELETE | P3 | High | frontend | src/pages/bond/ui/BondPage/BondPage.test.tsx | Unused failBond option добавляет ветвь helper без consumer | S | — |