diff --git a/README.md b/README.md index 97e2780..02de5e2 100644 --- a/README.md +++ b/README.md @@ -115,10 +115,10 @@ For that you need to send a field `ID` to the backend. Next changes sorted by priority -- Fix handler unit tests -- Switch configuration to TOML -- Add Authorization (oAuth e.g. Google) -- Add Deletion functionality (depends on the authorization) -- Add ability to track the visitors (Referrer, maybe also live) -- Test docker-compose installation -- Provide image on the docker hub \ No newline at end of file +- [x] Fix handler unit tests +- [ ] Switch configuration to TOML +- [ ] Add Authorization (oAuth e.g. Google) +- [ ] Add Deletion functionality (depends on the authorization) +- [ ] Add ability to track the visitors (Referrer, maybe also live) +- [ ] Test docker-compose installation +- [ ] Provide image on the docker hub \ No newline at end of file diff --git a/handlers/handlers_test.go b/handlers/handlers_test.go index 4703185..cf4645b 100644 --- a/handlers/handlers_test.go +++ b/handlers/handlers_test.go @@ -4,7 +4,6 @@ import ( "bytes" "encoding/json" "io/ioutil" - "mime/multipart" "net/http" "net/http/httptest" "net/url" @@ -12,6 +11,7 @@ import ( "strings" "testing" + "github.com/gin-gonic/gin" "github.com/maxibanki/golang-url-shortener/store" "github.com/pkg/errors" ) @@ -23,18 +23,18 @@ const ( var server *httptest.Server -func TestCreateEntryJSON(t *testing.T) { +func TestCreateEntry(t *testing.T) { tt := []struct { name string ignoreResponse bool contentType string - response string + response gin.H requestBody URLUtil statusCode int }{ { name: "body is nil", - response: "could not decode JSON: EOF", + response: gin.H{"error": "EOF"}, statusCode: http.StatusBadRequest, contentType: "application/json; charset=utf-8", ignoreResponse: true, @@ -54,7 +54,7 @@ func TestCreateEntryJSON(t *testing.T) { }, statusCode: http.StatusBadRequest, contentType: "application/json; charset=utf-8", - response: store.ErrNoValidURL.Error(), + response: gin.H{"error": store.ErrNoValidURL.Error()}, ignoreResponse: true, }, } @@ -76,8 +76,9 @@ func TestCreateEntryJSON(t *testing.T) { reqBody = nil } respBody := createEntryWithJSON(t, reqBody, tc.contentType, tc.statusCode) - if tc.response != "" { - if string(respBody) != string(tc.response) { + if len(tc.response) > 0 { + raw := makeJSON(t, tc.response) + if string(respBody) != raw { t.Fatalf("expected body: %s; got: %s", tc.response, respBody) } } @@ -96,161 +97,6 @@ func TestCreateEntryJSON(t *testing.T) { } } -func TestCreateEntryMultipart(t *testing.T) { - cleanup, err := getBackend() - if err != nil { - t.Fatalf("could not create backend: %v", err) - } - defer cleanup() - - t.Run("valid request", func(t *testing.T) { - // Prepare a form that you will submit to that URL. - var b bytes.Buffer - multipartWriter := multipart.NewWriter(&b) - formWriter, err := multipartWriter.CreateFormField("URL") - if err != nil { - t.Fatalf("could not create form field: %v", err) - } - formWriter.Write([]byte(testURL)) - multipartWriter.Close() - - resp, err := http.Post(server.URL+"/api/v1/create", multipartWriter.FormDataContentType(), &b) - if err != nil { - t.Fatalf("could not post to the backend: %v", err) - } - if resp.StatusCode != http.StatusOK { - t.Errorf("expected status %d; got %d", http.StatusOK, resp.StatusCode) - } - var parsed URLUtil - err = json.NewDecoder(resp.Body).Decode(&parsed) - if err != nil { - t.Fatalf("could not unmarshal data: %v", err) - } - t.Run("test if shorted URL is correct", func(t *testing.T) { - testRedirect(t, parsed.URL, testURL) - }) - }) - - t.Run("invalid url", func(t *testing.T) { - // Prepare a form that you will submit to that URL. - var b bytes.Buffer - multipartWriter := multipart.NewWriter(&b) - formWriter, err := multipartWriter.CreateFormField("URL") - if err != nil { - t.Fatalf("could not create form field: %v", err) - } - formWriter.Write([]byte("this is definitely not a valid url")) - multipartWriter.Close() - - resp, err := http.Post(server.URL+"/api/v1/create", multipartWriter.FormDataContentType(), &b) - if err != nil { - t.Fatalf("could not post to the backend: %v", err) - } - if resp.StatusCode != http.StatusBadRequest { - t.Errorf("expected status %d; got %d", http.StatusOK, resp.StatusCode) - } - body, err := ioutil.ReadAll(resp.Body) - body = bytes.TrimSpace(body) - if err != nil { - t.Fatalf("could not read the body: %v", err) - } - if string(body) != store.ErrNoValidURL.Error() { - t.Fatalf("received unexpected response: %s", body) - } - }) - - t.Run("invalid request", func(t *testing.T) { - // Prepare a form that you will submit to that URL. - var b bytes.Buffer - multipartWriter := multipart.NewWriter(&b) - multipartWriter.Close() - - resp, err := http.Post(server.URL+"/api/v1/create", multipartWriter.FormDataContentType(), &b) - if err != nil { - t.Fatalf("could not post to the backend: %v", err) - } - if resp.StatusCode != http.StatusBadRequest { - t.Errorf("expected status %d; got %d", http.StatusOK, resp.StatusCode) - } - body, err := ioutil.ReadAll(resp.Body) - body = bytes.TrimSpace(body) - if err != nil { - t.Fatalf("could not read the body") - } - if string(body) != "URL key does not exist" { - t.Fatalf("body has not the excepted payload; got: %s", body) - } - }) -} - -func TestCreateEntryForm(t *testing.T) { - cleanup, err := getBackend() - if err != nil { - t.Fatalf("could not create backend: %v", err) - } - defer cleanup() - - t.Run("valid request", func(t *testing.T) { - data := url.Values{} - data.Set("URL", testURL) - - resp, err := http.Post(server.URL+"/api/v1/create", "application/x-www-form-urlencoded", bytes.NewBufferString(data.Encode())) - if err != nil { - t.Fatalf("could not post to the backend: %v", err) - } - if resp.StatusCode != http.StatusOK { - t.Errorf("expected status %d; got %d", http.StatusOK, resp.StatusCode) - } - var parsed URLUtil - err = json.NewDecoder(resp.Body).Decode(&parsed) - if err != nil { - t.Fatalf("could not unmarshal data: %v", err) - } - t.Run("test if shorted URL is correct", func(t *testing.T) { - testRedirect(t, parsed.URL, testURL) - }) - }) - - t.Run("invalid request", func(t *testing.T) { - resp, err := http.Post(server.URL+"/api/v1/create", "application/x-www-form-urlencoded", bytes.NewBufferString(url.Values{}.Encode())) - if err != nil { - t.Fatalf("could not post to the backend: %v", err) - } - if resp.StatusCode != http.StatusBadRequest { - t.Errorf("expected status %d; got %d", http.StatusOK, resp.StatusCode) - } - body, err := ioutil.ReadAll(resp.Body) - body = bytes.TrimSpace(body) - if err != nil { - t.Fatalf("could not read the body: %v", err) - } - if string(body) != "URL key does not exist" { - t.Fatalf("received unexpected response: %s", body) - } - }) - - t.Run("invalid url", func(t *testing.T) { - data := url.Values{} - data.Set("URL", "this is definitely not a valid url") - - resp, err := http.Post(server.URL+"/api/v1/create", "application/x-www-form-urlencoded", bytes.NewBufferString(data.Encode())) - if err != nil { - t.Fatalf("could not post to the backend: %v", err) - } - if resp.StatusCode != http.StatusBadRequest { - t.Errorf("expected status %d; got %d", http.StatusOK, resp.StatusCode) - } - body, err := ioutil.ReadAll(resp.Body) - body = bytes.TrimSpace(body) - if err != nil { - t.Fatalf("could not read the body: %v", err) - } - if string(body) != store.ErrNoValidURL.Error() { - t.Fatalf("received unexpected response: %s", body) - } - }) -} - func TestHandleInfo(t *testing.T) { cleanup, err := getBackend() if err != nil { @@ -265,7 +111,7 @@ func TestHandleInfo(t *testing.T) { if err != nil { t.Fatalf("could not marshal json: %v", err) } - respBody := createEntryWithJSON(t, reqBody, "application/json", http.StatusOK) + respBody := createEntryWithJSON(t, reqBody, "application/json; charset=utf-8", http.StatusOK) var parsed URLUtil err = json.Unmarshal(respBody, &parsed) if err != nil { @@ -279,7 +125,7 @@ func TestHandleInfo(t *testing.T) { if err != nil { t.Fatalf("could not marshal the body: %v", err) } - resp, err := http.Post(server.URL+"/api/v1/info", "appplication/json", bytes.NewBuffer(body)) + resp, err := http.Post(server.URL+"/api/v1/info", "application/json; charset=utf-8", bytes.NewBuffer(body)) if err != nil { t.Fatalf("could not post to the backend: %v", err) } @@ -304,11 +150,14 @@ func TestHandleInfo(t *testing.T) { t.Errorf("expected status %d; got %d", http.StatusBadRequest, resp.StatusCode) } body, err := ioutil.ReadAll(resp.Body) - body = bytes.TrimSpace(body) if err != nil { t.Fatalf("could not read the body: %v", err) } - if string(body) != "could not decode JSON: EOF" { + body = bytes.TrimSpace(body) + raw := makeJSON(t, gin.H{ + "error": "Key: '.ID' Error:Field validation for 'ID' failed on the 'required' tag", + }) + if string(body) != raw { t.Fatalf("body is not the expected one: %s", body) } }) @@ -328,12 +177,23 @@ func TestHandleInfo(t *testing.T) { if err != nil { t.Fatalf("could not read the body: %v", err) } - if string(body) != "no ID provided" { + raw := makeJSON(t, gin.H{ + "error": "Key: '.ID' Error:Field validation for 'ID' failed on the 'required' tag", + }) + if string(body) != raw { t.Fatalf("body is not the expected one: %s", body) } }) } +func makeJSON(t *testing.T, data interface{}) string { + raw, err := json.Marshal(data) + if err != nil { + t.Fatalf("could not marshal json: %v", err) + } + return string(raw) +} + func createEntryWithJSON(t *testing.T, reqBody []byte, contentType string, statusCode int) []byte { resp, err := http.Post(server.URL+"/api/v1/create", "application/json", bytes.NewBuffer(reqBody)) if err != nil { @@ -377,6 +237,7 @@ func testRedirect(t *testing.T, shortURL, longURL string) { } func getBackend() (func(), error) { + gin.SetMode(gin.ReleaseMode) store, err := store.New(testingDBName, 4) if err != nil { return nil, errors.Wrap(err, "could not create store") @@ -389,7 +250,7 @@ func getBackend() (func(), error) { server = httptest.NewServer(handler.engine) return func() { server.Close() - handler.Stop() + handler.CloseStore() os.Remove(testingDBName) }, nil }