Files
knowledge-governance/internal/qaopsmirror/sync_integration_test.go
姚章浩 3e7fcce0b1
build / build (push) Successful in 1m11s
feat(qa-ops): 持久本地问答副本与安全答案预览
新增迁移50与每5分钟/手动持久同步,API只读本地;同步失败保留、权限阻断、租约与质量失效原子激活。
保留全量稳定分页/固定列,已存质量批量读取,元数据不反复解压长答案;详情安全Markdown与原文。

已运行整仓 make verify、源与副本隔离PG -race、真实只读源及隔离浏览器。
实际同步2525条;固定窗口640条本地中位1.46秒,对照直接源33.10秒。
停worker、缺源凭据、重启API后仍可查询;未更新8088,未修改生产原问答。
2026-10-18 19:10:47 +08:00

72 lines
1.8 KiB
Go

package qaopsmirror
import (
"context"
"errors"
pg "knowledge-governance/internal/platform/postgres"
"sync"
"testing"
"time"
)
func TestLocalMirrorDurableQueueCoalescesAndAudits(t *testing.T) {
p := mirrorFixture(t)
s := NewStore(p, "fixture-queue")
q := pg.NewStore(p)
ctx := context.Background()
if _, _, e := s.EnqueueSync(ctx, "fixture-no-worker", "fixture-admin"); !errors.Is(e, ErrWorkerUnavailable) {
t.Fatal("offline worker accepted work", e)
}
if _, e := q.Claim(ctx, "fixture-worker", []string{s.SyncJobType()}, time.Now(), time.Minute); e != nil {
t.Fatal(e)
}
var wg sync.WaitGroup
ids := make(chan string, 8)
errs := make(chan error, 8)
for range 8 {
wg.Add(1)
go func() {
defer wg.Done()
r, _, e := s.EnqueueSync(ctx, "fixture-same-action", "fixture-admin")
if e != nil {
errs <- e
return
}
ids <- r.JobID
}()
}
wg.Wait()
close(errs)
close(ids)
for e := range errs {
t.Fatal("durable enqueue failed", e)
}
id := ""
n := 0
for got := range ids {
if got == "" || id != "" && got != id {
t.Fatal("same action created different effects")
}
id = got
n++
}
if n != 8 {
t.Fatal("lost enqueue results")
}
again, _, e := s.EnqueueSync(ctx, "fixture-another-click", "fixture-admin")
if e != nil || !again.Deduplicated || again.JobID != id {
t.Fatal("in-flight synchronization did not coalesce", e)
}
if _, _, e = s.EnqueueSync(ctx, "fixture-same-action", "different-admin"); !errors.Is(e, ErrConflict) {
t.Fatal("replay changed actor", e)
}
var count int
if p.QueryRow(ctx, `SELECT count(*) FROM jobs WHERE type=$1`, s.SyncJobType()).Scan(&count) != nil || count != 1 {
t.Fatal("duplicate jobs")
}
job, e := q.Claim(ctx, "fixture-worker", []string{s.SyncJobType()}, time.Now(), time.Minute)
if e != nil || job == nil || job.ID != id {
t.Fatal("job not durable", e)
}
}