github.com/m3db/m3@v1.5.0/src/cmd/services/m3query/config/config_test.go (about) 1 // Copyright (c) 2018 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package config 22 23 import ( 24 "fmt" 25 "testing" 26 "time" 27 28 "github.com/m3db/m3/src/query/api/v1/handler/prometheus/handleroptions" 29 "github.com/m3db/m3/src/query/models" 30 "github.com/m3db/m3/src/query/storage" 31 xconfig "github.com/m3db/m3/src/x/config" 32 xtime "github.com/m3db/m3/src/x/time" 33 34 "github.com/stretchr/testify/assert" 35 "github.com/stretchr/testify/require" 36 "gopkg.in/validator.v2" 37 "gopkg.in/yaml.v2" 38 ) 39 40 const testConfigFile = "./testdata/config.yml" 41 42 func TestDefaultTagOptionsFromEmptyConfig(t *testing.T) { 43 cfg := TagOptionsConfiguration{} 44 opts, err := TagOptionsFromConfig(cfg) 45 require.NoError(t, err) 46 require.Equal(t, models.TypeQuoted, opts.IDSchemeType()) 47 } 48 49 func TestTagOptionsFromConfigWithIDGenerationScheme(t *testing.T) { 50 schemes := []models.IDSchemeType{ 51 models.TypePrependMeta, 52 models.TypeQuoted, 53 } 54 for _, scheme := range schemes { 55 cfg := TagOptionsConfiguration{ 56 Scheme: scheme, 57 } 58 59 opts, err := TagOptionsFromConfig(cfg) 60 require.NoError(t, err) 61 require.NotNil(t, opts) 62 assert.Equal(t, []byte("__name__"), opts.MetricName()) 63 assert.Equal(t, scheme, opts.IDSchemeType()) 64 } 65 } 66 67 func TestTagOptionsFromConfig(t *testing.T) { 68 name := "foobar" 69 cfg := TagOptionsConfiguration{ 70 MetricName: name, 71 Scheme: models.TypeQuoted, 72 Filters: []TagFilter{ 73 {Name: "foo", Values: []string{".", "abc"}}, 74 {Name: "bar", Values: []string{".*"}}, 75 }, 76 } 77 opts, err := TagOptionsFromConfig(cfg) 78 require.NoError(t, err) 79 require.NotNil(t, opts) 80 assert.Equal(t, []byte(name), opts.MetricName()) 81 filters := opts.Filters() 82 exNames := [][]byte{[]byte("foo"), []byte("bar")} 83 exVals := [][]string{{".", "abc"}, {".*"}} 84 require.Equal(t, 2, len(filters)) 85 for i, f := range filters { 86 assert.Equal(t, exNames[i], f.Name) 87 for j, v := range f.Values { 88 assert.Equal(t, []byte(exVals[i][j]), v) 89 } 90 } 91 } 92 93 func TestConfigLoading(t *testing.T) { 94 var cfg Configuration 95 require.NoError(t, xconfig.LoadFile(&cfg, testConfigFile, xconfig.Options{})) 96 97 var requireExhaustive bool 98 requireExhaustive = true 99 assert.Equal(t, &LimitsConfiguration{ 100 PerQuery: PerQueryLimitsConfiguration{ 101 MaxFetchedSeries: 12000, 102 MaxFetchedDocs: 11000, 103 RequireExhaustive: &requireExhaustive, 104 }, 105 }, &cfg.Limits) 106 107 assert.Equal(t, HTTPConfiguration{EnableH2C: true}, cfg.HTTP) 108 109 expectedTimestamp, err := time.Parse(time.RFC3339, "2022-01-01T00:00:00Z") 110 require.NoError(t, err) 111 expectedPromConvertOptions := storage.NewPromConvertOptions(). 112 SetResolutionThresholdForCounterNormalization(10 * time.Minute). 113 SetValueDecreaseTolerance(0.0000000001). 114 SetValueDecreaseToleranceUntil(xtime.UnixNano(expectedTimestamp.UnixNano())) 115 actualPromConvertOptions := cfg.Query.Prometheus.ConvertOptionsOrDefault() 116 assert.Equal(t, expectedPromConvertOptions, actualPromConvertOptions) 117 118 // TODO: assert on more fields here. 119 } 120 121 func TestDefaultAsFetchOptionsBuilderLimitsOptions(t *testing.T) { 122 limits := LimitsConfiguration{} 123 assert.Equal(t, handleroptions.FetchOptionsBuilderLimitsOptions{ 124 SeriesLimit: defaultStorageQuerySeriesLimit, 125 InstanceMultiple: float32(0), 126 DocsLimit: defaultStorageQueryDocsLimit, 127 RangeLimit: 0, 128 RequireExhaustive: defaultRequireExhaustive, 129 MaxMetricMetadataStats: defaultMaxMetricMetadataStats, 130 }, limits.PerQuery.AsFetchOptionsBuilderLimitsOptions()) 131 } 132 133 func TestConfigValidation(t *testing.T) { 134 baseCfg := func(t *testing.T) *Configuration { 135 var cfg Configuration 136 require.NoError(t, xconfig.LoadFile(&cfg, testConfigFile, xconfig.Options{}), 137 "sample configuration is no longer valid or loadable. Fix it up to provide a base config here") 138 139 return &cfg 140 } 141 142 // limits configuration 143 limitsCfgCases := []struct { 144 name string 145 limit int 146 }{{ 147 name: "empty LimitsConfiguration is valid (implies disabled)", 148 limit: 0, 149 }, { 150 name: "LimitsConfiguration with positive limit is valid", 151 limit: 5, 152 }, {}, { 153 name: "LimitsConfiguration with negative limit is valid (implies disabled)", 154 limit: -5, 155 }} 156 157 for _, tc := range limitsCfgCases { 158 t.Run(tc.name, func(t *testing.T) { 159 cfg := baseCfg(t) 160 cfg.Limits = LimitsConfiguration{ 161 PerQuery: PerQueryLimitsConfiguration{ 162 MaxFetchedSeries: tc.limit, 163 }, 164 } 165 166 assert.NoError(t, validator.Validate(cfg)) 167 }) 168 } 169 } 170 171 func TestDefaultTagOptionsConfigErrors(t *testing.T) { 172 var cfg TagOptionsConfiguration 173 require.NoError(t, yaml.Unmarshal([]byte(""), &cfg)) 174 opts, err := TagOptionsFromConfig(cfg) 175 require.NoError(t, err) 176 require.Equal(t, models.TypeQuoted, opts.IDSchemeType()) 177 } 178 179 func TestGraphiteIDGenerationSchemeIsInvalid(t *testing.T) { 180 var cfg TagOptionsConfiguration 181 require.Error(t, yaml.Unmarshal([]byte("idScheme: graphite"), &cfg)) 182 } 183 184 func TestTagOptionsConfigWithTagGenerationScheme(t *testing.T) { 185 tests := []struct { 186 schemeStr string 187 scheme models.IDSchemeType 188 }{ 189 {"prepend_meta", models.TypePrependMeta}, 190 {"quoted", models.TypeQuoted}, 191 } 192 193 for _, tt := range tests { 194 var cfg TagOptionsConfiguration 195 schemeConfig := fmt.Sprintf("idScheme: %s", tt.schemeStr) 196 require.NoError(t, yaml.Unmarshal([]byte(schemeConfig), &cfg)) 197 opts, err := TagOptionsFromConfig(cfg) 198 require.NoError(t, err) 199 assert.Equal(t, []byte("__name__"), opts.MetricName()) 200 assert.Equal(t, tt.scheme, opts.IDSchemeType()) 201 } 202 } 203 204 func TestTagOptionsConfig(t *testing.T) { 205 var cfg TagOptionsConfiguration 206 config := "metricName: abcdefg\nidScheme: prepend_meta\nbucketName: foo" 207 require.NoError(t, yaml.Unmarshal([]byte(config), &cfg)) 208 opts, err := TagOptionsFromConfig(cfg) 209 require.NoError(t, err) 210 assert.Equal(t, []byte("abcdefg"), opts.MetricName()) 211 assert.Equal(t, []byte("foo"), opts.BucketName()) 212 assert.Equal(t, models.TypePrependMeta, opts.IDSchemeType()) 213 } 214 215 func TestKeepNaNsDefault(t *testing.T) { 216 r := ResultOptions{ 217 KeepNaNs: true, 218 } 219 assert.Equal(t, true, r.KeepNaNs) 220 221 r = ResultOptions{} 222 assert.Equal(t, false, r.KeepNaNs) 223 }