Зависимые проверки

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

Принцип зависимых проверок

В основе зависимых проверок лежит идея контекстной обусловленности:

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

В отличие от статических схем валидации, где правила фиксированы, зависимые проверки формируют динамическое дерево условий, пересчитываемое при каждом изменении состояния данных.

Контекст и data как основа зависимости

Vest использует функцию test и общий контекст data, который передаётся в каждую проверку. Это позволяет получать доступ ко всей форме внутри любого теста.

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

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

  test('confirmPassword', 'Пароли не совпадают', () => {
    enforce(data.confirmPassword).equals(data.password);
  });
});

Здесь вторая проверка напрямую зависит от значения password. Любое изменение первого поля автоматически влияет на результат второго.

Условные проверки через зависимости

Одним из ключевых механизмов зависимых проверок является условное выполнение тестов.

test('companyName', 'Название компании обязательно', () => {
  if (data.isCompany) {
    enforce(data.companyName).isNotEmpty();
  }
});

В этом примере поле companyName становится обязательным только при включённом флаге isCompany. Подобная логика позволяет строить формы с динамическими требованиями.

Использование skip как механизма управления зависимостями

В Vest существует механизм пропуска тестов, который часто используется для зависимых проверок.

test('vatNumber', 'ИНН обязателен для компаний', () => {
  if (!data.isCompany) {
    return;
  }

  enforce(data.vatNumber).isNotEmpty();
});

Логика становится декларативной: тест существует, но активируется только при выполнении условия.

Зависимости между несколькими полями

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

test('startDate', 'Дата начала должна быть раньше окончания', () => {
  enforce(new Date(data.startDate)).lessThanOrEquals(new Date(data.endDate));
});

test('endDate', 'Дата окончания должна быть позже начала', () => {
  enforce(new Date(data.endDate)).greaterThanOrEquals(new Date(data.startDate));
});

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

Групповые зависимости и составные правила

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

test('discount', 'Скидка недоступна для бесплатного тарифа', () => {
  if (data.plan === 'free' && data.discount > 0) {
    enforce(data.discount).equals(0);
  }
});

Здесь зависимость строится от комбинации значений plan и discount. Подобные конструкции часто используются в тарифных системах и административных панелях.

Композиция зависимых проверок

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

const isAdult = (data) => data.age >= 18;

test('driversLicense', 'Права доступны только совершеннолетним', () => {
  if (!isAdult(data)) return;

  enforce(data.driversLicense).isNotEmpty();
});

Здесь зависимость вынесена в отдельную функцию, что позволяет повторно использовать её в разных частях валидации.

Динамическое изменение структуры проверок

В сложных сценариях набор тестов может зависеть от входных данных на уровне самой схемы валидации.

const suite = create((data) => {
  if (data.type === 'individual') {
    test('firstName', 'Имя обязательно', () => {
      enforce(data.firstName).isNotEmpty();
    });
  }

  if (data.type === 'company') {
    test('companyName', 'Название компании обязательно', () => {
      enforce(data.companyName).isNotEmpty();
    });
  }
});

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

Зависимости и производительность

Зависимые проверки влияют на стратегию пересчёта валидации. Vest оптимизирует выполнение тестов следующим образом:

  • выполняются только затронутые тесты;
  • пересчитываются зависимости, связанные с изменёнными полями;
  • изолируются независимые проверки;
  • минимизируется повторное выполнение логики.

Это особенно важно в больших формах с десятками или сотнями полей.

Вложенные зависимости

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

test('address.zip', 'Индекс обязателен', () => {
  enforce(data.address.zip).isNotEmpty();
});

test('address.country', 'Страна обязательна', () => {
  enforce(data.address.country).isNotEmpty();
});

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

Конфликт зависимых правил

При проектировании валидации важно учитывать конфликтующие зависимости, когда разные тесты накладывают противоречивые условия.

test('age', 'Возраст должен быть в допустимом диапазоне', () => {
  enforce(data.age).greaterThanOrEquals(0);
  enforce(data.age).lessThanOrEquals(120);
});

test('age', 'Возраст несовместим с выбранной услугой', () => {
  if (data.service === 'insurance' && data.age < 18) {
    enforce(false).equals(true);
  }
});

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

Логическая цепочка зависимостей

Зависимые проверки могут формировать цепочки, где одно поле влияет на второе, второе на третье и так далее.

test('step1', () => {
  enforce(data.step1).isNotEmpty();
});

test('step2', () => {
  if (!data.step1) return;
  enforce(data.step2).isNotEmpty();
});

test('step3', () => {
  if (!data.step2) return;
  enforce(data.step3).isNotEmpty();
});

Такая структура используется в многошаговых формах, где каждый шаг зависит от успешного завершения предыдущего.

Использование зависимостей в сложных бизнес-правилах

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

  • доступность функций;
  • расчёт тарифов;
  • ограничения доступа;
  • валидация финансовых операций;
  • правила регистрации и авторизации.

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

Согласованность зависимых данных

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