package handlers import ( "context" "encoding/json" "errors" "fmt" "log" "net/http" "net/http/httptest" "net/url" "regexp" "strconv" "strings" "testing" "time" "git.andreafazzi.eu/andrea/oef/config" "git.andreafazzi.eu/andrea/oef/orm" "git.andreafazzi.eu/andrea/oef/renderer" "github.com/PuerkitoBio/goquery" jwt "github.com/dgrijalva/jwt-go" "github.com/gorilla/mux" "github.com/remogatto/prettytest" ) var ( token string handlers *Handlers conf *config.ConfigT ) // Start of setup type testSuite struct { prettytest.Suite } func login(request *http.Request, handlers *Handlers, username string, password string) (*http.Request, error) { tokenString := requestToken(handlers, username, password) token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { return []byte(handlers.Config.Keys.JWTSigningKey), nil }) if err != nil { return nil, err } ctx := request.Context() ctx = context.WithValue(ctx, "user", token) return request.WithContext(ctx), nil } func requestToken(handlers *Handlers, username string, password string) string { req, err := http.NewRequest("GET", "/get_token", nil) if err != nil { panic(err) } req.SetBasicAuth(username, password) rr := httptest.NewRecorder() handlers.GetToken(handlers.Database, []byte(handlers.Config.Keys.JWTSigningKey)).ServeHTTP(rr, req) var data struct { Token string } if err := json.Unmarshal(rr.Body.Bytes(), &data); err != nil { panic(err) } return data.Token } func deleteParticipant(id uint) int { req, err := handlers.NewDeleteRequest(&orm.Participant{}, fmt.Sprintf("/participants/%d/delete", id), "html") if err != nil { panic(err) } req, err = login(req, handlers, "admin", "admin") if err != nil { panic(err) } rr := httptest.NewRecorder() router := mux.NewRouter() router.Handle("/participants/{id}/delete", handlers.Delete(&orm.Participant{})) router.ServeHTTP(rr, req) return rr.Code } func deleteSchool(id uint) int { req, err := handlers.NewDeleteRequest(&orm.School{}, fmt.Sprintf("/schools/%d/delete", id), "html") if err != nil { panic(err) } req, err = login(req, handlers, "admin", "admin") if err != nil { panic(err) } rr := httptest.NewRecorder() router := mux.NewRouter() router.Handle("/schools/{id}/delete", handlers.Delete(&orm.School{})) router.ServeHTTP(rr, req) return rr.Code } func subscribeSchoolAsSchool() (uint, error) { form := url.Values{} form.Add("Name", "Foo School") form.Add("Code", "123456789") form.Add("Email", "foo@school.org") req, err := handlers.NewCreateRequest(&orm.School{}, "/schools/create/", "html", "POST", form) if err != nil { panic(err) } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req, err = login(req, handlers, "subscriber", "subscribe") if err != nil { panic(err) } rr := httptest.NewRecorder() router := mux.NewRouter() router.Handle("/schools/create/", handlers.Create(&orm.School{})) router.ServeHTTP(rr, req) if rr.Code != http.StatusSeeOther { return 0, errors.New("Unexpected response code") } re := regexp.MustCompile(`/schools/([0-9]+)\?`) matches := re.FindStringSubmatch(rr.Header()["Location"][0]) id, err := strconv.Atoi(matches[1]) return uint(id), nil } func getIdFromPath(path string) (uint, error) { re := regexp.MustCompile(`/[a-z]+/([0-9]+)\?`) matches := re.FindStringSubmatch(path) id, err := strconv.Atoi(matches[1]) return uint(id), err } func TestRunner(t *testing.T) { prettytest.Run( t, new(testSuite), ) } func (t *testSuite) BeforeAll() { var db *orm.Database conf = new(config.ConfigT) // Load the configuration err := config.ReadFile("testdata/config.yaml", conf) if err != nil { panic(err) } conf.LogLevel = config.LOG_LEVEL_DEBUG // Initialize the ORM connected := false for !connected { var err error time.Sleep(5 * time.Second) db, err = orm.NewDatabase(conf, orm.Models) if err != nil { log.Print(err) continue } connected = true } db.AutoMigrate() // Initialize the renderers htmlRenderer, err := renderer.NewHTMLRenderer("../templates/") if err != nil { panic(err) } renderer := map[string]renderer.Renderer{ "html": htmlRenderer, } conf.Handlers.Permissions = map[string]map[string][]int{ "administrator": map[string][]int{ "Contest": []int{PermissionCreate, PermissionRead, PermissionReadAll, PermissionUpdate, PermissionDelete}, "Participant": []int{PermissionCreate, PermissionRead, PermissionReadAll, PermissionUpdate, PermissionDelete}, "School": []int{PermissionCreate, PermissionRead, PermissionReadAll, PermissionUpdate, PermissionDelete}, "Question": []int{PermissionCreate, PermissionRead, PermissionReadAll, PermissionUpdate, PermissionDelete}, "Answer": []int{PermissionCreate, PermissionRead, PermissionReadAll, PermissionUpdate, PermissionDelete}, "Response": []int{PermissionCreate, PermissionRead, PermissionReadAll, PermissionUpdate, PermissionDelete}, }, "school": map[string][]int{ "Participant": []int{PermissionCreate, PermissionRead, PermissionReadAll, PermissionUpdate, PermissionDelete}, "School": []int{PermissionRead, PermissionUpdate}, }, "participant": map[string][]int{ "Participant": []int{PermissionRead}, "Response": []int{PermissionUpdate, PermissionRead}, }, "subscriber": map[string][]int{ "School": []int{PermissionCreate, PermissionRead}, }, } conf.Handlers.PathPatterns = DefaultPathPatterns conf.Handlers.APIPathPatterns = DefaultAPIPathPatterns handlers = NewHandlers(conf, renderer, db, orm.Models) } func (t *testSuite) TestReadAllContests() { req, err := handlers.NewReadAllRequest(&orm.Contest{}, "/contests", "html") t.Nil(err) req, err = login(req, handlers, "admin", "admin") t.Nil(err) if !t.Failed() { rr := httptest.NewRecorder() router := mux.NewRouter() router.Handle("/contests", handlers.ReadAll(&orm.Contest{})) router.ServeHTTP(rr, req) t.Equal(http.StatusOK, rr.Code) if !t.Failed() { doc, err := goquery.NewDocumentFromReader(rr.Body) if err != nil { log.Fatal(err) } expected := []string{"JUNIOR Contest", "SENIOR Contest"} ok := true doc.Find(".list-group-item").Each(func(i int, s *goquery.Selection) { if !strings.Contains(s.Text(), expected[i]) { ok = false } }) t.True(ok) } } } func (t *testSuite) TestReadContest() { req, err := handlers.NewReadRequest(&orm.Contest{}, "/contests/1", "html") t.Nil(err) req, err = login(req, handlers, "admin", "admin") t.Nil(err) if !t.Failed() { rr := httptest.NewRecorder() router := mux.NewRouter() router.Handle("/contests/{id}", handlers.Read(&orm.Contest{})) router.ServeHTTP(rr, req) t.Equal(http.StatusOK, rr.Code) if !t.Failed() { doc, err := goquery.NewDocumentFromReader(rr.Body) if err != nil { log.Fatal(err) } expected := "JUNIOR Contest" ok := true doc.Find("h1").Each(func(i int, s *goquery.Selection) { if !strings.Contains(s.Text(), expected) { ok = false } }) t.True(ok) } } } func (t *testSuite) TestSchoolSubscriptionForm() { req, err := handlers.NewCreateRequest(&orm.School{}, "/schools/create/", "html", "GET", nil) t.Nil(err) req, err = login(req, handlers, "subscriber", "subscribe") t.Nil(err) rr := httptest.NewRecorder() router := mux.NewRouter() router.Handle("/schools/create/", handlers.Create(&orm.School{})) router.ServeHTTP(rr, req) t.Equal(http.StatusOK, rr.Code) if !t.Failed() { doc, err := goquery.NewDocumentFromReader(rr.Body) if err != nil { log.Fatal(err) } expected := []string{ "Denominazione dell'istituto", "Codice meccanografico", "Indirizzo dell'istituto", "Regione", "Indirizzo email", "Nome del referente di sede", "Cognome del referente di sede", "Nome del responsabile di gara", "Cognome del responsabile di gara", } ok := true doc.Find(".control-label").Each(func(i int, s *goquery.Selection) { t.Equal(expected[i], s.Text()) if t.Failed() { ok = false } }) t.True(ok) } } func (t *testSuite) TestSchoolSubscription() { form := url.Values{} form.Add("Name", "FooSchool") form.Add("Code", "123") form.Add("Email", "foo@school.org") req, err := handlers.NewCreateRequest(&orm.School{}, "/schools/create/", "html", "POST", form) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") t.Nil(err) req, err = login(req, handlers, "subscriber", "subscribe") t.Nil(err) if !t.Failed() { rr := httptest.NewRecorder() router := mux.NewRouter() router.Handle("/schools/create/", handlers.Create(&orm.School{})) router.ServeHTTP(rr, req) t.Equal(http.StatusSeeOther, rr.Code) schoolId, err := getIdFromPath(rr.Header()["Location"][0]) if err != nil { panic(err) } if !t.Failed() { doc, err := goquery.NewDocumentFromReader(rr.Body) if err != nil { log.Fatal(err) } expected := []string{ "FooSchool", "123", } ok := true doc.Find("dd").Each(func(i int, s *goquery.Selection) { t.Equal(expected[i], s.Text()) if t.Failed() { ok = false } }) t.True(ok) } var school orm.School err = handlers.Database.DB().First(&school, schoolId).Error t.Nil(err) if !t.Failed() { var user orm.User if err := handlers.Database.DB().First(&user, school.UserID).Error; err != nil { panic(err) } t.Equal("123", user.Username) form := url.Values{} form.Add("Firstname", "Mario") form.Add("Lastname", "BROS") form.Add("Fiscalcode", "BRSMRE815ZL16") form.Add("category_id", "1") form.Add("school_id", strconv.Itoa(int(school.ID))) req, err := handlers.NewCreateRequest(&orm.Participant{}, "/participants/create/", "html", "POST", form) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") t.Nil(err) req, err = login(req, handlers, user.Username, user.Password) t.Nil(err) rr := httptest.NewRecorder() router := mux.NewRouter() router.Handle("/participants/create/", handlers.Create(&orm.Participant{})) router.ServeHTTP(rr, req) t.Equal(http.StatusSeeOther, rr.Code) if !t.Failed() { participantId, err := getIdFromPath(rr.Header()["Location"][0]) if err != nil { panic(err) } t.Equal(http.StatusOK, deleteParticipant(participantId)) } } t.Equal(http.StatusOK, deleteSchool(schoolId)) } } func (t *testSuite) TestParticipantResponse() { form := url.Values{} form.Set("Singleresponses.0", "9") req, err := handlers.NewUpdateRequest(&orm.Response{}, "/responses/5/update", "html", "POST", form) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") t.Nil(err) req, err = login(req, handlers, "RHOMAT9HZ", "9HzXOfJPje") t.Nil(err) if !t.Failed() { rr := httptest.NewRecorder() router := mux.NewRouter() router.Handle("/responses/{id}/update", handlers.Update(&orm.Response{})) router.ServeHTTP(rr, req) t.Equal(http.StatusSeeOther, rr.Code) if !t.Failed() { req, err := handlers.NewReadRequest(&orm.Response{}, "/responses/5", "html") t.Nil(err) req, err = login(req, handlers, "admin", "admin") t.Nil(err) rr = httptest.NewRecorder() router = mux.NewRouter() router.Handle("/responses/{id}", handlers.Read(&orm.Response{})) router.ServeHTTP(rr, req) t.Equal(http.StatusOK, rr.Code) if !t.Failed() { doc, err := goquery.NewDocumentFromReader(rr.Body) if err != nil { log.Fatal(err) } expected := "1" ok := false doc.Find("dd").Each(func(i int, s *goquery.Selection) { if s.Text() == expected { ok = true } }) t.True(ok) } } } } func (t *testSuite) TestUserModifier() { id, err := subscribeSchoolAsSchool() t.Nil(err) req, err := handlers.NewReadRequest(&orm.School{}, fmt.Sprintf("/schools/%d", id), "html") t.Nil(err) req, err = login(req, handlers, "admin", "admin") t.Nil(err) if !t.Failed() { rr := httptest.NewRecorder() router := mux.NewRouter() router.Handle("/schools/{id}", handlers.Read(&orm.School{})) router.ServeHTTP(rr, req) t.Equal(http.StatusOK, rr.Code) if !t.Failed() { doc, err := goquery.NewDocumentFromReader(rr.Body) if err != nil { log.Fatal(err) } expected := "subscriber[subscriber]" ok := false doc.Find("dd").Each(func(i int, s *goquery.Selection) { if strings.Contains(s.Text(), expected) { ok = true } }) t.True(ok) } } t.Equal(http.StatusOK, deleteSchool(id)) }