#!/usr/bin/env bash set -euo pipefail repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" service="$repo_dir/config/dot/quickshell/services/Notifs.qml" page="$repo_dir/config/dot/quickshell/modules/settings/NotificationsPage.qml" fail() { printf 'notification application rules contract: %s\n' "$1" >&2 exit 1 } [[ -f "$service" ]] || fail 'notification service is missing' [[ -f "$page" ]] || fail 'notification settings page is missing' SERVICE_PATH="$service" PAGE_PATH="$page" bun -e ' const source = await Bun.file(process.env.SERVICE_PATH).text(); const page = await Bun.file(process.env.PAGE_PATH).text(); function fail(message) { console.error(`notification application rules contract: ${message}`); process.exit(1); } function functionBody(name) { const start = source.indexOf(`function ${name}(`); if (start === -1) fail(`missing ${name}()`); const open = source.indexOf("{", start); let depth = 0; for (let index = open; index < source.length; index++) { if (source[index] === "{") depth++; if (source[index] === "}" && --depth === 0) return source.slice(open + 1, index); } fail(`${name}() is unterminated`); } const notificationAppId = Function("notification", functionBody("notificationAppId")); const normalizedAppRule = Function("rule", functionBody("normalizedAppRule")); const identityFixtures = [ { notification: { desktopEntry: "org.signal.Signal.desktop", appName: "Signal" }, expected: "org.signal.Signal.desktop" }, { notification: { desktopEntry: "", appName: "Terminal" }, expected: "Terminal" }, { notification: { desktopEntry: "", appName: "" }, expected: "Notifications" } ]; for (const fixture of identityFixtures) { const actual = notificationAppId(fixture.notification); if (actual !== fixture.expected) fail(`stable app identity expected ${fixture.expected}, got ${actual}`); } const defaultRule = normalizedAppRule({}); if (JSON.stringify(defaultRule) !== JSON.stringify({ enabled: true, showOnLockScreen: true, showContentOnLockScreen: true })) fail(`missing rule fields did not default safely: ${JSON.stringify(defaultRule)}`); const explicitRule = normalizedAppRule({ enabled: false, showOnLockScreen: false, showContentOnLockScreen: false }); if (JSON.stringify(explicitRule) !== JSON.stringify({ enabled: false, showOnLockScreen: false, showContentOnLockScreen: false })) fail(`explicit rule was not preserved: ${JSON.stringify(explicitRule)}`); for (const required of ["rememberApplication", "appRule", "setAppRule", "shouldShowOnLockScreen", "shouldShowContentOnLockScreen"]) { functionBody(required); } const arrival = source.indexOf("onNotification: notification =>"); const tracked = source.indexOf("notification.tracked = true", arrival); const muted = source.indexOf("!root.appRule(appId).enabled", arrival); if (arrival === -1 || tracked === -1 || muted === -1 || muted > tracked) fail("muted applications are not rejected before tracking/history/unread/toast work"); if (!source.includes("DesktopPreferences.get(\"notificationAppRules\")")) fail("rules are not read through DesktopPreferences"); if (!source.includes("DesktopPreferences.set(\"notificationAppRules\", next)")) fail("rules are not written through DesktopPreferences"); if (!source.includes("next[knownAppId] = root.appRule(knownAppId)")) fail("persisted rules are not normalized to the required three-field shape"); if (!source.includes("root.fallbackAppRules = next")) fail("missing-schema preference writes do not retain an in-memory fallback"); if (!source.includes("if (!root.doNotDisturb)")) fail("global DND popup override was removed"); for (const required of [ "Notifs.applications", "Notifs.appRule(app.id).enabled", "showOnLockScreen", "showContentOnLockScreen", "Notifs.setAppRule" ]) { if (!page.includes(required)) fail(`settings page is missing ${required}`); } console.log("notification application rules contract: PASS"); '