Cybersecurity-Projects/PROJECTS/intermediate/security-news-scraper/cmd/nadezhda/ideate_test.go

134 lines
4.0 KiB
Go

// ©AngelaMos | 2026
// ideate_test.go
package main
import (
"bytes"
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
"github.com/spf13/cobra"
"github.com/CarterPerez-dev/nadezhda/internal/setup"
"github.com/CarterPerez-dev/nadezhda/internal/store"
)
func seedCluster(t *testing.T, st *store.Store) {
t.Helper()
stmts := []string{
`INSERT INTO sources (id, name, url, type, weight, enabled) VALUES (1, 'krebs', 'https://krebsonsecurity.com', 'rss', 1.0, 1)`,
`INSERT INTO articles (id, source_id, canonical_url, content_hash, title, published_at) VALUES (1, 1, 'https://krebsonsecurity.com/a', 'h1', 'Critical RCE exploited in the wild', 1000)`,
`INSERT INTO clusters (id, cluster_key, first_seen, last_seen, size) VALUES (1, 'k1', 900, 1000, 1)`,
`INSERT INTO cluster_members (cluster_id, article_id) VALUES (1, 1)`,
}
for _, s := range stmts {
if _, err := st.DB().Exec(s); err != nil {
t.Fatalf("seed %q: %v", s, err)
}
}
}
func TestIdeateCommandEndToEnd(t *testing.T) {
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
t.Setenv(setup.EnvProvider, "")
t.Setenv(setup.EnvQwenURL, "")
dir := t.TempDir()
dbPath := filepath.Join(dir, "test.db")
st, err := store.Open(dbPath)
if err != nil {
t.Fatalf("open store: %v", err)
}
seedCluster(t, st)
st.Close()
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
body, _ := json.Marshal(map[string]any{
"choices": []map[string]any{{"message": map[string]any{
"content": `{"summary":"a critical rce","why":"widely exploited","angles":["hook one","hook two","hook three"],"format":"newsletter"}`,
}}},
})
_, _ = w.Write(body)
}))
defer srv.Close()
cfgPath := filepath.Join(dir, "config.yaml")
cfgYAML := "ai:\n enabled: true\n provider: qwen\n qwen:\n base_url: " + srv.URL + "\n model: qwen2.5:7b\n"
if err := os.WriteFile(cfgPath, []byte(cfgYAML), 0o644); err != nil {
t.Fatal(err)
}
prevCfg, prevDB, prevTop, prevProv, prevForce, prevSince := flagConfig, flagDB, ideateTop, ideateProvider, ideateForce, ideateSince
defer func() {
flagConfig, flagDB, ideateTop, ideateProvider, ideateForce, ideateSince = prevCfg, prevDB, prevTop, prevProv, prevForce, prevSince
}()
flagConfig, flagDB, ideateTop, ideateProvider, ideateForce, ideateSince = cfgPath, dbPath, 1, "", false, ""
cmd := &cobra.Command{}
cmd.SetContext(context.Background())
var buf bytes.Buffer
cmd.SetOut(&buf)
if err := runIdeate(cmd, nil); err != nil {
t.Fatalf("runIdeate: %v\noutput:\n%s", err, buf.String())
}
out := buf.String()
for _, want := range []string{"ideated 1", "a critical rce", "hook one", "newsletter"} {
if !strings.Contains(out, want) {
t.Errorf("ideate output missing %q\n---\n%s", want, out)
}
}
st2, err := store.Open(dbPath)
if err != nil {
t.Fatal(err)
}
ok, err := st2.AINoteExists(1, "qwen")
st2.Close()
if err != nil || !ok {
t.Fatalf("note not persisted: ok=%v err=%v", ok, err)
}
buf.Reset()
if err := runIdeate(cmd, nil); err != nil {
t.Fatalf("second runIdeate: %v", err)
}
if !strings.Contains(buf.String(), "skip cluster 1") {
t.Errorf("second run should skip existing note:\n%s", buf.String())
}
buf.Reset()
ideateForce = true
if err := runIdeate(cmd, nil); err != nil {
t.Fatalf("force runIdeate: %v", err)
}
if !strings.Contains(buf.String(), "ideated 1") {
t.Errorf("--force should regenerate, not skip:\n%s", buf.String())
}
}
func TestIdeateDisabledErrors(t *testing.T) {
t.Setenv("XDG_CONFIG_HOME", t.TempDir())
t.Setenv(setup.EnvProvider, "")
t.Setenv(setup.EnvQwenURL, "")
prevCfg, prevProv := flagConfig, ideateProvider
defer func() { flagConfig, ideateProvider = prevCfg, prevProv }()
flagConfig, ideateProvider = "", ""
cmd := &cobra.Command{}
cmd.SetContext(context.Background())
cmd.SetOut(&bytes.Buffer{})
cmd.SetIn(strings.NewReader(""))
err := runIdeate(cmd, nil)
if err == nil || !strings.Contains(err.Error(), "not set up") {
t.Fatalf("expected a 'not set up' error, got %v", err)
}
}