Как тестировать, когда платы нет
Плата физически отключена и нед оступна. Всё, что осталось от живого железа, — это байты
в packages/lock-protocol/test/vectors.ts и описание протокола. Поэтому у проекта две
опоры: векторы и эмулятор платы.
Векторы — неприкосновенны
Векторы сгенерированы из съёма живого устройства и руками не правятся.
Если тест на векторах упал, значит код разошёлся с поведением железа. Правильное действие — вернуть код как было, а не подогнать ожидание. Проверить, кто прав, уже негде: платы нет.
Запуск:
npm test # весь репозиторий
npm run test:watch
Чем что покрыто на 2026-09-01 (числа быстро растут — смотрите вывод, а не таблицу):
| Пакет | Тесты | О чём они |
|---|---|---|
lock-protocol | векторы с живого железа | кадры, контрольная сумма, разбор потока, барьер запрещённых кадров |
contracts | 123 | схемы обмена: что примут и что отвергнут |
i18n | 80 | полнота словарей и длина строк под экран 800 × 1280 |
panel | 200 | состояния ячеек, журнал, тревоги, привязка, опознание звонящего |
⚠️ Сквозные тесты панели (Playwright) написаны, но ни разу не запускались — так и
помечено в apps/panel/README.md. Считать их зелёными нельзя.
Эмулятор платы — packages/lock-sim
Это не игрушка, а единственное место, где поведение платы теперь существует. Правится он не «как удобнее», а только следом за документом протокола и векторами.
| Файл | Что внутри |
|---|---|
board.ts | модель платы: 27 каналов, импульс 357 мс, эхо на открытие, тишина на битую сумму |
faults.ts | режимы неисправности — ради них модель и нужна |
transport.ts | LockTransport — то, через что приложение говорит с платой, одинаково с моделью и с нативным портом |
cli.ts | запуск моделью отдельным процессом (--stdio, --pty) |
Что модель воспроизводит намеренно неудобно
Эти решения выглядят странно, и «чинить» их нельзя — они и есть смысл эмулятора:
- На кадр с несошедшейся контрольной суммой модель не отвечает вообще. Не ошибку, не отказ — тишину. Так вела себя плата. Приложение обязано уметь ждать впустую.
- Команды «закрыть» нет. «Закрыто» появляется только оттого, что дверцу закрыли, или по таймеру автозакрытия — и никогда от команды по линии.
- Открытие подтверждается эхом, и это «команда принята», а не «дверь открылась». Дверь отходит через импульс в 357 мс, и модель честно держит эту паузу. Код, который считает подтверждение доказательством открытия, обязан на этом падать — иначе он упадёт на шкафу.
- Неподключённый канал отвечает ровно как открытая дверь. Различить их по линии нельзя, и модель такой возможности не даёт намеренно.
- Запрещённые кадры модель не исполняет, молчит в ответ и громко записывает попытку. Лучше поймать это на ноутбуке, чем на шкафу.
Часы
Модель не заводит таймеров: время приходит снаружи, а переходы «импульс дотолкнул дверь» и «человек закрыл» применяются лениво, при первом обращении после наступления срока. Поэтому тесты мгновенные и детерминированные, а транспорт при желании подставляет настоящие часы.
Неисправности, которые стоит гонять
Исправная плата в тестах почти ничего не доказывает. Проверять надо то, что случается на
полудуплексной линии в уличном шкафу: мусор в приёмнике при переключении направления,
разорванный на части кадр, ответ, пришедший слишком поздно, застрявший канал, порча
отдельных байтов. Набор режимов — в faults.ts, конкретные имена смотрите там: пакет
пишется прямо сейчас.
Чего эмулятор не проверит никогда
Список короткий и важный — здесь заканчивается зона, где «зелёные тесты» что-то значат:
- реальный датчик двери — есть ли он на канале, жив ли шлейф;
- физику импульса — что катушка действительно втянула язычок;
- питание — просадки при одновременных щелчках;
- сканер — его на плате не было вообще;
- сторожевой таймер — его поведение проверяется только аптаймом на живой плате.
Правило проекта: зелёные моки ничего не доказывают. Всё, что не покрыто векторами с живого железа, — гипотеза до первой проверки на шкафу.
Ручная проверка обмена
Пока плата была жива, протокол снимали инженерным экраном киоска: скрытый вход — пять касаний правого верхнего угла за три секунды. Экран умеет открыть порт, послать открытие, чтение или произвольные байты и показать лог обмена с разбором кадров. Он остаётся на месте и пригодится при первом же подключении собранного шкафа.