package storage import ( "context" "os" "path/filepath" "testing" "time" ) func TestURLLowerHost(t *testing.T) { cases := map[string]string{ "https://App.K6N.net/page?x=1#f": "app.k6n.net", "http://localhost:8080/a": "localhost", "//cdn.knatofs.se/x.js": "cdn.knatofs.se", "inline": "inline", "": "", "https://user:pw@host.tornberg.me/p": "host.tornberg.me", } for in, want := range cases { if got := URLLowerHost(in); got != want { t.Errorf("URLLowerHost(%q) = %q, want %q", in, got, want) } } } func TestCSPFileStoreRoundTrip(t *testing.T) { path := filepath.Join(t.TempDir(), "csp", "reports.jsonl") fs, err := NewCSPFileStore(path) if err != nil { t.Fatalf("open: %v", err) } now := time.Now().UTC() old := now.Add(-48 * time.Hour) reports := []CSPReport{ {ID: "1", Timestamp: now, Site: "app.k6n.net", DocumentURL: "https://app.k6n.net/", BlockedURL: "https://ads.example/x.js", EffectiveDirective: "script-src"}, {ID: "2", Timestamp: now, Site: "docs.tornberg.me", DocumentURL: "https://docs.tornberg.me/", BlockedURL: "inline", EffectiveDirective: "style-src", Sample: "color:red"}, {ID: "3", Timestamp: old, Site: "app.k6n.net", DocumentURL: "https://app.k6n.net/old", EffectiveDirective: "img-src"}, } for _, r := range reports { if err := fs.AppendCSPReport(context.Background(), r); err != nil { t.Fatalf("append: %v", err) } } // All reports, newest first. got, err := fs.CSPReports(context.Background(), CSPReportsQuery{Limit: 10}) if err != nil { t.Fatalf("list: %v", err) } if len(got) != 3 || got[0].ID != "1" { t.Errorf("list order: %+v", got) } // Filters: directive and since. if got, _ = fs.CSPReports(context.Background(), CSPReportsQuery{Directive: "script-src"}); len(got) != 1 || got[0].ID != "1" { t.Errorf("directive filter: %+v", got) } if got, _ = fs.CSPReports(context.Background(), CSPReportsQuery{Since: now.Add(-time.Hour), Limit: 10}); len(got) != 2 { t.Errorf("since filter: %+v", got) } if got, _ = fs.CSPReports(context.Background(), CSPReportsQuery{Search: "color:red"}); len(got) != 1 || got[0].ID != "2" { t.Errorf("search filter: %+v", got) } sum, err := fs.CSPSummary(context.Background(), 24) if err != nil { t.Fatalf("summary: %v", err) } if sum.Total != 2 || sum.Sites != 2 { t.Errorf("summary totals: %+v", sum) } if len(sum.ByDirective) != 2 || sum.ByDirective[0].Name != "script-src" { t.Errorf("summary directives: %+v", sum.ByDirective) } if len(sum.ByBlockedURL) != 2 || sum.ByBlockedURL[0].Name != "ads.example" { t.Errorf("summary blocked hosts: %+v", sum.ByBlockedURL) } // A fresh store over a missing file answers empty instead of failing. empty, err := NewCSPFileStore(filepath.Join(t.TempDir(), "none.jsonl")) if err != nil { t.Fatalf("open empty: %v", err) } if got, _ = empty.CSPReports(context.Background(), CSPReportsQuery{}); len(got) != 0 { t.Errorf("missing file: %+v", got) } if _, err := os.Stat(path); err != nil { t.Errorf("store file missing after appends: %v", err) } }