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 }