request_test.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. package autocomplete
  2. import (
  3. "errors"
  4. "testing"
  5. "time"
  6. "github.com/opencost/opencost/core/pkg/filter"
  7. "github.com/opencost/opencost/core/pkg/opencost"
  8. "github.com/opencost/opencost/core/pkg/util/httputil"
  9. )
  10. func validateTestField(field string) (string, error) {
  11. if field == "" {
  12. return "", ErrBadRequest
  13. }
  14. return field, nil
  15. }
  16. func TestNormalizeRequest(t *testing.T) {
  17. start := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
  18. req := &Request{
  19. TenantID: "t1",
  20. Field: "cluster",
  21. Search: " x ",
  22. Limit: 0,
  23. Window: opencost.NewClosedWindow(start, start.Add(24*time.Hour)),
  24. }
  25. field, err := NormalizeRequest(req, validateTestField, NormalizeOptions{RequireTenantID: true})
  26. if err != nil {
  27. t.Fatalf("unexpected error: %v", err)
  28. }
  29. if field != "cluster" || req.Search != "x" || req.Limit != DefaultResultLimit {
  30. t.Fatalf("unexpected normalized request: %+v", req)
  31. }
  32. _, err = NormalizeRequest(req, validateTestField, NormalizeOptions{RequireTenantID: true, EnsureLabelConfig: true})
  33. if err != nil {
  34. t.Fatalf("unexpected error: %v", err)
  35. }
  36. if req.LabelConfig == nil {
  37. t.Fatal("expected default label config")
  38. }
  39. _, err = NormalizeRequest(nil, validateTestField, NormalizeOptions{})
  40. if err == nil || !errors.Is(err, ErrBadRequest) {
  41. t.Fatalf("expected nil request error, got %v", err)
  42. }
  43. openReq := &Request{Field: "cluster", Window: opencost.NewWindow(&start, nil)}
  44. _, err = NormalizeRequest(openReq, validateTestField, NormalizeOptions{})
  45. if err == nil || !errors.Is(err, ErrBadRequest) {
  46. t.Fatalf("expected open window error, got %v", err)
  47. }
  48. _, err = NormalizeRequest(&Request{Field: "cluster", Window: req.Window}, validateTestField, NormalizeOptions{RequireTenantID: true})
  49. if err == nil || !errors.Is(err, ErrBadRequest) {
  50. t.Fatalf("expected tenant ID error, got %v", err)
  51. }
  52. limitReq := &Request{
  53. TenantID: "t1",
  54. Field: "cluster",
  55. Window: opencost.NewClosedWindow(start, start.Add(24*time.Hour)),
  56. Limit: MaxResultLimit + 1,
  57. }
  58. _, err = NormalizeRequest(limitReq, validateTestField, NormalizeOptions{RequireTenantID: true})
  59. if err == nil || !errors.Is(err, ErrBadRequest) {
  60. t.Fatalf("expected limit error, got %v", err)
  61. }
  62. }
  63. func TestDefaultWindowValidator(t *testing.T) {
  64. start := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
  65. end := start.Add(time.Hour)
  66. if err := DefaultWindowValidator(opencost.NewClosedWindow(start, end)); err != nil {
  67. t.Fatalf("unexpected error: %v", err)
  68. }
  69. if err := DefaultWindowValidator(opencost.NewWindow(&start, nil)); err == nil {
  70. t.Fatal("expected open window error")
  71. }
  72. }
  73. func TestParseRequest(t *testing.T) {
  74. windowStr := "2023-01-01T00:00:00Z,2023-01-02T00:00:00Z"
  75. qp := httputil.NewQueryParams(map[string][]string{
  76. "window": {windowStr},
  77. "field": {"cluster"},
  78. "search": {" ns "},
  79. "tenantId": {"t1"},
  80. })
  81. got, err := ParseRequest(qp, ParseOptions{}, validateTestField, nil)
  82. if err != nil {
  83. t.Fatalf("ParseRequest() error = %v", err)
  84. }
  85. if got.Field != "cluster" || got.Search != "ns" || got.TenantID != "t1" {
  86. t.Fatalf("unexpected request: %+v", got)
  87. }
  88. _, err = ParseRequest(httputil.NewQueryParams(map[string][]string{"field": {"cluster"}}), ParseOptions{}, validateTestField, nil)
  89. if err == nil || !errors.Is(err, ErrBadRequest) {
  90. t.Fatalf("expected missing window error, got %v", err)
  91. }
  92. _, err = ParseRequest(httputil.NewQueryParams(map[string][]string{
  93. "window": {"bad"},
  94. "field": {"cluster"},
  95. }), ParseOptions{}, validateTestField, nil)
  96. if err == nil || !errors.Is(err, ErrBadRequest) {
  97. t.Fatalf("expected invalid window error, got %v", err)
  98. }
  99. offset := 5 * time.Hour
  100. _, err = ParseRequest(qp, ParseOptions{UTCOffset: &offset}, validateTestField, nil)
  101. if err != nil {
  102. t.Fatalf("ParseRequest with offset error = %v", err)
  103. }
  104. noFilterQP := httputil.NewQueryParams(map[string][]string{
  105. "window": {windowStr},
  106. "field": {"cluster"},
  107. "filter": {"cluster:\"prod\""},
  108. })
  109. _, err = ParseRequest(noFilterQP, ParseOptions{}, validateTestField, nil)
  110. if err == nil || !errors.Is(err, ErrBadRequest) {
  111. t.Fatalf("expected filter parser required error, got %v", err)
  112. }
  113. parseFilter := func(filterString string) (filter.Filter, error) {
  114. if filterString == "bad" {
  115. return nil, errors.New("bad filter")
  116. }
  117. return nil, nil
  118. }
  119. badFilterQP := httputil.NewQueryParams(map[string][]string{
  120. "window": {windowStr},
  121. "field": {"cluster"},
  122. "filter": {"bad"},
  123. })
  124. _, err = ParseRequest(badFilterQP, ParseOptions{}, validateTestField, parseFilter)
  125. if err == nil || !errors.Is(err, ErrBadRequest) {
  126. t.Fatalf("expected filter parse error, got %v", err)
  127. }
  128. okFilterQP := httputil.NewQueryParams(map[string][]string{
  129. "window": {windowStr},
  130. "field": {"cluster"},
  131. "filter": {"ok"},
  132. })
  133. got, err = ParseRequest(okFilterQP, ParseOptions{}, validateTestField, parseFilter)
  134. if err != nil {
  135. t.Fatalf("ParseRequest with filter error = %v", err)
  136. }
  137. if got.Field != "cluster" {
  138. t.Fatalf("unexpected request: %+v", got)
  139. }
  140. }