128 lines
3.7 KiB
Go
128 lines
3.7 KiB
Go
// ©AngelaMos | 2026
|
|
// ai_notes_test.go
|
|
|
|
package store
|
|
|
|
import "testing"
|
|
|
|
func insertTestCluster(t *testing.T, s *Store, id int64, key string) {
|
|
t.Helper()
|
|
_, err := s.DB().Exec(
|
|
`INSERT INTO clusters (id, cluster_key, first_seen, last_seen, size) VALUES (?, ?, 0, 0, 1)`,
|
|
id, key,
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("insert cluster: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestAINoteRoundTrip(t *testing.T) {
|
|
s := openTemp(t)
|
|
insertTestCluster(t, s, 1, "k1")
|
|
|
|
note := AINote{
|
|
ClusterID: 1, Provider: "qwen",
|
|
Summary: "s", Why: "w", AnglesJSON: `["a","b"]`, Format: "blog", CreatedAt: 100,
|
|
}
|
|
if err := s.InsertAINote(note); err != nil {
|
|
t.Fatalf("InsertAINote: %v", err)
|
|
}
|
|
|
|
ok, err := s.AINoteExists(1, "qwen")
|
|
if err != nil || !ok {
|
|
t.Fatalf("AINoteExists(1,qwen) = %v, %v; want true", ok, err)
|
|
}
|
|
if ok, _ := s.AINoteExists(1, "openai"); ok {
|
|
t.Error("AINoteExists(1,openai) = true, want false")
|
|
}
|
|
if ok, _ := s.AINoteExists(2, "qwen"); ok {
|
|
t.Error("AINoteExists(2,qwen) = true, want false")
|
|
}
|
|
|
|
notes, err := s.AINotesForCluster(1)
|
|
if err != nil || len(notes) != 1 {
|
|
t.Fatalf("AINotesForCluster = %v, %v; want 1 note", notes, err)
|
|
}
|
|
if notes[0].Summary != "s" || notes[0].Format != "blog" || notes[0].AnglesJSON != `["a","b"]` {
|
|
t.Errorf("note = %+v", notes[0])
|
|
}
|
|
}
|
|
|
|
func TestAINoteUpsertOverwrites(t *testing.T) {
|
|
s := openTemp(t)
|
|
insertTestCluster(t, s, 1, "k1")
|
|
|
|
if err := s.InsertAINote(AINote{ClusterID: 1, Provider: "qwen", Summary: "first", AnglesJSON: "[]", Format: "blog", CreatedAt: 1}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := s.InsertAINote(AINote{ClusterID: 1, Provider: "qwen", Summary: "second", AnglesJSON: "[]", Format: "video", CreatedAt: 2}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
notes, err := s.AINotesForCluster(1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(notes) != 1 {
|
|
t.Fatalf("got %d notes, want 1 (upsert should overwrite)", len(notes))
|
|
}
|
|
if notes[0].Summary != "second" || notes[0].Format != "video" || notes[0].CreatedAt != 2 {
|
|
t.Errorf("upsert did not overwrite: %+v", notes[0])
|
|
}
|
|
}
|
|
|
|
func TestLatestAINotesNewestPerCluster(t *testing.T) {
|
|
s := openTemp(t)
|
|
insertTestCluster(t, s, 1, "k1")
|
|
insertTestCluster(t, s, 2, "k2")
|
|
|
|
must := func(err error) {
|
|
t.Helper()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
must(s.InsertAINote(AINote{ClusterID: 1, Provider: "qwen", Summary: "old", AnglesJSON: "[]", Format: "blog", CreatedAt: 10}))
|
|
must(s.InsertAINote(AINote{ClusterID: 1, Provider: "anthropic", Summary: "new", AnglesJSON: "[]", Format: "video", CreatedAt: 20}))
|
|
must(s.InsertAINote(AINote{ClusterID: 2, Provider: "qwen", Summary: "two", AnglesJSON: "[]", Format: "blog", CreatedAt: 5}))
|
|
|
|
notes, err := s.LatestAINotes()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(notes) != 2 {
|
|
t.Fatalf("got %d clusters, want 2", len(notes))
|
|
}
|
|
if notes[1].Summary != "new" {
|
|
t.Errorf("cluster 1 latest = %q, want new (highest created_at)", notes[1].Summary)
|
|
}
|
|
if notes[2].Summary != "two" {
|
|
t.Errorf("cluster 2 = %q, want two", notes[2].Summary)
|
|
}
|
|
}
|
|
|
|
func TestAINoteForeignKeyRejectsOrphan(t *testing.T) {
|
|
s := openTemp(t)
|
|
err := s.InsertAINote(AINote{ClusterID: 99, Provider: "qwen", AnglesJSON: "[]", Format: "blog", CreatedAt: 1})
|
|
if err == nil {
|
|
t.Error("insert for a nonexistent cluster should be rejected by the foreign key")
|
|
}
|
|
}
|
|
|
|
func TestAINoteCascadesOnClusterDelete(t *testing.T) {
|
|
s := openTemp(t)
|
|
insertTestCluster(t, s, 1, "k1")
|
|
if err := s.InsertAINote(AINote{ClusterID: 1, Provider: "qwen", AnglesJSON: "[]", Format: "blog", CreatedAt: 1}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := s.DB().Exec(`DELETE FROM clusters WHERE id = 1`); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
notes, err := s.AINotesForCluster(1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(notes) != 0 {
|
|
t.Errorf("notes should cascade on cluster delete, %d remain", len(notes))
|
|
}
|
|
}
|