Тесты и отладка¶
Инструменты встроены в rospanel:
rospanel plugin test . # запустить test.js
rospanel plugin dev . # интерактивная консоль с перезагрузкой при сохранении
rospanel plugin validate . # проверить пакет так, как его проверит панель
Плагин в тестах и в dev работает в той же песочнице, что и в панели: те же лимиты памяти
и базы, та же база SQLite с вашими миграциями. Своя база создаётся заново при каждом запуске
test/dev.
dev.config.json¶
Настройки, с которыми плагин работает в test и dev, — то, что оператор впишет в форму:
{"settings": {"token": "test-token", "mode": "test", "limit": "100"}}
Значения — строки, как в panel.config. Если обязательное поле не заполнено, плагин не
стартует: fill in the settings first: token.
test.js¶
test.js выполняется в отдельной песочнице рядом с плагином. Плагин вызывается через
plugin.*, его запросы наружу перехватываются mock.*.
test("название теста", () => {
mock.http("https://api.example.com/", { status: 200, body: { ok: true } });
const r = plugin.call("onAction", { key: "sync", user_ids: [] });
assert.equal(r.ok, true);
});
Тесты выполняются по порядку объявления. Моки сбрасываются перед каждым тестом, а база и
kv плагина — нет: данные, записанные одним тестом, видит следующий.
test и assert¶
test(name, fn) |
объявить тест; исключение внутри fn — тест не прошёл |
assert.equal(actual, expected, msg?) |
глубокое сравнение через JSON; порядок ключей объектов не важен |
assert.ok(value, msg?) |
значение истинно |
assert.throws(fn, msg?) |
fn бросает исключение |
plugin — вызвать плагин¶
plugin.call(export, arg?) |
вызвать экспорт и вернуть его ответ: plugin.call("payment.create", {...}), plugin.call("userFields", 42), plugin.call("bot.onCommand", {...}) |
plugin.event(name, data) |
доставить событие в onEvent так, как это делает панель: {id, event, created_at: сейчас, data} |
plugin.cron(name) |
выполнить задачу по расписанию |
plugin.kv.get/set/delete/list |
читать и наполнять kv плагина |
plugin.db.exec/query |
читать и наполнять базу плагина |
Исключение в плагине пробрасывается в тест — assert.throws(() => plugin.event(...))
проверяет, что плагин упал.
mock — ответы на запросы плагина¶
mock.http(prefix, {status?, headers?, body?}) |
ответ на panel.http.fetch для URL, начинающихся с prefix. body строкой или объектом (станет JSON), status по умолчанию 200 |
mock.api(method, path, {status?, body?}) |
ответ на panel.api. path точный (/v1/users/7) или с * на конце (/v1/users/*) |
mock.calls() |
что плагин запросил: [{kind: "http"\|"api", method, url, body}] |
mock.reset() |
забыть моки и записанные вызовы |
- Незамоканный
fetchв тесте — исключение (no mock for GET https://… — add mock.http(…)). Незамоканныйpanel.apiотвечает501с подсказкой. Тест никогда не ходит в сеть. - Ограничения
netдействуют и для моков: запрос к хосту, которого нет в манифесте, отклоняется, как в панели. - Тело запроса из
formили blob записывается вmock.calls()как текст (первый МБ) — проверяйте поля multipart черезincludes.
crypto — подписать то, что тест отправляет плагину¶
В тестах есть crypto.hash, crypto.hmac, crypto.sign, crypto.jwt — те же функции, что
panel.crypto. Ими тест подписывает уведомление «от платёжной системы»:
test("signed webhook is accepted", () => {
const body = JSON.stringify({ id: "inv-1", status: "paid", amount: 150 });
const sign = crypto.hmac("sha256", "k3y", body);
const r = plugin.call("payment.webhook", { body, headers: { "x-signature": sign } });
assert.equal(r.status, "paid");
});
Пример¶
Плагин с задачей sync, которая читает пользователей через API и запоминает, сколько их:
test("sync counts the users", () => {
mock.api("GET", "/v1/users*", { body: { data: [{ id: 7 }, { id: 8 }] } });
plugin.cron("sync");
assert.equal(plugin.kv.get("last_sync").count, 2);
assert.equal(mock.calls()[0].url, "/v1/users?limit=1000");
});
test("kv from the previous test is still there", () => {
assert.ok(plugin.kv.get("last_sync"));
});
Ещё примеры — в test.js каждого плагина из examples/plugins.
Вывод:
✓ sync counts the users
✗ kv from the previous test is still there
expected a truthy value, got undefined
1 passed, 1 failed
Код выхода ненулевой, если хоть один тест не прошёл, — rospanel plugin test можно ставить в CI.
Что тесты проверяют, а что нет¶
Как в панели: манифест (включая права, нужные точкам), экспорты, net — и для моков,
лимит памяти, квота базы и запрещённые команды, лимиты kv и blob.
В тестах нет — это проверяйте на настоящей панели:
- Права
panel.api. Мок отвечает на любой запрос плагина; с--panelдействуют права ключа, а не плагина. - Только чтение
panel.apiв решениях,bot.menuиtransformSubscription. - Время вызовов. Каждый
plugin.callполучает 30 с, каким бы ни был лимит экспорта (у решения — 0,3 с). - Остановка. Сбои не останавливают плагин, а упавшую точку не пропускают 30 с.
- Доставка событий.
plugin.eventвызываетonEventодин раз: без повторов, без защиты от зацикливания и безchannel.send. - Ответы
onHttpвозвращаются такими, какими их сделал плагин, без правил панели для заголовков. - Кто видит поля карточки, кнопки и виджеты в админке.
dev — консоль¶
rospanel plugin dev .
my-plugin is running. Type help for commands.
> event user.created {"id": 1, "name": "Ann"}
> call userFields 1
{"email":"ann@example.com"}
> cron sync
> kv user/
> sql SELECT * FROM orders LIMIT 5
> mock api GET /v1/users/1 {"data": {"id": 1, "name": "Ann"}}
> mock http https://api.example.com/ {"ok": true}
> logs
> reload
> quit
| Команда | |
|---|---|
event <имя> [json] |
доставить событие в onEvent |
call <экспорт> [json] |
вызвать экспорт: call payment.create {"order_id": 1, "amount_rub": 100} |
cron <имя> |
выполнить задачу |
kv [префикс] |
показать kv |
sql <запрос> |
выполнить запрос к базе плагина |
mock api <МЕТОД> <путь> <json> |
ответ для panel.api |
mock http <префикс-url> <json> |
ответ для panel.http.fetch |
logs |
лог плагина |
reload |
перезагрузить сейчас (файлы и так отслеживаются) |
help, quit |
Отличия от test:
- Сеть настоящая. Незамоканный
fetchуходит в интернет (только к хостам изnet). Так удобно проверить настоящий сервис с токеном изdev.config.json. - Перезагрузка. При сохранении любого файла плагин перезагружается как при обновлении:
база и
kvсохраняются, новые миграции применяются. Если вы поправили уже применённую миграцию илиdev.config.json, перезапуститеdev. - Настоящая панель. С
--panelи--keyвызовыpanel.apiидут в неё:
rospanel plugin dev . --panel https://vpn.example.com/<путь API> --key rp_…
Путь API и ключ — в Настройки → API панели. Возьмите ключ только с нужными правами: изменения через него настоящие.
validate — перед публикацией¶
rospanel plugin validate .
rospanel plugin validate my-plugin-1.2.0.zip
ok: my-plugin 1.2.0 — 14 KB, exports onEvent, sync, userFields
validate проверяет манифест, состав пакета и стили темы, загружает код, проверяет экспорты
и выводит все проблемы списком.
Отладка в панели¶
- Плагины → Лог — последние 1000 строк: ваши
panel.log/console, ошибки вызовов с причиной, остановки плагина. - Плагины → Код — то, что сейчас установлено.
- Журнал панели — что плагин изменил через
panel.api(акторplugin:<id>) и нажатия его кнопок. - Если плагин остановлен (
paused), в его карточке написаны причина и время, когда панель запустит его снова; нажмите Запустить снова, когда причина устранена.
Частые ошибки и их значение — в лимитах и ошибках.