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