2c0bb32183
build / build (push) Successful in 1m11s
通过持久来源关联读取最新本地反馈,强制总表有效正确性为否,保留原问答和人工证据。统一列表、详情、指标和两阶段读取的反馈快照,拒绝冲突选择并保留其他字段编辑。已通过完整本地门禁、隔离PG竞态和真实恢复库52条点踩逐条读回、桌面及手机验收;未调用模型或连接源生产库。
87 lines
4.8 KiB
Go
87 lines
4.8 KiB
Go
package qaopsmirror
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"knowledge-governance/internal/dobotaiops"
|
|
pg "knowledge-governance/internal/platform/postgres"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestMasterDownvoteOverridesStoredAndInlineCorrectnessAcrossReaders(t *testing.T) {
|
|
p := mirrorFixture(t)
|
|
ctx := context.Background()
|
|
s := NewStore(p, "fixture-feedback-rule")
|
|
queue := pg.NewStore(p)
|
|
entry := fixtureEntry("feedback-rule", "已有资料")
|
|
var wire map[string]any
|
|
if err := json.Unmarshal(entry.Payload, &wire); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
wire["asked_at"] = "2026-10-06T08:00:00+08:00"
|
|
entry.Payload, _ = json.Marshal(wire)
|
|
job := claimMirrorJob(t, s, queue, "feedback-rule-seed")
|
|
if err := s.HandleSync(ctx, *job, func(context.Context) (Source, error) {
|
|
return &fixtureSource{rows: []dobotaiops.MirrorRecord{entry}}, nil
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := p.Exec(ctx, `UPDATE qa_ops_master_records SET local_fields=local_fields||'{"coverage":"是","is_correct":"是","product_category":"MG400","problem_type":"参数"}'::jsonb WHERE tenant_key=$1 AND id=$2`, s.tenant, "feedback-rule"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
rec, err := s.GetMasterRecord(ctx, s.tenant, "feedback-rule")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
keys := []dobotaiops.QualityKey{{ID: rec.ID, InputHash: rec.QualityInputHash}}
|
|
views, err := s.ReadMasterQualityViews(ctx, keys)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
v := views[rec.ID]
|
|
receipt, err := s.PatchMasterFields(ctx, rec.ID, MasterFieldPatch{InputHash: v.InputHash, InputEpoch: v.InputEpoch, BaseReviewRevision: v.Revision, Nonce: "fixture_feedback_rule_001", Fields: map[string]string{"is_correct": "是"}}, "fixture-admin")
|
|
if err != nil || receipt.SavedRevision != 1 {
|
|
t.Fatal("fixture inline positive edit failed", err)
|
|
}
|
|
var before string
|
|
if err = p.QueryRow(ctx, `SELECT md5(payload::text||original_fields::text||local_fields::text) FROM qa_ops_master_records WHERE tenant_key=$1 AND id=$2`, s.tenant, rec.ID).Scan(&before); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Only the replaceable local source mirror receives the live feedback update.
|
|
var revisionBefore, revisionAfter int64
|
|
if err = p.QueryRow(ctx, `SELECT revision FROM qa_ops_master_state WHERE tenant_key=$1`, s.tenant).Scan(&revisionBefore); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err = p.Exec(ctx, `UPDATE qa_ops_mirror_records SET payload=jsonb_set(payload,'{feedback}','{"asker_value":"down","up":0,"down":1}') WHERE tenant_key=$1 AND id=$2`, s.tenant, rec.ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err = p.QueryRow(ctx, `SELECT revision FROM qa_ops_master_state WHERE tenant_key=$1`, s.tenant).Scan(&revisionAfter); err != nil || revisionAfter <= revisionBefore {
|
|
t.Fatal("current master feedback changed without invalidating its snapshot revision", err)
|
|
}
|
|
fresh, err := s.GetMasterRecord(ctx, s.tenant, rec.ID)
|
|
if err != nil || fresh.Feedback.Down != 1 || fresh.MasterFields["is_correct"] != "否" {
|
|
t.Fatalf("live downvote did not force detail/master fields to incorrect: fields=%v feedback=%+v error=%v", fresh.MasterFields, fresh.Feedback, err)
|
|
}
|
|
page, err := s.ListMasterRecords(ctx, dobotaiops.RecordQuery{Window: dobotaiops.Window{TenantKey: s.tenant, Start: rec.AskedAt.Add(-time.Hour), End: rec.AskedAt.Add(time.Hour)}, Limit: 10})
|
|
if err != nil || len(page.Items) != 1 || page.Items[0].MasterFields["is_correct"] != "否" {
|
|
t.Fatal("list differs from detail", err)
|
|
}
|
|
views, err = s.ReadMasterQualityViews(ctx, keys)
|
|
if err != nil || views[rec.ID].Effective.Correctness != "incorrect" {
|
|
t.Fatal("effective quality retained the old positive inline edit", err)
|
|
}
|
|
if _, err = s.PatchMasterFields(ctx, rec.ID, MasterFieldPatch{InputHash: fresh.QualityInputHash, InputEpoch: views[rec.ID].InputEpoch, BaseRevision: 1, BaseReviewRevision: views[rec.ID].Revision, Nonce: "fixture_feedback_rule_002", Fields: map[string]string{"is_correct": "是"}}, "fixture-admin"); err == nil {
|
|
t.Fatal("an active downvote accepted a positive correctness write")
|
|
}
|
|
start, _ := time.Parse(time.RFC3339, "2026-10-05T00:00:00+08:00")
|
|
dashboard, err := s.ReadMasterDashboard(ctx, dobotaiops.RecordQuery{Window: dobotaiops.Window{TenantKey: s.tenant, Start: start, End: start.AddDate(0, 0, 7)}})
|
|
if err != nil || dashboard.Quality.Rates.Correctness.Numerator != 0 || dashboard.Quality.Rates.Correctness.Denominator != 1 {
|
|
t.Fatal("dashboard did not consume the same downvote rule", err)
|
|
}
|
|
var after, originalInline string
|
|
if err = p.QueryRow(ctx, `SELECT md5(m.payload::text||m.original_fields::text||m.local_fields::text),e.fields->>'is_correct' FROM qa_ops_master_records m JOIN qa_ops_master_field_overrides e ON e.tenant_key=m.tenant_key AND e.record_id=m.id WHERE m.tenant_key=$1 AND m.id=$2`, s.tenant, rec.ID).Scan(&after, &originalInline); err != nil || after != before || originalInline != "是" {
|
|
t.Fatal("feedback projection rewrote original questions, labels or human evidence", err)
|
|
}
|
|
}
|