Files
go-synapse-backupper/pkg/domain/backup/backup_test.go
T

130 lines
4.5 KiB
Go

package backup
import (
"context"
"os"
"strings"
"testing"
"git.tswf.io/infra/go-synapse-backupper/pkg/adapters/config"
"git.tswf.io/infra/go-synapse-backupper/pkg/adapters/crypto/keymanager"
"git.tswf.io/infra/go-synapse-backupper/pkg/adapters/crypto/mlkem768"
"git.tswf.io/infra/go-synapse-backupper/pkg/adapters/crypto/x25519"
"git.tswf.io/infra/go-synapse-backupper/pkg/domain/crypto"
)
// TestRunOnce_MissingKeys verifies that RunOnce surfaces a configuration error
// when the public key paths are not provided, exercising the early validation
// path (registry build, keymanager construction, key load).
func TestRunOnce_MissingKeys(t *testing.T) {
cfg := &config.Config{}
// PQPublicKeyPath and ClassicalPublicKeyPath intentionally left empty.
err := RunOnce(context.Background(), cfg)
if err == nil {
t.Fatal("RunOnce returned nil error with empty config, want error")
}
if !strings.Contains(err.Error(), "load pq public key") {
t.Fatalf("RunOnce error = %q, want it to mention %q", err, "load pq public key")
}
}
// TestRunOnce_PQKeyNotFound verifies the error returned when the post-quantum
// public key path is set but the file does not exist on disk.
func TestRunOnce_PQKeyNotFound(t *testing.T) {
cfg := &config.Config{
PQScheme: 0x0006,
// Path points to a file that does not exist; use t.TempDir() to keep
// the test hermetic regardless of the working directory.
PQPublicKeyPath: t.TempDir() + "/does-not-exist-pq.pem",
ClassicalPublicKeyPath: t.TempDir() + "/does-not-exist-classical.pem",
}
err := RunOnce(context.Background(), cfg)
if err == nil {
t.Fatal("RunOnce returned nil error when keys are missing on disk, want error")
}
if !strings.Contains(err.Error(), "load pq public key") {
t.Fatalf("RunOnce error = %q, want it to mention %q", err, "load pq public key")
}
}
// TestRunOnce_ValidKeys_PgDumpMissing generates real dual-KEM keys, builds a
// fully valid config, and calls RunOnce. Since pg_dump is not installed in the
// test environment, the pipeline fails at the dump step, giving coverage of
// the full orchestration path (registry, keymanager, encryptor, sink, runner,
// pgDumpOpts assembly) while still asserting the expected error.
func TestRunOnce_ValidKeys_PgDumpMissing(t *testing.T) {
dir := t.TempDir()
keyDir := dir + "/keys"
backupDir := dir + "/backups"
_ = os.MkdirAll(keyDir, 0o755)
_ = os.MkdirAll(backupDir, 0o755)
reg := crypto.NewRegistry()
_ = reg.Register(0x0006, func() crypto.KEM { return mlkem768.New() })
_ = reg.Register(0x0007, func() crypto.KEM { return x25519.New() })
km := keymanager.NewKeyManager(reg)
pqPubFile, _ := os.Create(keyDir + "/pq.pub.pem")
pqPrivFile, _ := os.Create(keyDir + "/pq.priv.pem")
_ = km.Generate(0x0006, pqPubFile, pqPrivFile, nil)
_ = pqPubFile.Close()
_ = pqPrivFile.Close()
classicalPubFile, _ := os.Create(keyDir + "/classical.pub.pem")
classicalPrivFile, _ := os.Create(keyDir + "/classical.priv.pem")
_ = km.Generate(0x0007, classicalPubFile, classicalPrivFile, nil)
_ = classicalPubFile.Close()
_ = classicalPrivFile.Close()
cfg := &config.Config{
PQScheme: 0x0006,
ClassicalScheme: 0x0007,
PQPublicKeyPath: keyDir + "/pq.pub.pem",
ClassicalPublicKeyPath: keyDir + "/classical.pub.pem",
Backup: struct {
Dir string `mapstructure:"dir"`
RetentionDays int `mapstructure:"retention_days"`
Cron string `mapstructure:"cron"`
}{
Dir: backupDir,
RetentionDays: 180,
},
PG: struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
User string `mapstructure:"user"`
Password string `mapstructure:"password"`
Database string `mapstructure:"database"`
SSLMode string `mapstructure:"sslmode"`
ExcludeTables []string `mapstructure:"exclude_tables"`
}{
Host: "localhost",
Port: 5432,
User: "test",
Password: "test",
Database: "test",
ExcludeTables: []string{"e2e_one_time_keys_json"},
},
}
err := RunOnce(context.Background(), cfg)
if err == nil {
t.Fatal("expected error because pg_dump is not installed in the test environment")
}
if !strings.Contains(err.Error(), "backup pipeline failed") {
t.Fatalf("expected error containing 'backup pipeline failed', got: %v", err)
}
entries, _ := os.ReadDir(backupDir)
for _, e := range entries {
if strings.HasSuffix(e.Name(), ".tmp") {
t.Fatalf("unexpected .tmp file after failed backup: %s", e.Name())
}
}
}