- Stream parquet export rows instead of unbounded memory allocation - Replace QueryGoldenSet/QueryExpansionPrompts with iter.Seq2 iterators - Remove legacy runtime.GC() calls from distill (go-mlx handles cleanup) - Replace log.Fatalf with error return in tier_score.go - Add SIGINT/SIGTERM signal handling to agent and worker daemon loops - Add error checks for unchecked db.conn.Exec in import.go and tier_score.go - Update tests for iterator-based database methods Co-Authored-By: Gemini <noreply@google.com> Co-Authored-By: Virgil <virgil@lethean.io>
292 lines
6.7 KiB
Go
292 lines
6.7 KiB
Go
package lem
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func createTestDB(t *testing.T) *DB {
|
|
t.Helper()
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "test.duckdb")
|
|
|
|
db, err := OpenDBReadWrite(path)
|
|
if err != nil {
|
|
t.Fatalf("open test db: %v", err)
|
|
}
|
|
|
|
// Create golden_set table.
|
|
_, err = db.conn.Exec(`CREATE TABLE golden_set (
|
|
idx INTEGER, seed_id VARCHAR, domain VARCHAR, voice VARCHAR,
|
|
prompt VARCHAR, response VARCHAR, gen_time DOUBLE, char_count INTEGER
|
|
)`)
|
|
if err != nil {
|
|
t.Fatalf("create golden_set: %v", err)
|
|
}
|
|
|
|
// Create expansion_prompts table.
|
|
_, err = db.conn.Exec(`CREATE TABLE expansion_prompts (
|
|
idx BIGINT, seed_id VARCHAR, region VARCHAR, domain VARCHAR,
|
|
language VARCHAR, prompt VARCHAR, prompt_en VARCHAR, priority INTEGER, status VARCHAR
|
|
)`)
|
|
if err != nil {
|
|
t.Fatalf("create expansion_prompts: %v", err)
|
|
}
|
|
|
|
return db
|
|
}
|
|
|
|
func TestOpenDBReadOnly(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "test.duckdb")
|
|
|
|
// Create a DB first so the file exists.
|
|
db, err := OpenDBReadWrite(path)
|
|
if err != nil {
|
|
t.Fatalf("create db: %v", err)
|
|
}
|
|
db.Close()
|
|
|
|
// Now open read-only.
|
|
roDB, err := OpenDB(path)
|
|
if err != nil {
|
|
t.Fatalf("open read-only: %v", err)
|
|
}
|
|
defer roDB.Close()
|
|
|
|
if roDB.path != path {
|
|
t.Errorf("path = %q, want %q", roDB.path, path)
|
|
}
|
|
}
|
|
|
|
func TestOpenDBNotFound(t *testing.T) {
|
|
_, err := OpenDB("/nonexistent/path/to.duckdb")
|
|
if err == nil {
|
|
t.Fatal("expected error for nonexistent path")
|
|
}
|
|
}
|
|
|
|
func TestQueryGoldenSet(t *testing.T) {
|
|
db := createTestDB(t)
|
|
defer db.Close()
|
|
|
|
// Insert test data.
|
|
_, err := db.conn.Exec(`INSERT INTO golden_set VALUES
|
|
(0, 'seed1', 'Identity', 'junior', 'prompt one', 'response one with enough chars to pass', 10.5, 200),
|
|
(1, 'seed2', 'Ethics', 'senior', 'prompt two', 'short', 5.0, 5),
|
|
(2, 'seed3', 'Privacy', 'peer', 'prompt three', 'another good response with sufficient length', 8.2, 300)
|
|
`)
|
|
if err != nil {
|
|
t.Fatalf("insert: %v", err)
|
|
}
|
|
|
|
// Query with minChars=50 should return 2 (skip the short one).
|
|
var rows []GoldenSetRow
|
|
for r, err := range db.GoldenSet(50) {
|
|
if err != nil {
|
|
t.Fatalf("iter error: %v", err)
|
|
}
|
|
rows = append(rows, r)
|
|
}
|
|
if len(rows) != 2 {
|
|
t.Fatalf("got %d rows, want 2", len(rows))
|
|
}
|
|
if rows[0].SeedID != "seed1" {
|
|
t.Errorf("first row seed_id = %q, want seed1", rows[0].SeedID)
|
|
}
|
|
if rows[1].Domain != "Privacy" {
|
|
t.Errorf("second row domain = %q, want Privacy", rows[1].Domain)
|
|
}
|
|
}
|
|
|
|
func TestGoldenSetEmpty(t *testing.T) {
|
|
db := createTestDB(t)
|
|
defer db.Close()
|
|
|
|
count := 0
|
|
for _, err := range db.GoldenSet(0) {
|
|
if err != nil {
|
|
t.Fatalf("iter error: %v", err)
|
|
}
|
|
count++
|
|
}
|
|
if count != 0 {
|
|
t.Fatalf("got %d rows, want 0", count)
|
|
}
|
|
}
|
|
|
|
func TestCountGoldenSet(t *testing.T) {
|
|
db := createTestDB(t)
|
|
defer db.Close()
|
|
|
|
_, err := db.conn.Exec(`INSERT INTO golden_set VALUES
|
|
(0, 'seed1', 'Identity', 'junior', 'p1', 'r1', 10.5, 200),
|
|
(1, 'seed2', 'Ethics', 'senior', 'p2', 'r2', 5.0, 150)
|
|
`)
|
|
if err != nil {
|
|
t.Fatalf("insert: %v", err)
|
|
}
|
|
|
|
count, err := db.CountGoldenSet()
|
|
if err != nil {
|
|
t.Fatalf("count: %v", err)
|
|
}
|
|
if count != 2 {
|
|
t.Errorf("count = %d, want 2", count)
|
|
}
|
|
}
|
|
|
|
func TestExpansionPrompts(t *testing.T) {
|
|
db := createTestDB(t)
|
|
defer db.Close()
|
|
|
|
_, err := db.conn.Exec(`INSERT INTO expansion_prompts VALUES
|
|
(0, 'ep1', 'chinese', 'Identity', 'zh', 'prompt zh', 'prompt en', 1, 'pending'),
|
|
(1, 'ep2', 'russian', 'Ethics', 'ru', 'prompt ru', 'prompt en2', 2, 'pending'),
|
|
(2, 'ep3', 'english', 'Privacy', 'en', 'prompt en3', '', 1, 'completed')
|
|
`)
|
|
if err != nil {
|
|
t.Fatalf("insert: %v", err)
|
|
}
|
|
|
|
// Query pending only.
|
|
var rows []ExpansionPromptRow
|
|
for r, err := range db.ExpansionPrompts("pending", 0) {
|
|
if err != nil {
|
|
t.Fatalf("iter error: %v", err)
|
|
}
|
|
rows = append(rows, r)
|
|
}
|
|
if len(rows) != 2 {
|
|
t.Fatalf("got %d rows, want 2", len(rows))
|
|
}
|
|
// Should be ordered by priority, idx.
|
|
if rows[0].SeedID != "ep1" {
|
|
t.Errorf("first row = %q, want ep1", rows[0].SeedID)
|
|
}
|
|
|
|
// Query all.
|
|
var all []ExpansionPromptRow
|
|
for r, err := range db.ExpansionPrompts("", 0) {
|
|
if err != nil {
|
|
t.Fatalf("iter error: %v", err)
|
|
}
|
|
all = append(all, r)
|
|
}
|
|
if len(all) != 3 {
|
|
t.Fatalf("got %d rows, want 3", len(all))
|
|
}
|
|
|
|
// Query with limit.
|
|
var limited []ExpansionPromptRow
|
|
for r, err := range db.ExpansionPrompts("pending", 1) {
|
|
if err != nil {
|
|
t.Fatalf("iter error: %v", err)
|
|
}
|
|
limited = append(limited, r)
|
|
}
|
|
if len(limited) != 1 {
|
|
t.Fatalf("got %d rows, want 1", len(limited))
|
|
}
|
|
}
|
|
|
|
func TestCountExpansionPrompts(t *testing.T) {
|
|
db := createTestDB(t)
|
|
defer db.Close()
|
|
|
|
_, err := db.conn.Exec(`INSERT INTO expansion_prompts VALUES
|
|
(0, 'ep1', 'chinese', 'Identity', 'zh', 'p1', 'p1en', 1, 'pending'),
|
|
(1, 'ep2', 'russian', 'Ethics', 'ru', 'p2', 'p2en', 2, 'completed'),
|
|
(2, 'ep3', 'english', 'Privacy', 'en', 'p3', '', 1, 'pending')
|
|
`)
|
|
if err != nil {
|
|
t.Fatalf("insert: %v", err)
|
|
}
|
|
|
|
total, pending, err := db.CountExpansionPrompts()
|
|
if err != nil {
|
|
t.Fatalf("count: %v", err)
|
|
}
|
|
if total != 3 {
|
|
t.Errorf("total = %d, want 3", total)
|
|
}
|
|
if pending != 2 {
|
|
t.Errorf("pending = %d, want 2", pending)
|
|
}
|
|
}
|
|
|
|
func TestUpdateExpansionStatus(t *testing.T) {
|
|
db := createTestDB(t)
|
|
defer db.Close()
|
|
|
|
_, err := db.conn.Exec(`INSERT INTO expansion_prompts VALUES
|
|
(0, 'ep1', 'chinese', 'Identity', 'zh', 'p1', 'p1en', 1, 'pending')
|
|
`)
|
|
if err != nil {
|
|
t.Fatalf("insert: %v", err)
|
|
}
|
|
|
|
err = db.UpdateExpansionStatus(0, "completed")
|
|
if err != nil {
|
|
t.Fatalf("update: %v", err)
|
|
}
|
|
|
|
count := 0
|
|
for r, err := range db.ExpansionPrompts("completed", 0) {
|
|
if err != nil {
|
|
t.Fatalf("iter error: %v", err)
|
|
}
|
|
if r.Status != "completed" {
|
|
t.Errorf("status = %q, want completed", r.Status)
|
|
}
|
|
count++
|
|
}
|
|
if count != 1 {
|
|
t.Fatalf("got %d rows, want 1", count)
|
|
}
|
|
}
|
|
|
|
func TestTableCounts(t *testing.T) {
|
|
db := createTestDB(t)
|
|
defer db.Close()
|
|
|
|
_, err := db.conn.Exec(`INSERT INTO golden_set VALUES
|
|
(0, 's1', 'd1', 'v1', 'p1', 'r1', 1.0, 100)
|
|
`)
|
|
if err != nil {
|
|
t.Fatalf("insert golden: %v", err)
|
|
}
|
|
|
|
counts, err := db.TableCounts()
|
|
if err != nil {
|
|
t.Fatalf("table counts: %v", err)
|
|
}
|
|
if counts["golden_set"] != 1 {
|
|
t.Errorf("golden_set count = %d, want 1", counts["golden_set"])
|
|
}
|
|
if counts["expansion_prompts"] != 0 {
|
|
t.Errorf("expansion_prompts count = %d, want 0", counts["expansion_prompts"])
|
|
}
|
|
}
|
|
|
|
func TestOpenDBWithEnvDefault(t *testing.T) {
|
|
// Test that OpenDB uses the default path from LEM_DB env if available.
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "env-test.duckdb")
|
|
|
|
db, err := OpenDBReadWrite(path)
|
|
if err != nil {
|
|
t.Fatalf("create: %v", err)
|
|
}
|
|
db.Close()
|
|
|
|
os.Setenv("LEM_DB", path)
|
|
defer os.Unsetenv("LEM_DB")
|
|
|
|
db2, err := OpenDB(path)
|
|
if err != nil {
|
|
t.Fatalf("open via env: %v", err)
|
|
}
|
|
defer db2.Close()
|
|
}
|