Библиотека Vest реализует декларативную модель валидации, вдохновлённую подходом unit-тестирования. Каждая проверка рассматривается как изолированный тест, а набор проверок формирует suite. Исполнение suite происходит через вызов runner-функции, которая управляет порядком, условиями выполнения и асинхронностью.
Ключевая особенность архитектуры заключается в том, что выполнение проверок может комбинировать синхронные и асинхронные операции без явного изменения структуры suite. Это создаёт основу для параллельного выполнения отдельных веток валидации.
Синхронные проверки выполняются последовательно в рамках текущего потока исполнения. Их результат фиксируется немедленно, без ожидания.
import { create, test, enforce } fr om 'vest';
const suite = create((data = {}) => {
test('username', 'Имя обязательно', () => {
enforce(data.username).isNotEmpty();
});
test('age', 'Возраст должен быть числом', () => {
enforce(data.age).isNumber();
});
});
Асинхронные проверки добавляют возможность обращения к внешним источникам: API, базам данных, удалённым сервисам.
test('username_unique', 'Имя уже занято', async () => {
const exists = await api.checkUsername(data.username);
enforce(exists).equals(false);
});
Асинхронность становится базовым механизмом, на котором строится параллельное выполнение.
При наличии нескольких асинхронных проверок Vest не требует их последовательного ожидания. Внутри одного прохода suite такие проверки могут быть запущены одновременно, если они не зависят друг от друга.
Типовая реализация конкурентного поведения основана на запуске нескольких Promise одновременно с последующим агрегированием результатов.
const checks = [
api.checkUsername(data.username),
api.checkEmail(data.email),
api.checkProfile(data.profileId),
];
const results = await Promise.all(checks);
Такая модель снижает общее время выполнения валидации, особенно при большом количестве независимых запросов.
В Vest параллелизм проявляется не как отдельная сущность, а как следствие организации async-проверок. Каждая независимая ветка suite может выполняться одновременно с другими, если отсутствуют зависимости по данным.
При построении сложных схем валидации suite фактически разбивается на несколько групп проверок:
const suite = create(async (data) => {
test('email_format', () => {
enforce(data.email).matches(/.+@.+/);
});
test('email_unique', async () => {
const taken = await api.emailExists(data.email);
enforce(taken).equals(false);
});
test('profile_valid', async () => {
const profile = await api.getProfile(data.profileId);
enforce(profile.active).isTruthy();
});
});
Здесь асинхронные тесты могут выполняться конкурентно, не блокируя друг друга.
Параллельное выполнение требует контроля зависимостей между проверками. Если одна проверка опирается на результат другой, возникает необходимость сериализации.
Пример зависимости:
test('load_profile', async () => {
const profile = await api.getProfile(data.profileId);
return profile;
});
test('check_profile_permissions', async () => {
const profile = await api.getProfile(data.profileId);
enforce(profile.canEdit).isTruthy();
});
В подобных случаях повторный запрос можно избежать через кэширование промежуточного результата внутри scope suite:
let cachedProfile;
test('load_profile', async () => {
cachedProfile = await api.getProfile(data.profileId);
});
test('check_profile_permissions', () => {
enforce(cachedProfile.canEdit).isTruthy();
});
Такой подход сохраняет возможность параллельного выполнения других независимых тестов.
Эффективная стратегия заключается в разделении suite на логические группы, внутри которых возможен конкурентный запуск, а между группами — управляемая последовательность.
const suite = create(async (data) => {
// Группа 1: быстрые синхронные проверки
test('required_fields', () => {
enforce(data.email).isNotEmpty();
enforce(data.password).isNotEmpty();
});
// Группа 2: независимые async-запросы
test('email_unique', async () => api.emailExists(data.email));
test('username_unique', async () => api.usernameExists(data.username));
test('phone_unique', async () => api.phoneExists(data.phone));
// Группа 3: сложная серверная валидация
test('account_state', async () => api.checkAccount(data.userId));
});
Такое разделение позволяет рантайму эффективно распределять выполнение между async-задачами.
Одним из ключевых способов повышения производительности является объединение нескольких асинхронных проверок в один запрос.
test('uniqueness_checks', async () => {
const result = await api.checkBatch({
email: data.email,
username: data.username,
phone: data.phone,
});
enforce(result.emailTaken).equals(false);
enforce(result.usernameTaken).equals(false);
enforce(result.phoneTaken).equals(false);
});
В контексте параллельного выполнения это уменьшает количество отдельных Promise и снижает сетевые накладные расходы.
Параллельное выполнение асинхронных проверок требует строгой изоляции состояния suite. Общие переменные могут стать источником race conditions.
let user;
test('load_user', async () => {
user = await api.getUser(data.id);
});
test('check_role', () => {
enforce(user.role).equals('admin');
});
При конкурентном выполнении таких тестов возможна ситуация, когда check_role выполнится до завершения load_user.
Корректный подход заключается в локализации состояния:
test('user_flow', async () => {
const user = await api.getUser(data.id);
enforce(user.role).equals('admin');
enforce(user.active).isTruthy();
});
Таким образом устраняется необходимость внешней синхронизации.
Несмотря на наличие параллельных операций, итоговый результат suite должен оставаться детерминированным. Это означает:
Для этого используется механизм сбора результатов после завершения всех промисов, независимо от их порядка завершения.
Параллельное выполнение в контексте валидации ограничивается рядом факторов:
В таких случаях применяется частичная сериализация:
test('critical_check', async () => {
const valid = await api.criticalValidation(data);
if (!valid) return;
});
или условный запуск последующих проверок на основе промежуточного результата suite.
Эффективная организация параллельного выполнения требует балансировки между скоростью и стабильностью:
Эти стратегии позволяют удерживать производительность даже при большом количестве проверок без деградации времени отклика.