negentropy_test.mx raw
1 package negentropy
2
3 import (
4 "bytes"
5 "testing"
6 )
7
8 func makeItem(ts int64, id byte) Item {
9 b := []byte{:32}
10 b[0] = id
11 return Item{Timestamp: ts, ID: b}
12 }
13
14 func TestFingerprintIdentical(t *testing.T) {
15 items := []Item{makeItem(1, 0xaa), makeItem(2, 0xbb)}
16 f1 := Fingerprint(items)
17 f2 := Fingerprint(items)
18 if f1 != f2 {
19 t.Error("identical items should produce identical fingerprints")
20 }
21 }
22
23 func TestFingerprintDiffers(t *testing.T) {
24 a := []Item{makeItem(1, 0xaa)}
25 b := []Item{makeItem(1, 0xbb)}
26 if Fingerprint(a) == Fingerprint(b) {
27 t.Error("different items should produce different fingerprints")
28 }
29 }
30
31 func TestDiffIdentical(t *testing.T) {
32 items := []Item{makeItem(1, 1), makeItem(2, 2)}
33 have, need := Diff(items, items)
34 if len(have) != 0 || len(need) != 0 {
35 t.Errorf("identical sets should have no diff, got have=%d need=%d", len(have), len(need))
36 }
37 }
38
39 func TestDiffMissing(t *testing.T) {
40 local := []Item{makeItem(1, 1), makeItem(2, 2)}
41 remote := []Item{makeItem(1, 1), makeItem(2, 2), makeItem(3, 3)}
42 have, need := Diff(local, remote)
43 if len(have) != 0 {
44 t.Errorf("expected 0 have, got %d", len(have))
45 }
46 if len(need) != 1 || need[0].ID[0] != 3 {
47 t.Errorf("expected need=[item3], got %v", need)
48 }
49 }
50
51 func TestDiffExtra(t *testing.T) {
52 local := []Item{makeItem(1, 1), makeItem(2, 2), makeItem(3, 3)}
53 remote := []Item{makeItem(1, 1), makeItem(3, 3)}
54 have, need := Diff(local, remote)
55 if len(have) != 1 || have[0].ID[0] != 2 {
56 t.Errorf("expected have=[item2], got %v", have)
57 }
58 if len(need) != 0 {
59 t.Errorf("expected 0 need, got %d", len(need))
60 }
61 }
62
63 func TestSplit(t *testing.T) {
64 items := []Item{
65 makeItem(1, 1), makeItem(2, 2),
66 makeItem(3, 3), makeItem(4, 4),
67 }
68 r := NewReconciler(items)
69 ranges := r.Split(2)
70 if len(ranges) != 2 {
71 t.Fatalf("expected 2 ranges, got %d", len(ranges))
72 }
73 if ranges[0].Count != 2 || ranges[1].Count != 2 {
74 t.Errorf("expected counts [2,2], got [%d,%d]", ranges[0].Count, ranges[1].Count)
75 }
76 }
77
78 func TestFindMismatches(t *testing.T) {
79 items := []Item{makeItem(1, 1), makeItem(2, 2), makeItem(3, 3), makeItem(4, 4)}
80 r1 := NewReconciler(items)
81 local := r1.Split(2)
82
83 // Same items = no mismatches
84 r2 := NewReconciler(items)
85 remote := r2.Split(2)
86 if mm := FindMismatches(local, remote); len(mm) != 0 {
87 t.Errorf("expected 0 mismatches, got %d", len(mm))
88 }
89
90 // Different items in second range
91 items2 := []Item{makeItem(1, 1), makeItem(2, 2), makeItem(3, 3), makeItem(4, 0xff)}
92 r3 := NewReconciler(items2)
93 remote2 := r3.Split(2)
94 mm := FindMismatches(local, remote2)
95 if len(mm) != 1 || mm[0] != 1 {
96 t.Errorf("expected mismatch at index 1, got %v", mm)
97 }
98 }
99
100 func TestItemsFromEvents(t *testing.T) {
101 // Verify sorting
102 a := makeItem(2, 0xbb)
103 b := makeItem(1, 0xaa)
104 items := []Item{a, b}
105 sortItems(items)
106 if items[0].Timestamp != 1 {
107 t.Error("items should be sorted by timestamp")
108 }
109 }
110
111 func TestEstimateRanges(t *testing.T) {
112 if EstimateRanges(0) != 0 {
113 t.Error("0 items should give 0 ranges")
114 }
115 if EstimateRanges(1) != 2 {
116 t.Error("1 item should give min 2 ranges")
117 }
118 if r := EstimateRanges(100); r != 10 {
119 t.Errorf("100 items: expected 10 ranges, got %d", r)
120 }
121 if r := EstimateRanges(100000); r != 128 {
122 t.Errorf("100000 items: expected 128 max, got %d", r)
123 }
124 }
125
126 func TestCompareItems(t *testing.T) {
127 a := makeItem(1, 0xaa)
128 b := makeItem(1, 0xbb)
129 c := makeItem(2, 0xaa)
130
131 if compareItems(a, a) != 0 {
132 t.Error("equal items should compare as 0")
133 }
134 if compareItems(a, b) >= 0 {
135 t.Error("a < b by ID")
136 }
137 if compareItems(a, c) >= 0 {
138 t.Error("a < c by timestamp")
139 }
140 }
141
142 func TestFingerprintXOR(t *testing.T) {
143 // XOR property: fp(a) XOR fp(b) = fp(a,b) only if no overlap
144 a := makeItem(1, 0xff)
145 b := makeItem(2, 0xff)
146 fpA := Fingerprint([]Item{a})
147 fpB := Fingerprint([]Item{b})
148 fpAB := Fingerprint([]Item{a, b})
149
150 // XOR of two identical first bytes should be 0
151 var expected [32]byte
152 for j := 0; j < 32; j++ {
153 expected[j] = fpA[j] ^ fpB[j]
154 }
155 if !bytes.Equal(fpAB[:], expected[:]) {
156 t.Error("fingerprint should be XOR of individual fingerprints")
157 }
158 }
159