github.com/m3db/m3@v1.5.0/src/query/api/v1/handler/prometheus/handleroptions/header_test.go (about) 1 // Copyright (c) 2019 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 handleroptions 22 23 import ( 24 "context" 25 "encoding/json" 26 "fmt" 27 "net/http/httptest" 28 "testing" 29 "time" 30 31 "github.com/m3db/m3/src/query/block" 32 "github.com/m3db/m3/src/query/storage" 33 "github.com/m3db/m3/src/x/headers" 34 35 "github.com/stretchr/testify/assert" 36 "github.com/stretchr/testify/require" 37 ) 38 39 func TestAddDBResultResponseHeaders(t *testing.T) { 40 recorder := httptest.NewRecorder() 41 meta := block.NewResultMetadata() 42 require.NoError(t, AddDBResultResponseHeaders(recorder, meta, nil)) 43 assert.Equal(t, 0, len(recorder.Header())) 44 45 recorder = httptest.NewRecorder() 46 meta.Exhaustive = false 47 ex := headers.LimitHeaderSeriesLimitApplied 48 require.NoError(t, AddDBResultResponseHeaders(recorder, meta, nil)) 49 assert.Equal(t, 1, len(recorder.Header())) 50 assert.Equal(t, ex, recorder.Header().Get(headers.LimitHeader)) 51 52 recorder = httptest.NewRecorder() 53 meta.AddWarning("foo", "bar") 54 ex = fmt.Sprintf("%s,%s_%s", headers.LimitHeaderSeriesLimitApplied, "foo", "bar") 55 require.NoError(t, AddDBResultResponseHeaders(recorder, meta, nil)) 56 assert.Equal(t, 1, len(recorder.Header())) 57 assert.Equal(t, ex, recorder.Header().Get(headers.LimitHeader)) 58 59 recorder = httptest.NewRecorder() 60 meta.Exhaustive = true 61 ex = "foo_bar" 62 require.NoError(t, AddDBResultResponseHeaders(recorder, meta, nil)) 63 assert.Equal(t, 1, len(recorder.Header())) 64 assert.Equal(t, ex, recorder.Header().Get(headers.LimitHeader)) 65 66 recorder = httptest.NewRecorder() 67 meta = block.NewResultMetadata() 68 require.NoError(t, AddDBResultResponseHeaders(recorder, meta, &storage.FetchOptions{ 69 Timeout: 5 * time.Second, 70 })) 71 assert.Equal(t, 1, len(recorder.Header())) 72 assert.Equal(t, "5s", recorder.Header().Get(headers.TimeoutHeader)) 73 74 recorder = httptest.NewRecorder() 75 meta = block.NewResultMetadata() 76 meta.WaitedIndex = 3 77 meta.WaitedSeriesRead = 42 78 require.NoError(t, AddDBResultResponseHeaders(recorder, meta, nil)) 79 assert.Equal(t, 1, len(recorder.Header())) 80 assert.Equal(t, "{\"waitedIndex\":3,\"waitedSeriesRead\":42}", 81 recorder.Header().Get(headers.WaitedHeader)) 82 } 83 84 func TestAddDBResultResponseHeadersFetched(t *testing.T) { 85 recorder := httptest.NewRecorder() 86 meta := block.NewResultMetadata() 87 meta.FetchedSeriesCount = 42 88 meta.FetchedMetadataCount = 142 89 meta.FetchedResponses = 99 90 meta.FetchedBytesEstimate = 1072 91 require.NoError(t, AddDBResultResponseHeaders(recorder, meta, nil)) 92 assert.Equal(t, 4, len(recorder.Header())) 93 assert.Equal(t, "99", recorder.Header().Get(headers.FetchedResponsesHeader)) 94 assert.Equal(t, "1072", recorder.Header().Get(headers.FetchedBytesEstimateHeader)) 95 assert.Equal(t, "42", recorder.Header().Get(headers.FetchedSeriesCount)) 96 assert.Equal(t, "142", recorder.Header().Get(headers.FetchedMetadataCount)) 97 } 98 99 func TestAddDBResultResponseHeadersNamespaces(t *testing.T) { 100 recorder := httptest.NewRecorder() 101 meta := block.NewResultMetadata() 102 require.NoError(t, AddDBResultResponseHeaders(recorder, meta, nil)) 103 assert.Equal(t, 0, len(recorder.Header())) 104 105 recorder = httptest.NewRecorder() 106 meta = block.NewResultMetadata() 107 meta.AddNamespace("default") 108 require.NoError(t, AddDBResultResponseHeaders(recorder, meta, nil)) 109 assert.Equal(t, 1, len(recorder.Header())) 110 assert.Equal(t, "default", recorder.Header().Get(headers.NamespacesHeader)) 111 112 recorder = httptest.NewRecorder() 113 meta = block.NewResultMetadata() 114 meta.AddNamespace("default") 115 meta.AddNamespace("myfavoritens") 116 meta.AddNamespace("myfavoritens") 117 require.NoError(t, AddDBResultResponseHeaders(recorder, meta, nil)) 118 assert.Equal(t, 1, len(recorder.Header())) 119 assert.Equal(t, "default,myfavoritens", recorder.Header().Get(headers.NamespacesHeader)) 120 } 121 122 func TestAddDBResultResponseHeadersMetadataByName(t *testing.T) { 123 recorder := httptest.NewRecorder() 124 meta := block.NewResultMetadata() 125 *(meta.ByName([]byte("mymetric"))) = block.ResultMetricMetadata{ 126 NoSamples: 1, 127 WithSamples: 2, 128 Aggregated: 3, 129 Unaggregated: 4, 130 } 131 fetchOpts := storage.NewFetchOptions() 132 fetchOpts.MaxMetricMetadataStats = 10 133 require.NoError(t, AddDBResultResponseHeaders(recorder, meta, fetchOpts)) 134 assert.Equal(t, 6, len(recorder.Header())) 135 assert.Equal(t, "0s", recorder.Header().Get(headers.TimeoutHeader)) 136 assert.Equal(t, "1", recorder.Header().Get(headers.FetchedSeriesNoSamplesCount)) 137 assert.Equal(t, "2", recorder.Header().Get(headers.FetchedSeriesWithSamplesCount)) 138 assert.Equal(t, "3", recorder.Header().Get(headers.FetchedAggregatedSeriesCount)) 139 assert.Equal(t, "4", recorder.Header().Get(headers.FetchedUnaggregatedSeriesCount)) 140 assert.Equal(t, 141 "{\"mymetric\":{\"NoSamples\":1,\"WithSamples\":2,\"Aggregated\":3,\"Unaggregated\":4}}", 142 recorder.Header().Get(headers.MetricStats)) 143 144 recorder = httptest.NewRecorder() 145 meta = block.NewResultMetadata() 146 *(meta.ByName([]byte("metric_a"))) = block.ResultMetricMetadata{ 147 NoSamples: 1, 148 WithSamples: 2, 149 Aggregated: 3, 150 Unaggregated: 4, 151 } 152 *(meta.ByName([]byte("metric_b"))) = block.ResultMetricMetadata{ 153 NoSamples: 10, 154 WithSamples: 20, 155 Aggregated: 30, 156 Unaggregated: 40, 157 } 158 require.NoError(t, AddDBResultResponseHeaders(recorder, meta, fetchOpts)) 159 assert.Equal(t, 6, len(recorder.Header())) 160 assert.Equal(t, "0s", recorder.Header().Get(headers.TimeoutHeader)) 161 assert.Equal(t, "11", recorder.Header().Get(headers.FetchedSeriesNoSamplesCount)) 162 assert.Equal(t, "22", recorder.Header().Get(headers.FetchedSeriesWithSamplesCount)) 163 assert.Equal(t, "33", recorder.Header().Get(headers.FetchedAggregatedSeriesCount)) 164 assert.Equal(t, "44", recorder.Header().Get(headers.FetchedUnaggregatedSeriesCount)) 165 assert.Equal(t, 166 "{\"metric_a\":{\"NoSamples\":1,\"WithSamples\":2,\"Aggregated\":3,\"Unaggregated\":4},"+ 167 "\"metric_b\":{\"NoSamples\":10,\"WithSamples\":20,\"Aggregated\":30,\"Unaggregated\":40}}", 168 recorder.Header().Get(headers.MetricStats)) 169 170 recorder = httptest.NewRecorder() 171 meta = block.NewResultMetadata() 172 numStats := fetchOpts.MaxMetricMetadataStats + 2 173 totalCount := 0 174 for i := 0; i < numStats; i++ { 175 count := i + 1 176 meta.ByName([]byte(fmt.Sprintf("metric_%v", i))).Unaggregated = count 177 totalCount += count 178 } 179 require.NoError(t, AddDBResultResponseHeaders(recorder, meta, fetchOpts)) 180 assert.Equal(t, 3, len(recorder.Header())) 181 assert.Equal(t, "0s", recorder.Header().Get(headers.TimeoutHeader)) 182 assert.Equal(t, fmt.Sprint(totalCount), recorder.Header().Get(headers.FetchedUnaggregatedSeriesCount)) 183 184 parsed := make(map[string]*block.ResultMetricMetadata) 185 metricStatsHeader := recorder.Header().Get(headers.MetricStats) 186 assert.NotEmpty(t, metricStatsHeader) 187 err := json.Unmarshal([]byte(metricStatsHeader), &parsed) 188 assert.NoError(t, err) 189 assert.Equal(t, fetchOpts.MaxMetricMetadataStats, len(parsed)) 190 observedCount := 0 191 for _, stat := range parsed { 192 observedCount += stat.Unaggregated 193 } 194 // The total count includes `max+2` counters. The bottom two values of those 12 are 1 and 2. 195 // So we want to see the total minus (1 + 2), which means the count contains only the 196 // top `max` counts. 197 wantCount := totalCount - (1 + 2) 198 assert.Equal(t, observedCount, wantCount) 199 } 200 201 func TestAddDBResultResponseHeadersMetadataByNameMaxConfig(t *testing.T) { 202 recorder := httptest.NewRecorder() 203 meta := block.NewResultMetadata() 204 *(meta.ByName([]byte("mymetric"))) = block.ResultMetricMetadata{ 205 NoSamples: 1, 206 WithSamples: 2, 207 Aggregated: 3, 208 Unaggregated: 4, 209 } 210 211 // Disable metric metadata stats using a header 212 req := httptest.NewRequest("GET", "/api/v1/query", nil) 213 req.Header.Add(headers.LimitMaxMetricMetadataStatsHeader, "0") 214 fetchOptsBuilder, err := NewFetchOptionsBuilder( 215 FetchOptionsBuilderOptions{ 216 Timeout: 5 * time.Second, 217 }, 218 ) 219 require.NoError(t, err) 220 _, fetchOpts, err := fetchOptsBuilder.NewFetchOptions(context.Background(), req) 221 require.NoError(t, err) 222 require.NoError(t, AddDBResultResponseHeaders(recorder, meta, fetchOpts)) 223 assert.Equal(t, 5, len(recorder.Header())) 224 assert.Equal(t, "5s", recorder.Header().Get(headers.TimeoutHeader)) 225 assert.Equal(t, "1", recorder.Header().Get(headers.FetchedSeriesNoSamplesCount)) 226 assert.Equal(t, "2", recorder.Header().Get(headers.FetchedSeriesWithSamplesCount)) 227 assert.Equal(t, "3", recorder.Header().Get(headers.FetchedAggregatedSeriesCount)) 228 assert.Equal(t, "4", recorder.Header().Get(headers.FetchedUnaggregatedSeriesCount)) 229 assert.Empty(t, recorder.Header().Get(headers.MetricStats)) 230 231 // Disable metric metadata stats using config 232 recorder = httptest.NewRecorder() 233 fetchOpts.MaxMetricMetadataStats = 0 234 require.NoError(t, AddDBResultResponseHeaders(recorder, meta, fetchOpts)) 235 assert.Equal(t, 5, len(recorder.Header())) 236 assert.Equal(t, "5s", recorder.Header().Get(headers.TimeoutHeader)) 237 assert.Equal(t, "1", recorder.Header().Get(headers.FetchedSeriesNoSamplesCount)) 238 assert.Equal(t, "2", recorder.Header().Get(headers.FetchedSeriesWithSamplesCount)) 239 assert.Equal(t, "3", recorder.Header().Get(headers.FetchedAggregatedSeriesCount)) 240 assert.Equal(t, "4", recorder.Header().Get(headers.FetchedUnaggregatedSeriesCount)) 241 assert.Empty(t, recorder.Header().Get(headers.MetricStats)) 242 } 243 244 func TestAddReturnedLimitResponseHeaders(t *testing.T) { 245 recorder := httptest.NewRecorder() 246 require.NoError(t, AddReturnedLimitResponseHeaders(recorder, &ReturnedDataLimited{ 247 Series: 3, 248 Datapoints: 6, 249 TotalSeries: 3, 250 Limited: false, 251 }, nil)) 252 assert.Equal(t, 1, len(recorder.Header())) 253 assert.Equal(t, "{\"Series\":3,\"Datapoints\":6,\"TotalSeries\":3,\"Limited\":false}", 254 recorder.Header().Get(headers.ReturnedDataLimitedHeader)) 255 256 recorder = httptest.NewRecorder() 257 require.NoError(t, AddReturnedLimitResponseHeaders(recorder, nil, &ReturnedMetadataLimited{ 258 Results: 3, 259 TotalResults: 3, 260 Limited: false, 261 })) 262 assert.Equal(t, 1, len(recorder.Header())) 263 assert.Equal(t, "{\"Results\":3,\"TotalResults\":3,\"Limited\":false}", 264 recorder.Header().Get(headers.ReturnedMetadataLimitedHeader)) 265 }