// Copyright (C) 2026 Dcntrlized // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published // by the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . package alertstate import ( "testing" "time" ) func TestValidStatusAndKind(t *testing.T) { for _, s := range []string{StatusArmed, StatusTriggered, StatusDisarmed} { if !ValidStatus(s) { t.Errorf("ValidStatus(%q) = false", s) } } for _, s := range []string{"", "ARMED", "firing", "trigger"} { if ValidStatus(s) { t.Errorf("ValidStatus(%q) = true, want false", s) } } for _, k := range []string{KindTrigger, KindDisarm} { if !ValidKind(k) { t.Errorf("ValidKind(%q) = false", k) } } for _, k := range []string{"", "TRIGGER", "alarm", "stop"} { if ValidKind(k) { t.Errorf("ValidKind(%q) = true, want false", k) } } } func TestIsActive(t *testing.T) { now := time.Date(2026, 10, 18, 12, 0, 0, 0, time.UTC) earlier := now.Add(-10 * time.Minute) cases := []struct { name string status string at *time.Time auto int want bool }{ {"armed is never active", StatusArmed, &earlier, 0, false}, {"disarmed is never active", StatusDisarmed, &earlier, 0, false}, {"triggered without auto-stop stays active", StatusTriggered, &earlier, 0, true}, {"triggered with auto-stop still inside the window", StatusTriggered, &earlier, 3600, true}, {"triggered with auto-stop past the window", StatusTriggered, &earlier, 60, false}, {"triggered with no timestamp", StatusTriggered, nil, 0, false}, {"unknown status", "firing", &earlier, 0, false}, } for _, tc := range cases { if got := IsActive(tc.status, tc.at, tc.auto, now); got != tc.want { t.Errorf("%s: IsActive = %v, want %v", tc.name, got, tc.want) } } } func TestRemainingAutoStopSeconds(t *testing.T) { now := time.Date(2026, 10, 18, 12, 0, 0, 0, time.UTC) at := now.Add(-10 * time.Minute) if got := RemainingAutoStopSeconds(StatusTriggered, &at, 3600, now); got != 3000 { t.Errorf("remaining = %d, want 3000", got) } if got := RemainingAutoStopSeconds(StatusTriggered, &at, 60, now); got != 0 { t.Errorf("expired remaining = %d, want 0", got) } if got := RemainingAutoStopSeconds(StatusArmed, &at, 3600, now); got != 0 { t.Errorf("non-triggered remaining = %d, want 0", got) } if got := RemainingAutoStopSeconds(StatusTriggered, &at, 0, now); got != 0 { t.Errorf("auto-stop 0 remaining = %d, want 0", got) } } func TestCanDisarmAndDelete(t *testing.T) { if !CanDisarm(StatusTriggered) { t.Errorf("CanDisarm(triggered) = false") } for _, s := range []string{StatusArmed, StatusDisarmed, ""} { if CanDisarm(s) { t.Errorf("CanDisarm(%q) = true, want false", s) } } if CanDelete(true) { t.Errorf("CanDelete(active) = true, want false") } if !CanDelete(false) { t.Errorf("CanDelete(inactive) = false, want true") } }