diff --git a/handlers/api_test.go b/handlers/api_test.go new file mode 100644 index 00000000..f182644f --- /dev/null +++ b/handlers/api_test.go @@ -0,0 +1,510 @@ +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) { + body, t, err := RunHTTPTest(v, t) + assert.Nil(t, err) + if err != nil { + t.FailNow() + } + t.Logf("Test %v got: %v\n", v.Name, string(body)) + }) + } +} + +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"`}, + }} + + for _, v := range tests { + t.Run(v.Name, func(t *testing.T) { + body, t, err := RunHTTPTest(v, t) + assert.Nil(t, err) + if err != nil { + t.FailNow() + } + t.Logf("Test %v got: %v\n", v.Name, string(body)) + }) + } +} + +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 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) { + body, t, err := RunHTTPTest(v, t) + assert.Nil(t, err) + if err != nil { + t.FailNow() + } + t.Logf("Test %v got: %v\n", v.Name, string(body)) + }) + } +} + +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) { + body, t, err := RunHTTPTest(v, t) + assert.Nil(t, err) + if err != nil { + t.FailNow() + } + t.Logf("Test %v got: %v\n", v.Name, string(body)) + }) + } +} + +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) { + body, t, err := RunHTTPTest(v, t) + assert.Nil(t, err) + if err != nil { + t.FailNow() + } + t.Logf("Test %v got: %v\n", v.Name, string(body)) + }) + } +} + +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) { + body, t, err := RunHTTPTest(v, t) + assert.Nil(t, err) + if err != nil { + t.FailNow() + } + t.Logf("Test %v got: %v\n", v.Name, string(body)) + }) + } +} + +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) { + body, t, err := RunHTTPTest(v, t) + assert.Nil(t, err) + if err != nil { + t.FailNow() + } + t.Logf("Test %v got: %v\n", v.Name, string(body)) + }) + } +} + +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) { + body, t, err := RunHTTPTest(v, t) + assert.Nil(t, err) + if err != nil { + t.FailNow() + } + t.Logf("Test %v got: %v\n", v.Name, string(body)) + }) + } +} + +// 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") +} diff --git a/handlers/dashboard_test.go b/handlers/dashboard_test.go new file mode 100644 index 00000000..c721a715 --- /dev/null +++ b/handlers/dashboard_test.go @@ -0,0 +1,113 @@ +package handlers + +import ( + "github.com/stretchr/testify/assert" + "net/url" + "testing" +) + +func TestGenericRoutes(t *testing.T) { + + form := url.Values{} + form.Add("username", "admin") + form.Add("password", "password123") + + form2 := url.Values{} + form2.Add("username", "admin") + form2.Add("password", "wrongpassword") + + tests := []HTTPTest{ + { + Name: "Statping Index", + URL: "/", + Method: "GET", + ExpectedStatus: 200, + ExpectedContains: []string{ + `<title>Tester Status</title>`, + `<footer>`, + }, + }, + { + Name: "Statping Incorrect Login", + URL: "/dashboard", + Method: "POST", + Body: form.Encode(), + HttpHeaders: []string{"Content-Type=application/x-www-form-urlencoded"}, + ExpectedContains: []string{"Incorrect login information submitted, try again."}, + ExpectedStatus: 200, + }, + { + Name: "Dashboard Routes", + URL: "/dashboard", + Method: "GET", + ExpectedStatus: 200, + ExpectedContains: []string{"<title>Statping | Dashboard</title>"}, + }, + { + Name: "Services Routes", + URL: "/services", + Method: "GET", + ExpectedStatus: 200, + ExpectedContains: []string{"<title>Statping | Services</title>"}, + }, + { + Name: "Users Routes", + URL: "/users", + Method: "GET", + ExpectedStatus: 200, + ExpectedContains: []string{"<title>Statping | Users</title>"}, + }, + { + Name: "Messages Routes", + URL: "/messages", + Method: "GET", + ExpectedStatus: 200, + ExpectedContains: []string{"<title>Statping Messages</title>"}, + }, + { + Name: "Settings Routes", + URL: "/settings", + Method: "GET", + ExpectedStatus: 200, + ExpectedContains: []string{"<title>Statping | Settings</title>"}, + }, + { + Name: "Logs Routes", + URL: "/logs", + Method: "GET", + ExpectedStatus: 200, + ExpectedContains: []string{"<title>Statping | Logs</title>"}, + }, + { + Name: "Help Routes", + URL: "/help", + Method: "GET", + ExpectedStatus: 200, + ExpectedContains: []string{"<title>Statping | Help</title>"}, + }, + { + Name: "Logout", + URL: "/logout", + Method: "GET", + ExpectedStatus: 303, + }, + { + Name: "Prometheus Metrics Routes", + URL: "/metrics", + Method: "GET", + ExpectedStatus: 200, + ExpectedContains: []string{"statping_total_services 5"}, + }, + } + + for _, v := range tests { + t.Run(v.Name, func(t *testing.T) { + body, t, err := RunHTTPTest(v, t) + assert.Nil(t, err) + if err != nil { + t.FailNow() + } + t.Logf("Test %v got: %v\n", v.Name, string(body)) + }) + } +} diff --git a/handlers/handlers_test.go b/handlers/handlers_test.go deleted file mode 100644 index 94ea49a9..00000000 --- a/handlers/handlers_test.go +++ /dev/null @@ -1,493 +0,0 @@ -// Statping -// Copyright (C) 2018. Hunter Long and the project contributors -// Written by Hunter Long <info@socialeck.com> and the project contributors -// -// https://github.com/hunterlong/statping -// -// The licenses for most software and other practical works are designed -// to take away your freedom to share and change the works. By contrast, -// the GNU General Public License is intended to guarantee your freedom to -// share and change all versions of a program--to make sure it remains free -// software for all its users. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. - -package handlers - -import ( - "github.com/hunterlong/statping/core" - _ "github.com/hunterlong/statping/notifiers" - "github.com/hunterlong/statping/source" - "github.com/hunterlong/statping/utils" - "github.com/stretchr/testify/assert" - "net/http" - "net/http/httptest" - "net/url" - "os" - "strings" - "testing" -) - -func TestResetHandlerDatabase(t *testing.T) { - Clean() - //loadDatabase() - //createDatabase() -} - -func TestFailedHTTPServer(t *testing.T) { - err := RunHTTPServer("missinghost", 0) - assert.Error(t, err) -} - -func TestSetupHandler(t *testing.T) { - req, err := http.NewRequest("GET", "/setup", nil) - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - body := rr.Body.String() - assert.Equal(t, 200, rr.Code) - assert.Contains(t, body, "<title>Statping | Setup</title>") -} - -func TestProcessSetupHandler(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") - req, err := http.NewRequest("POST", "/setup", strings.NewReader(form.Encode())) - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - assert.Equal(t, 303, rr.Code) - assert.FileExists(t, dir+"/config.yml") - assert.FileExists(t, dir+"/statup.db") -} - -func TestCheckSetupHandler(t *testing.T) { - req, err := http.NewRequest("GET", "/setup", nil) - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - assert.Equal(t, 303, rr.Code) -} - -func TestCheckIndexHandler(t *testing.T) { - req, err := http.NewRequest("GET", "/", nil) - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - assert.Equal(t, 200, rr.Code) -} - -func TestServicesViewHandler(t *testing.T) { - req, err := http.NewRequest("GET", "/service/1", nil) - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - body := rr.Body.String() - assert.Equal(t, 200, rr.Code) - assert.Contains(t, body, "<title>Google Status</title>") - assert.Contains(t, body, "</footer>") -} - -func TestMissingServiceViewHandler(t *testing.T) { - req, err := http.NewRequest("GET", "/service/99999999", nil) - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - assert.Equal(t, 404, rr.Code) -} - -func TestServiceChartHandler(t *testing.T) { - req, err := http.NewRequest("GET", "/charts.js", nil) - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - body := rr.Body.String() - assert.Equal(t, 200, rr.Code) - assert.Contains(t, body, "var ctx_1") - assert.Contains(t, body, "var ctx_2") - assert.Contains(t, body, "var ctx_3") - assert.Contains(t, body, "var ctx_4") - assert.Contains(t, body, "var ctx_5") -} - -func TestDashboardHandler(t *testing.T) { - req, err := http.NewRequest("GET", "/dashboard", nil) - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - body := rr.Body.String() - assert.Equal(t, 200, rr.Code) - assert.Contains(t, body, "<title>Statping | Dashboard</title>") - assert.Contains(t, body, "</footer>") -} - -func TestLoginHandler(t *testing.T) { - form := url.Values{} - form.Add("username", "admin") - form.Add("password", "password123") - req, err := http.NewRequest("POST", "/dashboard", strings.NewReader(form.Encode())) - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - assert.Equal(t, 303, rr.Code) -} - -func TestBadLoginHandler(t *testing.T) { - form := url.Values{} - form.Add("username", "admin") - form.Add("password", "wrongpassword") - req, err := http.NewRequest("POST", "/dashboard", strings.NewReader(form.Encode())) - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - body := rr.Body.String() - assert.Contains(t, body, "Incorrect login information submitted, try again.") - assert.Equal(t, 200, rr.Code) -} - -func TestServicesHandler(t *testing.T) { - req, err := http.NewRequest("GET", "/services", nil) - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - body := rr.Body.String() - assert.Equal(t, 200, rr.Code) - assert.Contains(t, body, "<title>Statping | Services</title>") - //assert.Contains(t, body, "</footer>️") - assert.True(t, isRouteAuthenticated(req)) -} - -func TestUsersHandler(t *testing.T) { - req, err := http.NewRequest("GET", "/users", nil) - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - body := rr.Body.String() - assert.Equal(t, 200, rr.Code) - assert.Contains(t, body, "<title>Statping | Users</title>") - assert.Contains(t, body, "<td>admin</td>") - assert.NotContains(t, body, "<td>changedusername</td>") - assert.Contains(t, body, "</footer>") - assert.True(t, isRouteAuthenticated(req)) -} - -func TestUsersEditHandler(t *testing.T) { - req, err := http.NewRequest("GET", "/user/1", nil) - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - body := rr.Body.String() - assert.Equal(t, 200, rr.Code) - assert.Contains(t, body, "<title>Statping | admin</title>") - assert.Contains(t, body, "<h3>User admin</h3>") - assert.Contains(t, body, "value=\"info@statping.com\"") - //assert.Contains(t, body, "</footer>️") - assert.True(t, isRouteAuthenticated(req)) -} - -func TestSettingsHandler(t *testing.T) { - req, err := http.NewRequest("GET", "/settings", nil) - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - body := rr.Body.String() - assert.Equal(t, 200, rr.Code) - assert.Contains(t, body, "<title>Statping | Settings</title>") - //assert.Contains(t, body, "</footer>️") - assert.True(t, isRouteAuthenticated(req)) -} - -func TestHelpHandler(t *testing.T) { - req, err := http.NewRequest("GET", "/help", nil) - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - body := rr.Body.String() - assert.Equal(t, 200, rr.Code) - assert.Contains(t, body, "<title>Statping | Help</title>") - //assert.Contains(t, body, "</footer>️") - assert.True(t, isRouteAuthenticated(req)) -} - -func TestServicesHandler2(t *testing.T) { - req, err := http.NewRequest("GET", "/services", nil) - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - body := rr.Body.String() - assert.Equal(t, 200, rr.Code) - assert.Contains(t, body, "<title>Statping | Services</title>") - assert.Contains(t, body, "JSON Users Test") - assert.Contains(t, body, "JSON API Tester") - //assert.Contains(t, body, "</footer>️") - assert.True(t, isRouteAuthenticated(req)) -} - -func TestViewHTTPServicesHandler(t *testing.T) { - req, err := http.NewRequest("GET", "/service/5", nil) - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - body := rr.Body.String() - assert.Equal(t, 200, rr.Code) - assert.Contains(t, body, "<title>Google DNS Status</title>") - //assert.Contains(t, body, "</footer>️") -} - -func TestViewTCPServicesHandler(t *testing.T) { - req, err := http.NewRequest("GET", "/service/5", nil) - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - body := rr.Body.String() - assert.Equal(t, 200, rr.Code) - assert.Contains(t, body, "<title>Google DNS Status</title>") - //assert.Contains(t, body, "</footer>️") -} - -func TestServicesDeleteFailuresHandler(t *testing.T) { - req, err := http.NewRequest("GET", "/service/5/delete_failures", nil) - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - assert.Equal(t, 303, rr.Code) - assert.True(t, isRouteAuthenticated(req)) -} - -func TestFailingServicesDeleteFailuresHandler(t *testing.T) { - req, err := http.NewRequest("GET", "/service/5/delete_failures", nil) - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - assert.Equal(t, 303, rr.Code) - assert.True(t, isRouteAuthenticated(req)) -} - -func TestLogsHandler(t *testing.T) { - req, err := http.NewRequest("GET", "/logs", nil) - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - body := rr.Body.String() - assert.Equal(t, 200, rr.Code) - assert.Contains(t, body, "<title>Statping | Logs</title>") - //assert.Contains(t, body, "</footer>️") - assert.True(t, isRouteAuthenticated(req)) -} - -func TestLogsLineHandler(t *testing.T) { - req, err := http.NewRequest("GET", "/logs/line", nil) - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - body := rr.Body.String() - assert.Equal(t, 200, rr.Code) - assert.NotEmpty(t, body) - assert.True(t, isRouteAuthenticated(req)) -} - -func TestSaveSettingsHandler(t *testing.T) { - form := url.Values{} - form.Add("project", "Awesome Status") - form.Add("description", "These tests can probably be better") - req, err := http.NewRequest("POST", "/settings", strings.NewReader(form.Encode())) - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - assert.Equal(t, 303, rr.Code) - assert.True(t, isRouteAuthenticated(req)) -} - -func TestViewSettingsHandler(t *testing.T) { - req, err := http.NewRequest("GET", "/settings", nil) - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - body := rr.Body.String() - assert.Equal(t, 200, rr.Code) - assert.Contains(t, body, "<title>Statping | Settings</title>") - assert.Contains(t, body, "Awesome Status") - //assert.Contains(t, body, "</footer>️") - assert.True(t, isRouteAuthenticated(req)) -} - -func TestSaveAssetsHandler(t *testing.T) { - req, err := http.NewRequest("GET", "/settings/build", nil) - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - assert.Equal(t, 303, rr.Code) - assert.FileExists(t, utils.Directory+"/assets/css/base.css") - assert.DirExists(t, utils.Directory+"/assets") - assert.True(t, source.UsingAssets(dir)) - assert.True(t, isRouteAuthenticated(req)) -} - -func TestDeleteAssetsHandler(t *testing.T) { - req, err := http.NewRequest("GET", "/settings/delete_assets", nil) - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - assert.Equal(t, 303, rr.Code) - assert.False(t, source.UsingAssets(dir)) - assert.True(t, isRouteAuthenticated(req)) -} - -func TestPrometheusHandler(t *testing.T) { - req, err := http.NewRequest("GET", "/metrics", nil) - req.Header.Set("Authorization", core.CoreApp.ApiSecret) - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - body := rr.Body.String() - assert.Equal(t, 200, rr.Code) - assert.Contains(t, body, "statping_total_services 5") - assert.True(t, isRouteAuthenticated(req)) -} - -func TestUpdateNotificationHandler(t *testing.T) { - t.SkipNow() - data := `{"limits": 7, "enabled": true, "method": "email", "host": "smtp.emailserver.com", "username": "exampleuser", "password": "password123", "port": 543, "var1": "info@betatude.com", "var2": "sendto@gmail.com"}` - rr, err := httpRequestAPI(t, "POST", "/api/notifier/email", strings.NewReader(data)) - assert.Nil(t, err) - body := rr.Body.String() - var obj apiResponse - formatJSON(body, &obj) - assert.Equal(t, 200, rr.Code) - assert.Equal(t, "success", obj.Status) -} - -func TestViewNotificationSettingsHandler(t *testing.T) { - t.SkipNow() - req, err := http.NewRequest("GET", "/settings", nil) - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - body := rr.Body.String() - assert.Equal(t, 200, rr.Code) - assert.Contains(t, body, "<title>Statping | Settings</title>") - assert.Contains(t, body, `value="exampleuser" id="smtp_username"`) - assert.Contains(t, body, `value="587" id="smtp_port"`) - assert.Contains(t, body, `value="info@betatude.com" id="outgoing_email_address"`) - assert.Contains(t, body, `value="sendto@gmail.com" id="send_alerts_to"`) - assert.Contains(t, body, `id="limits_per_hour_email" value="7"`) - assert.Contains(t, body, `id="switch-email" checked`) - //assert.Contains(t, body, "</footer>️") - assert.True(t, isRouteAuthenticated(req)) -} - -func TestSaveFooterHandler(t *testing.T) { - form := url.Values{} - form.Add("footer", "Created by Hunter Long") - req, err := http.NewRequest("POST", "/settings", strings.NewReader(form.Encode())) - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - assert.Equal(t, 303, rr.Code) - assert.True(t, isRouteAuthenticated(req)) - - req, err = http.NewRequest("GET", "/", nil) - assert.Nil(t, err) - rr = httptest.NewRecorder() - Router().ServeHTTP(rr, req) - body := rr.Body.String() - assert.Equal(t, 200, rr.Code) - assert.Contains(t, body, "Created by Hunter Long") -} - -func TestError404Handler(t *testing.T) { - req, err := http.NewRequest("GET", "/404me", nil) - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - assert.Equal(t, 404, rr.Code) -} - -func TestLogoutHandler(t *testing.T) { - req, err := http.NewRequest("GET", "/logout", nil) - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - assert.Equal(t, 303, rr.Code) -} - -func TestBuildAssetsHandler(t *testing.T) { - req, err := http.NewRequest("GET", "/settings/build", nil) - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - assert.Equal(t, 303, rr.Code) - assert.True(t, isRouteAuthenticated(req)) - assert.FileExists(t, "../assets/scss/base.scss") -} - -func TestSaveSassHandler(t *testing.T) { - base := source.OpenAsset(utils.Directory, "scss/base.scss") - vars := source.OpenAsset(utils.Directory, "scss/variables.scss") - - form := url.Values{} - form.Add("theme", base+"\n .test_design { color: $test-design; }") - form.Add("variables", vars+"\n $test-design: #ffffff; ") - req, err := http.NewRequest("POST", "/settings/css", strings.NewReader(form.Encode())) - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - assert.Equal(t, 303, rr.Code) - assert.True(t, isRouteAuthenticated(req)) - - newBase := source.OpenAsset(utils.Directory, "css/base.css") - assert.Contains(t, newBase, ".test_design {") -} - -func TestReorderServiceHandler(t *testing.T) { - data := `[{id: 1, order: 3},{id: 2, order: 2},{id: 3, order: 1}]"` - req, err := http.NewRequest("POST", "/api/services/reorder", strings.NewReader(data)) - req.Header.Set("Content-Type", "application/json") - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - assert.Equal(t, 200, rr.Code) - assert.True(t, isRouteAuthenticated(req)) -} - -func TestExportHandler(t *testing.T) { - req, err := http.NewRequest("GET", "/settings/export", nil) - assert.Nil(t, err) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - assert.Equal(t, 200, rr.Code) - assert.True(t, isRouteAuthenticated(req)) -} - -func isRouteAuthenticated(req *http.Request) bool { - os.Setenv("GO_ENV", "production") - rr := httptest.NewRecorder() - req.Header.Set("Authorization", "badkey") - Router().ServeHTTP(rr, req) - code := rr.Code - if code == 200 { - os.Setenv("GO_ENV", "test") - return false - } - os.Setenv("GO_ENV", "test") - return true -} diff --git a/handlers/messages_test.go b/handlers/messages_test.go new file mode 100644 index 00000000..052cc332 --- /dev/null +++ b/handlers/messages_test.go @@ -0,0 +1,35 @@ +package handlers + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestMessageRoutes(t *testing.T) { + tests := []HTTPTest{ + { + Name: "Messages", + URL: "/messages", + Method: "GET", + ExpectedStatus: 200, + ExpectedContains: []string{`<title>Statping Messages</title>`}, + }, + { + Name: "Message 2", + URL: "/message/2", + Method: "GET", + ExpectedStatus: 200, + ExpectedContains: []string{`<title>Statping | Server Reboot</title>`}, + }} + + for _, v := range tests { + t.Run(v.Name, func(t *testing.T) { + body, t, err := RunHTTPTest(v, t) + assert.Nil(t, err) + if err != nil { + t.FailNow() + } + t.Logf("Test %v got: %v\n", v.Name, string(body)) + }) + } +} diff --git a/handlers/route_api_test.go b/handlers/route_api_test.go deleted file mode 100644 index 2cc37cf4..00000000 --- a/handlers/route_api_test.go +++ /dev/null @@ -1,327 +0,0 @@ -// Statping -// Copyright (C) 2018. Hunter Long and the project contributors -// Written by Hunter Long <info@socialeck.com> and the project contributors -// -// https://github.com/hunterlong/statping -// -// The licenses for most software and other practical works are designed -// to take away your freedom to share and change the works. By contrast, -// the GNU General Public License is intended to guarantee your freedom to -// share and change all versions of a program--to make sure it remains free -// software for all its users. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. - -package handlers - -import ( - "encoding/json" - "github.com/hunterlong/statping/core" - "github.com/hunterlong/statping/source" - "github.com/hunterlong/statping/types" - "github.com/hunterlong/statping/utils" - "github.com/stretchr/testify/assert" - "io" - "net/http" - "net/http/httptest" - "strings" - "testing" -) - -const ( - NEW_HTTP_SERVICE = `{"name": "Google Website", "domain": "https://google.com", "expected_status": 200, "check_interval": 10, "type": "http", "method": "GET"}` -) - -var ( - dir string -) - -func init() { - dir = utils.Directory - utils.InitLogs() - source.Assets() -} - -func loadDatabase() { - core.NewCore() - core.LoadConfigFile(dir) - core.Configs = &core.DbConfig{ - DbConn: "sqlite", - Location: dir, - } - core.CoreApp.DbConnection = "sqlite" - core.Configs.Connect(false, utils.Directory) - core.CoreApp.Version = "DEV" - core.Configs.Save() -} - -func createDatabase() { - core.Configs.DropDatabase() - core.Configs.CreateDatabase() -} - -func resetDatabase() { - core.Configs.DropDatabase() - core.Configs.CreateDatabase() - core.InsertLargeSampleData() -} - -func Clean() { - utils.DeleteFile(dir + "/config.yml") - utils.DeleteFile(dir + "/statup.db") - utils.DeleteDirectory(dir + "/assets") - utils.DeleteDirectory(dir + "/logs") -} - -func TestInit(t *testing.T) { - //Clean() - //loadDatabase() - //resetDatabase() - //loadDatabase() - //core.SelectCore() - //core.InsertNotifierDB() - //core.CoreApp.SelectAllServices(false) - //core.CoreApp.Notifications = notifier.Load() -} - -func formatJSON(res string, out interface{}) { - json.Unmarshal([]byte(res), &out) -} - -func TestApiIndexHandler(t *testing.T) { - rr, err := httpRequestAPI(t, "GET", "/api", nil) - assert.Nil(t, err) - body := rr.Body.String() - var obj types.Core - formatJSON(body, &obj) - assert.Equal(t, 200, rr.Code) - assert.Equal(t, "Awesome Status", obj.Name) - assert.Equal(t, "sqlite", obj.DbConnection) -} - -func TestApiAllServicesHandlerHandler(t *testing.T) { - rr, err := httpRequestAPI(t, "GET", "/api/services", nil) - assert.Nil(t, err) - body := rr.Body.String() - var obj []types.Service - formatJSON(body, &obj) - assert.Equal(t, 200, rr.Code) - assert.Equal(t, "Google", obj[0].Name) - assert.Equal(t, "https://google.com", obj[0].Domain) -} - -func TestApiServiceHandler(t *testing.T) { - rr, err := httpRequestAPI(t, "GET", "/api/services/1", nil) - assert.Nil(t, err) - body := rr.Body.String() - var obj types.Service - formatJSON(body, &obj) - assert.Equal(t, 200, rr.Code) - assert.Equal(t, "Google", obj.Name) - assert.Equal(t, "https://google.com", obj.Domain) -} - -func TestApiCreateServiceHandler(t *testing.T) { - rr, err := httpRequestAPI(t, "POST", "/api/services", strings.NewReader(NEW_HTTP_SERVICE)) - assert.Nil(t, err) - body := rr.Body.String() - assert.Equal(t, 200, rr.Code) - var obj apiResponse - formatJSON(body, &obj) - t.Log(body) - object := obj.Output.(map[string]interface{}) - assert.Equal(t, 200, rr.Code) - assert.Equal(t, "Google Website", object["name"]) - assert.Equal(t, "https://google.com", object["domain"]) -} - -func TestApiUpdateServiceHandler(t *testing.T) { - data := `{ - "name": "Updated 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}` - rr, err := httpRequestAPI(t, "POST", "/api/services/1", strings.NewReader(data)) - assert.Nil(t, err) - body := rr.Body.String() - t.Log(body) - var obj apiResponse - formatJSON(body, &obj) - assert.Equal(t, 200, rr.Code) - assert.Equal(t, "success", obj.Status) - assert.Equal(t, "update", obj.Method) - assert.Equal(t, int64(1), obj.Id) -} - -func TestApiDeleteServiceHandler(t *testing.T) { - rr, err := httpRequestAPI(t, "DELETE", "/api/services/2", nil) - assert.Nil(t, err) - body := rr.Body.String() - var obj apiResponse - formatJSON(body, &obj) - assert.Equal(t, 200, rr.Code) - assert.Equal(t, "delete", obj.Method) - assert.Equal(t, "success", obj.Status) -} - -func TestApiAllUsersHandler(t *testing.T) { - rr, err := httpRequestAPI(t, "GET", "/api/users", nil) - assert.Nil(t, err) - body := rr.Body.String() - assert.Equal(t, 200, rr.Code) - var obj []types.User - formatJSON(body, &obj) - assert.Equal(t, true, obj[0].Admin.Bool) - assert.Equal(t, "admin", obj[0].Username) -} - -func TestApiCreateUserHandler(t *testing.T) { - data := `{ - "username": "admin2", - "email": "info@email.com", - "password": "password123", - "admin": true}` - rr, err := httpRequestAPI(t, "POST", "/api/users", strings.NewReader(data)) - assert.Nil(t, err) - body := rr.Body.String() - var obj apiResponse - formatJSON(body, &obj) - t.Log(body) - assert.Equal(t, 200, rr.Code) - assert.Contains(t, "create", obj.Method) - assert.Contains(t, "success", obj.Status) -} - -func TestApiViewUserHandler(t *testing.T) { - rr, err := httpRequestAPI(t, "GET", "/api/users/1", nil) - assert.Nil(t, err) - body := rr.Body.String() - assert.Equal(t, 200, rr.Code) - var obj types.User - formatJSON(body, &obj) - t.Log(body) - assert.Equal(t, "admin", obj.Username) - assert.Equal(t, true, obj.Admin.Bool) -} - -func TestApiUpdateUserHandler(t *testing.T) { - data := `{ - "username": "adminupdated", - "password": "password123", - "admin": true}` - rr, err := httpRequestAPI(t, "POST", "/api/users/1", strings.NewReader(data)) - assert.Nil(t, err) - body := rr.Body.String() - var obj apiResponse - formatJSON(body, &obj) - t.Log(body) - assert.Equal(t, 200, rr.Code) - assert.Equal(t, "update", obj.Method) - assert.Equal(t, "success", obj.Status) - assert.Equal(t, int64(1), obj.Id) -} - -func TestApiDeleteUserHandler(t *testing.T) { - rr, err := httpRequestAPI(t, "DELETE", "/api/users/1", nil) - assert.Nil(t, err) - body := rr.Body.String() - var obj apiResponse - formatJSON(body, &obj) - assert.Equal(t, 200, rr.Code) - assert.Equal(t, "delete", obj.Method) - assert.Equal(t, "success", obj.Status) -} - -func TestApiServiceDataHandler(t *testing.T) { - grouping := []string{"minute", "hour", "day", "week"} - for _, g := range grouping { - params := "?start=0&end=999999999999&group=" + g - rr, err := httpRequestAPI(t, "GET", "/api/services/5/data"+params, nil) - assert.Nil(t, err) - body := rr.Body.String() - var obj core.DateScanObj - formatJSON(body, &obj) - assert.Equal(t, 200, rr.Code) - assert.NotZero(t, len(obj.Array)) - } -} - -func TestApiRenewHandler(t *testing.T) { - api := core.CoreApp.ApiKey - secret := core.CoreApp.ApiSecret - rr, err := httpRequestAPI(t, "GET", "/api/renew", nil) - assert.Nil(t, err) - body := rr.Body.String() - var obj apiResponse - formatJSON(body, &obj) - assert.Equal(t, 303, rr.Code) - assert.NotEqual(t, api, core.CoreApp.ApiKey) - assert.NotEqual(t, secret, core.CoreApp.ApiSecret) -} - -func TestApiCheckinHandler(t *testing.T) { - t.SkipNow() - service := core.SelectService(1) - checkin := service.AllCheckins() - rr, err := httpRequestAPI(t, "GET", "/api/checkin/"+checkin[0].ApiKey, nil) - assert.Nil(t, err) - body := rr.Body.String() - var obj apiResponse - formatJSON(body, &obj) - assert.Equal(t, 200, rr.Code) -} - -func TestApiCreateCheckinHandler(t *testing.T) { - data := `{ - "service_id": 1, - "name": "New API Checkin", - "interval": 60, - "grace": 30}` - rr, err := httpRequestAPI(t, "POST", "/api/checkin", strings.NewReader(data)) - assert.Nil(t, err) - body := rr.Body.String() - var obj apiResponse - formatJSON(body, &obj) - t.Log(body) - assert.Equal(t, 200, rr.Code) - assert.Equal(t, "create", obj.Method) - assert.Equal(t, "success", obj.Status) - assert.Equal(t, int64(1), obj.Id) -} - -func TestApiAllCheckinsHandler(t *testing.T) { - rr, err := httpRequestAPI(t, "GET", "/api/checkins", nil) - assert.Nil(t, err) - body := rr.Body.String() - assert.Equal(t, 200, rr.Code) - var obj []types.Checkin - formatJSON(body, &obj) - t.Log(body) - assert.Equal(t, int(1), len(obj)) - assert.Equal(t, int64(1), obj[0].Id) - assert.Equal(t, int64(1), obj[0].ServiceId) - assert.Equal(t, "New API Checkin", obj[0].Name) -} - -func httpRequestAPI(t *testing.T, method, url string, body io.Reader) (*httptest.ResponseRecorder, error) { - req, err := http.NewRequest(method, url, body) - if err != nil { - return nil, err - } - if body != nil { - req.Header.Set("Content-Type", "application/json") - } - req.Header.Set("Authorization", core.CoreApp.ApiSecret) - rr := httptest.NewRecorder() - Router().ServeHTTP(rr, req) - assert.Nil(t, err) - return rr, err -} diff --git a/handlers/routes.go b/handlers/routes.go index c2cd6075..7ac87a7f 100644 --- a/handlers/routes.go +++ b/handlers/routes.go @@ -40,17 +40,18 @@ func Router() *mux.Router { indexHandler := http.FileServer(http.Dir(dir + "/assets/")) r.PathPrefix("/css/").Handler(http.StripPrefix("/css/", http.FileServer(http.Dir(dir+"/assets/css")))) r.PathPrefix("/font/").Handler(http.StripPrefix("/font/", http.FileServer(http.Dir(dir+"/assets/font")))) + r.PathPrefix("/js/").Handler(http.StripPrefix("/js/", http.FileServer(http.Dir(dir+"/assets/js")))) r.PathPrefix("/robots.txt").Handler(indexHandler) r.PathPrefix("/favicon.ico").Handler(indexHandler) r.PathPrefix("/banner.png").Handler(indexHandler) } else { r.PathPrefix("/css/").Handler(http.StripPrefix("/css/", http.FileServer(source.CssBox.HTTPBox()))) r.PathPrefix("/font/").Handler(http.StripPrefix("/font/", http.FileServer(source.FontBox.HTTPBox()))) + r.PathPrefix("/js/").Handler(http.StripPrefix("/js/", http.FileServer(source.JsBox.HTTPBox()))) r.PathPrefix("/robots.txt").Handler(http.FileServer(source.TmplBox.HTTPBox())) r.PathPrefix("/favicon.ico").Handler(http.FileServer(source.TmplBox.HTTPBox())) r.PathPrefix("/banner.png").Handler(http.FileServer(source.TmplBox.HTTPBox())) } - r.PathPrefix("/js/").Handler(http.StripPrefix("/js/", http.FileServer(source.JsBox.HTTPBox()))) r.Handle("/charts.js", http.HandlerFunc(renderServiceChartsHandler)) r.Handle("/setup", http.HandlerFunc(setupHandler)).Methods("GET") r.Handle("/setup", http.HandlerFunc(processSetupHandler)).Methods("POST") diff --git a/handlers/services_test.go b/handlers/services_test.go new file mode 100644 index 00000000..f6a4d77a --- /dev/null +++ b/handlers/services_test.go @@ -0,0 +1,35 @@ +package handlers + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestServiceRoutes(t *testing.T) { + tests := []HTTPTest{ + { + Name: "Services", + URL: "/services", + Method: "GET", + ExpectedStatus: 200, + ExpectedContains: []string{`<title>Statping | Services</title>`}, + }, + { + Name: "Services 2", + URL: "/service/2", + Method: "GET", + ExpectedStatus: 200, + ExpectedContains: []string{`<title>Statping Github Status</title>`}, + }} + + for _, v := range tests { + t.Run(v.Name, func(t *testing.T) { + body, t, err := RunHTTPTest(v, t) + assert.Nil(t, err) + if err != nil { + t.FailNow() + } + t.Logf("Test %v got: %v\n", v.Name, string(body)) + }) + } +} diff --git a/handlers/users_test.go b/handlers/users_test.go new file mode 100644 index 00000000..3a1b6a5f --- /dev/null +++ b/handlers/users_test.go @@ -0,0 +1,35 @@ +package handlers + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestUserRoutes(t *testing.T) { + tests := []HTTPTest{ + { + Name: "Users", + URL: "/users", + Method: "GET", + ExpectedStatus: 200, + ExpectedContains: []string{`<title>Statping | Users</title>`}, + }, + { + Name: "User 2", + URL: "/user/2", + Method: "GET", + ExpectedStatus: 200, + ExpectedContains: []string{`<title>Statping | adminuser2</title>`}, + }} + + for _, v := range tests { + t.Run(v.Name, func(t *testing.T) { + body, t, err := RunHTTPTest(v, t) + assert.Nil(t, err) + if err != nil { + t.FailNow() + } + t.Logf("Test %v got: %v\n", v.Name, string(body)) + }) + } +} diff --git a/source/css/base.css b/source/css/base.css index da1b43c3..e10f4f01 100644 --- a/source/css/base.css +++ b/source/css/base.css @@ -7,54 +7,66 @@ /* Mobile Service Container */ HTML, BODY { background-color: #fcfcfc; - padding-bottom: 10px; } + padding-bottom: 10px; +} .container { padding-top: 20px; padding-bottom: 25px; max-width: 860px; - box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; } + box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; +} .header-title { - color: #464646; } + color: #464646; +} .header-desc { - color: #939393; } + color: #939393; +} .btn { - border-radius: 0.2rem; } + border-radius: 0.2rem; +} .online_list .badge { - margin-top: 0.2rem; } + margin-top: 0.2rem; +} .navbar { - margin-bottom: 30px; } + margin-bottom: 30px; +} .btn-sm { line-height: 1.3; - font-size: 0.75rem; } + font-size: 0.75rem; +} .view_service_btn { position: absolute; bottom: -40px; - right: 40px; } + right: 40px; +} .service_lower_info { position: absolute; bottom: -40px; left: 40px; color: #d1ffca; - font-size: 0.85rem; } + font-size: 0.85rem; +} .lg_number { font-size: 2.3rem; font-weight: bold; display: block; - color: #4f4f4f; } + color: #4f4f4f; +} .stats_area { text-align: center; - color: #a5a5a5; } + color: #a5a5a5; +} .lower_canvas { height: 3.4rem; @@ -62,112 +74,144 @@ HTML, BODY { background-color: #48d338; padding: 15px 10px; margin-left: 0px !important; - margin-right: 0px !important; } + margin-right: 0px !important; +} .lower_canvas SPAN { font-size: 1rem; - color: #fff; } + color: #fff; +} .footer { text-decoration: none; - margin-top: 20px; } + margin-top: 20px; +} .footer A { color: #8d8d8d; - text-decoration: none; } + text-decoration: none; +} .footer A:HOVER { - color: #6d6d6d; } + color: #6d6d6d; +} .badge { color: white; - border-radius: 0.2rem; } + border-radius: 0.2rem; +} .btn-group { - height: 25px; } - .btn-group A { - padding: 0.1rem .75rem; - font-size: 0.8rem; } + height: 25px; +} +.btn-group A { + padding: 0.1rem 0.75rem; + font-size: 0.8rem; +} .card-body .badge { - color: #fff; } + color: #fff; +} .nav-pills .nav-link { - border-radius: 0.2rem; } + border-radius: 0.2rem; +} .form-control { - border-radius: 0.2rem; } + border-radius: 0.2rem; +} .card { background-color: #ffffff; - border: 1px solid rgba(0, 0, 0, 0.125); } + border: 1px solid rgba(0, 0, 0, 0.125); +} .card-body { - overflow: hidden; } + overflow: hidden; +} .card-body H4 A { color: #444444; - text-decoration: none; } + text-decoration: none; +} .chart-container { position: relative; height: 170px; - width: 100%; } + width: 100%; +} .service-chart-container { position: relative; height: 400px; - width: 100%; } + width: 100%; +} .btn-primary { background-color: #3e9bff; border-color: #006fe6; - color: white; } - .btn-primary.dyn-dark { - background-color: #32a825 !important; - border-color: #2c9320 !important; } - .btn-primary.dyn-light { - background-color: #75de69 !important; - border-color: #88e37e !important; } + color: white; +} +.btn-primary.dyn-dark { + background-color: #32a825 !important; + border-color: #2c9320 !important; +} +.btn-primary.dyn-light { + background-color: #75de69 !important; + border-color: #88e37e !important; +} .btn-success { - background-color: #47d337; } - .btn-success.dyn-dark { - background-color: #32a825 !important; - border-color: #2c9320 !important; } - .btn-success.dyn-light { - background-color: #75de69 !important; - border-color: #88e37e !important; } + background-color: #47d337; +} +.btn-success.dyn-dark { + background-color: #32a825 !important; + border-color: #2c9320 !important; +} +.btn-success.dyn-light { + background-color: #75de69 !important; + border-color: #88e37e !important; +} .btn-danger { - background-color: #dd3545; } - .btn-danger.dyn-dark { - background-color: #b61f2d !important; - border-color: #a01b28 !important; } - .btn-danger.dyn-light { - background-color: #e66975 !important; - border-color: #e97f89 !important; } + background-color: #dd3545; +} +.btn-danger.dyn-dark { + background-color: #b61f2d !important; + border-color: #a01b28 !important; +} +.btn-danger.dyn-light { + background-color: #e66975 !important; + border-color: #e97f89 !important; +} .bg-success { - background-color: #47d337 !important; } + background-color: #47d337 !important; +} .bg-danger { - background-color: #dd3545 !important; } + background-color: #dd3545 !important; +} .bg-success .dyn-dark { - background-color: #35b027 !important; } + background-color: #35b027 !important; +} .bg-danger .dyn-dark { - background-color: #bf202f !important; } + background-color: #bf202f !important; +} .nav-pills .nav-link.active, .nav-pills .show > .nav-link { - background-color: #13a00d; } + background-color: #13a00d; +} .nav-pills A { - color: #424242; } + color: #424242; +} .nav-pills I { - margin-right: 10px; } + margin-right: 10px; +} .CodeMirror { /* Bootstrap Settings */ @@ -187,23 +231,26 @@ HTML, BODY { border: 1px solid #ccc; border-radius: 4px; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); - transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; /* Code Mirror Settings */ font-family: monospace; position: relative; overflow: hidden; - height: 80vh; } + height: 80vh; +} .CodeMirror-focused { /* Bootstrap Settings */ border-color: #66afe9; outline: 0; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); - transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; } + transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; +} .switch { font-size: 1rem; - position: relative; } + position: relative; +} .switch input { position: absolute; @@ -214,7 +261,8 @@ HTML, BODY { clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden; - padding: 0; } + padding: 0; +} .switch input + label { position: relative; @@ -227,23 +275,26 @@ HTML, BODY { outline: none; user-select: none; vertical-align: middle; - text-indent: calc(calc(calc(2.375rem * .8) * 2) + .5rem); } + text-indent: calc(calc(calc(2.375rem * .8) * 2) + .5rem); +} .switch input + label::before, .switch input + label::after { - content: ''; + content: ""; position: absolute; top: 0; left: 0; width: calc(calc(2.375rem * .8) * 2); bottom: 0; - display: block; } + display: block; +} .switch input + label::before { right: 0; background-color: #dee2e6; border-radius: calc(2.375rem * .8); - transition: 0.2s all; } + transition: 0.2s all; +} .switch input + label::after { top: 2px; @@ -252,120 +303,154 @@ HTML, BODY { height: calc(calc(2.375rem * .8) - calc(2px * 2)); border-radius: 50%; background-color: white; - transition: 0.2s all; } + transition: 0.2s all; +} .switch input:checked + label::before { - background-color: #08d; } + background-color: #08d; +} .switch input:checked + label::after { - margin-left: calc(2.375rem * .8); } + margin-left: calc(2.375rem * .8); +} .switch input:focus + label::before { outline: none; - box-shadow: 0 0 0 0.2rem rgba(0, 136, 221, 0.25); } + box-shadow: 0 0 0 0.2rem rgba(0, 136, 221, 0.25); +} .switch input:disabled + label { color: #868e96; - cursor: not-allowed; } + cursor: not-allowed; +} .switch input:disabled + label::before { - background-color: #e9ecef; } + background-color: #e9ecef; +} .switch.switch-sm { - font-size: 0.875rem; } + font-size: 0.875rem; +} .switch.switch-sm input + label { min-width: calc(calc(1.9375rem * .8) * 2); height: calc(1.9375rem * .8); line-height: calc(1.9375rem * .8); - text-indent: calc(calc(calc(1.9375rem * .8) * 2) + .5rem); } + text-indent: calc(calc(calc(1.9375rem * .8) * 2) + .5rem); +} .switch.switch-sm input + label::before { - width: calc(calc(1.9375rem * .8) * 2); } + width: calc(calc(1.9375rem * .8) * 2); +} .switch.switch-sm input + label::after { width: calc(calc(1.9375rem * .8) - calc(2px * 2)); - height: calc(calc(1.9375rem * .8) - calc(2px * 2)); } + height: calc(calc(1.9375rem * .8) - calc(2px * 2)); +} .switch.switch-sm input:checked + label::after { - margin-left: calc(1.9375rem * .8); } + margin-left: calc(1.9375rem * .8); +} .switch.switch-lg { - font-size: 1.25rem; } + font-size: 1.25rem; +} .switch.switch-lg input + label { min-width: calc(calc(3rem * .8) * 2); height: calc(3rem * .8); line-height: calc(3rem * .8); - text-indent: calc(calc(calc(3rem * .8) * 2) + .5rem); } + text-indent: calc(calc(calc(3rem * .8) * 2) + .5rem); +} .switch.switch-lg input + label::before { - width: calc(calc(3rem * .8) * 2); } + width: calc(calc(3rem * .8) * 2); +} .switch.switch-lg input + label::after { width: calc(calc(3rem * .8) - calc(2px * 2)); - height: calc(calc(3rem * .8) - calc(2px * 2)); } + height: calc(calc(3rem * .8) - calc(2px * 2)); +} .switch.switch-lg input:checked + label::after { - margin-left: calc(3rem * .8); } + margin-left: calc(3rem * .8); +} .switch + .switch { - margin-left: 1rem; } + margin-left: 1rem; +} @keyframes pulse_animation { 0% { - transform: scale(1); } + transform: scale(1); + } 30% { - transform: scale(1); } + transform: scale(1); + } 40% { - transform: scale(1.02); } + transform: scale(1.02); + } 50% { - transform: scale(1); } + transform: scale(1); + } 60% { - transform: scale(1); } + transform: scale(1); + } 70% { - transform: scale(1.05); } + transform: scale(1.05); + } 80% { - transform: scale(1); } + transform: scale(1); + } 100% { - transform: scale(1); } } + transform: scale(1); + } +} .pulse { animation-name: pulse_animation; animation-duration: 1500ms; transform-origin: 70% 70%; animation-iteration-count: infinite; - animation-timing-function: linear; } + animation-timing-function: linear; +} @keyframes glow-grow { 0% { opacity: 0; - transform: scale(1); } + transform: scale(1); + } 80% { - opacity: 1; } + opacity: 1; + } 100% { transform: scale(2); - opacity: 0; } } + opacity: 0; + } +} .pulse-glow { animation-name: glow-grown; animation-duration: 100ms; transform-origin: 70% 30%; animation-iteration-count: infinite; - animation-timing-function: linear; } + animation-timing-function: linear; +} .pulse-glow:before, .pulse-glow:after { position: absolute; - content: ''; + content: ""; height: 0.5rem; width: 1.75rem; top: 1.2rem; right: 2.15rem; border-radius: 0; box-shadow: 0 0 7px #47d337; - animation: glow-grow 2s ease-out infinite; } + animation: glow-grow 2s ease-out infinite; +} .sortable_drag { - background-color: #0000000f; } + background-color: #0000000f; +} .drag_icon { cursor: move; @@ -379,106 +464,132 @@ HTML, BODY { margin-right: 5px; margin-left: -10px; text-align: center; - color: #b1b1b1; } + color: #b1b1b1; +} /* (Optional) Apply a "closed-hand" cursor during drag operation. */ .drag_icon:active { cursor: grabbing; cursor: -moz-grabbing; - cursor: -webkit-grabbing; } + cursor: -webkit-grabbing; +} .switch_btn { float: right; margin: -1px 0px 0px 0px; - display: block; } + display: block; +} #start_container { position: absolute; z-index: 99999; - margin-top: 20px; } + margin-top: 20px; +} #end_container { position: absolute; z-index: 99999; margin-top: 20px; - right: 0; } + right: 0; +} .pointer { - cursor: pointer; } + cursor: pointer; +} .jumbotron { - background-color: white; } + background-color: white; +} @media (max-width: 767px) { HTML, BODY { - background-color: #fcfcfc; } + background-color: #fcfcfc; + } .sm-container { margin-top: 0px !important; - padding: 0 !important; } + padding: 0 !important; + } .list-group-item H5 { - font-size: 0.9rem; } + font-size: 0.9rem; + } .container { padding: 0px !important; - padding-top: 15px !important; } + padding-top: 15px !important; + } .group_header { - margin-left: 15px; } + margin-left: 15px; + } .navbar { margin-left: 0px; margin-top: 0px; width: 100%; - margin-bottom: 0; } + margin-bottom: 0; + } .btn-sm { line-height: 0.9rem; - font-size: 0.65rem; } + font-size: 0.65rem; + } .full-col-12 { padding-left: 0px; - padding-right: 0px; } + padding-right: 0px; + } .card { border: 0; border-radius: 0rem; padding: 0; - background-color: #ffffff; } + background-color: #ffffff; + } .card-body { font-size: 10pt; - padding: 0px 10px; } + padding: 0px 10px; + } .lg_number { - font-size: 7.8vw; } + font-size: 7.8vw; + } .stats_area { margin-top: 1.5rem !important; - margin-bottom: 1.5rem !important; } + margin-bottom: 1.5rem !important; + } .stats_area .col-4 { padding-left: 0; padding-right: 0; - font-size: 0.6rem; } + font-size: 0.6rem; + } .list-group-item { border-top: 1px solid #e4e4e4; - border: 0px; } + border: 0px; + } .list-group-item:first-child { border-top-left-radius: 0; - border-top-right-radius: 0; } + border-top-right-radius: 0; + } .list-group-item:last-child { border-bottom-right-radius: 0; - border-bottom-left-radius: 0; } + border-bottom-left-radius: 0; + } .list-group-item P { - font-size: 0.7rem; } + font-size: 0.7rem; + } .service-chart-container { - height: 200px; } } + height: 200px; + } +} /*# sourceMappingURL=base.css.map */ diff --git a/source/source_test.go b/source/source_test.go index d07b1488..7604aae7 100644 --- a/source/source_test.go +++ b/source/source_test.go @@ -40,8 +40,8 @@ func TestCore_UsingAssets(t *testing.T) { func TestCreateAssets(t *testing.T) { assert.Nil(t, CreateAllAssets(dir)) assert.True(t, UsingAssets(dir)) - assert.FileExists(t, "../assets/css/base.css") - assert.FileExists(t, "../assets/scss/base.scss") + assert.FileExists(t, "./assets/css/base.css") + assert.FileExists(t, "./assets/scss/base.scss") } func TestCompileSASS(t *testing.T) { diff --git a/source/wiki.go b/source/wiki.go index 4ede29c3..58fa01da 100644 --- a/source/wiki.go +++ b/source/wiki.go @@ -1,6 +1,6 @@ // Code generated by go generate; DO NOT EDIT. // This file was generated by robots at -// 2019-01-10 12:48:42.676069 -0800 PST m=+0.785435129 +// 2019-01-14 17:21:53.045328 -0800 PST m=+0.787121391 // // This contains the most recently Markdown source for the Statping Wiki. package source diff --git a/utils/utils_test.go b/utils/utils_test.go index 7bf71155..4cb5501f 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -31,11 +31,7 @@ func TestCreateLog(t *testing.T) { func TestInitLogs(t *testing.T) { assert.Nil(t, InitLogs()) - assert.FileExists(t, "../logs/statup.log") -} - -func TestFileExists(t *testing.T) { - assert.True(t, FileExists("../logs/statup.log")) + assert.FileExists(t, "./logs/statup.log") } func TestDir(t *testing.T) { diff --git a/version.txt b/version.txt index 63106728..9d8e01cc 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.80.36 +0.80.37