forked from extern/smegmesh
215 lines
4.1 KiB
Go
215 lines
4.1 KiB
Go
|
package crdt
|
||
|
|
||
|
import (
|
||
|
"hash/fnv"
|
||
|
"slices"
|
||
|
"testing"
|
||
|
)
|
||
|
|
||
|
func NewMap(processId string) *TwoPhaseMap[string, string] {
|
||
|
theMap := NewTwoPhaseMap[string, string](processId, func(key string) uint64 {
|
||
|
hash := fnv.New64a()
|
||
|
hash.Write([]byte(key))
|
||
|
return hash.Sum64()
|
||
|
}, 1)
|
||
|
return theMap
|
||
|
}
|
||
|
|
||
|
func TestTwoPhaseMapEmpty(t *testing.T) {
|
||
|
theMap := NewMap("a")
|
||
|
|
||
|
if theMap.Contains("a") {
|
||
|
t.Fatalf(`a should not be present in the map`)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestTwoPhaseMapValuePresent(t *testing.T) {
|
||
|
theMap := NewMap("a")
|
||
|
theMap.Put("a", "")
|
||
|
|
||
|
if !theMap.Contains("a") {
|
||
|
t.Fatalf(`should be present within the map`)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestTwoPhaseMapValueNotPresent(t *testing.T) {
|
||
|
theMap := NewMap("a")
|
||
|
theMap.Put("b", "")
|
||
|
|
||
|
if theMap.Contains("a") {
|
||
|
t.Fatalf(`a should not be present in the map`)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestTwoPhaseMapPutThenRemove(t *testing.T) {
|
||
|
theMap := NewMap("a")
|
||
|
|
||
|
theMap.Put("a", "")
|
||
|
theMap.Remove("a")
|
||
|
|
||
|
if theMap.Contains("a") {
|
||
|
t.Fatalf(`a should not be present within the map`)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestTwoPhaseMapPutThenRemoveThenPut(t *testing.T) {
|
||
|
theMap := NewMap("a")
|
||
|
|
||
|
theMap.Put("a", "")
|
||
|
theMap.Remove("a")
|
||
|
theMap.Put("a", "")
|
||
|
|
||
|
if !theMap.Contains("a") {
|
||
|
t.Fatalf(`a should be present within the map`)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestMarkMarksTheValueIn2PMap(t *testing.T) {
|
||
|
theMap := NewMap("a")
|
||
|
|
||
|
theMap.Put("a", "")
|
||
|
theMap.Mark("a")
|
||
|
|
||
|
if !theMap.IsMarked("a") {
|
||
|
t.Fatalf(`a should be marked`)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestAsListReturnsItemsInList(t *testing.T) {
|
||
|
theMap := NewMap("a")
|
||
|
|
||
|
theMap.Put("a", "bob")
|
||
|
theMap.Put("b", "dylan")
|
||
|
|
||
|
keys := theMap.AsList()
|
||
|
slices.Sort(keys)
|
||
|
|
||
|
if !slices.Equal([]string{"bob", "dylan"}, keys) {
|
||
|
t.Fatalf(`values should be bob, dylan`)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestSnapShotRemoveMapEmpty(t *testing.T) {
|
||
|
theMap := NewMap("a")
|
||
|
theMap.Put("a", "bob")
|
||
|
theMap.Put("b", "dylan")
|
||
|
|
||
|
snapshot := theMap.Snapshot()
|
||
|
|
||
|
if len(snapshot.Add) != 2 {
|
||
|
t.Fatalf(`add values length should be 2`)
|
||
|
}
|
||
|
|
||
|
if len(snapshot.Remove) != 0 {
|
||
|
t.Fatalf(`remove map length should be 0`)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestSnapshotMapEmpty(t *testing.T) {
|
||
|
theMap := NewMap("a")
|
||
|
|
||
|
snapshot := theMap.Snapshot()
|
||
|
|
||
|
if len(snapshot.Add) != 0 || len(snapshot.Remove) != 0 {
|
||
|
t.Fatalf(`snapshot length should be 0`)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestSnapShotFromStateReturnsIntersection(t *testing.T) {
|
||
|
map1 := NewMap("a")
|
||
|
map1.Put("a", "heyy")
|
||
|
|
||
|
map2 := NewMap("b")
|
||
|
map2.Put("b", "hmmm")
|
||
|
|
||
|
message := map2.GenerateMessage()
|
||
|
|
||
|
snapShot := map1.SnapShotFromState(message)
|
||
|
|
||
|
if len(snapShot.Add) != 1 {
|
||
|
t.Fatalf(`add length should be 1`)
|
||
|
}
|
||
|
|
||
|
if len(snapShot.Remove) != 0 {
|
||
|
t.Fatalf(`remove length should be 0`)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestGetHashDifferentOnChange(t *testing.T) {
|
||
|
theMap := NewMap("a")
|
||
|
|
||
|
prevHash := theMap.GetHash()
|
||
|
|
||
|
theMap.Put("b", "hmmhmhmh")
|
||
|
|
||
|
if prevHash == theMap.GetHash() {
|
||
|
t.Fatalf(`hashes should not be the same`)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestGenerateMessageReturnsClocks(t *testing.T) {
|
||
|
theMap := NewMap("a")
|
||
|
theMap.Put("a", "hmm")
|
||
|
theMap.Put("b", "hmm")
|
||
|
theMap.Remove("a")
|
||
|
|
||
|
message := theMap.GenerateMessage()
|
||
|
|
||
|
if len(message.AddContents) != 2 {
|
||
|
t.Fatalf(`two items added add should be 2`)
|
||
|
}
|
||
|
|
||
|
if len(message.RemoveContents) != 1 {
|
||
|
t.Fatalf(`a was removed remove map should be length 1`)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestDifferenceReturnsDifferenceOfMaps(t *testing.T) {
|
||
|
map1 := NewMap("a")
|
||
|
map1.Put("a", "ssms")
|
||
|
map1.Put("b", "sdmdsmd")
|
||
|
|
||
|
map2 := NewMap("b")
|
||
|
map2.Put("d", "eek")
|
||
|
map2.Put("c", "meh")
|
||
|
|
||
|
message1 := map1.GenerateMessage()
|
||
|
message2 := map2.GenerateMessage()
|
||
|
|
||
|
difference := message1.Difference(0, message2)
|
||
|
|
||
|
if len(difference.AddContents) != 2 {
|
||
|
t.Fatalf(`d and c are not in map1 they should be in add contents`)
|
||
|
}
|
||
|
|
||
|
if len(difference.RemoveContents) != 0 {
|
||
|
t.Fatalf(`remove should be empty`)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestMergeMergesValuesThatAreGreaterThanCurrentClock(t *testing.T) {
|
||
|
map1 := NewMap("a")
|
||
|
map1.Put("a", "ssms")
|
||
|
map1.Put("b", "sdmdsmd")
|
||
|
|
||
|
map2 := NewMap("b")
|
||
|
map2.Put("d", "eek")
|
||
|
map2.Put("c", "meh")
|
||
|
|
||
|
message1 := map1.GenerateMessage()
|
||
|
message2 := map2.GenerateMessage()
|
||
|
|
||
|
difference := message1.Difference(0, message2)
|
||
|
state := map2.SnapShotFromState(difference)
|
||
|
|
||
|
map1.Merge(*state)
|
||
|
|
||
|
if !map1.Contains("d") {
|
||
|
t.Fatalf(`d should be in the map`)
|
||
|
}
|
||
|
|
||
|
if !map2.Contains("c") {
|
||
|
t.Fatalf(`c should be in the map`)
|
||
|
}
|
||
|
}
|