Цель: проверять инварианты остатков на рандомизированных последовательностях операций, а не на единичных примерах.
Зависимость: #5 , #6 , #9 .
Что сделать
Подключи net.jqwik:jqwik. Сформулируй инварианты домена как свойства:
остаток никогда не уходит в минус (списание сверх остатка отклоняется, Валидация остатков #9 );
Σ(приходы) − Σ(успешных расходов) == текущий остаток;
порядок применённых операций не нарушает эти равенства.
Генерируй случайные последовательности операций и прогоняй через сервис:
@ Property
void stockNeverNegative (@ ForAll ("ops" ) List <Movement > ops ) {
int stock = apply (ops );
assertThat (stock ).isGreaterThanOrEqualTo (0 );
}
@ Provide Arbitrary <List <Movement >> ops () { /* RECEIVE/WRITE_OFF, random qty */ }
Трудные места: генераторы реалистичных последовательностей (смесь приходов/расходов, граничные количества, нули); jqwik сам сжимает контрпример (shrinking) — зафиксируй найденный сид как регрессионный тест; решить, гонять на чистом сервисе с моками или на Testcontainers.
Acceptance criteria
Цель: проверять инварианты остатков на рандомизированных последовательностях операций, а не на единичных примерах.
Зависимость: #5, #6, #9.
Что сделать
net.jqwik:jqwik. Сформулируй инварианты домена как свойства:Σ(приходы) − Σ(успешных расходов) == текущий остаток;Acceptance criteria