You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
statping/types/services/services_test.go

619 lines
15 KiB

package services
import (
"context"
"crypto/tls"
"github.com/gorilla/mux"
"github.com/statping/statping/database"
"github.com/statping/statping/types/checkins"
"github.com/statping/statping/types/failures"
"github.com/statping/statping/types/hits"
"github.com/statping/statping/types/incidents"
"github.com/statping/statping/types/messages"
"github.com/statping/statping/types/notifications"
"github.com/statping/statping/types/null"
"github.com/statping/statping/utils"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
pb "google.golang.org/grpc/examples/route_guide/routeguide"
"net"
"net/http"
"testing"
"time"
)
var example = &Service{
Name: "Example Service",
Domain: "https://statping.com",
ExpectedStatus: 200,
Interval: 30,
Type: "http",
Method: "GET",
Timeout: 5,
Order: 1,
VerifySSL: null.NewNullBool(true),
Public: null.NewNullBool(true),
GroupId: 1,
Permalink: null.NewNullString("statping"),
LastCheck: utils.Now().Add(-5 * time.Second),
LastOffline: utils.Now().Add(-5 * time.Second),
LastOnline: utils.Now().Add(-60 * time.Second),
}
var hit1 = &hits.Hit{
Service: 1,
Latency: 123456,
PingTime: 123456,
CreatedAt: utils.Now().Add(-120 * time.Second),
}
var hit2 = &hits.Hit{
Service: 1,
Latency: 123456,
PingTime: 123456,
CreatedAt: utils.Now().Add(-60 * time.Second),
}
var hit3 = &hits.Hit{
Service: 1,
Latency: 123456,
PingTime: 123456,
CreatedAt: utils.Now().Add(-30 * time.Second),
}
var exmapleCheckin = &checkins.Checkin{
ServiceId: 1,
Name: "Example Checkin",
Interval: 3,
ApiKey: "wdededede",
}
var fail1 = &failures.Failure{
Issue: "example not found",
ErrorCode: 404,
Service: 1,
PingTime: 123456,
CreatedAt: utils.Now().Add(-160 * time.Second),
}
var fail2 = &failures.Failure{
Issue: "example 2 not found",
ErrorCode: 500,
Service: 1,
PingTime: 123456,
CreatedAt: utils.Now().Add(-5 * time.Second),
}
var incident1 = &incidents.Incident{
Title: "Theres something going on",
Description: "this is an example",
ServiceId: 1,
CreatedAt: utils.Now().Add(-30 * time.Second),
}
var incidentUpdate1 = &incidents.IncidentUpdate{
IncidentId: 1,
Message: "This is an update",
Type: "pending",
CreatedAt: utils.Now().Add(-5 * time.Second),
}
var message1 = &messages.Message{
Title: "Example Message",
Description: "Used for testing",
StartOn: utils.Now().Add(15 * time.Minute),
EndOn: utils.Now().Add(30 * time.Minute),
ServiceId: 1,
NotifyUsers: null.NewNullBool(false),
NotifyMethod: "",
NotifyBefore: null.NewNullInt64(0),
NotifyBeforeScale: "",
CreatedAt: utils.Now(),
UpdatedAt: utils.Now(),
}
type exampleGRPC struct {
pb.UnimplementedRouteGuideServer
}
func (s *exampleGRPC) GetFeature(ctx context.Context, point *pb.Point) (*pb.Feature, error) {
return &pb.Feature{Location: point}, nil
}
func TestStartExampleEndpoints(t *testing.T) {
startupDb(t)
// root CA for Linux: /etc/ssl/certs/ca-certificates.crt
// root CA for MacOSX: /opt/local/share/curl/curl-ca-bundle.crt
tlsCert := utils.Params.GetString("STATPING_DIR") + "/cert.pem"
tlsCertKey := utils.Params.GetString("STATPING_DIR") + "/key.pem"
require.FileExists(t, tlsCert)
require.FileExists(t, tlsCertKey)
h := func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("ok"))
}
r := mux.NewRouter()
r.HandleFunc("/", h)
// start example HTTP server
go func(t *testing.T) {
require.Nil(t, http.ListenAndServe(":15000", r))
}(t)
// start example TLS HTTP server
go func(t *testing.T) {
require.Nil(t, http.ListenAndServeTLS(":15001", tlsCert, tlsCertKey, r))
}(t)
tcpHandle := func(conn net.Conn) {
defer conn.Close()
conn.Write([]byte("ok"))
}
// start TCP server
go func(t *testing.T, hdl func(conn net.Conn)) {
ln, err := net.Listen("tcp", ":15002")
require.Nil(t, err)
for {
conn, err := ln.Accept()
require.Nil(t, err)
go hdl(conn)
}
}(t, tcpHandle)
// start TLS TCP server
go func(t *testing.T, hdl func(conn net.Conn)) {
cer, err := tls.LoadX509KeyPair(tlsCert, tlsCertKey)
require.Nil(t, err)
ln, err := tls.Listen("tcp", ":15003", &tls.Config{Certificates: []tls.Certificate{cer}})
require.Nil(t, err)
for {
conn, err := ln.Accept()
require.Nil(t, err)
go hdl(conn)
}
}(t, tcpHandle)
// start GRPC server
go func(t *testing.T) {
list, err := net.Listen("tcp", ":15004")
require.Nil(t, err)
grpcServer := grpc.NewServer()
pb.RegisterRouteGuideServer(grpcServer, &exampleGRPC{})
require.Nil(t, grpcServer.Serve(list))
}(t)
time.Sleep(15 * time.Second)
}
func startupDb(t *testing.T) {
err := utils.InitLogs()
require.Nil(t, err)
db, err := database.OpenTester()
require.Nil(t, err)
db.AutoMigrate(&Service{}, &notifications.Notification{}, &messages.Message{}, &hits.Hit{}, &checkins.Checkin{}, &checkins.CheckinHit{}, &failures.Failure{}, &incidents.Incident{}, &incidents.IncidentUpdate{})
checkins.SetDB(db)
failures.SetDB(db)
incidents.SetDB(db)
notifications.SetDB(db)
messages.SetDB(db)
hits.SetDB(db)
SetDB(db)
db.Create(&example)
db.Create(&hit1)
db.Create(&hit2)
db.Create(&hit3)
db.Create(&exmapleCheckin)
db.Create(&fail1)
db.Create(&fail2)
db.Create(&incident1)
db.Create(&incidentUpdate1)
db.Create(&notification.Notification)
db.Create(&message1)
}
func TestServices(t *testing.T) {
tlsCert := utils.Params.GetString("STATPING_DIR") + "/cert.pem"
tlsCertKey := utils.Params.GetString("STATPING_DIR") + "/key.pem"
t.Run("Test Find service", func(t *testing.T) {
item, err := Find(1)
require.Nil(t, err)
assert.Equal(t, "Example Service", item.Name)
assert.NotZero(t, item.LastOnline)
assert.NotZero(t, item.LastOffline)
assert.NotZero(t, item.LastCheck)
})
t.Run("Test HTTP Check", func(t *testing.T) {
e := &Service{
Name: "Example HTTP",
Domain: "http://localhost:15000",
ExpectedStatus: 200,
Type: "http",
Method: "GET",
Timeout: 5,
VerifySSL: null.NewNullBool(false),
}
e, err := CheckHttp(e, false)
require.Nil(t, err)
assert.True(t, e.Online)
assert.False(t, e.LastCheck.IsZero())
assert.NotEqual(t, 0, e.PingTime)
assert.NotEqual(t, 0, e.Latency)
})
t.Run("Test Load TLS Certificates", func(t *testing.T) {
e := &Service{
Name: "Example TLS",
Domain: "http://localhost:15001",
ExpectedStatus: 200,
Type: "http",
Method: "GET",
Timeout: 5,
VerifySSL: null.NewNullBool(false),
TLSCert: null.NewNullString(tlsCert),
TLSCertKey: null.NewNullString(tlsCertKey),
}
customTLS, err := e.LoadTLSCert()
require.Nil(t, err)
require.NotNil(t, customTLS)
assert.Nil(t, customTLS.RootCAs)
assert.NotNil(t, customTLS.Certificates)
assert.Equal(t, 1, len(customTLS.Certificates))
})
t.Run("Test TLS HTTP Check", func(t *testing.T) {
e := &Service{
Name: "Example TLS HTTP",
Domain: "https://localhost:15001",
ExpectedStatus: 200,
Type: "http",
Method: "GET",
Timeout: 15,
VerifySSL: null.NewNullBool(false),
TLSCert: null.NewNullString(tlsCert),
TLSCertKey: null.NewNullString(tlsCertKey),
}
e, err := CheckHttp(e, false)
require.Nil(t, err)
assert.True(t, e.Online)
assert.False(t, e.LastCheck.IsZero())
assert.NotEqual(t, 0, e.PingTime)
assert.NotEqual(t, 0, e.Latency)
})
t.Run("Test TCP Check", func(t *testing.T) {
e := &Service{
Name: "Example TCP",
Domain: "localhost",
Port: 15002,
Type: "tcp",
Timeout: 5,
}
e, err := CheckTcp(e, false)
require.Nil(t, err)
assert.True(t, e.Online)
assert.False(t, e.LastCheck.IsZero())
assert.NotEqual(t, 0, e.PingTime)
assert.NotEqual(t, 0, e.Latency)
})
t.Run("Test TLS TCP Check", func(t *testing.T) {
e := &Service{
Name: "Example TLS TCP",
Domain: "localhost",
Port: 15003,
Type: "tcp",
Timeout: 15,
TLSCert: null.NewNullString(tlsCert),
TLSCertKey: null.NewNullString(tlsCertKey),
}
e, err := CheckTcp(e, false)
require.Nil(t, err)
assert.True(t, e.Online)
assert.False(t, e.LastCheck.IsZero())
assert.NotEqual(t, 0, e.PingTime)
assert.NotEqual(t, 0, e.Latency)
})
t.Run("Test UDP Check", func(t *testing.T) {
e := &Service{
Name: "Example UDP",
Domain: "localhost",
Port: 15003,
Type: "udp",
Timeout: 5,
}
e, err := CheckTcp(e, false)
require.Nil(t, err)
assert.True(t, e.Online)
assert.False(t, e.LastCheck.IsZero())
assert.NotEqual(t, 0, e.PingTime)
assert.NotEqual(t, 0, e.Latency)
})
t.Run("Test gRPC Check", func(t *testing.T) {
e := &Service{
Name: "Example gRPC",
Domain: "localhost",
Port: 15004,
Type: "grpc",
Timeout: 5,
}
e, err := CheckGrpc(e, false)
require.Nil(t, err)
assert.True(t, e.Online)
assert.False(t, e.LastCheck.IsZero())
assert.NotEqual(t, 0, e.PingTime)
assert.NotEqual(t, 0, e.Latency)
})
t.Run("Test ICMP Check", func(t *testing.T) {
e := &Service{
Name: "Example ICMP",
Domain: "localhost",
Type: "icmp",
Timeout: 5,
}
e, err := CheckIcmp(e, false)
require.Nil(t, err)
assert.True(t, e.Online)
assert.False(t, e.LastCheck.IsZero())
assert.NotEqual(t, 0, e.PingTime)
assert.NotEqual(t, 0, e.Latency)
})
t.Run("Test All", func(t *testing.T) {
items := All()
assert.Len(t, items, 1)
})
t.Run("Test Checkins", func(t *testing.T) {
item, err := Find(1)
require.Nil(t, err)
assert.Len(t, item.Checkins, 1)
})
t.Run("Test All Hits", func(t *testing.T) {
item, err := Find(1)
require.Nil(t, err)
assert.Len(t, item.AllHits().List(), 3)
assert.Equal(t, 3, item.AllHits().Count())
})
t.Run("Test All Failures", func(t *testing.T) {
item, err := Find(1)
require.Nil(t, err)
assert.Len(t, item.AllFailures().List(), 2)
assert.Equal(t, 2, item.AllFailures().Count())
})
t.Run("Test First Hit", func(t *testing.T) {
item, err := Find(1)
require.Nil(t, err)
hit := item.FirstHit()
assert.Equal(t, int64(1), hit.Id)
})
t.Run("Test Last Hit", func(t *testing.T) {
item, err := Find(1)
require.Nil(t, err)
hit := item.AllHits().Last()
assert.Equal(t, int64(3), hit.Id)
})
t.Run("Test Last Failure", func(t *testing.T) {
item, err := Find(1)
require.Nil(t, err)
fail := item.AllFailures().Last()
assert.Equal(t, int64(2), fail.Id)
})
t.Run("Test Duration", func(t *testing.T) {
item, err := Find(1)
require.Nil(t, err)
assert.Equal(t, float64(30), item.Duration().Seconds())
})
t.Run("Test Count Hits", func(t *testing.T) {
item, err := Find(1)
require.Nil(t, err)
count := item.AllHits().Count()
assert.NotZero(t, count)
})
t.Run("Test Average Time", func(t *testing.T) {
item, err := Find(1)
require.Nil(t, err)
assert.Equal(t, int64(123456), item.AvgTime())
})
t.Run("Test Hits Since", func(t *testing.T) {
item, err := Find(1)
require.Nil(t, err)
count := item.HitsSince(utils.Now().Add(-60 * time.Second))
assert.Equal(t, 1, count.Count())
count = item.HitsSince(utils.Now().Add(-360 * time.Second))
assert.Equal(t, 3, count.Count())
})
t.Run("Test Service Running", func(t *testing.T) {
item, err := Find(1)
require.Nil(t, err)
assert.False(t, item.IsRunning())
})
t.Run("Test Online Percent", func(t *testing.T) {
item, err := Find(1)
require.Nil(t, err)
amount := item.OnlineDaysPercent(1)
assert.Equal(t, float32(33.33), amount)
})
t.Run("Test Downtime", func(t *testing.T) {
item, err := Find(1)
require.Nil(t, err)
amount := item.Downtime().Seconds()
assert.GreaterOrEqual(t, int64(amount), int64(75))
})
t.Run("Test Failures Since", func(t *testing.T) {
item, err := Find(1)
require.Nil(t, err)
count := item.FailuresSince(utils.Now().Add(-30 * time.Second))
assert.Equal(t, 1, count.Count())
count = item.FailuresSince(utils.Now().Add(-180 * time.Second))
assert.Equal(t, 2, count.Count())
})
t.Run("Test Create", func(t *testing.T) {
example := &Service{
Name: "Example Service 2",
Domain: "https://slack.statping.com",
ExpectedStatus: 200,
Interval: 10,
Type: "http",
Method: "GET",
Timeout: 5,
Order: 3,
VerifySSL: null.NewNullBool(true),
Public: null.NewNullBool(false),
GroupId: 1,
Permalink: null.NewNullString("statping2"),
}
err := example.Create()
require.Nil(t, err)
assert.NotZero(t, example.Id)
assert.Equal(t, "Example Service 2", example.Name)
assert.False(t, example.Public.Bool)
assert.NotZero(t, example.CreatedAt)
assert.Equal(t, int64(2), example.Id)
assert.Len(t, allServices, 2)
})
t.Run("Test Update Service", func(t *testing.T) {
item, err := Find(1)
require.Nil(t, err)
item.Name = "Updated Service"
item.Order = 1
err = item.Update()
require.Nil(t, err)
assert.Equal(t, int64(1), item.Id)
assert.Equal(t, "Updated Service", item.Name)
})
t.Run("Test In Order", func(t *testing.T) {
inOrder := AllInOrder()
assert.Len(t, inOrder, 2)
assert.Equal(t, "Updated Service", inOrder[0].Name)
assert.Equal(t, "Example Service 2", inOrder[1].Name)
})
t.Run("Test Delete", func(t *testing.T) {
all := All()
assert.Len(t, all, 2)
item, err := Find(1)
require.Nil(t, err)
assert.Equal(t, int64(1), item.Id)
err = item.Delete()
require.Nil(t, err)
// after deleted service, make sure checkins, failures, hits, and incidents are also delete
assert.Len(t, item.AllFailures().List(), 0)
assert.Len(t, item.AllHits().List(), 0)
checkin := item.Checkins
assert.Len(t, checkin, 0)
for _, c := range checkin {
assert.Len(t, c.Failures().List(), 0)
assert.Len(t, c.Hits(), 0)
assert.False(t, c.IsRunning())
}
inc := item.Incidents
assert.Len(t, inc, 0)
for _, i := range inc {
assert.Len(t, i.Updates, 0)
}
all = All()
assert.Len(t, all, 1)
})
t.Run("Test Load services.yml", func(t *testing.T) {
file := `x-tcpservice: &tcpservice
type: tcp
check_interval: 60
timeout: 15
allow_notifications: true
notify_after: 0
notify_all_changes: true
public: true
redirect: true
x-httpservice: &httpservice
type: http
method: GET
check_interval: 45
timeout: 10
expected_status: 200
allow_notifications: true
notify_after: 2
notify_all_changes: true
public: true
redirect: true
services:
- name: Statping Demo
domain: https://demo.statping.com
<<: *httpservice
- name: Portainer
domain: portainer
port: 9000
<<: *tcpservice
- name: Statping Github
domain: https://github.com/statping/statping
<<: *httpservice`
err := utils.SaveFile(utils.Directory+"/services.yml", []byte(file))
require.Nil(t, err)
assert.FileExists(t, utils.Directory+"/services.yml")
srvs, err := LoadServicesYaml()
require.Nil(t, err)
require.Equal(t, 3, len(srvs.Services))
assert.Equal(t, "Statping Demo", srvs.Services[0].Name)
assert.Equal(t, 45, srvs.Services[0].Interval)
assert.Equal(t, "https://demo.statping.com", srvs.Services[0].Domain)
err = utils.DeleteFile(utils.Directory + "/services.yml")
require.Nil(t, err)
assert.NoFileExists(t, utils.Directory+"/services.yml")
})
}