mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-11 02:15:48 +02:00
466 lines
12 KiB
Go
466 lines
12 KiB
Go
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)
|
|
}
|