decred.org/dcrdex@v1.0.5/server/auth/cancel_test.go (about) 1 package auth 2 3 import ( 4 crand "crypto/rand" 5 "math/rand" 6 "sort" 7 "testing" 8 9 "decred.org/dcrdex/dex/order" 10 "decred.org/dcrdex/server/db" 11 ) 12 13 func randomOrderID() (oid order.OrderID) { 14 crand.Read(oid[:]) 15 return 16 } 17 18 func Test_latestOrders(t *testing.T) { 19 cap := int16(cancelThreshWindow) 20 ordList := newLatestOrders(cap) 21 22 maybeCancel := func() *order.OrderID { 23 if rand.Intn(6) > 4 { 24 oid := randomOrderID() 25 return &oid 26 } 27 return nil 28 } 29 30 checkSort := func() { 31 if !sort.IsSorted(ordsByTimeThenID(ordList.orders)) { 32 t.Fatal("list wasn't sorted") 33 } 34 if len(ordList.orders) > int(ordList.cap) { 35 t.Fatalf("list is above capacity somehow") 36 } 37 } 38 39 // empty list 40 total, cancels := ordList.counts() 41 if total != 0 { 42 t.Errorf("expected 0 orders, got %d", total) 43 } 44 if cancels != 0 { 45 t.Errorf("expected 0 cancels, got %d", total) 46 } 47 48 // add one cancel 49 ts := int64(1234) 50 coid := randomOrderID() 51 ordList.add(&oidStamped{order.OrderID{0x1}, ts, &coid, 1}) 52 checkSort() 53 total, cancels = ordList.counts() 54 if total != 1 { 55 t.Errorf("expected 1 orders, got %d", total) 56 } 57 if cancels != 1 { 58 t.Errorf("expected 1 cancels, got %d", cancels) 59 } 60 61 // add one non-cancel 62 ts++ 63 ordList.add(&oidStamped{order.OrderID{0x2}, ts, nil, db.EpochGapNA}) 64 checkSort() 65 total, cancels = ordList.counts() 66 if total != 2 { 67 t.Errorf("expected 2 orders, got %d", total) 68 } 69 if cancels != 1 { 70 t.Errorf("expected 1 cancels, got %d", total) 71 } 72 73 // add one that is the smallest 74 ordList.add(&oidStamped{order.OrderID{0x3}, ts - 10, nil, db.EpochGapNA}) 75 checkSort() 76 total, cancels = ordList.counts() 77 if total != 3 { 78 t.Errorf("expected 3 orders, got %d", total) 79 } 80 if cancels != 1 { 81 t.Errorf("expected 1 cancels, got %d", total) 82 } 83 84 for i := total; i < int(cap); i++ { 85 ts++ 86 ordList.add(&oidStamped{ 87 OrderID: randomOrderID(), 88 time: ts, 89 target: maybeCancel(), 90 epochGap: db.EpochGapNA, 91 }) 92 checkSort() 93 } 94 95 total, _ = ordList.counts() 96 if total != int(cap) { 97 t.Errorf("expected %d orders, got %d", int(cap), total) 98 } 99 //t.Logf("got %d cancels", cancels) 100 101 // Now that the list is at capacity, add another to test pop of the oldest order. 102 expectedOldest := ordList.orders[1] // the second oldest order 103 ts += 2 // still in order, leave space for an out of order add 104 ordList.add(&oidStamped{ 105 OrderID: order.OrderID{0x4}, 106 time: ts, 107 target: maybeCancel(), 108 }) 109 checkSort() 110 111 // should still be at capacity 112 total, _ = ordList.counts() 113 if total != int(cap) { 114 t.Errorf("expected %d orders, got %d", int(cap), total) 115 } 116 117 // verify the oldest order is the previously second oldest 118 if expectedOldest != ordList.orders[0] { 119 t.Errorf("expected oldest order to be %x, got %x", expectedOldest, ordList.orders[0]) 120 } 121 122 // Now add one with the same time as the last, but larger order ID, thus not 123 // requiring a swap. 124 oid4 := order.OrderID{0x5} 125 ordList.add(&oidStamped{ 126 OrderID: oid4, 127 time: ts, 128 target: maybeCancel(), 129 }) 130 checkSort() 131 132 // verify the latest order is the one just stored 133 latestOid := ordList.orders[len(ordList.orders)-1].OrderID 134 if oid4 != latestOid { 135 t.Errorf("expected latest order ID to be %x, got %x", oid4, latestOid) 136 } 137 138 // Add another with the same time as last, but this time with a smaller ID, 139 // thus requiring a swap. 140 oid0 := order.OrderID{0x0} 141 ordList.add(&oidStamped{ 142 OrderID: oid0, 143 time: ts, 144 target: maybeCancel(), 145 }) 146 checkSort() 147 148 // verify the latest order has not changed 149 latestOid = ordList.orders[len(ordList.orders)-1].OrderID 150 if oid4 != latestOid { 151 t.Errorf("expected latest order ID to be %x, got %x", oid4, latestOid) 152 } 153 154 // Add one with an older time, thus necessitating a few swaps. 155 ts-- 156 ordList.add(&oidStamped{ 157 OrderID: randomOrderID(), 158 time: ts, 159 target: maybeCancel(), 160 }) 161 checkSort() 162 163 // verify the latest order has not changed 164 latestOid = ordList.orders[len(ordList.orders)-1].OrderID 165 if oid4 != latestOid { 166 t.Errorf("expected latest order ID to be %x, got %x", oid4, latestOid) 167 } 168 169 // Now exercise it and ensure it is always sorted. 170 for i := 0; i < 100000; i++ { 171 ordList.add(&oidStamped{ 172 OrderID: randomOrderID(), 173 time: rand.Int63n(44444), 174 target: maybeCancel(), 175 }) 176 checkSort() 177 } 178 } 179 180 func Test_ordsByTimeThenID_Sort(t *testing.T) { 181 tests := []struct { 182 name string 183 ords []*oidStamped 184 wantOrds []*oidStamped 185 }{ 186 { 187 name: "unique, no swap", 188 ords: []*oidStamped{ 189 {order.OrderID{0x1}, 1234, nil, db.EpochGapNA}, 190 {order.OrderID{0x2}, 1235, nil, db.EpochGapNA}, 191 }, 192 wantOrds: []*oidStamped{ 193 {order.OrderID{0x1}, 1234, nil, db.EpochGapNA}, 194 {order.OrderID{0x2}, 1235, nil, db.EpochGapNA}, 195 }, 196 }, 197 { 198 name: "unique, one swap", 199 ords: []*oidStamped{ 200 {order.OrderID{0x2}, 1235, nil, db.EpochGapNA}, 201 {order.OrderID{0x1}, 1234, nil, db.EpochGapNA}, 202 }, 203 wantOrds: []*oidStamped{ 204 {order.OrderID{0x1}, 1234, nil, db.EpochGapNA}, 205 {order.OrderID{0x2}, 1235, nil, db.EpochGapNA}, 206 }, 207 }, 208 { 209 name: "time tie, swap by order ID", 210 ords: []*oidStamped{ 211 {order.OrderID{0x2}, 1234, nil, db.EpochGapNA}, 212 {order.OrderID{0x1}, 1234, nil, db.EpochGapNA}, 213 }, 214 wantOrds: []*oidStamped{ 215 {order.OrderID{0x1}, 1234, nil, db.EpochGapNA}, 216 {order.OrderID{0x2}, 1234, nil, db.EpochGapNA}, 217 }, 218 }, 219 } 220 for _, tt := range tests { 221 t.Run(tt.name, func(t *testing.T) { 222 sort.Sort(ordsByTimeThenID(tt.ords)) 223 for i, o := range tt.ords { 224 if o.OrderID != tt.wantOrds[i].OrderID { 225 t.Errorf("element %d has order ID %x, wanted %x", i, 226 o.OrderID, tt.wantOrds[i].OrderID) 227 } 228 } 229 }) 230 } 231 232 // Identical orders in the slice now just log and error, but the case can be 233 // tested with the panic instead: 234 // 235 // t.Run("dup order should panic", func(t *testing.T) { 236 // defer func() { 237 // if recover() == nil { 238 // t.Error("sort should have paniced with identical orders.") 239 // } 240 // }() 241 // dups := []*oidStamped{ 242 // {order.OrderID{0x1}, 1234, nil}, 243 // {order.OrderID{0x1}, 1234, nil}, 244 // } 245 // sort.Sort(ordsByTimeThenID(dups)) 246 // }) 247 }