Files
kapow/internal/server/user/mux/handlerbuilder_test.go
pancho horrillo ec227c2ba3 fix: don't flush status immediately
Closes: #148

Co-authored-by: Roberto Abdelkader Martínez Pérez <robertomartinezp@gmail.com>
2021-01-11 17:54:54 +01:00

277 lines
6.8 KiB
Go

// +build !race
/*
* Copyright 2019 Banco Bilbao Vizcaya Argentaria, S.A.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package mux
import (
"bytes"
"errors"
"io"
"log"
"net/http"
"net/http/httptest"
"reflect"
"strings"
"testing"
"time"
"github.com/google/uuid"
"github.com/BBVA/kapow/internal/logger"
"github.com/BBVA/kapow/internal/server/data"
"github.com/BBVA/kapow/internal/server/model"
"github.com/BBVA/kapow/internal/server/user/spawn"
)
func TestHandlerBuilderCallsSpawner(t *testing.T) {
route := model.Route{}
idGenerator = uuid.NewUUID
called := false
spawner = func(h *model.Handler, out io.Writer, er io.Writer) error {
called = true
return nil
}
w := httptest.NewRecorder()
handlerBuilder(route).ServeHTTP(w, nil)
if !called {
t.Error("Didn't call spawner")
}
}
func TestHandlerBuilderStoresHandlerInDataHandlers(t *testing.T) {
route := model.Route{}
added := false
spawner = func(h *model.Handler, out io.Writer, er io.Writer) error {
added = len(data.Handlers.ListIDs()) != 0
return nil
}
h := handlerBuilder(route)
data.Handlers = data.New()
w := httptest.NewRecorder()
h.ServeHTTP(w, nil)
if !added {
t.Error("handler not added to data.Handlers")
}
}
func TestHandlerBuilderStoresTheProperRoute(t *testing.T) {
data.Handlers = data.New()
route := model.Route{
ID: "foo",
}
var got model.Route
spawner = func(h *model.Handler, out io.Writer, er io.Writer) error {
hid := data.Handlers.ListIDs()[0]
handler, _ := data.Handlers.Get(hid)
got = handler.Route
return nil
}
w := httptest.NewRecorder()
handlerBuilder(route).ServeHTTP(w, nil)
if !reflect.DeepEqual(got, route) {
t.Error("Route not stored properly in the handler")
}
}
func TestHandlerBuilderStoresTheProperRequest(t *testing.T) {
data.Handlers = data.New()
route := model.Route{}
var got *http.Request
spawner = func(h *model.Handler, out io.Writer, er io.Writer) error {
hid := data.Handlers.ListIDs()[0]
handler, _ := data.Handlers.Get(hid)
got = handler.Request
return nil
}
r := &http.Request{}
w := httptest.NewRecorder()
handlerBuilder(route).ServeHTTP(w, r)
if got != r {
t.Error("Request not stored properly in the handler")
}
}
func TestHandlerBuilderStoresTheProperResponseWriter(t *testing.T) {
data.Handlers = data.New()
route := model.Route{}
var got http.ResponseWriter
spawner = func(h *model.Handler, out io.Writer, er io.Writer) error {
hid := data.Handlers.ListIDs()[0]
handler, _ := data.Handlers.Get(hid)
got = handler.Writer
return nil
}
w := httptest.NewRecorder()
w.Flushed = !w.Flushed
handlerBuilder(route).ServeHTTP(w, nil)
if !reflect.DeepEqual(got, w) {
t.Error("ResponseWriter not stored properly in the handler")
}
}
func TestHandlerBuilderGeneratesAProperID(t *testing.T) {
data.Handlers = data.New()
route := model.Route{}
var got string
spawner = func(h *model.Handler, out io.Writer, er io.Writer) error {
hid := data.Handlers.ListIDs()[0]
handler, _ := data.Handlers.Get(hid)
got = handler.ID
return nil
}
w := httptest.NewRecorder()
handlerBuilder(route).ServeHTTP(w, nil)
if _, err := uuid.Parse(got); err != nil {
t.Error("ID not generated properly")
}
}
func TestHandlerBuilderCallsSpawnerWithTheStoredHandler(t *testing.T) {
data.Handlers = data.New()
route := model.Route{}
var gotStored *model.Handler
var gotPassed *model.Handler
spawner = func(h *model.Handler, out io.Writer, er io.Writer) error {
gotPassed = h
hid := data.Handlers.ListIDs()[0]
gotStored, _ = data.Handlers.Get(hid)
return nil
}
w := httptest.NewRecorder()
handlerBuilder(route).ServeHTTP(w, nil)
if gotStored != gotPassed {
t.Error("Proper handler not passed to spawner()")
}
}
func TestHandlerBuilder500sWhenIDGeneratorFails(t *testing.T) {
data.Handlers = data.New()
spawner = spawn.Spawn
route := model.Route{}
w := httptest.NewRecorder()
idGenerator = func() (uuid.UUID, error) {
var uuid uuid.UUID
return uuid, errors.New(
"End of Time reached; Try again before, or in the next Big Bang cycle")
}
handlerBuilder(route).ServeHTTP(w, nil)
if w.Result().StatusCode != http.StatusInternalServerError {
t.Error("ID generation failure not handled gracefully")
}
}
func TestHandlerBuilderRemovesHandlerWhenDone(t *testing.T) {
data.Handlers = data.New()
spawner = spawn.Spawn
idGenerator = uuid.NewUUID
route := model.Route{}
w := httptest.NewRecorder()
handlerBuilder(route).ServeHTTP(w, nil)
if len(data.Handlers.ListIDs()) != 0 {
t.Error("Handler not removed upon completion")
}
}
func TestHandlerBuilderLogToLogHandlerWhenDebugIsEnabled(t *testing.T) {
data.Handlers = data.New()
route := model.Route{Debug: true}
var got string
logHandler := new(bytes.Buffer)
logger.L = log.New(logHandler, "", log.LstdFlags)
spawner = func(h *model.Handler, out io.Writer, er io.Writer) error {
_, _ = out.Write([]byte("this is stdout"))
_, _ = er.Write([]byte("this is stderr"))
return nil
}
w := httptest.NewRecorder()
handlerBuilder(route).ServeHTTP(w, nil)
// NOTE: logStream will write stdout and stderr contents eventually.
// We do not have any control the goroutines running logStream, thus we
// cannot use a synchronization primitive to wait for them. Sorry.
time.Sleep(1 * time.Second)
got = logHandler.String()
if !strings.Contains(got, "this is stdout") {
t.Errorf("Stdout not preserved. Actual: %+q", got)
}
if !strings.Contains(got, "this is stderr") {
t.Errorf("Stderr not preserved. Actual: %+q", got)
}
}
func TestHandlerBuilderDoesNotLogToLogHandlerWhenDebugIsDisabled(t *testing.T) {
data.Handlers = data.New()
route := model.Route{Debug: false}
logHandler := new(bytes.Buffer)
logger.L = log.New(logHandler, "", log.LstdFlags)
spawner = func(h *model.Handler, out io.Writer, er io.Writer) error {
if out != nil {
_, _ = out.Write([]byte("this is stdout"))
}
if er != nil {
_, _ = er.Write([]byte("this is stderr"))
}
return nil
}
w := httptest.NewRecorder()
handlerBuilder(route).ServeHTTP(w, nil)
// NOTE: logStream will write stdout and stderr contents eventually.
// We do not have any control the goroutines running logStream, thus we
// cannot use a synchronization primitive to wait for them. Sorry.
time.Sleep(1 * time.Second)
size := logHandler.Len()
if size != 0 {
t.Error("Something was logged to stderr with debug=false")
}
}