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