63f3b2c503
Annotation in 来源与目录 is now the only import decision surface: - migration 000033: catalog_import_intents (durable, rescan-proof per-file intent with locator snapshot + fetch lifecycle), source_items.policy_state, planned publication state, ui_setting_revisions.ingestion_concurrency - marking fans a folder decision out to per-file intents, materializes versioned items, reuses already-verified local bytes (compare, don't pull), and plans one publication per routed KB; blocked retires planned rows while live publications stay untouched (需下架 shows in the KB diff) - artifact.fetch worker lane (scripts/artifact_fetch.py): DingTalk export tree/live box API, AList /api/fs/get direct download with SSO-redirect fail-closed, PP ledger via locator hash; content-level dedupe on sha256 - 开始处理 promotes planned->pending inside the claim-time concurrency budget from system settings; claims enforce it in SQL - monitor overview rebuilt as knowledge-base diff: planned/needs_removal per KB + fetch pipeline summary; frontend lanes 待入库/需下架 replace the deprecated snapshot change-set confirmation - spool: images (png/jpg/gif/bmp/webp) accepted, ppt/pptx rejected in favour of PDF twins; DingTalk PPT resolves its converted PDF pair
52 lines
1.8 KiB
Go
52 lines
1.8 KiB
Go
package postgres
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
|
|
"knowledge-governance/internal/governance"
|
|
)
|
|
|
|
func TestUISettingsActivationRetiresPreviousRevisionIntegration(t *testing.T) {
|
|
databaseURL := isolatedIntegrationDatabaseURL(t, os.Getenv("TEST_DATABASE_URL"))
|
|
if databaseURL == "" {
|
|
t.Skip("TEST_DATABASE_URL is not set")
|
|
}
|
|
ctx := context.Background()
|
|
pool, err := pgxpool.New(ctx, databaseURL)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer pool.Close()
|
|
if err := ApplyMigrations(ctx, pool); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
store := NewStore(pool)
|
|
previous, err := store.GetActiveUISettings(ctx)
|
|
if err != nil || previous.ID != "settings-default" {
|
|
t.Fatalf("previous=%#v error=%v", previous, err)
|
|
}
|
|
now := time.Date(2026, 9, 10, 7, 0, 0, 0, time.UTC)
|
|
candidate, err := store.CreateUISettingsCandidate(ctx, governance.CreateUISettingsCandidateRequest{DocumentRefreshSeconds: 30, ResourceRefreshSeconds: 60, StalledAfterSeconds: 3600, FinalizingAfterSeconds: 14400, IngestionConcurrency: 2, UpdatedBy: "engineer-1", Now: now})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
active, err := store.ActivateUISettings(ctx, candidate.ID, now.Add(time.Second))
|
|
if err != nil || active.ID != candidate.ID || active.State != "active" || active.ActivatedAt == nil {
|
|
t.Fatalf("active=%#v error=%v", active, err)
|
|
}
|
|
again, err := store.ActivateUISettings(ctx, candidate.ID, now.Add(2*time.Second))
|
|
if err != nil || again.ID != candidate.ID || again.State != "active" {
|
|
t.Fatalf("again=%#v error=%v", again, err)
|
|
}
|
|
var state string
|
|
var retiredAt *time.Time
|
|
if err := pool.QueryRow(ctx, `SELECT state,retired_at FROM ui_setting_revisions WHERE id=$1`, previous.ID).Scan(&state, &retiredAt); err != nil || state != "retired" || retiredAt == nil {
|
|
t.Fatalf("previous state=%q retiredAt=%v error=%v", state, retiredAt, err)
|
|
}
|
|
}
|