Files
portal/bots/yandex-creatives/test/mailer.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

40 lines
2.1 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { createMailer } from '../src/mailer.js';
function stubTransport() {
const sent = [];
return { sent, sendMail: async (m) => { sent.push(m); } };
}
test('алярм до загрузки честно говорит, что в кабинете ничего не изменилось', async () => {
const t = stubTransport();
await createMailer(t, { from: 'bot@liderra.ru', to: 'ops@liderra.ru' })
.alarm({ step: 'проверка входа', reason: 'вход слетел', campaignId: 42, screenshotPath: null });
assert.match(t.sent[0].text, /ничего в кабинете не менял/);
assert.match(t.sent[0].text, /деньги не потрачены/);
});
/**
* Письмо было одно на все случаи и всегда утверждало «робот остановился и ничего
* в кабинете не менял». А тем же письмом сообщали, например, «окно загрузки не закрылось» —
* там файлы УЖЕ залиты и робот уже отчитался «готово». Человек читал, что ничего
* не произошло, и в кабинет не шёл. А идти надо: там лежат креативы, и повторная заливка
* оставит дубли, которые вычищаются только руками.
*/
test('алярм после загрузки не врёт, что ничего не менялось, и зовёт человека в кабинет', async () => {
const t = stubTransport();
await createMailer(t, { from: 'bot@liderra.ru', to: 'ops@liderra.ru' })
.alarm({ step: 'доклад порталу', reason: 'портал не ответил', campaignId: 42, screenshotPath: null, uploaded: true });
const text = t.sent[0].text;
assert.doesNotMatch(text, /ничего в кабинете не менял/);
assert.doesNotMatch(text, /деньги не потрачены/);
assert.match(text, /кабинет/i);
assert.match(text, /дубли/);
});