From eb9c8d955c6c62b8ba73142ec640164e8b46ff3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pedro=20P=C3=A9rez?= Date: Wed, 20 Nov 2024 16:01:55 +0100 Subject: [PATCH] test improvements and some refactor --- ron.go | 17 ++-- ron_test.go | 166 ++++++++++++++++++++++--------------- testhelpers/testhelpers.go | 31 +++++++ 3 files changed, 142 insertions(+), 72 deletions(-) diff --git a/ron.go b/ron.go index 73a0443..273345e 100644 --- a/ron.go +++ b/ron.go @@ -43,10 +43,12 @@ type ( ) const ( - contentType string = "Content-Type" - headerJSON string = "application/json" - headerHTML_UTF8 string = "text/html; charset=utf-8" - headerPlain_UTF8 string = "text/plain; charset=utf-8" + ContentType string = "Content-Type" + HeaderJSON string = "application/json" + HeaderHTML_UTF8 string = "text/html; charset=utf-8" + HeaderCSS_UTF8 string = "text/css; charset=utf-8" + HeaderJS_UTF8 string = "text/javascript; charset=utf-8" + HeaderPlain_UTF8 string = "text/plain; charset=utf-8" ) func defaultEngine() *Engine { @@ -166,9 +168,14 @@ func (e *Engine) Static(path, dir string) { dir = "./" + dir } + if _, err := os.Stat(dir); os.IsNotExist(err) { + slog.Error("static directory does not exist", "path", path, "dir", dir) + return + } + fs := http.FileServer(http.Dir(dir)) e.mux.Handle(path, http.StripPrefix(path, fs)) - slog.Info("Static files served", "path", path, "dir", dir) + slog.Info("static files served", "path", path, "dir", dir) } func (c *Context) JSON(code int, data any) { diff --git a/ron_test.go b/ron_test.go index 26f09eb..98c96b8 100644 --- a/ron_test.go +++ b/ron_test.go @@ -1,10 +1,12 @@ package ron import ( + "fmt" "log/slog" "net/http" "net/http/httptest" "os" + "ron/testhelpers" "testing" ) @@ -14,9 +16,20 @@ func TestMain(m *testing.M) { f.WriteString("

{{.Data.heading1}}

{{.Data.heading2}}

") f.Close() + os.Mkdir("assets", os.ModePerm) + f, _ = os.Create("assets/style.css") + f.WriteString("body { background-color: red; }") + f.Close() + + f, _ = os.Create("assets/script.js") + f.WriteString("console.log('Hello, World!');") + f.Close() + code := m.Run() os.RemoveAll("templates") + os.RemoveAll("assets") + os.Exit(code) } @@ -276,21 +289,56 @@ func Test_GROUPPOST(t *testing.T) { } func Test_Static(t *testing.T) { - os.Mkdir("assets", os.ModePerm) - f, _ := os.Create("assets/style.css") - f.WriteString("body { background-color: red; }") - f.Close() - defer os.RemoveAll("assets") + tests := map[string]struct { + givenPath string + givenFile string + givenDirectory string + expectedResponse testhelpers.ExpectedResponse + }{ + "valid css path": { + givenPath: "assets", + givenFile: "style.css", + givenDirectory: "assets", + expectedResponse: testhelpers.ExpectedResponse{ + Code: http.StatusOK, + Header: HeaderCSS_UTF8, + Body: "body { background-color: red; }", + }, + }, + "valid js path": { + givenPath: "assets", + givenFile: "script.js", + givenDirectory: "assets", + expectedResponse: testhelpers.ExpectedResponse{ + Code: http.StatusOK, + Header: HeaderJS_UTF8, + Body: "console.log('Hello, World!');", + }, + }, + "invalid path": { + givenPath: "assets", + givenFile: "nonexistent.css", + givenDirectory: "assets", + expectedResponse: testhelpers.ExpectedResponse{ + Code: http.StatusNotFound, + Header: HeaderPlain_UTF8, + Body: "404 page not found\n", + }, + }, + } - e := New() - e.Static("assets", "assets") + for name, tt := range tests { + t.Run(name, func(t *testing.T) { + t.Parallel() + e := New() + e.Static(tt.givenPath, tt.givenDirectory) - rr := httptest.NewRecorder() - req, _ := http.NewRequest("GET", "/assets/style.css", nil) - e.ServeHTTP(rr, req) + rr := httptest.NewRecorder() + req, _ := http.NewRequest("GET", fmt.Sprintf("/%s/%s", tt.givenPath, tt.givenFile), nil) + e.ServeHTTP(rr, req) - if status := rr.Code; status != http.StatusOK { - t.Errorf("Expected status code: %d, Actual: %d", http.StatusOK, status) + testhelpers.VerifyResponse(t, rr, tt.expectedResponse) + }) } } @@ -302,25 +350,27 @@ type Foo struct { func Test_JSON(t *testing.T) { tests := map[string]struct { - givenCode int - givenData any - expectedCode int - expectedHeader string - expectedBody string + givenCode int + givenData any + expectedResponse testhelpers.ExpectedResponse }{ "valid JSON": { - givenCode: http.StatusOK, - givenData: Foo{Bar: "bar", Taz: 30, Car: nil}, - expectedCode: http.StatusOK, - expectedHeader: headerJSON, - expectedBody: `{"bar":"bar","something":30,"car":null}` + "\n", + givenCode: http.StatusOK, + givenData: Foo{Bar: "bar", Taz: 30, Car: nil}, + expectedResponse: testhelpers.ExpectedResponse{ + Code: http.StatusOK, + Header: HeaderJSON, + Body: `{"bar":"bar","something":30,"car":null}` + "\n", + }, }, "invalid JSON": { - givenCode: http.StatusOK, - givenData: make(chan int), - expectedCode: http.StatusInternalServerError, - expectedHeader: headerPlain_UTF8, - expectedBody: "json: unsupported type: chan int\n", + givenCode: http.StatusOK, + givenData: make(chan int), + expectedResponse: testhelpers.ExpectedResponse{ + Code: http.StatusInternalServerError, + Header: HeaderPlain_UTF8, + Body: "json: unsupported type: chan int\n", + }, }, } @@ -334,45 +384,37 @@ func Test_JSON(t *testing.T) { c.JSON(tt.givenCode, tt.givenData) - if status := rr.Code; status != tt.expectedCode { - t.Errorf("Expected status code: %d, Actual: %d", tt.expectedCode, status) - } - - if rr.Header().Get(contentType) != tt.expectedHeader { - t.Errorf("Expected Content-Type: %s, Actual: %s", tt.expectedHeader, rr.Header().Get(contentType)) - } - - if rr.Body.String() != tt.expectedBody { - t.Errorf("Expected body: %q, Actual: %q", tt.expectedBody, rr.Body.String()) - } + testhelpers.VerifyResponse(t, rr, tt.expectedResponse) }) } } func Test_HTML(t *testing.T) { tests := map[string]struct { - givenCode int - givenTemplate string - givenData *TemplateData - expectedCode int - expectedHeader string - expectedBody string + givenCode int + givenTemplate string + givenData *TemplateData + expectedResponse testhelpers.ExpectedResponse }{ "valid HTML": { - givenCode: http.StatusOK, - givenTemplate: "page.index.gohtml", - givenData: &TemplateData{Data: Data{"heading1": "foo", "heading2": "bar"}}, - expectedCode: http.StatusOK, - expectedHeader: headerHTML_UTF8, - expectedBody: "

foo

bar

", + givenCode: http.StatusOK, + givenTemplate: "page.index.gohtml", + givenData: &TemplateData{Data: Data{"heading1": "foo", "heading2": "bar"}}, + expectedResponse: testhelpers.ExpectedResponse{ + Code: http.StatusOK, + Header: HeaderHTML_UTF8, + Body: "

foo

bar

", + }, }, "template not found": { - givenCode: http.StatusOK, - givenTemplate: "nonexistent.gohtml", - givenData: &TemplateData{Data: Data{"heading1": "foo", "heading2": "bar"}}, - expectedCode: http.StatusInternalServerError, - expectedHeader: headerPlain_UTF8, - expectedBody: "can't get template from cache\n", + givenCode: http.StatusOK, + givenTemplate: "nonexistent.gohtml", + givenData: &TemplateData{Data: Data{"heading1": "foo", "heading2": "bar"}}, + expectedResponse: testhelpers.ExpectedResponse{ + Code: http.StatusInternalServerError, + Header: HeaderPlain_UTF8, + Body: "can't get template from cache\n", + }, }, } @@ -389,17 +431,7 @@ func Test_HTML(t *testing.T) { c.HTML(tt.givenCode, tt.givenTemplate, tt.givenData) - if status := rr.Code; status != tt.expectedCode { - t.Errorf("Expected status code: %d, Actual: %d", tt.expectedCode, status) - } - - if rr.Header().Get(contentType) != tt.expectedHeader { - t.Errorf("Expected Content-Type: %s, Actual: %s", tt.expectedHeader, rr.Header().Get(contentType)) - } - - if rr.Body.String() != tt.expectedBody { - t.Errorf("Expected body: %q, Actual: %q", tt.expectedBody, rr.Body.String()) - } + testhelpers.VerifyResponse(t, rr, tt.expectedResponse) }) } diff --git a/testhelpers/testhelpers.go b/testhelpers/testhelpers.go index 1fc8a9c..32765f6 100644 --- a/testhelpers/testhelpers.go +++ b/testhelpers/testhelpers.go @@ -1,5 +1,10 @@ package testhelpers +import ( + "net/http/httptest" + "testing" +) + func CheckSlicesEquality(a []any, b []any) bool { if len(a) != len(b) { return false @@ -31,3 +36,29 @@ func StringSliceToAnySlice(s []string) []any { } return result } + +type ExpectedResponse struct { + Code int + Header string + Body string +} + +func VerifyResponse(t *testing.T, rr *httptest.ResponseRecorder, expectedResponse ExpectedResponse) { + t.Helper() + + expectedCode := expectedResponse.Code + expectedHeader := expectedResponse.Header + expectedBody := expectedResponse.Body + + if status := rr.Code; status != expectedCode { + t.Errorf("Expected status code: %d, Actual: %d", expectedCode, status) + } + + if header := rr.Header().Get("Content-Type"); header != expectedHeader { + t.Errorf("Expected Content-Type: %s, Actual: %s", expectedHeader, header) + } + + if body := rr.Body.String(); body != expectedBody { + t.Errorf("Expected body: %q, Actual: %q", expectedBody, body) + } +}