Files
gochat/deploy/quickstart/captain-fixture/main_test.go
T
Rogeeandrogee d948222ac6 H-337: restore Captain inbox takeover and KB citations (#65)
* fix(captain): restore inbox takeover and KB citations

* fix(captain): harden grounded citations and smoke seed

---------

Co-authored-by: Rogee <rogee@ipao.vip>
2026-08-20 22:33:38 +08:00

101 lines
4.2 KiB
Go

package main
import (
"bytes"
"encoding/json"
"io"
"log"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestAcceptanceFixtureContract(t *testing.T) {
server := httptest.NewServer(newFixtureServer(log.New(io.Discard, "", 0)))
t.Cleanup(server.Close)
t.Run("knowledge", func(t *testing.T) {
resp, err := http.Get(server.URL + "/knowledge")
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode != http.StatusOK || !bytes.Contains(body, []byte("KBASE-LOCAL-2026")) {
t.Fatalf("knowledge status=%d marker=%t", resp.StatusCode, bytes.Contains(body, []byte("KBASE-LOCAL-2026")))
}
})
t.Run("chat retry and faq", func(t *testing.T) {
fail := `{"model":"gpt-5.6-luna","messages":[{"role":"user","content":"[fixture:fail]"}]}`
if status := post(t, server.URL+"/v1/chat/completions", fail, nil); status != http.StatusServiceUnavailable {
t.Fatalf("failure status=%d", status)
}
recovered := `{"model":"gpt-5.6-luna","messages":[{"role":"user","content":"[fixture:fail]"},{"role":"user","content":"recovered"}]}`
if status := post(t, server.URL+"/v1/chat/completions", recovered, nil); status != http.StatusOK {
t.Fatalf("recovery status=%d", status)
}
retry := `{"model":"gpt-5.6-luna","messages":[{"role":"user","content":"[fixture:retry] one"}]}`
if status := post(t, server.URL+"/v1/chat/completions", retry, nil); status != http.StatusServiceUnavailable {
t.Fatalf("first retry status=%d", status)
}
if status := post(t, server.URL+"/v1/chat/completions", retry, nil); status != http.StatusOK {
t.Fatalf("second retry status=%d", status)
}
faq := `{"model":"gpt-5.6-luna","messages":[{"role":"system","content":"Extract factual, self-contained FAQs"}]}`
var response struct {
Choices []struct {
Message chatMessage `json:"message"`
} `json:"choices"`
}
if status := post(t, server.URL+"/v1/chat/completions", faq, &response); status != http.StatusOK || len(response.Choices) != 1 || !strings.Contains(response.Choices[0].Message.Content, `"faqs"`) {
t.Fatalf("faq status=%d choices=%d", status, len(response.Choices))
}
grounded := `{"model":"gpt-5.6-luna","messages":[{"role":"system","content":"Knowledge Base Context:\nSource: http://captain-fixture:8080/knowledge"},{"role":"user","content":"warranty?"}]}`
if status := post(t, server.URL+"/v1/chat/completions", grounded, &response); status != http.StatusOK || !strings.Contains(response.Choices[0].Message.Content, "[[1](http://captain-fixture:8080/knowledge)]") {
t.Fatalf("grounded status=%d content=%q", status, response.Choices[0].Message.Content)
}
})
t.Run("skill activation", func(t *testing.T) {
request := `{"model":"gpt-5.6-luna","messages":[{"role":"system","content":"<available_skills_json>[{\"name\":\"local-skill\"}]</available_skills_json>"},{"role":"user","content":"[fixture:skill]"}],"tools":[{"type":"function","function":{"name":"activate_skill"}}]}`
var response struct {
Choices []struct {
Message chatMessage `json:"message"`
} `json:"choices"`
}
status := post(t, server.URL+"/v1/chat/completions", request, &response)
if status != http.StatusOK || len(response.Choices) != 1 || len(response.Choices[0].Message.ToolCalls) != 1 || response.Choices[0].Message.ToolCalls[0].Function.Name != "activate_skill" {
t.Fatalf("status=%d choices=%d", status, len(response.Choices))
}
})
t.Run("embedding dimensions", func(t *testing.T) {
var response struct {
Data []struct {
Embedding []float64 `json:"embedding"`
} `json:"data"`
}
status := post(t, server.URL+"/v1/embeddings", `{"model":"fixture-embedding","input":["hello"]}`, &response)
if status != http.StatusOK || len(response.Data) != 1 || len(response.Data[0].Embedding) != 1536 {
t.Fatalf("status=%d vectors=%d dimensions=%d", status, len(response.Data), len(response.Data[0].Embedding))
}
})
}
func post(t *testing.T, url, body string, dst any) int {
t.Helper()
resp, err := http.Post(url, "application/json", strings.NewReader(body))
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
if dst != nil {
if err := json.NewDecoder(resp.Body).Decode(dst); err != nil {
t.Fatal(err)
}
}
return resp.StatusCode
}