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  }