dcdc8d1ddd
全栈知识治理平台首次入库(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 干净
102 lines
5.9 KiB
Go
102 lines
5.9 KiB
Go
package v071
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"knowledge-governance/internal/releasebundle"
|
|
releaseworkflow "knowledge-governance/internal/workflows/release"
|
|
)
|
|
|
|
func TestDocumentExporterBuildsSealedThreeLaneBundle(t *testing.T) {
|
|
pg := PostgresSnapshot{Tables: []TableSnapshot{
|
|
newTableSnapshot("knowledges", mustCanonicalRows(t, `{"id":"knowledge-safe"}`)),
|
|
newTableSnapshot("chunks", mustCanonicalRows(t, `{"id":"chunk-safe"}`)),
|
|
newTableSnapshot("knowledge_tag_relations", nil),
|
|
newTableSnapshot("resources", nil),
|
|
newTableSnapshot("resource_bindings", nil),
|
|
}}
|
|
search := SearchSnapshot{Documents: []SearchDocument{{ID: "es-safe", Source: mustCanonicalRows(t, `{"knowledge_id":"knowledge-safe"}`)[0]}}, MappingSHA256: strings.Repeat("a", 64), DocumentsSHA256: strings.Repeat("b", 64)}
|
|
objects := ObjectSnapshot{Objects: []AssetObject{}, TotalBytes: 0, SHA256: strings.Repeat("c", 64)}
|
|
exporter, err := NewDocumentExporter(DocumentExporterConfig{Contract: &stubSourceContract{contract: sourceContractFixture(strings.Repeat("a", 64))}, Postgres: &stubPGExporter{snapshot: pg}, Search: &stubSearchExporter{snapshot: search}, Objects: &stubObjectExporter{snapshot: objects}, TenantID: 10000, EmbeddingModel: "bge-m3", Now: func() time.Time { return time.Date(2026, 9, 6, 10, 0, 0, 0, time.UTC) }})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
bundleValue, err := exporter.Export(context.Background(), releaseworkflow.PlanItem{ReleaseExecutionID: "rx-safe", PublicationID: "tp-safe", SourceItemVersionID: "sv-safe", SourceRevision: "source-v1", ParseRevision: "parser-v1@index-v1", ReleaseRevision: "review-v1", TestKnowledgeBaseID: "kb-safe", KnowledgeID: "knowledge-safe", DecidedVisibility: "internal"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
bundle, ok := bundleValue.(*DocumentBundle)
|
|
if !ok {
|
|
t.Fatalf("bundle type=%T", bundleValue)
|
|
}
|
|
manifest := bundle.ReleaseManifest()
|
|
if len(manifest.BundleSHA256) != 64 || manifest.Contract.MigrationVersion != 74 || manifest.Contract.EmbeddingDimension != 1024 || manifest.Search.DocumentCount != 1 || len(manifest.Tables) != 5 {
|
|
t.Fatalf("manifest=%#v", manifest)
|
|
}
|
|
if bundle.Postgres.Table("chunks").RowCount != 1 || len(bundle.Search.Documents) != 1 || len(bundle.Objects.Objects) != 0 {
|
|
t.Fatalf("bundle=%#v", bundle)
|
|
}
|
|
}
|
|
|
|
func TestDocumentExporterRejectsMissingSearchDocuments(t *testing.T) {
|
|
pg := PostgresSnapshot{Tables: []TableSnapshot{newTableSnapshot("knowledges", mustCanonicalRows(t, `{"id":"knowledge-safe"}`)), newTableSnapshot("chunks", mustCanonicalRows(t, `{"id":"chunk-safe"}`))}}
|
|
exporter, _ := NewDocumentExporter(DocumentExporterConfig{Contract: &stubSourceContract{contract: sourceContractFixture(strings.Repeat("a", 64))}, Postgres: &stubPGExporter{snapshot: pg}, Search: &stubSearchExporter{snapshot: SearchSnapshot{MappingSHA256: strings.Repeat("a", 64), DocumentsSHA256: strings.Repeat("b", 64)}}, Objects: &stubObjectExporter{snapshot: ObjectSnapshot{SHA256: strings.Repeat("c", 64)}}, TenantID: 10000, EmbeddingModel: "bge-m3", Now: time.Now})
|
|
_, err := exporter.Export(context.Background(), releaseworkflow.PlanItem{ReleaseExecutionID: "rx-safe", PublicationID: "tp-safe", SourceItemVersionID: "sv-safe", SourceRevision: "source-v1", ParseRevision: "parser-v1@index-v1", ReleaseRevision: "review-v1", TestKnowledgeBaseID: "kb-safe", KnowledgeID: "knowledge-safe", DecidedVisibility: "internal"})
|
|
if err == nil {
|
|
t.Fatal("missing search documents accepted")
|
|
}
|
|
}
|
|
|
|
func TestDocumentExporterRejectsSourceContractMappingDrift(t *testing.T) {
|
|
pg := PostgresSnapshot{Tables: []TableSnapshot{newTableSnapshot("knowledges", mustCanonicalRows(t, `{"id":"knowledge-safe"}`)), newTableSnapshot("chunks", mustCanonicalRows(t, `{"id":"chunk-safe"}`))}}
|
|
search := SearchSnapshot{Documents: []SearchDocument{{ID: "es-safe", Source: mustCanonicalRows(t, `{"knowledge_id":"knowledge-safe"}`)[0]}}, MappingSHA256: strings.Repeat("a", 64), DocumentsSHA256: strings.Repeat("b", 64)}
|
|
exporter, _ := NewDocumentExporter(DocumentExporterConfig{Contract: &stubSourceContract{contract: sourceContractFixture(strings.Repeat("c", 64))}, Postgres: &stubPGExporter{snapshot: pg}, Search: &stubSearchExporter{snapshot: search}, Objects: &stubObjectExporter{snapshot: ObjectSnapshot{SHA256: strings.Repeat("d", 64)}}, TenantID: 10000, EmbeddingModel: "bge-m3", Now: time.Now})
|
|
_, err := exporter.Export(context.Background(), releaseworkflow.PlanItem{ReleaseExecutionID: "rx-safe", PublicationID: "tp-safe", SourceItemVersionID: "sv-safe", SourceRevision: "source-v1", ParseRevision: "parser-v1@index-v1", ReleaseRevision: "review-v1", TestKnowledgeBaseID: "kb-safe", KnowledgeID: "knowledge-safe", DecidedVisibility: "internal"})
|
|
if err == nil {
|
|
t.Fatal("source mapping drift accepted")
|
|
}
|
|
}
|
|
|
|
type stubSourceContract struct {
|
|
contract releasebundle.TargetContract
|
|
err error
|
|
}
|
|
|
|
func (s *stubSourceContract) Contract(context.Context, releaseworkflow.PlanItem) (releasebundle.TargetContract, error) {
|
|
return s.contract, s.err
|
|
}
|
|
|
|
func sourceContractFixture(mapping string) releasebundle.TargetContract {
|
|
return releasebundle.TargetContract{WeKnoraVersion: releasebundle.WeKnoraVersionV071, MigrationVersion: releasebundle.WeKnoraMigrationV071, TenantID: 10000, KnowledgeBaseID: "kb-safe", RetrieverEngine: "elasticsearch_v8", EmbeddingModel: "bge-m3", EmbeddingDimension: releasebundle.EmbeddingDimensionV071, ElasticsearchMappingSHA256: mapping}
|
|
}
|
|
|
|
type stubPGExporter struct {
|
|
snapshot PostgresSnapshot
|
|
err error
|
|
}
|
|
|
|
func (s *stubPGExporter) Export(context.Context, PostgresIdentity) (PostgresSnapshot, error) {
|
|
return s.snapshot, s.err
|
|
}
|
|
|
|
type stubSearchExporter struct {
|
|
snapshot SearchSnapshot
|
|
err error
|
|
}
|
|
|
|
func (s *stubSearchExporter) Export(context.Context, SearchIdentity) (SearchSnapshot, error) {
|
|
return s.snapshot, s.err
|
|
}
|
|
|
|
type stubObjectExporter struct {
|
|
snapshot ObjectSnapshot
|
|
err error
|
|
}
|
|
|
|
func (s *stubObjectExporter) Export(context.Context, PostgresSnapshot) (ObjectSnapshot, error) {
|
|
return s.snapshot, s.err
|
|
}
|