package handlers import ( "context" "encoding/json" "go-udemy-web-1/internal/driver" "go-udemy-web-1/internal/models" "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 driver.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}, } 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.StatusTemporaryRedirect}, {"non-existant room", 100, http.StatusTemporaryRedirect}, } 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.StatusTemporaryRedirect, }, {"no_post_data", []postData{}, http.StatusTemporaryRedirect}, { "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.StatusTemporaryRedirect, }, { "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.StatusTemporaryRedirect, }, } 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.StatusTemporaryRedirect}, } 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.StatusTemporaryRedirect}, {"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.StatusTemporaryRedirect}, {"wrong end date", []postData{ {key: "start", value: "2050-01-05"}, {key: "end", value: "01-06"}, }, http.StatusTemporaryRedirect}, {"wrong end date", []postData{}, http.StatusTemporaryRedirect}, } 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.StatusTemporaryRedirect}, {"no session", "/choose-room/1", false, http.StatusTemporaryRedirect}, } 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.StatusTemporaryRedirect}, {"wrong end date", []postData{ {key: "id", value: "1"}, {key: "s", value: "2050-01-01"}, {key: "e", value: "2050-0-02"}, }, http.StatusTemporaryRedirect}, {"wrong room id", []postData{ {key: "id", value: "w"}, {key: "s", value: "2050-01-01"}, {key: "e", value: "2050-01-02"}, }, http.StatusTemporaryRedirect}, } 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) } } } // }}} // {{{ 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 } // }}}