Files
Charon/backend/internal/services/backup_service_test.go

232 lines
6.0 KiB
Go

package services
import (
"archive/zip"
"os"
"path/filepath"
"testing"
"github.com/Wikid82/charon/backend/internal/config"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestBackupService_CreateAndList(t *testing.T) {
// Setup temp dirs
tmpDir, err := os.MkdirTemp("", "cpm-backup-service-test")
require.NoError(t, err)
defer os.RemoveAll(tmpDir)
dataDir := filepath.Join(tmpDir, "data")
err = os.MkdirAll(dataDir, 0755)
require.NoError(t, err)
// Create dummy DB
dbPath := filepath.Join(dataDir, "charon.db")
err = os.WriteFile(dbPath, []byte("dummy db"), 0644)
require.NoError(t, err)
// Create dummy caddy dir
caddyDir := filepath.Join(dataDir, "caddy")
err = os.MkdirAll(caddyDir, 0755)
require.NoError(t, err)
err = os.WriteFile(filepath.Join(caddyDir, "caddy.json"), []byte("{}"), 0644)
require.NoError(t, err)
cfg := &config.Config{DatabasePath: dbPath}
service := NewBackupService(cfg)
// Test Create
filename, err := service.CreateBackup()
require.NoError(t, err)
assert.NotEmpty(t, filename)
assert.FileExists(t, filepath.Join(service.BackupDir, filename))
// Test List
backups, err := service.ListBackups()
require.NoError(t, err)
assert.Len(t, backups, 1)
assert.Equal(t, filename, backups[0].Filename)
assert.True(t, backups[0].Size > 0)
// Test GetBackupPath
path, err := service.GetBackupPath(filename)
require.NoError(t, err)
assert.Equal(t, filepath.Join(service.BackupDir, filename), path)
// Test Restore
// Modify DB to verify restore
err = os.WriteFile(dbPath, []byte("modified db"), 0644)
require.NoError(t, err)
err = service.RestoreBackup(filename)
require.NoError(t, err)
// Verify DB content restored
content, err := os.ReadFile(dbPath)
require.NoError(t, err)
assert.Equal(t, "dummy db", string(content))
// Test Delete
err = service.DeleteBackup(filename)
require.NoError(t, err)
assert.NoFileExists(t, filepath.Join(service.BackupDir, filename))
// Test Delete Non-existent
err = service.DeleteBackup("non-existent.zip")
assert.Error(t, err)
}
func TestBackupService_Restore_ZipSlip(t *testing.T) {
// Setup temp dirs
tmpDir := t.TempDir()
service := &BackupService{
DataDir: filepath.Join(tmpDir, "data"),
BackupDir: filepath.Join(tmpDir, "backups"),
}
os.MkdirAll(service.BackupDir, 0755)
// Create malicious zip
zipPath := filepath.Join(service.BackupDir, "malicious.zip")
zipFile, err := os.Create(zipPath)
require.NoError(t, err)
w := zip.NewWriter(zipFile)
f, err := w.Create("../../../evil.txt")
require.NoError(t, err)
_, err = f.Write([]byte("evil"))
require.NoError(t, err)
w.Close()
zipFile.Close()
// Attempt restore
err = service.RestoreBackup("malicious.zip")
assert.Error(t, err)
assert.Contains(t, err.Error(), "illegal file path")
}
func TestBackupService_PathTraversal(t *testing.T) {
tmpDir := t.TempDir()
service := &BackupService{
DataDir: filepath.Join(tmpDir, "data"),
BackupDir: filepath.Join(tmpDir, "backups"),
}
os.MkdirAll(service.BackupDir, 0755)
// Test GetBackupPath with traversal
// Should return error
_, err := service.GetBackupPath("../../etc/passwd")
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalid filename")
// Test DeleteBackup with traversal
// Should return error
err = service.DeleteBackup("../../etc/passwd")
assert.Error(t, err)
assert.Contains(t, err.Error(), "invalid filename")
}
func TestBackupService_RunScheduledBackup(t *testing.T) {
// Setup temp dirs
tmpDir := t.TempDir()
dataDir := filepath.Join(tmpDir, "data")
os.MkdirAll(dataDir, 0755)
// Create dummy DB
dbPath := filepath.Join(dataDir, "charon.db")
os.WriteFile(dbPath, []byte("dummy db"), 0644)
cfg := &config.Config{DatabasePath: dbPath}
service := NewBackupService(cfg)
// Run scheduled backup manually
service.RunScheduledBackup()
// Verify backup created
backups, err := service.ListBackups()
require.NoError(t, err)
assert.Len(t, backups, 1)
}
func TestBackupService_CreateBackup_Errors(t *testing.T) {
t.Run("missing database file", func(t *testing.T) {
tmpDir := t.TempDir()
cfg := &config.Config{DatabasePath: filepath.Join(tmpDir, "nonexistent.db")}
service := NewBackupService(cfg)
_, err := service.CreateBackup()
assert.Error(t, err)
})
t.Run("cannot create backup directory", func(t *testing.T) {
tmpDir := t.TempDir()
dbPath := filepath.Join(tmpDir, "charon.db")
os.WriteFile(dbPath, []byte("test"), 0644)
// Create backup dir as a file to cause mkdir error
backupDir := filepath.Join(tmpDir, "backups")
os.WriteFile(backupDir, []byte("blocking"), 0644)
service := &BackupService{
DataDir: tmpDir,
BackupDir: backupDir,
}
_, err := service.CreateBackup()
assert.Error(t, err)
})
}
func TestBackupService_RestoreBackup_Errors(t *testing.T) {
t.Run("non-existent backup", func(t *testing.T) {
tmpDir := t.TempDir()
service := &BackupService{
DataDir: filepath.Join(tmpDir, "data"),
BackupDir: filepath.Join(tmpDir, "backups"),
}
os.MkdirAll(service.BackupDir, 0755)
err := service.RestoreBackup("nonexistent.zip")
assert.Error(t, err)
})
t.Run("invalid zip file", func(t *testing.T) {
tmpDir := t.TempDir()
service := &BackupService{
DataDir: filepath.Join(tmpDir, "data"),
BackupDir: filepath.Join(tmpDir, "backups"),
}
os.MkdirAll(service.BackupDir, 0755)
// Create invalid zip
badZip := filepath.Join(service.BackupDir, "bad.zip")
os.WriteFile(badZip, []byte("not a zip"), 0644)
err := service.RestoreBackup("bad.zip")
assert.Error(t, err)
})
}
func TestBackupService_ListBackups_EmptyDir(t *testing.T) {
tmpDir := t.TempDir()
service := &BackupService{
BackupDir: filepath.Join(tmpDir, "backups"),
}
os.MkdirAll(service.BackupDir, 0755)
backups, err := service.ListBackups()
require.NoError(t, err)
assert.Empty(t, backups)
}
func TestBackupService_ListBackups_MissingDir(t *testing.T) {
tmpDir := t.TempDir()
service := &BackupService{
BackupDir: filepath.Join(tmpDir, "nonexistent"),
}
backups, err := service.ListBackups()
require.NoError(t, err)
assert.Empty(t, backups)
}