package e2e import ( "fmt" "os" "os/exec" "path/filepath" "strings" "testing" ) // TestConfigHotSwap verifies that changing the configuration file on disk // does not affect an active session. A process joining an existing session // should use the established tunnel's state, not the updated file. func TestConfigHotSwap(t *testing.T) { binaryPath, err := GetBinaryPath() if err != nil { t.Skipf("Skipping test: %v", err) } tmpRuntimeDir := t.TempDir() tmpConfigDir := t.TempDir() profile := "hotswap-test" profilesDir := filepath.Join(tmpConfigDir, "wg-wrap", "profiles") if err := os.MkdirAll(profilesDir, 0755); err != nil { t.Fatal(err) } profileConfPath := filepath.Join(profilesDir, profile+".conf") // 1. Initial configuration conf1 := `[Interface] Address = 10.0.0.2/24 PrivateKey = 0000000000000000000000000000000000000000000000000000000000000000 [Peer] PublicKey = 0000000000000000000000000000000000000000000000000000000000000000 AllowedIPs = 0.0.0.0/0 Endpoint = 1.1.1.1:51820 ` if err := os.WriteFile(profileConfPath, []byte(conf1), 0644); err != nil { t.Fatal(err) } // Start a process to establish the session cmdA := exec.Command(binaryPath, "--profile", profile, "--", "sleep", "1.0") cmdA.Env = append(os.Environ(), fmt.Sprintf("XDG_RUNTIME_DIR=%s", tmpRuntimeDir), fmt.Sprintf("XDG_CONFIG_HOME=%s", tmpConfigDir), "WG_WRAP_VERBOSE=1", ) if err := cmdA.Start(); err != nil { t.Fatalf("Failed to start Process A: %v", err) } defer func() { _ = cmdA.Process.Kill() }() waitForLifecycle(t, binaryPath, tmpRuntimeDir, profile, true) // 2. "Hot-Swap" the configuration file while the tunnel is active. // We change the endpoint to something obviously different. conf2 := `[Interface] Address = 10.0.0.2/24 PrivateKey = 0000000000000000000000000000000000000000000000000000000000000000 [Peer] PublicKey = 0000000000000000000000000000000000000000000000000000000000000000 AllowedIPs = 0.0.0.0/0 Endpoint = 8.8.8.8:51820 ` if err := os.WriteFile(profileConfPath, []byte(conf2), 0644); err != nil { t.Fatal(err) } // 3. Launch a second process. It should join the existing session // regardless of the fact that the .conf file has changed. cmdB := exec.Command(binaryPath, "--profile", profile, "--", "ls") cmdB.Env = append(os.Environ(), fmt.Sprintf("XDG_RUNTIME_DIR=%s", tmpRuntimeDir), fmt.Sprintf("XDG_CONFIG_HOME=%s", tmpConfigDir), "WG_WRAP_VERBOSE=1", ) out, err := cmdB.CombinedOutput() if err != nil { t.Fatalf("Process B failed to join session after config change: %v\nOutput: %s", err, string(out)) } if !strings.Contains(string(out), "Joining active WireGuard tunnel") { t.Errorf("Expected Process B to join active tunnel, but it re-initialized. Output: %s", string(out)) } }