package database import ( "os" "path/filepath" "testing" "time" "go.uber.org/zap" ) func TestListConversationPlanTasksSortedAndToleratesMissingDirectory(t *testing.T) { tmp := t.TempDir() db, err := NewDB(filepath.Join(tmp, "plantask.db"), zap.NewNop()) if err != nil { t.Fatalf("NewDB: %v", err) } t.Cleanup(func() { _ = db.Close() }) base := filepath.Join(tmp, "skills", ".eino", "plantask") db.SetEinoConversationDirs(base, "", "", "") missing, err := db.ListConversationPlanTasks("missing") if err != nil || len(missing) != 0 { t.Fatalf("missing task board = %#v, err=%v", missing, err) } dir := filepath.Join(base, "conversation-1") if err := os.MkdirAll(dir, 0o755); err != nil { t.Fatalf("MkdirAll: %v", err) } files := map[string]string{ "10.json": `{"id":"10","subject":"最后检查","status":"pending"}`, "2.json": `{"id":"2","subject":"实现接口","status":"in_progress","activeForm":"正在实现接口"}`, "1.json": `{"id":"1","subject":"梳理需求","status":"completed"}`, "bad.json": `{`, } for name, content := range files { if err := os.WriteFile(filepath.Join(dir, name), []byte(content), 0o644); err != nil { t.Fatalf("WriteFile(%s): %v", name, err) } } if err := os.WriteFile(filepath.Join(dir, ".highwatermark"), []byte("10"), 0o644); err != nil { t.Fatalf("WriteFile(highwatermark): %v", err) } tasks, err := db.ListConversationPlanTasks("conversation-1") if err != nil { t.Fatalf("ListConversationPlanTasks: %v", err) } if len(tasks) != 3 { t.Fatalf("tasks = %#v, want 3", tasks) } if tasks[0].ID != "1" || tasks[1].ID != "2" || tasks[2].ID != "10" { t.Fatalf("task order = %q, %q, %q", tasks[0].ID, tasks[1].ID, tasks[2].ID) } if tasks[1].ActiveForm != "正在实现接口" { t.Fatalf("activeForm = %q", tasks[1].ActiveForm) } } func TestListConversationPlanTasksSinceHidesPreviousRunUntilTaskCreate(t *testing.T) { tmp := t.TempDir() db, err := NewDB(filepath.Join(tmp, "plantask-current-run.db"), zap.NewNop()) if err != nil { t.Fatalf("NewDB: %v", err) } t.Cleanup(func() { _ = db.Close() }) base := filepath.Join(tmp, "plantask") db.SetEinoConversationDirs(base, "", "", "") dir := filepath.Join(base, "conversation-current-run") if err := os.MkdirAll(dir, 0o755); err != nil { t.Fatalf("MkdirAll: %v", err) } oldPath := filepath.Join(dir, "1.json") if err := os.WriteFile(oldPath, []byte(`{"id":"1","subject":"上一轮任务","status":"in_progress"}`), 0o644); err != nil { t.Fatalf("WriteFile(old): %v", err) } runStartedAt := time.Now().Add(-time.Second) oldTime := runStartedAt.Add(-time.Minute) if err := os.Chtimes(oldPath, oldTime, oldTime); err != nil { t.Fatalf("Chtimes(old): %v", err) } tasks, err := db.ListConversationPlanTasksSince("conversation-current-run", runStartedAt) if err != nil { t.Fatalf("ListConversationPlanTasksSince(before TaskCreate): %v", err) } if len(tasks) != 0 { t.Fatalf("stale tasks shown before current TaskCreate: %#v", tasks) } newPath := filepath.Join(dir, "2.json") if err := os.WriteFile(newPath, []byte(`{"id":"2","subject":"本轮任务","status":"pending"}`), 0o644); err != nil { t.Fatalf("WriteFile(new): %v", err) } tasks, err = db.ListConversationPlanTasksSince("conversation-current-run", runStartedAt) if err != nil { t.Fatalf("ListConversationPlanTasksSince(after TaskCreate): %v", err) } if len(tasks) != 1 || tasks[0].ID != "2" { t.Fatalf("current tasks = %#v, want task 2 only", tasks) } }