Files
Agent-System/internal/sysadmin/store_pg_test.go
T
weichenandCursor 430a47ad8c test(sysadmin): cover author-only CAS, audit rollback, and HTTP gates
Verify concurrent revision conflicts, null/string rejection, plain-user
denials, and PG lock/read paths for the new policy key.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-17 09:15:15 +00:00

143 lines
4.0 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)
}
}