github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/api/api_key_test.go (about)

     1  package api_test
     2  
     3  import (
     4  	"context"
     5  	"net/http"
     6  	"net/http/httptest"
     7  	"time"
     8  
     9  	"github.com/golang/mock/gomock"
    10  	"github.com/hashicorp/go-multierror"
    11  	. "github.com/onsi/ginkgo/v2"
    12  	. "github.com/onsi/gomega"
    13  	"github.com/sirupsen/logrus"
    14  
    15  	"github.com/pyroscope-io/pyroscope/pkg/api/mocks"
    16  	"github.com/pyroscope-io/pyroscope/pkg/api/router"
    17  	"github.com/pyroscope-io/pyroscope/pkg/model"
    18  )
    19  
    20  var _ = Describe("APIKeyHandler", func() {
    21  	defer GinkgoRecover()
    22  
    23  	var (
    24  		// Mocks setup.
    25  		ctrl   *gomock.Controller
    26  		server *httptest.Server
    27  		m      *mocks.MockAPIKeyService
    28  
    29  		// Default configuration for all scenarios.
    30  		method, url string
    31  	)
    32  
    33  	BeforeEach(func() {
    34  		ctrl = gomock.NewController(GinkgoT())
    35  		m = mocks.NewMockAPIKeyService(ctrl)
    36  		server = httptest.NewServer(newTestRouter(defaultUserCtx, router.Services{
    37  			Logger:        logrus.StandardLogger(),
    38  			APIKeyService: m,
    39  		}))
    40  	})
    41  
    42  	AfterEach(func() {
    43  		ctrl.Finish()
    44  		server.Close()
    45  	})
    46  
    47  	Describe("create API key", func() {
    48  		var (
    49  			// Expected params passed to the mocked API key service.
    50  			expectedParams model.CreateAPIKeyParams
    51  			// API key and JWT token string returned by mocked service.
    52  			expectedAPIKey model.APIKey
    53  			expectedSecret string
    54  		)
    55  
    56  		BeforeEach(func() {
    57  			// Defaults for all "create API key" scenarios.
    58  			method = http.MethodPost
    59  			url = server.URL + "/keys"
    60  
    61  			// Note that the actual ExpiresAt is populated during the handler execution
    62  			// and it is relative to time.Now(). Therefore use this mather to evaluate
    63  			// the actual expiration time: BeTemporally("~", time.Now(), time.Minute).
    64  			now := time.Date(2021, 12, 10, 4, 14, 0, 0, time.UTC)
    65  			expiresAt := now.Add(time.Minute)
    66  
    67  			expectedSecret = "secret-string"
    68  			expectedParams = model.CreateAPIKeyParams{
    69  				Name:      "some-api-key",
    70  				Role:      model.ReadOnlyRole,
    71  				ExpiresAt: &expiresAt,
    72  			}
    73  
    74  			expectedAPIKey = model.APIKey{
    75  				ID:         1,
    76  				Name:       expectedParams.Name,
    77  				Role:       expectedParams.Role,
    78  				ExpiresAt:  expectedParams.ExpiresAt,
    79  				LastSeenAt: nil,
    80  				CreatedAt:  now,
    81  			}
    82  		})
    83  
    84  		Context("when request is complete and valid", func() {
    85  			It("responds with created API key", func() {
    86  				m.EXPECT().
    87  					CreateAPIKey(gomock.Any(), gomock.Any()).
    88  					Return(expectedAPIKey, expectedSecret, nil).
    89  					Do(func(_ context.Context, actual model.CreateAPIKeyParams) {
    90  						defer GinkgoRecover()
    91  						Expect(*actual.ExpiresAt).To(BeTemporally("~", time.Now(), time.Minute))
    92  						Expect(actual.Name).To(Equal(expectedParams.Name))
    93  						Expect(actual.Role).To(Equal(expectedParams.Role))
    94  					})
    95  
    96  				expectResponse(newRequest(method, url,
    97  					"api_key/create_request.json"),
    98  					"api_key/create_response.json",
    99  					http.StatusCreated)
   100  			})
   101  		})
   102  
   103  		Context("when api key ttl is not specified", func() {
   104  			It("responds with created API key", func() {
   105  				expectedParams.ExpiresAt = nil
   106  				expectedAPIKey.ExpiresAt = nil
   107  
   108  				m.EXPECT().
   109  					CreateAPIKey(gomock.Any(), expectedParams).
   110  					Return(expectedAPIKey, expectedSecret, nil)
   111  
   112  				expectResponse(newRequest(method, url,
   113  					"api_key/create_request_wo_ttl.json"),
   114  					"api_key/create_response_wo_ttl.json",
   115  					http.StatusCreated)
   116  			})
   117  		})
   118  
   119  		Context("when the request does not meet requirements", func() {
   120  			It("returns validation errors", func() {
   121  				m.EXPECT().
   122  					CreateAPIKey(gomock.Any(), model.CreateAPIKeyParams{}).
   123  					Return(model.APIKey{}, "", &multierror.Error{Errors: []error{
   124  						model.ErrAPIKeyNameEmpty,
   125  						model.ErrRoleUnknown,
   126  					}})
   127  
   128  				expectResponse(newRequest(method, url,
   129  					"request_empty_object.json"),
   130  					"api_key/create_response_invalid.json",
   131  					http.StatusBadRequest)
   132  			})
   133  		})
   134  	})
   135  })