105 lines
2.9 KiB
Go
105 lines
2.9 KiB
Go
// ©AngelaMos | 2026
|
|
// export_test.go
|
|
|
|
package export
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/CarterPerez-dev/nadezhda/internal/rank"
|
|
"github.com/CarterPerez-dev/nadezhda/internal/store"
|
|
)
|
|
|
|
func f(v float64) *float64 { return &v }
|
|
|
|
func sample() []rank.Scored {
|
|
return []rank.Scored{
|
|
{
|
|
Score: 0.9412,
|
|
Cluster: store.DigestCluster{
|
|
Size: 3, FirstSeen: 1751600000, LastSeen: 1751700000,
|
|
Articles: []store.DigestArticle{
|
|
{ID: 1, SourceName: "thehackernews", Title: "Old framing", CanonicalURL: "https://thn/1", PublishedAt: 100},
|
|
{ID: 2, SourceName: "bleepingcomputer", Title: "Freshest framing", CanonicalURL: "https://bc/2", PublishedAt: 300},
|
|
{ID: 3, SourceName: "bleepingcomputer", Title: "Mid framing", CanonicalURL: "https://bc/3", PublishedAt: 200},
|
|
},
|
|
CVEs: []store.DigestCVE{
|
|
{ID: "CVE-2026-2", CVSSScore: f(7.5), IsKEV: false},
|
|
{ID: "CVE-2026-1", CVSSScore: f(9.8), EPSS: f(0.91), IsKEV: true},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Score: 0.10,
|
|
Cluster: store.DigestCluster{
|
|
Size: 1, FirstSeen: 1751000000, LastSeen: 1751000000,
|
|
Articles: []store.DigestArticle{{ID: 9, SourceName: "krebs", Title: "Quiet story", CanonicalURL: "https://k/9", PublishedAt: 50}},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestBuildHeadlineAndOutlets(t *testing.T) {
|
|
d := Build(sample(), 0)
|
|
if d.Count != 2 {
|
|
t.Fatalf("count = %d, want 2", d.Count)
|
|
}
|
|
e := d.Entries[0]
|
|
if e.Rank != 1 || e.Score != 0.9412 {
|
|
t.Errorf("rank/score = %d/%v", e.Rank, e.Score)
|
|
}
|
|
if e.Headline != "Freshest framing" {
|
|
t.Errorf("headline = %q, want the freshest member", e.Headline)
|
|
}
|
|
if e.URL != "https://bc/2" {
|
|
t.Errorf("url = %q, want the freshest member's", e.URL)
|
|
}
|
|
if len(e.Outlets) != 2 || e.Outlets[0] != "bleepingcomputer" || e.Outlets[1] != "thehackernews" {
|
|
t.Errorf("outlets = %v, want distinct + sorted", e.Outlets)
|
|
}
|
|
if e.CVEs[0].ID != "CVE-2026-1" {
|
|
t.Errorf("first cve = %q, want the KEV one first", e.CVEs[0].ID)
|
|
}
|
|
}
|
|
|
|
func TestBuildTopLimit(t *testing.T) {
|
|
d := Build(sample(), 1)
|
|
if d.Count != 1 {
|
|
t.Errorf("top=1 should yield 1 entry, got %d", d.Count)
|
|
}
|
|
}
|
|
|
|
func TestJSONValidAndStable(t *testing.T) {
|
|
out, err := JSON(sample(), 0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var back Digest
|
|
if err := json.Unmarshal([]byte(out), &back); err != nil {
|
|
t.Fatalf("output is not valid JSON: %v", err)
|
|
}
|
|
if back.Entries[0].Headline != "Freshest framing" {
|
|
t.Errorf("round-trip headline = %q", back.Entries[0].Headline)
|
|
}
|
|
if !back.Entries[0].CVEs[0].KEV {
|
|
t.Error("first cve should be the KEV one")
|
|
}
|
|
}
|
|
|
|
func TestMarkdownContainsSignals(t *testing.T) {
|
|
md := Markdown(sample(), 0)
|
|
for _, want := range []string{
|
|
"# Nadezhda Digest",
|
|
"## 1. Freshest framing",
|
|
"2 outlet(s): bleepingcomputer, thehackernews",
|
|
"CVE-2026-1 (KEV, CVSS 9.8, EPSS 0.91)",
|
|
"https://bc/2",
|
|
} {
|
|
if !strings.Contains(md, want) {
|
|
t.Errorf("markdown missing %q\n---\n%s", want, md)
|
|
}
|
|
}
|
|
}
|