2021-07-17 01:12:14 +02:00
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"
2021-07-17 01:12:14 +02:00
)
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 )
2021-07-17 01:12:14 +02:00
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 {
2021-07-17 01:12:14 +02:00
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" )
2021-07-17 01:12:14 +02:00
if err != nil {
t . Fatal ( "failed to create store:" , err . Error ( ) )
}
2021-07-17 01:24:12 +02:00
return [ ] * Scenario {
2021-07-17 01:12:14 +02:00
{
Name : "memory" ,
Store : memoryStore ,
} ,
{
2021-07-18 22:13:05 +02:00
Name : "sqlite" ,
Store : sqliteStore ,
2021-07-17 01:12:14 +02:00
} ,
}
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 )
2021-07-17 01:12:14 +02:00
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 )
2021-07-17 01:12:14 +02:00
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 )
2021-07-17 01:12:14 +02:00
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 )
2021-07-17 01:12:14 +02:00
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 )
2021-07-17 01:12:14 +02:00
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 )
2021-07-17 01:12:14 +02:00
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 {
2021-07-17 01:12:14 +02:00
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-17 01:12:14 +02:00
}
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-17 01:12:14 +02:00
}
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-17 01:12:14 +02:00
}
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-17 01:12:14 +02:00
}
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-17 01:12:14 +02:00
}
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-17 01:12:14 +02:00
}
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-17 01:12:14 +02:00
}
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-17 01:12:14 +02:00
}
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-17 01:12:14 +02:00
}
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-17 01:12:14 +02:00
}
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-17 01:12:14 +02:00
}
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 ( ) )
2021-07-17 01:12:14 +02:00
}
}
} )
}
}
func TestStore_DeleteAllServiceStatusesNotInKeys ( t * testing . T ) {
2021-07-17 01:24:12 +02:00
scenarios := initStoresAndBaseScenarios ( t , "TestStore_DeleteAllServiceStatusesNotInKeys" )
defer cleanUp ( scenarios )
2021-07-17 01:12:14 +02:00
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" )
}
2021-07-17 01:12:14 +02:00
} )
}
}