|
|
|
@ -34,15 +34,11 @@ func newTestApplication(pool database.PgxIface) application {
|
|
|
|
func TestHealthRoute(t *testing.T) {
|
|
|
|
func TestHealthRoute(t *testing.T) {
|
|
|
|
respRec := httptest.NewRecorder()
|
|
|
|
respRec := httptest.NewRecorder()
|
|
|
|
mockPool, err := pgxmock.NewPool()
|
|
|
|
mockPool, err := pgxmock.NewPool()
|
|
|
|
if err != nil {
|
|
|
|
assert.NilError(t, err)
|
|
|
|
t.Fatal(err)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
defer mockPool.Close()
|
|
|
|
defer mockPool.Close()
|
|
|
|
|
|
|
|
|
|
|
|
r, err := http.NewRequest(http.MethodGet, "/v1/healthcheck", nil)
|
|
|
|
r, err := http.NewRequest(http.MethodGet, "/v1/healthcheck", nil)
|
|
|
|
if err != nil {
|
|
|
|
assert.NilError(t, err)
|
|
|
|
t.Fatal(err)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app := newTestApplication(mockPool)
|
|
|
|
app := newTestApplication(mockPool)
|
|
|
|
app.routes().ServeHTTP(respRec, r)
|
|
|
|
app.routes().ServeHTTP(respRec, r)
|
|
|
|
@ -93,9 +89,7 @@ func TestCreateMovieHandler(t *testing.T) {
|
|
|
|
rquestBody, _ := json.Marshal(movie)
|
|
|
|
rquestBody, _ := json.Marshal(movie)
|
|
|
|
|
|
|
|
|
|
|
|
r, err := http.NewRequest(http.MethodPost, "/v1/movies", bytes.NewBuffer(rquestBody))
|
|
|
|
r, err := http.NewRequest(http.MethodPost, "/v1/movies", bytes.NewBuffer(rquestBody))
|
|
|
|
if err != nil {
|
|
|
|
assert.NilError(t, err)
|
|
|
|
t.Fatal(err)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app := newTestApplication(mockPool)
|
|
|
|
app := newTestApplication(mockPool)
|
|
|
|
app.routes().ServeHTTP(respRec, r)
|
|
|
|
app.routes().ServeHTTP(respRec, r)
|
|
|
|
@ -194,9 +188,7 @@ func TestCreateMovieValidatorError(t *testing.T) {
|
|
|
|
t.Run(test.name, func(t *testing.T) {
|
|
|
|
t.Run(test.name, func(t *testing.T) {
|
|
|
|
respRec := httptest.NewRecorder()
|
|
|
|
respRec := httptest.NewRecorder()
|
|
|
|
r, err := http.NewRequest(http.MethodPost, "/v1/movies", test.input)
|
|
|
|
r, err := http.NewRequest(http.MethodPost, "/v1/movies", test.input)
|
|
|
|
if err != nil {
|
|
|
|
assert.NilError(t, err)
|
|
|
|
t.Fatal(err)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app := newTestApplication(mockPool)
|
|
|
|
app := newTestApplication(mockPool)
|
|
|
|
|
|
|
|
|
|
|
|
@ -310,3 +302,216 @@ func TestGetMovieHandlerErrors(t *testing.T) {
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func TestListMovieHandler(t *testing.T) {
|
|
|
|
|
|
|
|
respRec := httptest.NewRecorder()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mockPool, err := pgxmock.NewPool()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
defer mockPool.Close()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
movies := []data.Movie{{
|
|
|
|
|
|
|
|
ID: 1337,
|
|
|
|
|
|
|
|
CreatedAt: time.Now(),
|
|
|
|
|
|
|
|
Title: "Batteries not included",
|
|
|
|
|
|
|
|
Year: 1987,
|
|
|
|
|
|
|
|
Runtime: data.Runtime(120),
|
|
|
|
|
|
|
|
Genres: []string{"family", "comedy"},
|
|
|
|
|
|
|
|
Version: 1,
|
|
|
|
|
|
|
|
}, {
|
|
|
|
|
|
|
|
ID: 1338,
|
|
|
|
|
|
|
|
CreatedAt: time.Now(),
|
|
|
|
|
|
|
|
Title: "The Boy and the Heron",
|
|
|
|
|
|
|
|
Year: 2023,
|
|
|
|
|
|
|
|
Runtime: data.Runtime(140),
|
|
|
|
|
|
|
|
Genres: []string{"animation", "drama"},
|
|
|
|
|
|
|
|
Version: 1,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rows := pgxmock.NewRows([]string{"id", "created_at", "title", "year", "runtime", "genres", "version"})
|
|
|
|
|
|
|
|
for _, m := range movies {
|
|
|
|
|
|
|
|
rows.AddRow(m.ID, m.CreatedAt, m.Title, m.Year, m.Runtime, m.Genres, m.Version)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mockPool.ExpectQuery(`SELECT id, created_at, title, year, runtime, genres, version
|
|
|
|
|
|
|
|
FROM movies
|
|
|
|
|
|
|
|
ORDER BY id ASC`).WillReturnRows(rows)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
r, err := http.NewRequest(http.MethodGet, "/v1/movies", nil)
|
|
|
|
|
|
|
|
assert.NilError(t, err)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app := newTestApplication(mockPool)
|
|
|
|
|
|
|
|
app.routes().ServeHTTP(respRec, r)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
resp := respRec.Result()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
|
|
|
|
|
|
t.Fatalf("Got status code %d, wanted status code %d", resp.StatusCode, http.StatusOK)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
|
|
|
|
|
|
assert.NilError(t, err)
|
|
|
|
|
|
|
|
jsonContent := make(map[string][]data.Movie)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
err = json.Unmarshal(body, &jsonContent)
|
|
|
|
|
|
|
|
assert.NilError(t, err)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assert.Equal(t, len(jsonContent["movies"]), 2)
|
|
|
|
|
|
|
|
for i := range jsonContent["movies"] {
|
|
|
|
|
|
|
|
assert.MovieEqual(t, jsonContent["movies"][i], movies[i])
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
err = mockPool.ExpectationsWereMet()
|
|
|
|
|
|
|
|
assert.NilError(t, err)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func TestListHandlerServerError(t *testing.T) {
|
|
|
|
|
|
|
|
respRec := httptest.NewRecorder()
|
|
|
|
|
|
|
|
mockPool, err := pgxmock.NewPool()
|
|
|
|
|
|
|
|
assert.NilError(t, err)
|
|
|
|
|
|
|
|
getAllReturnColumns := []string{"id", "created_at", "title", "year", "runtime", "genres", "version"}
|
|
|
|
|
|
|
|
errorRows := pgxmock.NewRows(getAllReturnColumns).AddRow(1, time.Now(), "will error", 2026, 120, []string{}, 1).RowError(0, fmt.Errorf("network connection lost"))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mockPool.ExpectQuery("SELECT").WillReturnRows(errorRows)
|
|
|
|
|
|
|
|
r, err := http.NewRequest(http.MethodGet, "/v1/movies", nil)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app := newTestApplication(mockPool)
|
|
|
|
|
|
|
|
app.routes().ServeHTTP(respRec, r)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
resp := respRec.Result()
|
|
|
|
|
|
|
|
assert.Equal(t, resp.StatusCode, http.StatusInternalServerError)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func TestListHandlerValidation(t *testing.T) {
|
|
|
|
|
|
|
|
mockPool, err := pgxmock.NewPool()
|
|
|
|
|
|
|
|
assert.NilError(t, err)
|
|
|
|
|
|
|
|
defer mockPool.Close()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
getAllReturnColumns := []string{"id", "created_at", "title", "year", "runtime", "genres", "version"}
|
|
|
|
|
|
|
|
getAllQuery := `
|
|
|
|
|
|
|
|
SELECT id, created_at, title, year, runtime, genres, version
|
|
|
|
|
|
|
|
FROM movies
|
|
|
|
|
|
|
|
ORDER BY id ASC
|
|
|
|
|
|
|
|
`
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
testTable := []struct {
|
|
|
|
|
|
|
|
name string
|
|
|
|
|
|
|
|
query string
|
|
|
|
|
|
|
|
wantCode int
|
|
|
|
|
|
|
|
}{
|
|
|
|
|
|
|
|
// invalid requests
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
name: "bad sort key",
|
|
|
|
|
|
|
|
query: "/v1/movies?sort=dirp",
|
|
|
|
|
|
|
|
wantCode: http.StatusUnprocessableEntity,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
name: "bad page value",
|
|
|
|
|
|
|
|
query: "/v1/movies?page=shouldbeint",
|
|
|
|
|
|
|
|
wantCode: http.StatusUnprocessableEntity,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
name: "bad page_size value",
|
|
|
|
|
|
|
|
query: "/v1/movies?page_size=shouldbeint",
|
|
|
|
|
|
|
|
wantCode: http.StatusUnprocessableEntity,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
name: "bad page too large",
|
|
|
|
|
|
|
|
query: "/v1/movies?page=1000000000",
|
|
|
|
|
|
|
|
wantCode: http.StatusUnprocessableEntity,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
name: "bad page_size too large",
|
|
|
|
|
|
|
|
query: "/v1/movies?page_size=1000",
|
|
|
|
|
|
|
|
wantCode: http.StatusUnprocessableEntity,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
// valid requests
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
name: "no query params",
|
|
|
|
|
|
|
|
query: "/v1/movies",
|
|
|
|
|
|
|
|
wantCode: http.StatusOK,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
name: "valid page_size",
|
|
|
|
|
|
|
|
query: "/v1/movies?page_size=50",
|
|
|
|
|
|
|
|
wantCode: http.StatusOK,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
name: "valid page",
|
|
|
|
|
|
|
|
query: "/v1/movies?page=10",
|
|
|
|
|
|
|
|
wantCode: http.StatusOK,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
name: "sort by id ascending",
|
|
|
|
|
|
|
|
query: "/v1/movies?sort=id",
|
|
|
|
|
|
|
|
wantCode: http.StatusOK,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
name: "sort by id ascending",
|
|
|
|
|
|
|
|
query: "/v1/movies?sort=-id",
|
|
|
|
|
|
|
|
wantCode: http.StatusOK,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
name: "sort by year ascending",
|
|
|
|
|
|
|
|
query: "/v1/movies?sort=year",
|
|
|
|
|
|
|
|
wantCode: http.StatusOK,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
name: "sort by year descending",
|
|
|
|
|
|
|
|
query: "/v1/movies?sort=-year",
|
|
|
|
|
|
|
|
wantCode: http.StatusOK,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
name: "sort by runtime ascending",
|
|
|
|
|
|
|
|
query: "/v1/movies?sort=runtime",
|
|
|
|
|
|
|
|
wantCode: http.StatusOK,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
name: "sort by runtime descending",
|
|
|
|
|
|
|
|
query: "/v1/movies?sort=-runtime",
|
|
|
|
|
|
|
|
wantCode: http.StatusOK,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
name: "sort by title ascending",
|
|
|
|
|
|
|
|
query: "/v1/movies?sort=title",
|
|
|
|
|
|
|
|
wantCode: http.StatusOK,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
name: "sort by title descending",
|
|
|
|
|
|
|
|
query: "/v1/movies?sort=-title",
|
|
|
|
|
|
|
|
wantCode: http.StatusOK,
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app := newTestApplication(mockPool)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for _, test := range testTable {
|
|
|
|
|
|
|
|
t.Run(test.name, func(t *testing.T) {
|
|
|
|
|
|
|
|
respRec := httptest.NewRecorder()
|
|
|
|
|
|
|
|
mockPool, err := pgxmock.NewPool()
|
|
|
|
|
|
|
|
assert.NilError(t, err)
|
|
|
|
|
|
|
|
// assert if DB is expected to be hit
|
|
|
|
|
|
|
|
if test.wantCode == http.StatusOK {
|
|
|
|
|
|
|
|
// TODO expand return values and parameterize
|
|
|
|
|
|
|
|
// empty return is fine for validator test
|
|
|
|
|
|
|
|
mockPool.ExpectQuery(getAllQuery).WillReturnRows(mockPool.NewRows(getAllReturnColumns))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
r, err := http.NewRequest(http.MethodGet, test.query, nil)
|
|
|
|
|
|
|
|
assert.NilError(t, err)
|
|
|
|
|
|
|
|
app = newTestApplication(mockPool)
|
|
|
|
|
|
|
|
app.routes().ServeHTTP(respRec, r)
|
|
|
|
|
|
|
|
resp := respRec.Result()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assert.Equal(t, resp.StatusCode, test.wantCode)
|
|
|
|
|
|
|
|
err = mockPool.ExpectationsWereMet()
|
|
|
|
|
|
|
|
assert.NilError(t, err)
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|