From c3bc375ff185f61c8fbf7a4a383d8eab5fd967df Mon Sep 17 00:00:00 2001 From: Chris Heppell Date: Wed, 30 Dec 2020 10:57:17 +0000 Subject: [PATCH] add memory tests --- storage/memory.go | 7 + storage/memory_test.go | 404 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 411 insertions(+) create mode 100644 storage/memory_test.go diff --git a/storage/memory.go b/storage/memory.go index 9b4ec448..f9688dc0 100644 --- a/storage/memory.go +++ b/storage/memory.go @@ -97,3 +97,10 @@ func copyErrors(errors []string) []string { } return copiedErrors } + +// Clear will empty all the results from the in memory store +func (ims *InMemoryStore) Clear() { + serviceResultsMutex.Lock() + serviceStatuses = make(map[string]*core.ServiceStatus) + serviceResultsMutex.Unlock() +} diff --git a/storage/memory_test.go b/storage/memory_test.go new file mode 100644 index 00000000..6fe83e5d --- /dev/null +++ b/storage/memory_test.go @@ -0,0 +1,404 @@ +package storage + +import ( + "fmt" + "testing" + "time" + + "github.com/TwinProduction/gatus/core" +) + +var testService = core.Service{ + Name: "Name", + Group: "Group", + URL: "URL", + DNS: &core.DNS{QueryType: "QueryType", QueryName: "QueryName"}, + Method: "Method", + Body: "Body", + GraphQL: false, + Headers: nil, + Interval: time.Second * 2, + Conditions: nil, + Alerts: nil, + Insecure: false, + NumberOfFailuresInARow: 0, + NumberOfSuccessesInARow: 0, +} + +var memoryStore = NewInMemoryStore() + +func TestStorage_GetAllFromEmptyMemoryStoreReturnsNothing(t *testing.T) { + memoryStore.Clear() + results := memoryStore.GetAll() + if len(results) != 0 { + t.Errorf("MemoryStore should've returned 0 results, but actually returned %d", len(results)) + } +} + +func TestStorage_InsertIntoEmptyMemoryStoreThenGetAllReturnsOneResult(t *testing.T) { + memoryStore.Clear() + result := core.Result{ + HTTPStatus: 200, + DNSRCode: "DNSRCode", + Body: nil, + Hostname: "Hostname", + IP: "IP", + Connected: false, + Duration: time.Second * 2, + Errors: nil, + ConditionResults: nil, + Success: false, + Timestamp: time.Now(), + CertificateExpiration: time.Second * 2, + } + + memoryStore.Insert(&testService, &result) + + results := memoryStore.GetAll() + if len(results) != 1 { + t.Errorf("MemoryStore should've returned 0 results, but actually returned %d", len(results)) + } + + key := fmt.Sprintf("%s_%s", testService.Group, testService.Name) + storedResult, exists := results[key] + if !exists { + t.Fatalf("In Memory Store should've contained key '%s', but didn't", key) + } + + if storedResult.Name != testService.Name { + t.Errorf("Stored Results Name should've been %s, but was %s", testService.Name, storedResult.Name) + } + if storedResult.Group != testService.Group { + t.Errorf("Stored Results Group should've been %s, but was %s", testService.Group, storedResult.Group) + } + if len(storedResult.Results) != 1 { + t.Errorf("Stored Results for service %s should've had 1 result, but actually had %d", storedResult.Name, len(storedResult.Results)) + } + if storedResult.Results[0] == &result { + t.Errorf("Returned result is the same reference as result passed to insert. Returned result should be copies only") + } +} + +func TestStorage_InsertTwoResultsForSingleServiceIntoEmptyMemoryStore_ThenGetAllReturnsTwoResults(t *testing.T) { + memoryStore.Clear() + result1 := core.Result{ + HTTPStatus: 404, + DNSRCode: "DNSRCode", + Body: nil, + Hostname: "Hostname", + IP: "IP", + Connected: false, + Duration: time.Second * 2, + Errors: nil, + ConditionResults: nil, + Success: false, + Timestamp: time.Now(), + CertificateExpiration: time.Second * 2, + } + result2 := core.Result{ + HTTPStatus: 200, + DNSRCode: "DNSRCode", + Body: nil, + Hostname: "Hostname", + IP: "IP", + Connected: true, + Duration: time.Second * 2, + Errors: nil, + ConditionResults: nil, + Success: true, + Timestamp: time.Now(), + CertificateExpiration: time.Second * 2, + } + + resultsToInsert := []core.Result{result1, result2} + + memoryStore.Insert(&testService, &result1) + memoryStore.Insert(&testService, &result2) + + results := memoryStore.GetAll() + if len(results) != 1 { + t.Fatalf("MemoryStore should've returned 1 results, but actually returned %d", len(results)) + } + + key := fmt.Sprintf("%s_%s", testService.Group, testService.Name) + serviceResults, exists := results[key] + if !exists { + t.Fatalf("In Memory Store should've contained key '%s', but didn't", key) + } + + if len(serviceResults.Results) != 2 { + t.Fatalf("Service '%s' should've had 2 results, but actually returned %d", serviceResults.Name, len(serviceResults.Results)) + } + + for i, r := range serviceResults.Results { + expectedResult := resultsToInsert[i] + + if r.HTTPStatus != expectedResult.HTTPStatus { + t.Errorf("Result at index %d should've had a HTTPStatus of %d, but was actually %d", i, expectedResult.HTTPStatus, r.HTTPStatus) + } + if r.DNSRCode != expectedResult.DNSRCode { + t.Errorf("Result at index %d should've had a DNSRCode of %s, but was actually %s", i, expectedResult.DNSRCode, r.DNSRCode) + } + if len(r.Body) != len(expectedResult.Body) { + t.Errorf("Result at index %d should've had a body of length %d, but was actually %d", i, len(expectedResult.Body), len(r.Body)) + } + if r.Hostname != expectedResult.Hostname { + t.Errorf("Result at index %d should've had a Hostname of %s, but was actually %s", i, expectedResult.Hostname, r.Hostname) + } + if r.IP != expectedResult.IP { + t.Errorf("Result at index %d should've had a IP of %s, but was actually %s", i, expectedResult.IP, r.IP) + } + if r.Connected != expectedResult.Connected { + t.Errorf("Result at index %d should've had a Connected value of %t, but was actually %t", i, expectedResult.Connected, r.Connected) + } + if r.Duration != expectedResult.Duration { + t.Errorf("Result at index %d should've had a Duration of %s, but was actually %s", i, expectedResult.Duration.String(), r.Duration.String()) + } + if len(r.Errors) != len(expectedResult.Errors) { + t.Errorf("Result at index %d should've had %d errors, but actually had %d errors", i, len(expectedResult.Errors), len(r.Errors)) + } + if len(r.ConditionResults) != len(expectedResult.ConditionResults) { + t.Errorf("Result at index %d should've had %d ConditionResults, but actually had %d ConditionResults", i, len(expectedResult.ConditionResults), len(r.ConditionResults)) + } + if r.Success != expectedResult.Success { + t.Errorf("Result at index %d should've had a Success of %t, but was actually %t", i, expectedResult.Success, r.Success) + } + if r.Timestamp != expectedResult.Timestamp { + t.Errorf("Result at index %d should've had a Timestamp of %s, but was actually %s", i, expectedResult.Timestamp.String(), r.Timestamp.String()) + } + if r.CertificateExpiration != expectedResult.CertificateExpiration { + t.Errorf("Result at index %d should've had a CertificateExpiration of %s, but was actually %s", i, expectedResult.CertificateExpiration.String(), r.CertificateExpiration.String()) + } + } +} + +func TestStorage_InsertTwoResultsTwoServicesIntoEmptyMemoryStore_ThenGetAllReturnsTwoServicesWithOneResultEach(t *testing.T) { + memoryStore.Clear() + result1 := core.Result{ + HTTPStatus: 404, + DNSRCode: "DNSRCode", + Body: nil, + Hostname: "Hostname", + IP: "IP", + Connected: false, + Duration: time.Second * 2, + Errors: nil, + ConditionResults: nil, + Success: false, + Timestamp: time.Now(), + CertificateExpiration: time.Second * 2, + } + result2 := core.Result{ + HTTPStatus: 200, + DNSRCode: "DNSRCode", + Body: nil, + Hostname: "Hostname", + IP: "IP", + Connected: true, + Duration: time.Second * 2, + Errors: nil, + ConditionResults: nil, + Success: true, + Timestamp: time.Now(), + CertificateExpiration: time.Second * 2, + } + + testService2 := core.Service{ + Name: "Name2", + Group: "Group", + URL: "URL", + DNS: &core.DNS{QueryType: "QueryType", QueryName: "QueryName"}, + Method: "Method", + Body: "Body", + GraphQL: false, + Headers: nil, + Interval: time.Second * 2, + Conditions: nil, + Alerts: nil, + Insecure: false, + NumberOfFailuresInARow: 0, + NumberOfSuccessesInARow: 0, + } + + memoryStore.Insert(&testService, &result1) + memoryStore.Insert(&testService2, &result2) + + results := memoryStore.GetAll() + if len(results) != 2 { + t.Fatalf("MemoryStore should've returned 2 results, but actually returned %d", len(results)) + } + + key := fmt.Sprintf("%s_%s", testService.Group, testService.Name) + serviceResults1, exists := results[key] + if !exists { + t.Fatalf("In Memory Store should've contained key '%s', but didn't", key) + } + + if len(serviceResults1.Results) != 1 { + t.Fatalf("Service '%s' should've had 1 results, but actually returned %d", serviceResults1.Name, len(serviceResults1.Results)) + } + + key = fmt.Sprintf("%s_%s", testService2.Group, testService2.Name) + serviceResults2, exists := results[key] + if !exists { + t.Fatalf("In Memory Store should've contained key '%s', but didn't", key) + } + + if len(serviceResults2.Results) != 1 { + t.Fatalf("Service '%s' should've had 1 results, but actually returned %d", serviceResults1.Name, len(serviceResults1.Results)) + } +} + +func TestStorage_InsertResultForServiceWithErrorsIntoEmptyMemoryStore_ThenGetAllReturnsOneResultWithErrors(t *testing.T) { + memoryStore.Clear() + errors := []string{ + "error1", + "error2", + } + result1 := core.Result{ + HTTPStatus: 404, + DNSRCode: "DNSRCode", + Body: nil, + Hostname: "Hostname", + IP: "IP", + Connected: false, + Duration: time.Second * 2, + Errors: errors, + ConditionResults: nil, + Success: false, + Timestamp: time.Now(), + CertificateExpiration: time.Second * 2, + } + + memoryStore.Insert(&testService, &result1) + + results := memoryStore.GetAll() + if len(results) != 1 { + t.Fatalf("MemoryStore should've returned 1 results, but actually returned %d", len(results)) + } + + key := fmt.Sprintf("%s_%s", testService.Group, testService.Name) + serviceResults, exists := results[key] + if !exists { + t.Fatalf("In Memory Store should've contained key '%s', but didn't", key) + } + + if len(serviceResults.Results) != 1 { + t.Fatalf("Service '%s' should've had 2 results, but actually returned %d", serviceResults.Name, len(serviceResults.Results)) + } + + actualResult := serviceResults.Results[0] + + if len(actualResult.Errors) != len(errors) { + t.Errorf("Service result should've had 2 errors, but actually had %d errors", len(actualResult.Errors)) + } + + for i, err := range actualResult.Errors { + if err != errors[i] { + t.Errorf("Error at index %d should've been %s, but was actually %s", i, errors[i], err) + } + } +} + +func TestStorage_InsertResultForServiceWithConditionResultsIntoEmptyMemoryStore_ThenGetAllReturnsOneResultWithConditionResults(t *testing.T) { + memoryStore.Clear() + crs := []*core.ConditionResult{ + { + Condition: "condition1", + Success: true, + }, + { + Condition: "condition2", + Success: false, + }, + } + result := core.Result{ + HTTPStatus: 404, + DNSRCode: "DNSRCode", + Body: nil, + Hostname: "Hostname", + IP: "IP", + Connected: false, + Duration: time.Second * 2, + Errors: nil, + ConditionResults: crs, + Success: false, + Timestamp: time.Now(), + CertificateExpiration: time.Second * 2, + } + + memoryStore.Insert(&testService, &result) + + results := memoryStore.GetAll() + if len(results) != 1 { + t.Fatalf("MemoryStore should've returned 1 results, but actually returned %d", len(results)) + } + + key := fmt.Sprintf("%s_%s", testService.Group, testService.Name) + serviceResults, exists := results[key] + if !exists { + t.Fatalf("In Memory Store should've contained key '%s', but didn't", key) + } + + if len(serviceResults.Results) != 1 { + t.Fatalf("Service '%s' should've had 2 results, but actually returned %d", serviceResults.Name, len(serviceResults.Results)) + } + + actualResult := serviceResults.Results[0] + + if len(actualResult.ConditionResults) != len(crs) { + t.Errorf("Service result should've had 2 ConditionResults, but actually had %d ConditionResults", len(actualResult.Errors)) + } + + for i, cr := range actualResult.ConditionResults { + if cr.Condition != crs[i].Condition { + t.Errorf("ConditionResult at index %d should've had condition %s, but was actually %s", i, crs[i].Condition, cr.Condition) + } + if cr.Success != crs[i].Success { + t.Errorf("ConditionResult at index %d should've had success value of %t, but was actually %t", i, crs[i].Success, cr.Success) + } + } +} + +func TestStorage_MultipleMemoryStoreInstancesReferToSameMemoryMap(t *testing.T) { + memoryStore.Clear() + currentMap := memoryStore.GetAll() + + otherMemoryStore := NewInMemoryStore() + otherMemoryStoresMap := otherMemoryStore.GetAll() + + if len(currentMap) != len(otherMemoryStoresMap) { + t.Errorf("Multiple memory stores should refer to the same internal map, but 'memoryStore' returned %d results, and 'otherMemoryStore' returned %d results", len(currentMap), len(otherMemoryStoresMap)) + } + + memoryStore.Insert(&testService, &core.Result{}) + currentMap = memoryStore.GetAll() + otherMemoryStoresMap = otherMemoryStore.GetAll() + + if len(currentMap) != len(otherMemoryStoresMap) { + t.Errorf("Multiple memory stores should refer to the same internal map, but 'memoryStore' returned %d results after inserting, and 'otherMemoryStore' returned %d results after inserting", len(currentMap), len(otherMemoryStoresMap)) + } + + otherMemoryStore.Clear() + currentMap = memoryStore.GetAll() + otherMemoryStoresMap = otherMemoryStore.GetAll() + + if len(currentMap) != len(otherMemoryStoresMap) { + t.Errorf("Multiple memory stores should refer to the same internal map, but 'memoryStore' returned %d results after clearing, and 'otherMemoryStore' returned %d results after clearing", len(currentMap), len(otherMemoryStoresMap)) + } +} + +func TestStorage_ModificationsToReturnedMapDoNotAffectInternalMap(t *testing.T) { + memoryStore.Clear() + + memoryStore.Insert(&testService, &core.Result{}) + modifiedResults := memoryStore.GetAll() + for k := range modifiedResults { + delete(modifiedResults, k) + } + results := memoryStore.GetAll() + + if len(modifiedResults) == len(results) { + t.Errorf("Returned map from GetAll should be free to modify by the caller without affecting internal in-memory map, but length of results from in-memory map (%d) was equal to the length of results in modified map (%d)", len(results), len(modifiedResults)) + } +}