R-1199: a box whose household never saved its notification choice pushes the default list its page shows at start, so the hub mails by it; unreleased
gates / gates (push) Successful in 1m37s
gates / gates (push) Successful in 1m37s
This commit is contained in:
@@ -105,6 +105,14 @@
|
|||||||
line; the controller re-creates its definition while sharing is on. Any other refused share is still named.
|
line; the controller re-creates its definition while sharing is on. Any other refused share is still named.
|
||||||
`TestW28_TheSystemDropZoneIsNotNamedAsRefused`.
|
`TestW28_TheSystemDropZoneIsNotNamedAsRefused`.
|
||||||
|
|
||||||
|
- **R-1199 — a box whose household never saved its notification choice now tells the hub the list its page shows.**
|
||||||
|
Measured the night of 2026-10-21 (felhom.eu `audits/night-2026-10-21/P5-checks/`): the box's page showed the
|
||||||
|
default list ticked (disk warning, drive disconnected, second-drive failure …) but pushed nothing until the household
|
||||||
|
saved the page, and the hub mails the household only by its own stored list — for Tester4 a list saved 2026-10-18,
|
||||||
|
two days before the rehearsal box existed, without those three; the household got one alarm of nine. Now the
|
||||||
|
start-up sync also runs when no choice was ever saved, with the default list and no address (the hub's no-clobber
|
||||||
|
guard keeps the stored address). A saved choice is unchanged (R-922). *Decided by CC unattended — the box's page is
|
||||||
|
the source, following `08`'s „the household decides"; operator may reverse.* `TestR1199_*` (red-proved).
|
||||||
## v0.337.0 (2026-10-21) — kept second-drive copies (D25: R-1175, R-1181, R-1179); a `.fab` import keeps the family gate (R-1191); a stale restore temp no longer blocks the retry (R-1188 c); the overcommit sentence made true (R-1192); escrow recorded before the floor (R-1176); a finished off-site abandonment closes on the hub's word (R-1182); storage labels follow the language (W26)
|
## v0.337.0 (2026-10-21) — kept second-drive copies (D25: R-1175, R-1181, R-1179); a `.fab` import keeps the family gate (R-1191); a stale restore temp no longer blocks the retry (R-1188 c); the overcommit sentence made true (R-1192); escrow recorded before the floor (R-1176); a finished off-site abandonment closes on the hub's word (R-1182); storage labels follow the language (W26)
|
||||||
|
|
||||||
**MinAgent: 0.158.1** — the floor's, carried; no agent change is needed by this release (the R-1192 swap fix is the
|
**MinAgent: 0.158.1** — the floor's, carried; no agent change is needed by this release (the R-1192 swap fix is the
|
||||||
|
|||||||
@@ -1891,7 +1891,9 @@ func main() {
|
|||||||
go func() {
|
go func() {
|
||||||
prefs := sett.GetNotificationPrefs()
|
prefs := sett.GetNotificationPrefs()
|
||||||
// R-922: a household's clear is pushed too, so a clear the hub missed reaches it at the next start.
|
// R-922: a household's clear is pushed too, so a clear the hub missed reaches it at the next start.
|
||||||
if prefs.SyncOnStartup() {
|
// R-1199: a box whose household never saved its choice pushes the default list its page shows (with no
|
||||||
|
// address — the hub keeps the stored one, its no-clobber guard).
|
||||||
|
if prefsSyncAtStartup(prefs, sett.NotificationPrefsSaved(), logger) {
|
||||||
if err := notifier.SyncPreferences(prefs.Email, prefs.EnabledEvents, prefs.CooldownHours, prefs.EmailCleared); err != nil {
|
if err := notifier.SyncPreferences(prefs.Email, prefs.EnabledEvents, prefs.CooldownHours, prefs.EmailCleared); err != nil {
|
||||||
logger.Printf("[WARN] Failed to sync notification preferences on startup: %v", err)
|
logger.Printf("[WARN] Failed to sync notification preferences on startup: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"gitea.dooplex.hu/admin/felhom-controller/internal/settings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// R-1199 (rehearsal 2026-10-20; mechanism read the night of 2026-10-21, `audits/night-2026-10-21/P5-checks/`): a fresh
|
||||||
|
// box shows the household its DEFAULT notification list ticked (disk warning, drive disconnected, second-drive failure
|
||||||
|
// …), but pushed nothing to the hub until the household saved the page — and the hub mails the household only by its
|
||||||
|
// OWN stored list (`processCustomer`), which for Tester4 was a list saved two days before that box existed. So the
|
||||||
|
// household got one alarm of nine. The box's page is the household's choice (`08` „the household decides" — the three
|
||||||
|
// groups); the hub follows it. Decided by CC unattended — operator may reverse.
|
||||||
|
//
|
||||||
|
// prefsSyncAtStartup: push at start when the household saved an address or a clear (R-922, unchanged), or when it never
|
||||||
|
// saved a choice at all (the default list, pushed with no address — the hub's no-clobber guard keeps the stored one).
|
||||||
|
// Pinned by TestR1199_*.
|
||||||
|
func prefsSyncAtStartup(prefs *settings.NotificationPrefs, saved bool, logger *log.Logger) bool {
|
||||||
|
if prefs.SyncOnStartup() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !saved && prefs != nil {
|
||||||
|
if logger != nil {
|
||||||
|
logger.Printf("[INFO] notification prefs: the household never saved its choice — pushing the default list the page shows, so the hub mails by it (R-1199)")
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gitea.dooplex.hu/admin/felhom-controller/internal/settings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// R-1199: a box whose household never saved its notification choice pushes the default list at start; a saved choice
|
||||||
|
// without an address (and no clear) is still not pushed (R-922's rule unchanged); a saved address is pushed.
|
||||||
|
// RED-PROOF: with the `!saved` branch removed the first case fails — the hub keeps mailing by a list the page never showed.
|
||||||
|
func TestR1199_ANeverSavedChoiceIsPushedAtStart(t *testing.T) {
|
||||||
|
def := &settings.NotificationPrefs{EnabledEvents: settings.DefaultEnabledEvents, CooldownHours: 6}
|
||||||
|
if !prefsSyncAtStartup(def, false, nil) {
|
||||||
|
t.Fatal("a box with no saved choice must push the default list its page shows")
|
||||||
|
}
|
||||||
|
if prefsSyncAtStartup(&settings.NotificationPrefs{EnabledEvents: []string{"backup_failed"}}, true, nil) {
|
||||||
|
t.Fatal("a saved choice with no address and no clear stays local (R-922 unchanged)")
|
||||||
|
}
|
||||||
|
if !prefsSyncAtStartup(&settings.NotificationPrefs{Email: "x@example.invalid", EnabledEvents: []string{"backup_failed"}}, true, nil) {
|
||||||
|
t.Fatal("a saved address is pushed, as before")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The default list the page shows carries the three events the rehearsal household did not get (the consequence).
|
||||||
|
func TestR1199_TheDefaultListHoldsTheAlarmsTheHouseholdMissed(t *testing.T) {
|
||||||
|
want := map[string]bool{"disk_warning": false, "storage_disconnected": false, "crossdrive_failed": false}
|
||||||
|
for _, e := range settings.DefaultEnabledEvents {
|
||||||
|
if _, ok := want[e]; ok {
|
||||||
|
want[e] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for e, ok := range want {
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("%s is not in the default list", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -676,6 +676,15 @@ func (p *NotificationPrefs) SyncOnStartup() bool {
|
|||||||
return p != nil && (p.Email != "" || p.EmailCleared)
|
return p != nil && (p.Email != "" || p.EmailCleared)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NotificationPrefsSaved reports whether the household ever saved its notification choice on this box. A box that never
|
||||||
|
// did shows DefaultEnabledEvents ticked on its page (GetNotificationPrefs) — R-1199: the hub must then mail by that
|
||||||
|
// same list, so the start-up sync pushes it (main.go, prefsSyncAtStartup).
|
||||||
|
func (s *Settings) NotificationPrefsSaved() bool {
|
||||||
|
s.mu.RLock()
|
||||||
|
defer s.mu.RUnlock()
|
||||||
|
return s.Notifications != nil
|
||||||
|
}
|
||||||
|
|
||||||
// DefaultEnabledEvents are the events enabled by default for new customers.
|
// DefaultEnabledEvents are the events enabled by default for new customers.
|
||||||
var DefaultEnabledEvents = []string{
|
var DefaultEnabledEvents = []string{
|
||||||
"backup_failed",
|
"backup_failed",
|
||||||
|
|||||||
Reference in New Issue
Block a user