Приоритизация ошибок

Валидация в Vest строится вокруг декларативного описания тестов, которые выполняются последовательно внутри единого набора правил (suite). Каждый тест может завершиться либо успехом, либо фиксацией ошибки. При этом результат не прерывает выполнение остальных проверок по умолчанию: система стремится собрать максимально полную картину нарушений.

Каждое поле формирует собственный набор ошибок. Если для одного значения определено несколько проверок, каждая из них потенциально может добавить запись в результат. Однако итоговая структура ошибок после выполнения suite проходит нормализацию: для каждого поля формируется агрегированный список, который затем может быть дополнительно сокращён или приоритизирован в зависимости от конфигурации.

Ключевая особенность модели — разделение этапов:

  • выполнение тестов
  • накопление сырых ошибок
  • постобработка результата
  • формирование финального объекта ошибок

Именно на этапе постобработки вступают в силу правила приоритизации.


Порядок выполнения тестов и его влияние на приоритет

В Vest порядок объявления тестов напрямую влияет на порядок их исполнения. Это критично, поскольку первая обнаруженная ошибка часто становится основной в пользовательском интерфейсе.

import { create, test, enforce } from 'vest';

const suite = create((data = {}) => {
  test('email', 'Почта обязательна', () => {
    enforce(data.email).isNotBlank();
  });

  test('email', 'Неверный формат почты', () => {
    enforce(data.email).matches(/.+@.+\..+/);
  });
});

В данном случае приоритет ошибок определяется порядком:

  1. Проверка на пустое значение
  2. Проверка формата

Если поле пустое, обе проверки формально могут быть выполнены, но первая ошибка, соответствующая первому тесту, становится приоритетной при отображении.

Таким образом, естественный приоритет задаётся порядком объявления тестов, что позволяет управлять логикой отображения без дополнительных механизмов.


Базовый принцип: первая ошибка поля

По умолчанию Vest группирует ошибки по полям и применяет стратегию «первая ошибка поля».

Это означает:

  • все ошибки собираются
  • затем для каждого поля выбирается первая по порядку ошибка
  • остальные ошибки остаются в структуре результата, но могут не отображаться
const result = suite(data);

console.log(result.getErrors('email'));

В большинстве UI-сценариев используется именно этот подход, поскольку он снижает когнитивную нагрузку: пользователь видит одну проблему за раз.

Однако внутри объекта результата сохраняется полная информация, включая все обнаруженные нарушения, что позволяет переключаться между стратегиями отображения.


Fail-fast и остановка выполнения

Механизм fail-fast (или «ранняя остановка») изменяет модель приоритизации кардинально. Вместо накопления всех ошибок выполнение прерывается при первой критической ошибке.

Хотя Vest не навязывает единую реализацию fail-fast, подобное поведение достигается через управление выполнением тестов и условные конструкции.

test('password', 'Пароль слишком короткий', () => {
  enforce(data.password).longerThan(8);
  if (suite.get().hasErrors('password')) return;
});

Здесь приоритет задаётся вручную: после первой ошибки дальнейшие проверки не имеют смысла.

В более строгих конфигурациях применяется логика:

  • остановка проверки поля
  • остановка всей suite
  • пропуск последующих групп

Группировка как инструмент приоритизации

Группы (group) позволяют структурировать тесты и задавать приоритет целым блокам правил. Это особенно важно, когда необходимо разделить базовую и вторичную валидацию.

import { create, test, group, enforce } from 'vest';

const suite = create((data) => {
  group('required', () => {
    test('email', 'Обязательное поле', () => {
      enforce(data.email).isNotBlank();
    });
  });

  group('format', () => {
    test('email', 'Неверный email', () => {
      enforce(data.email).matches(/.+@.+\..+/);
    });
  });
});

Приоритет групп определяется порядком их объявления:

  1. required
  2. format

Это создаёт двухуровневую модель:

  • сначала критические ошибки
  • затем структурные или форматные

Если поле не заполнено, ошибки формата логически теряют смысл, и их отображение может быть подавлено на уровне UI.


Приоритизация на уровне поля

Vest предоставляет возможность тонко управлять тем, какие ошибки считаются первичными внутри одного поля.

Основная стратегия:

  • ошибки сортируются по порядку тестов
  • первая ошибка считается основной
  • остальные остаются вторичными

Однако возможна более сложная логика через условные проверки.

test('username', 'Некорректное имя', () => {
  const value = data.username;

  enforce(value).isNotBlank();

  if (!value) return;

  enforce(value).longerThan(3);
  enforce(value).shorterThan(20);
});

Здесь реализуется зависимая приоритизация:

  • пустое значение имеет абсолютный приоритет
  • остальные проверки выполняются только при прохождении базового условия

Это позволяет исключить «шум» из результатов и гарантировать, что пользователь видит только релевантные ошибки.


Влияние условных тестов на порядок ошибок

Vest поддерживает условное выполнение тестов, что напрямую влияет на приоритет ошибок. Условные конструкции позволяют пропускать целые цепочки проверок, если базовое условие не выполнено.

test('age', 'Возраст должен быть числом', () => {
  enforce(data.age).isNumber();
});

test('age', 'Возраст должен быть не меньше 18', () => {
  if (!Number.isFinite(data.age)) return;
  enforce(data.age).greaterThanOrEquals(18);
});

В данном сценарии:

  • первая ошибка имеет абсолютный приоритет
  • вторая становится зависимой

Таким образом формируется иерархия:

  1. тип данных
  2. бизнес-правила

Кастомная приоритизация через результат suite

Объект результата suite позволяет переопределять логику отображения ошибок без изменения тестов.

const result = suite(data);

const emailErrors = result.getErrors('email');
const firstEmailError = emailErrors?.[0];

На уровне потребления результата возможны стратегии:

  • отображать первую ошибку
  • отображать все ошибки
  • фильтровать по типу (required / format / business)

Vest предоставляет необработанный набор ошибок, что делает приоритизацию частью слоя представления, а не только слоя валидации.


Приоритет ошибок при множественных тестах одного поля

Когда поле участвует в большом количестве проверок, возникает необходимость контролировать порядок значимости ошибок.

test('password', 'Пароль обязателен', () => {
  enforce(data.password).isNotBlank();
});

test('password', 'Слабый пароль', () => {
  enforce(data.password).matches(/(?=.*[A-Z])(?=.*\d)/);
});

test('password', 'Слишком короткий пароль', () => {
  enforce(data.password).longerThan(8);
});

В этом случае порядок становится критически важным:

  1. обязательность
  2. длина
  3. сложность

Если не соблюдать последовательность, пользователь может получать менее полезные сообщения, например о сложности вместо сообщения о пустом значении.


Приоритизация и повторное выполнение suite

Каждый запуск suite полностью пересчитывает результат, что означает отсутствие накопления состояния между вызовами. Это важно для приоритизации:

  • порядок ошибок не сохраняется между запусками
  • каждая валидация независима
  • приоритет определяется только кодом тестов
suite(data1);
suite(data2);

Это обеспечивает детерминированность: одинаковый порядок тестов всегда даёт одинаковую структуру приоритетов.


Стратегии проектирования системы приоритетов

На практике применяются несколько устойчивых моделей:

1. Слоистая модель

  • required
  • format
  • business rules

Каждый слой имеет свой приоритет.

2. Линейная модель

  • строгий порядок тестов
  • первая ошибка доминирует

3. Зависимая модель

  • проверки включаются только при прохождении базовых условий

4. UI-ориентированная модель

  • приоритет определяется тем, что удобнее показать пользователю
  • backend хранит все ошибки, frontend решает отображение

Взаимодействие приоритета и читаемости ошибок

Приоритизация ошибок в Vest напрямую связана с качеством UX. Избыточное количество сообщений снижает эффективность интерфейса, поэтому система естественным образом поощряет:

  • ранние проверки обязательных полей
  • минимизацию каскадных ошибок
  • структурирование тестов по значимости

С точки зрения архитектуры, приоритет ошибок становится не побочным эффектом, а частью проектирования схемы валидации, где порядок тестов и их зависимость формируют итоговое поведение системы.