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

     1  // TODO: move most of these tests to pkg/api
     2  package admin_test
     3  
     4  import (
     5  	"bytes"
     6  	"context"
     7  	"encoding/json"
     8  	"fmt"
     9  	"io"
    10  	"net/http"
    11  	"net/http/httptest"
    12  
    13  	"github.com/pyroscope-io/pyroscope/pkg/api"
    14  	"github.com/pyroscope-io/pyroscope/pkg/model/appmetadata"
    15  
    16  	. "github.com/onsi/ginkgo/v2"
    17  	. "github.com/onsi/gomega"
    18  	"github.com/sirupsen/logrus/hooks/test"
    19  
    20  	"github.com/pyroscope-io/pyroscope/pkg/admin"
    21  	"github.com/pyroscope-io/pyroscope/pkg/model"
    22  )
    23  
    24  type mockStorage struct {
    25  	getAppsResult []appmetadata.ApplicationMetadata
    26  	deleteResult  error
    27  }
    28  
    29  func (m mockStorage) List(ctx context.Context) ([]appmetadata.ApplicationMetadata, error) {
    30  	return m.getAppsResult, nil
    31  }
    32  
    33  func (m mockStorage) Delete(ctx context.Context, appname string) error {
    34  	return m.deleteResult
    35  }
    36  
    37  type mockUserService struct{}
    38  
    39  func (mockUserService) UpdateUserByName(context.Context, string, model.UpdateUserParams) (model.User, error) {
    40  	return model.User{}, nil
    41  }
    42  
    43  type mockStorageService struct{}
    44  
    45  func (mockStorageService) Cleanup(context.Context) error {
    46  	return nil
    47  }
    48  
    49  var _ = Describe("controller", func() {
    50  	Describe("/v1/apps", func() {
    51  		var svr *admin.Server
    52  		var response *httptest.ResponseRecorder
    53  		var appSvc admin.ApplicationListerAndDeleter
    54  
    55  		// create a server
    56  		BeforeEach(func() {
    57  			appSvc = mockStorage{
    58  				getAppsResult: []appmetadata.ApplicationMetadata{
    59  					{FQName: "app1"},
    60  					{FQName: "app2"},
    61  				},
    62  				deleteResult: nil,
    63  			}
    64  		})
    65  
    66  		JustBeforeEach(func() {
    67  			// create a null logger, since we aren't interested
    68  			logger, _ := test.NewNullLogger()
    69  
    70  			ctrl := admin.NewController(logger, appSvc, mockUserService{}, mockStorageService{})
    71  			httpServer := &admin.UdsHTTPServer{}
    72  			server, err := admin.NewServer(logger, ctrl, httpServer)
    73  
    74  			Expect(err).ToNot(HaveOccurred())
    75  			svr = server
    76  			response = httptest.NewRecorder()
    77  		})
    78  
    79  		Describe("GET", func() {
    80  			Context("when everything is right", func() {
    81  				It("returns list of apps successfully", func() {
    82  					request, err := http.NewRequest(http.MethodGet, "/v1/apps", nil)
    83  					Expect(err).ToNot(HaveOccurred())
    84  
    85  					svr.Handler.ServeHTTP(response, request)
    86  
    87  					body, err := io.ReadAll(response.Body)
    88  					Expect(err).To(BeNil())
    89  
    90  					Expect(response.Code).To(Equal(http.StatusOK))
    91  					Expect(string(body)).To(Equal(`[{"name":"app1"},{"name":"app2"}]
    92  `))
    93  				})
    94  
    95  			})
    96  		})
    97  
    98  		Describe("DELETE", func() {
    99  			Context("when everything is right", func() {
   100  				It("returns StatusOK", func() {
   101  					// we are kinda robbing here
   102  					// since the server and client are defined in the same package
   103  					payload := api.DeleteAppInput{Name: "myapp"}
   104  					marshalledPayload, err := json.Marshal(payload)
   105  					request, err := http.NewRequest(http.MethodDelete, "/v1/apps", bytes.NewBuffer(marshalledPayload))
   106  					Expect(err).ToNot(HaveOccurred())
   107  
   108  					svr.Handler.ServeHTTP(response, request)
   109  					Expect(response.Code).To(Equal(http.StatusOK))
   110  				})
   111  			})
   112  
   113  			Context("when there's an error", func() {
   114  				Context("with the payload", func() {
   115  					BeforeEach(func() {
   116  						appSvc = mockStorage{
   117  							deleteResult: model.ValidationError{},
   118  						}
   119  					})
   120  
   121  					It("returns BadRequest", func() {
   122  						request, err := http.NewRequest(http.MethodDelete, "/v1/apps", bytes.NewBuffer([]byte(``)))
   123  						Expect(err).ToNot(HaveOccurred())
   124  
   125  						svr.Handler.ServeHTTP(response, request)
   126  						Expect(response.Code).To(Equal(http.StatusBadRequest))
   127  					})
   128  				})
   129  
   130  				Context("with the server", func() {
   131  					BeforeEach(func() {
   132  						appSvc = mockStorage{
   133  							deleteResult: fmt.Errorf("error"),
   134  						}
   135  					})
   136  
   137  					It("returns InternalServerError", func() {
   138  						// we are kinda robbing here
   139  						// since the server and client are defined in the same package
   140  						payload := api.DeleteAppInput{Name: "myapp"}
   141  						marshalledPayload, err := json.Marshal(payload)
   142  						request, err := http.NewRequest(http.MethodDelete, "/v1/apps", bytes.NewBuffer(marshalledPayload))
   143  						Expect(err).ToNot(HaveOccurred())
   144  
   145  						svr.Handler.ServeHTTP(response, request)
   146  						Expect(response.Code).To(Equal(http.StatusInternalServerError))
   147  					})
   148  				})
   149  			})
   150  		})
   151  
   152  		DescribeTable("Non supported requests return 405 (method not allowed)",
   153  			func(method string) {
   154  				request, _ := http.NewRequest(method, "/v1/apps", nil)
   155  				svr.Handler.ServeHTTP(response, request)
   156  				Expect(response.Code).To(Equal(405))
   157  			},
   158  			Entry("POST", http.MethodPost),
   159  			Entry("PUT", http.MethodPost),
   160  			Entry("NON_VALID_METHOD", http.MethodPost),
   161  		)
   162  	})
   163  })