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  }