Files
knowledge-governance/internal/platform/postgres/source_audit_integration_test.go
T
姚章浩 7448d130ec
build / build (push) Successful in 1m13s
feat(settings): 汇总全部数据源配置操作审计
2026-10-27 10:42:30 +08:00

86 lines
3.2 KiB
Go

package postgres
import (
"context"
"github.com/jackc/pgx/v5/pgxpool"
"os"
"testing"
)
func TestSourceAuditPersistsOperations(t *testing.T) {
url := isolatedIntegrationDatabaseURL(t, os.Getenv("TEST_DATABASE_URL"))
if url == "" {
t.Skip("TEST_DATABASE_URL is not set")
}
ctx := context.Background()
pool, err := pgxpool.New(ctx, url)
if err != nil {
t.Fatal(err)
}
defer pool.Close()
if err := ApplyMigrations(ctx, pool); err != nil {
t.Fatal(err)
}
var exists bool
if err := pool.QueryRow(ctx, `SELECT to_regclass('source_configuration_events') IS NOT NULL`).Scan(&exists); err != nil {
t.Fatal(err)
}
if !exists {
t.Fatal("configuration operations have no persistent audit log")
}
_, err = pool.Exec(ctx, `INSERT INTO source_connections(id,source_type,name,enabled,updated_by) VALUES ('audit-test','dingtalk','测试知识库',true,'tester');
UPDATE source_connections SET enabled=false,updated_by='tester',updated_at=now() WHERE id='audit-test';
UPDATE source_connections SET enabled=true,updated_by='tester',updated_at=now() WHERE id='audit-test';
UPDATE source_connections SET schedule='{"refresh_interval_seconds":3600}',updated_by='tester',updated_at=now() WHERE id='audit-test';
UPDATE source_connections SET archived_at=now(),archived_by='tester',enabled=false,updated_by='tester',updated_at=now() WHERE id='audit-test';
UPDATE source_connections SET archived_at=archived_at,updated_at=now() WHERE id='audit-test';
UPDATE source_connections SET archived_at=NULL,archived_by='',updated_by='tester',updated_at=now() WHERE id='audit-test';
UPDATE source_connections SET last_scheduled_at=now(),next_refresh_at=now(),updated_at=now() WHERE id='audit-test';`)
if err != nil {
t.Fatal(err)
}
var actions []string
if err := pool.QueryRow(ctx, `SELECT array_agg(action ORDER BY id) FROM source_configuration_events WHERE connection_id='audit-test'`).Scan(&actions); err != nil {
t.Fatal(err)
}
store := NewStore(pool)
page, err := store.ListSourceAudit(ctx, 2, 0)
if err != nil {
t.Fatal(err)
}
if len(page.Items) != 2 || page.Total < 6 || page.Items[0].Action != "restored" {
t.Fatalf("bad audit page %+v", page)
}
next, err := store.ListSourceAudit(ctx, 2, 2)
if err != nil || len(next.Items) != 2 || next.Items[0].ID == page.Items[0].ID {
t.Fatalf("bad second page %+v %v", next, err)
}
tx, err := pool.Begin(ctx)
if err != nil {
t.Fatal(err)
}
if _, err = tx.Exec(ctx, `UPDATE source_connections SET enabled=true WHERE id='audit-test'`); err != nil {
t.Fatal(err)
}
if err = tx.Rollback(ctx); err != nil {
t.Fatal(err)
}
after, err := store.ListSourceAudit(ctx, 100, 0)
if err != nil || after.Total != page.Total {
t.Fatalf("rolled back changes must not appear %+v %v", after, err)
}
want := []string{"created", "paused", "resumed", "settings_updated", "archived", "restored"}
if len(actions) != len(want) {
t.Fatalf("audit actions=%v want=%v", actions, want)
}
for i := range want {
if actions[i] != want[i] {
t.Fatalf("actions=%v", actions)
}
}
var wrong int
if err := pool.QueryRow(ctx, `SELECT count(*) FROM source_configuration_events WHERE connection_id='audit-test' AND actor<>'tester'`).Scan(&wrong); err != nil || wrong != 0 {
t.Fatalf("incorrect actors %d %v", wrong, err)
}
}