Cybersecurity-Projects/PROJECTS/intermediate/security-news-scraper/internal/store/digest_test.go

111 lines
3.8 KiB
Go

// ©AngelaMos | 2026
// digest_test.go
package store
import "testing"
func TestDigestClustersAggregates(t *testing.T) {
s := openTemp(t)
src1, _ := s.UpsertSource(SourceInput{Name: "a", URL: "https://a/f", Type: "rss", Weight: 1.0, Enabled: true})
src2, _ := s.UpsertSource(SourceInput{Name: "b", URL: "https://b/f", Type: "rss", Weight: 0.8, Enabled: true})
a1, _ := s.InsertArticle(Article{SourceID: src1, CanonicalURL: "https://a/1", ContentHash: "c1", TitleHash: "t1", Title: "Story one", PublishedAt: 1000})
a2, _ := s.InsertArticle(Article{SourceID: src2, CanonicalURL: "https://b/1", ContentHash: "c2", TitleHash: "t2", Title: "Story two", PublishedAt: 2000})
if err := s.UpsertCVEStub("CVE-2026-1"); err != nil {
t.Fatal(err)
}
score := 9.8
if err := s.UpdateCVEEnrichment(CVE{ID: "CVE-2026-1", CVSSScore: &score, IsKEV: true, EnrichedAt: 1, EnrichStatus: EnrichStatusOK}); err != nil {
t.Fatal(err)
}
if err := s.LinkArticleCVE(a1, "CVE-2026-1"); err != nil {
t.Fatal(err)
}
if err := s.LinkArticleCVE(a2, "CVE-2026-1"); err != nil {
t.Fatal(err)
}
if err := s.ReplaceClusters([]ClusterRow{
{Key: "1", Members: []int64{a1, a2}, FirstSeen: 1000, LastSeen: 2000},
}); err != nil {
t.Fatal(err)
}
clusters, err := s.DigestClusters(0)
if err != nil {
t.Fatal(err)
}
if len(clusters) != 1 {
t.Fatalf("clusters = %d, want 1", len(clusters))
}
c := clusters[0]
if len(c.Articles) != 2 {
t.Errorf("articles = %d, want 2", len(c.Articles))
}
if len(c.CVEs) != 1 {
t.Fatalf("cves = %d, want 1 (deduped across both articles)", len(c.CVEs))
}
if !c.CVEs[0].IsKEV || c.CVEs[0].CVSSScore == nil || *c.CVEs[0].CVSSScore != 9.8 {
t.Errorf("cve signals not aggregated: %+v", c.CVEs[0])
}
}
func TestDigestClustersAttachToCorrectCluster(t *testing.T) {
s := openTemp(t)
src, _ := s.UpsertSource(SourceInput{Name: "a", URL: "https://a/f", Type: "rss", Weight: 1, Enabled: true})
a1, _ := s.InsertArticle(Article{SourceID: src, CanonicalURL: "https://a/1", ContentHash: "c1", TitleHash: "t1", Title: "one"})
a2, _ := s.InsertArticle(Article{SourceID: src, CanonicalURL: "https://a/2", ContentHash: "c2", TitleHash: "t2", Title: "two"})
for _, id := range []string{"CVE-2026-1", "CVE-2026-2"} {
if err := s.UpsertCVEStub(id); err != nil {
t.Fatal(err)
}
}
_ = s.LinkArticleCVE(a1, "CVE-2026-1")
_ = s.LinkArticleCVE(a2, "CVE-2026-2")
if err := s.ReplaceClusters([]ClusterRow{
{Key: "1", Members: []int64{a1}, FirstSeen: 1000, LastSeen: 1000},
{Key: "2", Members: []int64{a2}, FirstSeen: 2000, LastSeen: 2000},
}); err != nil {
t.Fatal(err)
}
clusters, err := s.DigestClusters(0)
if err != nil {
t.Fatal(err)
}
if len(clusters) != 2 {
t.Fatalf("clusters = %d, want 2", len(clusters))
}
byArticle := map[int64]DigestCluster{}
for _, c := range clusters {
if len(c.Articles) != 1 || len(c.CVEs) != 1 {
t.Fatalf("cluster %d: articles=%d cves=%d, want 1/1", c.ClusterID, len(c.Articles), len(c.CVEs))
}
byArticle[c.Articles[0].ID] = c
}
if byArticle[a1].CVEs[0].ID != "CVE-2026-1" || byArticle[a2].CVEs[0].ID != "CVE-2026-2" {
t.Error("CVEs attached to the wrong cluster")
}
}
func TestDigestClustersSinceFilter(t *testing.T) {
s := openTemp(t)
src, _ := s.UpsertSource(SourceInput{Name: "a", URL: "https://a/f", Type: "rss", Weight: 1, Enabled: true})
a1, _ := s.InsertArticle(Article{SourceID: src, CanonicalURL: "https://a/1", ContentHash: "c1", TitleHash: "t1", Title: "old"})
if err := s.ReplaceClusters([]ClusterRow{{Key: "1", Members: []int64{a1}, FirstSeen: 500, LastSeen: 500}}); err != nil {
t.Fatal(err)
}
if got, _ := s.DigestClusters(1000); len(got) != 0 {
t.Errorf("cluster with last_seen 500 should be excluded by since=1000, got %d", len(got))
}
if got, _ := s.DigestClusters(0); len(got) != 1 {
t.Errorf("since=0 should include it, got %d", len(got))
}
}