This commit is contained in:
@@ -9,7 +9,10 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"kb-editor/internal/aifallback"
|
||||
"kb-editor/internal/staging"
|
||||
"kb-editor/internal/store"
|
||||
)
|
||||
|
||||
@@ -127,3 +130,75 @@ func TestSearchEndpointReturnsRankedHits(t *testing.T) {
|
||||
t.Fatalf("exact ID should rank first: %+v", result.Items)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAIFallbackOnlyRunsForZeroResultsAndReturnsStagingArticle(t *testing.T) {
|
||||
knowledge := t.TempDir()
|
||||
b, _ := json.Marshal(map[string]any{"id": "KB-KNOWN", "title": "Bekannter Fehler", "answer": "Bekannte Lösung"})
|
||||
if err := os.WriteFile(filepath.Join(knowledge, "known.json"), b, 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s, err := store.New(knowledge)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
calls := 0
|
||||
ollama := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
calls++
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
"message": map[string]any{"content": `{"title":"KI-Entwurf","text":"Symptom","answer":"1. Diagnose","categories":["Windows"],"keywords":["unbekannt"]}`},
|
||||
"done": true,
|
||||
})
|
||||
}))
|
||||
defer ollama.Close()
|
||||
|
||||
st, err := staging.New(t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ai, err := aifallback.New(aifallback.Config{BaseURL: ollama.URL, Model: "test-model", Timeout: time.Second, MaxConcurrent: 1, MinScore: 0.78}, st)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
web, err := fs.Sub(webFS, "viewer")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
h := newApp(s, web, appConfig{Mode: "google", Title: "Helpdesk", Writable: false, AIFallbackEnabled: true}).withAI(ai).routes()
|
||||
|
||||
// Existing results must block the AI path before Ollama is called.
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/ai/fallback", bytes.NewBufferString(`{"query":"Bekannter Fehler"}`))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
rr := httptest.NewRecorder()
|
||||
h.ServeHTTP(rr, req)
|
||||
if rr.Code != http.StatusConflict {
|
||||
t.Fatalf("known query status=%d body=%s", rr.Code, rr.Body.String())
|
||||
}
|
||||
if calls != 0 {
|
||||
t.Fatalf("Ollama should not be called when KB has hits, calls=%d", calls)
|
||||
}
|
||||
|
||||
// Unknown query is generated and stored in staging.
|
||||
req = httptest.NewRequest(http.MethodPost, "/api/ai/fallback", bytes.NewBufferString(`{"query":"0xDEADBEEF völlig unbekannt"}`))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
rr = httptest.NewRecorder()
|
||||
h.ServeHTTP(rr, req)
|
||||
if rr.Code != http.StatusCreated {
|
||||
t.Fatalf("unknown query status=%d body=%s", rr.Code, rr.Body.String())
|
||||
}
|
||||
var generated aifallback.Result
|
||||
if err := json.Unmarshal(rr.Body.Bytes(), &generated); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if calls != 1 || generated.Key == "" {
|
||||
t.Fatalf("calls=%d result=%+v", calls, generated)
|
||||
}
|
||||
|
||||
get := httptest.NewRequest(http.MethodGet, "/api/staging/"+generated.Key, nil)
|
||||
getRR := httptest.NewRecorder()
|
||||
h.ServeHTTP(getRR, get)
|
||||
if getRR.Code != http.StatusOK {
|
||||
t.Fatalf("staging get status=%d body=%s", getRR.Code, getRR.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user