e9e7e9dbac
- Implement backup package with SQLite VACUUM INTO for safe backups - Add scheduler with configurable interval (default: 24h) - Add retention policy with automatic cleanup (default: 7 days) - Add optional gzip compression (~10x size reduction) - Add comprehensive tests for backup creation and cleanup - Integrate backup scheduler in main.go with graceful shutdown - Add backup configuration variables (BACKUP_ENABLED, BACKUP_INTERVAL, BACKUP_RETENTION_DAYS, BACKUP_PATH, BACKUP_COMPRESS) - Backups run automatically in background goroutine - Coverage: backup package 52.7%
170 lines
3.9 KiB
Go
170 lines
3.9 KiB
Go
package backup
|
|
|
|
import (
|
|
"database/sql"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
_ "modernc.org/sqlite"
|
|
)
|
|
|
|
func TestCreateBackup(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
dbPath := filepath.Join(tempDir, "test.db")
|
|
|
|
db, err := sql.Open("sqlite", dbPath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create test database: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
_, err = db.Exec("CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create test table: %v", err)
|
|
}
|
|
|
|
_, err = db.Exec("INSERT INTO test (name) VALUES ('test1'), ('test2')")
|
|
if err != nil {
|
|
t.Fatalf("Failed to insert test data: %v", err)
|
|
}
|
|
|
|
backupPath := filepath.Join(tempDir, "backups")
|
|
config := Config{
|
|
Enabled: true,
|
|
Interval: 24 * time.Hour,
|
|
RetentionDays: 7,
|
|
Path: backupPath,
|
|
Compress: false,
|
|
DatabasePath: dbPath,
|
|
}
|
|
|
|
err = CreateBackup(db, config)
|
|
if err != nil {
|
|
t.Fatalf("CreateBackup failed: %v", err)
|
|
}
|
|
|
|
files, err := os.ReadDir(backupPath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to read backup directory: %v", err)
|
|
}
|
|
|
|
if len(files) != 1 {
|
|
t.Errorf("Expected 1 backup file, got %d", len(files))
|
|
}
|
|
|
|
if len(files) > 0 && filepath.Ext(files[0].Name()) != ".db" {
|
|
t.Errorf("Expected backup file to have .db extension, got %s", files[0].Name())
|
|
}
|
|
}
|
|
|
|
func TestCreateBackupWithCompression(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
dbPath := filepath.Join(tempDir, "test.db")
|
|
|
|
db, err := sql.Open("sqlite", dbPath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create test database: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
_, err = db.Exec("CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create test table: %v", err)
|
|
}
|
|
|
|
backupPath := filepath.Join(tempDir, "backups")
|
|
config := Config{
|
|
Enabled: true,
|
|
Interval: 24 * time.Hour,
|
|
RetentionDays: 7,
|
|
Path: backupPath,
|
|
Compress: true,
|
|
DatabasePath: dbPath,
|
|
}
|
|
|
|
err = CreateBackup(db, config)
|
|
if err != nil {
|
|
t.Fatalf("CreateBackup failed: %v", err)
|
|
}
|
|
|
|
files, err := os.ReadDir(backupPath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to read backup directory: %v", err)
|
|
}
|
|
|
|
if len(files) != 1 {
|
|
t.Errorf("Expected 1 backup file, got %d", len(files))
|
|
}
|
|
|
|
if len(files) > 0 && filepath.Ext(files[0].Name()) != ".gz" {
|
|
t.Errorf("Expected backup file to have .gz extension, got %s", files[0].Name())
|
|
}
|
|
}
|
|
|
|
func TestCreateBackupDisabled(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
dbPath := filepath.Join(tempDir, "test.db")
|
|
|
|
db, err := sql.Open("sqlite", dbPath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create test database: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
backupPath := filepath.Join(tempDir, "backups")
|
|
config := Config{
|
|
Enabled: false,
|
|
Interval: 24 * time.Hour,
|
|
RetentionDays: 7,
|
|
Path: backupPath,
|
|
Compress: false,
|
|
DatabasePath: dbPath,
|
|
}
|
|
|
|
err = CreateBackup(db, config)
|
|
if err != nil {
|
|
t.Fatalf("CreateBackup failed: %v", err)
|
|
}
|
|
|
|
_, err = os.Stat(backupPath)
|
|
if !os.IsNotExist(err) {
|
|
t.Error("Backup directory should not exist when backup is disabled")
|
|
}
|
|
}
|
|
|
|
func TestCleanOldBackups(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
backupPath := filepath.Join(tempDir, "backups")
|
|
os.MkdirAll(backupPath, 0755)
|
|
|
|
oldFile := filepath.Join(backupPath, "apocapoc_20200101_120000.db")
|
|
recentFile := filepath.Join(backupPath, "apocapoc_"+time.Now().Format("20060102_150405")+".db")
|
|
|
|
os.WriteFile(oldFile, []byte("old"), 0644)
|
|
os.WriteFile(recentFile, []byte("recent"), 0644)
|
|
|
|
oldTime := time.Now().AddDate(0, 0, -10)
|
|
os.Chtimes(oldFile, oldTime, oldTime)
|
|
|
|
config := Config{
|
|
Enabled: true,
|
|
RetentionDays: 7,
|
|
Path: backupPath,
|
|
}
|
|
|
|
err := CleanOldBackups(config)
|
|
if err != nil {
|
|
t.Fatalf("CleanOldBackups failed: %v", err)
|
|
}
|
|
|
|
if _, err := os.Stat(oldFile); !os.IsNotExist(err) {
|
|
t.Error("Old backup file should have been deleted")
|
|
}
|
|
|
|
if _, err := os.Stat(recentFile); err != nil {
|
|
t.Error("Recent backup file should still exist")
|
|
}
|
|
}
|