Files
knowledge-governance/internal/httpapi/qa_master_test.go
姚章浩 f5e0f8d20f
build / build (push) Successful in 1m7s
feat(qa-ops): 建立独立正式主表并保真导入历史问答
将可维护总表与可替换2.0镜像分离,稳定来源关联和原始事件键保留;
源端更新/删除不覆盖主表或人工复核。首次收录保留原评估审计,
主表输入/复核独立持久化;历史原标注不冒充新AI或冻结证据。

历史导入绑定源哈希、逐行批准清单与真实恢复备份;三字段精确去重,
批次及原字段留档,重复执行不覆盖后续修改。仅操作本地治理库。

验证:完整make verify退出0;镜像与区域两组隔离PG race退出0;
真实恢复数据隔离导入12522、跳过792、暂缓1,总表15050;
重放保留模拟修改,结构down拒绝丢历史;真实浏览器第41周551条完整,
历史原标注和独立主表复核读回通过,移动布局及JS异常0。
2026-10-19 08:49:18 +08:00

79 lines
3.1 KiB
Go

package httpapi
import (
"context"
"knowledge-governance/internal/dobotaiops"
"knowledge-governance/internal/qaopsquality"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
)
type masterFixtureReader struct {
calls int
tenant string
}
func (f *masterFixtureReader) ListRecords(context.Context, dobotaiops.RecordQuery) (dobotaiops.RecordPage, error) {
panic("master must never fall back to replaceable mirror/source reads")
}
func (f *masterFixtureReader) GetRecord(context.Context, string, string) (dobotaiops.Record, error) {
panic("master must never fetch the production source")
}
func (f *masterFixtureReader) Available(context.Context) error { return nil }
func (f *masterFixtureReader) Safety() dobotaiops.SourceSafety { return dobotaiops.SourceSafety{} }
func (f *masterFixtureReader) MasterQualityStore() *qaopsquality.Store { return nil }
func (f *masterFixtureReader) ListMasterRecords(_ context.Context, q dobotaiops.RecordQuery) (dobotaiops.RecordPage, error) {
f.calls++
f.tenant = q.Window.TenantKey
return dobotaiops.RecordPage{Items: []dobotaiops.Record{}, Columns: []string{"question"}, AsOf: time.Date(2026, 10, 19, 0, 0, 0, 0, time.UTC), LocalRevision: 3}, nil
}
func (f *masterFixtureReader) GetMasterRecord(_ context.Context, tenant, id string) (dobotaiops.Record, error) {
f.calls++
f.tenant = tenant
return dobotaiops.Record{ID: id, Question: "fixture master"}, nil
}
func (f *masterFixtureReader) ReadMasterQualityScope(context.Context, dobotaiops.RecordQuery) (dobotaiops.QualityScope, error) {
return dobotaiops.QualityScope{}, nil
}
func TestQAMasterUsesFixedTenantAndNeverSourceFallback(t *testing.T) {
auth := newFakeAuthStore()
auth.sessions["fixture-master-admin"] = "usr-boss"
reader := &masterFixtureReader{}
h, e := NewHandler(Config{Store: &fakeJobStore{}, Auth: &AuthConfig{Store: auth}, QAOps: reader, QAOpsTenantKey: "fixed-master-tenant", Now: func() time.Time { return time.Date(2026, 10, 19, 0, 0, 0, 0, time.UTC) }})
if e != nil {
t.Fatal(e)
}
base := "/api/v1/qa-ops/master/records"
for _, c := range []struct {
path string
cookie bool
status int
}{{base + "?start=2026-10-05T00:00:00Z&end=2026-10-12T00:00:00Z", true, 200}, {base + "/fixture-id", true, 200}, {base + "?tenant=other", true, 400}, {base, false, 401}} {
r := httptest.NewRequest(http.MethodGet, c.path, nil)
if c.cookie {
r.AddCookie(&http.Cookie{Name: sessionCookieName, Value: "fixture-master-admin"})
}
w := httptest.NewRecorder()
h.ServeHTTP(w, r)
if w.Code != c.status {
t.Fatalf("master route %s: got %d, wanted %d", c.path, w.Code, c.status)
}
if w.Header().Get("Cache-Control") != "no-store" {
t.Fatal("master employee data was cacheable")
}
}
if reader.calls != 2 || reader.tenant != "fixed-master-tenant" {
t.Fatal("master fixed-tenant read path missing")
}
r := httptest.NewRequest(http.MethodPost, base+"/fixture-id/reviews", strings.NewReader(`{}`))
r.AddCookie(&http.Cookie{Name: sessionCookieName, Value: "fixture-master-admin"})
w := httptest.NewRecorder()
h.ServeHTTP(w, r)
if w.Code != 403 {
t.Fatal("master review lacked explicit same-origin intent gate", w.Code)
}
}