package aerotun import ( "os" "path/filepath" "strings" "testing" "time" ) func TestHealthHistoryTransitionsAndPersistence(t *testing.T) { path := filepath.Join(t.TempDir(), "history.json") h := newHealthMonitor(path) now := time.Now().UTC() tunnel := TunnelInstance{ID: "id1", Name: "test0", Running: true, PSK: "private-psk", PrivateKey: "private-identity"} h.observe([]TunnelInstance{tunnel}, now) h.observe([]TunnelInstance{tunnel}, now.Add(time.Second)) tunnel.Connected = true h.observe([]TunnelInstance{tunnel}, now.Add(3*time.Second)) h.observe(nil, now.Add(8*time.Second)) rows := h.history("id1") if len(rows) != 3 || rows[0].Status != "removed" || rows[1].Status != "connected" || rows[1].Previous != "waiting" || rows[1].PreviousDurationSeconds != 3 { t.Fatalf("bad transitions: %+v", rows) } data, err := os.ReadFile(path) if err != nil { t.Fatal(err) } if strings.Contains(string(data), "private-") { t.Fatal("history contains secret") } info, _ := os.Stat(path) if info.Mode().Perm() != 0600 { t.Fatalf("unsafe permissions: %v", info.Mode()) } reloaded := newHealthMonitor(path) if got := reloaded.history("id1"); len(got) != 3 { t.Fatalf("history not restored: %+v", got) } } func TestHealthStatusDistinguishesDisabledErrorWaitingAndConnected(t *testing.T) { for _, tc := range []struct { tunnel TunnelInstance want string }{{TunnelInstance{Disabled: true}, "disabled"}, {TunnelInstance{LastError: "failed"}, "error"}, {TunnelInstance{}, "stopped"}, {TunnelInstance{Running: true}, "waiting"}, {TunnelInstance{Running: true, Connected: true}, "connected"}} { if got := tunnelHealthStatus(tc.tunnel); got != tc.want { t.Fatalf("got %s want %s", got, tc.want) } } } func TestDropBreakdownCountsOnlyReportedPackets(t *testing.T) { r := &TunnelRunner{} r.recordDrop(&r.dropSourcePolicy, 2) r.recordDrop(&r.dropSendFailure, 3) if r.droppedPackets != 5 || r.dropSourcePolicy != 2 || r.dropSendFailure != 3 || r.dropInvalidIncoming != 0 { t.Fatalf("incorrect breakdown: %+v", r) } }