test(config): guard reference docs against schema drift
- commit
- 32b8423545df43d94a5ab54b2625f414411b9c44
- author
- brice · · 2026-06-25 12:23 UTC
- parent
- 41c038a
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +83 -4
M docs/reference/configuration.md +19 -4
| @@ -31,6 +31,7 @@ | |||
| 31 | 31 | | `appearance.sans_font` | string | `Fira Sans` | | | |
| 32 | 32 | | `appearance.serif_font` | string | `Fira Sans` | | | |
| 33 | 33 | | `appearance.monospace_font` | string | `Fira Code` | | | |
| 34 | + | | `appearance.gtk_font` | string | `Adwaita Sans` | | | |
| 34 | 35 | | `appearance.default_font_size` | int | `16` | | | |
| 35 | 36 | | `appearance.color_scheme` | string | `default` | `prefer-dark`, `prefer-light`, `default` | | |
| 36 | 37 | | `appearance.external_theme.enabled` | bool | `false` | | | |
| @@ -52,7 +53,13 @@ | |||
| 52 | 53 | | `appearance.dark_palette.accent` | string | `#a8a8a8` | | | |
| 53 | 54 | | `appearance.dark_palette.border` | string | `#363636` | | | |
| 54 | 55 | | `debug.enable_devtools` | bool | `true` | | | |
| 56 | + | | `engine.cef.log_file` | string | `` | CEF runtime log path | | |
| 57 | + | | `engine.cef.log_severity` | int32 | `0` | `0`, `1`, `2`, `3`, `4`, `99` | | |
| 58 | + | | `engine.cef.trace_handlers` | bool | `false` | | | |
| 59 | + | | `engine.cef.enable_audio_handler` | bool | `true` | experimental | | |
| 55 | 60 | | `engine.type` | string | `cef` | `cef`, `webkit` (CEF default; WebKitGTK fallback) | | |
| 61 | + | | `engine.cookie_policy` | string | `always` | `always`, `no_third_party`, `never` | | |
| 62 | + | | `engine.webkit.itp_enabled` | bool | `true` | WebKit fallback only | | |
| 56 | 63 | | `engine.cef.render_stack` | string | `vulkan` | `vulkan`, `egl` | | |
| 57 | 64 | | `engine.cef.adaptive_windowless_frame_rate` | bool | `true` | | | |
| 58 | 65 | | `engine.cef.windowless_frame_rate` | int32 | `0` | >= 0 | | |
| @@ -79,21 +86,24 @@ | |||
| 79 | 86 | | `engine.webkit.gl_rendering_mode` | string | `auto` | `auto`, `gles2`, `gl3`, `none` (WebKit fallback only) | | |
| 80 | 87 | | `engine.webkit.gstreamer_debug_level` | int | `0` | `0-5` (WebKit fallback only) | | |
| 81 | 88 | | `default_ui_scale` | float | `1.0` | > 0 | | |
| 82 | - | ||
| 83 | - | Touchpad vertical scroll speed is controlled by `engine.cef.input.scroll_precise_multiplier` and the additional axis-specific `engine.cef.input.scroll_vertical_multiplier`. `engine.cef.input.touchpad_navigation_max_vertical_ratio` only filters horizontal back/forward swipe recognition; it does not tune vertical scroll speed. | |
| 84 | - | ||
| 85 | - | `engine.cef.input.touchpad_navigation_min_delta` is measured in raw GTK touchpad surface units. The default `200.0` matches WebKit-style commit distance to reduce accidental back/forward navigation and can be overridden in `config.toml`. | |
| 86 | 89 | | `default_webpage_zoom` | float | `1.2` | > 0 | | |
| 90 | + | | `sidebar_width` | int | `320` | `0` or `280-380` | | |
| 87 | 91 | | `workspace.new_pane_url` | string | `about:blank` | | | |
| 88 | 92 | | `workspace.switch_to_tab_on_move` | bool | `true` | | | |
| 93 | + | | `workspace.tab_bar_position` | string | `bottom` | `top`, `bottom` | | |
| 94 | + | | `workspace.hide_tab_bar_when_single_tab` | bool | `true` | | | |
| 89 | 95 | | `workspace.pane_mode.activation_shortcut` | string | `ctrl+p` | | | |
| 90 | 96 | | `workspace.pane_mode.timeout_ms` | int | `3000` | | | |
| 97 | + | | `workspace.pane_mode.actions.<action>` | []string | see defaults | pane mode key mappings | | |
| 91 | 98 | | `workspace.tab_mode.activation_shortcut` | string | `ctrl+t` | | | |
| 92 | 99 | | `workspace.tab_mode.timeout_ms` | int | `3000` | | | |
| 100 | + | | `workspace.tab_mode.actions.<action>` | []string | see defaults | tab mode key mappings | | |
| 93 | 101 | | `workspace.resize_mode.activation_shortcut` | string | `ctrl+n` | | | |
| 94 | 102 | | `workspace.resize_mode.timeout_ms` | int | `3000` | | | |
| 103 | + | | `workspace.resize_mode.actions.<action>` | []string | see defaults | resize mode key mappings | | |
| 95 | 104 | | `workspace.resize_mode.step_percent` | float | `5.0` | | | |
| 96 | 105 | | `workspace.resize_mode.min_pane_percent` | float | `10.0` | | | |
| 106 | + | | `workspace.shortcuts.actions` | map | see defaults | global shortcut action mappings | | |
| 97 | 107 | | `workspace.shortcuts.actions.toggle_floating_pane.keys` | []string | `alt+f` | key strings | | |
| 98 | 108 | | `workspace.shortcuts.actions.toggle_floating_pane.desc` | string | `Toggle floating pane` | | | |
| 99 | 109 | | `workspace.floating_pane.width_pct` | float | `0.82` | `(0,1]` | | |
| @@ -123,6 +133,7 @@ Touchpad vertical scroll speed is controlled by `engine.cef.input.scroll_precise | |||
| 123 | 133 | | `session.max_exited_session_age_days` | int | `7` | | | |
| 124 | 134 | | `session.session_mode.activation_shortcut` | string | `ctrl+o` | | | |
| 125 | 135 | | `session.session_mode.timeout_ms` | int | `3000` | | | |
| 136 | + | | `session.session_mode.actions.<action>` | []string | see defaults | session mode key mappings | | |
| 126 | 137 | | `media.hardware_decoding` | string | `auto` | `auto`, `force`, `disable` | | |
| 127 | 138 | | `media.prefer_av1` | bool | `false` | | | |
| 128 | 139 | | `media.show_diagnostics` | bool | `false` | | | |
| @@ -150,6 +161,10 @@ Touchpad vertical scroll speed is controlled by `engine.cef.input.scroll_precise | |||
| 150 | 161 | | `engine.zoom_cache_size` | int | `256` | >= 0 | | |
| 151 | 162 | | `downloads.path` | string | `` | | | |
| 152 | 163 | ||
| 164 | + | Touchpad vertical scroll speed is controlled by `engine.cef.input.scroll_precise_multiplier` and the additional axis-specific `engine.cef.input.scroll_vertical_multiplier`. `engine.cef.input.touchpad_navigation_max_vertical_ratio` only filters horizontal back/forward swipe recognition; it does not tune vertical scroll speed. | |
| 165 | + | ||
| 166 | + | `engine.cef.input.touchpad_navigation_min_delta` is measured in raw GTK touchpad surface units. The default `200.0` matches WebKit-style commit distance to reduce accidental back/forward navigation and can be overridden in `config.toml`. | |
| 167 | + | ||
| 153 | 168 | CEF is the default engine. WebKitGTK is a fallback selected with `engine.type = "webkit"`; `engine.webkit.*` keys are WebKit-specific. | |
| 154 | 169 | ||
| 155 | 170 | Legacy key migration: | |
| @@ -31,6 +31,7 @@ | |||||
| 31 | | `appearance.sans_font` | string | `Fira Sans` | | | 31 | | `appearance.sans_font` | string | `Fira Sans` | | | ||
| 32 | | `appearance.serif_font` | string | `Fira Sans` | | | 32 | | `appearance.serif_font` | string | `Fira Sans` | | | ||
| 33 | | `appearance.monospace_font` | string | `Fira Code` | | | 33 | | `appearance.monospace_font` | string | `Fira Code` | | | ||
| 34 | + | | `appearance.gtk_font` | string | `Adwaita Sans` | | | |||
| 34 | | `appearance.default_font_size` | int | `16` | | | 35 | | `appearance.default_font_size` | int | `16` | | | ||
| 35 | | `appearance.color_scheme` | string | `default` | `prefer-dark`, `prefer-light`, `default` | | 36 | | `appearance.color_scheme` | string | `default` | `prefer-dark`, `prefer-light`, `default` | | ||
| 36 | | `appearance.external_theme.enabled` | bool | `false` | | | 37 | | `appearance.external_theme.enabled` | bool | `false` | | | ||
| @@ -52,7 +53,13 @@ | |||||
| 52 | | `appearance.dark_palette.accent` | string | `#a8a8a8` | | | 53 | | `appearance.dark_palette.accent` | string | `#a8a8a8` | | | ||
| 53 | | `appearance.dark_palette.border` | string | `#363636` | | | 54 | | `appearance.dark_palette.border` | string | `#363636` | | | ||
| 54 | | `debug.enable_devtools` | bool | `true` | | | 55 | | `debug.enable_devtools` | bool | `true` | | | ||
| 56 | + | | `engine.cef.log_file` | string | `` | CEF runtime log path | | |||
| 57 | + | | `engine.cef.log_severity` | int32 | `0` | `0`, `1`, `2`, `3`, `4`, `99` | | |||
| 58 | + | | `engine.cef.trace_handlers` | bool | `false` | | | |||
| 59 | + | | `engine.cef.enable_audio_handler` | bool | `true` | experimental | | |||
| 55 | | `engine.type` | string | `cef` | `cef`, `webkit` (CEF default; WebKitGTK fallback) | | 60 | | `engine.type` | string | `cef` | `cef`, `webkit` (CEF default; WebKitGTK fallback) | | ||
| 61 | + | | `engine.cookie_policy` | string | `always` | `always`, `no_third_party`, `never` | | |||
| 62 | + | | `engine.webkit.itp_enabled` | bool | `true` | WebKit fallback only | | |||
| 56 | | `engine.cef.render_stack` | string | `vulkan` | `vulkan`, `egl` | | 63 | | `engine.cef.render_stack` | string | `vulkan` | `vulkan`, `egl` | | ||
| 57 | | `engine.cef.adaptive_windowless_frame_rate` | bool | `true` | | | 64 | | `engine.cef.adaptive_windowless_frame_rate` | bool | `true` | | | ||
| 58 | | `engine.cef.windowless_frame_rate` | int32 | `0` | >= 0 | | 65 | | `engine.cef.windowless_frame_rate` | int32 | `0` | >= 0 | | ||
| @@ -79,21 +86,24 @@ | |||||
| 79 | | `engine.webkit.gl_rendering_mode` | string | `auto` | `auto`, `gles2`, `gl3`, `none` (WebKit fallback only) | | 86 | | `engine.webkit.gl_rendering_mode` | string | `auto` | `auto`, `gles2`, `gl3`, `none` (WebKit fallback only) | | ||
| 80 | | `engine.webkit.gstreamer_debug_level` | int | `0` | `0-5` (WebKit fallback only) | | 87 | | `engine.webkit.gstreamer_debug_level` | int | `0` | `0-5` (WebKit fallback only) | | ||
| 81 | | `default_ui_scale` | float | `1.0` | > 0 | | 88 | | `default_ui_scale` | float | `1.0` | > 0 | | ||
| 82 | - | ||||
| 83 | - | Touchpad vertical scroll speed is controlled by `engine.cef.input.scroll_precise_multiplier` and the additional axis-specific `engine.cef.input.scroll_vertical_multiplier`. `engine.cef.input.touchpad_navigation_max_vertical_ratio` only filters horizontal back/forward swipe recognition; it does not tune vertical scroll speed. | |||
| 84 | - | ||||
| 85 | - | `engine.cef.input.touchpad_navigation_min_delta` is measured in raw GTK touchpad surface units. The default `200.0` matches WebKit-style commit distance to reduce accidental back/forward navigation and can be overridden in `config.toml`. | |||
| 86 | | `default_webpage_zoom` | float | `1.2` | > 0 | | 89 | | `default_webpage_zoom` | float | `1.2` | > 0 | | ||
| 90 | + | | `sidebar_width` | int | `320` | `0` or `280-380` | | |||
| 87 | | `workspace.new_pane_url` | string | `about:blank` | | | 91 | | `workspace.new_pane_url` | string | `about:blank` | | | ||
| 88 | | `workspace.switch_to_tab_on_move` | bool | `true` | | | 92 | | `workspace.switch_to_tab_on_move` | bool | `true` | | | ||
| 93 | + | | `workspace.tab_bar_position` | string | `bottom` | `top`, `bottom` | | |||
| 94 | + | | `workspace.hide_tab_bar_when_single_tab` | bool | `true` | | | |||
| 89 | | `workspace.pane_mode.activation_shortcut` | string | `ctrl+p` | | | 95 | | `workspace.pane_mode.activation_shortcut` | string | `ctrl+p` | | | ||
| 90 | | `workspace.pane_mode.timeout_ms` | int | `3000` | | | 96 | | `workspace.pane_mode.timeout_ms` | int | `3000` | | | ||
| 97 | + | | `workspace.pane_mode.actions.<action>` | []string | see defaults | pane mode key mappings | | |||
| 91 | | `workspace.tab_mode.activation_shortcut` | string | `ctrl+t` | | | 98 | | `workspace.tab_mode.activation_shortcut` | string | `ctrl+t` | | | ||
| 92 | | `workspace.tab_mode.timeout_ms` | int | `3000` | | | 99 | | `workspace.tab_mode.timeout_ms` | int | `3000` | | | ||
| 100 | + | | `workspace.tab_mode.actions.<action>` | []string | see defaults | tab mode key mappings | | |||
| 93 | | `workspace.resize_mode.activation_shortcut` | string | `ctrl+n` | | | 101 | | `workspace.resize_mode.activation_shortcut` | string | `ctrl+n` | | | ||
| 94 | | `workspace.resize_mode.timeout_ms` | int | `3000` | | | 102 | | `workspace.resize_mode.timeout_ms` | int | `3000` | | | ||
| 103 | + | | `workspace.resize_mode.actions.<action>` | []string | see defaults | resize mode key mappings | | |||
| 95 | | `workspace.resize_mode.step_percent` | float | `5.0` | | | 104 | | `workspace.resize_mode.step_percent` | float | `5.0` | | | ||
| 96 | | `workspace.resize_mode.min_pane_percent` | float | `10.0` | | | 105 | | `workspace.resize_mode.min_pane_percent` | float | `10.0` | | | ||
| 106 | + | | `workspace.shortcuts.actions` | map | see defaults | global shortcut action mappings | | |||
| 97 | | `workspace.shortcuts.actions.toggle_floating_pane.keys` | []string | `alt+f` | key strings | | 107 | | `workspace.shortcuts.actions.toggle_floating_pane.keys` | []string | `alt+f` | key strings | | ||
| 98 | | `workspace.shortcuts.actions.toggle_floating_pane.desc` | string | `Toggle floating pane` | | | 108 | | `workspace.shortcuts.actions.toggle_floating_pane.desc` | string | `Toggle floating pane` | | | ||
| 99 | | `workspace.floating_pane.width_pct` | float | `0.82` | `(0,1]` | | 109 | | `workspace.floating_pane.width_pct` | float | `0.82` | `(0,1]` | | ||
| @@ -123,6 +133,7 @@ Touchpad vertical scroll speed is controlled by `engine.cef.input.scroll_precise | |||||
| 123 | | `session.max_exited_session_age_days` | int | `7` | | | 133 | | `session.max_exited_session_age_days` | int | `7` | | | ||
| 124 | | `session.session_mode.activation_shortcut` | string | `ctrl+o` | | | 134 | | `session.session_mode.activation_shortcut` | string | `ctrl+o` | | | ||
| 125 | | `session.session_mode.timeout_ms` | int | `3000` | | | 135 | | `session.session_mode.timeout_ms` | int | `3000` | | | ||
| 136 | + | | `session.session_mode.actions.<action>` | []string | see defaults | session mode key mappings | | |||
| 126 | | `media.hardware_decoding` | string | `auto` | `auto`, `force`, `disable` | | 137 | | `media.hardware_decoding` | string | `auto` | `auto`, `force`, `disable` | | ||
| 127 | | `media.prefer_av1` | bool | `false` | | | 138 | | `media.prefer_av1` | bool | `false` | | | ||
| 128 | | `media.show_diagnostics` | bool | `false` | | | 139 | | `media.show_diagnostics` | bool | `false` | | | ||
| @@ -150,6 +161,10 @@ Touchpad vertical scroll speed is controlled by `engine.cef.input.scroll_precise | |||||
| 150 | | `engine.zoom_cache_size` | int | `256` | >= 0 | | 161 | | `engine.zoom_cache_size` | int | `256` | >= 0 | | ||
| 151 | | `downloads.path` | string | `` | | | 162 | | `downloads.path` | string | `` | | | ||
| 152 | 163 | ||||
| 164 | + | Touchpad vertical scroll speed is controlled by `engine.cef.input.scroll_precise_multiplier` and the additional axis-specific `engine.cef.input.scroll_vertical_multiplier`. `engine.cef.input.touchpad_navigation_max_vertical_ratio` only filters horizontal back/forward swipe recognition; it does not tune vertical scroll speed. | |||
| 165 | + | ||||
| 166 | + | `engine.cef.input.touchpad_navigation_min_delta` is measured in raw GTK touchpad surface units. The default `200.0` matches WebKit-style commit distance to reduce accidental back/forward navigation and can be overridden in `config.toml`. | |||
| 167 | + | ||||
| 153 | CEF is the default engine. WebKitGTK is a fallback selected with `engine.type = "webkit"`; `engine.webkit.*` keys are WebKit-specific. | 168 | CEF is the default engine. WebKitGTK is a fallback selected with `engine.type = "webkit"`; `engine.webkit.*` keys are WebKit-specific. | ||
| 154 | 169 | ||||
| 155 | Legacy key migration: | 170 | Legacy key migration: | ||
M internal/infrastructure/config/schema_provider_test.go +64 -0
| @@ -1,13 +1,77 @@ | |||
| 1 | 1 | package config | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | + | "os" | |
| 5 | + | "path/filepath" | |
| 6 | + | "regexp" | |
| 7 | + | "runtime" | |
| 4 | 8 | "sort" | |
| 9 | + | "strings" | |
| 5 | 10 | "testing" | |
| 6 | 11 | ||
| 7 | 12 | "github.com/spf13/viper" | |
| 8 | 13 | "github.com/stretchr/testify/require" | |
| 9 | 14 | ) | |
| 10 | 15 | ||
| 16 | + | func TestConfigurationReferenceCoversSchemaKeys(t *testing.T) { | |
| 17 | + | docKeys := configurationReferenceKeys(t) | |
| 18 | + | ||
| 19 | + | var missing []string | |
| 20 | + | for _, key := range NewSchemaProvider().GetSchema() { | |
| 21 | + | if configurationReferenceCoversKey(docKeys, key.Key) { | |
| 22 | + | continue | |
| 23 | + | } | |
| 24 | + | missing = append(missing, key.Key) | |
| 25 | + | } | |
| 26 | + | ||
| 27 | + | sort.Strings(missing) | |
| 28 | + | require.Empty(t, missing, "docs/reference/configuration.md should cover every SchemaProvider key") | |
| 29 | + | } | |
| 30 | + | ||
| 31 | + | func configurationReferenceKeys(t *testing.T) map[string]struct{} { | |
| 32 | + | t.Helper() | |
| 33 | + | ||
| 34 | + | _, filename, _, ok := runtime.Caller(0) | |
| 35 | + | require.True(t, ok, "locate current test file") | |
| 36 | + | docPath := filepath.Join(filepath.Dir(filename), "..", "..", "..", "docs", "reference", "configuration.md") | |
| 37 | + | data, err := os.ReadFile(docPath) | |
| 38 | + | require.NoError(t, err) | |
| 39 | + | ||
| 40 | + | canonicalReference, _, _ := strings.Cut(string(data), "Legacy key migration:") | |
| 41 | + | rowRE := regexp.MustCompile(`(?m)^\| ` + "`" + `([^` + "`" + `]+)` + "`" + ` \|`) | |
| 42 | + | keys := make(map[string]struct{}) | |
| 43 | + | for _, match := range rowRE.FindAllStringSubmatch(canonicalReference, -1) { | |
| 44 | + | keys[match[1]] = struct{}{} | |
| 45 | + | } | |
| 46 | + | return keys | |
| 47 | + | } | |
| 48 | + | ||
| 49 | + | func configurationReferenceCoversKey(docKeys map[string]struct{}, schemaKey string) bool { | |
| 50 | + | if _, ok := docKeys[schemaKey]; ok { | |
| 51 | + | return true | |
| 52 | + | } | |
| 53 | + | ||
| 54 | + | if strings.HasSuffix(schemaKey, ".*") { | |
| 55 | + | prefix := strings.TrimSuffix(schemaKey, "*") | |
| 56 | + | for docKey := range docKeys { | |
| 57 | + | if strings.HasPrefix(docKey, prefix) { | |
| 58 | + | return true | |
| 59 | + | } | |
| 60 | + | } | |
| 61 | + | } | |
| 62 | + | ||
| 63 | + | if idx := strings.Index(schemaKey, "<"); idx >= 0 { | |
| 64 | + | prefix := schemaKey[:idx] | |
| 65 | + | for docKey := range docKeys { | |
| 66 | + | if strings.HasPrefix(docKey, prefix) { | |
| 67 | + | return true | |
| 68 | + | } | |
| 69 | + | } | |
| 70 | + | } | |
| 71 | + | ||
| 72 | + | return false | |
| 73 | + | } | |
| 74 | + | ||
| 11 | 75 | func TestSchemaProviderCoversViperDefaults(t *testing.T) { | |
| 12 | 76 | m := &Manager{viper: viper.New()} | |
| 13 | 77 | m.setDefaults() | |
| @@ -1,13 +1,77 @@ | |||||
| 1 | package config | 1 | package config | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | + | "os" | |||
| 5 | + | "path/filepath" | |||
| 6 | + | "regexp" | |||
| 7 | + | "runtime" | |||
| 4 | "sort" | 8 | "sort" | ||
| 9 | + | "strings" | |||
| 5 | "testing" | 10 | "testing" | ||
| 6 | 11 | ||||
| 7 | "github.com/spf13/viper" | 12 | "github.com/spf13/viper" | ||
| 8 | "github.com/stretchr/testify/require" | 13 | "github.com/stretchr/testify/require" | ||
| 9 | ) | 14 | ) | ||
| 10 | 15 | ||||
| 16 | + | func TestConfigurationReferenceCoversSchemaKeys(t *testing.T) { | |||
| 17 | + | docKeys := configurationReferenceKeys(t) | |||
| 18 | + | ||||
| 19 | + | var missing []string | |||
| 20 | + | for _, key := range NewSchemaProvider().GetSchema() { | |||
| 21 | + | if configurationReferenceCoversKey(docKeys, key.Key) { | |||
| 22 | + | continue | |||
| 23 | + | } | |||
| 24 | + | missing = append(missing, key.Key) | |||
| 25 | + | } | |||
| 26 | + | ||||
| 27 | + | sort.Strings(missing) | |||
| 28 | + | require.Empty(t, missing, "docs/reference/configuration.md should cover every SchemaProvider key") | |||
| 29 | + | } | |||
| 30 | + | ||||
| 31 | + | func configurationReferenceKeys(t *testing.T) map[string]struct{} { | |||
| 32 | + | t.Helper() | |||
| 33 | + | ||||
| 34 | + | _, filename, _, ok := runtime.Caller(0) | |||
| 35 | + | require.True(t, ok, "locate current test file") | |||
| 36 | + | docPath := filepath.Join(filepath.Dir(filename), "..", "..", "..", "docs", "reference", "configuration.md") | |||
| 37 | + | data, err := os.ReadFile(docPath) | |||
| 38 | + | require.NoError(t, err) | |||
| 39 | + | ||||
| 40 | + | canonicalReference, _, _ := strings.Cut(string(data), "Legacy key migration:") | |||
| 41 | + | rowRE := regexp.MustCompile(`(?m)^\| ` + "`" + `([^` + "`" + `]+)` + "`" + ` \|`) | |||
| 42 | + | keys := make(map[string]struct{}) | |||
| 43 | + | for _, match := range rowRE.FindAllStringSubmatch(canonicalReference, -1) { | |||
| 44 | + | keys[match[1]] = struct{}{} | |||
| 45 | + | } | |||
| 46 | + | return keys | |||
| 47 | + | } | |||
| 48 | + | ||||
| 49 | + | func configurationReferenceCoversKey(docKeys map[string]struct{}, schemaKey string) bool { | |||
| 50 | + | if _, ok := docKeys[schemaKey]; ok { | |||
| 51 | + | return true | |||
| 52 | + | } | |||
| 53 | + | ||||
| 54 | + | if strings.HasSuffix(schemaKey, ".*") { | |||
| 55 | + | prefix := strings.TrimSuffix(schemaKey, "*") | |||
| 56 | + | for docKey := range docKeys { | |||
| 57 | + | if strings.HasPrefix(docKey, prefix) { | |||
| 58 | + | return true | |||
| 59 | + | } | |||
| 60 | + | } | |||
| 61 | + | } | |||
| 62 | + | ||||
| 63 | + | if idx := strings.Index(schemaKey, "<"); idx >= 0 { | |||
| 64 | + | prefix := schemaKey[:idx] | |||
| 65 | + | for docKey := range docKeys { | |||
| 66 | + | if strings.HasPrefix(docKey, prefix) { | |||
| 67 | + | return true | |||
| 68 | + | } | |||
| 69 | + | } | |||
| 70 | + | } | |||
| 71 | + | ||||
| 72 | + | return false | |||
| 73 | + | } | |||
| 74 | + | ||||
| 11 | func TestSchemaProviderCoversViperDefaults(t *testing.T) { | 75 | func TestSchemaProviderCoversViperDefaults(t *testing.T) { | ||
| 12 | m := &Manager{viper: viper.New()} | 76 | m := &Manager{viper: viper.New()} | ||
| 13 | m.setDefaults() | 77 | m.setDefaults() | ||