8febe12d7d
真实本机 PG/ES/MinIO:v071 目录镜像 10 模式、结构引用/游标 15 模式、 8 表×2 外部范围身份矩阵、13 模式自然键(含软删除与合法同 parent 正例)、 备份期间三注入、收尾故障与授权撤销两个真实治理 PG 用例。 需隔离服务的用例在 test-scope.json 显式声明:普通 CI 跳过不算通过。
40 lines
1.8 KiB
Go
40 lines
1.8 KiB
Go
package postgres
|
|
|
|
import (
|
|
"context"
|
|
"knowledge-governance/internal/governance"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestContractDispatchAttachFailureLeavesNoQueueOrBusinessJob(t *testing.T) {
|
|
store, pool := newIdentityStore(t)
|
|
ctx := context.Background()
|
|
now := time.Now().UTC()
|
|
if _, err := pool.Exec(ctx, `CREATE FUNCTION fail_dispatch_attach() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN IF NEW.queue_job_id <> '' THEN RAISE EXCEPTION 'injected attach failure'; END IF; RETURN NEW; END $$; CREATE TRIGGER fail_attach BEFORE UPDATE ON library_jobs FOR EACH ROW EXECUTE FUNCTION fail_dispatch_attach()`); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
job := governance.LibraryJob{ID: "atomic-dispatch", Scope: "internal", Kind: governance.LibraryJobSync, RequestDigest: "request", IdempotencyKey: "intent-atomic", TargetSnapshotID: "snapshot"}
|
|
if _, _, err := store.CreateLibraryDispatch(ctx, job, governance.SyncStages(), false, now); err == nil {
|
|
t.Fatal("injected attach did not fail")
|
|
}
|
|
var business, queue int
|
|
if err := pool.QueryRow(ctx, `SELECT (SELECT count(*) FROM library_jobs),(SELECT count(*) FROM jobs)`).Scan(&business, &queue); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if business != 0 || queue != 0 {
|
|
t.Fatalf("partial dispatch escaped transaction: business=%d queue=%d", business, queue)
|
|
}
|
|
if _, err := pool.Exec(ctx, `DROP TRIGGER fail_attach ON library_jobs`); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
first, reused, err := store.CreateLibraryDispatch(ctx, job, governance.SyncStages(), false, now)
|
|
if err != nil || reused || first.QueueJobID == "" {
|
|
t.Fatalf("dispatch after failure: %+v %v %v", first, reused, err)
|
|
}
|
|
replay, reused, err := store.CreateLibraryDispatch(ctx, job, governance.SyncStages(), false, now)
|
|
if err != nil || !reused || replay.QueueJobID != first.QueueJobID {
|
|
t.Fatalf("dispatch replay: %+v %v %v", replay, reused, err)
|
|
}
|
|
}
|