package pprof import ( "context" "io" "net/http" "testing" "time" "github.com/golang/mock/gomock" "github.com/qdm12/gluetun/internal/httpserver" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) //go:generate mockgen -destination=logger_mock_test.go -package $GOPACKAGE github.com/qdm12/gluetun/internal/httpserver Logger func Test_Server(t *testing.T) { t.Parallel() ctrl := gomock.NewController(t) const address = "127.0.0.1:0" logger := NewMockLogger(ctrl) logger.EXPECT().Info(newRegexMatcher("^http server listening on 127.0.0.1:[1-9][0-9]{0,4}$")) const httpServerShutdownTimeout = 10 * time.Second // 10s in case test worker is slow settings := Settings{ HTTPServer: httpserver.Settings{ Address: address, Logger: logger, ShutdownTimeout: httpServerShutdownTimeout, }, } server, err := New(settings) require.NoError(t, err) require.NotNil(t, server) ctx, cancel := context.WithCancel(context.Background()) ready := make(chan struct{}) done := make(chan struct{}) go server.Run(ctx, ready, done) select { case <-ready: case err := <-done: t.Fatalf("server crashed before being ready: %s", err) } serverAddress := server.GetAddress() const clientTimeout = 2 * time.Second httpClient := &http.Client{Timeout: clientTimeout} pathsToCheck := []string{ "debug/pprof/", "debug/pprof/cmdline", "debug/pprof/profile?seconds=1", "debug/pprof/symbol", "debug/pprof/trace?seconds=1", "debug/pprof/block", "debug/pprof/goroutine", "debug/pprof/heap", "debug/pprof/threadcreate", } type httpResult struct { url string response *http.Response err error } results := make(chan httpResult) for _, pathToCheck := range pathsToCheck { url := "http://" + serverAddress + "/" + pathToCheck request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) require.NoError(t, err) go func(client *http.Client, request *http.Request, results chan<- httpResult) { response, err := client.Do(request) //nolint:bodyclose results <- httpResult{ url: request.URL.String(), response: response, err: err, } }(httpClient, request, results) } for range pathsToCheck { httpResult := <-results require.NoErrorf(t, httpResult.err, "unexpected error for URL %s: %s", httpResult.url, httpResult.err) assert.Equalf(t, http.StatusOK, httpResult.response.StatusCode, "unexpected status code for URL %s: %s", httpResult.url, http.StatusText(httpResult.response.StatusCode)) b, err := io.ReadAll(httpResult.response.Body) require.NoErrorf(t, err, "unexpected error for URL %s: %s", httpResult.url, err) assert.NotEmptyf(t, b, "response body is empty for URL %s", httpResult.url) err = httpResult.response.Body.Close() assert.NoErrorf(t, err, "unexpected error for URL %s: %s", httpResult.url, err) } cancel() <-done } func Test_Server_BadSettings(t *testing.T) { t.Parallel() settings := Settings{ BlockProfileRate: -1, } server, err := New(settings) assert.Nil(t, server) assert.ErrorIs(t, err, ErrBlockProfileRateNegative) const expectedErrMessage = "pprof settings failed validation: block profile rate cannot be negative" assert.EqualError(t, err, expectedErrMessage) }