//go:build windows package config import ( "encoding/json" "os" "path/filepath" "strings" "testing" "gitgud.io/mike/mpv-manager/pkg/constants" "github.com/stretchr/testify/require" "golang.org/x/sys/windows" ) func holdConfigFile(t *testing.T, path string) func() { t.Helper() wide, err := windows.UTF16PtrFromString(path) require.NoError(t, err) handle, err := windows.CreateFile(wide, windows.GENERIC_READ, windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE, nil, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, 0) require.NoError(t, err) closed := false closeHandle := func() { if !closed { require.NoError(t, windows.CloseHandle(handle)) closed = true } } t.Cleanup(closeHandle) return closeHandle } func TestWindowsLocatorPublicationFailurePreservesConfig(t *testing.T) { root := t.TempDir() isolateConfigHome(t, root) _, err := Load() require.NoError(t, err) initialPath := GetInstallPath() old, err := os.ReadFile(getDefaultConfigPath()) require.NoError(t, err) unlock := holdConfigFile(t, getDefaultConfigPath()) target := filepath.Join(root, "custom-mpv") require.Error(t, SetInstallPath(target)) require.Equal(t, initialPath, GetInstallPath()) actual, err := os.ReadFile(getDefaultConfigPath()) require.NoError(t, err) require.Equal(t, old, actual) _, err = os.Stat(filepath.Join(target, constants.PortableConfigDir, constants.ConfigFileName)) require.True(t, os.IsNotExist(err)) unlock() require.NoError(t, Update(func(c *Config) error { c.LastCheck = "after failure"; return nil })) require.Equal(t, initialPath, GetInstallPath()) require.NoError(t, SetInstallPath(target)) resetGlobalConfig() loaded, err := Load() require.NoError(t, err) require.Equal(t, target, loaded.InstallPath) require.Equal(t, "after failure", loaded.LastCheck) } func TestWindowsPendingPublicationRecoversWithoutLosingEdits(t *testing.T) { for _, externalEdit := range []bool{false, true} { t.Run(map[bool]string{false: "recover", true: "preserve intervening edit"}[externalEdit], func(t *testing.T) { root := t.TempDir() isolateConfigHome(t, root) _, err := Load() require.NoError(t, err) target := filepath.Join(root, "custom-mpv") require.NoError(t, SetInstallPath(target)) custom := filepath.Join(target, constants.PortableConfigDir, constants.ConfigFileName) unlock := holdConfigFile(t, custom) err = Update(func(c *Config) error { c.LastCheck = "committed marker"; return nil }) require.ErrorContains(t, err, "publication committed") unlock() var pending windowsConfigLocator data, err := os.ReadFile(getDefaultConfigPath()) require.NoError(t, err) require.NoError(t, json.Unmarshal(data, &pending)) require.NotNil(t, pending.Pending) if externalEdit { require.NoError(t, os.WriteFile(custom, []byte("external editor bytes"), 0600)) } resetGlobalConfig() loaded, err := Load() if externalEdit { require.ErrorContains(t, err, "config changed during pending publication") actual, readErr := os.ReadFile(custom) require.NoError(t, readErr) require.Equal(t, "external editor bytes", string(actual)) locator, readErr := os.ReadFile(getDefaultConfigPath()) require.NoError(t, readErr) require.Equal(t, data, locator) } else { require.NoError(t, err) require.Equal(t, target, loaded.InstallPath) require.Equal(t, "committed marker", loaded.LastCheck) locator, readErr := os.ReadFile(getDefaultConfigPath()) require.NoError(t, readErr) require.False(t, strings.Contains(string(locator), "pending_config_write")) require.NoError(t, Update(func(c *Config) error { c.ManagerInPATH = true; return nil })) reloaded, reloadErr := Reload() require.NoError(t, reloadErr) require.Equal(t, "committed marker", reloaded.LastCheck) } }) } } func TestWindowsDefaultDestinationCaseAliasDoesNotCreatePendingPublication(t *testing.T) { isolateConfigHome(t, t.TempDir()) _, err := Load() require.NoError(t, err) target := strings.ToUpper(GetInstallPath()) require.NoError(t, SetInstallPath(target)) resetGlobalConfig() loaded, err := Load() require.NoError(t, err) require.Equal(t, target, loaded.InstallPath) data, err := os.ReadFile(getDefaultConfigPath()) require.NoError(t, err) require.NotContains(t, string(data), "pending_config_write") }