робот: чистая логика остановки — белый список кнопок и доказательство нажатия

Отдельным файлом от похода в кабинет нарочно: сам поход тестом не поймать, а «что жать»
и «нажалось ли» — обычная логика, и опасные случаи живут именно в ней.

🔴 Нажатие необратимо и делается в боевом кабинете за живые деньги, а рядом там живут
кнопки удаления. Поэтому: подпись кнопки сверяется полным словом, а не «похожим»
(«Завершить кампанию досрочно» — уже другая кнопка), и в окне подтверждения жмётся
только кнопка из белого списка; незнакомое окно не трогаем вовсе — лучше отказ, чем
удалённая кампания клиента.

Доказательством остановки считается только смена слова кабинета на «Останавливается»
или «Завершена». «Наверное получилось» не считается: отчитаться успехом, не проверив,
значит сказать клиенту «остановлено» о рекламе, которая продолжает крутиться.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Дмитрий
2026-08-08 23:21:14 +03:00
parent a3c58c3d56
commit ec35cd1931
2 changed files with 72 additions and 0 deletions
+41
View File
@@ -0,0 +1,41 @@
// Решения остановки, которые можно проверить без браузера. Отдельным файлом нарочно: сам
// поход в кабинет тестом не поймать, а «что жать» и «нажалось ли» — обычная логика,
// и именно в ней живут опасные случаи.
//
// 🔴 Нажатие необратимо и делается в БОЕВОМ кабинете за живые деньги: у завершённой
// кампании кнопок запуска больше нет (замер 08.08.2026, кампания 2237821).
/**
* Подпись кнопки завершения в ряду кампании. Полное совпадение, без «похожих»:
* «Завершить кампанию досрочно» — это уже другая кнопка, и жать её вслепую нельзя.
*/
export const NADPIS_ZAVERSHIT = /^завершить$/i;
/** Белый список кнопок окна подтверждения. Рядом в кабинете живут кнопки удаления. */
const PODTVERZHDENIE = [/^завершить$/i, /^да$/i, /^подтвердить$/i, /^ок$/i];
/**
* Какую кнопку окна подтверждения жать. null — окно незнакомое, не трогаем ничего:
* лучше отчитаться отказом, чем удалить кампанию клиента.
*/
export function vybratPodtverzhdenie(nadpisi) {
if (!Array.isArray(nadpisi)) return null;
for (const obrazec of PODTVERZHDENIE) {
const nayden = nadpisi.find((t) => obrazec.test(String(t ?? '').trim()));
if (nayden !== undefined) return nayden;
}
return null;
}
/**
* Кабинет подтвердил остановку? Доказательство — только смена слова в ряду:
* «Останавливается» (идёт остановка) или «Завершена» (уже закончилась).
*
* 🔴 «Наверное получилось» не считается. Отчитаться успехом, не проверив, значит
* сказать клиенту «остановлено» о рекламе, которая продолжает крутиться за его деньги.
*/
export function ostanovkaPodtverzhdena(status) {
return status === 'stopping' || status === 'completed';
}
@@ -0,0 +1,31 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { vybratPodtverzhdenie, ostanovkaPodtverzhdena, NADPIS_ZAVERSHIT } from '../src/stop-plan.js';
// 🔴 Нажатие необратимо и делается в БОЕВОМ кабинете за живые деньги. Рядом в кабинете
// живут кнопки удаления — ничего «похожего» вслепую не жмём.
test('в окне подтверждения жмём только кнопку из белого списка', () => {
assert.equal(vybratPodtverzhdenie(['Отмена', 'Завершить']), 'Завершить');
assert.equal(vybratPodtverzhdenie(['Нет', 'Да']), 'Да');
});
test('🔴 похожей кнопки мало — незнакомое окно не трогаем', () => {
assert.equal(vybratPodtverzhdenie(['Удалить кампанию', 'Отмена']), null);
assert.equal(vybratPodtverzhdenie([]), null);
assert.equal(vybratPodtverzhdenie(null), null);
});
test('подпись кнопки завершения — полное слово, а не «похожее»', () => {
assert.match('Завершить', NADPIS_ZAVERSHIT);
assert.equal(NADPIS_ZAVERSHIT.test('Завершить кампанию досрочно'), false);
assert.equal(NADPIS_ZAVERSHIT.test('Посмотреть настройки'), false);
});
test('🔴 доказательством остановки считаются ровно два слова кабинета', () => {
assert.equal(ostanovkaPodtverzhdena('stopping'), true);
assert.equal(ostanovkaPodtverzhdena('completed'), true);
// Слово не сменилось — значит не нажалось. «Наверное получилось» не считается.
assert.equal(ostanovkaPodtverzhdena('approved'), false);
assert.equal(ostanovkaPodtverzhdena(null), false);
});