Files
Agent-System/internal/sysadmin/store_pg_test.go
T
WeiCHEN b627134c62 feat(sysadmin): add system:* permissions and settings audit log
Seed fine-grained console permissions (goose 00065), gate admin
handlers by system:* keys, and persist settings change audits for
all upserts. KB ops and whitelist consumers stay deferred.
2026-09-23 10:09:10 +08:00

154 lines
4.3 KiB
Go

package sysadmin
import (
"context"
"encoding/json"
"errors"
"testing"
"chat-agent-system/internal/db"
"github.com/jackc/pgx/v5/pgxpool"
)
func TestPGSettingsStore_RoundTrip(t *testing.T) {
dsn := db.SkipIfNoTestDatabaseURL(t)
db.RefuseSharedEnvDSN(t, dsn)
ctx := context.Background()
pool, err := pgxpool.New(ctx, dsn)
if err != nil {
t.Fatalf("connect: %v", err)
}
defer pool.Close()
if err := db.RunMigrationsOnceForTest(ctx, pool, dsn); err != nil {
t.Fatalf("migrate: %v", err)
}
store := newPGSettingsStore(pool)
key := "ui.maintenance_notice"
if _, _, _, _, err := store.Get(ctx, key); err != ErrSettingNotFound {
t.Fatalf("expected ErrSettingNotFound, got %v", err)
}
if _, _, err := store.Upsert(ctx, key, json.RawMessage(`"维护中"`), 1); err != nil {
t.Fatalf("upsert: %v", err)
}
raw, updatedBy, updatedAt, revision, err := store.Get(ctx, key)
if err != nil {
t.Fatalf("get: %v", err)
}
if string(raw) != `"维护中"` {
t.Fatalf("unexpected value: %s", raw)
}
if updatedBy == nil || *updatedBy != 1 {
t.Fatalf("expected updatedBy=1, got %+v", updatedBy)
}
if updatedAt == nil {
t.Fatal("expected updatedAt")
}
if revision != 0 {
t.Fatalf("plain upsert must not bump revision, got %d", revision)
}
if _, _, err := store.Upsert(ctx, key, json.RawMessage(`"恢复"`), 2); err != nil {
t.Fatalf("second upsert: %v", err)
}
raw, updatedBy, _, _, err = store.Get(ctx, key)
if err != nil {
t.Fatalf("get after overwrite: %v", err)
}
if string(raw) != `"恢复"` || updatedBy == nil || *updatedBy != 2 {
t.Fatalf("expected overwrite, got %s by %+v", raw, updatedBy)
}
}
func TestPGSettingsStore_AuthorOnlyCASAndAudit(t *testing.T) {
dsn := db.SkipIfNoTestDatabaseURL(t)
db.RefuseSharedEnvDSN(t, dsn)
ctx := context.Background()
pool, err := pgxpool.New(ctx, dsn)
if err != nil {
t.Fatalf("connect: %v", err)
}
defer pool.Close()
if err := db.RunMigrationsOnceForTest(ctx, pool, dsn); err != nil {
t.Fatalf("migrate: %v", err)
}
store := newPGSettingsStore(pool)
svc := NewService(nil, Options{Store: store})
pol, err := svc.GetAuthorOnlyApprovalPolicy(ctx)
if err != nil {
t.Fatalf("get policy: %v", err)
}
if pol.Allowed {
t.Fatalf("seeded policy must default closed, got %+v", pol)
}
rev := pol.Revision
setting, err := svc.UpdateSettingEx(ctx, SettingUpdate{
Key: SettingKeyAllowAuthorOnlyApproval, Value: json.RawMessage(`true`),
UpdatedBy: 11, ExpectedRevision: &rev, RequestID: "pg-cas-1",
})
if err != nil {
t.Fatalf("enable: %v", err)
}
if setting.Value != true || setting.Revision != rev+1 {
t.Fatalf("expected true@%d, got value=%v rev=%d", rev+1, setting.Value, setting.Revision)
}
_, err = svc.UpdateSettingEx(ctx, SettingUpdate{
Key: SettingKeyAllowAuthorOnlyApproval, Value: json.RawMessage(`false`),
UpdatedBy: 11, ExpectedRevision: &rev,
})
if !errors.Is(err, ErrSettingRevisionConflict) {
t.Fatalf("expected conflict, got %v", err)
}
var auditCount int
if err := pool.QueryRow(ctx, `
SELECT COUNT(*) FROM system_setting_policy_audits
WHERE setting_key = $1 AND request_id = 'pg-cas-1'
`, SettingKeyAllowAuthorOnlyApproval).Scan(&auditCount); err != nil {
t.Fatalf("count audits: %v", err)
}
if auditCount != 1 {
t.Fatalf("expected 1 audit row, got %d", auditCount)
}
tx, err := pool.Begin(ctx)
if err != nil {
t.Fatalf("begin: %v", err)
}
defer tx.Rollback(ctx)
locked, err := svc.LockAuthorOnlyApprovalPolicy(ctx, tx)
if err != nil {
t.Fatalf("lock: %v", err)
}
if !locked.Allowed || locked.Revision != rev+1 {
t.Fatalf("locked snapshot mismatch: %+v", locked)
}
if err := tx.Commit(ctx); err != nil {
t.Fatalf("commit: %v", err)
}
// 复位为关闭,避免污染同库其它测试对默认态的假设
next := setting.Revision
if _, err := svc.UpdateSettingEx(ctx, SettingUpdate{
Key: SettingKeyAllowAuthorOnlyApproval, Value: json.RawMessage(`false`),
UpdatedBy: 11, ExpectedRevision: &next,
}); err != nil {
t.Fatalf("reset closed: %v", err)
}
audits, err := store.ListPolicyAudits(ctx, SettingKeyAllowAuthorOnlyApproval, 10)
if err != nil {
t.Fatalf("ListPolicyAudits: %v", err)
}
if len(audits) < 2 {
t.Fatalf("expected >=2 audits after enable+reset, got %d", len(audits))
}
if audits[0].SettingKey != SettingKeyAllowAuthorOnlyApproval || audits[0].ActorUserID != 11 {
t.Fatalf("unexpected newest audit: %+v", audits[0])
}
}