package httpapi import ( "context" "net/http" "net/http/httptest" "strings" "testing" "gitea.zheromiku.top/Weknora/dobotai-platform/internal/core" ) type fakeQA struct{} func (fakeQA) Ask(context.Context, core.QARequest) (core.QAResult, error) { return core.QAResult{TurnID: "turn-1", Answer: "answer"}, nil } type captureQA struct { request core.QARequest } func (f *captureQA) Ask(_ context.Context, request core.QARequest) (core.QAResult, error) { f.request = request return core.QAResult{TurnID: "turn-capture", Answer: "answer"}, nil } func TestVersionEndpointReturnsBuildMetadataWithoutAuthentication(t *testing.T) { server, err := NewWithOptions(fakeQA{}, "ingress-secret", Options{ BuildInfo: BuildInfo{ Version: "2.1.0", CommitSHA: "abc123def456", BuildTime: "2026-07-27T12:00:00Z", }, }) if err != nil { t.Fatal(err) } request := httptest.NewRequest(http.MethodGet, "/version", nil) result := httptest.NewRecorder() server.Handler().ServeHTTP(result, request) if result.Code != http.StatusOK { t.Fatalf("status=%d body=%s", result.Code, result.Body.String()) } if got := result.Header().Get("Cache-Control"); got != "no-store" { t.Fatalf("Cache-Control=%q, want no-store", got) } want := `{"version":"2.1.0","commit_sha":"abc123def456","build_time":"2026-07-27T12:00:00Z"}` if got := strings.TrimSpace(result.Body.String()); got != want { t.Fatalf("body=%s, want %s", got, want) } } func TestVersionEndpointUsesSafeDefaults(t *testing.T) { server, err := New(fakeQA{}, "ingress-secret") if err != nil { t.Fatal(err) } request := httptest.NewRequest(http.MethodGet, "/version", nil) result := httptest.NewRecorder() server.Handler().ServeHTTP(result, request) want := `{"version":"dev","commit_sha":"unknown","build_time":"unknown"}` if got := strings.TrimSpace(result.Body.String()); result.Code != http.StatusOK || got != want { t.Fatalf("status=%d body=%s, want %s", result.Code, got, want) } } func TestQAEndpointRequiresTokenAndReturnsResult(t *testing.T) { server, err := New(fakeQA{}, "ingress-secret") if err != nil { t.Fatal(err) } body := `{"tenant_key":"tenant","channel":"dingtalk","conversation_type":"direct","external_user_id":"user","external_message_id":"message","query":"hello"}` unauthorized := httptest.NewRequest(http.MethodPost, "/v1/qa", strings.NewReader(body)) unauthorizedResult := httptest.NewRecorder() server.Handler().ServeHTTP(unauthorizedResult, unauthorized) if unauthorizedResult.Code != http.StatusUnauthorized { t.Fatalf("unauthorized status=%d", unauthorizedResult.Code) } authorized := httptest.NewRequest(http.MethodPost, "/v1/qa", strings.NewReader(body)) authorized.Header.Set("Authorization", "Bearer ingress-secret") result := httptest.NewRecorder() server.Handler().ServeHTTP(result, authorized) if result.Code != http.StatusOK || !strings.Contains(result.Body.String(), `"turn_id":"turn-1"`) { t.Fatalf("status=%d body=%s", result.Code, result.Body.String()) } } func TestQAEndpointPassesOptionalMessageType(t *testing.T) { qa := &captureQA{} server, err := New(qa, "ingress-secret") if err != nil { t.Fatal(err) } body := `{"tenant_key":"tenant","channel":"web","conversation_type":"direct","external_user_id":"user","external_message_id":"message-rich","message_type":"rich_text","query":"hello"}` request := httptest.NewRequest(http.MethodPost, "/v1/qa", strings.NewReader(body)) request.Header.Set("Authorization", "Bearer ingress-secret") result := httptest.NewRecorder() server.Handler().ServeHTTP(result, request) if result.Code != http.StatusOK { t.Fatalf("status=%d body=%s", result.Code, result.Body.String()) } if qa.request.MessageType != "rich_text" { t.Fatalf("MessageType=%q, want rich_text", qa.request.MessageType) } }