// Copyright (c) 2026 Petr BalvĂ­n (https://petrbalvin.org) // SPDX-License-Identifier: MIT //go:build linux || freebsd package storage import ( "encoding/json" "os" "path/filepath" "strings" "testing" "time" ) func TestPendingStoreRoundTrip(t *testing.T) { p := NewPendingStore(filepath.Join(t.TempDir(), "pending.json"), 72*time.Hour) if _, ok := p.Consume("unknown-token"); ok { t.Fatal("unknown token must not consume") } if err := p.Issue("token-a", PendingSubscription{Email: "Jane@Example.COM", IP: "203.0.113.9"}); err != nil { t.Fatalf("issue: %v", err) } sub, ok := p.Consume("token-a") if !ok { t.Fatal("valid token must consume") } if sub.Email != "Jane@Example.COM" || sub.IP != "203.0.113.9" { t.Errorf("consumed entry = %+v", sub) } if _, ok := p.Consume("token-a"); ok { t.Error("token must be single-use") } } // An expired entry is never returned and is purged from the file. func TestPendingStoreExpiry(t *testing.T) { path := filepath.Join(t.TempDir(), "pending.json") stale, _ := json.Marshal(pendingFile{Schema: pendingSchema, Entries: map[string]PendingSubscription{ hashToken("old"): {Email: "a@b.c", CreatedAt: time.Now().Add(-96 * time.Hour)}, }}) if err := os.WriteFile(path, stale, 0o600); err != nil { t.Fatal(err) } if _, ok := NewPendingStore(path, 72*time.Hour).Consume("old"); ok { t.Fatal("expired token must not consume") } raw, _ := os.ReadFile(path) if strings.Contains(string(raw), `"a@b.c"`) { t.Error("expired entry was not purged from the file") } } // Issue replaces the previous live token for the same address. func TestPendingStoreRotation(t *testing.T) { path := filepath.Join(t.TempDir(), "pending.json") p := NewPendingStore(path, 72*time.Hour) if err := p.Issue("first", PendingSubscription{Email: "jane@example.com"}); err != nil { t.Fatal(err) } if err := p.Issue("second", PendingSubscription{Email: "jane@example.com"}); err != nil { t.Fatal(err) } raw, _ := os.ReadFile(path) if strings.Count(string(raw), "@") != 1 { t.Errorf("expected a single live entry after rotation, got %s", raw) } if _, ok := p.Consume("first"); ok { t.Error("superseded token must no longer work") } if _, ok := p.Consume("second"); !ok { t.Error("current token must still work") } } func TestRandomTokenIsHexAndUnique(t *testing.T) { a, err := RandomToken() if err != nil { t.Fatal(err) } b, err := RandomToken() if err != nil { t.Fatal(err) } if len(a) != 64 || a == b { t.Errorf("tokens = %q %q, want unique 64-char hex", a, b) } }