Files
ai-gateway-go/internal/audit/maintenance_integration_test.go
superidou 9501751792 0.10.1: 安全与业务逻辑加固、新品牌与部署加固
三轮审查修复(60+ 项),相对远端 main(b536672)的关键变更:
- 安全: 数据面 SSRF 拨号防护(防 DNS rebinding)/上游凭据剥离/登录防枚举
  与锁定态统一/可信代理(X-Forwarded-For)限流加固/会话版本失效机制/
  撤销即时传播/弱密钥拒绝启动/脱敏字节级重写(保签名契约)
- 业务逻辑: 裸 body 上传 panic/bootstrap 审计管线卡死/定价通配符优先级/
  全局工具可见性/调度器停机补跑/TOTP 挑战令牌消费顺序/熔断探针语义/
  >4MB 响应 token 计量/管理员重置密码作废会话 等
- 前端: 新 logo(语枢 AI 网关主题)/Provider 凭据异常警示/删除入口/
  后端错误消息透传/localStorage 敏感数据收敛
- 部署: CREDENTIAL_MASTER_KEY 持久化与弱值拒绝/Provider DELETE 接口/
  nginx 安全头/worker 内存限制
- 新增迁移 000029(key_hash 索引)/000030(usage_daily 币种维度)
2026-08-13 10:50:51 +08:00

74 lines
2.5 KiB
Go

package audit
import (
"context"
"os"
"testing"
"time"
"github.com/jackc/pgx/v5/pgxpool"
)
func TestMaintenancePartitionsRetentionAndIdempotency(t *testing.T) {
databaseURL := os.Getenv("AUDIT_MAINTENANCE_TEST_DATABASE_URL")
if databaseURL == "" {
t.Skip("AUDIT_MAINTENANCE_TEST_DATABASE_URL is not configured")
}
ctx := context.Background()
pool, err := pgxpool.New(ctx, databaseURL)
if err != nil {
t.Fatal(err)
}
defer pool.Close()
const oldID = "44444444-4444-4444-8444-444444444444"
const currentID = "55555555-5555-4555-8555-555555555555"
_, err = pool.Exec(ctx, `INSERT INTO gateway.audit_events(id,request_id,protocol,status_code,recorded_at) VALUES
($1,'maintenance-old','/v1/test',200,'2026-01-15T00:00:00Z'),
($2,'maintenance-current','/v1/test',200,'2026-08-10T00:00:00Z')`, oldID, currentID)
if err != nil {
t.Fatal(err)
}
now := time.Date(2026, 8, 11, 0, 0, 0, 0, time.UTC)
first, err := NewMaintenance(pool, 300*24*time.Hour, 730*24*time.Hour, 30*24*time.Hour, 1).Run(ctx, now)
if err != nil {
t.Fatal(err)
}
if !contains(first.CreatedPartitions, "audit_events_202601") || !contains(first.CreatedPartitions, "audit_events_202608") {
t.Fatalf("expected old and current partitions, got %#v", first.CreatedPartitions)
}
second, err := NewMaintenance(pool, 90*24*time.Hour, 730*24*time.Hour, 30*24*time.Hour, 1).Run(ctx, now)
if err != nil {
t.Fatal(err)
}
if !contains(second.DroppedPartitions, "audit_events_202601") {
t.Fatalf("expected stale partition to be dropped, got %#v", second.DroppedPartitions)
}
var currentTable string
if err := pool.QueryRow(ctx, `SELECT tableoid::regclass::text FROM gateway.audit_events WHERE id=$1`, currentID).Scan(&currentTable); err != nil {
t.Fatal(err)
}
if currentTable != "gateway.audit_events_202608" && currentTable != "audit_events_202608" {
t.Fatalf("current row was not routed to monthly partition: %s", currentTable)
}
var oldCount int
if err := pool.QueryRow(ctx, `SELECT count(*) FROM gateway.audit_events WHERE id=$1`, oldID).Scan(&oldCount); err != nil || oldCount != 0 {
t.Fatalf("expired row still exists: count=%d err=%v", oldCount, err)
}
third, err := NewMaintenance(pool, 90*24*time.Hour, 730*24*time.Hour, 30*24*time.Hour, 1).Run(ctx, now)
if err != nil {
t.Fatal(err)
}
if len(third.CreatedPartitions) != 0 || len(third.DroppedPartitions) != 0 {
t.Fatalf("maintenance must be idempotent: %#v", third)
}
}
func contains(values []string, wanted string) bool {
for _, value := range values {
if value == wanted {
return true
}
}
return false
}