fix(lineage): 3 e2e CI failures — Liquibase XSD + JSONB ?-op + size cap

Phase 4 e2e suite (skipped в local mvn verify, гонится только на CI с
testcontainers) поймал 3 issues. Все исправлены, e2e теперь 20/20 PASS
локально (Postgres testcontainer):
- AuthE2ETest 6/6
- AutoDeriveGeometryE2ETest 6/6
- BitemporalE2ETest 4/4
- OutboxKafkaE2ETest 1/1
- SmokeE2ETest 1/1
- WebhookE2ETest 2/2

Fix #1 — Liquibase XSD ordering (0016 changeset):
- <comment> должен идти ПОСЛЕ <preConditions>. Liquibase XSD строгий:
  preConditions → comment → operations.
- "Invalid content was found starting with element preConditions" на
  startup → ApplicationContext fail → весь webserver не поднимается.

Fix #2 — JSONB ?-operator vs JDBC parameter substitution (0016):
- Postgres JDBC PreparedStatement обрабатывает любой `?` как $N
  placeholder. SQL "prop.value ? 'x-references'" → "prop.value $1
  'x-references'" → "syntax error at or near $1". Same для regex с $
  anchor.
- Replaced `?` operator на эквивалент `-> 'key' IS NOT NULL`,
  drop-or-replace regex anchors на `LIKE '%.%'` (split_part guards
  malformed values gracefully).
- Comment в migration документирует ловушку для будущих changesets.

Fix #3 — service size cap mismatch (LineageIndexService):
- CascadeCloseService.evaluatePlan вызывает findRecordDependents с
  size=CASCADE_MAX_PER_REQUEST=500. Но findRecordDependents enforced
  size 1..200 → IllegalArgumentException → 400 на DELETE record path.
- Bumped service cap до 500. Controller отдельно держит public cap=200
  (UI не нуждается в больше).
- Test updated: expects "size 1..500".

Lessons learned:
- Local pre-push discipline должна включать `mvn -P e2e` для миграций.
  Не запустил, потому что: e2e требует Docker, медленнее, не было в
  routine. Now corrected — будущие migration changes будут идти через
  e2e check.
- Liquibase JDBC рабочий принцип: SQL прогоняется через PreparedStatement
  → любой `?` или `$N` ловится. Documenting в migration comment.

Verify:
- mvn -pl ordinis-app -am -P e2e test: BUILD SUCCESS, 20/20 PASS (28s).
- mvn test: 106 unit tests + 20 e2e — все green.
This commit is contained in:
Zimin A.N.
2026-05-08 11:48:39 +03:00
parent a7cdd5df5c
commit 7d2199a3f3
3 changed files with 18 additions and 7 deletions
@@ -29,7 +29,6 @@
pure JSONB indexing (jsonb_each + ?-marker) — не ожидаем больше.
-->
<changeSet id="0016-record-dependents-index-create" author="ordinis">
<comment>MV record_dependents_index: precomputed reverse FK</comment>
<preConditions onFail="MARK_RAN">
<not>
<sqlCheck expectedResult="1">
@@ -37,6 +36,7 @@
</sqlCheck>
</not>
</preConditions>
<comment>MV record_dependents_index: precomputed reverse FK</comment>
<sql splitStatements="false"><![CDATA[
CREATE MATERIALIZED VIEW record_dependents_index AS
SELECT
@@ -58,9 +58,17 @@
FROM dictionary_records r
JOIN dictionary_definitions src_def ON src_def.id = r.dictionary_id
JOIN LATERAL jsonb_each(src_def.schema_json -> 'properties') AS prop(key, value) ON true
WHERE prop.value ? 'x-references'
AND prop.value->>'x-references' ~ '^[a-zA-Z_][a-zA-Z0-9_]*\.[a-zA-Z_][a-zA-Z0-9_]*$'
AND r.data ? prop.key
-- Note: избегаем JSONB ?-operator — Liquibase прогоняет SQL через
-- PreparedStatement, который любой ? интерпретирует как $N
-- placeholder → "syntax error at $1". Заменяем на эквиваленты:
-- prop.value ? 'x-references' → prop.value -> 'x-references' IS NOT NULL
-- r.data ? prop.key → r.data -> prop.key IS NOT NULL
-- Регекс с $ anchor по той же причине заменён на LIKE — split_part'ы
-- ниже сами справятся с malformed (вернут empty strings, никогда не
-- match-ятся с real records).
WHERE prop.value -> 'x-references' IS NOT NULL
AND prop.value->>'x-references' LIKE '%.%'
AND r.data -> prop.key IS NOT NULL
AND jsonb_typeof(r.data -> prop.key) = 'string'
WITH NO DATA;
]]></sql>