package store import ( "testing" "time" ) // Aufräumen erst nach einer Frist (Job: 10 Tage), CLI-purge sofort (0). func TestPurgeEnrollmentTokens_Retention(t *testing.T) { s, clock := openTest(t) mustTenant(t, s, "t1") start := clock.now() s.AddEnrollmentToken(ctx, "alt-benutzt", "t1", start.Add(time.Hour)) s.AddEnrollmentToken(ctx, "jung-benutzt", "t1", start.Add(30*24*time.Hour)) s.AddEnrollmentToken(ctx, "alt-abgelaufen", "t1", start.Add(time.Hour)) s.AddEnrollmentToken(ctx, "gueltig", "t1", start.Add(30*24*time.Hour)) s.Enroll(ctx, "alt-benutzt", sampleNode("n1", "k1")) clock.advance(9 * 24 * time.Hour) s.Enroll(ctx, "jung-benutzt", sampleNode("n2", "k2")) clock.advance(2 * 24 * time.Hour) // jetzt: alt-* vor > 10 Tagen benutzt/abgelaufen n, err := s.PurgeEnrollmentTokens(ctx, 10*24*time.Hour) if err != nil || n != 2 { t.Errorf("gelöscht %d (%v), erwartet 2 (alt-benutzt, alt-abgelaufen)", n, err) } if n, _ := s.PurgeEnrollmentTokens(ctx, 0); n != 1 { t.Errorf("sofort: %d gelöscht, erwartet 1 (jung-benutzt)", n) } } func TestPurgeLeasesAndCommands(t *testing.T) { s, clock := openTest(t) mustTenant(t, s, "t1") mustNode(t, s, "n1", "t1", "k1") mustNode(t, s, "n2", "t1", "k2") now := clock.now() s.PutLease(ctx, Lease{NodeID: "n1", IssuedAt: now, RenewAfter: now, ValidUntil: now.Add(time.Hour), Document: "{}"}) s.PutLease(ctx, Lease{NodeID: "n2", IssuedAt: now, RenewAfter: now, ValidUntil: now.Add(20 * 24 * time.Hour), Document: "{}"}) s.EnqueueNodeCommand(ctx, "n1", "resync", "") s.EnqueueNodeCommand(ctx, "n1", "drain", "") cmds, _ := s.PendingNodeCommands(ctx, "n1") s.MarkNodeCommandSent(ctx, cmds[0].ID) clock.advance(11 * 24 * time.Hour) if n, err := s.PurgeLeases(ctx, 10*24*time.Hour); err != nil || n != 1 { t.Errorf("Leases: %d %v", n, err) } if _, err := s.GetLease(ctx, "n2"); err != nil { t.Error("gültige Lease gelöscht") } if n, err := s.PurgeNodeCommands(ctx, 10*24*time.Hour); err != nil || n != 1 { t.Errorf("Befehle: %d %v", n, err) } if cmds, _ := s.PendingNodeCommands(ctx, "n1"); len(cmds) != 1 { t.Error("nicht zugestellter Befehl gelöscht") } } func TestNotificationsOnce(t *testing.T) { s, _ := openTest(t) if s.Notified(ctx, "contract:t1:2026-12-15:14") { t.Error("neu: schon benachrichtigt") } if err := s.RecordNotification(ctx, "contract:t1:2026-12-15:14"); err != nil { t.Fatal(err) } if !s.Notified(ctx, "contract:t1:2026-12-15:14") { t.Error("nicht vermerkt") } if err := s.RecordNotification(ctx, "contract:t1:2026-12-15:14"); err != nil { t.Errorf("doppelt vermerken: %v", err) } } func TestMarkNodeSeen(t *testing.T) { s, clock := openTest(t) mustTenant(t, s, "t1") mustNode(t, s, "n1", "t1", "k1") clock.advance(time.Hour) if err := s.MarkNodeSeen(ctx, "n1"); err != nil { t.Fatal(err) } if n, _ := s.GetNode(ctx, "n1"); !n.LastSeen.Equal(clock.now()) { t.Errorf("last_seen = %v", n.LastSeen) } } func TestNotificationKeysWithPrefix(t *testing.T) { s, _ := openTest(t) for _, k := range []string{"offline:n1:a", "offline:n1:b", "offline:n10:c", "contract:x"} { s.RecordNotification(ctx, k) } got, err := s.NotificationKeys(ctx, "offline:n1:") if err != nil || len(got) != 2 || got[0] != "offline:n1:a" || got[1] != "offline:n1:b" { t.Errorf("Schlüssel: %v %v", got, err) } }