faas/gateway/handlers/logs_test.go
Alex Ellis (OpenFaaS Ltd) 2a88b5d2f7 Remove ioutil usage
This has been deprecated in Go for some time, in favour of the
io package.

Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alex@openfaas.com>
2023-08-22 18:29:28 +01:00

139 lines
3.9 KiB
Go

package handlers
import (
"context"
"fmt"
"io"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"time"
"go.uber.org/goleak"
)
func Test_logsProxyDoesNotLeakGoroutinesWhenProviderClosesConnection(t *testing.T) {
defer goleak.VerifyNone(t)
expectedMsg := "name: funcFoo msg: test message"
// mock log provider that sends one line and immediately closes the connection
mockLogsUpstreamEndpoint := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
t.Fatalf("expected method '%s' but got '%s'", http.MethodGet, r.Method)
}
if r.URL.Path != upstreamLogsEndpoint {
t.Fatalf("expected path '%s' but got '%s'", upstreamLogsEndpoint, r.URL.Path)
}
w.Header().Set(http.CanonicalHeaderKey("Connection"), "Keep-Alive")
w.Header().Set(http.CanonicalHeaderKey("Transfer-Encoding"), "chunked")
w.Header().Set(http.CanonicalHeaderKey("Content-Type"), "application/x-ndjson")
w.WriteHeader(http.StatusOK)
msg := fmt.Sprintf("name: %s msg: test message", r.URL.Query().Get("name"))
_, err := w.Write([]byte(msg))
if err != nil {
t.Fatalf("failed to write test log message: %s", err)
}
}))
defer mockLogsUpstreamEndpoint.Close()
logProviderURL, _ := url.Parse(mockLogsUpstreamEndpoint.URL)
logHandler := NewLogHandlerFunc(*logProviderURL, time.Minute)
testSrv := httptest.NewServer(http.HandlerFunc(logHandler))
defer testSrv.Close()
resp, err := http.Get(testSrv.URL + "?name=funcFoo")
if err != nil {
t.Fatalf("unexpected error sending log request: %s", err)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
t.Fatalf("unexpected error reading the response body: %s", err)
}
if string(body) != string(expectedMsg) {
t.Fatalf("expected log message %s, got: %s", expectedMsg, body)
}
}
func Test_logsProxyDoesNotLeakGoroutinesWhenClientClosesConnection(t *testing.T) {
defer goleak.VerifyNone(t)
// mock log provider that sends one line and holds until we cancel the context
mockLogsUpstreamEndpoint := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
cn, ok := w.(http.CloseNotifier)
if !ok {
http.Error(w, "cannot stream", http.StatusInternalServerError)
return
}
flusher, ok := w.(http.Flusher)
if !ok {
http.Error(w, "cannot stream", http.StatusInternalServerError)
return
}
if r.Method != http.MethodGet {
t.Fatalf("expected method '%s' but got '%s'", http.MethodGet, r.Method)
}
if r.URL.Path != upstreamLogsEndpoint {
t.Fatalf("expected path '%s' but got '%s'", upstreamLogsEndpoint, r.URL.Path)
}
w.Header().Set(http.CanonicalHeaderKey("Connection"), "Keep-Alive")
w.Header().Set(http.CanonicalHeaderKey("Transfer-Encoding"), "chunked")
w.Header().Set(http.CanonicalHeaderKey("Content-Type"), "application/x-ndjson")
w.WriteHeader(http.StatusOK)
msg := fmt.Sprintf("name: %s msg: test message", r.URL.Query().Get("name"))
_, err := w.Write([]byte(msg))
if err != nil {
t.Fatalf("failed to write test log message: %s", err)
}
flusher.Flush()
// "wait for connection to close"
<-cn.CloseNotify()
}))
defer mockLogsUpstreamEndpoint.Close()
logProviderURL, _ := url.Parse(mockLogsUpstreamEndpoint.URL)
logHandler := NewLogHandlerFunc(*logProviderURL, time.Minute)
testSrv := httptest.NewServer(http.HandlerFunc(logHandler))
defer testSrv.Close()
reqContext, cancel := context.WithCancel(context.Background())
req, _ := http.NewRequest(http.MethodGet, testSrv.URL+"?name=funcFoo", nil)
req = req.WithContext(reqContext)
resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatalf("unexpected error sending log request: %s", err)
}
errCh := make(chan error, 1)
go func() {
defer resp.Body.Close()
defer close(errCh)
_, err := io.ReadAll(resp.Body)
errCh <- err
}()
cancel()
err = <-errCh
if err != context.Canceled {
t.Fatalf("unexpected error reading the response body: %s", err)
}
}