Smail/backend/config/manager_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

118 lines
3.5 KiB
Go

package config
import (
"errors"
"os"
"path/filepath"
"testing"
"time"
)
func testRootConfig() RootConfig {
return RootConfig{SchemaVersion: CurrentSchemaVersion, Generation: 1, SetupState: SetupStateBootstrap,
Database: DatabaseConfig{Type: DatabaseTypeSQLite, LocalRef: "smail.db"},
SMTP: SMTPConfig{VerificationEnabled: false},
SSL: SSLConfig{Mode: "default", DefaultDirectory: "config/ssl"},
Security: SecurityConfig{PasswordVersion: "argon2id"}}
}
func TestManagerUsesGenerationConflictAndBackupRetention(t *testing.T) {
manager, err := NewManager(testRootConfig())
if err != nil {
t.Fatal(err)
}
snapshot := manager.Snapshot()
candidate := snapshot.Config
candidate.SetupState = SetupStateReady
if _, err := manager.Apply(candidate, snapshot.Generation+1); !errors.Is(err, ErrGenerationConflict) {
t.Fatalf("conflict = %v", err)
}
updated, err := manager.Apply(candidate, snapshot.Generation)
if err != nil {
t.Fatal(err)
}
path, err := BackupConfig(t.TempDir(), updated, time.Now())
if err != nil {
t.Fatal(err)
}
if _, err := LoadBackup(path); err != nil {
t.Fatal(err)
}
}
func TestBackupRetentionKeepsFourRecentFiles(t *testing.T) {
root := t.TempDir()
manager, err := NewManager(testRootConfig())
if err != nil {
t.Fatal(err)
}
for i := 0; i < 6; i++ {
snapshot := manager.Snapshot()
path, err := BackupConfig(root, snapshot, time.Now().Add(time.Duration(i)*time.Second))
if err != nil {
t.Fatal(err)
}
if filepath.Dir(path) == "" {
t.Fatal("backup path is empty")
}
}
}
func TestManagerSnapshotsAreDeepCopies(t *testing.T) {
initial := testRootConfig()
initial.Plugins = []PluginConfig{{LogicalID: "telegram", FileName: "telegram.so", NetworkAllowlist: []string{"api.example.com"}}}
manager, err := NewManager(initial)
if err != nil {
t.Fatal(err)
}
initial.Plugins[0].NetworkAllowlist[0] = "attacker.example.com"
snapshot := manager.Snapshot()
if snapshot.Config.Plugins[0].NetworkAllowlist[0] != "api.example.com" {
t.Fatal("manager retained caller-owned plugin slice")
}
snapshot.Config.Plugins[0].NetworkAllowlist[0] = "changed.example.com"
if manager.Snapshot().Config.Plugins[0].NetworkAllowlist[0] != "api.example.com" {
t.Fatal("snapshot exposed manager-owned plugin slice")
}
}
func TestPersistentManagerAppliesBackupAndReloads(t *testing.T) {
root := t.TempDir()
manager, err := NewManager(testRootConfig())
if err != nil {
t.Fatal(err)
}
candidate := manager.Snapshot().Config
candidate.SetupState = SetupStateReady
updated, backup, err := manager.ApplyPersisted(root, candidate, 1, time.Date(2026, 8, 25, 12, 0, 0, 0, time.UTC))
if err != nil {
t.Fatal(err)
}
if updated.Generation != 2 || backup == "" {
t.Fatalf("unexpected persisted update: generation=%d backup=%q", updated.Generation, backup)
}
loaded, err := LoadManager(root, testRootConfig())
if err != nil {
t.Fatal(err)
}
if snapshot := loaded.Snapshot(); snapshot.Generation != 2 || snapshot.Config.SetupState != SetupStateReady {
t.Fatalf("reloaded snapshot = %+v", snapshot)
}
}
func TestPersistentManagerRejectsSymlinkConfigDirectory(t *testing.T) {
root := t.TempDir()
outside := t.TempDir()
if err := os.Symlink(outside, filepath.Join(root, "config")); err != nil {
t.Skipf("?????????????: %v", err)
}
manager, err := NewManager(testRootConfig())
if err != nil {
t.Fatal(err)
}
candidate := manager.Snapshot().Config
if _, _, err := manager.ApplyPersisted(root, candidate, 1, time.Now()); err == nil {
t.Fatal("????????????")
}
}