|
|
@ -4,7 +4,6 @@ import ( |
|
|
"bytes" |
|
|
"bytes" |
|
|
"encoding/json" |
|
|
"encoding/json" |
|
|
"io/ioutil" |
|
|
"io/ioutil" |
|
|
"mime/multipart" |
|
|
|
|
|
"net/http" |
|
|
"net/http" |
|
|
"net/http/httptest" |
|
|
"net/http/httptest" |
|
|
"net/url" |
|
|
"net/url" |
|
|
@ -12,6 +11,7 @@ import ( |
|
|
"strings" |
|
|
"strings" |
|
|
"testing" |
|
|
"testing" |
|
|
|
|
|
|
|
|
|
|
|
"github.com/gin-gonic/gin" |
|
|
"github.com/maxibanki/golang-url-shortener/store" |
|
|
"github.com/maxibanki/golang-url-shortener/store" |
|
|
"github.com/pkg/errors" |
|
|
"github.com/pkg/errors" |
|
|
) |
|
|
) |
|
|
@ -23,18 +23,18 @@ const ( |
|
|
|
|
|
|
|
|
var server *httptest.Server |
|
|
var server *httptest.Server |
|
|
|
|
|
|
|
|
func TestCreateEntryJSON(t *testing.T) { |
|
|
func TestCreateEntry(t *testing.T) { |
|
|
tt := []struct { |
|
|
tt := []struct { |
|
|
name string |
|
|
name string |
|
|
ignoreResponse bool |
|
|
ignoreResponse bool |
|
|
contentType string |
|
|
contentType string |
|
|
response string |
|
|
response gin.H |
|
|
requestBody URLUtil |
|
|
requestBody URLUtil |
|
|
statusCode int |
|
|
statusCode int |
|
|
}{ |
|
|
}{ |
|
|
{ |
|
|
{ |
|
|
name: "body is nil", |
|
|
name: "body is nil", |
|
|
response: "could not decode JSON: EOF", |
|
|
response: gin.H{"error": "EOF"}, |
|
|
statusCode: http.StatusBadRequest, |
|
|
statusCode: http.StatusBadRequest, |
|
|
contentType: "application/json; charset=utf-8", |
|
|
contentType: "application/json; charset=utf-8", |
|
|
ignoreResponse: true, |
|
|
ignoreResponse: true, |
|
|
@ -54,7 +54,7 @@ func TestCreateEntryJSON(t *testing.T) { |
|
|
}, |
|
|
}, |
|
|
statusCode: http.StatusBadRequest, |
|
|
statusCode: http.StatusBadRequest, |
|
|
contentType: "application/json; charset=utf-8", |
|
|
contentType: "application/json; charset=utf-8", |
|
|
response: store.ErrNoValidURL.Error(), |
|
|
response: gin.H{"error": store.ErrNoValidURL.Error()}, |
|
|
ignoreResponse: true, |
|
|
ignoreResponse: true, |
|
|
}, |
|
|
}, |
|
|
} |
|
|
} |
|
|
@ -76,8 +76,9 @@ func TestCreateEntryJSON(t *testing.T) { |
|
|
reqBody = nil |
|
|
reqBody = nil |
|
|
} |
|
|
} |
|
|
respBody := createEntryWithJSON(t, reqBody, tc.contentType, tc.statusCode) |
|
|
respBody := createEntryWithJSON(t, reqBody, tc.contentType, tc.statusCode) |
|
|
if tc.response != "" { |
|
|
if len(tc.response) > 0 { |
|
|
if string(respBody) != string(tc.response) { |
|
|
raw := makeJSON(t, tc.response) |
|
|
|
|
|
if string(respBody) != raw { |
|
|
t.Fatalf("expected body: %s; got: %s", tc.response, respBody) |
|
|
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) { |
|
|
func TestHandleInfo(t *testing.T) { |
|
|
cleanup, err := getBackend() |
|
|
cleanup, err := getBackend() |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
@ -265,7 +111,7 @@ func TestHandleInfo(t *testing.T) { |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
t.Fatalf("could not marshal json: %v", err) |
|
|
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 |
|
|
var parsed URLUtil |
|
|
err = json.Unmarshal(respBody, &parsed) |
|
|
err = json.Unmarshal(respBody, &parsed) |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
@ -279,7 +125,7 @@ func TestHandleInfo(t *testing.T) { |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
t.Fatalf("could not marshal the body: %v", err) |
|
|
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 { |
|
|
if err != nil { |
|
|
t.Fatalf("could not post to the backend: %v", err) |
|
|
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) |
|
|
t.Errorf("expected status %d; got %d", http.StatusBadRequest, resp.StatusCode) |
|
|
} |
|
|
} |
|
|
body, err := ioutil.ReadAll(resp.Body) |
|
|
body, err := ioutil.ReadAll(resp.Body) |
|
|
body = bytes.TrimSpace(body) |
|
|
|
|
|
if err != nil { |
|
|
if err != nil { |
|
|
t.Fatalf("could not read the body: %v", err) |
|
|
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) |
|
|
t.Fatalf("body is not the expected one: %s", body) |
|
|
} |
|
|
} |
|
|
}) |
|
|
}) |
|
|
@ -328,12 +177,23 @@ func TestHandleInfo(t *testing.T) { |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
t.Fatalf("could not read the body: %v", err) |
|
|
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) |
|
|
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 { |
|
|
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)) |
|
|
resp, err := http.Post(server.URL+"/api/v1/create", "application/json", bytes.NewBuffer(reqBody)) |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
@ -377,6 +237,7 @@ func testRedirect(t *testing.T, shortURL, longURL string) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func getBackend() (func(), error) { |
|
|
func getBackend() (func(), error) { |
|
|
|
|
|
gin.SetMode(gin.ReleaseMode) |
|
|
store, err := store.New(testingDBName, 4) |
|
|
store, err := store.New(testingDBName, 4) |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
return nil, errors.Wrap(err, "could not create store") |
|
|
return nil, errors.Wrap(err, "could not create store") |
|
|
@ -389,7 +250,7 @@ func getBackend() (func(), error) { |
|
|
server = httptest.NewServer(handler.engine) |
|
|
server = httptest.NewServer(handler.engine) |
|
|
return func() { |
|
|
return func() { |
|
|
server.Close() |
|
|
server.Close() |
|
|
handler.Stop() |
|
|
handler.CloseStore() |
|
|
os.Remove(testingDBName) |
|
|
os.Remove(testingDBName) |
|
|
}, nil |
|
|
}, nil |
|
|
} |
|
|
} |
|
|
|