package database import ( "testing" "time" "github.com/TwinProduction/gatus/core" "github.com/TwinProduction/gatus/storage/store/paging" ) var ( firstCondition = core.Condition("[STATUS] == 200") secondCondition = core.Condition("[RESPONSE_TIME] < 500") thirdCondition = core.Condition("[CERTIFICATE_EXPIRATION] < 72h") now = time.Now() 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{ Hostname: "example.org", IP: "127.0.0.1", HTTPStatus: 200, Errors: nil, Connected: true, Success: true, Timestamp: now, 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{ Hostname: "example.org", IP: "127.0.0.1", HTTPStatus: 200, Errors: []string{"error-1", "error-2"}, Connected: true, Success: false, Timestamp: now, 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, }, }, } ) func TestNewStore(t *testing.T) { if _, err := NewStore("", "TestNewStore.db"); err != ErrDatabaseDriverNotSpecified { t.Error("expected error due to blank driver parameter") } if _, err := NewStore("sqlite", ""); err != ErrFilePathNotSpecified { t.Error("expected error due to blank path parameter") } if store, err := NewStore("sqlite", t.TempDir()+"/TestNewStore.db"); err != nil { t.Error("shouldn't have returned any error, got", err.Error()) } else { _ = store.db.Close() } } func TestStore_InsertCleansUpOldUptimeEntriesProperly(t *testing.T) { store, _ := NewStore("sqlite", t.TempDir()+"/TestStore_InsertCleansUpOldUptimeEntriesProperly.db") defer store.Close() now := time.Now().Round(time.Minute) now = time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, now.Location()) store.Insert(&testService, &core.Result{Timestamp: now.Add(-5 * time.Hour), Success: true}) tx, _ := store.db.Begin() oldest, _ := store.getAgeOfOldestServiceUptimeEntry(tx, 1) _ = tx.Commit() if oldest.Truncate(time.Hour) != 5*time.Hour { t.Errorf("oldest service uptime entry should've been ~5 hours old, was %s", oldest) } // The oldest cache entry should remain at ~5 hours old, because this entry is more recent store.Insert(&testService, &core.Result{Timestamp: now.Add(-3 * time.Hour), Success: true}) tx, _ = store.db.Begin() oldest, _ = store.getAgeOfOldestServiceUptimeEntry(tx, 1) _ = tx.Commit() if oldest.Truncate(time.Hour) != 5*time.Hour { t.Errorf("oldest service uptime entry should've been ~5 hours old, was %s", oldest) } // The oldest cache entry should now become at ~8 hours old, because this entry is older store.Insert(&testService, &core.Result{Timestamp: now.Add(-8 * time.Hour), Success: true}) tx, _ = store.db.Begin() oldest, _ = store.getAgeOfOldestServiceUptimeEntry(tx, 1) _ = tx.Commit() if oldest.Truncate(time.Hour) != 8*time.Hour { t.Errorf("oldest service uptime entry should've been ~8 hours old, was %s", oldest) } // Since this is one hour before reaching the clean up threshold, the oldest entry should now be this one store.Insert(&testService, &core.Result{Timestamp: now.Add(-(uptimeCleanUpThreshold - time.Hour)), Success: true}) tx, _ = store.db.Begin() oldest, _ = store.getAgeOfOldestServiceUptimeEntry(tx, 1) _ = tx.Commit() if oldest.Truncate(time.Hour) != uptimeCleanUpThreshold-time.Hour { t.Errorf("oldest service uptime entry should've been ~%s hours old, was %s", uptimeCleanUpThreshold-time.Hour, oldest) } // Since this entry is after the uptimeCleanUpThreshold, both this entry as well as the previous // one should be deleted since they both surpass uptimeRetention store.Insert(&testService, &core.Result{Timestamp: now.Add(-(uptimeCleanUpThreshold + time.Hour)), Success: true}) tx, _ = store.db.Begin() oldest, _ = store.getAgeOfOldestServiceUptimeEntry(tx, 1) _ = tx.Commit() if oldest.Truncate(time.Hour) != 8*time.Hour { t.Errorf("oldest service uptime entry should've been ~8 hours old, was %s", oldest) } } func TestStore_InsertCleansUpEventsAndResultsProperly(t *testing.T) { store, _ := NewStore("sqlite", t.TempDir()+"/TestStore_InsertCleansUpEventsAndResultsProperly.db") defer store.Close() for i := 0; i < resultsCleanUpThreshold+eventsCleanUpThreshold; i++ { store.Insert(&testService, &testSuccessfulResult) store.Insert(&testService, &testUnsuccessfulResult) ss := store.GetServiceStatusByKey(testService.Key(), paging.NewServiceStatusParams().WithResults(1, core.MaximumNumberOfResults*5).WithEvents(1, core.MaximumNumberOfEvents*5)) if len(ss.Results) > resultsCleanUpThreshold+1 { t.Errorf("number of results shouldn't have exceeded %d, reached %d", resultsCleanUpThreshold, len(ss.Results)) } if len(ss.Events) > eventsCleanUpThreshold+1 { t.Errorf("number of events shouldn't have exceeded %d, reached %d", eventsCleanUpThreshold, len(ss.Events)) } } store.Clear() }