/** * @file smsProblemSummary.test.js * @description Что показывать над таблицей: причина недоставки, «всё наладилось», * предупреждение о низком балансе — и когда не показывать ничего. */ import { describe, it, expect } from "vitest"; import { summarizeSmsFailures } from "./smsProblemSummary"; const row = (over = {}) => ({ order_group_id: "g1", campaign_type: "first_sms", status: "send_failed", sms_code: "201", error_message: "201\n", created_at: "2026-09-10T10:06:11Z", ...over, }); const daysAgo = (days) => new Date(Date.now() - days * 86400000).toISOString(); describe("summarizeSmsFailures — причина недоставки", () => { it("объясняет нехватку средств и считает заказы с сообщениями", () => { const summary = summarizeSmsFailures([ row({ order_group_id: "g1", created_at: "2026-09-10T10:06:11Z" }), row({ order_group_id: "g2", created_at: "2026-09-13T06:03:03Z" }), row({ order_group_id: "g2", created_at: "2026-09-13T06:03:04Z" }), ], ["g1", "g2"]); expect(summary.kind).toBe("funds"); expect(summary.reason).toBe("на счёте SMS-сервиса закончились средства"); expect(summary.groups).toBe(2); expect(summary.messages).toBe(3); expect(summary.affectedInTable).toBe(2); }); it("берёт самую частую причину, когда их несколько", () => { const summary = summarizeSmsFailures([ row({ order_group_id: "g1", sms_code: "202", error_message: "202" }), row({ order_group_id: "g2" }), row({ order_group_id: "g3" }), ], []); expect(summary.reason).toBe("на счёте SMS-сервиса закончились средства"); expect(summary.topMessages).toBe(2); }); it("другая причина — отдельный вид блока", () => { const summary = summarizeSmsFailures([row({ sms_code: "202", error_message: "202" })], []); expect(summary.kind).toBe("other"); }); }); describe("summarizeSmsFailures — актуальность по балансу", () => { it("счёт пополнен: вместо красного предупреждения — «всё наладилось»", () => { const summary = summarizeSmsFailures([row({ created_at: daysAgo(1) })], [], { balance: 12717.45 }); expect(summary.kind).toBe("resolved"); expect(summary.balance).toBe(12717.45); }); it("денег на счёте нет: проблема актуальна", () => { const summary = summarizeSmsFailures([row({ created_at: daysAgo(1) })], [], { balance: 42 }); expect(summary.kind).toBe("funds"); }); it("баланс узнать не удалось — считаем проблему актуальной", () => { const summary = summarizeSmsFailures([row({ created_at: daysAgo(1) })], [], { balance: null }); expect(summary.kind).toBe("funds"); }); it("старая история не висит месяцами: неделя прошла — не показываем", () => { const summary = summarizeSmsFailures([row({ created_at: daysAgo(8) })], [], { balance: 12717.45 }); expect(summary).toBeNull(); }); it("«всё наладилось» живёт день-два и гаснет само", () => { const fresh = summarizeSmsFailures([row({ created_at: daysAgo(0.5) })], [], { balance: 12717.45 }); expect(fresh.kind).toBe("resolved"); const stale = summarizeSmsFailures([row({ created_at: daysAgo(3) })], [], { balance: 12717.45 }); expect(stale).toBeNull(); }); it("отпечаток не зависит от суммы на счёте — закрытый баннер не возвращается", () => { const a = summarizeSmsFailures([row({ created_at: daysAgo(1) })], [], { balance: 12717.45 }); const b = summarizeSmsFailures([row({ created_at: daysAgo(1) })], [], { balance: 11122.69 }); expect(a.signature).toBe(b.signature); }); it("ошибок нет, но денег мало — предупреждаем заранее", () => { const summary = summarizeSmsFailures([], [], { balance: 55 }); expect(summary.kind).toBe("low_balance"); expect(summary.balance).toBe(55); expect(summary.reason).toBeNull(); }); it("ошибок нет и денег достаточно — блок не нужен", () => { expect(summarizeSmsFailures([], [], { balance: 12717.45 })).toBeNull(); expect(summarizeSmsFailures(null, [], { balance: null })).toBeNull(); }); }); describe("отпечаток состояния для закрытия блока", () => { it("меняется, когда проблема другая — закрытый блок вернётся с новой ошибкой", () => { const first = summarizeSmsFailures([row({ created_at: daysAgo(1) })], [], { balance: 42 }); const second = summarizeSmsFailures([row({ created_at: daysAgo(2) })], [], { balance: 42 }); const third = summarizeSmsFailures([row({ created_at: daysAgo(1), sms_code: "202", error_message: "202" })], [], { balance: 42 }); expect(first.signature).not.toBe(second.signature); expect(first.signature).not.toBe(third.signature); }); it("одинаковые данные — одинаковый отпечаток (повторно не всплывает)", () => { const a = summarizeSmsFailures([row({ created_at: daysAgo(1) })], [], { balance: 42 }); const b = summarizeSmsFailures([row({ created_at: daysAgo(1) })], [], { balance: 42 }); expect(a.signature).toBe(b.signature); }); });