Files
knowledge-governance/internal/platform/postgres/release_plan_test.go
T
zheromiku dcdc8d1ddd feat: 知识治理平台(含已验证发布/回滚契约)
全栈知识治理平台首次入库(P1-P10 + 发布链路模拟验证闭环):

- 治理核心:源目录/变更集/人工审核/发布批次/发布执行(fail-closed 闸口)
- 测试通道:WeKnora 测试 KB 上传/轮询/验收;poll 完成时写入
  parser_generation/index_generation,修复 manual/seed 路径发布闸口永久拦截
- 发布适配器 v071(本地 source=测试角色 / 云端 target=生产角色 模拟验证通过):
  * ES 导出/导入 mapping 自适应(.keyword 子字段、可选 tenant_id)
  * OSS locator 支持嵌套 scheme;chunks.seq_id 由目标序列分配
  * bundle 归档同身份重试原子替换;verifier 按 bundle chunk_type 计数
  * 回滚 job payload 支持 prior_execution_id(strict 解码)
- 三通道发布+回滚演练实测:publish 4/4 verified,检索命中,
  rollback 4/4 rolled_back,目标端 PG is_enabled=false、ES is_enabled=false
- 前端 A·Data-console 风格全站统一,120 用例通过
- 门禁:go vet / go test 全包通过,gofmt 干净
2026-09-30 10:54:06 +08:00

92 lines
3.3 KiB
Go

package postgres
import (
"context"
"errors"
"strings"
"testing"
"time"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
"knowledge-governance/internal/governance"
)
func TestListReleasePlanBuildsSafeSourceTruthActions(t *testing.T) {
rows := &releasePlanRows{items: []releasePlanRow{
{publicationID: "tp-add", title: "B.pdf", targetScope: "external", action: "add", testState: "ready", productionState: "absent", canRelease: true},
{publicationID: "tp-delete", title: "C.pdf", targetScope: "internal", action: "delete", testState: "not_required", productionState: "present", canRelease: true, blocker: ""},
{publicationID: "", title: "D.pdf", targetScope: "internal", action: "blocked", testState: "not_ready", productionState: "absent", canRelease: false, blocker: "test_not_ready"},
{publicationID: "tp-same", title: "E.pdf", targetScope: "internal", action: "unchanged", testState: "ready", productionState: "present", canRelease: false},
}}
db := &recordingDB{rows: rows}
plan, err := NewStore(db).ListReleasePlan(context.Background())
if err != nil {
t.Fatal(err)
}
if plan.Summary.Add != 1 || plan.Summary.Delete != 1 || plan.Summary.Blocked != 1 || plan.Summary.Unchanged != 1 {
t.Fatalf("plan=%#v", plan)
}
if len(plan.Items) != 3 || !plan.Items[0].CanRelease || !plan.Items[1].CanRelease || plan.Items[1].PublicationID != "tp-delete" {
t.Fatalf("items=%#v", plan.Items)
}
if db.querySQL != listReleasePlanSQL {
t.Fatalf("query=%q", db.querySQL)
}
sql := strings.ToLower(db.querySQL)
for _, required := range []string{"coalesce(test.publication_id, production.publication_id, '')", "action in ('add', 'update', 'delete')"} {
if !strings.Contains(sql, required) {
t.Fatalf("release plan SQL missing %q", required)
}
}
}
type releasePlanRow struct {
publicationID string
title string
targetScope string
action string
testState string
productionState string
canRelease bool
blocker string
}
func (row releasePlanRow) Scan(dest ...any) error {
*(dest[0].(*string)) = row.publicationID
*(dest[1].(*string)) = row.title
*(dest[2].(*string)) = row.targetScope
*(dest[3].(*string)) = row.action
*(dest[4].(*string)) = row.testState
*(dest[5].(*string)) = row.productionState
*(dest[6].(*bool)) = row.canRelease
*(dest[7].(*string)) = row.blocker
return nil
}
type releasePlanRows struct {
items []releasePlanRow
index int
}
func (rows *releasePlanRows) Close() {}
func (rows *releasePlanRows) Err() error { return nil }
func (rows *releasePlanRows) CommandTag() pgconn.CommandTag { return pgconn.CommandTag{} }
func (rows *releasePlanRows) FieldDescriptions() []pgconn.FieldDescription { return nil }
func (rows *releasePlanRows) Next() bool {
if rows.index >= len(rows.items) {
return false
}
rows.index++
return true
}
func (rows *releasePlanRows) Scan(dest ...any) error { return rows.items[rows.index-1].Scan(dest...) }
func (rows *releasePlanRows) Values() ([]any, error) { return nil, errors.New("not implemented") }
func (rows *releasePlanRows) RawValues() [][]byte { return nil }
func (rows *releasePlanRows) Conn() *pgx.Conn { return nil }
var _ pgx.Rows = (*releasePlanRows)(nil)
var _ = time.Time{}
var _ governance.ReleasePlan