513aa9e6f5
build / build (push) Has been cancelled
为什么: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 ./... 全绿。未迁移正式库、未部署、未激活。
93 lines
4.3 KiB
Go
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)
|
|
}
|
|
})
|
|
}
|
|
}
|