github.com/grafana/pyroscope@v1.18.0/pkg/test/integration/http_status_codes_test.go (about)

     1  package integration
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"net/http"
     9  	"net/url"
    10  	"strings"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/stretchr/testify/assert"
    15  	"github.com/stretchr/testify/require"
    16  )
    17  
    18  type Request struct {
    19  	ProfileType string
    20  }
    21  
    22  func TestStatusCodes(t *testing.T) {
    23  	const profileTypeProcessCPU = "process_cpu:cpu:nanoseconds:cpu:nanoseconds"
    24  
    25  	toJSON := func(obj any) string {
    26  		bytes, err := json.Marshal(obj)
    27  		if err != nil {
    28  			panic(fmt.Sprintf("failed to marshal to json: %v", err))
    29  		}
    30  		return string(bytes)
    31  	}
    32  
    33  	type Test struct {
    34  		Name             string
    35  		Method           string
    36  		Params           url.Values
    37  		Header           http.Header
    38  		Body             string
    39  		WantV1StatusCode int // For test cases which expect a different v1 status code
    40  	}
    41  
    42  	type EndpointTestGroup struct {
    43  		Path string
    44  		// All the test cases partitioned by expected HTTP status code.
    45  		Tests map[int][]Test
    46  	}
    47  
    48  	renderTests := EndpointTestGroup{
    49  		Path: "/pyroscope/render",
    50  		Tests: map[int][]Test{
    51  			http.StatusOK: {
    52  				{
    53  					Name:   "valid",
    54  					Method: http.MethodGet,
    55  					Params: url.Values{
    56  						"query": []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
    57  						"from":  []string{"now-15m"},
    58  						"until": []string{"now"},
    59  					},
    60  				},
    61  				{
    62  					Name:   "format_dot_valid",
    63  					Method: http.MethodGet,
    64  					Params: url.Values{
    65  						"query":  []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
    66  						"from":   []string{"now-15m"},
    67  						"until":  []string{"now"},
    68  						"format": []string{"dot"},
    69  					},
    70  				},
    71  				{
    72  					Name:   "format_dot_with_max_nodes",
    73  					Method: http.MethodGet,
    74  					Params: url.Values{
    75  						"query":    []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
    76  						"from":     []string{"now-15m"},
    77  						"until":    []string{"now"},
    78  						"format":   []string{"dot"},
    79  						"maxNodes": []string{"50"},
    80  					},
    81  				},
    82  				{
    83  					Name:   "format_dot_with_max_nodes_hyphen",
    84  					Method: http.MethodGet,
    85  					Params: url.Values{
    86  						"query":     []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
    87  						"from":      []string{"now-15m"},
    88  						"until":     []string{"now"},
    89  						"format":    []string{"dot"},
    90  						"max-nodes": []string{"50"},
    91  					},
    92  				},
    93  				{
    94  					Name:   "with_group_by",
    95  					Method: http.MethodGet,
    96  					Params: url.Values{
    97  						"query":   []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
    98  						"from":    []string{"now-15m"},
    99  						"until":   []string{"now"},
   100  						"groupBy": []string{"service_name"},
   101  					},
   102  				},
   103  				{
   104  					Name:   "with_group_by_multiple",
   105  					Method: http.MethodGet,
   106  					Params: url.Values{
   107  						"query":   []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   108  						"from":    []string{"now-15m"},
   109  						"until":   []string{"now"},
   110  						"groupBy": []string{"service_name", "region"},
   111  					},
   112  				},
   113  				{
   114  					Name:   "with_aggregation_sum",
   115  					Method: http.MethodGet,
   116  					Params: url.Values{
   117  						"query":       []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   118  						"from":        []string{"now-15m"},
   119  						"until":       []string{"now"},
   120  						"aggregation": []string{"sum"},
   121  					},
   122  				},
   123  				{
   124  					Name:   "with_aggregation_avg",
   125  					Method: http.MethodGet,
   126  					Params: url.Values{
   127  						"query":       []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   128  						"from":        []string{"now-15m"},
   129  						"until":       []string{"now"},
   130  						"aggregation": []string{"avg"},
   131  					},
   132  				},
   133  				{
   134  					Name:   "with_aggregation_invalid",
   135  					Method: http.MethodGet,
   136  					Params: url.Values{
   137  						"query":       []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   138  						"from":        []string{"now-15m"},
   139  						"until":       []string{"now"},
   140  						"aggregation": []string{"invalid"},
   141  					},
   142  				},
   143  				{
   144  					Name:   "with_max_nodes",
   145  					Method: http.MethodGet,
   146  					Params: url.Values{
   147  						"query":    []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   148  						"from":     []string{"now-15m"},
   149  						"until":    []string{"now"},
   150  						"maxNodes": []string{"1024"},
   151  					},
   152  				},
   153  				{
   154  					Name:   "with_max_nodes_hyphen",
   155  					Method: http.MethodGet,
   156  					Params: url.Values{
   157  						"query":     []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   158  						"from":      []string{"now-15m"},
   159  						"until":     []string{"now"},
   160  						"max-nodes": []string{"1024"},
   161  					},
   162  				},
   163  				{
   164  					Name:   "with_max_nodes_zero",
   165  					Method: http.MethodGet,
   166  					Params: url.Values{
   167  						"query":    []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   168  						"from":     []string{"now-15m"},
   169  						"until":    []string{"now"},
   170  						"maxNodes": []string{"0"},
   171  					},
   172  				},
   173  				{
   174  					Name:   "with_max_nodes_invalid",
   175  					Method: http.MethodGet,
   176  					Params: url.Values{
   177  						"query":    []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   178  						"from":     []string{"now-15m"},
   179  						"until":    []string{"now"},
   180  						"maxNodes": []string{"invalid"},
   181  					},
   182  				},
   183  				{
   184  					Name:   "all_params_combined",
   185  					Method: http.MethodGet,
   186  					Params: url.Values{
   187  						"query":       []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   188  						"from":        []string{"now-15m"},
   189  						"until":       []string{"now"},
   190  						"groupBy":     []string{"service_name"},
   191  						"aggregation": []string{"sum"},
   192  						"maxNodes":    []string{"512"},
   193  					},
   194  				},
   195  			},
   196  			http.StatusBadRequest: {
   197  				{
   198  					Name:   "all_zero_time_range",
   199  					Method: http.MethodGet,
   200  					Params: url.Values{
   201  						"query": []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   202  						"from":  []string{"0"},
   203  						"until": []string{"0"},
   204  					},
   205  				},
   206  				{
   207  					Name:   "from_zero_time_range",
   208  					Method: http.MethodGet,
   209  					Params: url.Values{
   210  						"query": []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   211  						"from":  []string{"0"},
   212  						"until": []string{"now"},
   213  					},
   214  				},
   215  				{
   216  					Name:   "until_zero_time_range",
   217  					Method: http.MethodGet,
   218  					Params: url.Values{
   219  						"query": []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   220  						"from":  []string{"now-15m"},
   221  						"until": []string{"0"},
   222  					},
   223  				},
   224  				{
   225  					Name:   "missing_query",
   226  					Method: http.MethodGet,
   227  					Params: url.Values{
   228  						"from":  []string{"now-15m"},
   229  						"until": []string{"now"},
   230  					},
   231  				},
   232  				{
   233  					Name:   "invalid_query_syntax",
   234  					Method: http.MethodGet,
   235  					Params: url.Values{
   236  						"query": []string{"bad_query"},
   237  						"from":  []string{"now-15m"},
   238  						"until": []string{"now"},
   239  					},
   240  				},
   241  				{
   242  					Name:   "query_without_profile_type",
   243  					Method: http.MethodGet,
   244  					Params: url.Values{
   245  						"query": []string{`{service_name="test"}`},
   246  						"from":  []string{"now-15m"},
   247  						"until": []string{"now"},
   248  					},
   249  				},
   250  				{
   251  					Name:   "invalid_profile_type_format",
   252  					Method: http.MethodGet,
   253  					Params: url.Values{
   254  						"query": []string{`invalid_format{service_name="test"}`},
   255  						"from":  []string{"now-15m"},
   256  						"until": []string{"now"},
   257  					},
   258  				},
   259  				{
   260  					Name:   "empty_query_param",
   261  					Method: http.MethodGet,
   262  					Params: url.Values{
   263  						"query": []string{""},
   264  						"from":  []string{"now-15m"},
   265  						"until": []string{"now"},
   266  					},
   267  				},
   268  				{
   269  					Name:   "from_and_until_0_with_dot_format",
   270  					Method: http.MethodGet,
   271  					Params: url.Values{
   272  						"query":  []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   273  						"from":   []string{"0"},
   274  						"until":  []string{"0"},
   275  						"format": []string{"dot"},
   276  					},
   277  				},
   278  			},
   279  			http.StatusMethodNotAllowed: {
   280  				{
   281  					Name:   "post_method_not_allowed",
   282  					Method: http.MethodPost,
   283  					Params: url.Values{
   284  						"query": []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   285  						"from":  []string{"now-15m"},
   286  						"until": []string{"now"},
   287  					},
   288  				},
   289  				{
   290  					Name:   "put_method_not_allowed",
   291  					Method: http.MethodPut,
   292  					Params: url.Values{
   293  						"query": []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   294  						"from":  []string{"now-15m"},
   295  						"until": []string{"now"},
   296  					},
   297  				},
   298  				{
   299  					Name:   "delete_method_not_allowed",
   300  					Method: http.MethodDelete,
   301  					Params: url.Values{
   302  						"query": []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   303  						"from":  []string{"now-15m"},
   304  						"until": []string{"now"},
   305  					},
   306  				},
   307  				{
   308  					Name:   "patch_method_not_allowed",
   309  					Method: http.MethodPatch,
   310  					Params: url.Values{
   311  						"query": []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   312  						"from":  []string{"now-15m"},
   313  						"until": []string{"now"},
   314  					},
   315  				},
   316  			},
   317  		},
   318  	}
   319  
   320  	renderDiffTests := EndpointTestGroup{
   321  		Path: "/pyroscope/render-diff",
   322  		Tests: map[int][]Test{
   323  			http.StatusOK: {
   324  				{
   325  					Name:   "valid",
   326  					Method: http.MethodGet,
   327  					Params: url.Values{
   328  						"leftQuery":  []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   329  						"leftFrom":   []string{"now-30m"},
   330  						"leftUntil":  []string{"now-15m"},
   331  						"rightQuery": []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   332  						"rightFrom":  []string{"now-15m"},
   333  						"rightUntil": []string{"now"},
   334  					},
   335  				},
   336  				{
   337  					Name:   "same_query_different_times",
   338  					Method: http.MethodGet,
   339  					Params: url.Values{
   340  						"leftQuery":  []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   341  						"leftFrom":   []string{"now-60m"},
   342  						"leftUntil":  []string{"now-30m"},
   343  						"rightQuery": []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   344  						"rightFrom":  []string{"now-30m"},
   345  						"rightUntil": []string{"now"},
   346  					},
   347  				},
   348  				{
   349  					Name:   "same_time_range",
   350  					Method: http.MethodGet,
   351  					Params: url.Values{
   352  						"leftQuery":  []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   353  						"leftFrom":   []string{"now-15m"},
   354  						"leftUntil":  []string{"now"},
   355  						"rightQuery": []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   356  						"rightFrom":  []string{"now-15m"},
   357  						"rightUntil": []string{"now"},
   358  					},
   359  				},
   360  			},
   361  			http.StatusBadRequest: {
   362  				{
   363  					Name:   "missing_left_query",
   364  					Method: http.MethodGet,
   365  					Params: url.Values{
   366  						"leftFrom":   []string{"now-30m"},
   367  						"leftUntil":  []string{"now-15m"},
   368  						"rightQuery": []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   369  						"rightFrom":  []string{"now-15m"},
   370  						"rightUntil": []string{"now"},
   371  					},
   372  				},
   373  				{
   374  					Name:   "missing_right_query",
   375  					Method: http.MethodGet,
   376  					Params: url.Values{
   377  						"leftQuery":  []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   378  						"leftFrom":   []string{"now-30m"},
   379  						"leftUntil":  []string{"now-15m"},
   380  						"rightFrom":  []string{"now-15m"},
   381  						"rightUntil": []string{"now"},
   382  					},
   383  				},
   384  				{
   385  					Name:   "invalid_left_query_syntax",
   386  					Method: http.MethodGet,
   387  					Params: url.Values{
   388  						"leftQuery":  []string{"bad_query"},
   389  						"leftFrom":   []string{"now-30m"},
   390  						"leftUntil":  []string{"now-15m"},
   391  						"rightQuery": []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   392  						"rightFrom":  []string{"now-15m"},
   393  						"rightUntil": []string{"now"},
   394  					},
   395  				},
   396  				{
   397  					Name:   "invalid_right_query_syntax",
   398  					Method: http.MethodGet,
   399  					Params: url.Values{
   400  						"leftQuery":  []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   401  						"leftFrom":   []string{"now-30m"},
   402  						"leftUntil":  []string{"now-15m"},
   403  						"rightQuery": []string{"bad_query"},
   404  						"rightFrom":  []string{"now-15m"},
   405  						"rightUntil": []string{"now"},
   406  					},
   407  				},
   408  				{
   409  					Name:   "left_query_without_profile_type",
   410  					Method: http.MethodGet,
   411  					Params: url.Values{
   412  						"leftQuery":  []string{`{service_name="test"}`},
   413  						"leftFrom":   []string{"now-30m"},
   414  						"leftUntil":  []string{"now-15m"},
   415  						"rightQuery": []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   416  						"rightFrom":  []string{"now-15m"},
   417  						"rightUntil": []string{"now"},
   418  					},
   419  				},
   420  				{
   421  					Name:   "right_query_without_profile_type",
   422  					Method: http.MethodGet,
   423  					Params: url.Values{
   424  						"leftQuery":  []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   425  						"leftFrom":   []string{"now-30m"},
   426  						"leftUntil":  []string{"now-15m"},
   427  						"rightQuery": []string{`{service_name="test"}`},
   428  						"rightFrom":  []string{"now-15m"},
   429  						"rightUntil": []string{"now"},
   430  					},
   431  				},
   432  				{
   433  					Name:   "invalid_left_profile_type_format",
   434  					Method: http.MethodGet,
   435  					Params: url.Values{
   436  						"leftQuery":  []string{`invalid_format{service_name="test"}`},
   437  						"leftFrom":   []string{"now-30m"},
   438  						"leftUntil":  []string{"now-15m"},
   439  						"rightQuery": []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   440  						"rightFrom":  []string{"now-15m"},
   441  						"rightUntil": []string{"now"},
   442  					},
   443  				},
   444  				{
   445  					Name:   "invalid_right_profile_type_format",
   446  					Method: http.MethodGet,
   447  					Params: url.Values{
   448  						"leftQuery":  []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   449  						"leftFrom":   []string{"now-30m"},
   450  						"leftUntil":  []string{"now-15m"},
   451  						"rightQuery": []string{`invalid_format{service_name="test"}`},
   452  						"rightFrom":  []string{"now-15m"},
   453  						"rightUntil": []string{"now"},
   454  					},
   455  				},
   456  				{
   457  					Name:   "profile_types_mismatch",
   458  					Method: http.MethodGet,
   459  					Params: url.Values{
   460  						"leftQuery":  []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   461  						"leftFrom":   []string{"now-30m"},
   462  						"leftUntil":  []string{"now-15m"},
   463  						"rightQuery": []string{createRenderQuery("memory:alloc_objects:count:space:bytes", "pyroscope")},
   464  						"rightFrom":  []string{"now-15m"},
   465  						"rightUntil": []string{"now"},
   466  					},
   467  				},
   468  				{
   469  					Name:   "empty_left_query",
   470  					Method: http.MethodGet,
   471  					Params: url.Values{
   472  						"leftQuery":  []string{""},
   473  						"leftFrom":   []string{"now-30m"},
   474  						"leftUntil":  []string{"now-15m"},
   475  						"rightQuery": []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   476  						"rightFrom":  []string{"now-15m"},
   477  						"rightUntil": []string{"now"},
   478  					},
   479  				},
   480  				{
   481  					Name:   "empty_right_query",
   482  					Method: http.MethodGet,
   483  					Params: url.Values{
   484  						"leftQuery":  []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   485  						"leftFrom":   []string{"now-30m"},
   486  						"leftUntil":  []string{"now-15m"},
   487  						"rightQuery": []string{""},
   488  						"rightFrom":  []string{"now-15m"},
   489  						"rightUntil": []string{"now"},
   490  					},
   491  				},
   492  				{
   493  					Name:   "left_from_zero",
   494  					Method: http.MethodGet,
   495  					Params: url.Values{
   496  						"leftQuery":  []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   497  						"leftFrom":   []string{"0"},
   498  						"leftUntil":  []string{"now-15m"},
   499  						"rightQuery": []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   500  						"rightFrom":  []string{"now-15m"},
   501  						"rightUntil": []string{"now"},
   502  					},
   503  				},
   504  				{
   505  					Name:   "left_until_zero",
   506  					Method: http.MethodGet,
   507  					Params: url.Values{
   508  						"leftQuery":  []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   509  						"leftFrom":   []string{"now-30m"},
   510  						"leftUntil":  []string{"0"},
   511  						"rightQuery": []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   512  						"rightFrom":  []string{"now-15m"},
   513  						"rightUntil": []string{"now"},
   514  					},
   515  				},
   516  				{
   517  					Name:   "right_from_zero",
   518  					Method: http.MethodGet,
   519  					Params: url.Values{
   520  						"leftQuery":  []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   521  						"leftFrom":   []string{"now-30m"},
   522  						"leftUntil":  []string{"now-15m"},
   523  						"rightQuery": []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   524  						"rightFrom":  []string{"0"},
   525  						"rightUntil": []string{"now"},
   526  					},
   527  				},
   528  				{
   529  					Name:   "right_until_zero",
   530  					Method: http.MethodGet,
   531  					Params: url.Values{
   532  						"leftQuery":  []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   533  						"leftFrom":   []string{"now-30m"},
   534  						"leftUntil":  []string{"now-15m"},
   535  						"rightQuery": []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   536  						"rightFrom":  []string{"now-15m"},
   537  						"rightUntil": []string{"0"},
   538  					},
   539  				},
   540  				{
   541  					Name:   "all_zero_time_ranges",
   542  					Method: http.MethodGet,
   543  					Params: url.Values{
   544  						"leftQuery":  []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   545  						"leftFrom":   []string{"0"},
   546  						"leftUntil":  []string{"0"},
   547  						"rightQuery": []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   548  						"rightFrom":  []string{"0"},
   549  						"rightUntil": []string{"0"},
   550  					},
   551  				},
   552  			},
   553  			http.StatusMethodNotAllowed: {
   554  				{
   555  					Name:   "post_method_not_allowed",
   556  					Method: http.MethodPost,
   557  					Params: url.Values{
   558  						"leftQuery":  []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   559  						"leftFrom":   []string{"now-30m"},
   560  						"leftUntil":  []string{"now-15m"},
   561  						"rightQuery": []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   562  						"rightFrom":  []string{"now-15m"},
   563  						"rightUntil": []string{"now"},
   564  					},
   565  				},
   566  				{
   567  					Name:   "put_method_not_allowed",
   568  					Method: http.MethodPut,
   569  					Params: url.Values{
   570  						"leftQuery":  []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   571  						"leftFrom":   []string{"now-30m"},
   572  						"leftUntil":  []string{"now-15m"},
   573  						"rightQuery": []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   574  						"rightFrom":  []string{"now-15m"},
   575  						"rightUntil": []string{"now"},
   576  					},
   577  				},
   578  				{
   579  					Name:   "delete_method_not_allowed",
   580  					Method: http.MethodDelete,
   581  					Params: url.Values{
   582  						"leftQuery":  []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   583  						"leftFrom":   []string{"now-30m"},
   584  						"leftUntil":  []string{"now-15m"},
   585  						"rightQuery": []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   586  						"rightFrom":  []string{"now-15m"},
   587  						"rightUntil": []string{"now"},
   588  					},
   589  				},
   590  				{
   591  					Name:   "patch_method_not_allowed",
   592  					Method: http.MethodPatch,
   593  					Params: url.Values{
   594  						"leftQuery":  []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   595  						"leftFrom":   []string{"now-30m"},
   596  						"leftUntil":  []string{"now-15m"},
   597  						"rightQuery": []string{createRenderQuery(profileTypeProcessCPU, "pyroscope")},
   598  						"rightFrom":  []string{"now-15m"},
   599  						"rightUntil": []string{"now"},
   600  					},
   601  				},
   602  			},
   603  		},
   604  	}
   605  
   606  	profileTypesTests := EndpointTestGroup{
   607  		Path: "/querier.v1.QuerierService/ProfileTypes",
   608  		Tests: map[int][]Test{
   609  			http.StatusOK: {
   610  				{
   611  					Name:   "valid",
   612  					Method: http.MethodPost,
   613  					Header: http.Header{
   614  						"Content-Type": []string{"application/json"},
   615  					},
   616  					Body: toJSON(map[string]any{
   617  						"start": time.Now().Add(-1 * time.Hour).UnixMilli(),
   618  						"end":   time.Now().UnixMilli(),
   619  					}),
   620  				},
   621  				{
   622  					Name:   "zero_time_range",
   623  					Method: http.MethodPost,
   624  					Header: http.Header{
   625  						"Content-Type": []string{"application/json"},
   626  					},
   627  					Body: toJSON(map[string]any{
   628  						"start": 0,
   629  						"end":   0,
   630  					}),
   631  				},
   632  				{
   633  					Name:   "valid_long_range",
   634  					Method: http.MethodPost,
   635  					Header: http.Header{
   636  						"Content-Type": []string{"application/json"},
   637  					},
   638  					Body: toJSON(map[string]any{
   639  						"start": time.Now().Add(-24 * time.Hour).UnixMilli(),
   640  						"end":   time.Now().UnixMilli(),
   641  					}),
   642  				},
   643  				{
   644  					Name:   "valid_short_range",
   645  					Method: http.MethodPost,
   646  					Header: http.Header{
   647  						"Content-Type": []string{"application/json"},
   648  					},
   649  					Body: toJSON(map[string]any{
   650  						"start": time.Now().Add(-5 * time.Minute).UnixMilli(),
   651  						"end":   time.Now().UnixMilli(),
   652  					}),
   653  				},
   654  				{
   655  					// TODO(bryan) this should be fixed
   656  					Name:   "negative_start",
   657  					Method: http.MethodPost,
   658  					Header: http.Header{
   659  						"Content-Type": []string{"application/json"},
   660  					},
   661  					Body: toJSON(map[string]any{
   662  						"start": -1,
   663  						"end":   time.Now().UnixMilli(),
   664  					}),
   665  				},
   666  				{
   667  					// TODO(bryan) this should be fixed
   668  					Name:   "negative_end",
   669  					Method: http.MethodPost,
   670  					Header: http.Header{
   671  						"Content-Type": []string{"application/json"},
   672  					},
   673  					Body: toJSON(map[string]any{
   674  						"start": -2,
   675  						"end":   -1,
   676  					}),
   677  				},
   678  			},
   679  			http.StatusBadRequest: {
   680  				{
   681  					Name:   "invalid_json",
   682  					Method: http.MethodPost,
   683  					Header: http.Header{
   684  						"Content-Type": []string{"application/json"},
   685  					},
   686  					Body: `{"invalid json"`,
   687  				},
   688  				{
   689  					Name:   "empty_body",
   690  					Method: http.MethodPost,
   691  					Header: http.Header{
   692  						"Content-Type": []string{"application/json"},
   693  					},
   694  					Body: "",
   695  				},
   696  				{
   697  					Name:   "start_after_end",
   698  					Method: http.MethodPost,
   699  					Header: http.Header{
   700  						"Content-Type": []string{"application/json"},
   701  					},
   702  					Body: toJSON(map[string]any{
   703  						"start": time.Now().UnixMilli(),
   704  						"end":   time.Now().Add(-1 * time.Hour).UnixMilli(),
   705  					}),
   706  				},
   707  				{
   708  					Name:   "start_string_instead_of_number",
   709  					Method: http.MethodPost,
   710  					Header: http.Header{
   711  						"Content-Type": []string{"application/json"},
   712  					},
   713  					Body: `{"start": "now-1h", "end": 0}`,
   714  				},
   715  				{
   716  					Name:   "end_string_instead_of_number",
   717  					Method: http.MethodPost,
   718  					Header: http.Header{
   719  						"Content-Type": []string{"application/json"},
   720  					},
   721  					Body: `{"start": 0, "end": "now"}`,
   722  				},
   723  			},
   724  			http.StatusMethodNotAllowed: {
   725  				{
   726  					Name:   "get_method_not_allowed",
   727  					Method: http.MethodGet,
   728  					Header: http.Header{
   729  						"Content-Type": []string{"application/json"},
   730  					},
   731  					Params: url.Values{
   732  						"start": []string{fmt.Sprintf("%d", time.Now().Add(-1*time.Hour).UnixMilli())},
   733  						"end":   []string{fmt.Sprintf("%d", time.Now().UnixMilli())},
   734  					},
   735  				},
   736  				{
   737  					Name:   "put_method_not_allowed",
   738  					Method: http.MethodPut,
   739  					Header: http.Header{
   740  						"Content-Type": []string{"application/json"},
   741  					},
   742  					Body: toJSON(map[string]any{
   743  						"start": time.Now().Add(-1 * time.Hour).UnixMilli(),
   744  						"end":   time.Now().UnixMilli(),
   745  					}),
   746  				},
   747  				{
   748  					Name:   "delete_method_not_allowed",
   749  					Method: http.MethodDelete,
   750  					Header: http.Header{
   751  						"Content-Type": []string{"application/json"},
   752  					},
   753  					Body: toJSON(map[string]any{
   754  						"start": time.Now().Add(-1 * time.Hour).UnixMilli(),
   755  						"end":   time.Now().UnixMilli(),
   756  					}),
   757  				},
   758  				{
   759  					Name:   "patch_method_not_allowed",
   760  					Method: http.MethodPatch,
   761  					Header: http.Header{
   762  						"Content-Type": []string{"application/json"},
   763  					},
   764  					Body: toJSON(map[string]any{
   765  						"start": time.Now().Add(-1 * time.Hour).UnixMilli(),
   766  						"end":   time.Now().UnixMilli(),
   767  					}),
   768  				},
   769  			},
   770  			http.StatusUnsupportedMediaType: {
   771  				{
   772  					Name:   "invalid_content_type",
   773  					Method: http.MethodPost,
   774  					Header: http.Header{
   775  						"Content-Type": []string{"text/plain"},
   776  					},
   777  					Body: toJSON(map[string]any{
   778  						"start": time.Now().Add(-1 * time.Hour).UnixMilli(),
   779  						"end":   time.Now().UnixMilli(),
   780  					}),
   781  				},
   782  			},
   783  		},
   784  	}
   785  
   786  	labelValuesTests := EndpointTestGroup{
   787  		Path: "/querier.v1.QuerierService/LabelValues",
   788  		Tests: map[int][]Test{
   789  			http.StatusOK: {
   790  				{
   791  					Name:   "valid",
   792  					Method: http.MethodPost,
   793  					Header: http.Header{
   794  						"Content-Type": []string{"application/json"},
   795  					},
   796  					Body: toJSON(map[string]any{
   797  						"name":  "service_name",
   798  						"start": time.Now().Add(-1 * time.Hour).UnixMilli(),
   799  						"end":   time.Now().UnixMilli(),
   800  					}),
   801  				},
   802  				{
   803  					Name:   "valid_with_matchers",
   804  					Method: http.MethodPost,
   805  					Header: http.Header{
   806  						"Content-Type": []string{"application/json"},
   807  					},
   808  					Body: toJSON(map[string]any{
   809  						"name":     "service_name",
   810  						"matchers": []string{`{namespace="default"}`},
   811  						"start":    time.Now().Add(-1 * time.Hour).UnixMilli(),
   812  						"end":      time.Now().UnixMilli(),
   813  					}),
   814  				},
   815  				{
   816  					Name:   "valid_with_multiple_matchers",
   817  					Method: http.MethodPost,
   818  					Header: http.Header{
   819  						"Content-Type": []string{"application/json"},
   820  					},
   821  					Body: toJSON(map[string]any{
   822  						"name":     "service_name",
   823  						"matchers": []string{`{namespace="default"}`, `{region="us-west"}`},
   824  						"start":    time.Now().Add(-1 * time.Hour).UnixMilli(),
   825  						"end":      time.Now().UnixMilli(),
   826  					}),
   827  				},
   828  			},
   829  			http.StatusBadRequest: {
   830  				{
   831  					Name:   "invalid_no_time_range",
   832  					Method: http.MethodPost,
   833  					Header: http.Header{
   834  						"Content-Type": []string{"application/json"},
   835  					},
   836  					Body: toJSON(map[string]any{
   837  						"name": "service_name",
   838  					}),
   839  					// V1 allows no time ranges
   840  					WantV1StatusCode: http.StatusOK,
   841  				},
   842  				{
   843  					Name:   "invalid_matchers_syntax",
   844  					Method: http.MethodPost,
   845  					Header: http.Header{
   846  						"Content-Type": []string{"application/json"},
   847  					},
   848  					Body: toJSON(map[string]any{
   849  						"name":     "service_name",
   850  						"matchers": []string{"!bad_syntax!"},
   851  						"start":    time.Now().Add(-1 * time.Hour).UnixMilli(),
   852  						"end":      time.Now().UnixMilli(),
   853  					}),
   854  					WantV1StatusCode: http.StatusOK,
   855  				},
   856  				{
   857  					Name:   "invalid_json",
   858  					Method: http.MethodPost,
   859  					Header: http.Header{
   860  						"Content-Type": []string{"application/json"},
   861  					},
   862  					Body: `{"invalid json"`,
   863  				},
   864  				{
   865  					Name:   "empty_body",
   866  					Method: http.MethodPost,
   867  					Header: http.Header{
   868  						"Content-Type": []string{"application/json"},
   869  					},
   870  					Body: "",
   871  				},
   872  				{
   873  					Name:   "start_after_end",
   874  					Method: http.MethodPost,
   875  					Header: http.Header{
   876  						"Content-Type": []string{"application/json"},
   877  					},
   878  					Body: toJSON(map[string]any{
   879  						"name":  "service_name",
   880  						"start": time.Now().UnixMilli(),
   881  						"end":   time.Now().Add(-1 * time.Hour).UnixMilli(),
   882  					}),
   883  				},
   884  				{
   885  					Name:   "empty_name",
   886  					Method: http.MethodPost,
   887  					Header: http.Header{
   888  						"Content-Type": []string{"application/json"},
   889  					},
   890  					Body: toJSON(map[string]any{
   891  						"name":  "",
   892  						"start": time.Now().Add(-1 * time.Hour).UnixMilli(),
   893  						"end":   time.Now().UnixMilli(),
   894  					}),
   895  				},
   896  				{
   897  					Name:   "missing_name",
   898  					Method: http.MethodPost,
   899  					Header: http.Header{
   900  						"Content-Type": []string{"application/json"},
   901  					},
   902  					Body: toJSON(map[string]any{
   903  						"start": time.Now().Add(-1 * time.Hour).UnixMilli(),
   904  						"end":   time.Now().UnixMilli(),
   905  					}),
   906  				},
   907  			},
   908  			http.StatusMethodNotAllowed: {
   909  				{
   910  					Name:   "get_method_not_allowed",
   911  					Method: http.MethodGet,
   912  					Header: http.Header{
   913  						"Content-Type": []string{"application/json"},
   914  					},
   915  				},
   916  				{
   917  					Name:   "put_method_not_allowed",
   918  					Method: http.MethodPut,
   919  					Header: http.Header{
   920  						"Content-Type": []string{"application/json"},
   921  					},
   922  					Body: toJSON(map[string]any{
   923  						"name":  "service_name",
   924  						"start": time.Now().Add(-1 * time.Hour).UnixMilli(),
   925  						"end":   time.Now().UnixMilli(),
   926  					}),
   927  				},
   928  				{
   929  					Name:   "delete_method_not_allowed",
   930  					Method: http.MethodDelete,
   931  					Header: http.Header{
   932  						"Content-Type": []string{"application/json"},
   933  					},
   934  					Body: toJSON(map[string]any{
   935  						"name":  "service_name",
   936  						"start": time.Now().Add(-1 * time.Hour).UnixMilli(),
   937  						"end":   time.Now().UnixMilli(),
   938  					}),
   939  				},
   940  				{
   941  					Name:   "patch_method_not_allowed",
   942  					Method: http.MethodPatch,
   943  					Header: http.Header{
   944  						"Content-Type": []string{"application/json"},
   945  					},
   946  					Body: toJSON(map[string]any{
   947  						"name":  "service_name",
   948  						"start": time.Now().Add(-1 * time.Hour).UnixMilli(),
   949  						"end":   time.Now().UnixMilli(),
   950  					}),
   951  				},
   952  			},
   953  			http.StatusUnsupportedMediaType: {
   954  				{
   955  					Name:   "invalid_content_type",
   956  					Method: http.MethodPost,
   957  					Header: http.Header{
   958  						"Content-Type": []string{"text/plain"},
   959  					},
   960  					Body: toJSON(map[string]any{
   961  						"name":  "service_name",
   962  						"start": time.Now().Add(-1 * time.Hour).UnixMilli(),
   963  						"end":   time.Now().UnixMilli(),
   964  					}),
   965  				},
   966  			},
   967  		},
   968  	}
   969  
   970  	labelNamesTests := EndpointTestGroup{
   971  		Path: "/querier.v1.QuerierService/LabelNames",
   972  		Tests: map[int][]Test{
   973  			http.StatusOK: {
   974  				{
   975  					Name:   "valid",
   976  					Method: http.MethodPost,
   977  					Header: http.Header{
   978  						"Content-Type": []string{"application/json"},
   979  					},
   980  					Body: toJSON(map[string]any{
   981  						"start": time.Now().Add(-1 * time.Hour).UnixMilli(),
   982  						"end":   time.Now().UnixMilli(),
   983  					}),
   984  				},
   985  				{
   986  					Name:   "valid_with_matchers",
   987  					Method: http.MethodPost,
   988  					Header: http.Header{
   989  						"Content-Type": []string{"application/json"},
   990  					},
   991  					Body: toJSON(map[string]any{
   992  						"matchers": []string{`{namespace="default"}`},
   993  						"start":    time.Now().Add(-1 * time.Hour).UnixMilli(),
   994  						"end":      time.Now().UnixMilli(),
   995  					}),
   996  				},
   997  			},
   998  			http.StatusBadRequest: {
   999  				{
  1000  					Name:   "valid_no_time_range",
  1001  					Method: http.MethodPost,
  1002  					Header: http.Header{
  1003  						"Content-Type": []string{"application/json"},
  1004  					},
  1005  					Body: toJSON(map[string]any{}),
  1006  					// V1 allows no time ranges
  1007  					WantV1StatusCode: http.StatusOK,
  1008  				},
  1009  				{
  1010  					Name:   "invalid_json",
  1011  					Method: http.MethodPost,
  1012  					Header: http.Header{
  1013  						"Content-Type": []string{"application/json"},
  1014  					},
  1015  					Body: `{"invalid json"`,
  1016  				},
  1017  				{
  1018  					Name:   "empty_body",
  1019  					Method: http.MethodPost,
  1020  					Header: http.Header{
  1021  						"Content-Type": []string{"application/json"},
  1022  					},
  1023  					Body: "",
  1024  				},
  1025  				{
  1026  					Name:   "start_after_end",
  1027  					Method: http.MethodPost,
  1028  					Header: http.Header{
  1029  						"Content-Type": []string{"application/json"},
  1030  					},
  1031  					Body: toJSON(map[string]any{
  1032  						"start": time.Now().UnixMilli(),
  1033  						"end":   time.Now().Add(-1 * time.Hour).UnixMilli(),
  1034  					}),
  1035  				},
  1036  				{
  1037  					Name:   "invalid_matchers_syntax",
  1038  					Method: http.MethodPost,
  1039  					Header: http.Header{
  1040  						"Content-Type": []string{"application/json"},
  1041  					},
  1042  					Body: toJSON(map[string]any{
  1043  						"matchers": []string{"!bad_syntax!"},
  1044  						"start":    time.Now().Add(-1 * time.Hour).UnixMilli(),
  1045  						"end":      time.Now().UnixMilli(),
  1046  					}),
  1047  					WantV1StatusCode: http.StatusOK,
  1048  				},
  1049  			},
  1050  			http.StatusMethodNotAllowed: {
  1051  				{
  1052  					Name:   "get_method_not_allowed",
  1053  					Method: http.MethodGet,
  1054  					Header: http.Header{
  1055  						"Content-Type": []string{"application/json"},
  1056  					},
  1057  				},
  1058  				{
  1059  					Name:   "put_method_not_allowed",
  1060  					Method: http.MethodPut,
  1061  					Header: http.Header{
  1062  						"Content-Type": []string{"application/json"},
  1063  					},
  1064  					Body: toJSON(map[string]any{
  1065  						"start": time.Now().Add(-1 * time.Hour).UnixMilli(),
  1066  						"end":   time.Now().UnixMilli(),
  1067  					}),
  1068  				},
  1069  				{
  1070  					Name:   "delete_method_not_allowed",
  1071  					Method: http.MethodDelete,
  1072  					Header: http.Header{
  1073  						"Content-Type": []string{"application/json"},
  1074  					},
  1075  					Body: toJSON(map[string]any{
  1076  						"start": time.Now().Add(-1 * time.Hour).UnixMilli(),
  1077  						"end":   time.Now().UnixMilli(),
  1078  					}),
  1079  				},
  1080  				{
  1081  					Name:   "patch_method_not_allowed",
  1082  					Method: http.MethodPatch,
  1083  					Header: http.Header{
  1084  						"Content-Type": []string{"application/json"},
  1085  					},
  1086  					Body: toJSON(map[string]any{
  1087  						"start": time.Now().Add(-1 * time.Hour).UnixMilli(),
  1088  						"end":   time.Now().UnixMilli(),
  1089  					}),
  1090  				},
  1091  			},
  1092  			http.StatusUnsupportedMediaType: {
  1093  				{
  1094  					Name:   "invalid_content_type",
  1095  					Method: http.MethodPost,
  1096  					Header: http.Header{
  1097  						"Content-Type": []string{"text/plain"},
  1098  					},
  1099  					Body: toJSON(map[string]any{
  1100  						"start": time.Now().Add(-1 * time.Hour).UnixMilli(),
  1101  						"end":   time.Now().UnixMilli(),
  1102  					}),
  1103  				},
  1104  			},
  1105  		},
  1106  	}
  1107  
  1108  	seriesTests := EndpointTestGroup{
  1109  		Path: "/querier.v1.QuerierService/Series",
  1110  		Tests: map[int][]Test{
  1111  			http.StatusOK: {
  1112  				{
  1113  					Name:   "valid",
  1114  					Method: http.MethodPost,
  1115  					Header: http.Header{
  1116  						"Content-Type": []string{"application/json"},
  1117  					},
  1118  					Body: toJSON(map[string]any{
  1119  						"matchers": []string{`{service_name="test"}`},
  1120  						"start":    time.Now().Add(-1 * time.Hour).UnixMilli(),
  1121  						"end":      time.Now().UnixMilli(),
  1122  					}),
  1123  				},
  1124  				{
  1125  					Name:   "valid_with_label_names",
  1126  					Method: http.MethodPost,
  1127  					Header: http.Header{
  1128  						"Content-Type": []string{"application/json"},
  1129  					},
  1130  					Body: toJSON(map[string]any{
  1131  						"matchers":   []string{`{service_name="test"}`},
  1132  						"labelNames": []string{"service_name", "namespace"},
  1133  						"start":      time.Now().Add(-1 * time.Hour).UnixMilli(),
  1134  						"end":        time.Now().UnixMilli(),
  1135  					}),
  1136  				},
  1137  				{
  1138  					Name:   "valid_no_matchers",
  1139  					Method: http.MethodPost,
  1140  					Header: http.Header{
  1141  						"Content-Type": []string{"application/json"},
  1142  					},
  1143  					Body: toJSON(map[string]any{
  1144  						"start": time.Now().Add(-1 * time.Hour).UnixMilli(),
  1145  						"end":   time.Now().UnixMilli(),
  1146  					}),
  1147  				},
  1148  			},
  1149  			http.StatusBadRequest: {
  1150  				{
  1151  					Name:   "invalid_json",
  1152  					Method: http.MethodPost,
  1153  					Header: http.Header{
  1154  						"Content-Type": []string{"application/json"},
  1155  					},
  1156  					Body: `{"invalid json"`,
  1157  				},
  1158  				{
  1159  					Name:   "empty_body",
  1160  					Method: http.MethodPost,
  1161  					Header: http.Header{
  1162  						"Content-Type": []string{"application/json"},
  1163  					},
  1164  					Body: "",
  1165  				},
  1166  				{
  1167  					Name:   "start_after_end",
  1168  					Method: http.MethodPost,
  1169  					Header: http.Header{
  1170  						"Content-Type": []string{"application/json"},
  1171  					},
  1172  					Body: toJSON(map[string]any{
  1173  						"matchers": []string{`{service_name="test"}`},
  1174  						"start":    time.Now().UnixMilli(),
  1175  						"end":      time.Now().Add(-1 * time.Hour).UnixMilli(),
  1176  					}),
  1177  				},
  1178  				{
  1179  					Name:   "invalid_no_time_range",
  1180  					Method: http.MethodPost,
  1181  					Header: http.Header{
  1182  						"Content-Type": []string{"application/json"},
  1183  					},
  1184  					Body: toJSON(map[string]any{
  1185  						"matchers": []string{`{service_name="test"}`},
  1186  					}),
  1187  					// V1 allows no time ranges
  1188  					WantV1StatusCode: http.StatusOK,
  1189  				},
  1190  				{
  1191  					Name:   "invalid_matchers_syntax",
  1192  					Method: http.MethodPost,
  1193  					Header: http.Header{
  1194  						"Content-Type": []string{"application/json"},
  1195  					},
  1196  					Body: toJSON(map[string]any{
  1197  						"matchers": []string{"!bad_syntax!"},
  1198  						"start":    time.Now().Add(-1 * time.Hour).UnixMilli(),
  1199  						"end":      time.Now().UnixMilli(),
  1200  					}),
  1201  					WantV1StatusCode: http.StatusOK,
  1202  				},
  1203  			},
  1204  			http.StatusMethodNotAllowed: {
  1205  				{
  1206  					Name:   "get_method_not_allowed",
  1207  					Method: http.MethodGet,
  1208  					Header: http.Header{
  1209  						"Content-Type": []string{"application/json"},
  1210  					},
  1211  				},
  1212  				{
  1213  					Name:   "put_method_not_allowed",
  1214  					Method: http.MethodPut,
  1215  					Header: http.Header{
  1216  						"Content-Type": []string{"application/json"},
  1217  					},
  1218  					Body: toJSON(map[string]any{
  1219  						"matchers": []string{`{service_name="test"}`},
  1220  						"start":    time.Now().Add(-1 * time.Hour).UnixMilli(),
  1221  						"end":      time.Now().UnixMilli(),
  1222  					}),
  1223  				},
  1224  				{
  1225  					Name:   "delete_method_not_allowed",
  1226  					Method: http.MethodDelete,
  1227  					Header: http.Header{
  1228  						"Content-Type": []string{"application/json"},
  1229  					},
  1230  					Body: toJSON(map[string]any{
  1231  						"matchers": []string{`{service_name="test"}`},
  1232  						"start":    time.Now().Add(-1 * time.Hour).UnixMilli(),
  1233  						"end":      time.Now().UnixMilli(),
  1234  					}),
  1235  				},
  1236  				{
  1237  					Name:   "patch_method_not_allowed",
  1238  					Method: http.MethodPatch,
  1239  					Header: http.Header{
  1240  						"Content-Type": []string{"application/json"},
  1241  					},
  1242  					Body: toJSON(map[string]any{
  1243  						"matchers": []string{`{service_name="test"}`},
  1244  						"start":    time.Now().Add(-1 * time.Hour).UnixMilli(),
  1245  						"end":      time.Now().UnixMilli(),
  1246  					}),
  1247  				},
  1248  			},
  1249  			http.StatusUnsupportedMediaType: {
  1250  				{
  1251  					Name:   "invalid_content_type",
  1252  					Method: http.MethodPost,
  1253  					Header: http.Header{
  1254  						"Content-Type": []string{"text/plain"},
  1255  					},
  1256  					Body: toJSON(map[string]any{
  1257  						"matchers": []string{`{service_name="test"}`},
  1258  						"start":    time.Now().Add(-1 * time.Hour).UnixMilli(),
  1259  						"end":      time.Now().UnixMilli(),
  1260  					}),
  1261  				},
  1262  			},
  1263  		},
  1264  	}
  1265  
  1266  	selectMergeStacktracesTests := EndpointTestGroup{
  1267  		Path: "/querier.v1.QuerierService/SelectMergeStacktraces",
  1268  		Tests: map[int][]Test{
  1269  			http.StatusOK: {
  1270  				{
  1271  					Name:   "valid",
  1272  					Method: http.MethodPost,
  1273  					Header: http.Header{
  1274  						"Content-Type": []string{"application/json"},
  1275  					},
  1276  					Body: toJSON(map[string]any{
  1277  						"profileTypeID": profileTypeProcessCPU,
  1278  						"labelSelector": "{}",
  1279  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1280  						"end":           time.Now().UnixMilli(),
  1281  					}),
  1282  				},
  1283  				{
  1284  					Name:   "valid_with_max_nodes",
  1285  					Method: http.MethodPost,
  1286  					Header: http.Header{
  1287  						"Content-Type": []string{"application/json"},
  1288  					},
  1289  					Body: toJSON(map[string]any{
  1290  						"profileTypeID": profileTypeProcessCPU,
  1291  						"labelSelector": "{}",
  1292  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1293  						"end":           time.Now().UnixMilli(),
  1294  						"maxNodes":      1024,
  1295  					}),
  1296  				},
  1297  				{
  1298  					Name:   "valid_with_format_tree",
  1299  					Method: http.MethodPost,
  1300  					Header: http.Header{
  1301  						"Content-Type": []string{"application/json"},
  1302  					},
  1303  					Body: toJSON(map[string]any{
  1304  						"profileTypeID": profileTypeProcessCPU,
  1305  						"labelSelector": "{}",
  1306  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1307  						"end":           time.Now().UnixMilli(),
  1308  						"format":        2,
  1309  					}),
  1310  				},
  1311  				{
  1312  					Name:   "valid_with_label_selector",
  1313  					Method: http.MethodPost,
  1314  					Header: http.Header{
  1315  						"Content-Type": []string{"application/json"},
  1316  					},
  1317  					Body: toJSON(map[string]any{
  1318  						"profileTypeID": profileTypeProcessCPU,
  1319  						"labelSelector": `{service_name="test"}`,
  1320  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1321  						"end":           time.Now().UnixMilli(),
  1322  					}),
  1323  				},
  1324  			},
  1325  			http.StatusBadRequest: {
  1326  				{
  1327  					Name:   "missing_profile_type_id",
  1328  					Method: http.MethodPost,
  1329  					Header: http.Header{
  1330  						"Content-Type": []string{"application/json"},
  1331  					},
  1332  					Body: toJSON(map[string]any{
  1333  						"labelSelector": "{}",
  1334  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1335  						"end":           time.Now().UnixMilli(),
  1336  					}),
  1337  				},
  1338  				{
  1339  					Name:   "empty_profile_type_id",
  1340  					Method: http.MethodPost,
  1341  					Header: http.Header{
  1342  						"Content-Type": []string{"application/json"},
  1343  					},
  1344  					Body: toJSON(map[string]any{
  1345  						"profileTypeID": "",
  1346  						"labelSelector": "{}",
  1347  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1348  						"end":           time.Now().UnixMilli(),
  1349  					}),
  1350  				},
  1351  				{
  1352  					Name:   "invalid_profile_type_format",
  1353  					Method: http.MethodPost,
  1354  					Header: http.Header{
  1355  						"Content-Type": []string{"application/json"},
  1356  					},
  1357  					Body: toJSON(map[string]any{
  1358  						"profileTypeID": "invalid_format",
  1359  						"labelSelector": "{}",
  1360  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1361  						"end":           time.Now().UnixMilli(),
  1362  					}),
  1363  				},
  1364  				{
  1365  					Name:   "missing_label_selector",
  1366  					Method: http.MethodPost,
  1367  					Header: http.Header{
  1368  						"Content-Type": []string{"application/json"},
  1369  					},
  1370  					Body: toJSON(map[string]any{
  1371  						"profileTypeID": profileTypeProcessCPU,
  1372  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1373  						"end":           time.Now().UnixMilli(),
  1374  					}),
  1375  				},
  1376  				{
  1377  					Name:   "invalid_label_selector",
  1378  					Method: http.MethodPost,
  1379  					Header: http.Header{
  1380  						"Content-Type": []string{"application/json"},
  1381  					},
  1382  					Body: toJSON(map[string]any{
  1383  						"profileTypeID": profileTypeProcessCPU,
  1384  						"labelSelector": "!bad_syntax!",
  1385  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1386  						"end":           time.Now().UnixMilli(),
  1387  					}),
  1388  				},
  1389  				{
  1390  					Name:   "invalid_json",
  1391  					Method: http.MethodPost,
  1392  					Header: http.Header{
  1393  						"Content-Type": []string{"application/json"},
  1394  					},
  1395  					Body: `{"invalid json"`,
  1396  				},
  1397  				{
  1398  					Name:   "empty_body",
  1399  					Method: http.MethodPost,
  1400  					Header: http.Header{
  1401  						"Content-Type": []string{"application/json"},
  1402  					},
  1403  					Body: "",
  1404  				},
  1405  				{
  1406  					Name:   "start_after_end",
  1407  					Method: http.MethodPost,
  1408  					Header: http.Header{
  1409  						"Content-Type": []string{"application/json"},
  1410  					},
  1411  					Body: toJSON(map[string]any{
  1412  						"profileTypeID": profileTypeProcessCPU,
  1413  						"labelSelector": "{}",
  1414  						"start":         time.Now().UnixMilli(),
  1415  						"end":           time.Now().Add(-1 * time.Hour).UnixMilli(),
  1416  					}),
  1417  				},
  1418  				{
  1419  					Name:   "invalid_no_time_range",
  1420  					Method: http.MethodPost,
  1421  					Header: http.Header{
  1422  						"Content-Type": []string{"application/json"},
  1423  					},
  1424  					Body: toJSON(map[string]any{
  1425  						"profileTypeID": profileTypeProcessCPU,
  1426  						"labelSelector": "{}",
  1427  					}),
  1428  				},
  1429  			},
  1430  			http.StatusMethodNotAllowed: {
  1431  				{
  1432  					Name:   "get_method_not_allowed",
  1433  					Method: http.MethodGet,
  1434  					Header: http.Header{
  1435  						"Content-Type": []string{"application/json"},
  1436  					},
  1437  				},
  1438  				{
  1439  					Name:   "put_method_not_allowed",
  1440  					Method: http.MethodPut,
  1441  					Header: http.Header{
  1442  						"Content-Type": []string{"application/json"},
  1443  					},
  1444  					Body: toJSON(map[string]any{
  1445  						"profileTypeID": profileTypeProcessCPU,
  1446  						"labelSelector": "{}",
  1447  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1448  						"end":           time.Now().UnixMilli(),
  1449  					}),
  1450  				},
  1451  				{
  1452  					Name:   "delete_method_not_allowed",
  1453  					Method: http.MethodDelete,
  1454  					Header: http.Header{
  1455  						"Content-Type": []string{"application/json"},
  1456  					},
  1457  					Body: toJSON(map[string]any{
  1458  						"profileTypeID": profileTypeProcessCPU,
  1459  						"labelSelector": "{}",
  1460  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1461  						"end":           time.Now().UnixMilli(),
  1462  					}),
  1463  				},
  1464  				{
  1465  					Name:   "patch_method_not_allowed",
  1466  					Method: http.MethodPatch,
  1467  					Header: http.Header{
  1468  						"Content-Type": []string{"application/json"},
  1469  					},
  1470  					Body: toJSON(map[string]any{
  1471  						"profileTypeID": profileTypeProcessCPU,
  1472  						"labelSelector": "{}",
  1473  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1474  						"end":           time.Now().UnixMilli(),
  1475  					}),
  1476  				},
  1477  			},
  1478  			http.StatusUnsupportedMediaType: {
  1479  				{
  1480  					Name:   "invalid_content_type",
  1481  					Method: http.MethodPost,
  1482  					Header: http.Header{
  1483  						"Content-Type": []string{"text/plain"},
  1484  					},
  1485  					Body: toJSON(map[string]any{
  1486  						"profileTypeID": profileTypeProcessCPU,
  1487  						"labelSelector": "{}",
  1488  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1489  						"end":           time.Now().UnixMilli(),
  1490  					}),
  1491  				},
  1492  			},
  1493  		},
  1494  	}
  1495  
  1496  	selectMergeProfileTests := EndpointTestGroup{
  1497  		Path: "/querier.v1.QuerierService/SelectMergeProfile",
  1498  		Tests: map[int][]Test{
  1499  			http.StatusOK: {
  1500  				{
  1501  					Name:   "valid",
  1502  					Method: http.MethodPost,
  1503  					Header: http.Header{
  1504  						"Content-Type": []string{"application/json"},
  1505  					},
  1506  					Body: toJSON(map[string]any{
  1507  						"profileTypeID": profileTypeProcessCPU,
  1508  						"labelSelector": "{}",
  1509  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1510  						"end":           time.Now().UnixMilli(),
  1511  					}),
  1512  				},
  1513  				{
  1514  					Name:   "valid_with_max_nodes",
  1515  					Method: http.MethodPost,
  1516  					Header: http.Header{
  1517  						"Content-Type": []string{"application/json"},
  1518  					},
  1519  					Body: toJSON(map[string]any{
  1520  						"profileTypeID": profileTypeProcessCPU,
  1521  						"labelSelector": "{}",
  1522  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1523  						"end":           time.Now().UnixMilli(),
  1524  						"maxNodes":      1024,
  1525  					}),
  1526  				},
  1527  				{
  1528  					Name:   "valid_with_label_selector",
  1529  					Method: http.MethodPost,
  1530  					Header: http.Header{
  1531  						"Content-Type": []string{"application/json"},
  1532  					},
  1533  					Body: toJSON(map[string]any{
  1534  						"profileTypeID": profileTypeProcessCPU,
  1535  						"labelSelector": `{service_name="test"}`,
  1536  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1537  						"end":           time.Now().UnixMilli(),
  1538  					}),
  1539  				},
  1540  			},
  1541  			http.StatusBadRequest: {
  1542  				{
  1543  					Name:   "missing_profile_type_id",
  1544  					Method: http.MethodPost,
  1545  					Header: http.Header{
  1546  						"Content-Type": []string{"application/json"},
  1547  					},
  1548  					Body: toJSON(map[string]any{
  1549  						"labelSelector": "{}",
  1550  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1551  						"end":           time.Now().UnixMilli(),
  1552  					}),
  1553  				},
  1554  				{
  1555  					Name:   "empty_profile_type_id",
  1556  					Method: http.MethodPost,
  1557  					Header: http.Header{
  1558  						"Content-Type": []string{"application/json"},
  1559  					},
  1560  					Body: toJSON(map[string]any{
  1561  						"profileTypeID": "",
  1562  						"labelSelector": "{}",
  1563  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1564  						"end":           time.Now().UnixMilli(),
  1565  					}),
  1566  				},
  1567  				{
  1568  					Name:   "invalid_profile_type_format",
  1569  					Method: http.MethodPost,
  1570  					Header: http.Header{
  1571  						"Content-Type": []string{"application/json"},
  1572  					},
  1573  					Body: toJSON(map[string]any{
  1574  						"profileTypeID": "invalid_format",
  1575  						"labelSelector": "{}",
  1576  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1577  						"end":           time.Now().UnixMilli(),
  1578  					}),
  1579  				},
  1580  				{
  1581  					Name:   "missing_label_selector",
  1582  					Method: http.MethodPost,
  1583  					Header: http.Header{
  1584  						"Content-Type": []string{"application/json"},
  1585  					},
  1586  					Body: toJSON(map[string]any{
  1587  						"profileTypeID": profileTypeProcessCPU,
  1588  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1589  						"end":           time.Now().UnixMilli(),
  1590  					}),
  1591  				},
  1592  				{
  1593  					Name:   "invalid_label_selector",
  1594  					Method: http.MethodPost,
  1595  					Header: http.Header{
  1596  						"Content-Type": []string{"application/json"},
  1597  					},
  1598  					Body: toJSON(map[string]any{
  1599  						"profileTypeID": profileTypeProcessCPU,
  1600  						"labelSelector": "!bad_syntax!",
  1601  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1602  						"end":           time.Now().UnixMilli(),
  1603  					}),
  1604  				},
  1605  				{
  1606  					Name:   "invalid_json",
  1607  					Method: http.MethodPost,
  1608  					Header: http.Header{
  1609  						"Content-Type": []string{"application/json"},
  1610  					},
  1611  					Body: `{"invalid json"`,
  1612  				},
  1613  				{
  1614  					Name:   "empty_body",
  1615  					Method: http.MethodPost,
  1616  					Header: http.Header{
  1617  						"Content-Type": []string{"application/json"},
  1618  					},
  1619  					Body: "",
  1620  				},
  1621  				{
  1622  					Name:   "start_after_end",
  1623  					Method: http.MethodPost,
  1624  					Header: http.Header{
  1625  						"Content-Type": []string{"application/json"},
  1626  					},
  1627  					Body: toJSON(map[string]any{
  1628  						"profileTypeID": profileTypeProcessCPU,
  1629  						"labelSelector": "{}",
  1630  						"start":         time.Now().UnixMilli(),
  1631  						"end":           time.Now().Add(-1 * time.Hour).UnixMilli(),
  1632  					}),
  1633  				},
  1634  				{
  1635  					Name:   "valid_no_time_range",
  1636  					Method: http.MethodPost,
  1637  					Header: http.Header{
  1638  						"Content-Type": []string{"application/json"},
  1639  					},
  1640  					Body: toJSON(map[string]any{
  1641  						"profileTypeID": profileTypeProcessCPU,
  1642  						"labelSelector": "{}",
  1643  					}),
  1644  				},
  1645  			},
  1646  			http.StatusMethodNotAllowed: {
  1647  				{
  1648  					Name:   "get_method_not_allowed",
  1649  					Method: http.MethodGet,
  1650  					Header: http.Header{
  1651  						"Content-Type": []string{"application/json"},
  1652  					},
  1653  				},
  1654  				{
  1655  					Name:   "put_method_not_allowed",
  1656  					Method: http.MethodPut,
  1657  					Header: http.Header{
  1658  						"Content-Type": []string{"application/json"},
  1659  					},
  1660  					Body: toJSON(map[string]any{
  1661  						"profileTypeID": profileTypeProcessCPU,
  1662  						"labelSelector": "{}",
  1663  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1664  						"end":           time.Now().UnixMilli(),
  1665  					}),
  1666  				},
  1667  				{
  1668  					Name:   "delete_method_not_allowed",
  1669  					Method: http.MethodDelete,
  1670  					Header: http.Header{
  1671  						"Content-Type": []string{"application/json"},
  1672  					},
  1673  					Body: toJSON(map[string]any{
  1674  						"profileTypeID": profileTypeProcessCPU,
  1675  						"labelSelector": "{}",
  1676  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1677  						"end":           time.Now().UnixMilli(),
  1678  					}),
  1679  				},
  1680  				{
  1681  					Name:   "patch_method_not_allowed",
  1682  					Method: http.MethodPatch,
  1683  					Header: http.Header{
  1684  						"Content-Type": []string{"application/json"},
  1685  					},
  1686  					Body: toJSON(map[string]any{
  1687  						"profileTypeID": profileTypeProcessCPU,
  1688  						"labelSelector": "{}",
  1689  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1690  						"end":           time.Now().UnixMilli(),
  1691  					}),
  1692  				},
  1693  			},
  1694  			http.StatusUnsupportedMediaType: {
  1695  				{
  1696  					Name:   "invalid_content_type",
  1697  					Method: http.MethodPost,
  1698  					Header: http.Header{
  1699  						"Content-Type": []string{"text/plain"},
  1700  					},
  1701  					Body: toJSON(map[string]any{
  1702  						"profileTypeID": profileTypeProcessCPU,
  1703  						"labelSelector": "{}",
  1704  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1705  						"end":           time.Now().UnixMilli(),
  1706  					}),
  1707  				},
  1708  			},
  1709  		},
  1710  	}
  1711  
  1712  	selectSeriesTests := EndpointTestGroup{
  1713  		Path: "/querier.v1.QuerierService/SelectSeries",
  1714  		Tests: map[int][]Test{
  1715  			http.StatusOK: {
  1716  				{
  1717  					Name:   "valid",
  1718  					Method: http.MethodPost,
  1719  					Header: http.Header{
  1720  						"Content-Type": []string{"application/json"},
  1721  					},
  1722  					Body: toJSON(map[string]any{
  1723  						"profileTypeID": profileTypeProcessCPU,
  1724  						"labelSelector": "{}",
  1725  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1726  						"end":           time.Now().UnixMilli(),
  1727  						"step":          15.0,
  1728  					}),
  1729  				},
  1730  				{
  1731  					Name:   "valid_with_group_by",
  1732  					Method: http.MethodPost,
  1733  					Header: http.Header{
  1734  						"Content-Type": []string{"application/json"},
  1735  					},
  1736  					Body: toJSON(map[string]any{
  1737  						"profileTypeID": profileTypeProcessCPU,
  1738  						"labelSelector": "{}",
  1739  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1740  						"end":           time.Now().UnixMilli(),
  1741  						"step":          15.0,
  1742  						"groupBy":       []string{"service_name"},
  1743  					}),
  1744  				},
  1745  				{
  1746  					Name:   "valid_with_limit",
  1747  					Method: http.MethodPost,
  1748  					Header: http.Header{
  1749  						"Content-Type": []string{"application/json"},
  1750  					},
  1751  					Body: toJSON(map[string]any{
  1752  						"profileTypeID": profileTypeProcessCPU,
  1753  						"labelSelector": "{}",
  1754  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1755  						"end":           time.Now().UnixMilli(),
  1756  						"step":          15.0,
  1757  						"limit":         10,
  1758  					}),
  1759  				},
  1760  			},
  1761  			http.StatusBadRequest: {
  1762  				{
  1763  					Name:   "missing_profile_type_id",
  1764  					Method: http.MethodPost,
  1765  					Header: http.Header{
  1766  						"Content-Type": []string{"application/json"},
  1767  					},
  1768  					Body: toJSON(map[string]any{
  1769  						"labelSelector": "{}",
  1770  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1771  						"end":           time.Now().UnixMilli(),
  1772  						"step":          15.0,
  1773  					}),
  1774  				},
  1775  				{
  1776  					Name:   "empty_profile_type_id",
  1777  					Method: http.MethodPost,
  1778  					Header: http.Header{
  1779  						"Content-Type": []string{"application/json"},
  1780  					},
  1781  					Body: toJSON(map[string]any{
  1782  						"profileTypeID": "",
  1783  						"labelSelector": "{}",
  1784  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1785  						"end":           time.Now().UnixMilli(),
  1786  						"step":          15.0,
  1787  					}),
  1788  				},
  1789  				{
  1790  					Name:   "invalid_profile_type_format",
  1791  					Method: http.MethodPost,
  1792  					Header: http.Header{
  1793  						"Content-Type": []string{"application/json"},
  1794  					},
  1795  					Body: toJSON(map[string]any{
  1796  						"profileTypeID": "invalid_format",
  1797  						"labelSelector": "{}",
  1798  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1799  						"end":           time.Now().UnixMilli(),
  1800  						"step":          15.0,
  1801  					}),
  1802  				},
  1803  				{
  1804  					Name:   "missing_label_selector",
  1805  					Method: http.MethodPost,
  1806  					Header: http.Header{
  1807  						"Content-Type": []string{"application/json"},
  1808  					},
  1809  					Body: toJSON(map[string]any{
  1810  						"profileTypeID": profileTypeProcessCPU,
  1811  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1812  						"end":           time.Now().UnixMilli(),
  1813  						"step":          15.0,
  1814  					}),
  1815  				},
  1816  				{
  1817  					Name:   "invalid_label_selector",
  1818  					Method: http.MethodPost,
  1819  					Header: http.Header{
  1820  						"Content-Type": []string{"application/json"},
  1821  					},
  1822  					Body: toJSON(map[string]any{
  1823  						"profileTypeID": profileTypeProcessCPU,
  1824  						"labelSelector": "!bad_syntax!",
  1825  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1826  						"end":           time.Now().UnixMilli(),
  1827  						"step":          15.0,
  1828  					}),
  1829  				},
  1830  				{
  1831  					Name:   "invalid_no_step",
  1832  					Method: http.MethodPost,
  1833  					Header: http.Header{
  1834  						"Content-Type": []string{"application/json"},
  1835  					},
  1836  					Body: toJSON(map[string]any{
  1837  						"profileTypeID": profileTypeProcessCPU,
  1838  						"labelSelector": "{}",
  1839  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1840  						"end":           time.Now().UnixMilli(),
  1841  					}),
  1842  					WantV1StatusCode: http.StatusBadRequest,
  1843  				},
  1844  				{
  1845  					Name:   "invalid_json",
  1846  					Method: http.MethodPost,
  1847  					Header: http.Header{
  1848  						"Content-Type": []string{"application/json"},
  1849  					},
  1850  					Body: `{"invalid json"`,
  1851  				},
  1852  				{
  1853  					Name:   "empty_body",
  1854  					Method: http.MethodPost,
  1855  					Header: http.Header{
  1856  						"Content-Type": []string{"application/json"},
  1857  					},
  1858  					Body: "",
  1859  				},
  1860  				{
  1861  					Name:   "start_after_end",
  1862  					Method: http.MethodPost,
  1863  					Header: http.Header{
  1864  						"Content-Type": []string{"application/json"},
  1865  					},
  1866  					Body: toJSON(map[string]any{
  1867  						"profileTypeID": profileTypeProcessCPU,
  1868  						"labelSelector": "{}",
  1869  						"start":         time.Now().UnixMilli(),
  1870  						"end":           time.Now().Add(-1 * time.Hour).UnixMilli(),
  1871  						"step":          15.0,
  1872  					}),
  1873  				},
  1874  				{
  1875  					Name:   "invalid_no_time_range",
  1876  					Method: http.MethodPost,
  1877  					Header: http.Header{
  1878  						"Content-Type": []string{"application/json"},
  1879  					},
  1880  					Body: toJSON(map[string]any{
  1881  						"profileTypeID": profileTypeProcessCPU,
  1882  						"labelSelector": "{}",
  1883  						"step":          15.0,
  1884  					}),
  1885  				},
  1886  			},
  1887  			http.StatusMethodNotAllowed: {
  1888  				{
  1889  					Name:   "get_method_not_allowed",
  1890  					Method: http.MethodGet,
  1891  					Header: http.Header{
  1892  						"Content-Type": []string{"application/json"},
  1893  					},
  1894  				},
  1895  				{
  1896  					Name:   "put_method_not_allowed",
  1897  					Method: http.MethodPut,
  1898  					Header: http.Header{
  1899  						"Content-Type": []string{"application/json"},
  1900  					},
  1901  					Body: toJSON(map[string]any{
  1902  						"profileTypeID": profileTypeProcessCPU,
  1903  						"labelSelector": "{}",
  1904  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1905  						"end":           time.Now().UnixMilli(),
  1906  						"step":          15.0,
  1907  					}),
  1908  				},
  1909  				{
  1910  					Name:   "delete_method_not_allowed",
  1911  					Method: http.MethodDelete,
  1912  					Header: http.Header{
  1913  						"Content-Type": []string{"application/json"},
  1914  					},
  1915  					Body: toJSON(map[string]any{
  1916  						"profileTypeID": profileTypeProcessCPU,
  1917  						"labelSelector": "{}",
  1918  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1919  						"end":           time.Now().UnixMilli(),
  1920  						"step":          15.0,
  1921  					}),
  1922  				},
  1923  				{
  1924  					Name:   "patch_method_not_allowed",
  1925  					Method: http.MethodPatch,
  1926  					Header: http.Header{
  1927  						"Content-Type": []string{"application/json"},
  1928  					},
  1929  					Body: toJSON(map[string]any{
  1930  						"profileTypeID": profileTypeProcessCPU,
  1931  						"labelSelector": "{}",
  1932  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1933  						"end":           time.Now().UnixMilli(),
  1934  						"step":          15.0,
  1935  					}),
  1936  				},
  1937  			},
  1938  			http.StatusUnsupportedMediaType: {
  1939  				{
  1940  					Name:   "invalid_content_type",
  1941  					Method: http.MethodPost,
  1942  					Header: http.Header{
  1943  						"Content-Type": []string{"text/plain"},
  1944  					},
  1945  					Body: toJSON(map[string]any{
  1946  						"profileTypeID": profileTypeProcessCPU,
  1947  						"labelSelector": "{}",
  1948  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1949  						"end":           time.Now().UnixMilli(),
  1950  						"step":          15.0,
  1951  					}),
  1952  				},
  1953  			},
  1954  		},
  1955  	}
  1956  
  1957  	diffTests := EndpointTestGroup{
  1958  		Path: "/querier.v1.QuerierService/Diff",
  1959  		Tests: map[int][]Test{
  1960  			http.StatusOK: {
  1961  				{
  1962  					Name:   "valid",
  1963  					Method: http.MethodPost,
  1964  					Header: http.Header{
  1965  						"Content-Type": []string{"application/json"},
  1966  					},
  1967  					Body: toJSON(map[string]any{
  1968  						"left": map[string]any{
  1969  							"profileTypeID": profileTypeProcessCPU,
  1970  							"labelSelector": "{}",
  1971  							"start":         time.Now().Add(-2 * time.Hour).UnixMilli(),
  1972  							"end":           time.Now().Add(-1 * time.Hour).UnixMilli(),
  1973  						},
  1974  						"right": map[string]any{
  1975  							"profileTypeID": profileTypeProcessCPU,
  1976  							"labelSelector": "{}",
  1977  							"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  1978  							"end":           time.Now().UnixMilli(),
  1979  						},
  1980  					}),
  1981  				},
  1982  				{
  1983  					Name:   "valid_with_max_nodes",
  1984  					Method: http.MethodPost,
  1985  					Header: http.Header{
  1986  						"Content-Type": []string{"application/json"},
  1987  					},
  1988  					Body: toJSON(map[string]any{
  1989  						"left": map[string]any{
  1990  							"profileTypeID": profileTypeProcessCPU,
  1991  							"labelSelector": "{}",
  1992  							"start":         time.Now().Add(-2 * time.Hour).UnixMilli(),
  1993  							"end":           time.Now().Add(-1 * time.Hour).UnixMilli(),
  1994  							"maxNodes":      1024,
  1995  						},
  1996  						"right": map[string]any{
  1997  							"profileTypeID": profileTypeProcessCPU,
  1998  							"labelSelector": "{}",
  1999  							"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  2000  							"end":           time.Now().UnixMilli(),
  2001  							"maxNodes":      512,
  2002  						},
  2003  					}),
  2004  				},
  2005  				{
  2006  					Name:   "valid_same_time_range",
  2007  					Method: http.MethodPost,
  2008  					Header: http.Header{
  2009  						"Content-Type": []string{"application/json"},
  2010  					},
  2011  					Body: toJSON(map[string]any{
  2012  						"left": map[string]any{
  2013  							"profileTypeID": profileTypeProcessCPU,
  2014  							"labelSelector": "{}",
  2015  							"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  2016  							"end":           time.Now().UnixMilli(),
  2017  						},
  2018  						"right": map[string]any{
  2019  							"profileTypeID": profileTypeProcessCPU,
  2020  							"labelSelector": "{}",
  2021  							"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  2022  							"end":           time.Now().UnixMilli(),
  2023  						},
  2024  					}),
  2025  				},
  2026  			},
  2027  			http.StatusBadRequest: {
  2028  				{
  2029  					Name:   "missing_left",
  2030  					Method: http.MethodPost,
  2031  					Header: http.Header{
  2032  						"Content-Type": []string{"application/json"},
  2033  					},
  2034  					Body: toJSON(map[string]any{
  2035  						"right": map[string]any{
  2036  							"profileTypeID": profileTypeProcessCPU,
  2037  							"labelSelector": "{}",
  2038  							"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  2039  							"end":           time.Now().UnixMilli(),
  2040  						},
  2041  					}),
  2042  				},
  2043  				{
  2044  					Name:   "missing_right",
  2045  					Method: http.MethodPost,
  2046  					Header: http.Header{
  2047  						"Content-Type": []string{"application/json"},
  2048  					},
  2049  					Body: toJSON(map[string]any{
  2050  						"left": map[string]any{
  2051  							"profileTypeID": profileTypeProcessCPU,
  2052  							"labelSelector": "{}",
  2053  							"start":         time.Now().Add(-2 * time.Hour).UnixMilli(),
  2054  							"end":           time.Now().Add(-1 * time.Hour).UnixMilli(),
  2055  						},
  2056  					}),
  2057  				},
  2058  				{
  2059  					Name:   "left_missing_profile_type_id",
  2060  					Method: http.MethodPost,
  2061  					Header: http.Header{
  2062  						"Content-Type": []string{"application/json"},
  2063  					},
  2064  					Body: toJSON(map[string]any{
  2065  						"left": map[string]any{
  2066  							"labelSelector": "{}",
  2067  							"start":         time.Now().Add(-2 * time.Hour).UnixMilli(),
  2068  							"end":           time.Now().Add(-1 * time.Hour).UnixMilli(),
  2069  						},
  2070  						"right": map[string]any{
  2071  							"profileTypeID": profileTypeProcessCPU,
  2072  							"labelSelector": "{}",
  2073  							"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  2074  							"end":           time.Now().UnixMilli(),
  2075  						},
  2076  					}),
  2077  				},
  2078  				{
  2079  					Name:   "right_missing_profile_type_id",
  2080  					Method: http.MethodPost,
  2081  					Header: http.Header{
  2082  						"Content-Type": []string{"application/json"},
  2083  					},
  2084  					Body: toJSON(map[string]any{
  2085  						"left": map[string]any{
  2086  							"profileTypeID": profileTypeProcessCPU,
  2087  							"labelSelector": "{}",
  2088  							"start":         time.Now().Add(-2 * time.Hour).UnixMilli(),
  2089  							"end":           time.Now().Add(-1 * time.Hour).UnixMilli(),
  2090  						},
  2091  						"right": map[string]any{
  2092  							"labelSelector": "{}",
  2093  							"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  2094  							"end":           time.Now().UnixMilli(),
  2095  						},
  2096  					}),
  2097  				},
  2098  				{
  2099  					Name:   "left_invalid_profile_type",
  2100  					Method: http.MethodPost,
  2101  					Header: http.Header{
  2102  						"Content-Type": []string{"application/json"},
  2103  					},
  2104  					Body: toJSON(map[string]any{
  2105  						"left": map[string]any{
  2106  							"profileTypeID": "invalid_format",
  2107  							"labelSelector": "{}",
  2108  							"start":         time.Now().Add(-2 * time.Hour).UnixMilli(),
  2109  							"end":           time.Now().Add(-1 * time.Hour).UnixMilli(),
  2110  						},
  2111  						"right": map[string]any{
  2112  							"profileTypeID": profileTypeProcessCPU,
  2113  							"labelSelector": "{}",
  2114  							"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  2115  							"end":           time.Now().UnixMilli(),
  2116  						},
  2117  					}),
  2118  				},
  2119  				{
  2120  					Name:   "right_invalid_profile_type",
  2121  					Method: http.MethodPost,
  2122  					Header: http.Header{
  2123  						"Content-Type": []string{"application/json"},
  2124  					},
  2125  					Body: toJSON(map[string]any{
  2126  						"left": map[string]any{
  2127  							"profileTypeID": profileTypeProcessCPU,
  2128  							"labelSelector": "{}",
  2129  							"start":         time.Now().Add(-2 * time.Hour).UnixMilli(),
  2130  							"end":           time.Now().Add(-1 * time.Hour).UnixMilli(),
  2131  						},
  2132  						"right": map[string]any{
  2133  							"profileTypeID": "invalid_format",
  2134  							"labelSelector": "{}",
  2135  							"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  2136  							"end":           time.Now().UnixMilli(),
  2137  						},
  2138  					}),
  2139  				},
  2140  				{
  2141  					Name:   "left_missing_label_selector",
  2142  					Method: http.MethodPost,
  2143  					Header: http.Header{
  2144  						"Content-Type": []string{"application/json"},
  2145  					},
  2146  					Body: toJSON(map[string]any{
  2147  						"left": map[string]any{
  2148  							"profileTypeID": profileTypeProcessCPU,
  2149  							"start":         time.Now().Add(-2 * time.Hour).UnixMilli(),
  2150  							"end":           time.Now().Add(-1 * time.Hour).UnixMilli(),
  2151  						},
  2152  						"right": map[string]any{
  2153  							"profileTypeID": profileTypeProcessCPU,
  2154  							"labelSelector": "{}",
  2155  							"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  2156  							"end":           time.Now().UnixMilli(),
  2157  						},
  2158  					}),
  2159  				},
  2160  				{
  2161  					Name:   "right_missing_label_selector",
  2162  					Method: http.MethodPost,
  2163  					Header: http.Header{
  2164  						"Content-Type": []string{"application/json"},
  2165  					},
  2166  					Body: toJSON(map[string]any{
  2167  						"left": map[string]any{
  2168  							"profileTypeID": profileTypeProcessCPU,
  2169  							"labelSelector": "{}",
  2170  							"start":         time.Now().Add(-2 * time.Hour).UnixMilli(),
  2171  							"end":           time.Now().Add(-1 * time.Hour).UnixMilli(),
  2172  						},
  2173  						"right": map[string]any{
  2174  							"profileTypeID": profileTypeProcessCPU,
  2175  							"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  2176  							"end":           time.Now().UnixMilli(),
  2177  						},
  2178  					}),
  2179  				},
  2180  				{
  2181  					Name:   "invalid_json",
  2182  					Method: http.MethodPost,
  2183  					Header: http.Header{
  2184  						"Content-Type": []string{"application/json"},
  2185  					},
  2186  					Body: `{"invalid json"`,
  2187  				},
  2188  				{
  2189  					Name:   "empty_body",
  2190  					Method: http.MethodPost,
  2191  					Header: http.Header{
  2192  						"Content-Type": []string{"application/json"},
  2193  					},
  2194  					Body: "",
  2195  				},
  2196  			},
  2197  			http.StatusMethodNotAllowed: {
  2198  				{
  2199  					Name:   "get_method_not_allowed",
  2200  					Method: http.MethodGet,
  2201  					Header: http.Header{
  2202  						"Content-Type": []string{"application/json"},
  2203  					},
  2204  				},
  2205  				{
  2206  					Name:   "put_method_not_allowed",
  2207  					Method: http.MethodPut,
  2208  					Header: http.Header{
  2209  						"Content-Type": []string{"application/json"},
  2210  					},
  2211  					Body: toJSON(map[string]any{
  2212  						"left": map[string]any{
  2213  							"profileTypeID": profileTypeProcessCPU,
  2214  							"labelSelector": "{}",
  2215  							"start":         time.Now().Add(-2 * time.Hour).UnixMilli(),
  2216  							"end":           time.Now().Add(-1 * time.Hour).UnixMilli(),
  2217  						},
  2218  						"right": map[string]any{
  2219  							"profileTypeID": profileTypeProcessCPU,
  2220  							"labelSelector": "{}",
  2221  							"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  2222  							"end":           time.Now().UnixMilli(),
  2223  						},
  2224  					}),
  2225  				},
  2226  				{
  2227  					Name:   "delete_method_not_allowed",
  2228  					Method: http.MethodDelete,
  2229  					Header: http.Header{
  2230  						"Content-Type": []string{"application/json"},
  2231  					},
  2232  					Body: toJSON(map[string]any{
  2233  						"left": map[string]any{
  2234  							"profileTypeID": profileTypeProcessCPU,
  2235  							"labelSelector": "{}",
  2236  							"start":         time.Now().Add(-2 * time.Hour).UnixMilli(),
  2237  							"end":           time.Now().Add(-1 * time.Hour).UnixMilli(),
  2238  						},
  2239  						"right": map[string]any{
  2240  							"profileTypeID": profileTypeProcessCPU,
  2241  							"labelSelector": "{}",
  2242  							"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  2243  							"end":           time.Now().UnixMilli(),
  2244  						},
  2245  					}),
  2246  				},
  2247  				{
  2248  					Name:   "patch_method_not_allowed",
  2249  					Method: http.MethodPatch,
  2250  					Header: http.Header{
  2251  						"Content-Type": []string{"application/json"},
  2252  					},
  2253  					Body: toJSON(map[string]any{
  2254  						"left": map[string]any{
  2255  							"profileTypeID": profileTypeProcessCPU,
  2256  							"labelSelector": "{}",
  2257  							"start":         time.Now().Add(-2 * time.Hour).UnixMilli(),
  2258  							"end":           time.Now().Add(-1 * time.Hour).UnixMilli(),
  2259  						},
  2260  						"right": map[string]any{
  2261  							"profileTypeID": profileTypeProcessCPU,
  2262  							"labelSelector": "{}",
  2263  							"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  2264  							"end":           time.Now().UnixMilli(),
  2265  						},
  2266  					}),
  2267  				},
  2268  			},
  2269  			http.StatusUnsupportedMediaType: {
  2270  				{
  2271  					Name:   "invalid_content_type",
  2272  					Method: http.MethodPost,
  2273  					Header: http.Header{
  2274  						"Content-Type": []string{"text/plain"},
  2275  					},
  2276  					Body: toJSON(map[string]any{
  2277  						"left": map[string]any{
  2278  							"profileTypeID": profileTypeProcessCPU,
  2279  							"labelSelector": "{}",
  2280  							"start":         time.Now().Add(-2 * time.Hour).UnixMilli(),
  2281  							"end":           time.Now().Add(-1 * time.Hour).UnixMilli(),
  2282  						},
  2283  						"right": map[string]any{
  2284  							"profileTypeID": profileTypeProcessCPU,
  2285  							"labelSelector": "{}",
  2286  							"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  2287  							"end":           time.Now().UnixMilli(),
  2288  						},
  2289  					}),
  2290  				},
  2291  			},
  2292  		},
  2293  	}
  2294  
  2295  	selectMergeSpanProfileTests := EndpointTestGroup{
  2296  		Path: "/querier.v1.QuerierService/SelectMergeSpanProfile",
  2297  		Tests: map[int][]Test{
  2298  			http.StatusOK: {
  2299  				{
  2300  					Name:   "valid",
  2301  					Method: http.MethodPost,
  2302  					Header: http.Header{
  2303  						"Content-Type": []string{"application/json"},
  2304  					},
  2305  					Body: toJSON(map[string]any{
  2306  						"profileTypeID": profileTypeProcessCPU,
  2307  						"labelSelector": "{}",
  2308  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  2309  						"end":           time.Now().UnixMilli(),
  2310  						"spanSelector":  []string{"span1", "span2"},
  2311  					}),
  2312  				},
  2313  				{
  2314  					Name:   "valid_with_max_nodes",
  2315  					Method: http.MethodPost,
  2316  					Header: http.Header{
  2317  						"Content-Type": []string{"application/json"},
  2318  					},
  2319  					Body: toJSON(map[string]any{
  2320  						"profileTypeID": profileTypeProcessCPU,
  2321  						"labelSelector": "{}",
  2322  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  2323  						"end":           time.Now().UnixMilli(),
  2324  						"spanSelector":  []string{"span1"},
  2325  						"maxNodes":      1024,
  2326  					}),
  2327  				},
  2328  				{
  2329  					Name:   "valid_with_format_tree",
  2330  					Method: http.MethodPost,
  2331  					Header: http.Header{
  2332  						"Content-Type": []string{"application/json"},
  2333  					},
  2334  					Body: toJSON(map[string]any{
  2335  						"profileTypeID": profileTypeProcessCPU,
  2336  						"labelSelector": "{}",
  2337  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  2338  						"end":           time.Now().UnixMilli(),
  2339  						"spanSelector":  []string{"span1"},
  2340  						"format":        2,
  2341  					}),
  2342  				},
  2343  			},
  2344  			http.StatusBadRequest: {
  2345  				{
  2346  					Name:   "missing_profile_type_id",
  2347  					Method: http.MethodPost,
  2348  					Header: http.Header{
  2349  						"Content-Type": []string{"application/json"},
  2350  					},
  2351  					Body: toJSON(map[string]any{
  2352  						"labelSelector": "{}",
  2353  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  2354  						"end":           time.Now().UnixMilli(),
  2355  						"spanSelector":  []string{"span1"},
  2356  					}),
  2357  				},
  2358  				{
  2359  					Name:   "empty_profile_type_id",
  2360  					Method: http.MethodPost,
  2361  					Header: http.Header{
  2362  						"Content-Type": []string{"application/json"},
  2363  					},
  2364  					Body: toJSON(map[string]any{
  2365  						"profileTypeID": "",
  2366  						"labelSelector": "{}",
  2367  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  2368  						"end":           time.Now().UnixMilli(),
  2369  						"spanSelector":  []string{"span1"},
  2370  					}),
  2371  				},
  2372  				{
  2373  					Name:   "invalid_profile_type_format",
  2374  					Method: http.MethodPost,
  2375  					Header: http.Header{
  2376  						"Content-Type": []string{"application/json"},
  2377  					},
  2378  					Body: toJSON(map[string]any{
  2379  						"profileTypeID": "invalid_format",
  2380  						"labelSelector": "{}",
  2381  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  2382  						"end":           time.Now().UnixMilli(),
  2383  						"spanSelector":  []string{"span1"},
  2384  					}),
  2385  				},
  2386  				{
  2387  					Name:   "missing_label_selector",
  2388  					Method: http.MethodPost,
  2389  					Header: http.Header{
  2390  						"Content-Type": []string{"application/json"},
  2391  					},
  2392  					Body: toJSON(map[string]any{
  2393  						"profileTypeID": profileTypeProcessCPU,
  2394  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  2395  						"end":           time.Now().UnixMilli(),
  2396  						"spanSelector":  []string{"span1"},
  2397  					}),
  2398  				},
  2399  				{
  2400  					Name:   "invalid_label_selector",
  2401  					Method: http.MethodPost,
  2402  					Header: http.Header{
  2403  						"Content-Type": []string{"application/json"},
  2404  					},
  2405  					Body: toJSON(map[string]any{
  2406  						"profileTypeID": profileTypeProcessCPU,
  2407  						"labelSelector": "!bad_syntax!",
  2408  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  2409  						"end":           time.Now().UnixMilli(),
  2410  						"spanSelector":  []string{"span1"},
  2411  					}),
  2412  				},
  2413  				{
  2414  					Name:   "invalid_json",
  2415  					Method: http.MethodPost,
  2416  					Header: http.Header{
  2417  						"Content-Type": []string{"application/json"},
  2418  					},
  2419  					Body: `{"invalid json"`,
  2420  				},
  2421  				{
  2422  					Name:   "empty_body",
  2423  					Method: http.MethodPost,
  2424  					Header: http.Header{
  2425  						"Content-Type": []string{"application/json"},
  2426  					},
  2427  					Body: "",
  2428  				},
  2429  				{
  2430  					Name:   "start_after_end",
  2431  					Method: http.MethodPost,
  2432  					Header: http.Header{
  2433  						"Content-Type": []string{"application/json"},
  2434  					},
  2435  					Body: toJSON(map[string]any{
  2436  						"profileTypeID": profileTypeProcessCPU,
  2437  						"labelSelector": "{}",
  2438  						"start":         time.Now().UnixMilli(),
  2439  						"end":           time.Now().Add(-1 * time.Hour).UnixMilli(),
  2440  						"spanSelector":  []string{"span1"},
  2441  					}),
  2442  				},
  2443  				{
  2444  					Name:   "invalid_no_time_range",
  2445  					Method: http.MethodPost,
  2446  					Header: http.Header{
  2447  						"Content-Type": []string{"application/json"},
  2448  					},
  2449  					Body: toJSON(map[string]any{
  2450  						"profileTypeID": profileTypeProcessCPU,
  2451  						"labelSelector": "{}",
  2452  						"spanSelector":  []string{"span1"},
  2453  					}),
  2454  				},
  2455  			},
  2456  			http.StatusMethodNotAllowed: {
  2457  				{
  2458  					Name:   "get_method_not_allowed",
  2459  					Method: http.MethodGet,
  2460  					Header: http.Header{
  2461  						"Content-Type": []string{"application/json"},
  2462  					},
  2463  				},
  2464  				{
  2465  					Name:   "put_method_not_allowed",
  2466  					Method: http.MethodPut,
  2467  					Header: http.Header{
  2468  						"Content-Type": []string{"application/json"},
  2469  					},
  2470  					Body: toJSON(map[string]any{
  2471  						"profileTypeID": profileTypeProcessCPU,
  2472  						"labelSelector": "{}",
  2473  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  2474  						"end":           time.Now().UnixMilli(),
  2475  						"spanSelector":  []string{"span1"},
  2476  					}),
  2477  				},
  2478  				{
  2479  					Name:   "delete_method_not_allowed",
  2480  					Method: http.MethodDelete,
  2481  					Header: http.Header{
  2482  						"Content-Type": []string{"application/json"},
  2483  					},
  2484  					Body: toJSON(map[string]any{
  2485  						"profileTypeID": profileTypeProcessCPU,
  2486  						"labelSelector": "{}",
  2487  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  2488  						"end":           time.Now().UnixMilli(),
  2489  						"spanSelector":  []string{"span1"},
  2490  					}),
  2491  				},
  2492  				{
  2493  					Name:   "patch_method_not_allowed",
  2494  					Method: http.MethodPatch,
  2495  					Header: http.Header{
  2496  						"Content-Type": []string{"application/json"},
  2497  					},
  2498  					Body: toJSON(map[string]any{
  2499  						"profileTypeID": profileTypeProcessCPU,
  2500  						"labelSelector": "{}",
  2501  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  2502  						"end":           time.Now().UnixMilli(),
  2503  						"spanSelector":  []string{"span1"},
  2504  					}),
  2505  				},
  2506  			},
  2507  			http.StatusUnsupportedMediaType: {
  2508  				{
  2509  					Name:   "invalid_content_type",
  2510  					Method: http.MethodPost,
  2511  					Header: http.Header{
  2512  						"Content-Type": []string{"text/plain"},
  2513  					},
  2514  					Body: toJSON(map[string]any{
  2515  						"profileTypeID": profileTypeProcessCPU,
  2516  						"labelSelector": "{}",
  2517  						"start":         time.Now().Add(-1 * time.Hour).UnixMilli(),
  2518  						"end":           time.Now().UnixMilli(),
  2519  						"spanSelector":  []string{"span1"},
  2520  					}),
  2521  				},
  2522  			},
  2523  		},
  2524  	}
  2525  
  2526  	allTests := []EndpointTestGroup{
  2527  		renderTests,
  2528  		renderDiffTests,
  2529  		profileTypesTests,
  2530  		labelValuesTests,
  2531  		labelNamesTests,
  2532  		seriesTests,
  2533  		selectMergeStacktracesTests,
  2534  		selectMergeProfileTests,
  2535  		selectSeriesTests,
  2536  		selectMergeSpanProfileTests,
  2537  		diffTests,
  2538  	}
  2539  
  2540  	EachPyroscopeTest(t, func(p *PyroscopeTest, t *testing.T) {
  2541  		client := http.DefaultClient
  2542  		isV1Test := strings.HasSuffix(t.Name(), "v1")
  2543  
  2544  		for _, endpoint := range allTests {
  2545  			for wantCode, tests := range endpoint.Tests {
  2546  				for _, tt := range tests {
  2547  					t.Run(fmt.Sprintf("%s/%s", endpoint.Path, tt.Name), func(t *testing.T) {
  2548  						t.Parallel()
  2549  
  2550  						path, err := url.JoinPath(p.URL(), endpoint.Path)
  2551  						require.NoError(t, err, "failed to create path")
  2552  
  2553  						u, err := url.Parse(path)
  2554  						require.NoError(t, err, "failed to parse url")
  2555  						u.RawQuery = tt.Params.Encode()
  2556  
  2557  						var reqBody io.Reader
  2558  						if tt.Body != "" {
  2559  							reqBody = bytes.NewReader([]byte(tt.Body))
  2560  						}
  2561  
  2562  						req, err := http.NewRequest(tt.Method, u.String(), reqBody)
  2563  						require.NoError(t, err)
  2564  
  2565  						for key, vals := range tt.Header {
  2566  							for _, val := range vals {
  2567  								req.Header.Add(key, val)
  2568  							}
  2569  						}
  2570  
  2571  						res, err := client.Do(req)
  2572  						require.NoError(t, err)
  2573  
  2574  						wantCode := wantCode
  2575  						if tt.WantV1StatusCode != 0 && isV1Test {
  2576  							wantCode = tt.WantV1StatusCode
  2577  						}
  2578  
  2579  						if !assert.Equal(t, wantCode, res.StatusCode) {
  2580  							bytes, err := io.ReadAll(res.Body)
  2581  							res.Body.Close()
  2582  							if err != nil {
  2583  								t.Log("failed to read response body:", err)
  2584  							} else {
  2585  								t.Log("response body:", string(bytes))
  2586  							}
  2587  						}
  2588  					})
  2589  				}
  2590  			}
  2591  		}
  2592  	})
  2593  }