00c4c13283
build / build (push) Successful in 1m19s
列表正文改为按需读取;保留完整导出、版本隔离和有效标签筛选。 验证:前端全量、Go全量及隔离PG全mirror竞态通过;覆盖首批回调、续载失败与筛选分页。
94 lines
3.6 KiB
Go
94 lines
3.6 KiB
Go
package httpapi
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"knowledge-governance/internal/dobotaiops"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
type qaPreviewSpy struct {
|
|
qaOpsSpy
|
|
record dobotaiops.Record
|
|
}
|
|
|
|
func (s *qaPreviewSpy) ListRecords(_ context.Context, q dobotaiops.RecordQuery) (dobotaiops.RecordPage, error) {
|
|
s.query = q
|
|
return dobotaiops.RecordPage{Items: []dobotaiops.Record{s.record}, Total: 1}, nil
|
|
}
|
|
func TestQALightweightListPreservesFullRecordAndRejectsUnknownProjection(t *testing.T) {
|
|
full := strings.Repeat("长😀", 900)
|
|
spy := &qaPreviewSpy{record: dobotaiops.Record{ID: "preview-fixture", Question: full, Answer: full, MasterFields: map[string]string{"question_chinese": full, "product_category": "MG400"}, References: json.RawMessage(`[{"content":"private-reference-body"}]`), Media: json.RawMessage(`[{"url":"private-media"}]`)}}
|
|
auth := newFakeAuthStore()
|
|
auth.sessions["admin-qa-fixture"] = "usr-boss"
|
|
h, err := NewHandler(Config{Store: &fakeJobStore{}, QAOps: spy, QAOpsTenantKey: "approved-tenant", Auth: &AuthConfig{Store: auth}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
base := "/api/v1/qa-ops/records?start=2026-10-14T00:00:00Z&end=2026-10-15T00:00:00Z"
|
|
for _, mode := range []string{"summary", "full", "invalid"} {
|
|
req := httptest.NewRequest("GET", base+"&projection="+mode, nil)
|
|
req.AddCookie(&http.Cookie{Name: sessionCookieName, Value: "admin-qa-fixture"})
|
|
out := httptest.NewRecorder()
|
|
h.ServeHTTP(out, req)
|
|
if mode == "invalid" {
|
|
if out.Code != 400 {
|
|
t.Fatal("unknown projection accepted")
|
|
}
|
|
continue
|
|
}
|
|
if out.Code != 200 {
|
|
t.Fatalf("projection %s status %d", mode, out.Code)
|
|
}
|
|
var data struct{ Data dobotaiops.RecordPage }
|
|
if json.Unmarshal(out.Body.Bytes(), &data) != nil || len(data.Data.Items) != 1 {
|
|
t.Fatal("invalid page")
|
|
}
|
|
row := data.Data.Items[0]
|
|
if mode == "summary" {
|
|
if utf8.RuneCountInString(row.Answer) > 401 || row.Answer == full || strings.Contains(out.Body.String(), "private-reference-body") || strings.Contains(out.Body.String(), "private-media") {
|
|
t.Fatal("summary leaked full content")
|
|
}
|
|
} else if row.Answer != full || !strings.Contains(out.Body.String(), "private-reference-body") {
|
|
t.Fatal("full export content changed")
|
|
}
|
|
}
|
|
if spy.record.Answer != full || spy.record.MasterFields["question_chinese"] != full {
|
|
t.Fatal("preview mutated original")
|
|
}
|
|
_ = http.StatusOK
|
|
}
|
|
|
|
func TestQARecordsAcceptFiveExactFiltersAndRejectUnsupportedValues(t *testing.T) {
|
|
spy := &qaOpsSpy{}
|
|
auth := newFakeAuthStore()
|
|
auth.sessions["admin-qa-fixture"] = "usr-boss"
|
|
h, err := NewHandler(Config{Store: &fakeJobStore{}, QAOps: spy, QAOpsTenantKey: "approved-tenant", Auth: &AuthConfig{Store: auth}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
base := "/api/v1/qa-ops/records?start=2026-10-14T00:00:00Z&end=2026-10-15T00:00:00Z"
|
|
for _, suffix := range []string{"&coverage=是&is_correct=否&product_category=MG400&problem_type=参数&root_cause=知识缺失", "&root_cause=__empty__"} {
|
|
req := httptest.NewRequest("GET", base+suffix, nil)
|
|
req.AddCookie(&http.Cookie{Name: sessionCookieName, Value: "admin-qa-fixture"})
|
|
out := httptest.NewRecorder()
|
|
h.ServeHTTP(out, req)
|
|
if out.Code != 200 || len(spy.query.Filters) == 0 {
|
|
t.Fatalf("filter not forwarded: %d", out.Code)
|
|
}
|
|
}
|
|
for _, suffix := range []string{"&coverage=invalid", "&is_correct=是&is_correct=否", "&product_category=" + strings.Repeat("x", 501)} {
|
|
req := httptest.NewRequest("GET", base+suffix, nil)
|
|
req.AddCookie(&http.Cookie{Name: sessionCookieName, Value: "admin-qa-fixture"})
|
|
out := httptest.NewRecorder()
|
|
h.ServeHTTP(out, req)
|
|
if out.Code != 400 {
|
|
t.Fatal("invalid filters accepted")
|
|
}
|
|
}
|
|
}
|