<assert> — конфиг проверяет свой собственный вывод
Когда нужно: форма данных важна тому, кто их получит, и вы хотите, чтобы запуск остановился, а не отдал файл, который незаметно «поехал».
Утверждение описывает свойство, которым готовый запуск обязан обладать. Держится — ничего не происходит. Не держится — запуск останавливается вашей же фразой, до того как записана хоть одна строка.
<assert that="Tracked == 700" says="у каждого отправленного заказа должен быть трек-номер"/>
Живёт в <env>, рядом с <uniq> и <distinct>: как и они, это
высказывание о том, чему обязан удовлетворять весь запуск, а не о том, чем является одна
колонка.
Их два. that= читают один раз, по числам за весь запуск. each= отвечают на
каждой строке — см. Каждая строка ниже.
Что стоит утверждать
Не то, что конфиг и так написал. Вы написали percent="70" и утверждаете 70 процентов —
вы проверили, что TDC умеет считать.
Ценность в том, чего конфиг не говорит. Здесь фильтр и условие накладываются друг на друга, и доля, доехавшая до файла, нигде в тексте не написана:
<tdc>
<env count="1000" seed="orders" local="en">
<sequence name="Status"><gen type="text" value="shipped,pending" percent="70,30"/></sequence>
<sequence name="Tracking" parent="Status.shipped">
<gen type="regex" value="[A-Z]{2}[0-9]{9}" if="Status == 'shipped' && _count % 4 != 0"/>
</sequence>
<sequence name="Tracked"><gen type="stat" of="Tracking" op="count"/></sequence>
<assert that="Tracked == 700" says="every shipped order should carry a tracking number"/>
</env>
<block>
<line><data>${{Status}},${{Tracking}}</data></line>
</block>
</tdc>
tdcv2: assert failed: every shipped order should carry a tracking number Tracked == 700 with Tracked = 522
Больше ничто в TDC про этот конфиг мнения не имеет. Он разбирается, проходит проверку, запускается — и 178 отправленных заказов выходят с пустым трек-номером. Ровно ради такого сбоя утверждение и существует.
Код возврата — 1, поэтому CI на нём останавливается.
Кратко
| Атрибут | Обязателен | Что делает |
|---|---|---|
that | одно из двух | Условие по значениям за весь запуск, читается один раз, на языке if= |
each | одно из двух | Условие, которому обязана удовлетворять каждая строка, на том же языке |
says | да | Фраза, которую получит читатель, когда условие не сойдётся |
says= обязателен в любом случае. Утверждение, которое срабатывает, показывая одно лишь
выражение, заставляет читателя месяцы спустя разбираться в CI-логе, ради чего оно было
написано. that= и each= нельзя написать на одном теге: их проверяют разное число раз,
и читатель не смог бы понять, которое из двух описывает фраза в says=.
Флага нет. Утверждение работает потому, что оно написано: проверка, которую нужно не забыть включить, — это проверка, которую никто не запускал, на конфиге, который выглядит проверенным.
Откуда берутся числа
that= читает колонки, и читать обычно стоит
<gen type="stat"> — одно число на весь запуск.
<env count="500" seed="clinic" local="en">
<sequence name="Visit"><gen type="date" from="2026-01-01" to="2026-06-30" format="YYYY-MM-DD"/></sequence>
<sequence name="Follow"><gen type="date" of="Visit" plus="7..30d" format="YYYY-MM-DD"/></sequence>
<sequence name="Ward"><gen type="text" value="A,B,C" percent="50,30,20"/></sequence>
<sequence name="Rows"><gen type="stat" of="Visit" op="count"/></sequence>
<sequence name="Wards"><gen type="stat" of="Ward" op="count"/></sequence>
<assert that="Rows == _total" says="у каждой строки есть дата визита"/>
<assert that="Wards == _total" says="у каждой строки есть отделение"/>
</env>
_total — число строк, и это единственная встроенная величина, которую утверждению
разрешено читать.
Правило, которое не даёт себя обмануть
Каждое имя в that= должно быть одинаковым на всех строках. Колонка stat такова по
построению. Колонка text с одним значением — по факту. Вытянутая колонка — нет, и её
отвергают:
<sequence name="Amount"><gen type="number" value="1..500"/></sequence>
<assert that="Amount > 0" says="every amount is positive"/>
tdcv2: assert ("Amount > 0"): "Amount" is not the same on every row, so this would have checked the first row and called the run verified. A whole-run assertion reads whole-run values: give it a <gen type="stat" of="Amount" op="…"/> column, or _total. To state it of every row instead, write each= rather than that=.Без этого правила that="Amount > 0" прочитало бы нулевую строку и отчиталось об одной
строке из пятисот — проверка прошла потому, что почти не смотрела, но носит значок
«проверено». Это ровно та болезнь, ради которой вся эта возможность и сделана.
Колонку, которую фильтр parent= оставляет пустой на части строк, отвергают по той же
причине: у запуска нет для неё одного значения, и условие сравнивало бы с тем, что
случайно оказалось в нулевой строке. Сверните её через op="count".
Каждая строка — each=
Отказ выше — не тупик, а указатель. «Каждая сумма положительна» — настоящее утверждение,
и место ему в each=:
<sequence name="Amount"><gen type="number" value="1..500"/></sequence>
<assert each="Amount > 0" says="каждая сумма положительна"/>
Условие отвечают на каждой строке, на том же языке, что и if=, и по тем же именам —
колонки этой строки плюс встроенные значения. Учить нечего,
и колонку stat заранее строить не нужно.
Когда строка его нарушает, запуск останавливается на ней и говорит, на какой:
<sequence name="Fee"><gen type="number" value="-3..20"/></sequence>
<assert each="Fee >= 0" says="комиссия никогда не бывает отрицательной"/>
tdcv2: assert failed on row 16: комиссия никогда не бывает отрицательной Fee >= 0 with Fee = -3
Код выхода — 1, как и у that=.
Останавливается на первой сбойной строке, а не считает их все. На потоковом движке строки до неё уже на диске, так что «проверить весь файл, а потом отчитаться» — обещание, которое не всякий движок мог бы дать честно; а запуск, чьи данные уже неверны, не станет вернее от того, что мы узнаем, насколько. Названная строка всегда первая: строки проверяются по порядку, каждая — перед тем, как её запишут.
Из-за этого конфиг с each= работает в один поток. Воркеры берут по диапазону строк
и каждый остановился бы на своей первой сбойной, так что читателю показали бы ту, до
которой первым добрался чей-то поток, — разное число на одном и том же конфиге и сиде.
Явный --jobs скажет об этом и продолжит в одном потоке.
Больше никаких последствий для движков нет: в отличие от that=, построчное утверждение
не требует колонки stat, поэтому конфиг с ним по-прежнему стримится. См.
большие выгрузки.
Чего пока нет
- Проверки контрольной цифры. Вот это ловушка: её посчитал
<compute>, и пересчёт утверждает лишь то, что один и тот же код согласен сам с собой.
Движки
Утверждение с that= читает колонки stat, а stat и так отправляет конфиг на движок в
памяти, — своих последствий оно не добавляет. Утверждение с each= тоже не добавляет,
если не считать одного потока. См. большие выгрузки.
См. также
stat— откуда берутся числа- Выражения — язык, на котором пишется
that= - Уникальность — другие высказывания про весь запуск