package handlers import ( "fmt" _ "github.com/hunterlong/statping/notifiers" "github.com/hunterlong/statping/source" "github.com/hunterlong/statping/utils" "github.com/stretchr/testify/assert" "io/ioutil" "net/http" "net/http/httptest" "net/url" "strings" "testing" ) const ( serverDomain = "http://localhost:8080" ) var ( dir string ) func init() { source.Assets() utils.InitLogs() dir = utils.Directory } func TestResetDatabase(t *testing.T) { Clean() } func TestFailedHTTPServer(t *testing.T) { err := RunHTTPServer("missinghost", 0) assert.Error(t, err) } func TestSetupRoutes(t *testing.T) { form := url.Values{} form.Add("db_host", "") form.Add("db_user", "") form.Add("db_password", "") form.Add("db_database", "") form.Add("db_connection", "sqlite") form.Add("db_port", "") form.Add("project", "Tester") form.Add("username", "admin") form.Add("password", "password123") form.Add("sample_data", "on") form.Add("description", "This is an awesome test") form.Add("domain", "http://localhost:8080") form.Add("email", "info@statping.com") tests := []HTTPTest{ { Name: "Statping Setup Check", URL: "/setup", Method: "GET", ExpectedStatus: 200, }, { Name: "Statping Run Setup", URL: "/setup", Method: "POST", Body: form.Encode(), ExpectedStatus: 303, HttpHeaders: []string{"Content-Type=application/x-www-form-urlencoded"}, ExpectedFiles: []string{utils.Directory + "/config.yml", utils.Directory + "/statup.db"}, }} for _, v := range tests { t.Run(v.Name, func(t *testing.T) { _, t, err := RunHTTPTest(v, t) assert.Nil(t, err) if err != nil { t.FailNow() } }) } } func TestMainApiRoutes(t *testing.T) { tests := []HTTPTest{ { Name: "Statping Details", URL: "/api", Method: "GET", ExpectedStatus: 200, ExpectedContains: []string{`"name":"Tester","description":"This is an awesome test"`}, }, { Name: "Statping Renew API Keys", URL: "/api/renew", Method: "POST", ExpectedStatus: 303, }, { Name: "Statping Clear Cache", URL: "/api/clear_cache", Method: "POST", ExpectedStatus: 303, }, { Name: "404 Error Page", URL: "/api/missing_404_page", Method: "GET", ExpectedStatus: 404, }} for _, v := range tests { t.Run(v.Name, func(t *testing.T) { _, t, err := RunHTTPTest(v, t) assert.Nil(t, err) if err != nil { t.FailNow() } }) } } func TestApiServiceRoutes(t *testing.T) { tests := []HTTPTest{ { Name: "Statping All Services", URL: "/api/services", Method: "GET", ExpectedStatus: 200, ExpectedContains: []string{`"id":1,"name":"Google","domain":"https://google.com"`}, }, { Name: "Statping Service 1", URL: "/api/services/1", Method: "GET", ExpectedContains: []string{`"id":1,"name":"Google","domain":"https://google.com"`}, ExpectedStatus: 200, }, { Name: "Statping Service 1 Data", URL: "/api/services/1/data", Method: "GET", Body: "", ExpectedStatus: 200, }, { Name: "Statping Service 1 Ping Data", URL: "/api/services/1/ping", Method: "GET", Body: "", ExpectedStatus: 200, }, { Name: "Statping Service 1 Heatmap Data", URL: "/api/services/1/heatmap", Method: "GET", Body: "", ExpectedStatus: 200, }, { Name: "Statping Service 1 Hits", URL: "/api/services/1/hits", Method: "GET", Body: "", ExpectedStatus: 200, }, { Name: "Statping Service 1 Failures", URL: "/api/services/1/failures", Method: "GET", Body: "", ExpectedStatus: 200, }, { Name: "Statping Reorder Services", URL: "/api/services/reorder", Method: "POST", Body: `[{"service":1,"order":1},{"service":5,"order":2},{"service":2,"order":3},{"service":3,"order":4},{"service":4,"order":5}]`, ExpectedStatus: 200, HttpHeaders: []string{"Content-Type=application/json"}, }, { Name: "Statping Create Service", URL: "/api/services", HttpHeaders: []string{"Content-Type=application/json"}, Method: "POST", Body: `{ "name": "New Service", "domain": "https://statping.com", "expected": "", "expected_status": 200, "check_interval": 30, "type": "http", "method": "GET", "post_data": "", "port": 0, "timeout": 30, "order_id": 0 }`, ExpectedStatus: 200, ExpectedContains: []string{`"status":"success","type":"service","method":"create"`}, }, { Name: "Statping Update Service", URL: "/api/services/1", HttpHeaders: []string{"Content-Type=application/json"}, Method: "POST", Body: `{ "name": "Updated New Service", "domain": "https://google.com", "expected": "", "expected_status": 200, "check_interval": 60, "type": "http", "method": "GET", "post_data": "", "port": 0, "timeout": 10, "order_id": 0 }`, ExpectedStatus: 200, ExpectedContains: []string{`"status":"success","type":"service","method":"update"`}, }, { Name: "Statping Delete Service", URL: "/api/services/1", Method: "DELETE", ExpectedStatus: 200, ExpectedContains: []string{`"status":"success","type":"service","method":"delete"`}, }} for _, v := range tests { t.Run(v.Name, func(t *testing.T) { _, t, err := RunHTTPTest(v, t) assert.Nil(t, err) if err != nil { t.FailNow() } }) } } func TestGroupAPIRoutes(t *testing.T) { tests := []HTTPTest{ { Name: "Statping Groups", URL: "/api/groups", Method: "GET", ExpectedStatus: 200, }, { Name: "Statping View Group", URL: "/api/groups/1", Method: "GET", ExpectedStatus: 200, }, { Name: "Statping Create Group", URL: "/api/groups", HttpHeaders: []string{"Content-Type=application/json"}, Body: `{ "name": "New Group", "public": true }`, Method: "POST", ExpectedStatus: 200, }, { Name: "Statping Delete Group", URL: "/api/groups/1", Method: "DELETE", ExpectedStatus: 200, }} for _, v := range tests { t.Run(v.Name, func(t *testing.T) { _, t, err := RunHTTPTest(v, t) assert.Nil(t, err) if err != nil { t.FailNow() } }) } } func TestApiUsersRoutes(t *testing.T) { tests := []HTTPTest{ { Name: "Statping All Users", URL: "/api/users", Method: "GET", ExpectedStatus: 200, }, { Name: "Statping Create User", URL: "/api/users", HttpHeaders: []string{"Content-Type=application/json"}, Method: "POST", Body: `{ "username": "adminuser2", "email": "info@adminemail.com", "password": "passsword123", "admin": true }`, ExpectedStatus: 200, }, { Name: "Statping View User", URL: "/api/users/1", Method: "GET", ExpectedStatus: 200, }, { Name: "Statping Update User", URL: "/api/users/1", Method: "POST", Body: `{ "username": "adminupdated", "email": "info@email.com", "password": "password12345", "admin": true }`, ExpectedStatus: 200, }, { Name: "Statping Delete User", URL: "/api/users/1", Method: "DELETE", ExpectedStatus: 200, }} for _, v := range tests { t.Run(v.Name, func(t *testing.T) { _, t, err := RunHTTPTest(v, t) assert.Nil(t, err) if err != nil { t.FailNow() } }) } } func TestApiNotifiersRoutes(t *testing.T) { tests := []HTTPTest{ { Name: "Statping Notifiers", URL: "/api/notifiers", Method: "GET", ExpectedStatus: 200, }, { Name: "Statping Mobile Notifier", URL: "/api/notifier/mobile", Method: "GET", ExpectedStatus: 200, }, { Name: "Statping Update Notifier", URL: "/api/notifier/mobile", Method: "POST", Body: `{ "method": "mobile", "var1": "ExponentPushToken[ToBadIWillError123456]", "enabled": true, "limits": 55 }`, ExpectedStatus: 200, }} for _, v := range tests { t.Run(v.Name, func(t *testing.T) { _, t, err := RunHTTPTest(v, t) assert.Nil(t, err) if err != nil { t.FailNow() } }) } } func TestMessagesApiRoutes(t *testing.T) { tests := []HTTPTest{ { Name: "Statping Messages", URL: "/api/messages", Method: "GET", ExpectedStatus: 200, ExpectedContains: []string{`"id":1,"title":"Routine Downtime"`}, }, { Name: "Statping Create Message", URL: "/api/messages", Method: "POST", Body: `{ "title": "API Message", "description": "This is an example a upcoming message for a service!", "start_on": "2022-11-17T03:28:16.323797-08:00", "end_on": "2022-11-17T05:13:16.323798-08:00", "service": 1, "notify_users": true, "notify_method": "email", "notify_before": 6, "notify_before_scale": "hour" }`, ExpectedStatus: 200, ExpectedContains: []string{`"status":"success","type":"message","method":"create"`}, }, { Name: "Statping View Message", URL: "/api/messages/1", Method: "GET", ExpectedStatus: 200, ExpectedContains: []string{`"id":1,"title":"Routine Downtime"`}, }, { Name: "Statping Update Message", URL: "/api/messages/1", Method: "POST", Body: `{ "title": "Updated Message", "description": "This message was updated", "start_on": "2022-11-17T03:28:16.323797-08:00", "end_on": "2022-11-17T05:13:16.323798-08:00", "service": 1, "notify_users": true, "notify_method": "email", "notify_before": 3, "notify_before_scale": "hour" }`, ExpectedStatus: 200, ExpectedContains: []string{`"status":"success","type":"message","method":"update"`}, }, { Name: "Statping Delete Message", URL: "/api/messages/1", Method: "DELETE", ExpectedStatus: 200, ExpectedContains: []string{`"status":"success","type":"message","method":"delete"`}, }} for _, v := range tests { t.Run(v.Name, func(t *testing.T) { _, t, err := RunHTTPTest(v, t) assert.Nil(t, err) if err != nil { t.FailNow() } }) } } func TestApiCheckinRoutes(t *testing.T) { tests := []HTTPTest{ { Name: "Statping Checkins", URL: "/api/checkins", Method: "GET", ExpectedStatus: 200, }, { Name: "Statping Create Checkin", URL: "/api/checkin", Method: "POST", Body: `{ "service_id": 2, "name": "Server Checkin", "interval": 900, "grace": 60 }`, ExpectedStatus: 200, ExpectedContains: []string{`"status":"success","type":"checkin","method":"create"`}, }, { Name: "Statping Checkins", URL: "/api/checkins", Method: "GET", ExpectedStatus: 200, }} for _, v := range tests { t.Run(v.Name, func(t *testing.T) { _, t, err := RunHTTPTest(v, t) assert.Nil(t, err) if err != nil { t.FailNow() } }) } } // HTTPTest contains all the parameters for a HTTP Unit Test type HTTPTest struct { Name string URL string Method string Body string ExpectedStatus int ExpectedContains []string HttpHeaders []string ExpectedFiles []string } // RunHTTPTest accepts a HTTPTest type to execute the HTTP request func RunHTTPTest(test HTTPTest, t *testing.T) (string, *testing.T, error) { req, err := http.NewRequest(test.Method, serverDomain+test.URL, strings.NewReader(test.Body)) if err != nil { assert.Nil(t, err) return "", t, err } if len(test.HttpHeaders) != 0 { for _, v := range test.HttpHeaders { splits := strings.Split(v, "=") req.Header.Set(splits[0], splits[1]) } } rr := httptest.NewRecorder() Router().ServeHTTP(rr, req) if err != nil { assert.Nil(t, err) return "", t, err } body, err := ioutil.ReadAll(rr.Result().Body) if err != nil { assert.Nil(t, err) return "", t, err } stringBody := string(body) if test.ExpectedStatus != rr.Result().StatusCode { assert.Equal(t, test.ExpectedStatus, rr.Result().StatusCode) return stringBody, t, fmt.Errorf("status code %v does not match %v", rr.Result().StatusCode, test.ExpectedStatus) } if len(test.ExpectedContains) != 0 { for _, v := range test.ExpectedContains { assert.Contains(t, stringBody, v) } } if len(test.ExpectedFiles) != 0 { for _, v := range test.ExpectedFiles { assert.FileExists(t, v) } } return stringBody, t, err } func Clean() { utils.DeleteFile(dir + "/config.yml") utils.DeleteFile(dir + "/statup.db") utils.DeleteDirectory(dir + "/logs") }