import { describe, expect, it } from "bun:test"; import type { DeviceRow } from "../src/db.ts"; import type { Push } from "../src/notify.ts"; import { apnsPayload, notificationPrefs, wants } from "../src/push.ts"; const push = (category: Push["category"], fromSubagent = false): Push => ({ title: "Receipt OCR", body: "Finished.", category, fromSubagent, data: { hostId: "host-1", threadId: "thread-1" }, collapseId: "host-1:thread-1", }); describe("notificationPrefs", () => { it("leaves every kind the watch never set on", () => { const device = { notifications: JSON.stringify({ finished: false }) } as DeviceRow; expect(notificationPrefs(device)).toEqual({ needsMe: true, finished: false, failed: true, subagents: true }); }); }); describe("wants", () => { const prefs = { needsMe: true, finished: false, failed: true, subagents: false }; it("mutes a subagent's request apart from your own threads' requests", () => { expect(wants(prefs, push("approval"))).toBe(true); expect(wants(prefs, push("approval", true))).toBe(false); expect(wants(prefs, push("input", true))).toBe(false); }); it("follows the finished and failed switches, and always sends host problems", () => { expect(wants(prefs, push("finished"))).toBe(false); expect(wants(prefs, push("failed"))).toBe(true); expect(wants({ needsMe: false, finished: false, failed: false, subagents: false }, push("host"))).toBe(true); }); }); describe("apnsPayload", () => { it("drops the question's buttons, then cuts the body, to stay under APNs' 4 KB", () => { const wide = "界".repeat(400); const big: Push = { ...push("input"), title: "界".repeat(80), body: wide, data: { hostId: "host-1", threadId: "thread-1", question: { id: "q", options: [1, 2, 3, 4].map((n) => ({ label: `${n}${"界".repeat(39)}`, value: "界".repeat(200) })), allowCustomAnswer: true }, }, }; const payload = apnsPayload(big); expect(Buffer.byteLength(JSON.stringify(payload))).toBeLessThanOrEqual(3900); expect("question" in payload).toBe(false); expect(apnsPayload(push("finished"))).toMatchObject({ aps: { alert: { body: "Finished." } }, threadId: "thread-1" }); }); });