From b9ee28ca8deefc454687c5c0580bd6b767938768 Mon Sep 17 00:00:00 2001 From: Amir Raminfar Date: Thu, 29 Nov 2018 10:55:25 -0800 Subject: [PATCH] Adds tests and snapshots --- __snapshots__/dozzle.snapshot | 6 ++ main.go | 110 ++++++++++++++++++---------------- main_test.go | 58 ++++++++++++++++++ package-lock.json | 14 ++--- package.json | 3 +- 5 files changed, 130 insertions(+), 61 deletions(-) create mode 100644 __snapshots__/dozzle.snapshot create mode 100644 main_test.go diff --git a/__snapshots__/dozzle.snapshot b/__snapshots__/dozzle.snapshot new file mode 100644 index 00000000..c0b32781 --- /dev/null +++ b/__snapshots__/dozzle.snapshot @@ -0,0 +1,6 @@ +/* snapshot: /api/containers.json */ +HTTP/1.1 200 OK +Connection: close +Content-Type: text/plain; charset=utf-8 + +[{"id":"1234567890","names":null,"name":"test","image":"image","imageId":"image_id","command":"command","created":0,"state":"state","status":"status"}] \ No newline at end of file diff --git a/main.go b/main.go index 68245224..cb64994b 100644 --- a/main.go +++ b/main.go @@ -11,20 +11,25 @@ import ( log "github.com/sirupsen/logrus" flag "github.com/spf13/pflag" "html/template" + "io" "net/http" "strings" ) var ( - dockerClient docker.Client - addr = "" - base = "" - level = "" - version = "dev" - commit = "none" - date = "unknown" + addr = "" + base = "" + level = "" + version = "dev" + commit = "none" + date = "unknown" ) +type handler struct { + client docker.Client + box packr.Box +} + func init() { flag.StringVar(&addr, "addr", ":8080", "http service address") flag.StringVar(&base, "base", "/", "base address of the application to mount") @@ -35,19 +40,22 @@ func init() { log.SetLevel(l) log.SetFormatter(&log.TextFormatter{ - DisableTimestamp: true, - DisableLevelTruncation: true, + DisableTimestamp: true, + DisableLevelTruncation: true, }) +} - dockerClient = docker.NewClient() +func main() { + dockerClient := docker.NewClient() _, err := dockerClient.ListContainers() if err != nil { log.Fatalf("Could not connect to Docker Engine: %v", err) } -} -func main() { + box := packr.NewBox("./static") + h := &handler{dockerClient, box} + r := mux.NewRouter() if base != "/" { @@ -57,53 +65,43 @@ func main() { } s := r.PathPrefix(base).Subrouter() - box := packr.NewBox("./static") - - s.HandleFunc("/api/containers.json", listContainers) - s.HandleFunc("/api/logs/stream", streamLogs) - s.HandleFunc("/api/events/stream", streamEvents) - s.HandleFunc("/version", versionHandler) - s.PathPrefix("/").Handler(http.StripPrefix(base, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - fileServer := http.FileServer(box) - if box.Has(req.URL.Path) && req.URL.Path != "" && req.URL.Path != "/" { - fileServer.ServeHTTP(w, req) - } else { - handleIndex(box, w) - } - }))) + s.HandleFunc("/api/containers.json", h.listContainers) + s.HandleFunc("/api/logs/stream", h.streamLogs) + s.HandleFunc("/api/events/stream", h.streamEvents) + s.HandleFunc("/version", h.version) + s.PathPrefix("/").Handler(http.StripPrefix(base, http.HandlerFunc(h.index))) log.Infof("Accepting connections on %s", addr) log.Fatal(http.ListenAndServe(addr, r)) } -func versionHandler(w http.ResponseWriter, r *http.Request) { - fmt.Fprintln(w, version) - fmt.Fprintln(w, commit) - fmt.Fprintln(w, date) -} +func (h *handler) index(w http.ResponseWriter, req *http.Request) { + fileServer := http.FileServer(h.box) + if h.box.Has(req.URL.Path) && req.URL.Path != "" && req.URL.Path != "/" { + fileServer.ServeHTTP(w, req) + } else { + text, _ := h.box.FindString("index.html") + text = strings.Replace(text, "__BASE__", "{{ .Base }}", -1) + tmpl, err := template.New("index.html").Parse(text) + if err != nil { + panic(err) + } -func handleIndex(box packr.Box, w http.ResponseWriter) { - text, _ := box.FindString("index.html") - text = strings.Replace(text, "__BASE__", "{{ .Base }}", -1) - tmpl, err := template.New("index.html").Parse(text) - if err != nil { - panic(err) - } + path := "" + if base != "/" { + path = base + } - path := "" - if base != "/" { - path = base - } - - data := struct{ Base string }{path} - err = tmpl.Execute(w, data) - if err != nil { - panic(err) + data := struct{ Base string }{path} + err = tmpl.Execute(w, data) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } } } -func listContainers(w http.ResponseWriter, r *http.Request) { - containers, err := dockerClient.ListContainers() +func (h *handler) listContainers(w http.ResponseWriter, r *http.Request) { + containers, err := h.client.ListContainers() if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return @@ -115,7 +113,7 @@ func listContainers(w http.ResponseWriter, r *http.Request) { } } -func streamLogs(w http.ResponseWriter, r *http.Request) { +func (h *handler) streamLogs(w http.ResponseWriter, r *http.Request) { id := r.URL.Query().Get("id") if id == "" { http.Error(w, "id is required", http.StatusBadRequest) @@ -130,7 +128,7 @@ func streamLogs(w http.ResponseWriter, r *http.Request) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - reader, err := dockerClient.ContainerLogs(ctx, id) + reader, err := h.client.ContainerLogs(ctx, id) if err != nil { log.Println(err) http.Error(w, err.Error(), http.StatusInternalServerError) @@ -170,7 +168,7 @@ func streamLogs(w http.ResponseWriter, r *http.Request) { } } -func streamEvents(w http.ResponseWriter, r *http.Request) { +func (h *handler) streamEvents(w http.ResponseWriter, r *http.Request) { f, ok := w.(http.Flusher) if !ok { http.Error(w, "Streaming unsupported!", http.StatusInternalServerError) @@ -184,7 +182,7 @@ func streamEvents(w http.ResponseWriter, r *http.Request) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - messages, err := dockerClient.Events(ctx) + messages, err := h.client.Events(ctx) Loop: for { @@ -216,3 +214,9 @@ Loop: } } } + +func (h *handler) version(w http.ResponseWriter, r *http.Request) { + io.WriteString(w, version) + io.WriteString(w, commit) + io.WriteString(w, date) +} diff --git a/main_test.go b/main_test.go new file mode 100644 index 00000000..16e785c1 --- /dev/null +++ b/main_test.go @@ -0,0 +1,58 @@ +package main + +import ( + "github.com/amir20/dozzle/docker" + "github.com/beme/abide" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "net/http" + "net/http/httptest" + "os" + "testing" +) + +type MockedClient struct { + mock.Mock + docker.Client +} + +func (m *MockedClient) ListContainers() ([]docker.Container, error) { + args := m.Called() + containers, _ := args.Get(0).([]docker.Container) + return containers, args.Error(1) +} + +func Test_listContainers(t *testing.T) { + req, err := http.NewRequest("GET", "/health-check", nil) + require.NoError(t, err, "NewRequest should not return an error.") + + rr := httptest.NewRecorder() + + mockedClient := new(MockedClient) + containers := []docker.Container{ + { + ID: "1234567890", + Status: "status", + State: "state", + Name: "test", + Created: 0, + Command: "command", + ImageID: "image_id", + Image: "image", + }, + } + mockedClient.On("ListContainers", mock.Anything).Return(containers, nil) + + h := handler{client: mockedClient} + + handler := http.HandlerFunc(h.listContainers) + + handler.ServeHTTP(rr, req) + abide.AssertHTTPResponse(t, "/api/containers.json", rr.Result()) +} + +func TestMain(m *testing.M) { + exit := m.Run() + abide.Cleanup() + os.Exit(exit) +} diff --git a/package-lock.json b/package-lock.json index 38c86f1d..0ea557a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9263,9 +9263,9 @@ "dev": true }, "yargs": { - "version": "12.0.4", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.4.tgz", - "integrity": "sha512-f5esswlPO351AnejaO2A1ZZr0zesz19RehQKwiRDqWtrraWrJy16tsUIKgDXFMVytvNOHPVmTiaTh3wO67I0fQ==", + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", "dev": true, "requires": { "cliui": "^4.0.0", @@ -9279,13 +9279,13 @@ "string-width": "^2.0.0", "which-module": "^2.0.0", "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^11.1.0" + "yargs-parser": "^11.1.1" } }, "yargs-parser": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.0.tgz", - "integrity": "sha512-lGA5HsbjkpCfekDBHAhgE5OE8xEoqiUDylowr+BvhRCwG1xVYTsd8hx2CYC0NY4k9RIgJeybFTG2EZW4P2aN1w==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", "dev": true, "requires": { "camelcase": "^5.0.0", diff --git a/package.json b/package.json index b8f81f75..f21882ad 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,9 @@ "main": "index.js", "scripts": { "prestart": "npm run clean", - "start": "DOCKER_API_VERSION=1.38 concurrently 'reflex -g '*.go' -R 'node_modules' -s -- go run main.go --level debug' 'npm run watch-assets'", + "start": "DOCKER_API_VERSION=1.38 concurrently 'npm run watch-server' 'npm run watch-assets'", "watch-assets": "parcel watch --public-url '__BASE__' assets/index.html -d static", + "watch-server": "reflex -g '*.go' -R '^node_modules/' -R '^static/' -R '^.cache/' -G '*_test.go' -s -- go run main.go --level debug", "prebuild": "npm run clean", "build": "parcel build --no-source-maps --public-url '__BASE__' assets/index.html -d static", "clean": "rm -rf static/ a_main-packr.go",