648 lines
16 KiB
Go
648 lines
16 KiB
Go
package handlers
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"go-udemy-web-1/internal/models"
|
|
"go-udemy-web-1/internal/repository"
|
|
"log"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
type postData struct {
|
|
key string
|
|
value string
|
|
}
|
|
|
|
// {{{ Test NewRepo
|
|
|
|
func Test_NewRepo(t *testing.T) {
|
|
var db repository.DB
|
|
repo := NewRepo(&app, &db)
|
|
|
|
if reflect.TypeOf(repo).String() != "*handlers.Repository" {
|
|
t.Errorf("repo is of type %s instead of *handlers.Repository", reflect.TypeOf(repo).String())
|
|
}
|
|
}
|
|
|
|
// }}}
|
|
// {{{ Simple get tests
|
|
|
|
var theTests = []struct {
|
|
name string
|
|
url string
|
|
method string
|
|
expectedStatusCode int
|
|
}{
|
|
{"home", "/", "GET", http.StatusOK},
|
|
{"about", "/about", "GET", http.StatusOK},
|
|
{"gq", "/generals-quarters", "GET", http.StatusOK},
|
|
{"ms", "/majors-suite", "GET", http.StatusOK},
|
|
{"sa", "/availability", "GET", http.StatusOK},
|
|
{"contact", "/contact", "GET", http.StatusOK},
|
|
{"non-existant", "/some/link", "GET", http.StatusNotFound},
|
|
{"login", "/user/login", "GET", http.StatusOK},
|
|
{"logout", "/user/logout", "GET", http.StatusOK},
|
|
{"dashboard", "/admin/dashboard", "GET", http.StatusOK},
|
|
{"all reservations", "/admin/reservations-all", "GET", http.StatusOK},
|
|
{"new reservations", "/admin/reservations-new", "GET", http.StatusOK},
|
|
{"show reservation", "/admin/reservations/new/1/show", "GET", http.StatusOK},
|
|
}
|
|
|
|
func TestHandlers(t *testing.T) {
|
|
routes := getRoutes()
|
|
ts := httptest.NewTLSServer(routes)
|
|
defer ts.Close()
|
|
|
|
for _, e := range theTests {
|
|
resp, err := ts.Client().Get(ts.URL + e.url)
|
|
if err != nil {
|
|
t.Log(err)
|
|
t.Fatal(err)
|
|
}
|
|
if resp.StatusCode != e.expectedStatusCode {
|
|
t.Errorf("for %s, expected %d but got %d\n", e.name, e.expectedStatusCode, resp.StatusCode)
|
|
}
|
|
}
|
|
}
|
|
|
|
// }}}
|
|
// {{{ Make Reservation Tests
|
|
|
|
var makeReservationTests = []struct {
|
|
name string
|
|
roomID int
|
|
expectedStatusCode int
|
|
}{
|
|
{"ok", 1, http.StatusOK},
|
|
{"no session", 0, http.StatusSeeOther},
|
|
{"non-existant room", 100, http.StatusSeeOther},
|
|
}
|
|
|
|
func TestRepository_MakeReservation(t *testing.T) {
|
|
for _, test := range makeReservationTests {
|
|
reservation := models.Reservation{
|
|
RoomID: test.roomID,
|
|
Room: models.Room{
|
|
ID: test.roomID,
|
|
RoomName: "Room name",
|
|
},
|
|
}
|
|
req, _ := http.NewRequest("GET", "/make-reservation", nil)
|
|
ctx := getCtx(req)
|
|
req = req.WithContext(ctx)
|
|
rr := httptest.NewRecorder()
|
|
if test.roomID == 0 {
|
|
session.Put(ctx, "reservation", nil)
|
|
} else {
|
|
session.Put(ctx, "reservation", reservation)
|
|
}
|
|
handler := http.HandlerFunc(Repo.MakeReservation)
|
|
|
|
handler.ServeHTTP(rr, req)
|
|
|
|
if rr.Code != test.expectedStatusCode {
|
|
t.Errorf("for %s, reservation handler returned response code: got %d, wanted %d\n",
|
|
test.name, rr.Code, http.StatusOK)
|
|
}
|
|
}
|
|
}
|
|
|
|
// }}}
|
|
// {{{ PostMakeReservation tests
|
|
|
|
var postMakeReservationTests = []struct {
|
|
name string
|
|
reservationInfo []postData
|
|
|
|
expectedStatusCode int
|
|
}{
|
|
{
|
|
"ok",
|
|
[]postData{
|
|
{key: "first_name", value: "John"},
|
|
{key: "last_name", value: "Smith"},
|
|
{key: "email", value: "john@smith.com"},
|
|
{key: "phone", value: "1234"},
|
|
{key: "room_id", value: "1"},
|
|
{key: "start_date", value: "2050-01-01"},
|
|
{key: "end_date", value: "2050-01-02"},
|
|
},
|
|
http.StatusSeeOther,
|
|
},
|
|
{
|
|
"no_session",
|
|
[]postData{
|
|
{key: "first_name", value: "John"},
|
|
{key: "last_name", value: "Smith"},
|
|
{key: "email", value: "john@smith.com"},
|
|
{key: "phone", value: "1234"},
|
|
{key: "room_id", value: "0"},
|
|
{key: "start_date", value: "2050-01-01"},
|
|
{key: "end_date", value: "2050-01-02"},
|
|
},
|
|
http.StatusSeeOther,
|
|
},
|
|
{"no_post_data", []postData{}, http.StatusSeeOther},
|
|
{
|
|
"missing first name",
|
|
[]postData{
|
|
{key: "last_name", value: "Smith"},
|
|
{key: "email", value: "john@smith.com"},
|
|
{key: "phone", value: "1234"},
|
|
{key: "room_id", value: "1"},
|
|
{key: "start_date", value: "2050-01-01"},
|
|
{key: "end_date", value: "2050-01-02"},
|
|
},
|
|
http.StatusOK,
|
|
},
|
|
{
|
|
"wrong first name",
|
|
[]postData{
|
|
{key: "first_name", value: "J"},
|
|
{key: "last_name", value: "Smith"},
|
|
{key: "email", value: "john@smith.com"},
|
|
{key: "phone", value: "1234"},
|
|
{key: "room_id", value: "1"},
|
|
{key: "start_date", value: "2050-01-01"},
|
|
{key: "end_date", value: "2050-01-02"},
|
|
},
|
|
http.StatusOK,
|
|
},
|
|
{
|
|
"wrong email",
|
|
[]postData{
|
|
{key: "first_name", value: "John"},
|
|
{key: "last_name", value: "Smith"},
|
|
{key: "email", value: "john@smith"},
|
|
{key: "phone", value: "1234"},
|
|
{key: "room_id", value: "1"},
|
|
{key: "start_date", value: "2050-01-01"},
|
|
{key: "end_date", value: "2050-01-02"},
|
|
},
|
|
http.StatusOK,
|
|
},
|
|
{
|
|
"insert reservation error",
|
|
[]postData{
|
|
{key: "first_name", value: "John"},
|
|
{key: "last_name", value: "Smith"},
|
|
{key: "email", value: "john@smith.com"},
|
|
{key: "phone", value: "1234"},
|
|
{key: "room_id", value: "2"},
|
|
{key: "start_date", value: "2050-01-01"},
|
|
{key: "end_date", value: "2050-01-02"},
|
|
},
|
|
http.StatusSeeOther,
|
|
},
|
|
{
|
|
"insert room restriction error",
|
|
[]postData{
|
|
{key: "first_name", value: "John"},
|
|
{key: "last_name", value: "Smith"},
|
|
{key: "email", value: "john@smith.com"},
|
|
{key: "phone", value: "1234"},
|
|
{key: "room_id", value: "100"},
|
|
{key: "start_date", value: "2050-01-01"},
|
|
{key: "end_date", value: "2050-01-02"},
|
|
},
|
|
http.StatusSeeOther,
|
|
},
|
|
}
|
|
|
|
func TestRepository_PostMakeReservation(t *testing.T) {
|
|
for _, test := range postMakeReservationTests {
|
|
roomID := 1
|
|
reqBody := url.Values{}
|
|
var req *http.Request
|
|
if len(test.reservationInfo) > 0 {
|
|
for _, element := range test.reservationInfo {
|
|
reqBody.Add(element.key, element.value)
|
|
if element.key == "room_id" {
|
|
roomID, _ = strconv.Atoi(element.value)
|
|
}
|
|
}
|
|
req, _ = http.NewRequest("POST", "/make-reservation", strings.NewReader(reqBody.Encode()))
|
|
} else {
|
|
req, _ = http.NewRequest("POST", "/make-reservation", nil)
|
|
}
|
|
layout := "2006-01-02"
|
|
sd, _ := time.Parse(layout, "2050-01-01")
|
|
ed, _ := time.Parse(layout, "2050-01-02")
|
|
reservation := models.Reservation{
|
|
RoomID: roomID,
|
|
StartDate: sd,
|
|
EndDate: ed,
|
|
}
|
|
|
|
ctx := getCtx(req)
|
|
req = req.WithContext(ctx)
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
|
|
rr := httptest.NewRecorder()
|
|
if roomID == 0 {
|
|
session.Put(ctx, "reservation", nil)
|
|
} else {
|
|
session.Put(ctx, "reservation", reservation)
|
|
}
|
|
|
|
handler := http.HandlerFunc(Repo.PostMakeReservation)
|
|
handler.ServeHTTP(rr, req)
|
|
|
|
if rr.Code != test.expectedStatusCode {
|
|
t.Errorf("for %s, reservation handler returned response code: got %d, wanted %d\n",
|
|
test.name, rr.Code, test.expectedStatusCode)
|
|
}
|
|
}
|
|
}
|
|
|
|
// }}}
|
|
// {{{ Test AvailabilityJSON
|
|
|
|
var availabilityJSONTests = []struct {
|
|
name string
|
|
queryInfo []postData
|
|
expectedStatusJSON jsonResponse
|
|
}{
|
|
{"ok", []postData{
|
|
{key: "start", value: "2050-01-01"},
|
|
{key: "end", value: "2050-01-02"},
|
|
{key: "room_id", value: "1"},
|
|
}, jsonResponse{
|
|
OK: true,
|
|
Message: "",
|
|
StartDate: "2050-01-01",
|
|
EndDate: "2050-01-02",
|
|
RoomID: "1",
|
|
}},
|
|
{"not available", []postData{
|
|
{key: "start", value: "2050-01-01"},
|
|
{key: "end", value: "2050-01-02"},
|
|
{key: "room_id", value: "2"},
|
|
}, jsonResponse{
|
|
OK: false,
|
|
Message: "",
|
|
StartDate: "2050-01-01",
|
|
EndDate: "2050-01-02",
|
|
RoomID: "2",
|
|
}},
|
|
{"wrong start date", []postData{
|
|
{key: "start", value: "2050-01"},
|
|
{key: "end", value: "2050-01-02"},
|
|
{key: "room_id", value: "1"},
|
|
}, jsonResponse{
|
|
OK: false,
|
|
Message: "Wrong startDate",
|
|
}},
|
|
{"wrong end date", []postData{
|
|
{key: "start", value: "2050-01-01"},
|
|
{key: "end", value: "wrong"},
|
|
{key: "room_id", value: "1"},
|
|
}, jsonResponse{
|
|
OK: false,
|
|
Message: "Wrong endDate",
|
|
}},
|
|
{"wrong room id", []postData{
|
|
{key: "start", value: "2050-01-01"},
|
|
{key: "end", value: "2050-01-02"},
|
|
{key: "room_id", value: "x"},
|
|
}, jsonResponse{
|
|
OK: false,
|
|
Message: "Wrong roomID",
|
|
}},
|
|
{"not available", []postData{
|
|
{key: "start", value: "2050-01-01"},
|
|
{key: "end", value: "2050-01-02"},
|
|
{key: "room_id", value: "100"},
|
|
}, jsonResponse{
|
|
OK: false,
|
|
Message: "Error connecting to database",
|
|
}},
|
|
{"no form", []postData{}, jsonResponse{
|
|
OK: false,
|
|
Message: "Internal server error",
|
|
}},
|
|
}
|
|
|
|
func Test_AvailabilityJSON(t *testing.T) {
|
|
for _, test := range availabilityJSONTests {
|
|
var req *http.Request
|
|
reqBody := url.Values{}
|
|
if len(test.queryInfo) > 0 {
|
|
for _, element := range test.queryInfo {
|
|
reqBody.Add(element.key, element.value)
|
|
}
|
|
|
|
req, _ = http.NewRequest("POST", "/make-reservation", strings.NewReader(reqBody.Encode()))
|
|
} else {
|
|
req, _ = http.NewRequest("POST", "/make-reservation", nil)
|
|
}
|
|
ctx := getCtx(req)
|
|
req = req.WithContext(ctx)
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
|
|
rr := httptest.NewRecorder()
|
|
|
|
handler := http.HandlerFunc(Repo.AvailabilityJSON)
|
|
handler.ServeHTTP(rr, req)
|
|
|
|
if rr.Code != http.StatusOK {
|
|
t.Errorf("for %s, reservation handler returned response code: got %d, wanted %d\n",
|
|
test.name, rr.Code, http.StatusOK)
|
|
}
|
|
|
|
var j jsonResponse
|
|
err := json.Unmarshal(rr.Body.Bytes(), &j)
|
|
if err != nil {
|
|
t.Errorf("for %s, failed to parse json", test.name)
|
|
}
|
|
if j != test.expectedStatusJSON {
|
|
expected, _ := json.MarshalIndent(test.expectedStatusJSON, "", " ")
|
|
t.Errorf("for %s, returned json is wrong, expected: %s, returned: %s",
|
|
test.name, expected, rr.Body.String())
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// }}}
|
|
// {{{ Test ReservationSummary
|
|
|
|
var reservationSummaryTests = []struct {
|
|
name string
|
|
haveSession bool
|
|
expectedStatusCode int
|
|
}{
|
|
{"ok", true, http.StatusOK},
|
|
{"nok", false, http.StatusSeeOther},
|
|
}
|
|
|
|
func Test_ReservationSummary(t *testing.T) {
|
|
for _, test := range reservationSummaryTests {
|
|
req, _ := http.NewRequest("GET", "/reservation-summary", nil)
|
|
ctx := getCtx(req)
|
|
req = req.WithContext(ctx)
|
|
|
|
rr := httptest.NewRecorder()
|
|
|
|
layout := "2006-01-02"
|
|
sd, _ := time.Parse(layout, "2050-01-01")
|
|
ed, _ := time.Parse(layout, "2050-01-02")
|
|
reservation := models.Reservation{
|
|
StartDate: sd,
|
|
EndDate: ed,
|
|
FirstName: "John",
|
|
LastName: "Smith",
|
|
Email: "john@smith.com",
|
|
RoomID: 1,
|
|
}
|
|
|
|
if test.haveSession {
|
|
session.Put(req.Context(), "reservation", reservation)
|
|
}
|
|
|
|
handler := http.HandlerFunc(Repo.ReservationSummary)
|
|
handler.ServeHTTP(rr, req)
|
|
|
|
if rr.Code != test.expectedStatusCode {
|
|
t.Errorf("for %s, reservation handler returned response code: got %d, wanted %d\n",
|
|
test.name, rr.Code, test.expectedStatusCode)
|
|
}
|
|
}
|
|
}
|
|
|
|
// }}}
|
|
// {{{ Test PostAvailability
|
|
|
|
var postAvailabilityTests = []struct {
|
|
name string
|
|
queryInfo []postData
|
|
expectedStatusCode int
|
|
}{
|
|
{"ok", []postData{
|
|
{key: "start", value: "2050-01-01"},
|
|
{key: "end", value: "2050-01-02"},
|
|
}, http.StatusOK},
|
|
{"database error", []postData{
|
|
{key: "start", value: "2050-01-03"},
|
|
{key: "end", value: "2050-01-04"},
|
|
}, http.StatusSeeOther},
|
|
{"no availability", []postData{
|
|
{key: "start", value: "2050-01-05"},
|
|
{key: "end", value: "2050-01-06"},
|
|
}, http.StatusSeeOther},
|
|
{"wrong start date", []postData{
|
|
{key: "start", value: "2050-05"},
|
|
{key: "end", value: "2050-01-06"},
|
|
}, http.StatusSeeOther},
|
|
{"wrong end date", []postData{
|
|
{key: "start", value: "2050-01-05"},
|
|
{key: "end", value: "01-06"},
|
|
}, http.StatusSeeOther},
|
|
{"wrong end date", []postData{}, http.StatusSeeOther},
|
|
}
|
|
|
|
func Test_PostAvailability(t *testing.T) {
|
|
for _, test := range postAvailabilityTests {
|
|
var req *http.Request
|
|
if len(test.queryInfo) > 0 {
|
|
reqBody := url.Values{}
|
|
for _, element := range test.queryInfo {
|
|
reqBody.Add(element.key, element.value)
|
|
}
|
|
|
|
req, _ = http.NewRequest("POST", "/availability", strings.NewReader(reqBody.Encode()))
|
|
} else {
|
|
req, _ = http.NewRequest("POST", "/availability", nil)
|
|
}
|
|
ctx := getCtx(req)
|
|
req = req.WithContext(ctx)
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
|
|
rr := httptest.NewRecorder()
|
|
|
|
handler := http.HandlerFunc(Repo.PostAvailability)
|
|
handler.ServeHTTP(rr, req)
|
|
|
|
if rr.Code != test.expectedStatusCode {
|
|
t.Errorf("for %s, reservation handler returned response code: got %d, wanted %d\n",
|
|
test.name, rr.Code, test.expectedStatusCode)
|
|
}
|
|
}
|
|
}
|
|
|
|
// }}}
|
|
// {{{ Test ChooseRoom
|
|
|
|
var chooseRoomTests = []struct {
|
|
name string
|
|
request string
|
|
haveSession bool
|
|
expectedStatusCode int
|
|
}{
|
|
{"ok", "/choose-room/1", true, http.StatusSeeOther},
|
|
{"wrong room id", "/choose-room/1wrong", true, http.StatusSeeOther},
|
|
{"no session", "/choose-room/1", false, http.StatusSeeOther},
|
|
}
|
|
|
|
func Test_ChooseRoom(t *testing.T) {
|
|
for _, test := range chooseRoomTests {
|
|
req, _ := http.NewRequest("GET", test.request, nil)
|
|
ctx := getCtx(req)
|
|
req = req.WithContext(ctx)
|
|
|
|
layout := "2006-01-02"
|
|
sd, _ := time.Parse(layout, "2050-01-01")
|
|
ed, _ := time.Parse(layout, "2050-01-02")
|
|
reservation := models.Reservation{
|
|
StartDate: sd,
|
|
EndDate: ed,
|
|
}
|
|
|
|
if test.haveSession {
|
|
session.Put(req.Context(), "reservation", reservation)
|
|
}
|
|
rr := httptest.NewRecorder()
|
|
|
|
handler := http.HandlerFunc(Repo.ChooseRoom)
|
|
handler.ServeHTTP(rr, req)
|
|
|
|
if rr.Code != test.expectedStatusCode {
|
|
t.Errorf("for %s, reservation handler returned response code: got %d, wanted %d\n",
|
|
test.name, rr.Code, test.expectedStatusCode)
|
|
}
|
|
}
|
|
}
|
|
|
|
// }}}
|
|
// {{{ Test BookRoom
|
|
|
|
var bookRoomTests = []struct {
|
|
name string
|
|
queryInfo []postData
|
|
expectedStatusCode int
|
|
}{
|
|
{"ok", []postData{
|
|
{key: "id", value: "1"},
|
|
{key: "s", value: "2050-01-01"},
|
|
{key: "e", value: "2050-01-02"},
|
|
}, http.StatusSeeOther},
|
|
{"wrong start date", []postData{
|
|
{key: "id", value: "1"},
|
|
{key: "s", value: "20-01-01"},
|
|
{key: "e", value: "2050-01-02"},
|
|
}, http.StatusSeeOther},
|
|
{"wrong end date", []postData{
|
|
{key: "id", value: "1"},
|
|
{key: "s", value: "2050-01-01"},
|
|
{key: "e", value: "2050-0-02"},
|
|
}, http.StatusSeeOther},
|
|
{"wrong room id", []postData{
|
|
{key: "id", value: "w"},
|
|
{key: "s", value: "2050-01-01"},
|
|
{key: "e", value: "2050-01-02"},
|
|
}, http.StatusSeeOther},
|
|
}
|
|
|
|
func Test_BookRoom(t *testing.T) {
|
|
for _, test := range bookRoomTests {
|
|
request := "/book-room?"
|
|
for _, element := range test.queryInfo {
|
|
request += element.key + "=" + element.value + "&"
|
|
}
|
|
request = request[:len(request)-1]
|
|
req, _ := http.NewRequest("GET", request, nil)
|
|
ctx := getCtx(req)
|
|
req = req.WithContext(ctx)
|
|
|
|
rr := httptest.NewRecorder()
|
|
|
|
handler := http.HandlerFunc(Repo.BookRoom)
|
|
handler.ServeHTTP(rr, req)
|
|
|
|
if rr.Code != test.expectedStatusCode {
|
|
t.Errorf("for %s, reservation handler returned response code: got %d, wanted %d\n",
|
|
test.name, rr.Code, test.expectedStatusCode)
|
|
}
|
|
}
|
|
}
|
|
|
|
// }}}
|
|
// {{{ Login
|
|
|
|
var loginTests = []struct {
|
|
name string
|
|
email string
|
|
expectedStatusCode int
|
|
expectedHTML string
|
|
expectedLocation string
|
|
}{
|
|
{"valid-credentials", "a@b.c", http.StatusSeeOther, "", "/admin/dashboard"},
|
|
{"invalid-credentials", "invalid@b.c", http.StatusSeeOther, "", "/user/login"},
|
|
{"invalid-data", "a@b", http.StatusOK, `action="/user/login"`, ""},
|
|
}
|
|
|
|
func TestLogin(t *testing.T) {
|
|
// range through all tests
|
|
for _, e := range loginTests {
|
|
postedData := url.Values{}
|
|
postedData.Add("email", e.email)
|
|
postedData.Add("password", "password")
|
|
|
|
// create request
|
|
req, _ := http.NewRequest("POST", "/user/login", strings.NewReader(postedData.Encode()))
|
|
ctx := getCtx(req)
|
|
req = req.WithContext(ctx)
|
|
|
|
// set the header
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
rr := httptest.NewRecorder()
|
|
|
|
// call the handler
|
|
handler := http.HandlerFunc(Repo.PostShowLogin)
|
|
handler.ServeHTTP(rr, req)
|
|
|
|
if rr.Code != e.expectedStatusCode {
|
|
t.Errorf("failed %s: expected code %d, but got %d", e.name, e.expectedStatusCode, rr.Code)
|
|
}
|
|
|
|
if e.expectedLocation != "" {
|
|
// get the URL from test
|
|
actualLoc, _ := rr.Result().Location()
|
|
if actualLoc.String() != e.expectedLocation {
|
|
t.Errorf("failed %s: expected location %s, but got location %s", e.name, e.expectedLocation, actualLoc.String())
|
|
}
|
|
}
|
|
|
|
// checking for expected values in HTML
|
|
if e.expectedHTML != "" {
|
|
// read the response body into a string
|
|
html := rr.Body.String()
|
|
if !strings.Contains(html, e.expectedHTML) {
|
|
t.Errorf("failed %s: expected html contains %s, but got html %s", e.name, e.expectedHTML, html)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// }}}
|
|
// {{{ Test Helpers
|
|
|
|
func getCtx(req *http.Request) context.Context {
|
|
ctx, err := session.Load(req.Context(), req.Header.Get("X-Session"))
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
|
|
return ctx
|
|
}
|
|
|
|
// }}}
|