Files
knowledge-governance/internal/qaopsmirror/dashboard_integration_test.go
T
姚章浩 09b2c1dce5
build / build (push) Successful in 1m17s
fix(qa): 表头筛选并统一未覆盖正确性待确认
修正筛选入口位置:五项条件在表头就地选择,保持完整集筛选与无感续载。
统一固定规则:未覆盖一律待确认,原问答/人工模型证据不改写,已覆盖仍保留点踩优先。
验证:行为红绿、前端/Go全套及隔离PostgreSQL全mirror竞态通过;完整本地门禁与原生备份真实恢复通过。
2026-10-25 13:58:08 +08:00

157 lines
7.6 KiB
Go

package qaopsmirror
import (
"context"
"encoding/json"
"errors"
"github.com/jackc/pgx/v5/pgconn"
"knowledge-governance/internal/dobotaiops"
pg "knowledge-governance/internal/platform/postgres"
"knowledge-governance/internal/qaopsorg"
"knowledge-governance/internal/qaopsquality"
"strings"
"testing"
"time"
)
func TestDashboardUsesLifetimeWeekMonthAndDeduplicatedRegionalRoster(t *testing.T) {
p := mirrorFixture(t)
ctx := context.Background()
s := NewStore(p, "fixture-dashboard")
queue := pg.NewStore(p)
dates := []string{"2026-09-28T08:00:00+08:00", "2026-10-02T08:00:00+08:00", "2026-10-06T08:00:00+08:00", "2026-10-10T08:00:00+08:00"}
source := &fixtureSource{}
for i, date := range dates {
entry := fixtureEntry([]string{"older", "month", "covered", "uncovered"}[i], "暂无已发布")
var wire map[string]any
_ = json.Unmarshal(entry.Payload, &wire)
wire["asked_at"] = date
wire["question"] = "needle 100%_ fixture"
entry.Payload, _ = json.Marshal(wire)
source.rows = append(source.rows, entry)
}
job := claimMirrorJob(t, s, queue, "dashboard-seed")
if e := s.HandleSync(ctx, *job, func(context.Context) (Source, error) { return source, nil }); e != nil {
t.Fatal(e)
}
org := qaopsorg.NewStore(p, s.tenant)
state, e := org.ReplaceDirectory(ctx, qaopsorg.Import{Source: "weekly_report_snapshot", SyncedAt: time.Date(2026, 10, 10, 0, 0, 0, 0, time.UTC), Complete: true, Directory: qaopsorg.Directory{Departments: []qaopsorg.Department{{ID: "root", Name: "示例组织"}}, People: []qaopsorg.Person{{ID: "s", DepartmentIDs: []string{"root"}}, {ID: "zero-use", DepartmentIDs: []string{"root"}}}}})
if e != nil {
t.Fatal(e)
}
_, e = org.SaveRules(ctx, qaopsorg.SaveRequest{DirectoryID: state.DirectoryID, Regions: []qaopsorg.RegionDefinition{{ID: "education", Name: "国内教育"}}, Rules: []qaopsorg.Rule{{DepartmentID: "root", Region: "education"}}}, "fixture-admin")
if e != nil {
t.Fatal(e)
}
record, e := s.GetRecord(ctx, s.tenant, "covered")
if e != nil {
t.Fatal(e)
}
quality := qaopsquality.NewStore(p, s.tenant)
views, e := quality.Observe(ctx, []dobotaiops.Record{record})
if e != nil {
t.Fatal(e)
}
v := views[record.ID]
_, e = quality.Save(ctx, record, qaopsquality.SaveRequest{InputHash: v.InputHash, InputEpoch: v.InputEpoch, BaseRevision: v.Revision, Nonce: strings.Repeat("d", 32), Fields: qaopsquality.Fields{Coverage: "covered", Correctness: "correct", ProductCategory: "CR", ProblemType: "使用咨询"}, Reason: "explicit isolated test review"}, "fixture-admin")
if e != nil {
t.Fatal(e)
}
start, _ := time.Parse(time.RFC3339, "2026-10-05T00:00:00+08:00")
end := start.AddDate(0, 0, 7)
data, e := s.ReadDashboard(ctx, dobotaiops.RecordQuery{Window: dobotaiops.Window{TenantKey: s.tenant, Start: start, End: end}, Keyword: "100%_"})
if e != nil {
t.Fatal("dashboard must read one local snapshot without a source client", e)
}
if data.TotalIssues != 4 || data.NewThisWeek != 2 || data.Quality.Total != 2 || data.Quality.Rates.Coverage.Numerator != 1 || data.Quality.Rates.Coverage.Denominator != 2 {
t.Fatal("lifetime/selected-week quality were mixed")
}
if len(data.Daily) != 7 || len(data.Regions) != 1 {
t.Fatal("daily range or custom region catalog missing")
}
r := data.Regions[0]
if r.Questions != 3 || r.Users == nil || *r.Users != 1 || r.RosterPeople == nil || *r.RosterPeople != 2 || r.Frequency == nil || *r.Frequency != 1.5 || r.UsageCoverage.Percent == nil || *r.UsageCoverage.Percent != 50 {
t.Fatal("monthly regional scope or full-roster denominator was replaced by active users")
}
if len(data.FrozenWeeks) != 2 {
t.Fatal("historical weeks did not start at first data week")
}
for _, point := range data.FrozenWeeks {
if point.FrozenAt != nil || point.Coverage.Percent != nil || point.Correctness.Percent != nil {
t.Fatal("current quality was backfilled into absent frozen history")
}
}
t.Run("historical quality is frozen once and never backfilled", func(t *testing.T) {
tracked := start.AddDate(0, 0, 2)
if e := s.TrackQualityWeeks(ctx, tracked); e != nil {
t.Fatal(e)
}
freezeAt := end.Add(6 * time.Minute)
// Explicit fake-clock fixture: the source snapshot and review existed before the closing capture.
if _, e = p.Exec(ctx, "UPDATE qa_ops_mirror_state SET source_observed_at=$2,synced_at=$2 WHERE tenant_key=$1", s.tenant, freezeAt.Add(-time.Minute)); e != nil {
t.Fatal(e)
}
if _, e = p.Exec(ctx, "UPDATE qa_ops_quality_reviews SET created_at=$2 WHERE tenant_key=$1", s.tenant, end.Add(-time.Hour)); e != nil {
t.Fatal(e)
}
if _, e = p.Exec(ctx, "UPDATE qa_ops_quality_checks SET created_at=$2 WHERE tenant_key=$1", s.tenant, end.Add(-time.Hour)); e != nil {
t.Fatal(e)
}
frozen, e := s.FreezePreviousQualityWeek(ctx, freezeAt)
if e != nil || !frozen {
t.Fatal("first eligible Monday capture was not frozen", e)
}
for _, statement := range []string{
`UPDATE qa_ops_weekly_quality_snapshots SET policy_version=policy_version||'-changed' WHERE tenant_key=$1`,
`UPDATE qa_ops_weekly_quality_snapshot_records SET question=question||' changed' WHERE tenant_key=$1`,
`DELETE FROM qa_ops_weekly_quality_snapshots WHERE tenant_key=$1`,
`DELETE FROM qa_ops_weekly_quality_snapshot_records WHERE tenant_key=$1`,
`INSERT INTO qa_ops_weekly_quality_snapshot_records SELECT tenant_key,week_start,record_id||'-late',question,coverage_numerator,coverage_denominator,correctness_numerator,correctness_denominator FROM qa_ops_weekly_quality_snapshot_records WHERE tenant_key=$1 LIMIT 1`,
} {
mutation, err := p.Begin(ctx)
if err != nil {
t.Fatal(err)
}
_, err = mutation.Exec(ctx, statement, s.tenant)
_ = mutation.Rollback(ctx)
var dbError *pgconn.PgError
if !errors.As(err, &dbError) || dbError.Code != "55000" {
t.Fatal("committed snapshot mutation was not rejected by the immutable guard", err)
}
}
historical, e := s.ReadDashboard(ctx, dobotaiops.RecordQuery{Window: dobotaiops.Window{TenantKey: s.tenant, Start: start, End: end}, Keyword: "100%_"})
if e != nil {
t.Fatal(e)
}
last := historical.FrozenWeeks[len(historical.FrozenWeeks)-1]
if last.FrozenAt == nil || last.Coverage.Percent == nil || *last.Coverage.Percent != 50 || last.Correctness.Percent == nil || *last.Correctness.Percent != 100 {
t.Fatal("frozen ratios or literal keyword projection were lost")
}
latest, e := quality.ReadViews(ctx, []dobotaiops.QualityKey{{ID: record.ID, InputHash: v.InputHash}})
if e != nil {
t.Fatal(e)
}
current := latest[record.ID]
if _, e = quality.Save(ctx, record, qaopsquality.SaveRequest{InputHash: current.InputHash, InputEpoch: current.InputEpoch, BaseRevision: current.Revision, Nonce: strings.Repeat("e", 32), Fields: qaopsquality.Fields{Coverage: "uncovered", Correctness: "pending"}, Reason: "later isolated correction, must not rewrite frozen history"}, "fixture-admin"); e != nil {
t.Fatal(e)
}
repeated, e := s.FreezePreviousQualityWeek(ctx, freezeAt.Add(time.Minute))
if e != nil || repeated {
t.Fatal("immutable weekly snapshot was replaced")
}
changed, e := s.ReadDashboard(ctx, dobotaiops.RecordQuery{Window: dobotaiops.Window{TenantKey: s.tenant, Start: start, End: end}, Keyword: "100%_"})
if e != nil {
t.Fatal(e)
}
point := changed.FrozenWeeks[len(changed.FrozenWeeks)-1]
if changed.Quality.Rates.Coverage.Numerator != 0 || point.Coverage.Percent == nil || *point.Coverage.Percent != 50 || changed.FrozenWeeks[0].FrozenAt != nil {
t.Fatal("current correction rewrote frozen history or backfilled pre-tracking weeks")
}
})
raw, _ := json.Marshal(data)
if strings.Contains(string(raw), "zero-use") || strings.Contains(string(raw), "needle") {
t.Fatal("dashboard leaked identity or original question rows")
}
}