Files
portal/bots/yandex-creatives/test/config.test.js
T
Дмитрий 9cc3f5e950 fix реклама за показы: пауза не врёт про остановку, цена запуска остаётся на кампании, робот не дерётся сам с собой
Хвосты денег Д1-Д6 и робота Р-х1-Р-х6 из приёмочного листа v12.

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

Д2 суточное списание берёт кампанию под замком строки. Ключ идемпотентности зависит
от числа показов, поэтому два одновременных прогона получали разные ключи и списали
бы клиента дважды.

Д3 пауза, не дошедшая до Директа, больше не считается паузой: отказ 409, заморозка
остаётся. Раньше реклама крутилась дальше, портал показывал паузу, а деньги были уже
свободны. У возобновления поведение намеренно прежнее — иначе понадобилось бы пятое
место разморозки, а их ровно четыре. Там же убрана мина строгого сравнения рубильника.

Д4 не трогали — это вопрос владельца.

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

Д6 проверка рубильника приведена к общему виду: YANDEX_DIRECT_ENABLED=0 давало строку,
которую строгое сравнение читало как включено.

Р-х1 настройки читаются из .env робота, а не каталога запуска.

Р-х2 файл-замок robot.lock: проход и поддержание входа больше не дерутся за профиль
браузера. Занят — уходим молча, задание остаётся в очереди. Брошенный замок
перехватывается через полчаса.

Р-х3 письмо-алярм честно говорит, залиты ли уже креативы в кабинет. Побочно вскрылось,
что тексты писем не проверялись ни одним тестом — транспорт вынесен в src/smtp.js.

Р-х4 тест-пустышка про рабочую папку заменён настоящим: запуск из чужого каталога без
явной папки. Проверено вырезанием.

Р-х6 пустое значение в окружении читается как значение по умолчанию, мусор даёт внятную
ошибку вместо тихого NaN.

Портал 293/293, робот 57/57. Денежных выходов снятия заморозки по-прежнему четыре.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 06:36:11 +03:00

75 lines
4.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import test from 'node:test';
import assert from 'node:assert/strict';
import { loadConfig } from '../src/config.js';
const full = {
YC_BROWSER_PROFILE_DIR: '/var/lib/liderra-robot/profile',
YC_CABINET_URL: 'https://direct.yandex.ru',
PORTAL_BASE_URL: 'https://lk.liderra.ru',
CREATIVE_ROBOT_TOKEN: 'secret',
SMTP_HOST: 'smtp.example', SMTP_PORT: '587', SMTP_USER: 'u', SMTP_PASS: 'p',
ALARM_FROM: 'bot@liderra.ru', ALARM_TO: 'ops@liderra.ru',
YC_CABINET_LOGIN: 'nash-login',
YC_ANTEROOM_CAMPAIGN_ID: '713051718',
YC_ANTEROOM_GROUP_ID: '5776749172',
};
test('загружает полный конфиг', () => {
const c = loadConfig(full);
assert.equal(c.portalBaseUrl, 'https://lk.liderra.ru');
assert.equal(c.robotToken, 'secret');
assert.equal(c.humanDelayMs, 800);
});
test('знает паузу между повторами доклада порталу', () => {
assert.equal(loadConfig(full).reportRetryDelayMs, 3000);
assert.equal(loadConfig({ ...full, REPORT_RETRY_DELAY_MS: '500' }).reportRetryDelayMs, 500);
});
// 🪤 `Number(env.X ?? '800')` ловит только undefined. Пустое значение в .env
// (`HUMAN_DELAY_MS=`) даёт пустую строку, а Number('') — это 0: робот начинал бы щёлкать
// по кабинету с машинной скоростью вместо человекоподобного темпа, и именно так выглядит
// поведение бота для антифрода Яндекса.
test('пустое значение паузы в окружении читается как значение по умолчанию, а не как ноль', () => {
assert.equal(loadConfig({ ...full, HUMAN_DELAY_MS: '' }).humanDelayMs, 800);
assert.equal(loadConfig({ ...full, REPORT_RETRY_DELAY_MS: '' }).reportRetryDelayMs, 3000);
});
// Мусор в значении молча превращался в NaN: пауза становилась «никакой», а порт почты —
// NaN, и письма переставали уходить без единого внятного слова в журнале. Пустое значение
// и опечатка — разные вещи: пустое значит «не задавал, возьми обычное», опечатка значит
// «человек хотел что-то задать и ошибся». Про второе надо сказать вслух, а не подставлять
// умолчание молча.
test('мусор в значении паузы падает с понятным сообщением', () => {
assert.throws(() => loadConfig({ ...full, HUMAN_DELAY_MS: 'быстро' }), /HUMAN_DELAY_MS/);
});
test('нечисловой порт почты падает с понятным сообщением, а не превращается в NaN', () => {
assert.throws(() => loadConfig({ ...full, SMTP_PORT: 'пятьсот' }), /SMTP_PORT/);
});
test('падает с понятным сообщением, если нет обязательной переменной', () => {
const { CREATIVE_ROBOT_TOKEN, ...without } = full;
assert.throws(() => loadConfig(without), /CREATIVE_ROBOT_TOKEN/);
});
test('срезает хвостовой слеш у адреса портала, чтобы не собирать двойные слеши', () => {
assert.equal(loadConfig({ ...full, PORTAL_BASE_URL: 'https://lk.liderra.ru/' }).portalBaseUrl, 'https://lk.liderra.ru');
});
test('срезает хвостовой слеш и у адреса кабинета', () => {
assert.equal(loadConfig({ ...full, YC_CABINET_URL: 'https://direct.yandex.ru/' }).cabinetUrl, 'https://direct.yandex.ru');
});
test('знает логин кабинета и «прихожую» — кампанию с группой, форму которой открывает робот', () => {
const c = loadConfig(full);
assert.equal(c.cabinetLogin, 'nash-login');
assert.equal(c.anteroomCampaignId, '713051718');
assert.equal(c.anteroomGroupId, '5776749172');
});
test('требует «прихожую»: без неё роботу негде открыть окно загрузки', () => {
const { YC_ANTEROOM_GROUP_ID, ...without } = full;
assert.throws(() => loadConfig(without), /YC_ANTEROOM_GROUP_ID/);
});