diff --git a/Makefile b/Makefile index b938c87..a93892e 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,7 @@ clean: .PHONY: test test: @echo "Running tests..." - cd $(SOURCE_DIR) && go test ./... + cd $(SOURCE_DIR) && go test -v . # Format Go code .PHONY: fmt diff --git a/src/main_test.go b/src/main_test.go new file mode 100644 index 0000000..5e8b3dd --- /dev/null +++ b/src/main_test.go @@ -0,0 +1,285 @@ +package main + +import ( + "os" + "path/filepath" + "testing" +) + +func TestNewRouterBackup(t *testing.T) { + rb := NewRouterBackup("test-host", "test-user", "test-pass", "/test/key", 2222) + + if rb.hostname != "test-host" { + t.Errorf("Expected hostname 'test-host', got '%s'", rb.hostname) + } + if rb.username != "test-user" { + t.Errorf("Expected username 'test-user', got '%s'", rb.username) + } + if rb.password != "test-pass" { + t.Errorf("Expected password 'test-pass', got '%s'", rb.password) + } + if rb.keyFile != "/test/key" { + t.Errorf("Expected keyFile '/test/key', got '%s'", rb.keyFile) + } + if rb.port != 2222 { + t.Errorf("Expected port 2222, got %d", rb.port) + } +} + +func TestFindDefaultSSHKey(t *testing.T) { + // Create a temporary directory to simulate home directory + tempDir := t.TempDir() + sshDir := filepath.Join(tempDir, ".ssh") + err := os.MkdirAll(sshDir, 0755) + if err != nil { + t.Fatalf("Failed to create .ssh directory: %v", err) + } + + // Create a fake SSH key + keyPath := filepath.Join(sshDir, "id_rsa") + err = os.WriteFile(keyPath, []byte("fake-key"), 0600) + if err != nil { + t.Fatalf("Failed to create fake SSH key: %v", err) + } + + // Temporarily change HOME environment variable + originalHome := os.Getenv("HOME") + defer os.Setenv("HOME", originalHome) + os.Setenv("HOME", tempDir) + + result := findDefaultSSHKey() + if result != keyPath { + t.Errorf("Expected SSH key path '%s', got '%s'", keyPath, result) + } +} + +func TestFindDefaultSSHKeyNotFound(t *testing.T) { + // Create a temporary directory with no SSH keys + tempDir := t.TempDir() + + // Temporarily change HOME environment variable + originalHome := os.Getenv("HOME") + defer os.Setenv("HOME", originalHome) + os.Setenv("HOME", tempDir) + + result := findDefaultSSHKey() + if result != "" { + t.Errorf("Expected empty string when no SSH key found, got '%s'", result) + } +} + +func TestLoadConfig(t *testing.T) { + // Create a temporary config file + tempDir := t.TempDir() + configPath := filepath.Join(tempDir, "test-config.yaml") + + configContent := `types: + test-type: + commands: + - show version + - show status + +devices: + test-device: + user: testuser + type: test-type + direct-device: + user: directuser + commands: + - direct command +` + + err := os.WriteFile(configPath, []byte(configContent), 0644) + if err != nil { + t.Fatalf("Failed to create test config file: %v", err) + } + + config, err := loadConfig(configPath) + if err != nil { + t.Fatalf("Failed to load config: %v", err) + } + + // Test types section + if len(config.Types) != 1 { + t.Errorf("Expected 1 type, got %d", len(config.Types)) + } + + testType, exists := config.Types["test-type"] + if !exists { + t.Error("Expected 'test-type' to exist in types") + } + + if len(testType.Commands) != 2 { + t.Errorf("Expected 2 commands in test-type, got %d", len(testType.Commands)) + } + + // Test devices section + if len(config.Devices) != 2 { + t.Errorf("Expected 2 devices, got %d", len(config.Devices)) + } + + testDevice, exists := config.Devices["test-device"] + if !exists { + t.Error("Expected 'test-device' to exist in devices") + } + + if testDevice.User != "testuser" { + t.Errorf("Expected user 'testuser', got '%s'", testDevice.User) + } + + if testDevice.Type != "test-type" { + t.Errorf("Expected type 'test-type', got '%s'", testDevice.Type) + } +} + +func TestLoadConfigInvalidFile(t *testing.T) { + _, err := loadConfig("/nonexistent/config.yaml") + if err == nil { + t.Error("Expected error when loading nonexistent config file") + } +} + +func TestLoadConfigInvalidYAML(t *testing.T) { + tempDir := t.TempDir() + configPath := filepath.Join(tempDir, "invalid-config.yaml") + + // Create invalid YAML content + invalidYAML := `types: + test-type: + commands: + - show version + invalid: [unclosed +` + + err := os.WriteFile(configPath, []byte(invalidYAML), 0644) + if err != nil { + t.Fatalf("Failed to create invalid config file: %v", err) + } + + _, err = loadConfig(configPath) + if err == nil { + t.Error("Expected error when loading invalid YAML") + } +} + +func TestBackupCommandsDirectoryCreation(t *testing.T) { + rb := NewRouterBackup("test-host", "test-user", "", "", 22) + + tempDir := t.TempDir() + outputDir := filepath.Join(tempDir, "new-directory") + + // Test with empty commands to avoid SSH connection + err := rb.BackupCommands([]string{}, outputDir) + if err != nil { + t.Fatalf("BackupCommands failed: %v", err) + } + + // Check if directory was created + if _, err := os.Stat(outputDir); os.IsNotExist(err) { + t.Error("Expected output directory to be created") + } +} + +func TestBackupCommandsFileCreation(t *testing.T) { + rb := NewRouterBackup("test-host", "test-user", "", "", 22) + + tempDir := t.TempDir() + expectedFilePath := filepath.Join(tempDir, "test-host") + + // Test with empty commands to avoid SSH connection + err := rb.BackupCommands([]string{}, tempDir) + if err != nil { + t.Fatalf("BackupCommands failed: %v", err) + } + + // Check if file was created + if _, err := os.Stat(expectedFilePath); os.IsNotExist(err) { + t.Error("Expected output file to be created") + } +} + +// Benchmark tests +func BenchmarkNewRouterBackup(b *testing.B) { + for i := 0; i < b.N; i++ { + NewRouterBackup("bench-host", "bench-user", "bench-pass", "/bench/key", 22) + } +} + +func BenchmarkLoadConfig(b *testing.B) { + // Create a temporary config file + tempDir := b.TempDir() + configPath := filepath.Join(tempDir, "bench-config.yaml") + + configContent := `types: + srlinux: + commands: + - show version + - show platform linecard + +devices: + device1: + user: user1 + type: srlinux + device2: + user: user2 + type: srlinux +` + + err := os.WriteFile(configPath, []byte(configContent), 0644) + if err != nil { + b.Fatalf("Failed to create benchmark config file: %v", err) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := loadConfig(configPath) + if err != nil { + b.Fatalf("Failed to load config: %v", err) + } + } +} + +// Example test to demonstrate usage +func ExampleNewRouterBackup() { + rb := NewRouterBackup("example-host", "admin", "", "/home/user/.ssh/id_rsa", 22) + _ = rb // Use the router backup instance +} + +// Table-driven test for multiple scenarios +func TestRouterBackupCreation(t *testing.T) { + tests := []struct { + name string + hostname string + username string + password string + keyFile string + port int + }{ + {"Basic", "host1", "user1", "pass1", "/key1", 22}, + {"Custom Port", "host2", "user2", "pass2", "/key2", 2222}, + {"No Password", "host3", "user3", "", "/key3", 22}, + {"No Key", "host4", "user4", "pass4", "", 22}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rb := NewRouterBackup(tt.hostname, tt.username, tt.password, tt.keyFile, tt.port) + + if rb.hostname != tt.hostname { + t.Errorf("Expected hostname '%s', got '%s'", tt.hostname, rb.hostname) + } + if rb.username != tt.username { + t.Errorf("Expected username '%s', got '%s'", tt.username, rb.username) + } + if rb.password != tt.password { + t.Errorf("Expected password '%s', got '%s'", tt.password, rb.password) + } + if rb.keyFile != tt.keyFile { + t.Errorf("Expected keyFile '%s', got '%s'", tt.keyFile, rb.keyFile) + } + if rb.port != tt.port { + t.Errorf("Expected port %d, got %d", tt.port, rb.port) + } + }) + } +} \ No newline at end of file