gatus/storage/store/store_test.go

383 lines
15 KiB
Go
Raw Normal View History

package store
import (
"testing"
"time"
"github.com/TwinProduction/gatus/core"
"github.com/TwinProduction/gatus/storage/store/memory"
"github.com/TwinProduction/gatus/storage/store/paging"
2021-07-18 22:13:05 +02:00
"github.com/TwinProduction/gatus/storage/store/sqlite"
)
var (
firstCondition = core.Condition("[STATUS] == 200")
secondCondition = core.Condition("[RESPONSE_TIME] < 500")
thirdCondition = core.Condition("[CERTIFICATE_EXPIRATION] < 72h")
2021-07-25 01:12:57 +02:00
now = time.Now().Truncate(time.Minute)
testService = core.Service{
Name: "name",
Group: "group",
URL: "https://example.org/what/ever",
Method: "GET",
Body: "body",
Interval: 30 * time.Second,
Conditions: []*core.Condition{&firstCondition, &secondCondition, &thirdCondition},
Alerts: nil,
Insecure: false,
NumberOfFailuresInARow: 0,
NumberOfSuccessesInARow: 0,
}
testSuccessfulResult = core.Result{
Timestamp: now,
Success: true,
Hostname: "example.org",
IP: "127.0.0.1",
HTTPStatus: 200,
Errors: nil,
Connected: true,
Duration: 150 * time.Millisecond,
CertificateExpiration: 10 * time.Hour,
ConditionResults: []*core.ConditionResult{
{
Condition: "[STATUS] == 200",
Success: true,
},
{
Condition: "[RESPONSE_TIME] < 500",
Success: true,
},
{
Condition: "[CERTIFICATE_EXPIRATION] < 72h",
Success: true,
},
},
}
testUnsuccessfulResult = core.Result{
Timestamp: now,
Success: false,
Hostname: "example.org",
IP: "127.0.0.1",
HTTPStatus: 200,
Errors: []string{"error-1", "error-2"},
Connected: true,
Duration: 750 * time.Millisecond,
CertificateExpiration: 10 * time.Hour,
ConditionResults: []*core.ConditionResult{
{
Condition: "[STATUS] == 200",
Success: true,
},
{
Condition: "[RESPONSE_TIME] < 500",
Success: false,
},
{
Condition: "[CERTIFICATE_EXPIRATION] < 72h",
Success: false,
},
},
}
)
2021-07-17 01:24:12 +02:00
type Scenario struct {
Name string
Store Store
}
func initStoresAndBaseScenarios(t *testing.T, testName string) []*Scenario {
memoryStore, err := memory.NewStore("")
if err != nil {
t.Fatal("failed to create store:", err.Error())
}
2021-07-18 22:13:05 +02:00
sqliteStore, err := sqlite.NewStore("sqlite", t.TempDir()+"/"+testName+".db")
if err != nil {
t.Fatal("failed to create store:", err.Error())
}
2021-07-17 01:24:12 +02:00
return []*Scenario{
{
Name: "memory",
Store: memoryStore,
},
{
2021-07-18 22:13:05 +02:00
Name: "sqlite",
Store: sqliteStore,
},
}
2021-07-17 01:24:12 +02:00
}
func cleanUp(scenarios []*Scenario) {
for _, scenario := range scenarios {
scenario.Store.Close()
}
}
func TestStore_GetServiceStatusByKey(t *testing.T) {
scenarios := initStoresAndBaseScenarios(t, "TestStore_GetServiceStatusByKey")
defer cleanUp(scenarios)
firstResult := testSuccessfulResult
firstResult.Timestamp = now.Add(-time.Minute)
secondResult := testUnsuccessfulResult
secondResult.Timestamp = now
for _, scenario := range scenarios {
t.Run(scenario.Name, func(t *testing.T) {
scenario.Store.Insert(&testService, &firstResult)
scenario.Store.Insert(&testService, &secondResult)
serviceStatus := scenario.Store.GetServiceStatusByKey(testService.Key(), paging.NewServiceStatusParams().WithEvents(1, core.MaximumNumberOfEvents).WithResults(1, core.MaximumNumberOfResults).WithUptime())
if serviceStatus == nil {
t.Fatalf("serviceStatus shouldn't have been nil")
}
if serviceStatus.Name != testService.Name {
t.Fatalf("serviceStatus.Name should've been %s, got %s", testService.Name, serviceStatus.Name)
}
if serviceStatus.Group != testService.Group {
t.Fatalf("serviceStatus.Group should've been %s, got %s", testService.Group, serviceStatus.Group)
}
if len(serviceStatus.Results) != 2 {
t.Fatalf("serviceStatus.Results should've had 2 entries")
}
if serviceStatus.Results[0].Timestamp.After(serviceStatus.Results[1].Timestamp) {
t.Error("The result at index 0 should've been older than the result at index 1")
}
if serviceStatus.Uptime == nil {
t.Fatalf("serviceStatus.Uptime shouldn't have been nil")
}
if serviceStatus.Uptime.LastHour != 0.5 {
t.Errorf("serviceStatus.Uptime.LastHour should've been 0.5, got %f", serviceStatus.Uptime.LastHour)
}
if serviceStatus.Uptime.LastTwentyFourHours != 0.5 {
t.Errorf("serviceStatus.Uptime.LastTwentyFourHours should've been 0.5, got %f", serviceStatus.Uptime.LastTwentyFourHours)
}
if serviceStatus.Uptime.LastSevenDays != 0.5 {
t.Errorf("serviceStatus.Uptime.LastSevenDays should've been 0.5, got %f", serviceStatus.Uptime.LastSevenDays)
}
scenario.Store.Clear()
})
}
}
func TestStore_GetServiceStatusForMissingStatusReturnsNil(t *testing.T) {
2021-07-17 01:24:12 +02:00
scenarios := initStoresAndBaseScenarios(t, "TestStore_GetServiceStatusForMissingStatusReturnsNil")
defer cleanUp(scenarios)
for _, scenario := range scenarios {
t.Run(scenario.Name, func(t *testing.T) {
scenario.Store.Insert(&testService, &testSuccessfulResult)
serviceStatus := scenario.Store.GetServiceStatus("nonexistantgroup", "nonexistantname", paging.NewServiceStatusParams().WithEvents(1, core.MaximumNumberOfEvents).WithResults(1, core.MaximumNumberOfResults).WithUptime())
if serviceStatus != nil {
t.Errorf("Returned service status for group '%s' and name '%s' not nil after inserting the service into the store", testService.Group, testService.Name)
}
serviceStatus = scenario.Store.GetServiceStatus(testService.Group, "nonexistantname", paging.NewServiceStatusParams().WithEvents(1, core.MaximumNumberOfEvents).WithResults(1, core.MaximumNumberOfResults).WithUptime())
if serviceStatus != nil {
t.Errorf("Returned service status for group '%s' and name '%s' not nil after inserting the service into the store", testService.Group, "nonexistantname")
}
serviceStatus = scenario.Store.GetServiceStatus("nonexistantgroup", testService.Name, paging.NewServiceStatusParams().WithEvents(1, core.MaximumNumberOfEvents).WithResults(1, core.MaximumNumberOfResults).WithUptime())
if serviceStatus != nil {
t.Errorf("Returned service status for group '%s' and name '%s' not nil after inserting the service into the store", "nonexistantgroup", testService.Name)
}
})
}
}
func TestStore_GetAllServiceStatuses(t *testing.T) {
2021-07-17 01:24:12 +02:00
scenarios := initStoresAndBaseScenarios(t, "TestStore_GetAllServiceStatuses")
defer cleanUp(scenarios)
firstResult := testSuccessfulResult
secondResult := testUnsuccessfulResult
for _, scenario := range scenarios {
t.Run(scenario.Name, func(t *testing.T) {
scenario.Store.Insert(&testService, &firstResult)
scenario.Store.Insert(&testService, &secondResult)
// Can't be bothered dealing with timezone issues on the worker that runs the automated tests
serviceStatuses := scenario.Store.GetAllServiceStatuses(paging.NewServiceStatusParams().WithResults(1, 20))
if len(serviceStatuses) != 1 {
t.Fatal("expected 1 service status")
}
actual, exists := serviceStatuses[testService.Key()]
if !exists {
t.Fatal("expected service status to exist")
}
if len(actual.Results) != 2 {
t.Error("expected 2 results, got", len(actual.Results))
}
if len(actual.Events) != 0 {
t.Error("expected 0 events, got", len(actual.Events))
}
scenario.Store.Clear()
})
}
}
func TestStore_GetAllServiceStatusesWithResultsAndEvents(t *testing.T) {
2021-07-17 01:24:12 +02:00
scenarios := initStoresAndBaseScenarios(t, "TestStore_GetAllServiceStatusesWithResultsAndEvents")
defer cleanUp(scenarios)
firstResult := testSuccessfulResult
secondResult := testUnsuccessfulResult
for _, scenario := range scenarios {
t.Run(scenario.Name, func(t *testing.T) {
scenario.Store.Insert(&testService, &firstResult)
scenario.Store.Insert(&testService, &secondResult)
// Can't be bothered dealing with timezone issues on the worker that runs the automated tests
serviceStatuses := scenario.Store.GetAllServiceStatuses(paging.NewServiceStatusParams().WithResults(1, 20).WithEvents(1, 50))
if len(serviceStatuses) != 1 {
t.Fatal("expected 1 service status")
}
actual, exists := serviceStatuses[testService.Key()]
if !exists {
t.Fatal("expected service status to exist")
}
if len(actual.Results) != 2 {
t.Error("expected 2 results, got", len(actual.Results))
}
if len(actual.Events) != 3 {
t.Error("expected 3 events, got", len(actual.Events))
}
scenario.Store.Clear()
})
}
}
func TestStore_GetServiceStatusPage1IsHasMoreRecentResultsThanPage2(t *testing.T) {
2021-07-17 01:24:12 +02:00
scenarios := initStoresAndBaseScenarios(t, "TestStore_GetServiceStatusPage1IsHasMoreRecentResultsThanPage2")
defer cleanUp(scenarios)
firstResult := testSuccessfulResult
firstResult.Timestamp = now.Add(-time.Minute)
secondResult := testUnsuccessfulResult
secondResult.Timestamp = now
for _, scenario := range scenarios {
t.Run(scenario.Name, func(t *testing.T) {
scenario.Store.Insert(&testService, &firstResult)
scenario.Store.Insert(&testService, &secondResult)
serviceStatusPage1 := scenario.Store.GetServiceStatusByKey(testService.Key(), paging.NewServiceStatusParams().WithResults(1, 1))
if serviceStatusPage1 == nil {
t.Fatalf("serviceStatusPage1 shouldn't have been nil")
}
if len(serviceStatusPage1.Results) != 1 {
t.Fatalf("serviceStatusPage1 should've had 1 result")
}
serviceStatusPage2 := scenario.Store.GetServiceStatusByKey(testService.Key(), paging.NewServiceStatusParams().WithResults(2, 1))
if serviceStatusPage2 == nil {
t.Fatalf("serviceStatusPage2 shouldn't have been nil")
}
if len(serviceStatusPage2.Results) != 1 {
t.Fatalf("serviceStatusPage2 should've had 1 result")
}
// Compare the timestamp of both pages
if !serviceStatusPage1.Results[0].Timestamp.After(serviceStatusPage2.Results[0].Timestamp) {
t.Errorf("The result from the first page should've been more recent than the results from the second page")
}
scenario.Store.Clear()
})
}
}
func TestStore_Insert(t *testing.T) {
2021-07-17 01:24:12 +02:00
scenarios := initStoresAndBaseScenarios(t, "TestStore_Insert")
defer cleanUp(scenarios)
firstResult := testSuccessfulResult
firstResult.Timestamp = now.Add(-time.Minute)
secondResult := testUnsuccessfulResult
secondResult.Timestamp = now
for _, scenario := range scenarios {
t.Run(scenario.Name, func(t *testing.T) {
scenario.Store.Insert(&testService, &testSuccessfulResult)
scenario.Store.Insert(&testService, &testUnsuccessfulResult)
2021-07-19 05:02:27 +02:00
ss := scenario.Store.GetServiceStatusByKey(testService.Key(), paging.NewServiceStatusParams().WithEvents(1, core.MaximumNumberOfEvents).WithResults(1, core.MaximumNumberOfResults).WithUptime())
if ss == nil {
t.Fatalf("Store should've had key '%s', but didn't", testService.Key())
}
2021-07-19 05:02:27 +02:00
if len(ss.Events) != 3 {
t.Fatalf("Service '%s' should've had 3 events, got %d", ss.Name, len(ss.Events))
}
if len(ss.Results) != 2 {
t.Fatalf("Service '%s' should've had 2 results, got %d", ss.Name, len(ss.Results))
}
2021-07-19 05:02:27 +02:00
for i, expectedResult := range []core.Result{testSuccessfulResult, testUnsuccessfulResult} {
if expectedResult.HTTPStatus != ss.Results[i].HTTPStatus {
t.Errorf("Result at index %d should've had a HTTPStatus of %d, got %d", i, ss.Results[i].HTTPStatus, expectedResult.HTTPStatus)
}
2021-07-19 05:02:27 +02:00
if expectedResult.DNSRCode != ss.Results[i].DNSRCode {
t.Errorf("Result at index %d should've had a DNSRCode of %s, got %s", i, ss.Results[i].DNSRCode, expectedResult.DNSRCode)
}
2021-07-19 05:02:27 +02:00
if expectedResult.Hostname != ss.Results[i].Hostname {
t.Errorf("Result at index %d should've had a Hostname of %s, got %s", i, ss.Results[i].Hostname, expectedResult.Hostname)
}
2021-07-19 05:02:27 +02:00
if expectedResult.IP != ss.Results[i].IP {
t.Errorf("Result at index %d should've had a IP of %s, got %s", i, ss.Results[i].IP, expectedResult.IP)
}
2021-07-19 05:02:27 +02:00
if expectedResult.Connected != ss.Results[i].Connected {
t.Errorf("Result at index %d should've had a Connected value of %t, got %t", i, ss.Results[i].Connected, expectedResult.Connected)
}
2021-07-19 05:02:27 +02:00
if expectedResult.Duration != ss.Results[i].Duration {
t.Errorf("Result at index %d should've had a Duration of %s, got %s", i, ss.Results[i].Duration.String(), expectedResult.Duration.String())
}
2021-07-19 05:02:27 +02:00
if len(expectedResult.Errors) != len(ss.Results[i].Errors) {
t.Errorf("Result at index %d should've had %d errors, but actually had %d errors", i, len(ss.Results[i].Errors), len(expectedResult.Errors))
} else {
for j := range expectedResult.Errors {
if ss.Results[i].Errors[j] != expectedResult.Errors[j] {
t.Error("should've been the same")
}
}
}
2021-07-19 05:02:27 +02:00
if len(expectedResult.ConditionResults) != len(ss.Results[i].ConditionResults) {
t.Errorf("Result at index %d should've had %d ConditionResults, but actually had %d ConditionResults", i, len(ss.Results[i].ConditionResults), len(expectedResult.ConditionResults))
} else {
for j := range expectedResult.ConditionResults {
if ss.Results[i].ConditionResults[j].Condition != expectedResult.ConditionResults[j].Condition {
t.Error("should've been the same")
}
if ss.Results[i].ConditionResults[j].Success != expectedResult.ConditionResults[j].Success {
t.Error("should've been the same")
}
}
}
2021-07-19 05:02:27 +02:00
if expectedResult.Success != ss.Results[i].Success {
t.Errorf("Result at index %d should've had a Success of %t, got %t", i, ss.Results[i].Success, expectedResult.Success)
}
2021-07-19 05:02:27 +02:00
if expectedResult.Timestamp.Unix() != ss.Results[i].Timestamp.Unix() {
t.Errorf("Result at index %d should've had a Timestamp of %d, got %d", i, ss.Results[i].Timestamp.Unix(), expectedResult.Timestamp.Unix())
}
2021-07-19 05:02:27 +02:00
if expectedResult.CertificateExpiration != ss.Results[i].CertificateExpiration {
t.Errorf("Result at index %d should've had a CertificateExpiration of %s, got %s", i, ss.Results[i].CertificateExpiration.String(), expectedResult.CertificateExpiration.String())
}
}
})
}
}
func TestStore_DeleteAllServiceStatusesNotInKeys(t *testing.T) {
2021-07-17 01:24:12 +02:00
scenarios := initStoresAndBaseScenarios(t, "TestStore_DeleteAllServiceStatusesNotInKeys")
defer cleanUp(scenarios)
firstService := core.Service{Name: "service-1", Group: "group"}
secondService := core.Service{Name: "service-2", Group: "group"}
result := &testSuccessfulResult
for _, scenario := range scenarios {
t.Run(scenario.Name, func(t *testing.T) {
scenario.Store.Insert(&firstService, result)
scenario.Store.Insert(&secondService, result)
if scenario.Store.GetServiceStatusByKey(firstService.Key(), paging.NewServiceStatusParams()) == nil {
t.Fatal("firstService should exist")
}
if scenario.Store.GetServiceStatusByKey(secondService.Key(), paging.NewServiceStatusParams()) == nil {
t.Fatal("secondService should exist")
}
scenario.Store.DeleteAllServiceStatusesNotInKeys([]string{firstService.Key()})
if scenario.Store.GetServiceStatusByKey(firstService.Key(), paging.NewServiceStatusParams()) == nil {
t.Error("secondService should've been deleted")
}
if scenario.Store.GetServiceStatusByKey(secondService.Key(), paging.NewServiceStatusParams()) != nil {
t.Error("firstService should still exist")
}
2021-07-18 06:34:22 +02:00
// Delete everything
scenario.Store.DeleteAllServiceStatusesNotInKeys([]string{})
if len(scenario.Store.GetAllServiceStatuses(paging.NewServiceStatusParams())) != 0 {
t.Errorf("everything should've been deleted")
}
})
}
}