feat: model selection at spawn (POST /api/spawn model -> LVMH_MODEL -> bridge createAgentSession); ops prepare resets ops session (fresh context per prepare)
This commit is contained in:
@@ -114,3 +114,137 @@ func TestAutoTitleAppliesRename(t *testing.T) {
|
||||
}
|
||||
t.Fatal("session row missing")
|
||||
}
|
||||
|
||||
func TestRequestTitleFailures(t *testing.T) {
|
||||
t.Run("http-500", func(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}))
|
||||
t.Cleanup(srv.Close)
|
||||
t.Setenv("LVMH_AUTOTITLE_URL", srv.URL)
|
||||
if _, err := requestTitle("x"); err == nil {
|
||||
t.Fatal("500 must error")
|
||||
}
|
||||
})
|
||||
t.Run("bad-json", func(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_, _ = w.Write([]byte("not json"))
|
||||
}))
|
||||
t.Cleanup(srv.Close)
|
||||
t.Setenv("LVMH_AUTOTITLE_URL", srv.URL)
|
||||
if _, err := requestTitle("x"); err == nil {
|
||||
t.Fatal("bad json must error")
|
||||
}
|
||||
})
|
||||
t.Run("empty-choices", func(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_, _ = w.Write([]byte(`{"choices":[]}`))
|
||||
}))
|
||||
t.Cleanup(srv.Close)
|
||||
t.Setenv("LVMH_AUTOTITLE_URL", srv.URL)
|
||||
if _, err := requestTitle("x"); err == nil {
|
||||
t.Fatal("empty choices must error")
|
||||
}
|
||||
})
|
||||
t.Run("whitespace-only-title", func(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_, _ = w.Write([]byte(`{"choices":[{"message":{"content":" *** "}}]}`))
|
||||
}))
|
||||
t.Cleanup(srv.Close)
|
||||
t.Setenv("LVMH_AUTOTITLE_URL", srv.URL)
|
||||
if _, err := requestTitle("x"); err == nil {
|
||||
t.Fatal("whitespace-only title must error")
|
||||
}
|
||||
})
|
||||
t.Run("long-title-clamped", func(t *testing.T) {
|
||||
long := make([]byte, 200)
|
||||
for i := range long {
|
||||
long[i] = 'a'
|
||||
}
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_, _ = w.Write([]byte(`{"choices":[{"message":{"content":"` + string(long) + `"}}]}`))
|
||||
}))
|
||||
t.Cleanup(srv.Close)
|
||||
t.Setenv("LVMH_AUTOTITLE_URL", srv.URL)
|
||||
got, err := requestTitle("x")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(got) != 60 {
|
||||
t.Fatalf("clamp = %d", len(got))
|
||||
}
|
||||
})
|
||||
t.Run("multiline-title-first-line", func(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_, _ = w.Write([]byte(`{"choices":[{"message":{"content":"First line\nsecond"}}]}`))
|
||||
}))
|
||||
t.Cleanup(srv.Close)
|
||||
t.Setenv("LVMH_AUTOTITLE_URL", srv.URL)
|
||||
got, err := requestTitle("x")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got != "First line" {
|
||||
t.Fatalf("got %q", got)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestAutoTitleRequestShape(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
Model string `json:"model"`
|
||||
MaxTok int `json:"max_tokens"`
|
||||
Thinking map[string]any `json:"thinking"`
|
||||
Messages []struct {
|
||||
Content string `json:"content"`
|
||||
} `json:"messages"`
|
||||
}
|
||||
_ = json.NewDecoder(r.Body).Decode(&req)
|
||||
if req.Model == "" || req.MaxTok == 0 || req.Thinking == nil {
|
||||
t.Errorf("request shape wrong: %+v", req)
|
||||
}
|
||||
if len(req.Messages) != 1 || !contains(req.Messages[0].Content, autoTitlePromptTail[:20]) {
|
||||
t.Errorf("messages wrong: %+v", req.Messages)
|
||||
}
|
||||
_, _ = w.Write([]byte(`{"choices":[{"message":{"content":"ok title"}}]}`))
|
||||
}))
|
||||
t.Cleanup(srv.Close)
|
||||
t.Setenv("LVMH_AUTOTITLE_URL", srv.URL)
|
||||
if _, err := requestTitle("the thing"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func contains(s, sub string) bool {
|
||||
return len(s) >= len(sub) && (s == sub || len(sub) == 0 || indexOf(s, sub) >= 0)
|
||||
}
|
||||
|
||||
func indexOf(s, sub string) int {
|
||||
for i := 0; i+len(sub) <= len(s); i++ {
|
||||
if s[i:i+len(sub)] == sub {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func TestAutoTitleClosedStoreFails(t *testing.T) {
|
||||
_, store, hub := newTestServerHub(t)
|
||||
if err := hub.store.UpsertSession(SessionInfo{ID: "s-x"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = store.Close()
|
||||
t.Setenv("LVMH_AUTOTITLE_URL", dummyTitleServer(t))
|
||||
// must not panic; logs the failure
|
||||
hub.autoTitle("s-x", "text")
|
||||
}
|
||||
|
||||
func TestAutoTitleNoKey(t *testing.T) {
|
||||
// no ZAI key in env -> requestTitle errors before any HTTP call
|
||||
t.Setenv(envProviderAPIKey, "")
|
||||
t.Setenv("LVMH_AUTOTITLE_URL", "http://127.0.0.1:1")
|
||||
if _, err := requestTitle("x"); err == nil {
|
||||
t.Fatal("missing key must error")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user