github.com/thanos-io/thanos@v0.32.5/pkg/api/blocks/v1_test.go (about) 1 // Copyright (c) The Thanos Authors. 2 // Licensed under the Apache License 2.0. 3 4 package v1 5 6 import ( 7 "context" 8 "fmt" 9 "io" 10 "net/http" 11 "net/url" 12 "os" 13 "path" 14 "reflect" 15 "strings" 16 "testing" 17 "time" 18 19 "github.com/go-kit/log" 20 "github.com/oklog/ulid" 21 "github.com/prometheus/common/route" 22 "github.com/prometheus/prometheus/model/labels" 23 "github.com/thanos-io/objstore" 24 25 "github.com/efficientgo/core/testutil" 26 baseAPI "github.com/thanos-io/thanos/pkg/api" 27 "github.com/thanos-io/thanos/pkg/block" 28 "github.com/thanos-io/thanos/pkg/block/metadata" 29 "github.com/thanos-io/thanos/pkg/testutil/custom" 30 "github.com/thanos-io/thanos/pkg/testutil/e2eutil" 31 ) 32 33 func TestMain(m *testing.M) { 34 custom.TolerantVerifyLeakMain(m) 35 } 36 37 type endpointTestCase struct { 38 endpoint baseAPI.ApiFunc 39 params map[string]string 40 query url.Values 41 method string 42 response interface{} 43 errType baseAPI.ErrorType 44 } 45 type responeCompareFunction func(interface{}, interface{}) bool 46 47 func testEndpoint(t *testing.T, test endpointTestCase, name string, responseCompareFunc responeCompareFunction) bool { 48 return t.Run(name, func(t *testing.T) { 49 // Build a context with the correct request params. 50 ctx := context.Background() 51 for p, v := range test.params { 52 ctx = route.WithParam(ctx, p, v) 53 } 54 55 reqURL := "http://example.com" 56 params := test.query.Encode() 57 58 var body io.Reader 59 if test.method == http.MethodPost { 60 body = strings.NewReader(params) 61 } else if test.method == "" { 62 test.method = "ANY" 63 reqURL += "?" + params 64 } 65 66 req, err := http.NewRequest(test.method, reqURL, body) 67 if err != nil { 68 t.Fatal(err) 69 } 70 71 if body != nil { 72 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") 73 } 74 75 resp, _, apiErr, releaseResources := test.endpoint(req.WithContext(ctx)) 76 defer releaseResources() 77 if apiErr != nil { 78 if test.errType == baseAPI.ErrorNone { 79 t.Fatalf("Unexpected error: %s", apiErr) 80 } 81 if test.errType != apiErr.Typ { 82 t.Fatalf("Expected error of type %q but got type %q", test.errType, apiErr.Typ) 83 } 84 return 85 } 86 if test.errType != baseAPI.ErrorNone { 87 t.Fatalf("Expected error of type %q but got none", test.errType) 88 } 89 90 if !responseCompareFunc(resp, test.response) { 91 t.Fatalf("Response does not match, expected:\n%+v\ngot:\n%+v", test.response, resp) 92 } 93 }) 94 } 95 96 func TestMarkBlockEndpoint(t *testing.T) { 97 ctx := context.Background() 98 tmpDir := t.TempDir() 99 100 // create block 101 b1, err := e2eutil.CreateBlock(ctx, tmpDir, []labels.Labels{ 102 {{Name: "a", Value: "1"}}, 103 {{Name: "a", Value: "2"}}, 104 {{Name: "a", Value: "3"}}, 105 {{Name: "a", Value: "4"}}, 106 {{Name: "b", Value: "1"}}, 107 }, 100, 0, 1000, labels.Labels{{Name: "ext1", Value: "val1"}}, 124, metadata.NoneFunc) 108 testutil.Ok(t, err) 109 110 // upload block 111 bkt := objstore.WithNoopInstr(objstore.NewInMemBucket()) 112 logger := log.NewNopLogger() 113 testutil.Ok(t, block.Upload(ctx, logger, bkt, path.Join(tmpDir, b1.String()), metadata.NoneFunc)) 114 115 now := time.Now() 116 api := &BlocksAPI{ 117 baseAPI: &baseAPI.BaseAPI{ 118 Now: func() time.Time { return now }, 119 }, 120 logger: logger, 121 globalBlocksInfo: &BlocksInfo{ 122 Blocks: []metadata.Meta{}, 123 Label: "foo", 124 }, 125 loadedBlocksInfo: &BlocksInfo{ 126 Blocks: []metadata.Meta{}, 127 Label: "foo", 128 }, 129 disableCORS: true, 130 bkt: bkt, 131 } 132 133 var tests = []endpointTestCase{ 134 // Empty ID 135 { 136 endpoint: api.markBlock, 137 query: url.Values{ 138 "id": []string{""}, 139 }, 140 errType: baseAPI.ErrorBadData, 141 }, 142 // Empty action 143 { 144 endpoint: api.markBlock, 145 query: url.Values{ 146 "id": []string{ulid.MustNew(1, nil).String()}, 147 "action": []string{""}, 148 }, 149 errType: baseAPI.ErrorBadData, 150 }, 151 // invalid ULID 152 { 153 endpoint: api.markBlock, 154 query: url.Values{ 155 "id": []string{"invalid_id"}, 156 "action": []string{"DELETION"}, 157 }, 158 errType: baseAPI.ErrorBadData, 159 }, 160 // invalid action 161 { 162 endpoint: api.markBlock, 163 query: url.Values{ 164 "id": []string{ulid.MustNew(2, nil).String()}, 165 "action": []string{"INVALID_ACTION"}, 166 }, 167 errType: baseAPI.ErrorBadData, 168 }, 169 { 170 endpoint: api.markBlock, 171 query: url.Values{ 172 "id": []string{b1.String()}, 173 "action": []string{"DELETION"}, 174 }, 175 response: nil, 176 }, 177 } 178 179 for i, test := range tests { 180 if ok := testEndpoint(t, test, fmt.Sprintf("#%d %s", i, test.query.Encode()), reflect.DeepEqual); !ok { 181 return 182 } 183 } 184 185 file := path.Join(tmpDir, b1.String()) 186 _, err = os.Stat(file) 187 testutil.Ok(t, err) 188 }