feat: add dry-run mode, GFS retention policies, and notifications
- Add --dry-run/-n flag for backup commands with comprehensive preflight checks - Database connectivity validation - Required tools availability check - Storage target and permissions verification - Backup size estimation - Encryption and cloud storage configuration validation - Implement GFS (Grandfather-Father-Son) retention policies - Daily/Weekly/Monthly/Yearly tier classification - Configurable retention counts per tier - Custom weekly day and monthly day settings - ISO week handling for proper week boundaries - Add notification system with SMTP and webhook support - SMTP email notifications with TLS/STARTTLS - Webhook HTTP notifications with HMAC-SHA256 signing - Slack-compatible webhook payload format - Event types: backup/restore started/completed/failed, cleanup, verify, PITR - Configurable severity levels and retry logic - Update README.md with documentation for all new features
This commit is contained in:
134
internal/checks/preflight_test.go
Normal file
134
internal/checks/preflight_test.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package checks
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPreflightResult(t *testing.T) {
|
||||
result := &PreflightResult{
|
||||
Checks: []PreflightCheck{},
|
||||
AllPassed: true,
|
||||
DatabaseInfo: &DatabaseInfo{
|
||||
Type: "postgres",
|
||||
Version: "PostgreSQL 15.0",
|
||||
Host: "localhost",
|
||||
Port: 5432,
|
||||
User: "postgres",
|
||||
},
|
||||
StorageInfo: &StorageInfo{
|
||||
Type: "local",
|
||||
Path: "/backups",
|
||||
AvailableBytes: 10 * 1024 * 1024 * 1024,
|
||||
TotalBytes: 100 * 1024 * 1024 * 1024,
|
||||
},
|
||||
EstimatedSize: 1 * 1024 * 1024 * 1024,
|
||||
}
|
||||
|
||||
if !result.AllPassed {
|
||||
t.Error("Result should be AllPassed")
|
||||
}
|
||||
|
||||
if result.DatabaseInfo.Type != "postgres" {
|
||||
t.Errorf("DatabaseInfo.Type = %q, expected postgres", result.DatabaseInfo.Type)
|
||||
}
|
||||
|
||||
if result.StorageInfo.Path != "/backups" {
|
||||
t.Errorf("StorageInfo.Path = %q, expected /backups", result.StorageInfo.Path)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreflightCheck(t *testing.T) {
|
||||
check := PreflightCheck{
|
||||
Name: "Database Connectivity",
|
||||
Status: StatusPassed,
|
||||
Message: "Connected successfully",
|
||||
Details: "PostgreSQL 15.0",
|
||||
}
|
||||
|
||||
if check.Status != StatusPassed {
|
||||
t.Error("Check status should be passed")
|
||||
}
|
||||
|
||||
if check.Name != "Database Connectivity" {
|
||||
t.Errorf("Check name = %q", check.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckStatusString(t *testing.T) {
|
||||
tests := []struct {
|
||||
status CheckStatus
|
||||
expected string
|
||||
}{
|
||||
{StatusPassed, "PASSED"},
|
||||
{StatusFailed, "FAILED"},
|
||||
{StatusWarning, "WARNING"},
|
||||
{StatusSkipped, "SKIPPED"},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
result := tc.status.String()
|
||||
if result != tc.expected {
|
||||
t.Errorf("Status.String() = %q, expected %q", result, tc.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatPreflightReport(t *testing.T) {
|
||||
result := &PreflightResult{
|
||||
Checks: []PreflightCheck{
|
||||
{Name: "Test Check", Status: StatusPassed, Message: "OK"},
|
||||
},
|
||||
AllPassed: true,
|
||||
DatabaseInfo: &DatabaseInfo{
|
||||
Type: "postgres",
|
||||
Version: "PostgreSQL 15.0",
|
||||
Host: "localhost",
|
||||
Port: 5432,
|
||||
},
|
||||
StorageInfo: &StorageInfo{
|
||||
Type: "local",
|
||||
Path: "/backups",
|
||||
AvailableBytes: 10 * 1024 * 1024 * 1024,
|
||||
},
|
||||
}
|
||||
|
||||
report := FormatPreflightReport(result, "testdb", false)
|
||||
if report == "" {
|
||||
t.Error("Report should not be empty")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatPreflightReportPlain(t *testing.T) {
|
||||
result := &PreflightResult{
|
||||
Checks: []PreflightCheck{
|
||||
{Name: "Test Check", Status: StatusFailed, Message: "Connection failed"},
|
||||
},
|
||||
AllPassed: false,
|
||||
FailureCount: 1,
|
||||
}
|
||||
|
||||
report := FormatPreflightReportPlain(result, "testdb")
|
||||
if report == "" {
|
||||
t.Error("Report should not be empty")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatPreflightReportJSON(t *testing.T) {
|
||||
result := &PreflightResult{
|
||||
Checks: []PreflightCheck{},
|
||||
AllPassed: true,
|
||||
}
|
||||
|
||||
report, err := FormatPreflightReportJSON(result, "testdb")
|
||||
if err != nil {
|
||||
t.Errorf("FormatPreflightReportJSON() error = %v", err)
|
||||
}
|
||||
|
||||
if len(report) == 0 {
|
||||
t.Error("Report should not be empty")
|
||||
}
|
||||
|
||||
if report[0] != '{' {
|
||||
t.Error("Report should start with '{'")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user