111 lines
4.0 KiB
Go
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("生成了独立审计文件")
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|