package cmd import ( "context" "fmt" "runtime" "testing" "time" "github.com/kardianos/service" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) const ( serviceStartTimeout = 5 * time.Second serviceStopTimeout = 5 * time.Second ) // TestServiceLifecycle tests the complete service lifecycle func TestServiceLifecycle(t *testing.T) { originalServiceName := serviceName serviceName = "netbird-test-" + fmt.Sprintf("%d", time.Now().Unix()) defer func() { serviceName = originalServiceName }() configPath = "/tmp/netbird-test-config.json" logLevel = "info" daemonAddr = "unix:///tmp/netbird-test.sock" ctx := context.Background() t.Run("Install", func(t *testing.T) { installCmd.SetContext(ctx) err := installCmd.RunE(installCmd, []string{}) require.NoError(t, err) cfg, err := newSVCConfig() require.NoError(t, err) ctxSvc, cancel := context.WithCancel(context.Background()) defer cancel() s, err := newSVC(newProgram(ctxSvc, cancel), cfg) require.NoError(t, err) status, err := s.Status() assert.NoError(t, err) assert.NotEqual(t, service.StatusUnknown, status) }) t.Run("Start", func(t *testing.T) { startCmd.SetContext(ctx) err := startCmd.RunE(startCmd, []string{}) require.NoError(t, err) time.Sleep(serviceStartTimeout) running, err := isServiceRunning() require.NoError(t, err) assert.True(t, running) }) t.Run("Restart", func(t *testing.T) { restartCmd.SetContext(ctx) err := restartCmd.RunE(restartCmd, []string{}) require.NoError(t, err) time.Sleep(serviceStartTimeout) running, err := isServiceRunning() require.NoError(t, err) assert.True(t, running) }) t.Run("Reconfigure", func(t *testing.T) { originalLogLevel := logLevel logLevel = "debug" defer func() { logLevel = originalLogLevel }() reconfigureCmd.SetContext(ctx) err := reconfigureCmd.RunE(reconfigureCmd, []string{}) require.NoError(t, err) time.Sleep(serviceStartTimeout) running, err := isServiceRunning() require.NoError(t, err) assert.True(t, running) }) t.Run("Stop", func(t *testing.T) { stopCmd.SetContext(ctx) err := stopCmd.RunE(stopCmd, []string{}) require.NoError(t, err) time.Sleep(serviceStopTimeout) running, err := isServiceRunning() require.NoError(t, err) assert.False(t, running) }) t.Run("Uninstall", func(t *testing.T) { uninstallCmd.SetContext(ctx) err := uninstallCmd.RunE(uninstallCmd, []string{}) require.NoError(t, err) cfg, err := newSVCConfig() require.NoError(t, err) ctxSvc, cancel := context.WithCancel(context.Background()) defer cancel() s, err := newSVC(newProgram(ctxSvc, cancel), cfg) require.NoError(t, err) _, err = s.Status() assert.Error(t, err) }) } // TestServiceEnvVars tests environment variable parsing func TestServiceEnvVars(t *testing.T) { tests := []struct { name string envVars []string expected map[string]string expectErr bool }{ { name: "Valid single env var", envVars: []string{"LOG_LEVEL=debug"}, expected: map[string]string{ "LOG_LEVEL": "debug", }, }, { name: "Valid multiple env vars", envVars: []string{"LOG_LEVEL=debug", "CUSTOM_VAR=value"}, expected: map[string]string{ "LOG_LEVEL": "debug", "CUSTOM_VAR": "value", }, }, { name: "Env var with spaces", envVars: []string{" KEY = value "}, expected: map[string]string{ "KEY": "value", }, }, { name: "Invalid format - no equals", envVars: []string{"INVALID"}, expectErr: true, }, { name: "Invalid format - empty key", envVars: []string{"=value"}, expectErr: true, }, { name: "Empty value is valid", envVars: []string{"KEY="}, expected: map[string]string{ "KEY": "", }, }, { name: "Empty slice", envVars: []string{}, expected: map[string]string{}, }, { name: "Empty string in slice", envVars: []string{"", "KEY=value", ""}, expected: map[string]string{"KEY": "value"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result, err := parseServiceEnvVars(tt.envVars) if tt.expectErr { assert.Error(t, err) } else { require.NoError(t, err) assert.Equal(t, tt.expected, result) } }) } } // TestServiceConfigWithEnvVars tests service config creation with env vars func TestServiceConfigWithEnvVars(t *testing.T) { originalServiceName := serviceName originalServiceEnvVars := serviceEnvVars defer func() { serviceName = originalServiceName serviceEnvVars = originalServiceEnvVars }() serviceName = "test-service" serviceEnvVars = []string{"TEST_VAR=test_value", "ANOTHER_VAR=another_value"} cfg, err := newSVCConfig() require.NoError(t, err) assert.Equal(t, "test-service", cfg.Name) assert.Equal(t, "test_value", cfg.EnvVars["TEST_VAR"]) assert.Equal(t, "another_value", cfg.EnvVars["ANOTHER_VAR"]) if runtime.GOOS == "linux" { assert.Equal(t, "test-service", cfg.EnvVars["SYSTEMD_UNIT"]) } }