From e06e8c4e25cc3176ac67d818a5cf98909f2cd821 Mon Sep 17 00:00:00 2001 From: grumbulon Date: Fri, 10 Mar 2023 22:37:25 -0500 Subject: [PATCH] make tests fully table driven and increase coverage by a lot --- internal/api/api_test.go | 303 ++++++++++++++++++++++++--------------- 1 file changed, 188 insertions(+), 115 deletions(-) diff --git a/internal/api/api_test.go b/internal/api/api_test.go index 5f87f59..6f67767 100644 --- a/internal/api/api_test.go +++ b/internal/api/api_test.go @@ -88,10 +88,9 @@ func TestAPI(t *testing.T) { db.Create(&internal.User{Username: tester.username, HashedPassword: string(hashedPassword)}) - tester.TestMakeAccount(t) - tester.TestLogin(t) - tester.TestLogout(t) - tester.TestUpload(t) + tester.TestOpenRoutes(t) + tester.TestProtectedRoutes(t) + tester.TestRateLimit(t) tester.CleanUp(db) } @@ -115,119 +114,203 @@ func Init(url string) accountTest { return test } -func (a *accountTest) TestMakeAccount(t *testing.T) { - var target response - - client := http.Client{} - - form := url.Values{} - - form.Add("username", a.username) - - form.Add("password", a.password) - - if req, err := http.NewRequest(http.MethodPost, a.url+`api/create`, strings.NewReader(form.Encode())); err == nil { - req.Header.Add("Content-Type", "application/x-www-form-urlencoded") - - resp, err := client.Do(req) - if err != nil { - assert.NotNil(t, err) - } - - respBody, _ := io.ReadAll(resp.Body) - - err = json.Unmarshal(respBody, &target) - if err != nil { - assert.NotNil(t, err) - } - - assert.Equal(t, http.StatusCreated, target.Status) - } -} - -func (a *accountTest) TestLogin(t *testing.T) { - var target response - - client := http.Client{} - - form := url.Values{} - - form.Add("username", a.username) - - form.Add("password", a.password) - - if req, err := http.NewRequest(http.MethodPost, a.url+`/api/login`, strings.NewReader(form.Encode())); err == nil { - req.Header.Add("Content-Type", "application/x-www-form-urlencoded") - - resp, err := client.Do(req) - if err != nil { - assert.NotNil(t, err) - } - - respBody, _ := io.ReadAll(resp.Body) - - err = json.Unmarshal(respBody, &target) - if err != nil { - assert.NotNil(t, err) - } - - assert.Equal(t, http.StatusOK, resp.StatusCode) - } -} - -func (a *accountTest) TestLogout(t *testing.T) { - var target response - - client := http.Client{} - - form := url.Values{} - - form.Add("username", a.username) - - if req, err := http.NewRequest(http.MethodPost, a.url+`/api/logout`, strings.NewReader(form.Encode())); err == nil { - req.Header.Add("Content-Type", "application/x-www-form-urlencoded") - - resp, err := client.Do(req) - if err != nil { - assert.NotNil(t, err) - } - - respBody, _ := io.ReadAll(resp.Body) - - err = json.Unmarshal(respBody, &target) - if err != nil { - assert.NotNil(t, err) - } - - assert.Equal(t, http.StatusOK, resp.StatusCode) - } -} - type expectedValues struct { response int } -func (a *accountTest) TestUpload(t *testing.T) { +func (a *accountTest) TestOpenRoutes(t *testing.T) { + testCases := []struct { + name string + route string + token string + password string + username string + expected expectedValues + }{ + { + name: "Should fail to login with bad username", + route: "login", + username: "a.username", + password: a.password, + expected: expectedValues{ + response: http.StatusUnauthorized, + }, + }, + { + name: "Should fail to login with bad password", + route: "login", + username: a.username, + password: "a.password", + expected: expectedValues{ + response: http.StatusUnauthorized, + }, + }, + { + name: "Should fail to login with bad password or username", + route: "login", + username: "apple", + password: "a.password", + expected: expectedValues{ + response: http.StatusUnauthorized, + }, + }, + { + name: "Should fail to login with empty form", + route: "login", + expected: expectedValues{ + response: http.StatusInternalServerError, + }, + }, + { + name: "Should login successfully", + route: "login", + username: a.username, + password: a.password, + expected: expectedValues{ + response: http.StatusOK, + }, + }, + { + name: "Should fail to create account with empty form", + route: "create", + expected: expectedValues{ + response: http.StatusInternalServerError, + }, + }, + { + name: "Should create account with empty username", + route: "create", + password: "asdf", + expected: expectedValues{ + response: http.StatusCreated, + }, + }, + { + name: "Should fail to create account with empty password", + route: "create", + expected: expectedValues{ + response: http.StatusInternalServerError, + }, + }, + { + name: "Should log out successfully", + route: "logout", + username: a.username, + expected: expectedValues{ + response: http.StatusOK, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var target response + + client := http.Client{} + + form := url.Values{} + + form.Add("username", tc.username) + + form.Add("password", tc.password) + + if req, err := http.NewRequest(http.MethodPost, a.url+`/api/`+tc.route, strings.NewReader(form.Encode())); err == nil { + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + + resp, err := client.Do(req) + if err != nil { + assert.NotNil(t, err) + } + + respBody, _ := io.ReadAll(resp.Body) + + err = json.Unmarshal(respBody, &target) + if err != nil { + assert.NotNil(t, err) + } + + assert.Equal(t, tc.expected.response, resp.StatusCode) + } + }) + } +} + +func (a *accountTest) TestRateLimit(t *testing.T) { + testCases := []struct { + name string + route string + password string + username string + expected expectedValues + }{ + { + name: "Should rate limit open routes", + route: "login", + expected: expectedValues{ + response: http.StatusTooManyRequests, + }, + }, + { + name: "Should rate limit private routes", + route: "upload", + expected: expectedValues{ + response: http.StatusTooManyRequests, + }, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var target response + + client := http.Client{} + + form := url.Values{} + + if req, err := http.NewRequest(http.MethodPost, a.url+`/api/`+tc.route, strings.NewReader(form.Encode())); err == nil { + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + + for i := 0; i < 100; i++ { + resp, err := client.Do(req) + if err != nil { + assert.NotNil(t, err) + } + + respBody, _ := io.ReadAll(resp.Body) + + err = json.Unmarshal(respBody, &target) + if err != nil { + assert.NotNil(t, err) + } + if resp.StatusCode == tc.expected.response { + assert.Equal(t, tc.expected.response, resp.StatusCode) + } + } + } + }) + } +} + +func (a *accountTest) TestProtectedRoutes(t *testing.T) { testCases := []struct { name string - file string contentType string + route string fileContents []byte expected expectedValues }{ { name: "Should fail to upload an empty file", - file: "badzonefile", contentType: "audio/aac", + route: "upload", expected: expectedValues{ response: http.StatusInternalServerError, }, }, { name: "Should upload a valid file", - file: "zonefile", contentType: "multipart/form-data", fileContents: []byte{}, + route: "upload", expected: expectedValues{ response: http.StatusCreated, }, @@ -244,18 +327,12 @@ func (a *accountTest) TestUpload(t *testing.T) { w = multipart.NewWriter(buf) ) - switch tc.name { - case "Should fail to upload an empty file": - f, err = os.CreateTemp(".", tc.file) - if err != nil { - assert.NotNil(t, err) - } - default: - f, err = os.CreateTemp(".", "zonefile") - if err != nil { - assert.NotNil(t, err) - } + f, err = os.CreateTemp(".", "zonefile") + if err != nil { + assert.NotNil(t, err) + } + if tc.name == "Should upload a valid file" { if err = os.WriteFile(f.Name(), zonebytes, 0o600); err != nil { assert.NotNil(t, err) } @@ -263,8 +340,6 @@ func (a *accountTest) TestUpload(t *testing.T) { defer os.Remove(f.Name()) //nolint: errcheck - client := http.Client{} - part, err := w.CreateFormFile("file", filepath.Base(f.Name())) if err != nil { assert.NotNil(t, err) @@ -284,12 +359,11 @@ func (a *accountTest) TestUpload(t *testing.T) { if err != nil { assert.NotNil(t, err) } - - if req, err := http.NewRequest(http.MethodPost, a.url+`/api/upload`, buf); err == nil { - switch tc.name { - case "Should fail to upload an empty file": + client := http.Client{} + if req, err := http.NewRequest(http.MethodPost, a.url+`/api/`+tc.route, buf); err == nil { + if tc.name == "Should fail to upload an empty file" { req.Header.Add("Content-Type", tc.contentType) - default: + } else { req.Header.Add("Content-Type", w.FormDataContentType()) } @@ -306,7 +380,6 @@ func (a *accountTest) TestUpload(t *testing.T) { if err != nil { assert.NotNil(t, err) } - assert.Equal(t, tc.expected.response, resp.StatusCode) } })