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