Smail/backend/config/audit_query_test.go
smail f60d3b0880
Some checks are pending
Security Checks / security (push) Waiting to run
整理前后端与测试构建目录并完善配置和安全引导
统一 frontend、backend、test、test-log 和 out 目录,更新构建、部署及测试入口;纳入配置管理兼容与首次安全引导修复;排除本机配置、密钥和测试产物。
2026-09-05 19:19:52 +08:00

98 lines
3.2 KiB
Go

package config
import (
"errors"
"strings"
"testing"
"time"
)
func TestValidateConfigAuditQueryAcceptsSupportedFilters(t *testing.T) {
query := ConfigAuditQuery{
From: time.Date(2026, 8, 1, 0, 0, 0, 0, time.UTC),
To: time.Date(2026, 8, 30, 0, 0, 0, 0, time.UTC),
ActorID: "42",
Operation: ConfigOperationUpdate,
Section: SectionSMTP,
Limit: 1000,
}
if err := ValidateConfigAuditQuery(query); err != nil {
t.Fatalf("valid audit query rejected: %v", err)
}
}
func TestValidateConfigAuditQueryRejectsAmbiguousFilters(t *testing.T) {
tests := []ConfigAuditQuery{
{Limit: -1},
{Limit: 1001},
{From: time.Date(2026, 8, 30, 0, 0, 0, 0, time.UTC), To: time.Date(2026, 8, 1, 0, 0, 0, 0, time.UTC)},
{ActorID: "bad\nactor"},
{Operation: ConfigOperation("unknown")},
{Section: Section("unknown")},
}
for index, query := range tests {
if err := ValidateConfigAuditQuery(query); !errors.Is(err, ErrInvalidAuditQuery) {
t.Fatalf("case %d returned %v, want ErrInvalidAuditQuery", index, err)
}
}
}
func TestConfigManagerRejectsInvalidAuditQueryBeforeStorageAccess(t *testing.T) {
var manager *ConfigManager
_, err := manager.AuditQuery(nil, ConfigAuditQuery{Limit: 1001})
if !errors.Is(err, ErrInvalidAuditQuery) {
t.Fatalf("AuditQuery returned %v, want ErrInvalidAuditQuery", err)
}
}
func TestConfigManagerAuditDetailUsesStableIDAndRedactsExport(t *testing.T) {
writer := &MemoryAuditWriter{}
manager := &ConfigManager{security: ConfigSecurity{Audit: writer}}
event := ConfigAuditEvent{
Operation: ConfigOperationUpdate,
Section: SectionSMTP,
ActorID: "42",
ActorRole: "super_admin",
FromGeneration: 1,
ToGeneration: 2,
ChangedFields: []string{"relay.password"},
ConfirmationMethod: "recent_auth",
Result: "success",
CreatedAt: time.Date(2026, 8, 30, 0, 0, 0, 0, time.UTC),
}
if err := writer.WriteConfigAudit(nil, event); err != nil {
t.Fatal(err)
}
items := writer.Events()
if len(items) != 1 || items[0].ID == "" || !strings.HasPrefix(items[0].ID, "audit-") {
t.Fatalf("unexpected stable audit ID: %+v", items)
}
detail, err := manager.AuditDetail(nil, items[0].ID)
if err != nil {
t.Fatal(err)
}
if detail.ID != items[0].ID || detail.ChangedFields[0] != "relay.password" {
t.Fatalf("unexpected audit detail: %+v", detail)
}
if _, err := manager.AuditDetail(nil, "audit-not-valid"); !errors.Is(err, ErrInvalidAuditQuery) {
t.Fatalf("invalid audit ID returned %v", err)
}
if _, err := manager.AuditDetail(nil, "audit-00000000000000000000000000000000"); !errors.Is(err, ErrAuditNotFound) {
t.Fatalf("missing audit ID returned %v", err)
}
jsonData, err := manager.ExportAuditJSON(nil, ConfigAuditQuery{ID: items[0].ID})
if err != nil {
t.Fatal(err)
}
if strings.Contains(string(jsonData), "password-value") || !strings.Contains(string(jsonData), items[0].ID) {
t.Fatalf("unexpected audit export: %s", jsonData)
}
csvData, err := manager.ExportAuditCSV(nil, ConfigAuditQuery{ID: items[0].ID})
if err != nil {
t.Fatal(err)
}
if !strings.Contains(string(csvData), "id,operation") || !strings.Contains(string(csvData), items[0].ID) {
t.Fatalf("CSV export does not include stable ID: %s", csvData)
}
}