2023-12-23 00:05:19 +01:00
|
|
|
// crdt_test unit tests the crdt implementations
|
|
|
|
package crdt
|
|
|
|
|
|
|
|
import (
|
|
|
|
"hash/fnv"
|
|
|
|
"slices"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2024-01-02 01:09:31 +01:00
|
|
|
"github.com/tim-beatham/smegmesh/pkg/lib"
|
2023-12-23 00:05:19 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
func NewGmap() *GMap[string, bool] {
|
|
|
|
vectorClock := NewVectorClock("a", func(key string) uint64 {
|
|
|
|
hash := fnv.New64a()
|
|
|
|
hash.Write([]byte(key))
|
|
|
|
return hash.Sum64()
|
|
|
|
}, 1) // 1 second stale time
|
|
|
|
|
|
|
|
gMap := NewGMap[string, bool](vectorClock)
|
|
|
|
return gMap
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGMapPutInsertsItems(t *testing.T) {
|
|
|
|
gMap := NewGmap()
|
|
|
|
gMap.Put("bruh1234", true)
|
|
|
|
|
|
|
|
if !gMap.Contains("bruh1234") {
|
|
|
|
t.Fatalf(`value not added to map`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGMapPutReplacesItems(t *testing.T) {
|
|
|
|
gMap := NewGmap()
|
|
|
|
gMap.Put("bruh1234", true)
|
|
|
|
gMap.Put("bruh1234", false)
|
|
|
|
|
|
|
|
value := gMap.Get("bruh1234")
|
|
|
|
|
|
|
|
if value {
|
|
|
|
t.Fatalf(`value should ahve been replaced to false`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestContainsValueNotPresent(t *testing.T) {
|
|
|
|
gMap := NewGmap()
|
|
|
|
|
|
|
|
if gMap.Contains("sdhjsdhsdj") {
|
|
|
|
t.Fatalf(`value should not be present in the map`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestContainsValuePresent(t *testing.T) {
|
|
|
|
gMap := NewGmap()
|
|
|
|
key := "hehehehe"
|
|
|
|
gMap.Put(key, false)
|
|
|
|
|
|
|
|
if !gMap.Contains(key) {
|
|
|
|
t.Fatalf(`%s should not be present in the map`, key)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGMapGetNotPresentReturnsError(t *testing.T) {
|
|
|
|
gMap := NewGmap()
|
|
|
|
value := gMap.Get("bruh123")
|
|
|
|
|
|
|
|
if value != false {
|
|
|
|
t.Fatalf(`value should be default type false`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGMapGetReturnsValue(t *testing.T) {
|
|
|
|
gMap := NewGmap()
|
|
|
|
gMap.Put("bobdylan", true)
|
|
|
|
|
|
|
|
value := gMap.Get("bobdylan")
|
|
|
|
|
|
|
|
if !value {
|
|
|
|
t.Fatalf("value should be true but was false")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMarkMarksTheValue(t *testing.T) {
|
|
|
|
gMap := NewGmap()
|
|
|
|
gMap.Put("hello123", true)
|
|
|
|
|
|
|
|
gMap.Mark("hello123")
|
|
|
|
|
|
|
|
if !gMap.IsMarked("hello123") {
|
|
|
|
t.Fatal(`hello123 should be marked`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMarkValueNotPresent(t *testing.T) {
|
|
|
|
gMap := NewGmap()
|
|
|
|
gMap.Mark("ok123456")
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestKeysMapEmpty(t *testing.T) {
|
|
|
|
gMap := NewGmap()
|
|
|
|
|
|
|
|
keys := gMap.Keys()
|
|
|
|
|
|
|
|
if len(keys) != 0 {
|
|
|
|
t.Fatal(`list of keys was not empty but should be empty`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestKeysMapReturnsKeysInMap(t *testing.T) {
|
|
|
|
gMap := NewGmap()
|
|
|
|
|
|
|
|
gMap.Put("a", false)
|
|
|
|
gMap.Put("b", false)
|
|
|
|
gMap.Put("c", false)
|
|
|
|
|
|
|
|
keys := gMap.Keys()
|
|
|
|
|
|
|
|
if len(keys) != 3 {
|
|
|
|
t.Fatal(`key length should be 3`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSaveMapEmptyReturnsEmptyMap(t *testing.T) {
|
|
|
|
gMap := NewGmap()
|
|
|
|
|
|
|
|
saveMap := gMap.Save()
|
|
|
|
|
|
|
|
if len(saveMap) != 0 {
|
|
|
|
t.Fatal(`saves should be empty`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSaveMapReturnsMapOfBuckets(t *testing.T) {
|
|
|
|
gMap := NewGmap()
|
|
|
|
gMap.Put("a", false)
|
|
|
|
gMap.Put("b", false)
|
|
|
|
gMap.Put("c", false)
|
|
|
|
|
|
|
|
saveMap := gMap.Save()
|
|
|
|
|
|
|
|
if len(saveMap) != 3 {
|
|
|
|
t.Fatalf(`save length should be 3`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSaveWithKeysNoKeysReturnsEmptyBucket(t *testing.T) {
|
|
|
|
gMap := NewGmap()
|
|
|
|
gMap.Put("a", false)
|
|
|
|
gMap.Put("b", false)
|
|
|
|
gMap.Put("c", false)
|
|
|
|
|
|
|
|
saveMap := gMap.SaveWithKeys([]uint64{})
|
|
|
|
|
|
|
|
if len(saveMap) != 0 {
|
|
|
|
t.Fatalf(`save map should be empty`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSaveWithKeysReturnsIntersection(t *testing.T) {
|
|
|
|
gMap := NewGmap()
|
|
|
|
gMap.Put("a", false)
|
|
|
|
gMap.Put("b", false)
|
|
|
|
gMap.Put("c", false)
|
|
|
|
|
|
|
|
clock := lib.MapKeys(gMap.GetClock())
|
|
|
|
clock = clock[:len(clock)-1]
|
|
|
|
|
|
|
|
values := gMap.SaveWithKeys(clock)
|
|
|
|
if len(values) != len(clock) {
|
|
|
|
t.Fatalf(`intersection not returned`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGetClockMapEmptyReturnsEmptyClock(t *testing.T) {
|
|
|
|
gMap := NewGmap()
|
|
|
|
|
|
|
|
clocks := gMap.GetClock()
|
|
|
|
|
|
|
|
if len(clocks) != 0 {
|
|
|
|
t.Fatalf(`vector clock is not empty`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGetClockReturnsAllCLocks(t *testing.T) {
|
|
|
|
gMap := NewGmap()
|
|
|
|
gMap.Put("a", false)
|
|
|
|
gMap.Put("b", false)
|
|
|
|
gMap.Put("c", false)
|
|
|
|
|
|
|
|
clocks := lib.MapValues(gMap.GetClock())
|
|
|
|
slices.Sort(clocks)
|
|
|
|
|
|
|
|
if !slices.Equal([]uint64{0, 1, 2}, clocks) {
|
|
|
|
t.Fatalf(`clocks are invalid`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGetHashChangesHashOnValueAdded(t *testing.T) {
|
|
|
|
gMap := NewGmap()
|
|
|
|
gMap.Put("a", false)
|
|
|
|
prevHash := gMap.GetHash()
|
|
|
|
|
|
|
|
gMap.Put("b", true)
|
|
|
|
|
|
|
|
if prevHash == gMap.GetHash() {
|
|
|
|
t.Fatalf(`hash should be different`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestPruneGarbageCollectsValuesThatHaveNotBeenUpdated(t *testing.T) {
|
|
|
|
gMap := NewGmap()
|
|
|
|
gMap.clock.Put("c", 12)
|
|
|
|
gMap.Put("c", false)
|
|
|
|
gMap.Put("a", false)
|
|
|
|
|
|
|
|
time.Sleep(4 * time.Second)
|
|
|
|
gMap.Put("a", true)
|
|
|
|
|
|
|
|
gMap.Prune()
|
|
|
|
|
|
|
|
if gMap.Contains("c") {
|
|
|
|
t.Fatalf(`a should have been pruned`)
|
|
|
|
}
|
|
|
|
}
|