Files
knowledge-governance/internal/librarystack/stack_test.go
T
姚章浩 513aa9e6f5
build / build (push) Has been cancelled
fix(library): 收紧恢复派发真实绑定与同事务授权
为什么:Astra 复核证实五组实现缺陷——正式装配缺 scope/快照且来源与 before-image
混用;激活撤销不在 CAS 事务内、lease 用事务起始时间;激活只接受任意字符串且撤销可
被 SQL 重置/删除;准入忽略绑定冲突与 prepared 矛盾、收尾只看当前 plan;prepared 已
提交但 mark-intent 未落盘时被误判 legacy。

改了什么:
- 绑定来自原锚点(scope/来源快照/backup/实例/目标指纹/索引 UUID/恢复投递),每次
  重建都实时重读目标身份并与冻结值比较;
- activation 行锁与 CAS 同事务,lease 用 clock_timestamp(),同事务核对 job/operation/
  attempt/维护窗口/恢复 queue 与 operation,同 effect 换 plan 摘要拒绝;
- 激活消费带摘要的证据文件(writer 退出+排空、目标/治理库身份、有效期),撤销终局,
  down 在 activation 有行时拒绝;
- 前置/收尾共用权威窗口全集,绑定冲突与 prepared 矛盾零写 held,合法 dispatching+
  精确 marker 仍可确认;
- prepared 无 mark-intent 仅在绑定一致且无矛盾时补同内容意图,旧无台账意图仍 held。

验证:真 PG 锁等待覆盖撤销先胜/CAS 先胜/等锁跨租约/错锚点/plan 别名/恢复投递;
COMMIT 已到服务端但回包丢失与未决事务分别读回;真 ES+PG+对象存储的 DS01 用例全部
通过;go test ./... 全绿。未迁移正式库、未部署、未激活。
2026-11-02 18:09:26 +08:00

93 lines
4.3 KiB
Go

package librarystack
import (
"knowledge-governance/internal/appconfig"
"knowledge-governance/internal/objectgateway"
"testing"
)
// DS01-F1:正式装配必须把 scope/instance/真实 attempt 身份传到 adapter;快照绑定
// 不再来自这里(来源/写前镜像只从原 WriteSet 与 backup 引用取得)。
func TestRestoreDispatchWiringUsesRealClaim(t *testing.T) {
config := appconfig.Config{
LibraryTargetInstance: "inst-prod",
LibraryProductionDatabaseURL: "postgres://user:pass@127.0.0.1:5432/kg_prod?sslmode=disable",
LibraryProductionInternalKB: "kb-internal",
LibraryProductionExternalKB: "kb-external",
}
claim := objectgateway.Claim{
JobID: "job-window", OperationID: "op-original", Owner: "worker-1#queue#job-window",
AttemptEpoch: 3, RecoveryJobID: "queue-recovery", RecoveryOperationID: "op-recovery",
}
wiring, err := RestoreDispatchWiringFor(config, "internal", claim)
if err != nil {
t.Fatalf("real wiring: %v", err)
}
if wiring.Scope != "internal" || wiring.Instance != "inst-prod" {
t.Fatalf("scope/instance = %q/%q", wiring.Scope, wiring.Instance)
}
if wiring.TargetIdentity == "" || wiring.TargetIdentity == "unconfigured" {
t.Fatalf("target identity = %q", wiring.TargetIdentity)
}
if wiring.OperationID != claim.OperationID || wiring.WindowJobID != claim.JobID ||
wiring.AttemptOwner != claim.Owner || wiring.AttemptEpoch != claim.AttemptEpoch {
t.Fatalf("attempt identity not propagated: %+v", wiring)
}
if wiring.RecoveryJobID != claim.RecoveryJobID || wiring.RecoveryOperationID != claim.RecoveryOperationID {
t.Fatalf("recovery delivery identity not propagated: %+v", wiring)
}
// 自动回滚(非恢复 worker)没有 recovery 投递身份,必须保持为空。
rollback, err := RestoreDispatchWiringFor(config, "internal", objectgateway.Claim{
JobID: "job-window", OperationID: "op-original", Owner: "worker-1#queue#job-window", AttemptEpoch: 3})
if err != nil {
t.Fatal(err)
}
if rollback.RecoveryJobID != "" || rollback.RecoveryOperationID != "" {
t.Fatalf("rollback wiring invented a recovery identity: %+v", rollback)
}
}
func TestRestoreDispatchWiringFailsClosed(t *testing.T) {
config := appconfig.Config{
LibraryTargetInstance: "inst-prod",
LibraryProductionDatabaseURL: "postgres://user:pass@127.0.0.1:5432/kg_prod?sslmode=disable",
LibraryProductionInternalKB: "kb-internal",
}
if _, err := RestoreDispatchWiringFor(config, "internal", objectgateway.Claim{}); err == nil {
t.Fatal("missing attempt identity was accepted")
}
if _, err := RestoreDispatchWiringFor(config, "tenant-wide", objectgateway.Claim{
JobID: "j", OperationID: "o", Owner: "w", AttemptEpoch: 1}); err == nil {
t.Fatal("unknown scope was accepted")
}
if _, err := RestoreDispatchWiringFor(config, "external", objectgateway.Claim{
JobID: "j", OperationID: "o", Owner: "w", AttemptEpoch: 1}); err == nil {
t.Fatal("external scope without its knowledge base was accepted")
}
if _, err := RestoreDispatchWiringFor(config, "internal", objectgateway.Claim{
JobID: "j", OperationID: "o", Owner: "w", AttemptEpoch: 1, RecoveryJobID: "q"}); err == nil {
t.Fatal("partial recovery delivery identity was accepted")
}
unconfigured := config
unconfigured.LibraryProductionDatabaseURL = ""
if _, err := RestoreDispatchWiringFor(unconfigured, "internal", objectgateway.Claim{
JobID: "j", OperationID: "o", Owner: "w", AttemptEpoch: 1}); err == nil {
t.Fatal("unconfigured target database was accepted as a dispatch target")
}
}
func TestObjectAddressingMatchesProvider(t *testing.T) {
for _, provider := range []string{"oss", "s3", "minio"} {
t.Run(provider, func(t *testing.T) {
config := appconfig.Config{LibraryProductionDatabaseURL: "configured", LibraryProductionElasticURL: "configured", LibraryProductionTenantID: 1, LibraryProductionInternalKB: "kb-target", LibraryProductionS3Endpoint: "https://storage.example", LibraryProductionS3Bucket: "test-bucket", LibraryProductionS3AccessKey: "fixture", LibraryProductionS3SecretKey: "fixture", LibraryProductionS3Provider: provider}
scope, err := scopeConfigOf(config, "internal")
if err != nil {
t.Fatal(err)
}
if scope.s3.ForcePathStyle != (provider == "minio") {
t.Fatalf("provider %s: ForcePathStyle=%v", provider, scope.s3.ForcePathStyle)
}
})
}
}