Files
sencho/backend/src/__tests__/configuration-status-normalize.test.ts
T
Anso 83b3d932e5 feat: add Apprise as a fourth notification channel (#1644)
* feat: add Apprise as a fourth notification channel

Support keyed and stateless Apprise endpoints with secret-safe public DTOs, fail-closed malformed config, and mode-specific Settings UI. Docs and screenshots updated for four-channel Channels and routing.

* fix: harden Apprise secrets at rest and preserve-on-write saves

Encrypt Apprise endpoint and config with CryptoService so a downgrade cannot leak via SELECT *. Align channel and routing saves so blank destination fields omit config on same-mode URL edits, enforce keyed notify IDs, and keep secrets_redacted truthful.

* fix: harden Apprise route type changes and mixed-version config UI

Require a raw channel_url when switching notification-route types so ciphertext cannot strand under Discord/Slack/webhook. Default missing remote apprise status, replace Channels state on node switch, and exercise the production config-column migrator.

* fix: tolerate stub fleet configuration payloads without agents

Normalize remote Apprise agent status only when notifications.agents is present so successful Pilot/stub fetches stay online instead of throwing into the offline catch path.

* fix: correct TypeScript in configuration normalize tests

* fix: ignore stale Channels agent bodies after node switch

Compare the active node after response JSON parsing so a slow body
cannot overwrite the newly selected node's channel state.

* fix: isolate corrupt Apprise crypto and keep keyed Tags visible

Decrypt failures on one Apprise row no longer 500 agent/route lists or
suppress sibling channel dispatch. Treat public /notify/<redacted> as keyed
so Tags remain editable after reload.
2026-07-18 16:32:58 -04:00

51 lines
1.9 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { normalizeConfigurationAgents, normalizeRemoteConfigurationStatus } from '../helpers/configurationStatus';
describe('normalizeConfigurationAgents', () => {
it('defaults missing apprise to disabled/unconfigured', () => {
const normalized = normalizeConfigurationAgents({
discord: { configured: true, enabled: true },
slack: { configured: false, enabled: false },
webhook: { configured: true, enabled: false },
});
expect(normalized.apprise).toEqual({ configured: false, enabled: false });
expect(normalized.discord.enabled).toBe(true);
});
it('preserves an explicit apprise slot', () => {
const normalized = normalizeConfigurationAgents({
discord: { configured: false, enabled: false },
slack: { configured: false, enabled: false },
webhook: { configured: false, enabled: false },
apprise: { configured: true, enabled: true },
});
expect(normalized.apprise).toEqual({ configured: true, enabled: true });
});
});
describe('normalizeRemoteConfigurationStatus', () => {
it('passes through stub payloads without notifications.agents', () => {
const stub = { ssoConfigured: false, alertsConfigured: true };
expect(normalizeRemoteConfigurationStatus(stub)).toEqual(stub);
});
it('fills missing apprise when an agents block is present', () => {
const raw = {
notifications: {
agents: {
discord: { configured: true, enabled: true },
slack: { configured: false, enabled: false },
webhook: { configured: false, enabled: false },
},
},
};
const normalized = normalizeRemoteConfigurationStatus(raw);
expect(normalized.notifications.agents).toEqual({
discord: { configured: true, enabled: true },
slack: { configured: false, enabled: false },
webhook: { configured: false, enabled: false },
apprise: { configured: false, enabled: false },
});
});
});