Files
wehub-resource-sync f99010fae1
CI / lint (push) Failing after 1s
CI / frontend (push) Failing after 1s
CI / scripts (push) Failing after 1s
CI / Go Test (ubuntu-latest) (push) Failing after 0s
CI / frontend-node-25 (push) Failing after 1s
CI / docs (push) Failing after 0s
CI / coverage (push) Failing after 0s
CI / e2e (push) Failing after 0s
Docker / build-and-push (push) Failing after 1s
CI / integration (push) Failing after 4m43s
CI / Go Test (windows-latest) (push) Has been cancelled
CI / Desktop Unit Tests (Windows) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux (arm64)) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Windows) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (aarch64)) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (x86_64)) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:30:36 +08:00

367 lines
11 KiB
Go

package db
import (
"context"
"fmt"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestInsights_InsertAndGet(t *testing.T) {
d := testDB(t)
ctx := context.Background()
want := &Insight{
Type: "daily_activity",
DateFrom: "2025-01-15",
DateTo: "2025-01-15",
Project: new("my-app"),
Agent: "claude",
Model: new("claude-sonnet-4-20250514"),
Prompt: new("What happened today?"),
Content: "# Summary\nStuff happened.",
}
id, err := d.InsertInsight(*want)
require.NoError(t, err, "InsertInsight")
require.Positive(t, id, "expected positive ID")
got, err := d.GetInsight(ctx, id)
require.NoError(t, err, "GetInsight")
require.NotNil(t, got, "expected insight")
diff := cmp.Diff(want, got, cmpopts.IgnoreFields(Insight{}, "ID", "CreatedAt"))
assert.Empty(t, diff, "Insight mismatch (-want +got)")
assert.NotEmpty(t, got.CreatedAt, "expected created_at to be set")
}
func TestInsights_CannedMetadataAndCacheLookup(t *testing.T) {
d := testDB(t)
ctx := context.Background()
id, err := d.InsertInsight(Insight{
Type: "llm_canned",
DateFrom: "2025-01-15",
DateTo: "2025-01-15",
Agent: "claude",
Model: new("test-model"),
Content: "generated recommendation",
Kind: "prompt_maturity_review",
SchemaVersion: "llm_insight.v1",
TemplateID: "prompt_maturity_review",
TemplateVersion: "2026-05-26",
AggregateHash: "abc123",
CacheKey: "prompt_maturity_review:abc123",
CacheStatus: "fresh",
ProvenanceJSON: `{"template_id":"prompt_maturity_review"}`,
StructuredJSON: `{"schema_version":"llm_insight.v1"}`,
})
if err != nil {
t.Fatalf("InsertInsight: %v", err)
}
got, err := d.GetCachedInsight(ctx, "prompt_maturity_review:abc123")
if err != nil {
t.Fatalf("GetCachedInsight: %v", err)
}
if got == nil {
t.Fatal("expected cached insight")
}
if got.ID != id || got.Kind != "prompt_maturity_review" ||
got.SchemaVersion != "llm_insight.v1" ||
got.ProvenanceJSON == "" || got.StructuredJSON == "" {
t.Fatalf("cached insight metadata mismatch: %+v", got)
}
}
func TestInsights_InsertDateRange(t *testing.T) {
d := testDB(t)
ctx := context.Background()
want := &Insight{
Type: "daily_activity",
DateFrom: "2025-01-13",
DateTo: "2025-01-17",
Agent: "claude",
Content: "Weekly summary",
}
id, err := d.InsertInsight(*want)
require.NoError(t, err, "InsertInsight")
got, err := d.GetInsight(ctx, id)
require.NoError(t, err, "GetInsight")
require.NotNil(t, got, "expected insight")
diff := cmp.Diff(want, got, cmpopts.IgnoreFields(Insight{}, "ID", "CreatedAt"))
assert.Empty(t, diff, "Insight mismatch (-want +got)")
}
func TestInsights_GetNonexistent(t *testing.T) {
d := testDB(t)
ctx := context.Background()
got, err := d.GetInsight(ctx, 99999)
require.NoError(t, err, "GetInsight")
assert.Nil(t, got, "expected nil")
}
func insertInsightFixtures(t *testing.T, d *DB, entries []Insight) []int64 {
t.Helper()
d.mu.Lock()
defer d.mu.Unlock()
tx, err := d.getWriter().Begin()
require.NoError(t, err, "begin insights seed tx")
defer func() { _ = tx.Rollback() }()
stmt, err := tx.Prepare(`
INSERT INTO insights (
type, date_from, date_to, project,
agent, model, prompt, content,
kind, schema_version, template_id,
template_version, aggregate_hash, cache_key,
cache_status, provenance_json, structured_json
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
require.NoError(t, err, "prepare insights seed")
defer func() { _ = stmt.Close() }()
ids := make([]int64, 0, len(entries))
for i, s := range entries {
res, err := stmt.Exec(
s.Type, s.DateFrom, s.DateTo, s.Project,
s.Agent, s.Model, s.Prompt, s.Content,
s.Kind, s.SchemaVersion, s.TemplateID,
s.TemplateVersion, s.AggregateHash, s.CacheKey,
s.CacheStatus, s.ProvenanceJSON, s.StructuredJSON,
)
require.NoError(t, err, "insert insight fixture %d", i)
id, err := res.LastInsertId()
require.NoError(t, err, "fixture insight id %d", i)
ids = append(ids, id)
}
require.NoError(t, tx.Commit(), "commit insights seed")
return ids
}
func TestListInsights(t *testing.T) {
ctx := context.Background()
seedFiltersData := func(t *testing.T, d *DB) []int64 {
entries := []Insight{
{Type: "daily_activity", DateFrom: "2025-01-15", DateTo: "2025-01-15", Project: new("app-a"), Agent: "claude", Content: "Day 1 app-a"},
{Type: "daily_activity", DateFrom: "2025-01-15", DateTo: "2025-01-15", Project: new("app-b"), Agent: "claude", Content: "Day 1 app-b"},
{Type: "agent_analysis", DateFrom: "2025-01-15", DateTo: "2025-01-15", Agent: "claude", Content: "Analysis"},
{Type: "daily_activity", DateFrom: "2025-01-16", DateTo: "2025-01-16", Project: new("app-a"), Agent: "claude", Content: "Day 2 app-a"},
}
ids := make([]int64, 0, len(entries))
for i, insight := range entries {
id, err := d.InsertInsight(insight)
require.NoError(t, err, "InsertInsight %d", i)
ids = append(ids, id)
}
return ids
}
tests := []struct {
name string
seed func(t *testing.T, d *DB) []int64
filter InsightFilter
verify func(t *testing.T, got []Insight, ids []int64)
}{
{
name: "AllInsights",
seed: seedFiltersData,
filter: InsightFilter{},
verify: func(t *testing.T, got []Insight, _ []int64) {
wantContent := []string{"Day 2 app-a", "Analysis", "Day 1 app-b", "Day 1 app-a"}
require.Len(t, got, len(wantContent))
for i, want := range wantContent {
assert.Equal(t, want, got[i].Content, "got[%d].Content", i)
}
},
},
{
name: "ByType",
seed: seedFiltersData,
filter: InsightFilter{Type: "daily_activity"},
verify: func(t *testing.T, got []Insight, _ []int64) {
wantContent := []string{"Day 2 app-a", "Day 1 app-b", "Day 1 app-a"}
require.Len(t, got, len(wantContent))
for i, want := range wantContent {
assert.Equal(t, want, got[i].Content, "got[%d].Content", i)
}
},
},
{
name: "ByProject",
seed: seedFiltersData,
filter: InsightFilter{Project: "app-a"},
verify: func(t *testing.T, got []Insight, _ []int64) {
wantContent := []string{"Day 2 app-a", "Day 1 app-a"}
require.Len(t, got, len(wantContent))
for i, want := range wantContent {
assert.Equal(t, want, got[i].Content, "got[%d].Content", i)
}
},
},
{
name: "GlobalOnly",
seed: seedFiltersData,
filter: InsightFilter{GlobalOnly: true},
verify: func(t *testing.T, got []Insight, _ []int64) {
wantContent := []string{"Analysis"}
require.Len(t, got, len(wantContent))
for i, want := range wantContent {
assert.Equal(t, want, got[i].Content, "got[%d].Content", i)
}
},
},
{
name: "NoMatch",
seed: seedFiltersData,
filter: InsightFilter{Type: "agent_analysis", Project: "nonexistent"},
verify: func(t *testing.T, got []Insight, _ []int64) {
assert.Empty(t, got, "got insights")
},
},
{
name: "OrderByCreatedAtDesc",
seed: func(t *testing.T, d *DB) []int64 {
ids := make([]int64, 0, 3)
for _, content := range []string{"first", "second", "third"} {
id, err := d.InsertInsight(Insight{
Type: "daily_activity",
DateFrom: "2025-01-15", DateTo: "2025-01-15",
Agent: "claude", Content: content,
})
require.NoError(t, err, "InsertInsight")
ids = append(ids, id)
}
return ids
},
filter: InsightFilter{},
verify: func(t *testing.T, got []Insight, ids []int64) {
require.Len(t, got, 3)
assert.Equal(t, ids[2], got[0].ID, "first id")
assert.Equal(t, ids[0], got[2].ID, "last id")
},
},
{
name: "CappedAt500",
seed: func(t *testing.T, d *DB) []int64 {
const total = 502
entries := make([]Insight, 0, total)
for i := range total {
entries = append(entries, Insight{
Type: "daily_activity",
DateFrom: "2025-01-15",
DateTo: "2025-01-15",
Agent: "claude",
Content: fmt.Sprintf("insight %d", i),
})
}
return insertInsightFixtures(t, d, entries)
},
filter: InsightFilter{},
verify: func(t *testing.T, got []Insight, ids []int64) {
const total = 502
require.Len(t, got, 500, "capped at 500")
// Newest (id 502) should be first.
newestID := ids[total-1]
assert.Equal(t, newestID, got[0].ID, "first ID (newest)")
// Oldest retained should be id 3 (skipping 1 and 2).
oldestRetainedID := ids[total-500]
assert.Equal(t, oldestRetainedID, got[499].ID,
"last ID (oldest retained)")
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
d := testDB(t)
ids := tt.seed(t, d)
got, err := d.ListInsights(ctx, tt.filter)
require.NoError(t, err, "ListInsights")
tt.verify(t, got, ids)
})
}
}
func TestListInsights_DateFilter(t *testing.T) {
d := testDB(t)
ctx := context.Background()
_, err := d.InsertInsight(Insight{Type: "daily_activity",
DateFrom: "2026-06-16", DateTo: "2026-06-16", Agent: "claude", Content: "x"})
require.NoError(t, err)
_, err = d.InsertInsight(Insight{Type: "daily_activity",
DateFrom: "2026-06-17", DateTo: "2026-06-17", Agent: "claude", Content: "y"})
require.NoError(t, err)
got, err := d.ListInsights(ctx, InsightFilter{
Type: "daily_activity", DateFrom: "2026-06-16", DateTo: "2026-06-16"})
require.NoError(t, err)
require.Len(t, got, 1)
assert.Equal(t, "2026-06-16", got[0].DateFrom)
}
func TestListInsights_DistinguishesByDateTo(t *testing.T) {
d := testDB(t)
// A single-day insight and a week insight sharing the same date_from.
_, err := d.InsertInsight(Insight{
Type: "daily_activity", DateFrom: "2026-06-15", DateTo: "2026-06-15",
Agent: "claude", Content: "day",
})
require.NoError(t, err)
_, err = d.InsertInsight(Insight{
Type: "daily_activity", DateFrom: "2026-06-15", DateTo: "2026-06-21",
Agent: "claude", Content: "week",
})
require.NoError(t, err)
got, err := d.ListInsights(context.Background(), InsightFilter{
Type: "daily_activity", DateFrom: "2026-06-15", DateTo: "2026-06-15",
})
require.NoError(t, err)
require.Len(t, got, 1, "filtering both bounds isolates the single-day insight")
assert.Equal(t, "day", got[0].Content)
}
func TestInsightsLookupIndexHasDateTo(t *testing.T) {
d := testDB(t)
rows, err := d.Reader().Query("PRAGMA index_info(idx_insights_lookup)")
require.NoError(t, err)
defer rows.Close()
var n int
for rows.Next() {
n++
}
require.NoError(t, rows.Err())
assert.Equal(t, 4, n, "lookup index covers type, date_from, date_to, project")
}
func TestInsights_Delete(t *testing.T) {
d := testDB(t)
ctx := context.Background()
id, err := d.InsertInsight(Insight{
Type: "daily_activity",
DateFrom: "2025-01-15", DateTo: "2025-01-15",
Agent: "claude", Content: "to be deleted",
})
require.NoError(t, err, "InsertInsight")
require.NoError(t, d.DeleteInsight(id), "DeleteInsight")
got, err := d.GetInsight(ctx, id)
require.NoError(t, err, "GetInsight after delete")
assert.Nil(t, got, "expected nil after delete")
}