Files
knowledge-governance/internal/monitoring/removal_demand_integration_test.go
T
姚章浩 d0a45d563f fix(monitor): 需下架车道恒为 0(判据用了没人写的状态)+ 隔离会改线上设置的集成测试
界面验收第三遍(禁止导入 → 需下架)暴露两处真问题:

1) 需下架车道死掉:判据 `retrieval_state='enabled'`,而 retrieval_state 在这套状态机里
   描述「是否发布到生产」(unpublished/disabling/disabled/failed),全仓没有任何代码写过
   'enabled',线上 5/5 发布单元都是 unpublished → 车道恒 0,界面「仍在知识库里的文件会
   出现在这里」的承诺不成立。改为与设计注释一致的口径:仍在测试库存活(completed + 有
   knowledge_id + 未 disabling/disabled)+ 来源项 policy_state='blocked'。
   线上对照:旧判据 0 → 新判据 1,命中被禁止导入的 DH-AG95夹爪适配-指令协议.md;
   修后页面显示「需下架(1)· 对内知识库 · 1 份仍在检索中」。新增集成测试覆盖
   存活/未禁止/移除中/已移除/未上传五种情形(真实 Postgres + 独立 schema)。

2) 集成测试改到线上设置(直接踩到并已恢复):ui_settings_integration_test.go 只判断
   TEST_DATABASE_URL 是否存在、无 schema 隔离,而它「创建候选并激活」——跑在共享库上会把
   真实 active 设置挤成 retired。已先备份(backups/ui-settings/…pre-restore.jsonl),再用
   单事务(先 retire 再 activate,uq_ui_setting_active 只允许一条 active)恢复
   uis-d40ea1fc…(15/30/1800/7200 + https://weknora-test.dobot.cn/),并复核线上值。
   根因修复:给该用例加 per-test schema 隔离,复跑后线上设置不再被改动。

同时把两处与事实不符的文案改实话(WeKnoraMonitor.tsx 需下架说明、SourceCatalogBrowser.tsx
禁止导入说明):移除走 test.remove 工作流(删除测试库知识条目,可再入库),入口当前由
变更集删除路径触发,页面尚无独立按钮——条目级列表与下架按钮列为待排期。
2026-10-10 10:55:03 +08:00

91 lines
4.1 KiB
Go

package monitoring
import (
"context"
"testing"
"github.com/jackc/pgx/v5/pgxpool"
"knowledge-governance/internal/platform/postgres"
)
// TestRealOverviewRemovalDemandCountsLiveBlockedPublications freezes the
// 需下架 lane definition after the 2026-10-10 UI acceptance run proved it dead:
// the SQL asked for `retrieval_state='enabled'`, a value this state machine
// never writes (retrieval_state describes production publication:
// unpublished/disabling/disabled/failed), so "把已入库的文件改成禁止导入后
// 车道仍显示 0" — the page promise in SourceCatalogBrowser ("仍在知识库里的文件
// 会出现在解析监控 → 需下架") did not hold.
//
// The lane must count knowledge that is still alive in the test/authoring KB
// (completed + real knowledge id + not already flagged/removed) whose source
// item has been marked 禁止导入, and must drop each row as soon as removal is
// under way ('disabling') or done ('disabled').
func TestRealOverviewRemovalDemandCountsLiveBlockedPublications(t *testing.T) {
ctx := context.Background()
pool, err := pgxpool.New(ctx, isolatedMetricsURL(t))
if err != nil {
t.Fatal(err)
}
defer pool.Close()
if err := postgres.ApplyMigrations(ctx, pool); err != nil {
t.Fatal(err)
}
if _, err := pool.Exec(ctx, `INSERT INTO source_connections(id,source_type,name,enabled) VALUES('conn-removal','dingtalk','需下架用例',TRUE)`); err != nil {
t.Fatal(err)
}
seedItem := func(itemID, stableID, policy string) {
if _, err := pool.Exec(ctx, `INSERT INTO source_items(id,source_connection_id,stable_external_id,canonical_uri_hash,title,item_type,policy_state)
VALUES($1,'conn-removal',$2,repeat('a',64),$2,'file',$3)`, itemID, stableID, policy); err != nil {
t.Fatalf("seed item %s: %v", itemID, err)
}
if _, err := pool.Exec(ctx, `INSERT INTO source_item_versions(id,source_item_id,revision,content_state) VALUES($1,$2,'r1','verified')`, "ver-"+itemID, itemID); err != nil {
t.Fatalf("seed version %s: %v", itemID, err)
}
}
seedPublication := func(publicationID, itemID, knowledgeID, processing, retrieval string) {
if _, err := pool.Exec(ctx, `INSERT INTO test_publications(id,source_item_version_id,test_kb_id,knowledge_id,ingest_state,processing_state,retrieval_state)
VALUES($1,$2,'kb-removal',$3,'uploaded',$4,$5)`, publicationID, "ver-"+itemID, knowledgeID, processing, retrieval); err != nil {
t.Fatalf("seed publication %s: %v", publicationID, err)
}
}
// Live in the test KB and now marked 禁止导入 → must be exposed.
seedItem("item-blocked", "blocked.md", "blocked")
seedPublication("pub-blocked", "item-blocked", "knowledge-blocked", "completed", "unpublished")
// Same shape but still routed internally → nothing to do.
seedItem("item-internal", "internal.md", "internal")
seedPublication("pub-internal", "item-internal", "knowledge-internal", "completed", "unpublished")
// Removal already running / already done → drop out of the lane.
seedItem("item-disabling", "disabling.md", "blocked")
seedPublication("pub-disabling", "item-disabling", "knowledge-disabling", "completed", "disabling")
seedItem("item-disabled", "disabled.md", "blocked")
seedPublication("pub-disabled", "item-disabled", "knowledge-disabled", "completed", "disabled")
// Blocked before it ever reached a KB → nothing live to remove.
seedItem("item-planned", "planned.md", "blocked")
seedPublication("pub-planned", "item-planned", "", "planned", "unpublished")
provider, err := NewRealProvider(RealProviderConfig{DB: metricsPool{pool: pool}})
if err != nil {
t.Fatal(err)
}
overview, err := provider.Overview(ctx)
if err != nil {
t.Fatalf("overview: %v", err)
}
if len(overview.KnowledgeBases) != 1 {
t.Fatalf("expected one knowledge base row, got %#v", overview.KnowledgeBases)
}
progress := overview.KnowledgeBases[0]
if progress.NeedsRemoval != 1 {
t.Fatalf("needs_removal=%d, want 1 (live + blocked only)", progress.NeedsRemoval)
}
if progress.StalePlanned != 1 {
t.Fatalf("stale_planned=%d, want 1 (blocked before upload)", progress.StalePlanned)
}
if progress.Completed != 4 {
t.Fatalf("completed=%d, want 4", progress.Completed)
}
}