The Jerusalem PostIDF's next-generation artillery gets NIS 600 million training boost with Elbit dealUN NewsSix in seven journalist murders go unpunished, UNESCO reportsPunchNLC can negotiate alone, Ajaero replies TUC presidentBollywood HungamaBigg Boss 20: Panchayat fame Aasif Khan eliminated from Salman Khan-hosted showInquirerPITX logs nearly 1.5 million passengers during Undas weekDaily MaverickDIGITAL POLITICS OP-ED: The anti-foreigner movement squares up to the state in run-up to electionsWirtualna PolskaKryzys w TK. Ćwiąkalski: Kancelaria Premiera musiała być poinformowana7sur7Élections de mi-mandat aux États-Unis: Trump veut faire mentir les mauvais sondages dans les urnesIl Fatto QuotidianoPier Paolo Pasolini, la Procura di Roma riapre le indagini sull’omicidio: nuovo fascicolo dopo un espostoNU.nlVerstappen trots op F1-wereldkampioen Antonelli: 'Ongelooflijk goed gereden'La PresseLa revue de presse de Paul Arcand | Négociations Canada–États-Unis, les propos de Trump et élections de mi-mandatGMA NewsFore sweeps through homes in Barangay E. Rodriguez, QC
The Daily Newsstand · Free, Always
Monday, November 2, 2026

Зелёный мониторинг и сломанная корзина: как устроены сценарные проверки в Statuser

Translate

Почти каждый, кто выкатывал релизы, видел такую картину. Сайт открывается, сервер отвечает 200, сертификат в порядке, и мониторинг весь вечер показывает зелёный статус. А кнопка «Оформить заказ» после релиза перестала работать, и узнают об этом утром из писем покупателей.

Обычная проверка доступности такой сбой не заметит: она узнаёт, отвечает ли сайт, а не можно ли им пользоваться. Чтобы это узнать, проверка должна сама пройти путь пользователя: открыть страницу, войти в кабинет, положить товар в корзину. Так в Statuser появились сценарные проверки: тесты на Playwright, которые мы запускаем по расписанию в настоящем браузере.

Снаружи это ещё один тип мониторинга, а внутри Statuser впервые выполняет код, который написал клиент. Почти вся работа над фичей свелась к двум вещам: выполнять этот код безопасно и показывать человеку, что именно сломалось на его сайте.

Меня зовут Михаил Шпаков, я развиваю Statuser. В прошлых статьях я рассказывал, зачем научил Statuser следить за DNS и почему 200 OK от гейтвея ничего не значит, а сегодня расскажу, как устроены сценарные проверки.

❯ Откуда взялись сценарии

Просьба о таких проверках появилась на портале идей Statuser ещё в январе: «войти, сходить в API, проверить результат». Потом под ней собирались голоса, а в комментариях появлялась конкретика: одним нужен был вход в кабинет, другим оформление заказа, третьим цепочка запросов с кукой, которая живёт несколько минут. К осени стало понятно, какие пути люди хотят проверять и что им важно увидеть, когда проверка упала.

Изобретать формат не пришлось: синтетический мониторинг на Playwright давно стал привычным инструментом. Тест пишется так же, как для CI, и умеет всё, что умеет браузер: дождаться формы, которая дорисовывается после загрузки, пройти оплату в iframe, передать токен из одного ответа в следующий запрос.

Минус у кода один: его нужно написать, и обойтись без этого можно двумя способами. Можно записать тест в Codegen: запускаете npx playwright codegen, проходите путь в браузере, и каждое действие превращается в строку кода. А можно описать путь словами Statuser AI, ассистенту в панели, и получить черновик, который переживёт редизайн: элементы в нём ищутся по роли и подписи, после каждого важного шага стоит проверка, а логины и пароли берутся из переменных окружения.

Сайта ассистент не видит, поэтому подписи кнопок стоит сверить со страницей. Для этого в форме сценария есть кнопка «Запустить»: она прогоняет черновик так же, как по расписанию, и показывает результат со скриншотом, видео и трейсом.

❯ Один сбой от начала до конца

Вернёмся к кнопке «Оформить заказ» из начала. Сценарий, который за ней следит, занимает несколько строк. Для примера возьмём нашу собственную страницу тарифов: тест открывает главную, переходит в «Тарифы» и ждёт кнопку оформления.

import { test, expect } from '@playwright/test';

test('оформление заказа', async ({ page }) => {
  await page.goto('https://statuser.cloud/');
  await page.getByRole('link', { name: 'Тарифы' }).first().click();
  await expect(page).toHaveURL(/pricing/);
  await expect(page.getByRole('button', { name: 'Оформить заказ' })).toBeVisible();
});

После релиза кнопка пропала. Главная по-прежнему отвечает 200, и HTTP-проверка остаётся зелёной, а сценарий на ближайшем прогоне доходит до страницы тарифов, пять секунд ждёт кнопку и падает. Инцидент открывается сразу, а уведомление уходит туда же, куда и остальные: в почту, Телеграм, MAX, Slack, Discord, Пачку и ещё несколько каналов. Покупатели написать ещё не успели.

Подтверждения из других городов, как у HTTP-проверок, здесь нет: сценарий проверяется из одного города, который выбирает сам пользователь. Случайные срывы гасит повтор внутри самого теста: строка test.describe.configure({ retries: 1 }) перезапустит упавший тест в пределах того же таймаута.

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

Но главное в ней трейс. Во встроенном просмотрщике Playwright сценарий проходится по шагам, и у каждого действия видны страница до и после, сетевые запросы и сообщения консоли. Обычно уже по нему понятно, почему кнопки нет: не ответил запрос к API, сменилась подпись или кнопку перекрыл баннер.

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

Когда кнопку вернут, следующий успешный прогон сам закроет инцидент.

❯ Что ещё ловит сценарий

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

Вход в личный кабинет

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

Отдельная учётная запись нужна и по другой причине. Логин и пароль тест читает из зашифрованных переменных окружения, но как только пароль попадает в поле на странице, Playwright записывает его в трейс вместе с остальными действиями. Что из этого следует для нас, расскажу ниже.

API с авторизацией

Обычная HTTP-проверка умеет ходить в API с заголовком авторизации, но токен в ней записан один раз. Короткоживущий токен или сессионная кука истекают через несколько минут, и проверка начинает падать, хотя API работает. Поэтому закрытую часть API часто либо проверяют вечным токеном, который жалко отдавать стороннему сервису, либо не проверяют вовсе.

Сценарий проходит авторизацию так же, как настоящий клиент: на каждом прогоне получает свежий токен и сразу идёт с ним в защищённый метод. Браузер для этого не нужен: цепочка запросов через request из Playwright проходит за пару секунд, а куки из ответа подставляются в следующие запросы сами.

import { test, expect } from '@playwright/test';

test('заказы через API', async ({ request }) => {
  const auth = await request.post('https://api.example.com/oauth/token', {
    form: {
      grant_type: 'client_credentials',
      client_id: process.env.CLIENT_ID!,
      client_secret: process.env.CLIENT_SECRET!,
    },
  });
  expect(auth.ok()).toBeTruthy();
  const { access_token } = await auth.json();

  const orders = await request.get('https://api.example.com/v1/orders?limit=1', {
    headers: { Authorization: `Bearer ${access_token}` },
  });
  expect(orders.status()).toBe(200);
});

Сайт, который пускает не всех

Часть сайтов отвечает по-разному в зависимости от адреса: закрытые разделы открыты только для белого списка, цены и язык зависят от страны, а вход с незнакомого адреса требует подтверждения. Поэтому город для сценария выбирает сам пользователь: Москва, Санкт-Петербург, Амстердам, Алматы или Нью-Йорк.

Запросы сценария уходят с тех же IP-адресов, что и остальные проверки Statuser. Если они уже добавлены в белый список, больше ничего настраивать не нужно.

❯ Что происходит на нашей стороне

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

Чужой код

Тест на Playwright — это обычный код на Node.js. Ему доступно всё, что доступно процессу, в котором он запущен: файловая система, переменные окружения, сеть. Запускать такой код рядом с бэкендом, базой и очередями было нельзя, даже если бы сценарий писал только сам владелец аккаунта.

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

Связь с песочницей только входящая: бэкенд сам приходит к ней с заданием и забирает результат в ответе. Доступа к базе, очередям и хранилищу файлов у неё нет, и это принципиально: всё, что дали бы песочнице «для удобства», получил бы и скрипт клиента.

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

Отдельно пришлось следить за памятью. Если сценарий съедает её целиком, ядро убивает контейнер вместе с процессом, который обслуживает прогон, и ответа нет вообще. Для нас такой прогон выглядел бы сбоем песочницы, и клиент не узнал бы, что дело в его сценарии. Поэтому процесс сам следит за памятью и останавливает тест чуть раньше, с понятной ошибкой.

Пароли в трейсе

Трейс Playwright хранит всё, что происходило на странице, включая введённые пароли, куки и заголовки. Поэтому файлы сценария живут ровно столько, сколько инцидент: удалили инцидент, и скриншот, видео и трейс удаляются вместе с ним. Файлы пробных прогонов из формы удаляются через сутки.

По той же причине трейс открывается у нас. Проще всего было бы дать ссылку на публичный просмотрщик trace.playwright.dev с адресом файла в параметре, но тогда адрес файла с паролями оказался бы в чужой адресной строке. Поэтому просмотрщик Playwright встроен в панель Statuser, а ссылка на файл работает час: ей можно поделиться с коллегой, чтобы вместе разобрать падение, но надолго она не сохраняется.

Пропавший трейс

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

Песочница отдаёт результат одним потоком, и трейс в нём идёт последним. Закончив запись, процесс в контейнере сразу завершался и гасил вместе с собой сеть контейнера. С его точки зрения данные были отправлены, а на деле хвост ещё лежал в буфере и до бэкенда не доходил. Скриншот и видео успевали проскочить, трейс нет: на медленном чтении терялось 1,8 МБ из 4,5.

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

❯ В заключение

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

HTTP-проверка отвечает на вопрос, жив ли сайт. Сценарий отвечает на другой: может ли человек сделать на нём то, ради чего пришёл.

Спасибо, что дочитали до конца! Расскажите в комментариях, как вы проверяете вход, корзину и оплату у себя: автотестами в CI, синтетическим мониторингом или узнаёте о поломке от пользователей.

Если после статьи захотелось проверить, работает ли у вас оформление заказа, попробуйте завести сценарий в Statuser: он доступен на тарифах Pro и Team, а первый тест можно записать в Codegen или попросить у Statuser AI.

Может быть интересно:
Перейти ↩

Перейти ↩

Новости, обзоры продуктов и конкурсы от команды Timeweb.Cloud — в нашем Telegram‑канале ↩

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.