Smail/test/backend/config_runtime_log_test.go
smail ae224649f3
Some checks are pending
Security Checks / security (push) Waiting to run
取消独立配置审计并将配置结果写入运行日志
2026-09-09 01:46:30 +08:00

111 lines
4.0 KiB
Go

package config
import (
"bytes"
"context"
"encoding/json"
"os"
"path/filepath"
"strings"
"testing"
"time"
"git.pcux.cn/xuans/Smail/services/runtimelog"
log "github.com/sirupsen/logrus"
)
func TestDefaultConfigManagerUsesRuntimeLogAndPreservesLegacyFile(t *testing.T) {
for _, withSecurity := range []bool{false, true} {
t.Run(map[bool]string{false: "默认构造", true: "默认安全配置"}[withSecurity], func(t *testing.T) {
root := t.TempDir()
writeRuntimeFixture(t, root, validRuntimeConfig())
legacy, err := NewFileAuditWriter(root)
if err != nil {
t.Fatal(err)
}
history := []byte("保留旧审计原文,不解析、不导入、不追加\n")
if err := os.WriteFile(legacy.path, history, 0600); err != nil {
t.Fatal(err)
}
logger := log.StandardLogger()
oldHooks, oldOutput, oldFormatter, oldLevel, oldCaller := logger.Hooks, logger.Out, logger.Formatter, logger.GetLevel(), logger.ReportCaller
var output bytes.Buffer
logger.ReplaceHooks(make(log.LevelHooks))
logger.SetOutput(&output)
logger.SetFormatter(&logFormatter{})
logger.SetLevel(log.InfoLevel)
logger.SetReportCaller(true)
t.Cleanup(func() {
logger.ReplaceHooks(oldHooks)
logger.SetOutput(oldOutput)
logger.SetFormatter(oldFormatter)
logger.SetLevel(oldLevel)
logger.SetReportCaller(oldCaller)
})
writer, err := runtimelog.Start(root)
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = writer.Close() })
options := ConfigLoaderOptions{Validation: RuntimeValidationOptions{HasSuperAdmin: true, Production: true}}
var manager *ConfigManager
if withSecurity {
manager, err = NewConfigManagerWithSecurity(root, options, ConfigSecurity{})
} else {
manager, err = NewConfigManager(root, options)
}
if err != nil {
t.Fatal(err)
}
if _, ok := manager.security.Audit.(RuntimeConfigLogWriter); !ok {
t.Fatal("默认写入器仍使用独立配置审计")
}
payload, err := json.Marshal(manager.Snapshot().Email)
if err != nil {
t.Fatal(err)
}
updated, err := manager.Update(context.Background(), UpdateRequest{Section: SectionEmail, ExpectedGeneration: 1, Payload: payload, Actor: authorizedConfigActor()})
if err != nil {
t.Fatal(err)
}
if _, err := manager.Apply(context.Background(), updated.Generation); err != nil {
t.Fatal(err)
}
if manager.Status().SetupState != SetupStateReady {
t.Fatal("取消审计影响了配置保存")
}
if _, err := manager.AuditQuery(context.Background(), ConfigAuditQuery{}); err != ErrAuditQueryUnavailable {
t.Fatal("默认管理器仍提供审计查询")
}
event := ConfigAuditEvent{Operation: ConfigOperationRollback, ActorID: "1", ActorRole: "super_admin", ActorName: "不应记录的名称", FromGeneration: 2, ToGeneration: 3, Result: "failure", CreatedAt: time.Now(), Changes: []ConfigAuditChange{{Field: "root.database.password", Before: "不应记录的旧值", After: "不应记录的新值"}}}
if err := (RuntimeConfigLogWriter{}).WriteConfigAudit(context.Background(), event); err != nil {
t.Fatal(err)
}
page, err := writer.Query(context.Background(), runtimelog.Query{Limit: 10})
if err != nil {
t.Fatal(err)
}
text := strings.Join(page.Lines, "\n")
if len(page.Lines) != 2 || !strings.Contains(text, "保存配置成功") || !strings.Contains(text, "回滚配置失败") || !strings.Contains(text, "[warning]") {
t.Fatalf("运行日志缺少配置结果:%s", text)
}
if strings.Contains(text, "不应记录") || strings.Contains(text, "password") || strings.Contains(text, "\"changes\"") {
t.Fatal("运行日志泄露配置详情")
}
unchanged, err := os.ReadFile(legacy.path)
if err != nil || !bytes.Equal(unchanged, history) {
t.Fatal("旧审计文件被修改")
}
files, err := os.ReadDir(filepath.Join(root, "log"))
if err != nil {
t.Fatal(err)
}
for _, file := range files {
if strings.Contains(file.Name(), "audit") {
t.Fatal("生成了独立审计文件")
}
}
})
}
}