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

89 lines
3.5 KiB
Go

package config
import (
"context"
"errors"
"testing"
)
func TestConfigManagerSwitchDatabaseCommitsSQLiteReference(t *testing.T) {
root := t.TempDir()
writeRuntimeFixture(t, root, validRuntimeConfig())
manager, err := NewConfigManager(root, ConfigLoaderOptions{Validation: RuntimeValidationOptions{HasSuperAdmin: true, Production: true}})
if err != nil {
t.Fatal(err)
}
target := manager.Snapshot().Root.Database
target.LocalRef = "smail-next.db"
if err := manager.SwitchDatabase(context.Background(), 1, target, authorizedConfigActor()); err != nil {
t.Fatal(err)
}
if got := manager.Snapshot(); got.Generation != 2 || got.Root.Database.LocalRef != "smail-next.db" {
t.Fatalf("switched snapshot = %+v", got)
}
status := manager.Status()
if len(status.PendingRestart) != 1 || status.PendingRestart[0] != ServiceDatabase {
t.Fatalf("pending restart services = %+v", status.PendingRestart)
}
audit := manager.security.Audit.(*FileAuditWriter)
events, err := audit.Query(context.Background(), ConfigAuditQuery{Operation: ConfigOperationMigration, Limit: 10})
if err != nil {
t.Fatal(err)
}
if len(events) != 1 || events[0].ChangedFields[0] != "root.database.localRef" {
t.Fatalf("migration audit events = %+v", events)
}
}
func TestConfigManagerSwitchDatabaseRejectsImmutableChangesAndStaleGeneration(t *testing.T) {
root := t.TempDir()
writeRuntimeFixture(t, root, validRuntimeConfig())
manager, err := NewConfigManager(root, ConfigLoaderOptions{Validation: RuntimeValidationOptions{HasSuperAdmin: true, Production: true}})
if err != nil {
t.Fatal(err)
}
target := manager.Snapshot().Root.Database
target.LocalRef = "next.db"
target.Host = "127.0.0.1"
if err := manager.SwitchDatabase(context.Background(), 1, target, authorizedConfigActor()); !errors.Is(err, ErrDatabaseMigrationTarget) {
t.Fatalf("immutable target error = %v, want invalid migration target", err)
}
target = manager.Snapshot().Root.Database
target.LocalRef = "next.db"
if err := manager.SwitchDatabase(context.Background(), 2, target, authorizedConfigActor()); !errors.Is(err, ErrGenerationConflict) {
t.Fatalf("stale generation error = %v, want generation conflict", err)
}
}
func TestConfigManagerSwitchDatabaseRequiresAuthorizedActor(t *testing.T) {
root := t.TempDir()
writeRuntimeFixture(t, root, validRuntimeConfig())
manager, err := NewConfigManager(root, ConfigLoaderOptions{Validation: RuntimeValidationOptions{HasSuperAdmin: true, Production: true}})
if err != nil {
t.Fatal(err)
}
target := manager.Snapshot().Root.Database
target.LocalRef = "next.db"
if err := manager.SwitchDatabase(context.Background(), 1, target, AuditActor{}); !errors.Is(err, ErrAuthorizationRequired) {
t.Fatalf("missing actor error = %v, want authorization required", err)
}
}
func TestConfigManagerSwitchDatabaseHonorsCancellationBeforePersistence(t *testing.T) {
root := t.TempDir()
writeRuntimeFixture(t, root, validRuntimeConfig())
manager, err := NewConfigManager(root, ConfigLoaderOptions{Validation: RuntimeValidationOptions{HasSuperAdmin: true, Production: true}})
if err != nil {
t.Fatal(err)
}
target := manager.Snapshot().Root.Database
target.LocalRef = "next.db"
if err := manager.SwitchDatabase(newCancelAfterFirstContext(), 1, target, authorizedConfigActor()); !errors.Is(err, context.Canceled) {
t.Fatalf("canceled database switch error = %v, want context canceled", err)
}
if manager.Snapshot().Generation != 1 || manager.Snapshot().Root.Database.LocalRef != "smail.db" {
t.Fatalf("canceled database switch changed snapshot: %+v", manager.Snapshot())
}
}