118 lines
3.5 KiB
Go
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("????????????")
|
|
}
|
|
}
|