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

117 lines
2.8 KiB
Go

// ©AngelaMos | 2026
// store_test.go
package store
import (
"errors"
"path/filepath"
"testing"
)
func openTemp(t *testing.T) *Store {
t.Helper()
s, err := Open(filepath.Join(t.TempDir(), "test.db"))
if err != nil {
t.Fatalf("Open: %v", err)
}
t.Cleanup(func() { _ = s.Close() })
return s
}
func TestMigrateAppliesLatest(t *testing.T) {
s := openTemp(t)
if s.Version() < 1 {
t.Fatalf("schema version = %d, want >= 1", s.Version())
}
var n int
if err := s.DB().QueryRow(
`SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='articles'`,
).Scan(&n); err != nil {
t.Fatal(err)
}
if n != 1 {
t.Error("articles table not created by migration")
}
}
func TestMigrateIsIdempotent(t *testing.T) {
path := filepath.Join(t.TempDir(), "idem.db")
s1, err := Open(path)
if err != nil {
t.Fatal(err)
}
v1 := s1.Version()
_ = s1.Close()
s2, err := Open(path)
if err != nil {
t.Fatalf("reopen: %v", err)
}
defer s2.Close()
if s2.Version() != v1 {
t.Errorf("reopen version = %d, want %d", s2.Version(), v1)
}
}
func TestSourceRoundTrip(t *testing.T) {
s := openTemp(t)
id, err := s.UpsertSource(SourceInput{
Name: "krebs", Title: "Krebs", URL: "https://krebsonsecurity.com/feed/",
Type: "rss", Weight: 1.0, Tags: []string{"news"}, Enabled: true,
})
if err != nil {
t.Fatalf("UpsertSource: %v", err)
}
row, err := s.GetSourceByName("krebs")
if err != nil {
t.Fatalf("GetSourceByName: %v", err)
}
if row.ID != id || row.Weight != 1.0 || !row.Enabled || len(row.Tags) != 1 {
t.Errorf("round trip mismatch: %+v", row)
}
id2, err := s.UpsertSource(SourceInput{
Name: "krebs", Title: "Krebs Updated", URL: "https://krebsonsecurity.com/feed/",
Type: "rss", Weight: 0.9, Tags: []string{"news"}, Enabled: true,
})
if err != nil {
t.Fatalf("re-upsert: %v", err)
}
if id2 != id {
t.Errorf("upsert should keep id %d, got %d", id, id2)
}
row2, _ := s.GetSourceByName("krebs")
if row2.Title != "Krebs Updated" || row2.Weight != 0.9 {
t.Errorf("upsert did not update fields: %+v", row2)
}
}
func TestArticleUniqueConstraint(t *testing.T) {
s := openTemp(t)
srcID, err := s.UpsertSource(SourceInput{
Name: "krebs", URL: "https://krebsonsecurity.com/feed/", Type: "rss", Enabled: true,
})
if err != nil {
t.Fatal(err)
}
a := Article{
SourceID: srcID, CanonicalURL: "https://krebsonsecurity.com/post-1",
ContentHash: "hash-1", Title: "Post 1", PublishedAt: 100, FetchedAt: 200,
}
if _, err := s.InsertArticle(a); err != nil {
t.Fatalf("first insert: %v", err)
}
if _, err := s.InsertArticle(a); !errors.Is(err, ErrDuplicate) {
t.Errorf("duplicate insert: got %v, want ErrDuplicate", err)
}
n, err := s.CountArticles()
if err != nil {
t.Fatal(err)
}
if n != 1 {
t.Errorf("article count = %d, want 1 (dup rejected)", n)
}
}