package backup import ( "os" "path/filepath" "testing" "github.com/0xJacky/Nginx-UI/settings" "github.com/stretchr/testify/assert" cosylogger "github.com/uozi-tech/cosy/logger" cosysettings "github.com/uozi-tech/cosy/settings" ) func init() { // Initialize logging system to avoid nil pointer exceptions during tests cosylogger.Init("debug") // Clean up backup files at the start of tests cleanupBackupFiles() } // cleanupBackupFiles removes all backup files in the current directory func cleanupBackupFiles() { // Get current directory dir, err := os.Getwd() if err != nil { return } // Delete all backup files matches, err := filepath.Glob(filepath.Join(dir, "backup-*.zip")) if err == nil { for _, file := range matches { os.Remove(file) } } } // setupTestEnvironment creates a temporary environment for testing func setupTestEnvironment(t *testing.T) (string, func()) { // Create temporary test directory tempDir, err := os.MkdirTemp("", "backup-test-*") assert.NoError(t, err) // Set up necessary directories nginxDir := filepath.Join(tempDir, "nginx") nginxUIDir := filepath.Join(tempDir, "nginx-ui") configDir := filepath.Join(tempDir, "config") backupDir := filepath.Join(tempDir, "backup") // Create directories for _, dir := range []string{nginxDir, nginxUIDir, configDir, backupDir} { err = os.MkdirAll(dir, 0755) assert.NoError(t, err) } // Create some test files testFiles := map[string]string{ filepath.Join(nginxDir, "nginx.conf"): "user nginx;\nworker_processes auto;\n", filepath.Join(nginxUIDir, "config.json"): `{"version": "1.0", "settings": {"theme": "dark"}}`, } for file, content := range testFiles { err = os.WriteFile(file, []byte(content), 0644) assert.NoError(t, err) } // Save original configuration origNginxConfigDir := settings.NginxSettings.ConfigDir origNginxUIConfigPath := cosysettings.ConfPath // Set test configuration settings.NginxSettings.ConfigDir = nginxDir cosysettings.ConfPath = filepath.Join(configDir, "config.ini") // Return cleanup function cleanup := func() { // Restore original configuration settings.NginxSettings.ConfigDir = origNginxConfigDir cosysettings.ConfPath = origNginxUIConfigPath // Delete temporary directory os.RemoveAll(tempDir) } return tempDir, cleanup } // Test backup and restore functionality func TestBackupAndRestore(t *testing.T) { // Make sure backup files are cleaned up at the start and end of the test cleanupBackupFiles() defer cleanupBackupFiles() // Create test configuration tempDir, err := os.MkdirTemp("", "nginx-ui-backup-test-*") assert.NoError(t, err) defer os.RemoveAll(tempDir) // Create config file configPath := filepath.Join(tempDir, "config.ini") testConfig := []byte("[app]\nName = Nginx UI Test\n") err = os.WriteFile(configPath, testConfig, 0644) assert.NoError(t, err) // Create database file dbName := settings.DatabaseSettings.GetName() dbFile := dbName + ".db" dbPath := filepath.Join(tempDir, dbFile) testDB := []byte("CREATE TABLE users (id INT, name TEXT);") err = os.WriteFile(dbPath, testDB, 0644) assert.NoError(t, err) // Create nginx directory nginxConfigDir := filepath.Join(tempDir, "nginx") err = os.MkdirAll(nginxConfigDir, 0755) assert.NoError(t, err) // Create test nginx config testNginxContent := []byte("server {\n listen 80;\n server_name example.com;\n}\n") err = os.WriteFile(filepath.Join(nginxConfigDir, "nginx.conf"), testNginxContent, 0644) assert.NoError(t, err) // Setup settings for testing originalConfPath := cosysettings.ConfPath originalNginxConfigDir := settings.NginxSettings.ConfigDir cosysettings.ConfPath = configPath settings.NginxSettings.ConfigDir = nginxConfigDir // Restore original settings after test defer func() { cosysettings.ConfPath = originalConfPath settings.NginxSettings.ConfigDir = originalNginxConfigDir }() // Run backup result, err := Backup() assert.NoError(t, err) assert.NotEmpty(t, result.BackupContent) assert.NotEmpty(t, result.BackupName) assert.NotEmpty(t, result.AESKey) assert.NotEmpty(t, result.AESIv) // Save backup content to a temporary file for restore testing backupPath := filepath.Join(tempDir, result.BackupName) err = os.WriteFile(backupPath, result.BackupContent, 0644) assert.NoError(t, err) // Test restore functionality restoreDir, err := os.MkdirTemp("", "nginx-ui-restore-test-*") assert.NoError(t, err) defer os.RemoveAll(restoreDir) // Decode AES key and IV aesKey, err := DecodeFromBase64(result.AESKey) assert.NoError(t, err) aesIv, err := DecodeFromBase64(result.AESIv) assert.NoError(t, err) // Perform restore restoreResult, err := Restore(RestoreOptions{ BackupPath: backupPath, AESKey: aesKey, AESIv: aesIv, RestoreDir: restoreDir, RestoreNginx: true, RestoreNginxUI: true, VerifyHash: true, }) assert.NoError(t, err) assert.NotEmpty(t, restoreResult.RestoreDir) // Verify restored directories nginxUIDir := filepath.Join(restoreDir, NginxUIDir) nginxDir := filepath.Join(restoreDir, NginxDir) _, err = os.Stat(nginxUIDir) assert.NoError(t, err) _, err = os.Stat(nginxDir) assert.NoError(t, err) // Verify hash info exists _, err = os.Stat(filepath.Join(restoreDir, HashInfoFile)) assert.NoError(t, err) } // Test AES encryption/decryption func TestEncryptionDecryption(t *testing.T) { // Test data testData := []byte("This is a test message to encrypt and decrypt") // Create temp dir for testing testDir, err := os.MkdirTemp("", "nginx-ui-crypto-test-*") assert.NoError(t, err) defer os.RemoveAll(testDir) // Create test file testFile := filepath.Join(testDir, "test.txt") err = os.WriteFile(testFile, testData, 0644) assert.NoError(t, err) // Generate AES key and IV key, err := GenerateAESKey() assert.NoError(t, err) iv, err := GenerateIV() assert.NoError(t, err) // Test encrypt file err = encryptFile(testFile, key, iv) assert.NoError(t, err) // Read encrypted data encryptedData, err := os.ReadFile(testFile) assert.NoError(t, err) assert.NotEqual(t, string(testData), string(encryptedData)) // Test decrypt file err = decryptFile(testFile, key, iv) assert.NoError(t, err) // Read decrypted data decryptedData, err := os.ReadFile(testFile) assert.NoError(t, err) assert.Equal(t, string(testData), string(decryptedData)) } // Test AES direct encryption/decryption func TestAESEncryptDecrypt(t *testing.T) { // Generate key and IV key, err := GenerateAESKey() assert.NoError(t, err) iv, err := GenerateIV() assert.NoError(t, err) // Test data original := []byte("This is a test message for encryption and decryption") // Encrypt encrypted, err := AESEncrypt(original, key, iv) assert.NoError(t, err) assert.NotEqual(t, original, encrypted) // Decrypt decrypted, err := AESDecrypt(encrypted, key, iv) assert.NoError(t, err) assert.Equal(t, original, decrypted) } // Test Base64 encoding/decoding func TestEncodeDecodeBase64(t *testing.T) { original := []byte("Test data for base64 encoding") // Encode encoded := EncodeToBase64(original) // Decode decoded, err := DecodeFromBase64(encoded) assert.NoError(t, err) assert.Equal(t, original, decoded) } func TestGenerateAESKey(t *testing.T) { key, err := GenerateAESKey() assert.NoError(t, err) assert.Equal(t, 32, len(key)) } func TestGenerateIV(t *testing.T) { iv, err := GenerateIV() assert.NoError(t, err) assert.Equal(t, 16, len(iv)) } func TestEncryptDecryptFile(t *testing.T) { // Create temp directory tempDir, err := os.MkdirTemp("", "encrypt-file-test-*") assert.NoError(t, err) defer os.RemoveAll(tempDir) // Create test file testFile := filepath.Join(tempDir, "test.txt") testContent := []byte("This is test content for file encryption") err = os.WriteFile(testFile, testContent, 0644) assert.NoError(t, err) // Generate key and IV key, err := GenerateAESKey() assert.NoError(t, err) iv, err := GenerateIV() assert.NoError(t, err) // Encrypt file err = encryptFile(testFile, key, iv) assert.NoError(t, err) // Read encrypted content encryptedContent, err := os.ReadFile(testFile) assert.NoError(t, err) assert.NotEqual(t, testContent, encryptedContent) // Decrypt file err = decryptFile(testFile, key, iv) assert.NoError(t, err) // Read decrypted content decryptedContent, err := os.ReadFile(testFile) assert.NoError(t, err) assert.Equal(t, testContent, decryptedContent) } func TestBackupRestore(t *testing.T) { // Set up test environment tempDir, cleanup := setupTestEnvironment(t) defer cleanup() // Create a config.ini file since it's required for the test configDir := filepath.Join(tempDir, "config") configPath := filepath.Join(configDir, "config.ini") err := os.WriteFile(configPath, []byte("[app]\nName = Nginx UI Test\n"), 0644) assert.NoError(t, err) // Update Cosy settings path originalConfPath := cosysettings.ConfPath cosysettings.ConfPath = configPath defer func() { cosysettings.ConfPath = originalConfPath }() // Create backup backupResult, err := Backup() // If there's an error, log it but continue testing if err != nil { t.Logf("Backup failed with error: %v", err) t.Fail() return } assert.NotNil(t, backupResult.BackupContent) assert.NotEmpty(t, backupResult.BackupName) assert.NotEmpty(t, backupResult.AESKey) assert.NotEmpty(t, backupResult.AESIv) // Create temporary file for restore testing backupPath := filepath.Join(tempDir, backupResult.BackupName) err = os.WriteFile(backupPath, backupResult.BackupContent, 0644) assert.NoError(t, err) // Decode key and IV key, err := DecodeFromBase64(backupResult.AESKey) assert.NoError(t, err) iv, err := DecodeFromBase64(backupResult.AESIv) assert.NoError(t, err) // Create restore directory restoreDir := filepath.Join(tempDir, "restore") err = os.MkdirAll(restoreDir, 0755) assert.NoError(t, err) // Create restore options options := RestoreOptions{ BackupPath: backupPath, AESKey: key, AESIv: iv, RestoreDir: restoreDir, VerifyHash: true, // Avoid modifying the system RestoreNginx: false, RestoreNginxUI: false, } // Test restore result, err := Restore(options) if err != nil { t.Logf("Restore failed with error: %v", err) t.Fail() return } assert.Equal(t, restoreDir, result.RestoreDir) // If hash verification is enabled, check the result if options.VerifyHash { assert.True(t, result.HashMatch, "Hash verification should pass") } } func TestCreateZipArchive(t *testing.T) { // Create temp directories tempSourceDir, err := os.MkdirTemp("", "zip-source-test-*") assert.NoError(t, err) defer os.RemoveAll(tempSourceDir) // Create some test files testFiles := []string{"file1.txt", "file2.txt", "subdir/file3.txt"} testContent := []byte("Test content") for _, file := range testFiles { filePath := filepath.Join(tempSourceDir, file) dirPath := filepath.Dir(filePath) err = os.MkdirAll(dirPath, 0755) assert.NoError(t, err) err = os.WriteFile(filePath, testContent, 0644) assert.NoError(t, err) } // Create zip file zipPath := filepath.Join(tempSourceDir, "test.zip") err = createZipArchive(zipPath, tempSourceDir) assert.NoError(t, err) // Verify zip file was created _, err = os.Stat(zipPath) assert.NoError(t, err) // Extract to new directory to verify contents extractDir := filepath.Join(tempSourceDir, "extract") err = os.MkdirAll(extractDir, 0755) assert.NoError(t, err) err = extractZipArchive(zipPath, extractDir) assert.NoError(t, err) // Verify extracted files for _, file := range testFiles { extractedPath := filepath.Join(extractDir, file) content, err := os.ReadFile(extractedPath) assert.NoError(t, err) assert.Equal(t, testContent, content) } } func TestHashCalculation(t *testing.T) { // Create temp file tempFile, err := os.CreateTemp("", "hash-test-*.txt") assert.NoError(t, err) defer os.Remove(tempFile.Name()) // Write content testContent := []byte("Test content for hash calculation") _, err = tempFile.Write(testContent) assert.NoError(t, err) tempFile.Close() // Calculate hash hash, err := calculateFileHash(tempFile.Name()) assert.NoError(t, err) assert.NotEmpty(t, hash) // Calculate again to verify consistency hash2, err := calculateFileHash(tempFile.Name()) assert.NoError(t, err) assert.Equal(t, hash, hash2) // Modify file and check hash changes err = os.WriteFile(tempFile.Name(), []byte("Modified content"), 0644) assert.NoError(t, err) hash3, err := calculateFileHash(tempFile.Name()) assert.NoError(t, err) assert.NotEqual(t, hash, hash3) }