test(config): validate schema-backed engine settings
- commit
- dbc9709dd4e86f4c6e338abcc76e4d2d16376c97
- author
- brice · · 2026-06-25 12:31 UTC
- parent
- 32b8423
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +155 -0
M internal/infrastructure/config/validation.go +32 -0
| @@ -32,6 +32,7 @@ func validateConfig(config *Config) error { | |||
| 32 | 32 | validationErrors = append(validationErrors, validateLogging(config)...) | |
| 33 | 33 | validationErrors = append(validationErrors, validateWorkspaceNewPaneURL(config)...) | |
| 34 | 34 | validationErrors = append(validationErrors, validateOmnibox(config)...) | |
| 35 | + | validationErrors = append(validationErrors, validateEngine(config)...) | |
| 35 | 36 | validationErrors = append(validationErrors, validateRendering(config)...) | |
| 36 | 37 | validationErrors = append(validationErrors, validatePrivacy(config)...) | |
| 37 | 38 | validationErrors = append(validationErrors, validateColorScheme(config)...) | |
| @@ -508,6 +509,18 @@ func validateOmnibox(config *Config) []string { | |||
| 508 | 509 | return validationErrors | |
| 509 | 510 | } | |
| 510 | 511 | ||
| 512 | + | func validateEngine(config *Config) []string { | |
| 513 | + | switch config.Engine.Type { | |
| 514 | + | case "", EngineTypeCEF, EngineTypeWebKit: | |
| 515 | + | return nil | |
| 516 | + | default: | |
| 517 | + | return []string{fmt.Sprintf( | |
| 518 | + | "engine.type must be one of: cef, webkit (got: %s)", | |
| 519 | + | config.Engine.Type, | |
| 520 | + | )} | |
| 521 | + | } | |
| 522 | + | } | |
| 523 | + | ||
| 511 | 524 | func validateRendering(config *Config) []string { | |
| 512 | 525 | var validationErrors []string | |
| 513 | 526 | ||
| @@ -523,6 +536,25 @@ func validateRendering(config *Config) []string { | |||
| 523 | 536 | ) | |
| 524 | 537 | } | |
| 525 | 538 | ||
| 539 | + | switch config.Engine.WebKit.GLRenderingMode { | |
| 540 | + | case GLRenderingModeAuto, GLRenderingModeGLES2, GLRenderingModeGL3, GLRenderingModeNone, "": | |
| 541 | + | default: | |
| 542 | + | validationErrors = append( | |
| 543 | + | validationErrors, | |
| 544 | + | fmt.Sprintf( | |
| 545 | + | "engine.webkit.gl_rendering_mode must be one of: auto, gles2, gl3, none (got: %s)", | |
| 546 | + | config.Engine.WebKit.GLRenderingMode, | |
| 547 | + | ), | |
| 548 | + | ) | |
| 549 | + | } | |
| 550 | + | ||
| 551 | + | if config.Engine.WebKit.GStreamerDebugLevel < 0 || config.Engine.WebKit.GStreamerDebugLevel > 5 { | |
| 552 | + | validationErrors = append(validationErrors, fmt.Sprintf( | |
| 553 | + | "engine.webkit.gstreamer_debug_level must be between 0 and 5 (got: %d)", | |
| 554 | + | config.Engine.WebKit.GStreamerDebugLevel, | |
| 555 | + | )) | |
| 556 | + | } | |
| 557 | + | ||
| 526 | 558 | if config.Engine.WebKit.ForceCompositingMode && config.Engine.WebKit.DisableCompositingMode { | |
| 527 | 559 | validationErrors = append(validationErrors, | |
| 528 | 560 | "engine.webkit.force_compositing_mode and engine.webkit.disable_compositing_mode cannot both be true", | |
| @@ -32,6 +32,7 @@ func validateConfig(config *Config) error { | |||||
| 32 | validationErrors = append(validationErrors, validateLogging(config)...) | 32 | validationErrors = append(validationErrors, validateLogging(config)...) | ||
| 33 | validationErrors = append(validationErrors, validateWorkspaceNewPaneURL(config)...) | 33 | validationErrors = append(validationErrors, validateWorkspaceNewPaneURL(config)...) | ||
| 34 | validationErrors = append(validationErrors, validateOmnibox(config)...) | 34 | validationErrors = append(validationErrors, validateOmnibox(config)...) | ||
| 35 | + | validationErrors = append(validationErrors, validateEngine(config)...) | |||
| 35 | validationErrors = append(validationErrors, validateRendering(config)...) | 36 | validationErrors = append(validationErrors, validateRendering(config)...) | ||
| 36 | validationErrors = append(validationErrors, validatePrivacy(config)...) | 37 | validationErrors = append(validationErrors, validatePrivacy(config)...) | ||
| 37 | validationErrors = append(validationErrors, validateColorScheme(config)...) | 38 | validationErrors = append(validationErrors, validateColorScheme(config)...) | ||
| @@ -508,6 +509,18 @@ func validateOmnibox(config *Config) []string { | |||||
| 508 | return validationErrors | 509 | return validationErrors | ||
| 509 | } | 510 | } | ||
| 510 | 511 | ||||
| 512 | + | func validateEngine(config *Config) []string { | |||
| 513 | + | switch config.Engine.Type { | |||
| 514 | + | case "", EngineTypeCEF, EngineTypeWebKit: | |||
| 515 | + | return nil | |||
| 516 | + | default: | |||
| 517 | + | return []string{fmt.Sprintf( | |||
| 518 | + | "engine.type must be one of: cef, webkit (got: %s)", | |||
| 519 | + | config.Engine.Type, | |||
| 520 | + | )} | |||
| 521 | + | } | |||
| 522 | + | } | |||
| 523 | + | ||||
| 511 | func validateRendering(config *Config) []string { | 524 | func validateRendering(config *Config) []string { | ||
| 512 | var validationErrors []string | 525 | var validationErrors []string | ||
| 513 | 526 | ||||
| @@ -523,6 +536,25 @@ func validateRendering(config *Config) []string { | |||||
| 523 | ) | 536 | ) | ||
| 524 | } | 537 | } | ||
| 525 | 538 | ||||
| 539 | + | switch config.Engine.WebKit.GLRenderingMode { | |||
| 540 | + | case GLRenderingModeAuto, GLRenderingModeGLES2, GLRenderingModeGL3, GLRenderingModeNone, "": | |||
| 541 | + | default: | |||
| 542 | + | validationErrors = append( | |||
| 543 | + | validationErrors, | |||
| 544 | + | fmt.Sprintf( | |||
| 545 | + | "engine.webkit.gl_rendering_mode must be one of: auto, gles2, gl3, none (got: %s)", | |||
| 546 | + | config.Engine.WebKit.GLRenderingMode, | |||
| 547 | + | ), | |||
| 548 | + | ) | |||
| 549 | + | } | |||
| 550 | + | ||||
| 551 | + | if config.Engine.WebKit.GStreamerDebugLevel < 0 || config.Engine.WebKit.GStreamerDebugLevel > 5 { | |||
| 552 | + | validationErrors = append(validationErrors, fmt.Sprintf( | |||
| 553 | + | "engine.webkit.gstreamer_debug_level must be between 0 and 5 (got: %d)", | |||
| 554 | + | config.Engine.WebKit.GStreamerDebugLevel, | |||
| 555 | + | )) | |||
| 556 | + | } | |||
| 557 | + | ||||
| 526 | if config.Engine.WebKit.ForceCompositingMode && config.Engine.WebKit.DisableCompositingMode { | 558 | if config.Engine.WebKit.ForceCompositingMode && config.Engine.WebKit.DisableCompositingMode { | ||
| 527 | validationErrors = append(validationErrors, | 559 | validationErrors = append(validationErrors, | ||
| 528 | "engine.webkit.force_compositing_mode and engine.webkit.disable_compositing_mode cannot both be true", | 560 | "engine.webkit.force_compositing_mode and engine.webkit.disable_compositing_mode cannot both be true", | ||
M internal/infrastructure/config/validation_test.go +123 -0
| @@ -9,6 +9,59 @@ import ( | |||
| 9 | 9 | "github.com/stretchr/testify/require" | |
| 10 | 10 | ) | |
| 11 | 11 | ||
| 12 | + | func TestValidateConfig_EngineType(t *testing.T) { | |
| 13 | + | t.Setenv("DUMBER_ENGINE", "") | |
| 14 | + | ||
| 15 | + | tests := []struct { | |
| 16 | + | name string | |
| 17 | + | engineType string | |
| 18 | + | wantErr bool | |
| 19 | + | }{ | |
| 20 | + | {name: "cef", engineType: EngineTypeCEF, wantErr: false}, | |
| 21 | + | {name: "webkit", engineType: EngineTypeWebKit, wantErr: false}, | |
| 22 | + | {name: "empty defaults to cef", engineType: "", wantErr: false}, | |
| 23 | + | {name: "invalid", engineType: "netscape", wantErr: true}, | |
| 24 | + | } | |
| 25 | + | ||
| 26 | + | for _, tt := range tests { | |
| 27 | + | t.Run(tt.name, func(t *testing.T) { | |
| 28 | + | cfg := DefaultConfig() | |
| 29 | + | cfg.Engine.Type = tt.engineType | |
| 30 | + | ||
| 31 | + | err := validateConfig(cfg) | |
| 32 | + | if tt.wantErr { | |
| 33 | + | require.Error(t, err) | |
| 34 | + | assert.Contains(t, err.Error(), "engine.type") | |
| 35 | + | return | |
| 36 | + | } | |
| 37 | + | require.NoError(t, err) | |
| 38 | + | }) | |
| 39 | + | } | |
| 40 | + | } | |
| 41 | + | ||
| 42 | + | func TestValidateConfig_EngineTypeIgnoresEnvOverride(t *testing.T) { | |
| 43 | + | t.Run("invalid configured type is still rejected when env override is valid", func(t *testing.T) { | |
| 44 | + | t.Setenv("DUMBER_ENGINE", EngineTypeWebKit) | |
| 45 | + | cfg := DefaultConfig() | |
| 46 | + | cfg.Engine.Type = "netscape" | |
| 47 | + | ||
| 48 | + | err := validateConfig(cfg) | |
| 49 | + | ||
| 50 | + | require.Error(t, err) | |
| 51 | + | assert.Contains(t, err.Error(), "engine.type") | |
| 52 | + | }) | |
| 53 | + | ||
| 54 | + | t.Run("valid configured type is not rejected when env override is invalid", func(t *testing.T) { | |
| 55 | + | t.Setenv("DUMBER_ENGINE", "netscape") | |
| 56 | + | cfg := DefaultConfig() | |
| 57 | + | cfg.Engine.Type = EngineTypeCEF | |
| 58 | + | ||
| 59 | + | err := validateConfig(cfg) | |
| 60 | + | ||
| 61 | + | require.NoError(t, err) | |
| 62 | + | }) | |
| 63 | + | } | |
| 64 | + | ||
| 12 | 65 | func TestValidateConfig_EngineCookiePolicy(t *testing.T) { | |
| 13 | 66 | tests := []struct { | |
| 14 | 67 | name string | |
| @@ -37,6 +90,76 @@ func TestValidateConfig_EngineCookiePolicy(t *testing.T) { | |||
| 37 | 90 | } | |
| 38 | 91 | } | |
| 39 | 92 | ||
| 93 | + | func TestValidateConfig_WebKitMediaSettings(t *testing.T) { | |
| 94 | + | tests := []struct { | |
| 95 | + | name string | |
| 96 | + | mutate func(*Config) | |
| 97 | + | wantErr bool | |
| 98 | + | wantField string | |
| 99 | + | }{ | |
| 100 | + | { | |
| 101 | + | name: "valid gl rendering modes", | |
| 102 | + | mutate: func(cfg *Config) { | |
| 103 | + | cfg.Engine.WebKit.GLRenderingMode = GLRenderingModeGLES2 | |
| 104 | + | }, | |
| 105 | + | wantErr: false, | |
| 106 | + | }, | |
| 107 | + | { | |
| 108 | + | name: "invalid gl rendering mode", | |
| 109 | + | mutate: func(cfg *Config) { | |
| 110 | + | cfg.Engine.WebKit.GLRenderingMode = GLRenderingMode("metal") | |
| 111 | + | }, | |
| 112 | + | wantErr: true, | |
| 113 | + | wantField: "engine.webkit.gl_rendering_mode", | |
| 114 | + | }, | |
| 115 | + | { | |
| 116 | + | name: "gstreamer debug level lower bound", | |
| 117 | + | mutate: func(cfg *Config) { | |
| 118 | + | cfg.Engine.WebKit.GStreamerDebugLevel = 0 | |
| 119 | + | }, | |
| 120 | + | wantErr: false, | |
| 121 | + | }, | |
| 122 | + | { | |
| 123 | + | name: "gstreamer debug level upper bound", | |
| 124 | + | mutate: func(cfg *Config) { | |
| 125 | + | cfg.Engine.WebKit.GStreamerDebugLevel = 5 | |
| 126 | + | }, | |
| 127 | + | wantErr: false, | |
| 128 | + | }, | |
| 129 | + | { | |
| 130 | + | name: "negative gstreamer debug level", | |
| 131 | + | mutate: func(cfg *Config) { | |
| 132 | + | cfg.Engine.WebKit.GStreamerDebugLevel = -1 | |
| 133 | + | }, | |
| 134 | + | wantErr: true, | |
| 135 | + | wantField: "engine.webkit.gstreamer_debug_level", | |
| 136 | + | }, | |
| 137 | + | { | |
| 138 | + | name: "too high gstreamer debug level", | |
| 139 | + | mutate: func(cfg *Config) { | |
| 140 | + | cfg.Engine.WebKit.GStreamerDebugLevel = 6 | |
| 141 | + | }, | |
| 142 | + | wantErr: true, | |
| 143 | + | wantField: "engine.webkit.gstreamer_debug_level", | |
| 144 | + | }, | |
| 145 | + | } | |
| 146 | + | ||
| 147 | + | for _, tt := range tests { | |
| 148 | + | t.Run(tt.name, func(t *testing.T) { | |
| 149 | + | cfg := DefaultConfig() | |
| 150 | + | tt.mutate(cfg) | |
| 151 | + | ||
| 152 | + | err := validateConfig(cfg) | |
| 153 | + | if tt.wantErr { | |
| 154 | + | require.Error(t, err) | |
| 155 | + | assert.Contains(t, err.Error(), tt.wantField) | |
| 156 | + | return | |
| 157 | + | } | |
| 158 | + | require.NoError(t, err) | |
| 159 | + | }) | |
| 160 | + | } | |
| 161 | + | } | |
| 162 | + | ||
| 40 | 163 | func TestValidateConfig_CEFConfig(t *testing.T) { | |
| 41 | 164 | tests := []struct { | |
| 42 | 165 | name string | |
| @@ -9,6 +9,59 @@ import ( | |||||
| 9 | "github.com/stretchr/testify/require" | 9 | "github.com/stretchr/testify/require" | ||
| 10 | ) | 10 | ) | ||
| 11 | 11 | ||||
| 12 | + | func TestValidateConfig_EngineType(t *testing.T) { | |||
| 13 | + | t.Setenv("DUMBER_ENGINE", "") | |||
| 14 | + | ||||
| 15 | + | tests := []struct { | |||
| 16 | + | name string | |||
| 17 | + | engineType string | |||
| 18 | + | wantErr bool | |||
| 19 | + | }{ | |||
| 20 | + | {name: "cef", engineType: EngineTypeCEF, wantErr: false}, | |||
| 21 | + | {name: "webkit", engineType: EngineTypeWebKit, wantErr: false}, | |||
| 22 | + | {name: "empty defaults to cef", engineType: "", wantErr: false}, | |||
| 23 | + | {name: "invalid", engineType: "netscape", wantErr: true}, | |||
| 24 | + | } | |||
| 25 | + | ||||
| 26 | + | for _, tt := range tests { | |||
| 27 | + | t.Run(tt.name, func(t *testing.T) { | |||
| 28 | + | cfg := DefaultConfig() | |||
| 29 | + | cfg.Engine.Type = tt.engineType | |||
| 30 | + | ||||
| 31 | + | err := validateConfig(cfg) | |||
| 32 | + | if tt.wantErr { | |||
| 33 | + | require.Error(t, err) | |||
| 34 | + | assert.Contains(t, err.Error(), "engine.type") | |||
| 35 | + | return | |||
| 36 | + | } | |||
| 37 | + | require.NoError(t, err) | |||
| 38 | + | }) | |||
| 39 | + | } | |||
| 40 | + | } | |||
| 41 | + | ||||
| 42 | + | func TestValidateConfig_EngineTypeIgnoresEnvOverride(t *testing.T) { | |||
| 43 | + | t.Run("invalid configured type is still rejected when env override is valid", func(t *testing.T) { | |||
| 44 | + | t.Setenv("DUMBER_ENGINE", EngineTypeWebKit) | |||
| 45 | + | cfg := DefaultConfig() | |||
| 46 | + | cfg.Engine.Type = "netscape" | |||
| 47 | + | ||||
| 48 | + | err := validateConfig(cfg) | |||
| 49 | + | ||||
| 50 | + | require.Error(t, err) | |||
| 51 | + | assert.Contains(t, err.Error(), "engine.type") | |||
| 52 | + | }) | |||
| 53 | + | ||||
| 54 | + | t.Run("valid configured type is not rejected when env override is invalid", func(t *testing.T) { | |||
| 55 | + | t.Setenv("DUMBER_ENGINE", "netscape") | |||
| 56 | + | cfg := DefaultConfig() | |||
| 57 | + | cfg.Engine.Type = EngineTypeCEF | |||
| 58 | + | ||||
| 59 | + | err := validateConfig(cfg) | |||
| 60 | + | ||||
| 61 | + | require.NoError(t, err) | |||
| 62 | + | }) | |||
| 63 | + | } | |||
| 64 | + | ||||
| 12 | func TestValidateConfig_EngineCookiePolicy(t *testing.T) { | 65 | func TestValidateConfig_EngineCookiePolicy(t *testing.T) { | ||
| 13 | tests := []struct { | 66 | tests := []struct { | ||
| 14 | name string | 67 | name string | ||
| @@ -37,6 +90,76 @@ func TestValidateConfig_EngineCookiePolicy(t *testing.T) { | |||||
| 37 | } | 90 | } | ||
| 38 | } | 91 | } | ||
| 39 | 92 | ||||
| 93 | + | func TestValidateConfig_WebKitMediaSettings(t *testing.T) { | |||
| 94 | + | tests := []struct { | |||
| 95 | + | name string | |||
| 96 | + | mutate func(*Config) | |||
| 97 | + | wantErr bool | |||
| 98 | + | wantField string | |||
| 99 | + | }{ | |||
| 100 | + | { | |||
| 101 | + | name: "valid gl rendering modes", | |||
| 102 | + | mutate: func(cfg *Config) { | |||
| 103 | + | cfg.Engine.WebKit.GLRenderingMode = GLRenderingModeGLES2 | |||
| 104 | + | }, | |||
| 105 | + | wantErr: false, | |||
| 106 | + | }, | |||
| 107 | + | { | |||
| 108 | + | name: "invalid gl rendering mode", | |||
| 109 | + | mutate: func(cfg *Config) { | |||
| 110 | + | cfg.Engine.WebKit.GLRenderingMode = GLRenderingMode("metal") | |||
| 111 | + | }, | |||
| 112 | + | wantErr: true, | |||
| 113 | + | wantField: "engine.webkit.gl_rendering_mode", | |||
| 114 | + | }, | |||
| 115 | + | { | |||
| 116 | + | name: "gstreamer debug level lower bound", | |||
| 117 | + | mutate: func(cfg *Config) { | |||
| 118 | + | cfg.Engine.WebKit.GStreamerDebugLevel = 0 | |||
| 119 | + | }, | |||
| 120 | + | wantErr: false, | |||
| 121 | + | }, | |||
| 122 | + | { | |||
| 123 | + | name: "gstreamer debug level upper bound", | |||
| 124 | + | mutate: func(cfg *Config) { | |||
| 125 | + | cfg.Engine.WebKit.GStreamerDebugLevel = 5 | |||
| 126 | + | }, | |||
| 127 | + | wantErr: false, | |||
| 128 | + | }, | |||
| 129 | + | { | |||
| 130 | + | name: "negative gstreamer debug level", | |||
| 131 | + | mutate: func(cfg *Config) { | |||
| 132 | + | cfg.Engine.WebKit.GStreamerDebugLevel = -1 | |||
| 133 | + | }, | |||
| 134 | + | wantErr: true, | |||
| 135 | + | wantField: "engine.webkit.gstreamer_debug_level", | |||
| 136 | + | }, | |||
| 137 | + | { | |||
| 138 | + | name: "too high gstreamer debug level", | |||
| 139 | + | mutate: func(cfg *Config) { | |||
| 140 | + | cfg.Engine.WebKit.GStreamerDebugLevel = 6 | |||
| 141 | + | }, | |||
| 142 | + | wantErr: true, | |||
| 143 | + | wantField: "engine.webkit.gstreamer_debug_level", | |||
| 144 | + | }, | |||
| 145 | + | } | |||
| 146 | + | ||||
| 147 | + | for _, tt := range tests { | |||
| 148 | + | t.Run(tt.name, func(t *testing.T) { | |||
| 149 | + | cfg := DefaultConfig() | |||
| 150 | + | tt.mutate(cfg) | |||
| 151 | + | ||||
| 152 | + | err := validateConfig(cfg) | |||
| 153 | + | if tt.wantErr { | |||
| 154 | + | require.Error(t, err) | |||
| 155 | + | assert.Contains(t, err.Error(), tt.wantField) | |||
| 156 | + | return | |||
| 157 | + | } | |||
| 158 | + | require.NoError(t, err) | |||
| 159 | + | }) | |||
| 160 | + | } | |||
| 161 | + | } | |||
| 162 | + | ||||
| 40 | func TestValidateConfig_CEFConfig(t *testing.T) { | 163 | func TestValidateConfig_CEFConfig(t *testing.T) { | ||
| 41 | tests := []struct { | 164 | tests := []struct { | ||
| 42 | name string | 165 | name string | ||