From a2c52ebf2adf61d8d1300c454d819c31545e2617 Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Fri, 18 Sep 2026 16:25:20 +0200 Subject: [PATCH] hub v0.118.1: the test mail follows the language too (R-558) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Found during v0.118.0's own live proof, which is the only reason it was found: I went to press "send test notification" for the English demo box and read sendTestEmail first. It had its own hardcoded Hungarian subject and body and never went through FormatCustomerEmail, so it was the one customer mail v0.118.0 did not localise — and it is the only customer mail an operator can trigger on demand, which makes it the one most likely to be used to check whether the localisation works. Pressing the button for an English household would have answered that question wrongly, and convincingly. The two sentences are extracted byte-for-byte into the bundle, so the Hungarian test mail is unchanged. Red-proofed against the hardcoded version. The general form worth keeping: the surface you would use to CHECK a feature is the one most worth checking first. A broken instrument that reports success is worse than a broken feature. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS --- hub/CHANGELOG.md | 18 ++++++++++++ hub/internal/i18n/locales/en.json | 4 ++- hub/internal/i18n/locales/hu.json | 4 ++- hub/internal/notify/dispatcher.go | 15 ++++++++-- hub/internal/notify/language_test.go | 44 ++++++++++++++++++++++++++++ manifests/hub.yaml | 2 +- 6 files changed, 82 insertions(+), 5 deletions(-) diff --git a/hub/CHANGELOG.md b/hub/CHANGELOG.md index ab2be7d0..5b2132ae 100644 --- a/hub/CHANGELOG.md +++ b/hub/CHANGELOG.md @@ -1,3 +1,21 @@ +## v0.118.1 — the test mail follows the language too (2026-09-18, R-558) + +Found during v0.118.0's own live proof, which is the only reason it was found: I went to press "send +test notification" for the English demo box and read `sendTestEmail` first. + +- **`sendTestEmail` had its own hardcoded Hungarian subject and body** and never went through + `FormatCustomerEmail`, so it was the one customer mail v0.118.0 did not localise. It is also **the + only customer mail an operator can trigger on demand** — which makes it the one most likely to be + used to check whether the localisation works. Left alone, pressing the button for an English + household would have answered that question wrongly, and convincingly. +- Its two sentences are now `mail.test.subject` / `mail.test.body` in the bundle, extracted + byte-for-byte from the literals so the Hungarian test mail is unchanged. +- `TestTestMailFollowsTheHouseholdLanguage`, red-proofed against the hardcoded version ("en test mail + subject = \"[Felhom] Teszt értesítés\", want it to contain \"Test notification\""). + +**The general form worth keeping: the surface you would use to CHECK a feature is the one most worth +checking first.** A broken instrument that reports success is worse than a broken feature. + ## v0.118.0 — the household's e-mails follow the household's language (2026-09-18, R-558 / R-555) **Localisation slice 3, Part A.** The hub has written every customer e-mail in Hungarian whatever the diff --git a/hub/internal/i18n/locales/en.json b/hub/internal/i18n/locales/en.json index 63c4c052..321d2a36 100644 --- a/hub/internal/i18n/locales/en.json +++ b/hub/internal/i18n/locales/en.json @@ -77,5 +77,7 @@ "bind.locked.body": "There were too many failed attempts. For safety the link has locked — contact support to link the box.", "bind.invalid.lead": "This link is invalid or has expired.", "bind.invalid.body": "The link is valid for 7 days. If it has expired, ask support for a new one, or your operator can do the linking.", - "bind.htmllang": "en" + "bind.htmllang": "en", + "mail.test.subject": "[Felhom] Test notification", + "mail.test.body": "Dear Customer,\n\nThis is a test notification from the Felhom monitoring system.\nNotifications are working correctly.\n\nBest regards,\nFelhom.eu monitoring" } diff --git a/hub/internal/i18n/locales/hu.json b/hub/internal/i18n/locales/hu.json index 60cc4d4d..3c36ed79 100644 --- a/hub/internal/i18n/locales/hu.json +++ b/hub/internal/i18n/locales/hu.json @@ -77,5 +77,7 @@ "bind.locked.body": "Túl sok sikertelen próbálkozás történt. Biztonsági okból a hivatkozás zárolódott — kérjük, vedd fel a kapcsolatot az ügyfélszolgálattal a doboz összekötéséhez.", "bind.invalid.lead": "Ez a hivatkozás érvénytelen vagy lejárt.", "bind.invalid.body": "A hivatkozás 7 napig érvényes. Ha lejárt, kérj újat az ügyfélszolgálattól, vagy az összekötést az üzemeltető is elvégezheti.", - "bind.htmllang": "hu" + "bind.htmllang": "hu", + "mail.test.subject": "[Felhom] Teszt értesítés", + "mail.test.body": "Kedves Ügyfél!\n\nEz egy teszt értesítés a Felhom monitoring rendszerből.\nAz értesítések megfelelően működnek.\n\nÜdvözlettel,\nFelhom.eu monitoring" } diff --git a/hub/internal/notify/dispatcher.go b/hub/internal/notify/dispatcher.go index 9f4e490f..8f2798c4 100644 --- a/hub/internal/notify/dispatcher.go +++ b/hub/internal/notify/dispatcher.go @@ -12,6 +12,8 @@ import ( "time" "gitea.dooplex.hu/admin/felhom-hub/internal/store" + + "gitea.dooplex.hu/admin/felhom-hub/internal/i18n" ) // Dispatcher routes events to operator and/or customer email channels. @@ -199,8 +201,17 @@ func (d *Dispatcher) sendTestEmail(customerID string) { if err != nil || prefs == nil || prefs.Email == "" { d.logger.Printf("[WARN] Test email: no email configured for %s", customerID) } else { - subject := "[Felhom] Teszt értesítés" - body := "Kedves Ügyfél!\n\nEz egy teszt értesítés a Felhom monitoring rendszerből.\nAz értesítések megfelelően működnek.\n\nÜdvözlettel,\nFelhom.eu monitoring" + // The test mail follows the household's language like every other customer mail (R-558). + // + // It was the ONE that would not have: it is the only customer mail with its own hardcoded + // text, because it never goes through FormatCustomerEmail — and it is also the only one an + // operator can trigger on demand, so it is the one most likely to be used to CHECK whether + // the localisation works. Left alone, pressing "send test" for an English household would + // have answered that question wrongly. + b := i18n.Shared() + lang := d.store.CustomerLanguage(customerID) + subject := b.Msg(lang, "mail.test.subject") + body := b.Msg(lang, "mail.test.body") if err := d.sendEmailFn(prefs.Email, subject, body, nil); err != nil { d.logger.Printf("[ERROR] Test email to %s failed: %v", prefs.Email, err) diff --git a/hub/internal/notify/language_test.go b/hub/internal/notify/language_test.go index 20b412a1..5ee9d6d5 100644 --- a/hub/internal/notify/language_test.go +++ b/hub/internal/notify/language_test.go @@ -1,10 +1,13 @@ package notify import ( + "io" + "log" "strings" "testing" "gitea.dooplex.hu/admin/felhom-hub/internal/i18n" + "gitea.dooplex.hu/admin/felhom-hub/internal/store" ) // Localisation slice 3 (R-558) — the behaviour the bundle exists to produce. @@ -123,3 +126,44 @@ func TestUnknownLanguageRendersHungarian(t *testing.T) { } } } + +// The TEST mail follows the household's language too. +// +// It is the one customer mail with its own text — it never goes through FormatCustomerEmail — and +// it is also the only one an operator can trigger on demand, so it is the one most likely to be +// used to CHECK whether the localisation works. It was still hardcoded Hungarian when the rest of +// this release was finished; pressing "send test" for an English household would have answered that +// question wrongly, in the most convincing possible way. +func TestTestMailFollowsTheHouseholdLanguage(t *testing.T) { + for _, tc := range []struct{ lang, want, notWant string }{ + {"en", "Test notification", "Teszt"}, + {"hu", "Teszt értesítés", "Test notification"}, + } { + st := newDispStore(t) + if err := st.SaveCustomerConfig(&store.CustomerConfig{ + CustomerID: "c1", APIKey: "k", RetrievalPassword: "p", Language: tc.lang, + }); err != nil { + t.Fatal(err) + } + if err := st.SaveNotificationPrefs("c1", "fam@example.com", []string{"test"}, 6); err != nil { + t.Fatal(err) + } + d := NewDispatcher(st, "test-key", "from@felhom.eu", "", false, log.New(io.Discard, "", 0)) + var subject, body string + d.sendEmailFn = func(_, s, b string, _ map[string]string) error { + subject, body = s, b + return nil + } + d.ProcessEvent("c1", "test", "info", "", "{}", "hub") + + if !strings.Contains(subject, tc.want) { + t.Errorf("%s test mail subject = %q, want it to contain %q", tc.lang, subject, tc.want) + } + if strings.Contains(subject, tc.notWant) || strings.Contains(body, tc.notWant) { + t.Errorf("%s test mail carries the other language (%q):\n%s\n%s", tc.lang, tc.notWant, subject, body) + } + if body == "" { + t.Fatalf("%s test mail has an empty body — the seam did not fire", tc.lang) + } + } +} diff --git a/manifests/hub.yaml b/manifests/hub.yaml index 10301cf3..978d1505 100644 --- a/manifests/hub.yaml +++ b/manifests/hub.yaml @@ -128,7 +128,7 @@ spec: spec: containers: - name: hub - image: gitea.dooplex.hu/admin/felhom-hub:0.118.0 + image: gitea.dooplex.hu/admin/felhom-hub:0.118.1 ports: - containerPort: 8080 name: http