Files
felhom.eu/hub/internal/notify/r79_health_mail_test.go
T
admin 135a8b02aa hub: household health mail points to the dashboard, no raw note (R-79, D3)
The customer mail of health_degraded / health_critical / health_recovered no longer
appends the raw details JSON (frozen wire text, English or Hungarian whatever the
household's language). It says where to read the details: the dashboard. The
operator mail keeps the note; every other event keeps its note.

Red tests: r79_health_mail_test.go (2 failed on the old templates.go). Goldens
regenerated for the six health mails.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-08 15:17:05 +02:00

67 lines
3.0 KiB
Go

package notify
import (
"strings"
"testing"
)
// R-79 (D3, operator ruling 2026-10-08, `09` §3 decision 187): the HOUSEHOLD's system-health mail
// no longer carries the raw details JSON — a curly-bracket note with English lines in it that the
// household could not read. It says in plain words that something needs attention and points to the
// dashboard, where each issue is shown in the household's language. The OPERATOR's mail keeps the
// note, and every other event type keeps its note too.
const r79HealthDetails = `{"previous_status":"ok","current_status":"fail","issues":["Protected container not running: cloudflared"]}`
func TestR79_CustomerHealthMailHasNoRawNote(t *testing.T) {
for _, et := range []string{"health_critical", "health_degraded", "health_recovered"} {
for _, tc := range []struct{ lang, pointer string }{
{"hu", "vezerl"}, // ASCII fragment of „vezérlőpult" (accented greps lie)
{"en", "dashboard"},
} {
_, body := FormatCustomerEmail(tc.lang, "c1", et, "error", "msg", "", r79HealthDetails)
if strings.Contains(body, "{") || strings.Contains(body, "issues") ||
strings.Contains(body, "Protected container") {
t.Errorf("%s/%s: customer mail still carries the raw note:\n%s", et, tc.lang, body)
}
if !strings.Contains(asciiFold(body), tc.pointer) {
t.Errorf("%s/%s: customer mail lacks the dashboard pointer %q:\n%s", et, tc.lang, tc.pointer, body)
}
}
}
}
// Negative control: a health event with no details still gets the pointer (it is the event that
// carries it, not the note), and the pointer never appears on a non-health event.
func TestR79_PointerOnlyOnHealthEvents(t *testing.T) {
_, body := FormatCustomerEmail("en", "c1", "health_critical", "error", "msg", "", "")
if !strings.Contains(body, "dashboard") {
t.Errorf("health mail without details lacks the pointer:\n%s", body)
}
_, other := FormatCustomerEmail("en", "c1", "backup_failed", "error", "msg", "", `{"stack_name":"x"}`)
if strings.Contains(other, "on your dashboard") {
t.Errorf("a non-health event got the health pointer:\n%s", other)
}
}
func TestR79_OperatorHealthMailKeepsNote(t *testing.T) {
_, body := FormatOperatorEmail("c1", "health_critical", "error", "msg", r79HealthDetails)
if !strings.Contains(body, "Protected container not running: cloudflared") {
t.Errorf("operator mail lost the details:\n%s", body)
}
}
func TestR79_NonHealthCustomerMailKeepsNote(t *testing.T) {
_, body := FormatCustomerEmail("hu", "c1", "backup_failed", "error", "msg", "", `{"reason":"disk full"}`)
if !strings.Contains(body, `{"reason":"disk full"}`) {
t.Errorf("non-health customer mail lost its note:\n%s", body)
}
}
// asciiFold drops the Hungarian accents so a search can use an ASCII fragment.
func asciiFold(s string) string {
r := strings.NewReplacer("á", "a", "é", "e", "í", "i", "ó", "o", "ö", "o", "ő", "o", "ú", "u", "ü", "u", "ű", "u",
"Á", "A", "É", "E", "Í", "I", "Ó", "O", "Ö", "O", "Ő", "O", "Ú", "U", "Ü", "U", "Ű", "U")
return r.Replace(s)
}