123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466 |
- 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)
- }
|