fix(arch): address coderabbit cleanup findings
- commit
- 895ec2552a011037a204a9713c677257774f028c
- author
- brice · · 2026-06-25 16:14 UTC
- parent
- 573527d
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
10 files · +69 -29
M docs/config/index.md +3 -0
| @@ -592,6 +592,7 @@ Dumber automatically detects your GPU vendor (AMD/Intel/NVIDIA) and sets optimal | |||
| 592 | 592 | - **NVIDIA**: Uses `nvidia` driver with EGL platform | |
| 593 | 593 | ||
| 594 | 594 | **Example:** | |
| 595 | + | ||
| 595 | 596 | ```toml | |
| 596 | 597 | [media] | |
| 597 | 598 | hardware_decoding = "auto" # HW preferred, SW fallback | |
| @@ -600,6 +601,7 @@ show_diagnostics = false # Keep off for daily use | |||
| 600 | 601 | ``` | |
| 601 | 602 | ||
| 602 | 603 | **WebKit fallback GStreamer diagnostics:** | |
| 604 | + | ||
| 603 | 605 | ```toml | |
| 604 | 606 | [engine.webkit] | |
| 605 | 607 | gstreamer_debug_level = 3 | |
| @@ -608,6 +610,7 @@ gstreamer_debug_level = 3 | |||
| 608 | 610 | Set `media.show_diagnostics = false` and `engine.webkit.gstreamer_debug_level = 0` to return to normal mode. | |
| 609 | 611 | ||
| 610 | 612 | **Diagnostics CLI:** | |
| 613 | + | ||
| 611 | 614 | ```bash | |
| 612 | 615 | # Check GStreamer plugins and VA-API status | |
| 613 | 616 | dumber doctor --media | |
| @@ -592,6 +592,7 @@ Dumber automatically detects your GPU vendor (AMD/Intel/NVIDIA) and sets optimal | |||||
| 592 | - **NVIDIA**: Uses `nvidia` driver with EGL platform | 592 | - **NVIDIA**: Uses `nvidia` driver with EGL platform | ||
| 593 | 593 | ||||
| 594 | **Example:** | 594 | **Example:** | ||
| 595 | + | ||||
| 595 | ```toml | 596 | ```toml | ||
| 596 | [media] | 597 | [media] | ||
| 597 | hardware_decoding = "auto" # HW preferred, SW fallback | 598 | hardware_decoding = "auto" # HW preferred, SW fallback | ||
| @@ -600,6 +601,7 @@ show_diagnostics = false # Keep off for daily use | |||||
| 600 | ``` | 601 | ``` | ||
| 601 | 602 | ||||
| 602 | **WebKit fallback GStreamer diagnostics:** | 603 | **WebKit fallback GStreamer diagnostics:** | ||
| 604 | + | ||||
| 603 | ```toml | 605 | ```toml | ||
| 604 | [engine.webkit] | 606 | [engine.webkit] | ||
| 605 | gstreamer_debug_level = 3 | 607 | gstreamer_debug_level = 3 | ||
| @@ -608,6 +610,7 @@ gstreamer_debug_level = 3 | |||||
| 608 | Set `media.show_diagnostics = false` and `engine.webkit.gstreamer_debug_level = 0` to return to normal mode. | 610 | Set `media.show_diagnostics = false` and `engine.webkit.gstreamer_debug_level = 0` to return to normal mode. | ||
| 609 | 611 | ||||
| 610 | **Diagnostics CLI:** | 612 | **Diagnostics CLI:** | ||
| 613 | + | ||||
| 611 | ```bash | 614 | ```bash | ||
| 612 | # Check GStreamer plugins and VA-API status | 615 | # Check GStreamer plugins and VA-API status | ||
| 613 | dumber doctor --media | 616 | dumber doctor --media | ||
M docs/reference/troubleshooting.md +5 -6
| @@ -14,14 +14,13 @@ dumber doctor --media # GStreamer/VA-API only | |||
| 14 | 14 | ||
| 15 | 15 | ### Browser won't start | |
| 16 | 16 | ||
| 17 | - | **Symptoms:** Error about missing GUI runtime libraries or GTK/WebKitGTK fallback version | |
| 17 | + | **Symptoms:** Error about the default CEF runtime, GTK libraries, or WebKitGTK fallback version. | |
| 18 | 18 | ||
| 19 | 19 | **Solution:** | |
| 20 | - | 1. Run `dumber doctor --runtime` | |
| 21 | - | 2. Install missing dependencies: | |
| 22 | - | - Arch: `pacman -S cef webkitgtk-6.0 gtk4` | |
| 23 | - | - Fedora: install GTK4 and WebKitGTK packages from your distribution and a CEF runtime/package if available | |
| 24 | - | - Ubuntu: `apt install libwebkitgtk-6.0-4 libgtk-4-1` and provide a CEF runtime for the default backend | |
| 20 | + | 1. Run `dumber doctor --runtime`. | |
| 21 | + | 2. Match the fix to the backend in the error: | |
| 22 | + | - **Default CEF backend:** install or provide a CEF runtime. On Arch, use `pacman -S cef`. On Fedora or Ubuntu, install an available CEF runtime/package or set `engine.cef.cef_dir` to a downloaded runtime. | |
| 23 | + | - **GTK/WebKit fallback backend:** install GTK4 and WebKitGTK. On Arch, use `pacman -S webkitgtk-6.0 gtk4`. On Fedora, install GTK4 and WebKitGTK packages from your distribution. On Ubuntu, use `apt install libwebkitgtk-6.0-4 libgtk-4-1`. | |
| 25 | 24 | ||
| 26 | 25 | ### Video playback issues | |
| 27 | 26 | ||
| @@ -14,14 +14,13 @@ dumber doctor --media # GStreamer/VA-API only | |||||
| 14 | 14 | ||||
| 15 | ### Browser won't start | 15 | ### Browser won't start | ||
| 16 | 16 | ||||
| 17 | - | **Symptoms:** Error about missing GUI runtime libraries or GTK/WebKitGTK fallback version | 17 | + | **Symptoms:** Error about the default CEF runtime, GTK libraries, or WebKitGTK fallback version. |
| 18 | 18 | ||||
| 19 | **Solution:** | 19 | **Solution:** | ||
| 20 | - | 1. Run `dumber doctor --runtime` | 20 | + | 1. Run `dumber doctor --runtime`. |
| 21 | - | 2. Install missing dependencies: | 21 | + | 2. Match the fix to the backend in the error: |
| 22 | - | - Arch: `pacman -S cef webkitgtk-6.0 gtk4` | 22 | + | - **Default CEF backend:** install or provide a CEF runtime. On Arch, use `pacman -S cef`. On Fedora or Ubuntu, install an available CEF runtime/package or set `engine.cef.cef_dir` to a downloaded runtime. |
| 23 | - | - Fedora: install GTK4 and WebKitGTK packages from your distribution and a CEF runtime/package if available | 23 | + | - **GTK/WebKit fallback backend:** install GTK4 and WebKitGTK. On Arch, use `pacman -S webkitgtk-6.0 gtk4`. On Fedora, install GTK4 and WebKitGTK packages from your distribution. On Ubuntu, use `apt install libwebkitgtk-6.0-4 libgtk-4-1`. |
| 24 | - | - Ubuntu: `apt install libwebkitgtk-6.0-4 libgtk-4-1` and provide a CEF runtime for the default backend | |||
| 25 | 24 | ||||
| 26 | ### Video playback issues | 25 | ### Video playback issues | ||
| 27 | 26 | ||||
M internal/application/usecase/navigation_url_normalizer.go +2 -1
| @@ -2,6 +2,7 @@ package usecase | |||
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "context" | |
| 5 | + | stdurl "net/url" | |
| 5 | 6 | "strings" | |
| 6 | 7 | ||
| 7 | 8 | "github.com/bnema/dumber/internal/application/port" | |
| @@ -27,7 +28,7 @@ func (n *NavigationURLNormalizer) Normalize(ctx context.Context, input string) s | |||
| 27 | 28 | } | |
| 28 | 29 | if n != nil && n.localPaths != nil && shouldProbeLocalPath(input) { | |
| 29 | 30 | if absPath, ok, err := n.localPaths.ResolveExistingPath(ctx, input); err == nil && ok { | |
| 30 | - | return "file://" + absPath | |
| 31 | + | return (&stdurl.URL{Scheme: "file", Path: absPath}).String() | |
| 31 | 32 | } | |
| 32 | 33 | } | |
| 33 | 34 | return domainurl.Normalize(input) | |
| @@ -2,6 +2,7 @@ package usecase | |||||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "context" | 4 | "context" | ||
| 5 | + | stdurl "net/url" | |||
| 5 | "strings" | 6 | "strings" | ||
| 6 | 7 | ||||
| 7 | "github.com/bnema/dumber/internal/application/port" | 8 | "github.com/bnema/dumber/internal/application/port" | ||
| @@ -27,7 +28,7 @@ func (n *NavigationURLNormalizer) Normalize(ctx context.Context, input string) s | |||||
| 27 | } | 28 | } | ||
| 28 | if n != nil && n.localPaths != nil && shouldProbeLocalPath(input) { | 29 | if n != nil && n.localPaths != nil && shouldProbeLocalPath(input) { | ||
| 29 | if absPath, ok, err := n.localPaths.ResolveExistingPath(ctx, input); err == nil && ok { | 30 | if absPath, ok, err := n.localPaths.ResolveExistingPath(ctx, input); err == nil && ok { | ||
| 30 | - | return "file://" + absPath | 31 | + | return (&stdurl.URL{Scheme: "file", Path: absPath}).String() |
| 31 | } | 32 | } | ||
| 32 | } | 33 | } | ||
| 33 | return domainurl.Normalize(input) | 34 | return domainurl.Normalize(input) | ||
M internal/application/usecase/navigation_url_normalizer_test.go +2 -14
| @@ -3,23 +3,11 @@ package usecase | |||
| 3 | 3 | import ( | |
| 4 | 4 | "context" | |
| 5 | 5 | "errors" | |
| 6 | + | stdurl "net/url" | |
| 6 | 7 | "path/filepath" | |
| 7 | 8 | "testing" | |
| 8 | 9 | ) | |
| 9 | 10 | ||
| 10 | - | type fakeLocalPathResolver struct { | |
| 11 | - | paths map[string]string | |
| 12 | - | err error | |
| 13 | - | } | |
| 14 | - | ||
| 15 | - | func (r fakeLocalPathResolver) ResolveExistingPath(_ context.Context, input string) (string, bool, error) { | |
| 16 | - | if r.err != nil { | |
| 17 | - | return "", false, r.err | |
| 18 | - | } | |
| 19 | - | abs, ok := r.paths[input] | |
| 20 | - | return abs, ok, nil | |
| 21 | - | } | |
| 22 | - | ||
| 23 | 11 | func TestNavigationURLNormalizerResolvesExistingLocalPaths(t *testing.T) { | |
| 24 | 12 | ctx := context.Background() | |
| 25 | 13 | absFile := filepath.Join(string(filepath.Separator), "tmp", "page.html") | |
| @@ -32,7 +20,7 @@ func TestNavigationURLNormalizerResolvesExistingLocalPaths(t *testing.T) { | |||
| 32 | 20 | for _, input := range []string{absFile, "page.html", "~/page.html"} { | |
| 33 | 21 | t.Run(input, func(t *testing.T) { | |
| 34 | 22 | got := normalizer.Normalize(ctx, input) | |
| 35 | - | want := "file://" + absFile | |
| 23 | + | want := (&stdurl.URL{Scheme: "file", Path: absFile}).String() | |
| 36 | 24 | if got != want { | |
| 37 | 25 | t.Fatalf("Normalize(%q) = %q, want %q", input, got, want) | |
| 38 | 26 | } | |
| @@ -3,23 +3,11 @@ package usecase | |||||
| 3 | import ( | 3 | import ( | ||
| 4 | "context" | 4 | "context" | ||
| 5 | "errors" | 5 | "errors" | ||
| 6 | + | stdurl "net/url" | |||
| 6 | "path/filepath" | 7 | "path/filepath" | ||
| 7 | "testing" | 8 | "testing" | ||
| 8 | ) | 9 | ) | ||
| 9 | 10 | ||||
| 10 | - | type fakeLocalPathResolver struct { | |||
| 11 | - | paths map[string]string | |||
| 12 | - | err error | |||
| 13 | - | } | |||
| 14 | - | ||||
| 15 | - | func (r fakeLocalPathResolver) ResolveExistingPath(_ context.Context, input string) (string, bool, error) { | |||
| 16 | - | if r.err != nil { | |||
| 17 | - | return "", false, r.err | |||
| 18 | - | } | |||
| 19 | - | abs, ok := r.paths[input] | |||
| 20 | - | return abs, ok, nil | |||
| 21 | - | } | |||
| 22 | - | ||||
| 23 | func TestNavigationURLNormalizerResolvesExistingLocalPaths(t *testing.T) { | 11 | func TestNavigationURLNormalizerResolvesExistingLocalPaths(t *testing.T) { | ||
| 24 | ctx := context.Background() | 12 | ctx := context.Background() | ||
| 25 | absFile := filepath.Join(string(filepath.Separator), "tmp", "page.html") | 13 | absFile := filepath.Join(string(filepath.Separator), "tmp", "page.html") | ||
| @@ -32,7 +20,7 @@ func TestNavigationURLNormalizerResolvesExistingLocalPaths(t *testing.T) { | |||||
| 32 | for _, input := range []string{absFile, "page.html", "~/page.html"} { | 20 | for _, input := range []string{absFile, "page.html", "~/page.html"} { | ||
| 33 | t.Run(input, func(t *testing.T) { | 21 | t.Run(input, func(t *testing.T) { | ||
| 34 | got := normalizer.Normalize(ctx, input) | 22 | got := normalizer.Normalize(ctx, input) | ||
| 35 | - | want := "file://" + absFile | 23 | + | want := (&stdurl.URL{Scheme: "file", Path: absFile}).String() |
| 36 | if got != want { | 24 | if got != want { | ||
| 37 | t.Fatalf("Normalize(%q) = %q, want %q", input, got, want) | 25 | t.Fatalf("Normalize(%q) = %q, want %q", input, got, want) | ||
| 38 | } | 26 | } | ||
M internal/application/usecase/navigation_url_usecase_integration_test.go +13 -0
| @@ -8,6 +8,19 @@ import ( | |||
| 8 | 8 | "github.com/bnema/dumber/internal/domain/entity" | |
| 9 | 9 | ) | |
| 10 | 10 | ||
| 11 | + | type fakeLocalPathResolver struct { | |
| 12 | + | paths map[string]string | |
| 13 | + | err error | |
| 14 | + | } | |
| 15 | + | ||
| 16 | + | func (r fakeLocalPathResolver) ResolveExistingPath(_ context.Context, input string) (string, bool, error) { | |
| 17 | + | if r.err != nil { | |
| 18 | + | return "", false, r.err | |
| 19 | + | } | |
| 20 | + | abs, ok := r.paths[input] | |
| 21 | + | return abs, ok, nil | |
| 22 | + | } | |
| 23 | + | ||
| 11 | 24 | func TestManageTabsCreateResolvesExistingLocalInitialURL(t *testing.T) { | |
| 12 | 25 | ctx := context.Background() | |
| 13 | 26 | tabs := entity.NewTabList() | |
| @@ -8,6 +8,19 @@ import ( | |||||
| 8 | "github.com/bnema/dumber/internal/domain/entity" | 8 | "github.com/bnema/dumber/internal/domain/entity" | ||
| 9 | ) | 9 | ) | ||
| 10 | 10 | ||||
| 11 | + | type fakeLocalPathResolver struct { | |||
| 12 | + | paths map[string]string | |||
| 13 | + | err error | |||
| 14 | + | } | |||
| 15 | + | ||||
| 16 | + | func (r fakeLocalPathResolver) ResolveExistingPath(_ context.Context, input string) (string, bool, error) { | |||
| 17 | + | if r.err != nil { | |||
| 18 | + | return "", false, r.err | |||
| 19 | + | } | |||
| 20 | + | abs, ok := r.paths[input] | |||
| 21 | + | return abs, ok, nil | |||
| 22 | + | } | |||
| 23 | + | ||||
| 11 | func TestManageTabsCreateResolvesExistingLocalInitialURL(t *testing.T) { | 24 | func TestManageTabsCreateResolvesExistingLocalInitialURL(t *testing.T) { | ||
| 12 | ctx := context.Background() | 25 | ctx := context.Background() | ||
| 13 | tabs := entity.NewTabList() | 26 | tabs := entity.NewTabList() | ||
M internal/infrastructure/config/loader.go +3 -3
| @@ -325,10 +325,10 @@ func (m *Manager) checkLegacyFormat() error { | |||
| 325 | 325 | ||
| 326 | 326 | if hasOldSections && !hasEngineSection { | |
| 327 | 327 | return fmt.Errorf( | |
| 328 | - | "config format outdated: legacy engine keys in [rendering], [performance], " + | |
| 329 | - | "[privacy], [runtime], or migrated [media] fields have moved to " + | |
| 330 | - | "[engine]/[engine.webkit] — run \"dumber config migrate\" " + | |
| 328 | + | "config format outdated: legacy engine keys in %s have moved to "+ | |
| 329 | + | "[engine]/[engine.webkit] — run \"dumber config migrate\" "+ | |
| 331 | 330 | "to update your config file", | |
| 331 | + | legacyEngineInputSectionsMessage(), | |
| 332 | 332 | ) | |
| 333 | 333 | } | |
| 334 | 334 | return nil | |
| @@ -325,10 +325,10 @@ func (m *Manager) checkLegacyFormat() error { | |||||
| 325 | 325 | ||||
| 326 | if hasOldSections && !hasEngineSection { | 326 | if hasOldSections && !hasEngineSection { | ||
| 327 | return fmt.Errorf( | 327 | return fmt.Errorf( | ||
| 328 | - | "config format outdated: legacy engine keys in [rendering], [performance], " + | 328 | + | "config format outdated: legacy engine keys in %s have moved to "+ |
| 329 | - | "[privacy], [runtime], or migrated [media] fields have moved to " + | 329 | + | "[engine]/[engine.webkit] — run \"dumber config migrate\" "+ |
| 330 | - | "[engine]/[engine.webkit] — run \"dumber config migrate\" " + | |||
| 331 | "to update your config file", | 330 | "to update your config file", | ||
| 331 | + | legacyEngineInputSectionsMessage(), | |||
| 332 | ) | 332 | ) | ||
| 333 | } | 333 | } | ||
| 334 | return nil | 334 | return nil | ||
M internal/infrastructure/config/migrate_engine.go +27 -3
| @@ -2,9 +2,11 @@ package config | |||
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "fmt" | |
| 5 | - | "github.com/pelletier/go-toml/v2" | |
| 6 | 5 | "io/fs" | |
| 7 | 6 | "os" | |
| 7 | + | "strings" | |
| 8 | + | ||
| 9 | + | "github.com/pelletier/go-toml/v2" | |
| 8 | 10 | ) | |
| 9 | 11 | ||
| 10 | 12 | // configFilePermissions is the file permission used when writing migrated config files. | |
| @@ -119,6 +121,28 @@ func legacyEngineSectionsToRemove() []string { | |||
| 119 | 121 | return sections | |
| 120 | 122 | } | |
| 121 | 123 | ||
| 124 | + | func legacyEngineInputSections() []string { | |
| 125 | + | sections := make([]string, 0) | |
| 126 | + | seen := make(map[string]struct{}) | |
| 127 | + | for _, alias := range legacyEngineAliases { | |
| 128 | + | if _, ok := seen[alias.legacySection]; ok { | |
| 129 | + | continue | |
| 130 | + | } | |
| 131 | + | seen[alias.legacySection] = struct{}{} | |
| 132 | + | sections = append(sections, alias.legacySection) | |
| 133 | + | } | |
| 134 | + | return sections | |
| 135 | + | } | |
| 136 | + | ||
| 137 | + | func legacyEngineInputSectionsMessage() string { | |
| 138 | + | sections := legacyEngineInputSections() | |
| 139 | + | formatted := make([]string, 0, len(sections)) | |
| 140 | + | for _, section := range sections { | |
| 141 | + | formatted = append(formatted, "["+section+"]") | |
| 142 | + | } | |
| 143 | + | return strings.Join(formatted, ", ") | |
| 144 | + | } | |
| 145 | + | ||
| 122 | 146 | func hasLegacyEngineAlias(raw map[string]any, alias legacyEngineAlias) bool { | |
| 123 | 147 | sectionAny, exists := raw[alias.legacySection] | |
| 124 | 148 | if !exists { | |
| @@ -222,9 +246,9 @@ func applyEngineMappings(raw map[string]any) { | |||
| 222 | 246 | continue | |
| 223 | 247 | } | |
| 224 | 248 | switch alias.targetSection { | |
| 225 | - | case "engine": | |
| 249 | + | case engineTargetSection: | |
| 226 | 250 | engineMap[alias.targetField] = val | |
| 227 | - | case "engine.webkit": | |
| 251 | + | case engineWebKitTargetSection: | |
| 228 | 252 | webkitMap[alias.targetField] = val | |
| 229 | 253 | } | |
| 230 | 254 | } | |
| @@ -2,9 +2,11 @@ package config | |||||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "fmt" | 4 | "fmt" | ||
| 5 | - | "github.com/pelletier/go-toml/v2" | |||
| 6 | "io/fs" | 5 | "io/fs" | ||
| 7 | "os" | 6 | "os" | ||
| 7 | + | "strings" | |||
| 8 | + | ||||
| 9 | + | "github.com/pelletier/go-toml/v2" | |||
| 8 | ) | 10 | ) | ||
| 9 | 11 | ||||
| 10 | // configFilePermissions is the file permission used when writing migrated config files. | 12 | // configFilePermissions is the file permission used when writing migrated config files. | ||
| @@ -119,6 +121,28 @@ func legacyEngineSectionsToRemove() []string { | |||||
| 119 | return sections | 121 | return sections | ||
| 120 | } | 122 | } | ||
| 121 | 123 | ||||
| 124 | + | func legacyEngineInputSections() []string { | |||
| 125 | + | sections := make([]string, 0) | |||
| 126 | + | seen := make(map[string]struct{}) | |||
| 127 | + | for _, alias := range legacyEngineAliases { | |||
| 128 | + | if _, ok := seen[alias.legacySection]; ok { | |||
| 129 | + | continue | |||
| 130 | + | } | |||
| 131 | + | seen[alias.legacySection] = struct{}{} | |||
| 132 | + | sections = append(sections, alias.legacySection) | |||
| 133 | + | } | |||
| 134 | + | return sections | |||
| 135 | + | } | |||
| 136 | + | ||||
| 137 | + | func legacyEngineInputSectionsMessage() string { | |||
| 138 | + | sections := legacyEngineInputSections() | |||
| 139 | + | formatted := make([]string, 0, len(sections)) | |||
| 140 | + | for _, section := range sections { | |||
| 141 | + | formatted = append(formatted, "["+section+"]") | |||
| 142 | + | } | |||
| 143 | + | return strings.Join(formatted, ", ") | |||
| 144 | + | } | |||
| 145 | + | ||||
| 122 | func hasLegacyEngineAlias(raw map[string]any, alias legacyEngineAlias) bool { | 146 | func hasLegacyEngineAlias(raw map[string]any, alias legacyEngineAlias) bool { | ||
| 123 | sectionAny, exists := raw[alias.legacySection] | 147 | sectionAny, exists := raw[alias.legacySection] | ||
| 124 | if !exists { | 148 | if !exists { | ||
| @@ -222,9 +246,9 @@ func applyEngineMappings(raw map[string]any) { | |||||
| 222 | continue | 246 | continue | ||
| 223 | } | 247 | } | ||
| 224 | switch alias.targetSection { | 248 | switch alias.targetSection { | ||
| 225 | - | case "engine": | 249 | + | case engineTargetSection: |
| 226 | engineMap[alias.targetField] = val | 250 | engineMap[alias.targetField] = val | ||
| 227 | - | case "engine.webkit": | 251 | + | case engineWebKitTargetSection: |
| 228 | webkitMap[alias.targetField] = val | 252 | webkitMap[alias.targetField] = val | ||
| 229 | } | 253 | } | ||
| 230 | } | 254 | } | ||
M internal/infrastructure/config/schema_provider_test.go +2 -0
| @@ -37,6 +37,8 @@ func configurationReferenceKeys(t *testing.T) map[string]struct{} { | |||
| 37 | 37 | data, err := os.ReadFile(docPath) | |
| 38 | 38 | require.NoError(t, err) | |
| 39 | 39 | ||
| 40 | + | // Keep this split point aligned with the markdown heading so legacy-key rows | |
| 41 | + | // stay out of the canonical schema key coverage set. | |
| 40 | 42 | canonicalReference, _, _ := strings.Cut(string(data), "Legacy key migration:") | |
| 41 | 43 | rowRE := regexp.MustCompile(`(?m)^\| ` + "`" + `([^` + "`" + `]+)` + "`" + ` \|`) | |
| 42 | 44 | keys := make(map[string]struct{}) | |
| @@ -37,6 +37,8 @@ func configurationReferenceKeys(t *testing.T) map[string]struct{} { | |||||
| 37 | data, err := os.ReadFile(docPath) | 37 | data, err := os.ReadFile(docPath) | ||
| 38 | require.NoError(t, err) | 38 | require.NoError(t, err) | ||
| 39 | 39 | ||||
| 40 | + | // Keep this split point aligned with the markdown heading so legacy-key rows | |||
| 41 | + | // stay out of the canonical schema key coverage set. | |||
| 40 | canonicalReference, _, _ := strings.Cut(string(data), "Legacy key migration:") | 42 | canonicalReference, _, _ := strings.Cut(string(data), "Legacy key migration:") | ||
| 41 | rowRE := regexp.MustCompile(`(?m)^\| ` + "`" + `([^` + "`" + `]+)` + "`" + ` \|`) | 43 | rowRE := regexp.MustCompile(`(?m)^\| ` + "`" + `([^` + "`" + `]+)` + "`" + ` \|`) | ||
| 42 | keys := make(map[string]struct{}) | 44 | keys := make(map[string]struct{}) | ||
M internal/infrastructure/config/validation_test.go +2 -0
| @@ -295,6 +295,7 @@ func TestValidateConfig_CEFConfig(t *testing.T) { | |||
| 295 | 295 | } | |
| 296 | 296 | ||
| 297 | 297 | func TestValidateConfig_WorkspaceNewPaneURLAllowsExistingLocalPathLikeValues(t *testing.T) { | |
| 298 | + | // This test mutates process CWD; do not add t.Parallel here. | |
| 298 | 299 | tmpDir := t.TempDir() | |
| 299 | 300 | oldWD, err := os.Getwd() | |
| 300 | 301 | require.NoError(t, err) | |
| @@ -330,6 +331,7 @@ func TestValidateConfig_WorkspaceNewPaneURLRejectsMissingLocalPathLikeValues(t * | |||
| 330 | 331 | } | |
| 331 | 332 | ||
| 332 | 333 | func TestValidateConfig_WorkspaceNewPaneURLAllowsExistingBareRelativeFile(t *testing.T) { | |
| 334 | + | // This test mutates process CWD; do not add t.Parallel here. | |
| 333 | 335 | tmpDir := t.TempDir() | |
| 334 | 336 | oldWD, err := os.Getwd() | |
| 335 | 337 | require.NoError(t, err) | |
| @@ -295,6 +295,7 @@ func TestValidateConfig_CEFConfig(t *testing.T) { | |||||
| 295 | } | 295 | } | ||
| 296 | 296 | ||||
| 297 | func TestValidateConfig_WorkspaceNewPaneURLAllowsExistingLocalPathLikeValues(t *testing.T) { | 297 | func TestValidateConfig_WorkspaceNewPaneURLAllowsExistingLocalPathLikeValues(t *testing.T) { | ||
| 298 | + | // This test mutates process CWD; do not add t.Parallel here. | |||
| 298 | tmpDir := t.TempDir() | 299 | tmpDir := t.TempDir() | ||
| 299 | oldWD, err := os.Getwd() | 300 | oldWD, err := os.Getwd() | ||
| 300 | require.NoError(t, err) | 301 | require.NoError(t, err) | ||
| @@ -330,6 +331,7 @@ func TestValidateConfig_WorkspaceNewPaneURLRejectsMissingLocalPathLikeValues(t * | |||||
| 330 | } | 331 | } | ||
| 331 | 332 | ||||
| 332 | func TestValidateConfig_WorkspaceNewPaneURLAllowsExistingBareRelativeFile(t *testing.T) { | 333 | func TestValidateConfig_WorkspaceNewPaneURLAllowsExistingBareRelativeFile(t *testing.T) { | ||
| 334 | + | // This test mutates process CWD; do not add t.Parallel here. | |||
| 333 | tmpDir := t.TempDir() | 335 | tmpDir := t.TempDir() | ||
| 334 | oldWD, err := os.Getwd() | 336 | oldWD, err := os.Getwd() | ||
| 335 | require.NoError(t, err) | 337 | require.NoError(t, err) | ||
M internal/infrastructure/filesystem/local_path_resolver_test.go +10 -2
| @@ -15,6 +15,10 @@ func TestResolveExistingPath(t *testing.T) { | |||
| 15 | 15 | if err := os.WriteFile(file, []byte("ok"), 0644); err != nil { | |
| 16 | 16 | t.Fatal(err) | |
| 17 | 17 | } | |
| 18 | + | wantFile, err := filepath.EvalSymlinks(file) | |
| 19 | + | if err != nil { | |
| 20 | + | t.Fatal(err) | |
| 21 | + | } | |
| 18 | 22 | ||
| 19 | 23 | oldWD, err := os.Getwd() | |
| 20 | 24 | if err != nil { | |
| @@ -45,8 +49,12 @@ func TestResolveExistingPath(t *testing.T) { | |||
| 45 | 49 | if !ok { | |
| 46 | 50 | t.Fatalf("ResolveExistingPath ok=false") | |
| 47 | 51 | } | |
| 48 | - | if got != file { | |
| 49 | - | t.Fatalf("ResolveExistingPath(%q) = %q, want %q", tt.input, got, file) | |
| 52 | + | gotFile, err := filepath.EvalSymlinks(got) | |
| 53 | + | if err != nil { | |
| 54 | + | t.Fatal(err) | |
| 55 | + | } | |
| 56 | + | if gotFile != wantFile { | |
| 57 | + | t.Fatalf("ResolveExistingPath(%q) = %q, want %q", tt.input, gotFile, wantFile) | |
| 50 | 58 | } | |
| 51 | 59 | }) | |
| 52 | 60 | } | |
| @@ -15,6 +15,10 @@ func TestResolveExistingPath(t *testing.T) { | |||||
| 15 | if err := os.WriteFile(file, []byte("ok"), 0644); err != nil { | 15 | if err := os.WriteFile(file, []byte("ok"), 0644); err != nil { | ||
| 16 | t.Fatal(err) | 16 | t.Fatal(err) | ||
| 17 | } | 17 | } | ||
| 18 | + | wantFile, err := filepath.EvalSymlinks(file) | |||
| 19 | + | if err != nil { | |||
| 20 | + | t.Fatal(err) | |||
| 21 | + | } | |||
| 18 | 22 | ||||
| 19 | oldWD, err := os.Getwd() | 23 | oldWD, err := os.Getwd() | ||
| 20 | if err != nil { | 24 | if err != nil { | ||
| @@ -45,8 +49,12 @@ func TestResolveExistingPath(t *testing.T) { | |||||
| 45 | if !ok { | 49 | if !ok { | ||
| 46 | t.Fatalf("ResolveExistingPath ok=false") | 50 | t.Fatalf("ResolveExistingPath ok=false") | ||
| 47 | } | 51 | } | ||
| 48 | - | if got != file { | 52 | + | gotFile, err := filepath.EvalSymlinks(got) |
| 49 | - | t.Fatalf("ResolveExistingPath(%q) = %q, want %q", tt.input, got, file) | 53 | + | if err != nil { |
| 54 | + | t.Fatal(err) | |||
| 55 | + | } | |||
| 56 | + | if gotFile != wantFile { | |||
| 57 | + | t.Fatalf("ResolveExistingPath(%q) = %q, want %q", tt.input, gotFile, wantFile) | |||
| 50 | } | 58 | } | ||
| 51 | }) | 59 | }) | ||
| 52 | } | 60 | } | ||