decred.org/dcrdex@v1.0.5/server/db/driver/pg/orders_online_test.go (about) 1 //go:build pgonline 2 3 package pg 4 5 import ( 6 "bytes" 7 "context" 8 "encoding/hex" 9 "fmt" 10 "testing" 11 "time" 12 13 "decred.org/dcrdex/dex/order" 14 "decred.org/dcrdex/server/account" 15 "decred.org/dcrdex/server/db" 16 "decred.org/dcrdex/server/db/driver/pg/internal" 17 "github.com/davecgh/go-spew/spew" 18 ) 19 20 const cancelThreshWindow = 100 // spec 21 22 func TestStoreOrder(t *testing.T) { 23 if err := cleanTables(archie.db); err != nil { 24 t.Fatalf("cleanTables: %v", err) 25 } 26 27 orderBadLotSize := newLimitOrder(false, 4900000, 1, order.StandingTiF, 0) 28 orderBadLotSize.Quantity /= 2 29 30 orderBadMarket := newLimitOrder(false, 4900000, 1, order.StandingTiF, 0) 31 orderBadMarket.BaseAsset = AssetDCR 32 orderBadMarket.QuoteAsset = AssetDCR // same as base 33 34 // order ID for a cancel order 35 orderID0, _ := hex.DecodeString("dd64e2ae2845d281ba55a6d46eceb9297b2bdec5c5bada78f9ae9e373164df0d") 36 var targetOrderID order.OrderID 37 copy(targetOrderID[:], orderID0) 38 39 limitA := newLimitOrder(false, 4800000, 1, order.StandingTiF, 0) 40 marketSellA := newMarketSellOrder(2, 1) 41 marketSellB := newMarketSellOrder(2, 0) 42 cancelA := newCancelOrder(targetOrderID, AssetDCR, AssetBTC, 0) 43 44 // Order with the same commitment as limitA, but different order id. 45 limitAx := new(order.LimitOrder) 46 *limitAx = *limitA 47 limitAx.SetTime(time.Now()) 48 49 var epochIdx, epochDur int64 = 13245678, 6000 50 51 type args struct { 52 ord order.Order 53 status order.OrderStatus 54 } 55 tests := []struct { 56 name string 57 args args 58 wantErr bool 59 wantErrType error 60 }{ 61 { 62 name: "ok limit booked (active)", 63 args: args{ 64 ord: newLimitOrder(false, 4500000, 1, order.StandingTiF, 0), 65 status: order.OrderStatusBooked, 66 }, 67 wantErr: false, 68 }, 69 { 70 name: "ok limit epoch (active)", 71 args: args{ 72 ord: newLimitOrder(false, 5000000, 1, order.StandingTiF, 0), 73 status: order.OrderStatusEpoch, 74 }, 75 wantErr: false, 76 }, 77 { 78 name: "ok limit canceled (archived)", 79 args: args{ 80 ord: newLimitOrder(false, 4700000, 1, order.StandingTiF, 0), 81 status: order.OrderStatusCanceled, 82 }, 83 wantErr: false, 84 }, 85 { 86 name: "ok limit executed (archived)", 87 args: args{ 88 ord: limitA, 89 status: order.OrderStatusExecuted, 90 }, 91 wantErr: false, 92 }, 93 { 94 name: "limit duplicate", 95 args: args{ 96 ord: limitA, 97 status: order.OrderStatusExecuted, 98 }, 99 wantErr: true, 100 wantErrType: db.ArchiveError{Code: db.ErrReusedCommit}, 101 }, 102 { 103 name: "limit duplicate by commit only", 104 args: args{ 105 ord: limitAx, 106 status: order.OrderStatusExecuted, 107 }, 108 wantErr: true, 109 wantErrType: db.ArchiveError{Code: db.ErrReusedCommit}, 110 }, 111 { 112 name: "limit bad quantity (lot size)", 113 args: args{ 114 ord: orderBadLotSize, 115 status: order.OrderStatusEpoch, 116 }, 117 wantErr: true, 118 wantErrType: db.ArchiveError{Code: db.ErrInvalidOrder}, 119 }, 120 { 121 name: "limit bad trading pair", 122 args: args{ 123 ord: orderBadMarket, 124 status: order.OrderStatusEpoch, 125 }, 126 wantErr: true, 127 wantErrType: db.ArchiveError{Code: db.ErrUnsupportedMarket}, 128 }, 129 { 130 name: "market sell - bad status (booked)", 131 args: args{ 132 ord: marketSellB, 133 status: order.OrderStatusBooked, 134 }, 135 wantErr: true, 136 wantErrType: db.ArchiveError{Code: db.ErrInvalidOrder}, 137 }, 138 { 139 name: "market sell - bad status (canceled)", 140 args: args{ 141 ord: marketSellB, 142 status: order.OrderStatusCanceled, 143 }, 144 wantErr: true, 145 wantErrType: db.ArchiveError{Code: db.ErrInvalidOrder}, 146 }, 147 { 148 name: "market sell - active", 149 args: args{ 150 ord: marketSellB, 151 status: order.OrderStatusEpoch, 152 }, 153 wantErr: false, 154 }, 155 { 156 name: "market sell - archived", 157 args: args{ 158 ord: marketSellA, 159 status: order.OrderStatusExecuted, 160 }, 161 wantErr: false, 162 }, 163 { 164 name: "market sell - already in other table", 165 args: args{ 166 ord: marketSellA, 167 status: order.OrderStatusExecuted, 168 }, 169 wantErr: true, 170 wantErrType: db.ArchiveError{Code: db.ErrReusedCommit}, 171 }, 172 { 173 name: "market sell - duplicate archived order", 174 args: args{ 175 ord: marketSellB, // dd64e2ae2845d281ba55a6d46eceb9297b2bdec5c5bada78f9ae9e373164df0d 176 status: order.OrderStatusExecuted, 177 }, 178 wantErr: true, 179 wantErrType: db.ArchiveError{Code: db.ErrReusedCommit}, 180 }, 181 { 182 name: "cancel order", 183 args: args{ 184 ord: cancelA, 185 status: order.OrderStatusExecuted, 186 }, 187 wantErr: false, 188 }, 189 { 190 name: "cancel order - duplicate archived order", 191 args: args{ 192 ord: cancelA, 193 status: order.OrderStatusExecuted, 194 }, 195 wantErr: true, 196 wantErrType: db.ArchiveError{Code: db.ErrReusedCommit}, 197 }, 198 } 199 for _, tt := range tests { 200 t.Run(tt.name, func(t *testing.T) { 201 err := archie.StoreOrder(tt.args.ord, epochIdx, epochDur, tt.args.status) 202 if (err != nil) != tt.wantErr { 203 t.Errorf("StoreOrder() error = %v, wantErr %v", err, tt.wantErr) 204 } 205 if err != nil { 206 t.Logf("%s: %v", tt.name, err) 207 if !db.SameErrorTypes(err, tt.wantErrType) { 208 t.Errorf("Wrong error. Got %v, expected %v", err, tt.wantErrType) 209 } 210 } 211 }) 212 } 213 } 214 215 func TestBookOrder(t *testing.T) { 216 if err := cleanTables(archie.db); err != nil { 217 t.Fatalf("cleanTables: %v", err) 218 } 219 220 // Store order (epoch) for new order 221 // BookOrder for existing order 222 223 var epochIdx, epochDur int64 = 13245678, 6000 224 225 // Store standing limit order in epoch status. 226 lo := newLimitOrder(true, 4200000, 1, order.StandingTiF, 0) 227 err := archie.StoreOrder(lo, epochIdx, epochDur, order.OrderStatusEpoch) 228 if err != nil { 229 t.Fatalf("StoreOrder failed: %v", err) 230 } 231 232 // Book the same limit order. 233 err = archie.BookOrder(lo) 234 if err != nil { 235 t.Fatalf("BookOrder failed: %v", err) 236 } 237 } 238 239 func TestExecuteOrder(t *testing.T) { 240 if err := cleanTables(archie.db); err != nil { 241 t.Fatalf("cleanTables: %v", err) 242 } 243 244 // Store order (executed) for new order 245 // ExecuteOrder for existing order 246 247 var epochIdx, epochDur int64 = 13245678, 6000 248 249 // Store standing limit order in executed status. 250 lo := newLimitOrder(true, 4200000, 1, order.StandingTiF, 0) 251 err := archie.StoreOrder(lo, epochIdx, epochDur, order.OrderStatusExecuted) 252 if err != nil { 253 t.Fatalf("StoreOrder failed: %v", err) 254 } 255 256 // Execute the same limit order. 257 err = archie.ExecuteOrder(lo) 258 if err != nil { 259 t.Fatalf("BookOrder failed: %v", err) 260 } 261 } 262 263 func TestCancelOrder(t *testing.T) { 264 if err := cleanTables(archie.db); err != nil { 265 t.Fatalf("cleanTables: %v", err) 266 } 267 268 // Standing limit == OK 269 var epochIdx, epochDur int64 = 13245678, 6000 270 lo := newLimitOrder(false, 4800000, 1, order.StandingTiF, 0) 271 err := archie.StoreOrder(lo, epochIdx, epochDur, order.OrderStatusBooked) 272 if err != nil { 273 t.Fatalf("BookOrder failed: %v", err) 274 } 275 276 // Execute the same limit order. 277 err = archie.CancelOrder(lo) 278 if err != nil { 279 t.Fatalf("CancelOrder failed: %v", err) 280 } 281 282 // Cancel an order not in the tables yet 283 lo2 := newLimitOrder(true, 4600000, 1, order.StandingTiF, 0) 284 err = archie.CancelOrder(lo2) 285 if !db.IsErrOrderUnknown(err) { 286 t.Fatalf("CancelOrder should have failed for unknown order.") 287 } 288 } 289 290 func TestRevokeOrder(t *testing.T) { 291 if err := cleanTables(archie.db); err != nil { 292 t.Fatalf("cleanTables: %v", err) 293 } 294 295 // Standing limit == OK 296 var epochIdx, epochDur int64 = 13245678, 6000 297 lo := newLimitOrder(false, 4800000, 1, order.StandingTiF, 0) 298 err := archie.StoreOrder(lo, epochIdx, epochDur, order.OrderStatusBooked) 299 if err != nil { 300 t.Fatalf("StoreOrder failed: %v", err) 301 } 302 303 // Revoke the same limit order. 304 cancelID, timeStamp, err := archie.RevokeOrder(lo) 305 if err != nil { 306 t.Fatalf("RevokeOrder failed: %v", err) 307 } 308 309 // Check for the server-generated cancel order. 310 co, coStatus, err := archie.Order(cancelID, lo.BaseAsset, lo.QuoteAsset) 311 if err != nil { 312 t.Fatalf("Failed to locate cancel order: %v", err) 313 } 314 if co.ID() != cancelID { 315 t.Errorf("incorrect cancel ID retrieved") 316 } 317 coT, ok := co.(*order.CancelOrder) 318 if !ok { 319 t.Fatalf("not a cancel order") 320 } 321 if coT.ClientTime != timeStamp { 322 t.Errorf("got ClientTime %v, expected %v", coT.ClientTime, timeStamp) 323 } 324 if coT.ServerTime != timeStamp { 325 t.Errorf("got ServerTime %v, expected %v", coT.ServerTime, timeStamp) 326 } 327 if coStatus != order.OrderStatusRevoked { 328 t.Errorf("got order status %v, expected %v", coStatus, order.OrderStatusRevoked) 329 } 330 if !coT.Commit.IsZero() { 331 t.Errorf("generated cancel order did not have NULL/zero-value commitment") 332 } 333 334 // Market orders may be revoked too, while swap is in progress. 335 // NOTE: executed -> revoked status change may be odd. 336 mo := newMarketSellOrder(1, 0) 337 err = archie.StoreOrder(mo, epochIdx, epochDur, order.OrderStatusExecuted) 338 if err != nil { 339 t.Fatalf("StoreOrder failed: %v", err) 340 } 341 342 cancelID, timeStamp, err = archie.RevokeOrderUncounted(mo) 343 if err != nil { 344 t.Fatalf("RevokeOrder failed: %v", err) 345 } 346 347 co, coStatus, err = archie.Order(cancelID, mo.BaseAsset, mo.QuoteAsset) 348 if err != nil { 349 t.Fatalf("Failed to locate cancel order: %v", err) 350 } 351 if co.ID() != cancelID { 352 t.Errorf("incorrect cancel ID retrieved") 353 } 354 coT, ok = co.(*order.CancelOrder) 355 if !ok { 356 t.Fatalf("not a cancel order") 357 } 358 if coT.ClientTime != timeStamp { 359 t.Errorf("got ClientTime %v, expected %v", coT.ClientTime, timeStamp) 360 } 361 if coT.ServerTime != timeStamp { 362 t.Errorf("got ServerTime %v, expected %v", coT.ServerTime, timeStamp) 363 } 364 if coStatus != order.OrderStatusRevoked { 365 t.Errorf("got order status %v, expected %v", coStatus, order.OrderStatusRevoked) 366 } 367 if !coT.Commit.IsZero() { 368 t.Errorf("generated cancel order did not have NULL/zero-value commitment") 369 } 370 371 // Revoke an order not in the tables yet 372 lo2 := newLimitOrder(true, 4600000, 1, order.StandingTiF, 0) 373 _, _, err = archie.RevokeOrder(lo2) 374 if !db.IsErrOrderUnknown(err) { 375 t.Fatalf("RevokeOrder should have failed for unknown order.") 376 } 377 } 378 379 func TestFlushBook(t *testing.T) { 380 if err := cleanTables(archie.db); err != nil { 381 t.Fatalf("cleanTables: %v", err) 382 } 383 384 // Standing limit == OK as booked 385 var epochIdx, epochDur int64 = 13245678, 6000 386 lo := newLimitOrder(false, 4800000, 1, order.StandingTiF, 0) 387 err := archie.StoreOrder(lo, epochIdx, epochDur, order.OrderStatusBooked) 388 if err != nil { 389 t.Fatalf("StoreOrder failed: %v", err) 390 } 391 392 // A not booked order. 393 mo := newMarketSellOrder(1, 0) 394 mo.AccountID = lo.AccountID 395 err = archie.StoreOrder(mo, epochIdx, epochDur, order.OrderStatusExecuted) 396 if err != nil { 397 t.Fatalf("StoreOrder failed: %v", err) 398 } 399 400 sellsRemoved, buysRemoved, err := archie.FlushBook(lo.BaseAsset, lo.QuoteAsset) 401 if err != nil { 402 t.Fatalf("FlushBook failed: %v", err) 403 } 404 if len(sellsRemoved) != 0 { 405 t.Fatalf("flushed %d book sell orders, expected 0", len(sellsRemoved)) 406 } 407 if len(buysRemoved) != 1 { 408 t.Fatalf("flushed %d book buy orders, expected 1", len(buysRemoved)) 409 } 410 if buysRemoved[0] != lo.ID() { 411 t.Errorf("flushed sell order has ID %v, expected %v", buysRemoved[0], lo.ID()) 412 } 413 414 // Check for new status of the order. 415 loNow, loStatus, err := archie.Order(lo.ID(), lo.BaseAsset, lo.QuoteAsset) 416 if err != nil { 417 t.Fatalf("Failed to locate order: %v", err) 418 } 419 if loNow.ID() != lo.ID() { 420 t.Errorf("incorrect order ID retrieved") 421 } 422 _, ok := loNow.(*order.LimitOrder) 423 if !ok { 424 t.Fatalf("not a limit order") 425 } 426 if loStatus != order.OrderStatusRevoked { 427 t.Errorf("got order status %v, expected %v", loStatus, order.OrderStatusRevoked) 428 } 429 430 ordersOut, _, err := archie.UserOrders(context.Background(), lo.User(), lo.BaseAsset, lo.QuoteAsset) 431 if err != nil { 432 t.Fatalf("UserOrders failed: %v", err) 433 } 434 435 wantNumOrders := 2 // market and limit 436 if len(ordersOut) != wantNumOrders { 437 t.Fatalf("got %d user orders, expected %d", len(ordersOut), wantNumOrders) 438 } 439 440 cancels, err := archie.ExecutedCancelsForUser(lo.User(), cancelThreshWindow) 441 if err != nil { 442 t.Errorf("ExecutedCancelsForUser failed: %v", err) 443 } 444 // ExecutedCancelsForUser should not find the (exempt) cancels created by 445 // FlushBook. 446 if len(cancels) != 0 { 447 t.Fatalf("got %d cancels, expected 0", len(cancels)) 448 } 449 450 // Query for the revoke associated cancels without the exemption filter. 451 cancelTableName := fullCancelOrderTableName(archie.dbName, mktInfo.Name, false) 452 stmt := fmt.Sprintf(internal.SelectRevokeCancels, cancelTableName) 453 rows, err := archie.db.QueryContext(context.Background(), stmt, lo.User(), orderStatusRevoked, cancelThreshWindow) 454 if err != nil { 455 t.Fatalf("QueryContext failed: %v", err) 456 } 457 458 var ords []*db.CancelRecord 459 for rows.Next() { 460 var oid, target order.OrderID 461 var revokeTime time.Time 462 var epochIdx int64 463 err = rows.Scan(&oid, &target, &revokeTime, &epochIdx) 464 if err != nil { 465 rows.Close() 466 t.Fatalf("rows Scan failed") 467 } 468 469 if epochIdx != exemptEpochIdx { 470 t.Errorf("got epoch index %d, expected %d", epochIdx, exemptEpochIdx) 471 } 472 473 ords = append(ords, &db.CancelRecord{ 474 ID: oid, 475 TargetID: target, 476 MatchTime: revokeTime.UnixMilli(), 477 }) 478 } 479 480 if err = rows.Err(); err != nil { 481 t.Fatalf("rows Scan failed") 482 } 483 484 if len(ords) != 1 { 485 t.Fatalf("found %d cancels, wanted 1", len(ords)) 486 } 487 488 if ords[0].TargetID != lo.ID() { 489 t.Fatalf("cancel order is targeting %v, expected %v", ords[0].TargetID, lo.ID()) 490 } 491 492 // Ensure market order is still there. 493 moNow, moStatus, err := archie.Order(mo.ID(), mo.BaseAsset, mo.QuoteAsset) 494 if err != nil { 495 t.Fatalf("Failed to locate order: %v", err) 496 } 497 if moNow.ID() != mo.ID() { 498 t.Errorf("incorrect order ID retrieved") 499 } 500 _, ok = moNow.(*order.MarketOrder) 501 if !ok { 502 t.Fatalf("not a market order") 503 } 504 if moStatus != order.OrderStatusExecuted { 505 t.Errorf("got order status %v, expected %v", loStatus, order.OrderStatusExecuted) 506 } 507 } 508 509 func TestLoadOrderUnknown(t *testing.T) { 510 if err := cleanTables(archie.db); err != nil { 511 t.Fatalf("cleanTables: %v", err) 512 } 513 514 orderID0, _ := hex.DecodeString("dd64e2ae2845d281ba55a6d46eceb9297b2bdec5c5bada78f9ae9e373164df0d") 515 var oid order.OrderID 516 copy(oid[:], orderID0) 517 518 ordOut, statusOut, err := archie.Order(oid, mktInfo.Base, mktInfo.Quote) 519 if err == nil || ordOut != nil { 520 t.Errorf("Order should have failed to load non-existent order") 521 } 522 if statusOut != order.OrderStatusUnknown { 523 t.Errorf("status of non-existent order should be OrderStatusUnknown, got %s", statusOut) 524 } 525 } 526 527 func TestStoreLoadLimitOrderActive(t *testing.T) { 528 if err := cleanTables(archie.db); err != nil { 529 t.Fatalf("cleanTables: %v", err) 530 } 531 532 var epochIdx, epochDur int64 = 13245678, 6000 533 534 // Limit: buy, standing, booked 535 ordIn := newLimitOrder(false, 4900000, 1, order.StandingTiF, 0) 536 statusIn := order.OrderStatusBooked 537 538 // Do not use Stringers when dumping, and stop after 4 levels deep 539 spew.Config.MaxDepth = 4 540 spew.Config.DisableMethods = true 541 542 oid, base, quote := ordIn.ID(), ordIn.BaseAsset, ordIn.QuoteAsset 543 544 err := archie.StoreOrder(ordIn, epochIdx, epochDur, statusIn) 545 if err != nil { 546 t.Fatalf("StoreOrder failed: %v", err) 547 } 548 549 ordOut, statusOut, err := archie.Order(oid, base, quote) 550 if err != nil { 551 t.Fatalf("Order failed: %v", err) 552 } 553 554 if ordOut.ID() != oid { 555 t.Errorf("Incorrect OrderId for retrieved order. Got %v, expected %v.", 556 ordOut.ID(), oid) 557 spew.Dump(ordIn) 558 spew.Dump(ordOut) 559 } 560 561 if statusOut != statusIn { 562 t.Errorf("Incorrect OrderStatus for retrieved order. Got %v, expected %v.", 563 statusOut, statusIn) 564 } 565 } 566 567 func TestStoreLoadLimitOrderArchived(t *testing.T) { 568 if err := cleanTables(archie.db); err != nil { 569 t.Fatalf("cleanTables: %v", err) 570 } 571 572 var epochIdx, epochDur int64 = 13245678, 6000 573 574 // Limit: buy, standing, executed 575 ordIn := newLimitOrder(false, 4900000, 1, order.StandingTiF, 0) 576 statusIn := order.OrderStatusExecuted 577 578 // Do not use Stringers when dumping, and stop after 4 levels deep 579 spew.Config.MaxDepth = 4 580 spew.Config.DisableMethods = true 581 582 oid, base, quote := ordIn.ID(), ordIn.BaseAsset, ordIn.QuoteAsset 583 584 err := archie.StoreOrder(ordIn, epochIdx, epochDur, statusIn) 585 if err != nil { 586 t.Fatalf("StoreOrder failed: %v", err) 587 } 588 589 ordOut, statusOut, err := archie.Order(oid, base, quote) 590 if err != nil { 591 t.Fatalf("Order failed: %v", err) 592 } 593 594 if ordOut.ID() != oid { 595 t.Errorf("Incorrect OrderId for retrieved order. Got %v, expected %v.", 596 ordOut.ID(), oid) 597 spew.Dump(ordIn) 598 spew.Dump(ordOut) 599 } 600 601 if statusOut != statusIn { 602 t.Errorf("Incorrect OrderStatus for retrieved order. Got %v, expected %v.", 603 statusOut, statusIn) 604 } 605 } 606 607 func TestStoreLoadMarketOrderActive(t *testing.T) { 608 if err := cleanTables(archie.db); err != nil { 609 t.Fatalf("cleanTables: %v", err) 610 } 611 612 var epochIdx, epochDur int64 = 13245678, 6000 613 614 // Market: sell, epoch (active) 615 ordIn := newMarketSellOrder(1, 0) 616 statusIn := order.OrderStatusEpoch 617 618 // Do not use Stringers when dumping, and stop after 4 levels deep 619 spew.Config.MaxDepth = 4 620 spew.Config.DisableMethods = true 621 622 oid, base, quote := ordIn.ID(), ordIn.BaseAsset, ordIn.QuoteAsset 623 624 err := archie.StoreOrder(ordIn, epochIdx, epochDur, statusIn) 625 if err != nil { 626 t.Fatalf("StoreOrder failed: %v", err) 627 } 628 629 ordOut, statusOut, err := archie.Order(oid, base, quote) 630 if err != nil { 631 t.Fatalf("Order failed: %v", err) 632 } 633 634 if ordOut.ID() != oid { 635 t.Errorf("Incorrect OrderId for retrieved order. Got %v, expected %v.", 636 ordOut.ID(), oid) 637 spew.Dump(ordIn) 638 spew.Dump(ordOut) 639 } 640 641 if statusOut != statusIn { 642 t.Errorf("Incorrect OrderStatus for retrieved order. Got %v, expected %v.", 643 statusOut, statusIn) 644 } 645 } 646 647 func TestStoreLoadCancelOrder(t *testing.T) { 648 if err := cleanTables(archie.db); err != nil { 649 t.Fatalf("cleanTables: %v", err) 650 } 651 652 var epochIdx, epochDur int64 = 13245678, 6000 653 654 // order ID for a cancel order 655 orderID0, _ := hex.DecodeString("dd64e2ae2845d281ba55a6d46eceb9297b2bdec5c5bada78f9ae9e373164df0d") 656 var targetOrderID order.OrderID 657 copy(targetOrderID[:], orderID0) 658 659 // Cancel: epoch (active) 660 ordIn := newCancelOrder(targetOrderID, AssetDCR, AssetBTC, 0) 661 statusIn := order.OrderStatusEpoch 662 663 // Do not use Stringers when dumping, and stop after 4 levels deep 664 spew.Config.MaxDepth = 4 665 spew.Config.DisableMethods = true 666 667 oid, base, quote := ordIn.ID(), ordIn.BaseAsset, ordIn.QuoteAsset 668 669 err := archie.StoreOrder(ordIn, epochIdx, epochDur, statusIn) 670 if err != nil { 671 t.Fatalf("StoreOrder failed: %v", err) 672 } 673 674 ordOut, statusOut, err := archie.Order(oid, base, quote) 675 if err != nil { 676 t.Fatalf("Order failed: %v", err) 677 } 678 679 if ordOut.ID() != oid { 680 t.Errorf("Incorrect OrderId for retrieved order. Got %v, expected %v.", 681 ordOut.ID(), oid) 682 spew.Dump(ordIn) 683 spew.Dump(ordOut) 684 } 685 686 if statusOut != statusIn { 687 t.Errorf("Incorrect OrderStatus for retrieved order. Got %v, expected %v.", 688 statusOut, statusIn) 689 } 690 } 691 692 func TestOrderStatusUnknown(t *testing.T) { 693 if err := cleanTables(archie.db); err != nil { 694 t.Fatalf("cleanTables: %v", err) 695 } 696 697 ord := newLimitOrder(false, 4900000, 1, order.StandingTiF, 0) // not stored 698 _, _, _, err := archie.OrderStatus(ord) 699 if err == nil { 700 t.Fatalf("OrderStatus succeeded to find nonexistent order!") 701 } 702 if !db.SameErrorTypes(err, db.ArchiveError{Code: db.ErrUnknownOrder}) { 703 if errA, ok := err.(db.ArchiveError); ok { 704 t.Fatalf("Expected ArchiveError with code ErrUnknownOrder, got %d", errA.Code) 705 } 706 t.Fatalf("Expected ArchiveError with code ErrUnknownOrder, got %v", err) 707 } 708 } 709 710 // Test ActiveOrderCoins, BookOrders, and EpochOrders. 711 func TestActiveOrderCoins(t *testing.T) { 712 if err := cleanTables(archie.db); err != nil { 713 t.Fatalf("cleanTables: %v", err) 714 } 715 716 var epochIdx, epochDur int64 = 13245678, 6000 717 718 multiCoinLO := newLimitOrder(false, 4900000, 1, order.StandingTiF, 0) 719 multiCoinLO.Coins = append(multiCoinLO.Coins, order.CoinID{0x22, 0x23}) 720 721 epochLO := newLimitOrder(true, 1, 1, order.StandingTiF, 0) 722 epochCO := newCancelOrder(multiCoinLO.ID(), AssetDCR, AssetBTC, 0) 723 724 orderStatuses := []struct { 725 ord order.Order 726 status order.OrderStatus 727 activeCoins int 728 }{ 729 { 730 multiCoinLO, 731 order.OrderStatusBooked, // active, buy, booked 732 -1, 733 }, 734 { 735 newLimitOrder(false, 4500000, 1, order.StandingTiF, 0), 736 order.OrderStatusExecuted, // archived, buy 737 0, 738 }, 739 { 740 newMarketSellOrder(2, 0), 741 order.OrderStatusEpoch, // active, sell, epoch 742 1, 743 }, 744 { 745 epochLO, 746 order.OrderStatusEpoch, // active, buy, epoch 747 1, 748 }, 749 { 750 epochCO, 751 order.OrderStatusEpoch, // cancel, epoch 752 0, 753 }, 754 { 755 newMarketSellOrder(1, 0), 756 order.OrderStatusExecuted, // archived, sell 757 0, 758 }, 759 { 760 newMarketBuyOrder(2000000000, 0), 761 order.OrderStatusEpoch, // active, buy 762 -1, 763 }, 764 { 765 newMarketBuyOrder(2100000000, 0), 766 order.OrderStatusExecuted, // archived, buy 767 0, 768 }, 769 } 770 771 for i := range orderStatuses { 772 ordIn := orderStatuses[i].ord 773 statusIn := orderStatuses[i].status 774 err := archie.StoreOrder(ordIn, epochIdx, epochDur, statusIn) 775 if err != nil { 776 t.Fatalf("StoreOrder failed: %v", err) 777 } 778 } 779 780 baseCoins, quoteCoins, err := archie.ActiveOrderCoins(mktInfo.Base, mktInfo.Quote) 781 if err != nil { 782 t.Fatalf("ActiveOrderCoins failed: %v", err) 783 } 784 785 for _, os := range orderStatuses { 786 var coins, wantCoins []order.CoinID 787 switch os.activeCoins { 788 case 0: // no active 789 case 1: // active base coins (sell order) 790 coins = baseCoins[os.ord.ID()] 791 wantCoins = os.ord.Trade().Coins 792 case -1: // active quote coins (buy order) 793 coins = quoteCoins[os.ord.ID()] 794 wantCoins = os.ord.Trade().Coins 795 } 796 797 if len(coins) != len(wantCoins) { 798 t.Errorf("Order %v has %d coins, expected %d", os.ord.ID(), 799 len(coins), len(wantCoins)) 800 continue 801 } 802 for i := range coins { 803 if !bytes.Equal(coins[i], wantCoins[i]) { 804 t.Errorf("Order %v coin %d mismatch:\n\tgot %v\n\texpected %v", 805 os.ord.ID(), i, coins[i], wantCoins[i]) 806 } 807 } 808 } 809 810 bookOrders, err := archie.BookOrders(mktInfo.Base, mktInfo.Quote) 811 if err != nil { 812 t.Fatalf("BookOrders failed: %v", err) 813 } 814 815 if len(bookOrders) != 1 { 816 t.Fatalf("got %d book orders, expected 1", len(bookOrders)) 817 } 818 819 // Verify the order ID of the loaded order is correct. This ensures the 820 // order is being loaded with all the fields to provide and identical 821 // serialization. 822 if multiCoinLO.ID() != bookOrders[0].ID() { 823 t.Errorf("loaded book order has an incorrect order ID. Got %v, expected %v", 824 bookOrders[0].ID(), multiCoinLO.ID()) 825 } 826 827 los, mos, cos, err := archie.epochOrders(mktInfo.Base, mktInfo.Quote) 828 if err != nil { 829 t.Fatalf("epochOrders failed: %v", err) 830 } 831 832 if len(los) != 1 || len(mos) != 2 || len(cos) != 1 { 833 t.Fatalf("got %d epoch limit orders, %d epoch market orders, and %d epoch cancel orders, expected 1, 2, and 1", 834 len(los), len(mos), len(cos)) 835 } 836 837 // Verify the order ID of the loaded order is correct. This ensures the 838 // order is being loaded with all the fields to provide and identical 839 // serialization. 840 if epochLO.ID() != los[0].ID() { 841 t.Errorf("epoch limit order has an incorrect order ID. Got %v, expected %v", 842 los[0].ID(), epochLO.ID()) 843 } 844 if epochCO.ID() != cos[0].ID() { 845 t.Errorf("epoch cancel order has an incorrect order ID. Got %v, expected %v", 846 cos[0].ID(), epochCO.ID()) 847 } 848 849 // The exported version should return the same orders. 850 orders, err := archie.EpochOrders(mktInfo.Base, mktInfo.Quote) 851 if err != nil { 852 t.Fatalf("EpochOrders failed: %v", err) 853 } 854 855 if len(orders) != 4 { 856 t.Fatalf("got %d epoch orders, expected 4", len(orders)) 857 } 858 for _, o := range orders { 859 if o.ID() == los[0].ID() || 860 o.ID() == mos[0].ID() || 861 o.ID() == mos[1].ID() || 862 o.ID() == cos[0].ID() { 863 continue 864 } 865 t.Fatalf("order %v in EpochOrders but not epochOrders", o.ID()) 866 } 867 } 868 869 func TestOrderStatus(t *testing.T) { 870 if err := cleanTables(archie.db); err != nil { 871 t.Fatalf("cleanTables: %v", err) 872 } 873 874 var epochIdx, epochDur int64 = 13245678, 6000 875 876 orderStatuses := []struct { 877 ord order.Order 878 status order.OrderStatus 879 }{ 880 { 881 newLimitOrder(false, 4900000, 1, order.StandingTiF, 0), 882 order.OrderStatusBooked, // active 883 }, 884 { 885 newLimitOrder(false, 4500000, 1, order.StandingTiF, 0), 886 order.OrderStatusExecuted, // archived 887 }, 888 { 889 newMarketSellOrder(2, 0), 890 order.OrderStatusEpoch, // active 891 }, 892 { 893 newMarketSellOrder(1, 0), 894 order.OrderStatusExecuted, // archived 895 }, 896 { 897 newMarketBuyOrder(2000000000, 0), 898 order.OrderStatusEpoch, // active 899 }, 900 { 901 newMarketBuyOrder(2100000000, 0), 902 order.OrderStatusExecuted, // archived 903 }, 904 } 905 906 for i := range orderStatuses { 907 ordIn := orderStatuses[i].ord 908 trade := ordIn.Trade() 909 statusIn := orderStatuses[i].status 910 err := archie.StoreOrder(ordIn, epochIdx, epochDur, statusIn) 911 if err != nil { 912 t.Fatalf("StoreOrder failed: %v", err) 913 } 914 915 statusOut, typeOut, filledOut, err := archie.OrderStatus(ordIn) 916 if err != nil { 917 t.Fatalf("OrderStatus(%d:%v) failed: %v", i, ordIn, err) 918 } 919 920 if statusOut != statusIn { 921 t.Errorf("Incorrect OrderStatus for retrieved order. Got %v, expected %v.", 922 statusOut, statusIn) 923 } 924 925 if typeOut != ordIn.Type() { 926 t.Errorf("Incorrect OrderType for retrieved order. Got %v, expected %v.", 927 typeOut, ordIn.Type()) 928 } 929 930 if filledOut != int64(trade.Filled()) { 931 t.Errorf("Incorrect FillAmt for retrieved order. Got %v, expected %v.", 932 filledOut, trade.Filled()) 933 } 934 } 935 } 936 937 func TestCancelOrderStatus(t *testing.T) { 938 if err := cleanTables(archie.db); err != nil { 939 t.Fatalf("cleanTables: %v", err) 940 } 941 942 var epochIdx, epochDur int64 = 13245678, 6000 943 944 // order ID for a cancel order 945 orderID0, _ := hex.DecodeString("dd64e2ae2845d281ba55a6d46eceb9297b2bdec5c5bada78f9ae9e373164df0d") 946 var targetOrderID order.OrderID 947 copy(targetOrderID[:], orderID0) 948 949 // Cancel: executed (archived) 950 ordIn := newCancelOrder(targetOrderID, mktInfo.Base, mktInfo.Quote, 0) 951 statusIn := order.OrderStatusExecuted 952 953 //oid, base, quote := ordIn.ID(), ordIn.BaseAsset, ordIn.QuoteAsset 954 955 err := archie.StoreOrder(ordIn, epochIdx, epochDur, statusIn) 956 if err != nil { 957 t.Fatalf("StoreOrder failed: %v", err) 958 } 959 960 statusOut, typeOut, filledOut, err := archie.OrderStatus(ordIn) 961 if err != nil { 962 t.Fatalf("Order failed: %v", err) 963 } 964 965 if statusOut != statusIn { 966 t.Errorf("Incorrect OrderStatus for retrieved order. Got %v, expected %v.", 967 statusOut, statusIn) 968 } 969 970 if typeOut != ordIn.Type() { 971 t.Errorf("Incorrect OrderType for retrieved order. Got %v, expected %v.", 972 typeOut, ordIn.Type()) 973 } 974 975 if filledOut != -1 { 976 t.Errorf("Incorrect FilledAmt for retrieved order. Got %v, expected %v.", 977 filledOut, -1) 978 } 979 } 980 981 func TestUpdateOrderUnknown(t *testing.T) { 982 if err := cleanTables(archie.db); err != nil { 983 t.Fatalf("cleanTables: %v", err) 984 } 985 986 ord := newLimitOrder(false, 4900000, 1, order.StandingTiF, 0) // not stored 987 988 err := archie.UpdateOrderStatus(ord, order.OrderStatusExecuted) 989 if err == nil { 990 t.Fatalf("UpdateOrder succeeded to update nonexistent order!") 991 } 992 if !db.SameErrorTypes(err, db.ArchiveError{Code: db.ErrUnknownOrder}) { 993 if errA, ok := err.(db.ArchiveError); ok { 994 t.Fatalf("Expected ArchiveError with code ErrUnknownOrder, got %d", errA.Code) 995 } 996 t.Fatalf("Expected ArchiveError with code ErrUnknownOrder, got %v", err) 997 } 998 } 999 1000 func TestUpdateOrder(t *testing.T) { 1001 if err := cleanTables(archie.db); err != nil { 1002 t.Fatalf("cleanTables: %v", err) 1003 } 1004 1005 var epochIdx, epochDur int64 = 13245678, 6000 1006 1007 // order ID for a cancel order 1008 orderID0, _ := hex.DecodeString("dd64e2ae2845d281ba55a6d46eceb9297b2bdec5c5bada78f9ae9e373164df0d") 1009 var targetOrderID order.OrderID 1010 copy(targetOrderID[:], orderID0) 1011 1012 orderStatuses := []struct { 1013 ord order.Order 1014 status order.OrderStatus 1015 newStatus order.OrderStatus 1016 newFilled uint64 1017 wantErr bool 1018 }{ 1019 { 1020 newLimitOrder(false, 4900000, 1, order.StandingTiF, 0), 1021 order.OrderStatusEpoch, // active 1022 order.OrderStatusBooked, // active 1023 0, 1024 false, 1025 }, 1026 { 1027 newLimitOrder(false, 4100000, 1, order.StandingTiF, 0), 1028 order.OrderStatusBooked, // active 1029 order.OrderStatusExecuted, // archived 1030 0, 1031 false, 1032 }, 1033 { 1034 newLimitOrder(false, 4500000, 1, order.StandingTiF, 0), 1035 order.OrderStatusExecuted, // archived 1036 order.OrderStatusBooked, // active, should err 1037 0, 1038 true, 1039 }, 1040 { 1041 newMarketSellOrder(2, 0), 1042 order.OrderStatusEpoch, // active 1043 order.OrderStatusBooked, // active, invalid for market 1044 0, 1045 false, 1046 }, 1047 { 1048 newMarketSellOrder(1, 0), 1049 order.OrderStatusExecuted, // archived 1050 order.OrderStatusExecuted, // archived, no change 1051 0, 1052 false, 1053 }, 1054 { 1055 newMarketBuyOrder(2000000000, 0), 1056 order.OrderStatusEpoch, // active 1057 order.OrderStatusExecuted, // archived 1058 2000000000, 1059 false, 1060 }, 1061 { 1062 newCancelOrder(targetOrderID, mktInfo.Base, mktInfo.Quote, 1), 1063 order.OrderStatusEpoch, // active 1064 order.OrderStatusExecuted, // archived 1065 0, 1066 false, 1067 }, 1068 { 1069 newCancelOrder(targetOrderID, mktInfo.Base, mktInfo.Quote, 2), 1070 order.OrderStatusExecuted, // archived 1071 order.OrderStatusCanceled, // archived 1072 0, 1073 false, 1074 }, 1075 } 1076 1077 for i := range orderStatuses { 1078 ordIn := orderStatuses[i].ord 1079 statusIn := orderStatuses[i].status 1080 err := archie.StoreOrder(ordIn, epochIdx, epochDur, statusIn) 1081 if err != nil { 1082 t.Fatalf("StoreOrder failed: %v", err) 1083 } 1084 1085 switch ot := ordIn.(type) { 1086 case *order.LimitOrder: 1087 ot.FillAmt = orderStatuses[i].newFilled 1088 case *order.MarketOrder: 1089 ot.FillAmt = orderStatuses[i].newFilled 1090 } 1091 1092 newStatus := orderStatuses[i].newStatus 1093 err = archie.UpdateOrderStatus(ordIn, newStatus) 1094 if (err != nil) != orderStatuses[i].wantErr { 1095 t.Fatalf("UpdateOrderStatus(%d:%v, %s) failed: %v", i, ordIn, newStatus, err) 1096 } 1097 } 1098 } 1099 1100 func TestStorePreimage(t *testing.T) { 1101 if err := cleanTables(archie.db); err != nil { 1102 t.Fatalf("cleanTables: %v", err) 1103 } 1104 1105 var epochIdx, epochDur int64 = 13245678, 6000 1106 1107 orderID0, _ := hex.DecodeString("dd64e2ae2845d281ba55a6d46eceb9297b2bdec5c5bada78f9ae9e373164df0d") 1108 var targetOrderID order.OrderID 1109 copy(targetOrderID[:], orderID0) 1110 1111 lo, pi := newLimitOrderRevealed(false, 4900000, 1, order.StandingTiF, 0) 1112 err := archie.StoreOrder(lo, epochIdx, epochDur, order.OrderStatusEpoch) 1113 if err != nil { 1114 t.Fatalf("StoreOrder failed: %v", err) 1115 } 1116 1117 err = archie.StorePreimage(lo, pi) 1118 if err != nil { 1119 t.Fatalf("StoreOrder failed: %v", err) 1120 } 1121 1122 piOut, err := archie.OrderPreimage(lo) 1123 if err != nil { 1124 t.Fatalf("OrderPreimage failed: %v", err) 1125 } 1126 1127 if pi != piOut { 1128 t.Errorf("got preimage %v, expected %v", piOut, pi) 1129 } 1130 1131 // Now test OrderPreimage when preimage is NULL. 1132 lo2, _ := newLimitOrderRevealed(false, 4900000, 1, order.StandingTiF, 0) 1133 err = archie.StoreOrder(lo2, epochIdx, epochDur, order.OrderStatusEpoch) 1134 if err != nil { 1135 t.Fatalf("StoreOrder failed: %v", err) 1136 } 1137 1138 piOut2, err := archie.OrderPreimage(lo2) 1139 if err != nil { 1140 t.Fatalf("OrderPreimage failed: %v", err) 1141 } 1142 if !piOut2.IsZero() { 1143 t.Errorf("Preimage should have been the zero value, got %v", piOut2) 1144 } 1145 } 1146 1147 func TestFailCancelOrder(t *testing.T) { 1148 if err := cleanTables(archie.db); err != nil { 1149 t.Fatalf("cleanTables: %v", err) 1150 } 1151 1152 var epochIdx, epochDur int64 = 13245678, 6000 1153 1154 // order ID for a cancel order 1155 orderID0, _ := hex.DecodeString("dd64e2ae2845d281ba55a6d46eceb9297b2bdec5c5bada78f9ae9e373164df0d") 1156 var targetOrderID order.OrderID 1157 copy(targetOrderID[:], orderID0) 1158 1159 co := newCancelOrder(targetOrderID, mktInfo.Base, mktInfo.Quote, 1) 1160 err := archie.StoreOrder(co, epochIdx, epochDur, order.OrderStatusEpoch) 1161 if err != nil { 1162 t.Fatalf("StoreOrder failed: %v", err) 1163 } 1164 1165 err = archie.FailCancelOrder(co) 1166 if err != nil { 1167 t.Fatalf("StoreOrder failed: %v", err) 1168 } 1169 _, status, err := loadCancelOrder(archie.db, archie.dbName, mktInfo.Name, co.ID()) 1170 if err != nil { 1171 t.Errorf("loadCancelOrder failed: %v", err) 1172 } 1173 1174 if status != orderStatusFailed { 1175 t.Errorf("cancel order should have been %s, got %s", orderStatusFailed, status) 1176 } 1177 } 1178 1179 func TestUpdateOrderFilled(t *testing.T) { 1180 if err := cleanTables(archie.db); err != nil { 1181 t.Fatalf("cleanTables: %v", err) 1182 } 1183 1184 var epochIdx, epochDur int64 = 13245678, 6000 1185 1186 // order ID for a cancel order 1187 orderID0, _ := hex.DecodeString("dd64e2ae2845d281ba55a6d46eceb9297b2bdec5c5bada78f9ae9e373164df0d") 1188 var targetOrderID order.OrderID 1189 copy(targetOrderID[:], orderID0) 1190 1191 orderStatuses := []struct { 1192 ord *order.LimitOrder 1193 status order.OrderStatus 1194 newFilled uint64 1195 wantUpdateErr bool 1196 }{ 1197 { 1198 newLimitOrder(false, 4900000, 1, order.StandingTiF, 0), 1199 order.OrderStatusBooked, // active 1200 0, 1201 false, 1202 }, 1203 { 1204 newLimitOrder(false, 4100000, 1, order.StandingTiF, 0), 1205 order.OrderStatusBooked, // active 1206 0, 1207 false, 1208 }, 1209 { 1210 newLimitOrder(false, 4500000, 1, order.StandingTiF, 0), 1211 order.OrderStatusExecuted, // archived 1212 0, 1213 false, 1214 }, 1215 } 1216 1217 for i := range orderStatuses { 1218 ordIn := orderStatuses[i].ord 1219 statusIn := orderStatuses[i].status 1220 err := archie.StoreOrder(ordIn, epochIdx, epochDur, statusIn) 1221 if err != nil { 1222 t.Fatalf("StoreOrder failed: %v", err) 1223 } 1224 1225 ordIn.FillAmt = orderStatuses[i].newFilled 1226 1227 err = archie.UpdateOrderFilled(ordIn) 1228 if (err != nil) != orderStatuses[i].wantUpdateErr { 1229 t.Fatalf("UpdateOrderFilled(%d:%v) failed: %v", i, ordIn, err) 1230 } 1231 } 1232 } 1233 1234 func TestUserOrders(t *testing.T) { 1235 if err := cleanTables(archie.db); err != nil { 1236 t.Fatalf("cleanTables: %v", err) 1237 } 1238 1239 var epochIdx, epochDur int64 = 13245678, 6000 1240 1241 limitSell := newLimitOrder(true, 4900000, 1, order.StandingTiF, 0) 1242 limitBuy := newLimitOrder(false, 4100000, 1, order.StandingTiF, 0) 1243 marketSell := newMarketSellOrder(2, 0) 1244 marketBuy := newMarketBuyOrder(2000000000, 0) 1245 1246 // Make all of the above orders belong to the same user. 1247 aid := limitSell.AccountID 1248 limitBuy.AccountID = aid 1249 limitBuy.AccountID = aid 1250 marketSell.AccountID = aid 1251 marketBuy.AccountID = aid 1252 1253 marketSellOtherGuy := newMarketSellOrder(2, 0) 1254 marketSellOtherGuy.Address = "1MUz4VMYui5qY1mxUiG8BQ1Luv6tqkvaiL" 1255 1256 orderStatuses := []struct { 1257 ord order.Order 1258 status order.OrderStatus 1259 ordType order.OrderType 1260 wantErr bool 1261 }{ 1262 { 1263 limitSell, 1264 order.OrderStatusBooked, // active 1265 order.LimitOrderType, 1266 false, 1267 }, 1268 { 1269 limitBuy, 1270 order.OrderStatusCanceled, // archived 1271 order.LimitOrderType, 1272 false, 1273 }, 1274 { 1275 marketSell, 1276 order.OrderStatusEpoch, // active 1277 order.MarketOrderType, 1278 false, 1279 }, 1280 { 1281 marketBuy, 1282 order.OrderStatusExecuted, // archived 1283 order.MarketOrderType, 1284 false, 1285 }, 1286 { 1287 marketSellOtherGuy, 1288 order.OrderStatusExecuted, // archived 1289 order.MarketOrderType, 1290 false, 1291 }, 1292 } 1293 1294 for i := range orderStatuses { 1295 ordIn := orderStatuses[i].ord 1296 statusIn := orderStatuses[i].status 1297 err := archie.StoreOrder(ordIn, epochIdx, epochDur, statusIn) 1298 if err != nil { 1299 t.Fatalf("StoreOrder failed: %v", err) 1300 } 1301 } 1302 1303 ordersOut, statusesOut, err := archie.UserOrders(context.Background(), aid, mktInfo.Base, mktInfo.Quote) 1304 if err != nil { 1305 t.Error(err) 1306 } 1307 1308 if len(ordersOut) != len(statusesOut) { 1309 t.Errorf("UserOrders returned %d orders, but %d order status. Should be equal.", 1310 len(ordersOut), len(statusesOut)) 1311 } 1312 1313 numOrdersForGuy0 := len(orderStatuses) - 1 1314 if len(ordersOut) != numOrdersForGuy0 { 1315 t.Errorf("incorrect number of orders for user %d retrieved. "+ 1316 "got %d, expected %d", aid, len(ordersOut), numOrdersForGuy0) 1317 } 1318 1319 findExpected := func(ord order.Order) int { 1320 for i := range orderStatuses { 1321 if orderStatuses[i].ord.ID() == ord.ID() { 1322 return i 1323 } 1324 } 1325 return -1 1326 } 1327 1328 for i := range ordersOut { 1329 j := findExpected(ordersOut[i]) 1330 if j == -1 { 1331 t.Errorf("failed to find order %v", ordersOut[i]) 1332 continue 1333 } 1334 if ordersOut[i].Type() != orderStatuses[j].ordType { 1335 t.Errorf("wrong type %v, wanted %v", ordersOut[i].Type(), orderStatuses[j].ordType) 1336 } 1337 if statusesOut[i] != orderStatuses[j].status { 1338 t.Errorf("wrong status %v, wanted %v", statusesOut[i], orderStatuses[j].status) 1339 } 1340 } 1341 } 1342 func TestUserOrderStatuses(t *testing.T) { 1343 if err := cleanTables(archie.db); err != nil { 1344 t.Fatalf("cleanTables: %v", err) 1345 } 1346 1347 var epochIdx, epochDur int64 = 13245678, 6000 1348 1349 orderStatuses := []struct { 1350 ord order.Order 1351 status order.OrderStatus 1352 }{ 1353 { 1354 newLimitOrder(false, 4900000, 1, order.StandingTiF, 0), 1355 order.OrderStatusBooked, // active 1356 }, 1357 { 1358 newLimitOrder(false, 4500000, 1, order.StandingTiF, 0), 1359 order.OrderStatusExecuted, // archived 1360 }, 1361 { 1362 newMarketSellOrder(2, 0), 1363 order.OrderStatusEpoch, // active 1364 }, 1365 { 1366 newMarketSellOrder(1, 0), 1367 order.OrderStatusExecuted, // archived 1368 }, 1369 { 1370 newMarketBuyOrder(2000000000, 0), 1371 order.OrderStatusEpoch, // active 1372 }, 1373 { 1374 newMarketBuyOrder(2100000000, 0), 1375 order.OrderStatusExecuted, // archived 1376 }, 1377 } 1378 1379 unsavedOrder1 := newMarketBuyOrder(3000000000, 0) 1380 unsavedOrder2 := newMarketSellOrder(4, 0) 1381 1382 orders := make([]order.Order, 0, len(orderStatuses)+2) 1383 orderIDs := make([]order.OrderID, 0, len(orderStatuses)+2) 1384 1385 // Add unsaved orders 1386 orders = append(orders, unsavedOrder1, unsavedOrder2) 1387 orderIDs = append(orderIDs, unsavedOrder1.ID(), unsavedOrder2.ID()) 1388 1389 accountID := randomAccountID() 1390 for i := range orderStatuses { 1391 ordIn := orderStatuses[i].ord 1392 statusIn := orderStatuses[i].status 1393 if lo, ok := ordIn.(*order.LimitOrder); ok { 1394 lo.BaseAsset, lo.QuoteAsset = AssetBTC, AssetLTC // swap the assets to test across different mkts 1395 } 1396 ordIn.Prefix().AccountID = accountID 1397 err := archie.StoreOrder(ordIn, epochIdx, epochDur, statusIn) 1398 if err != nil { 1399 t.Fatalf("StoreOrder failed: %v", err) 1400 } 1401 orders = append(orders, ordIn) 1402 orderIDs = append(orderIDs, ordIn.ID()) 1403 } 1404 1405 // All orders except the 2 limit orders are DCR-BTC. 1406 orderStatusesOut, err := archie.UserOrderStatuses(accountID, AssetDCR, AssetBTC, orderIDs) 1407 if err != nil { 1408 t.Fatalf("OrderStatuses failed: %v", err) 1409 } 1410 if len(orderStatusesOut) != len(orderStatuses)-2 /*the 2 limits*/ { 1411 t.Fatalf("OrderStatuses returned %d orders instead of %d", len(orderStatusesOut), len(orderStatuses)-2) 1412 } 1413 outMap := make(map[order.OrderID]*db.OrderStatus, len(orderStatusesOut)) 1414 for _, orderStatus := range orderStatusesOut { 1415 outMap[orderStatus.ID] = orderStatus 1416 } 1417 for i := range orderStatuses { 1418 ordIn := orderStatuses[i].ord 1419 orderOut, found := outMap[ordIn.ID()] 1420 if !found { 1421 continue 1422 } 1423 statusIn := orderStatuses[i].status 1424 if orderOut.Status != statusIn { 1425 t.Errorf("Incorrect OrderStatus for retrieved order. Got %v, expected %v.", 1426 orderOut.Status, statusIn) 1427 } 1428 } 1429 1430 // Check statuses for the 2 limit orders that are BTC-LTC. 1431 orderStatusesOut, err = archie.UserOrderStatuses(accountID, AssetBTC, AssetLTC, orderIDs) 1432 if err != nil { 1433 t.Fatalf("OrderStatuses failed: %v", err) 1434 } 1435 if len(orderStatusesOut) != 2 /*the 2 limits*/ { 1436 t.Fatalf("OrderStatuses returned %d orders instead of %d", len(orderStatusesOut), 2) 1437 } 1438 outMap = make(map[order.OrderID]*db.OrderStatus, len(orderStatusesOut)) 1439 for _, orderStatus := range orderStatusesOut { 1440 outMap[orderStatus.ID] = orderStatus 1441 } 1442 for i := range orderStatuses { 1443 ordIn := orderStatuses[i].ord 1444 orderOut, found := outMap[ordIn.ID()] 1445 if !found { 1446 continue 1447 } 1448 statusIn := orderStatuses[i].status 1449 if orderOut.Status != statusIn { 1450 t.Errorf("Incorrect OrderStatus for retrieved order. Got %v, expected %v.", 1451 orderOut.Status, statusIn) 1452 } 1453 } 1454 1455 // Expect nothing for wrong user ID. 1456 orderStatusesOut, err = archie.UserOrderStatuses(randomAccountID(), AssetDCR, AssetBTC, orderIDs) 1457 if err != nil { 1458 t.Fatalf("OrderStatuses failed: %v", err) 1459 } 1460 if len(orderStatusesOut) != 0 { 1461 t.Fatalf("OrderStatuses returned %d orders for wrong account ID", len(orderStatusesOut)) 1462 } 1463 } 1464 func TestActiveUserOrderStatuses(t *testing.T) { 1465 if err := cleanTables(archie.db); err != nil { 1466 t.Fatalf("cleanTables: %v", err) 1467 } 1468 1469 // Two orders, different accounts, DCR-BTC. 1470 maker := newLimitOrder(false, 4500000, 1, order.StandingTiF, 0) 1471 taker := newLimitOrder(true, 4490000, 1, order.StandingTiF, 10) 1472 1473 var epochIdx, epochDur int64 = 13245678, 6000 1474 err := archie.StoreOrder(maker, epochIdx, epochDur, order.OrderStatusBooked) 1475 if err != nil { 1476 t.Fatalf("StoreOrder failed: %v", err) 1477 } 1478 err = archie.StoreOrder(taker, epochIdx, epochDur, order.OrderStatusBooked) 1479 if err != nil { 1480 t.Fatalf("StoreOrder failed: %v", err) 1481 } 1482 1483 // Second order from the same maker account. 1484 maker2 := newLimitOrder(false, 4500000, 1, order.ImmediateTiF, 20) 1485 maker2.AccountID = maker.AccountID 1486 1487 // Store it. 1488 err = archie.StoreOrder(maker2, epochIdx, epochDur, order.OrderStatusEpoch) 1489 if err != nil { 1490 t.Fatalf("StoreOrder failed: %v", err) 1491 } 1492 1493 // Same taker account, different market (BTC-LTC). 1494 taker2 := newLimitOrder(true, 4490000, 1, order.ImmediateTiF, 30) 1495 taker2.BaseAsset = AssetBTC 1496 taker2.QuoteAsset = AssetLTC 1497 taker2.AccountID = taker.AccountID 1498 1499 // Store it. 1500 err = archie.StoreOrder(taker2, epochIdx, epochDur, order.OrderStatusEpoch) 1501 if err != nil { 1502 t.Fatalf("StoreOrder failed: %v", err) 1503 } 1504 1505 // Store cancel order for taker account. 1506 taker2Incomplete := newLimitOrder(true, 4390000, 1, order.StandingTiF, 20) 1507 taker2Incomplete.AccountID = taker.AccountID 1508 err = archie.StoreOrder(taker2Incomplete, epochIdx, epochDur, order.OrderStatusCanceled) 1509 if err != nil { 1510 t.Fatalf("StoreOrder failed: %v", err) 1511 } 1512 1513 // Maker should have 2 active orders in 1 market. 1514 // Taker should have 2 active orders in 2 markets and 1 inactive (canceled) order. 1515 1516 tests := []struct { 1517 name string 1518 acctID account.AccountID 1519 numExpected int 1520 wantOrderIDs []order.OrderID 1521 wantOrderStatuses []order.OrderStatus 1522 wantedErr error 1523 }{ 1524 { 1525 "ok maker", 1526 maker.User(), 1527 2, 1528 []order.OrderID{maker.ID(), maker2.ID()}, 1529 []order.OrderStatus{order.OrderStatusBooked, order.OrderStatusEpoch}, 1530 nil, 1531 }, 1532 { 1533 "ok taker", 1534 taker.User(), 1535 2, 1536 []order.OrderID{taker.ID(), taker2.ID()}, 1537 []order.OrderStatus{order.OrderStatusBooked, order.OrderStatusEpoch}, 1538 nil, 1539 }, 1540 { 1541 "nope", 1542 randomAccountID(), 1543 0, 1544 nil, 1545 nil, 1546 nil, 1547 }, 1548 } 1549 1550 idInSlice := func(oid order.OrderID, oids []order.OrderID) int { 1551 for i := range oids { 1552 if oids[i] == oid { 1553 return i 1554 } 1555 } 1556 return -1 1557 } 1558 1559 for _, tt := range tests { 1560 t.Run(tt.name, func(t *testing.T) { 1561 orderStatuses, err := archie.ActiveUserOrderStatuses(tt.acctID) 1562 if err != tt.wantedErr { 1563 t.Fatal(err) 1564 } 1565 if len(orderStatuses) != tt.numExpected { 1566 t.Errorf("Retrieved %d active orders for user %v, expected %d.", len(orderStatuses), tt.acctID, tt.numExpected) 1567 } 1568 for _, ord := range orderStatuses { 1569 wantId := idInSlice(ord.ID, tt.wantOrderIDs) 1570 if wantId == -1 { 1571 t.Errorf("Unexpected order ID %v retrieved.", ord.ID) 1572 continue 1573 } 1574 if ord.Status != tt.wantOrderStatuses[wantId] { 1575 t.Errorf("Incorrect order status for order %v. Got %d, want %d.", 1576 ord.ID, ord.Status, tt.wantOrderStatuses[wantId]) 1577 } 1578 } 1579 }) 1580 } 1581 } 1582 1583 func TestCompletedUserOrders(t *testing.T) { 1584 if err := cleanTables(archie.db); err != nil { 1585 t.Fatalf("cleanTables: %v", err) 1586 } 1587 1588 nowMs := func() int64 { 1589 return time.Now().UnixMilli() 1590 } 1591 1592 // Two orders, different accounts, DCR-BTC. 1593 maker := newLimitOrder(false, 4500000, 1, order.StandingTiF, 0) 1594 taker := newLimitOrder(true, 4490000, 1, order.ImmediateTiF, 10) 1595 1596 var epochIdx, epochDur int64 = 13245678, 6000 1597 err := archie.StoreOrder(maker, epochIdx, epochDur, order.OrderStatusExecuted) 1598 if err != nil { 1599 t.Fatalf("StoreOrder failed: %v", err) 1600 } 1601 err = archie.StoreOrder(taker, epochIdx, epochDur, order.OrderStatusExecuted) 1602 if err != nil { 1603 t.Fatalf("StoreOrder failed: %v", err) 1604 } 1605 1606 // Set the orders' swap completion times. 1607 tSwapDoneMaker := nowMs() 1608 if err = archie.SetOrderCompleteTime(maker, tSwapDoneMaker); err != nil { 1609 t.Fatalf("SetOrderCompleteTime failed: %v", err) 1610 } 1611 1612 tSwapDoneTaker := tSwapDoneMaker + 10 1613 if err = archie.SetOrderCompleteTime(taker, tSwapDoneTaker); err != nil { 1614 t.Fatalf("SetOrderCompleteTime failed: %v", err) 1615 } 1616 1617 // Second order from the same maker account. 1618 maker2 := newLimitOrder(false, 4500000, 1, order.StandingTiF, 20) 1619 maker2.AccountID = maker.AccountID 1620 1621 // Store it. 1622 err = archie.StoreOrder(maker2, epochIdx, epochDur, order.OrderStatusExecuted) 1623 if err != nil { 1624 t.Fatalf("StoreOrder failed: %v", err) 1625 } 1626 // Set swap complete time. 1627 tSwapDoneMaker2 := nowMs() 1628 if err = archie.SetOrderCompleteTime(maker2, tSwapDoneMaker2); err != nil { 1629 t.Fatalf("SetOrderCompleteTime failed: %v", err) 1630 } 1631 1632 // Same taker account, different market (BTC-LTC). 1633 taker2 := newLimitOrder(true, 4490000, 1, order.ImmediateTiF, 30) 1634 taker2.BaseAsset = AssetBTC 1635 taker2.QuoteAsset = AssetLTC 1636 taker2.AccountID = taker.AccountID 1637 1638 // Store it. 1639 err = archie.StoreOrder(taker2, epochIdx, epochDur, order.OrderStatusExecuted) 1640 if err != nil { 1641 t.Fatalf("StoreOrder failed: %v", err) 1642 } 1643 1644 // Set swap complete time. 1645 tSwapDoneTaker2 := nowMs() 1646 if err = archie.SetOrderCompleteTime(taker2, tSwapDoneTaker2); err != nil { 1647 t.Fatalf("SetOrderCompleteTime failed: %v", err) 1648 } 1649 1650 // Order without completion time set. 1651 taker2Incomplete := newLimitOrder(true, 4390000, 1, order.StandingTiF, 20) 1652 taker2Incomplete.AccountID = taker.AccountID 1653 err = archie.StoreOrder(taker2Incomplete, epochIdx, epochDur, order.OrderStatusCanceled) // archived, but not complete 1654 if err != nil { 1655 t.Fatalf("StoreOrder failed: %v", err) 1656 } 1657 // NO SetOrderCompleteTime, BUT in an orders_archived table. 1658 1659 // Try and fail to set completion time for an order not in executed status. 1660 taker3 := newLimitOrder(true, 4390000, 1, order.StandingTiF, 20) 1661 err = archie.StoreOrder(taker3, epochIdx, epochDur, order.OrderStatusBooked) 1662 if err != nil { 1663 t.Fatalf("StoreOrder failed: %v", err) 1664 } 1665 1666 // Set swap complete time. 1667 tSwapDoneTaker3 := nowMs() 1668 if err = archie.SetOrderCompleteTime(taker3, tSwapDoneTaker3); !db.IsErrOrderNotExecuted(err) { 1669 t.Fatalf("SetOrderCompleteTime should have returned a ErrOrderNotExecuted error for booked (not executed) order") 1670 } 1671 1672 // Maker should have 2 completed orders in 1 market. 1673 // Taker should have 2 completed orders in 2 markets. 1674 1675 tests := []struct { 1676 name string 1677 acctID account.AccountID 1678 numExpected int 1679 wantOrderIDs []order.OrderID 1680 wantCompTimes []int64 1681 wantedErr error 1682 }{ 1683 { 1684 "ok maker", 1685 maker.User(), 1686 2, 1687 []order.OrderID{maker.ID(), maker2.ID()}, 1688 []int64{tSwapDoneMaker, tSwapDoneMaker2}, 1689 nil, 1690 }, 1691 { 1692 "ok taker", 1693 taker.User(), 1694 2, 1695 []order.OrderID{taker.ID(), taker2.ID()}, 1696 []int64{tSwapDoneTaker, tSwapDoneTaker2}, 1697 nil, 1698 }, 1699 { 1700 "nope", 1701 randomAccountID(), 1702 0, 1703 nil, 1704 nil, 1705 nil, 1706 }, 1707 } 1708 1709 idInSlice := func(mid order.OrderID, mids []order.OrderID) int { 1710 for i := range mids { 1711 if mids[i] == mid { 1712 return i 1713 } 1714 } 1715 return -1 1716 } 1717 1718 for _, tt := range tests { 1719 t.Run(tt.name, func(t *testing.T) { 1720 oids, compTimes, err := archie.CompletedUserOrders(tt.acctID, cancelThreshWindow) 1721 if err != tt.wantedErr { 1722 t.Fatal(err) 1723 } 1724 if len(oids) != tt.numExpected { 1725 t.Errorf("Retrieved %d completed orders for user %v, expected %d.", len(oids), tt.acctID, tt.numExpected) 1726 } 1727 for i := range oids { 1728 loc := idInSlice(oids[i], tt.wantOrderIDs) 1729 if loc == -1 { 1730 t.Errorf("Unexpected order ID %v retrieved.", oids[i]) 1731 continue 1732 } 1733 if compTimes[i] != tt.wantCompTimes[loc] { 1734 t.Errorf("Incorrect order completion time. Got %d, want %d.", 1735 compTimes[loc], tt.wantCompTimes[i]) 1736 } 1737 } 1738 }) 1739 } 1740 } 1741 1742 func TestExecutedCancelsForUser(t *testing.T) { 1743 if err := cleanTables(archie.db); err != nil { 1744 t.Fatalf("cleanTables: %v", err) 1745 } 1746 1747 var epochIdx, epochDur int64 = 13245678, 6000 1748 1749 // order ID for a cancel order 1750 orderID0, _ := hex.DecodeString("dd64e2ae2845d281ba55a6d46eceb9297b2bdec5c5bada78f9ae9e373164df0d") 1751 var targetOrderID order.OrderID 1752 copy(targetOrderID[:], orderID0) 1753 1754 co := newCancelOrder(targetOrderID, mktInfo.Base, mktInfo.Quote, 1) 1755 err := archie.StoreOrder(co, epochIdx, epochDur, order.OrderStatusEpoch) 1756 if err != nil { 1757 t.Fatalf("StoreOrder failed: %v", err) 1758 } 1759 1760 // Mark the cancel order executed. 1761 err = archie.ExecuteOrder(co) 1762 if err != nil { 1763 t.Fatalf("ExecuteOrder failed: %v", err) 1764 } 1765 _, status, err := loadCancelOrder(archie.db, archie.dbName, mktInfo.Name, co.ID()) 1766 if err != nil { 1767 t.Errorf("loadCancelOrder failed: %v", err) 1768 } 1769 if status != orderStatusExecuted { 1770 t.Fatalf("cancel order should have been %s, got %s", orderStatusFailed, status) 1771 } 1772 1773 // order ID for a revoked order 1774 lo := newLimitOrder(true, 4900000, 1, order.StandingTiF, 0) 1775 lo.AccountID = co.AccountID // same user 1776 lo.BaseAsset, lo.QuoteAsset = mktInfo.Base, mktInfo.Quote 1777 err = archie.StoreOrder(lo, epochIdx, epochDur, order.OrderStatusBooked) 1778 if err != nil { 1779 t.Fatalf("StoreOrder failed: %v", err) 1780 } 1781 1782 // Revoke the order. 1783 time.Sleep(time.Millisecond * 10) // ensure the resulting cancel order is newer than the other cancel order above. 1784 coID, coTime, err := archie.RevokeOrder(lo) 1785 if err != nil { 1786 t.Fatalf("StoreOrder failed: %v", err) 1787 } 1788 coOut, coStatusOut, err := loadCancelOrder(archie.db, archie.dbName, mktInfo.Name, coID) 1789 // loadCancelOrder does not set base and quote 1790 coOut.BaseAsset, coOut.QuoteAsset = mktInfo.Base, mktInfo.Quote 1791 if err != nil { 1792 t.Errorf("loadCancelOrder failed: %v", err) 1793 } 1794 if coStatusOut != orderStatusRevoked { 1795 t.Fatalf("cancel order should have been %s, got %s", orderStatusRevoked, status) 1796 } 1797 if coOut.ID() != coID { 1798 t.Errorf("incorrect cancel order ID. got %v, expected %v", coOut.ID(), coID) 1799 } 1800 if coTimeMs := coTime.UnixMilli(); coOut.Time() != coTimeMs { 1801 t.Errorf("incorrect cancel time. got %d, expected %d", coOut.Time(), coTimeMs) 1802 } 1803 1804 // Store the epoch. 1805 matchTime := time.Now().UnixMilli() 1806 err = archie.InsertEpoch(&db.EpochResults{ 1807 MktBase: mktInfo.Base, 1808 MktQuote: mktInfo.Quote, 1809 Idx: epochIdx, 1810 Dur: epochDur, 1811 MatchTime: matchTime, 1812 OrdersRevealed: []order.OrderID{co.ID()}, // not needed, but would be the case if it were executed in this epoch 1813 }) 1814 if err != nil { 1815 t.Errorf("InsertEpoch failed: %v", err) 1816 } 1817 1818 // A revoked order (exempt cancel), which should NOT be found with 1819 // ExecutedCancelsForUser. 1820 lo2 := newLimitOrder(true, 4900000, 1, order.StandingTiF, 1) 1821 lo2.AccountID = co.AccountID // same user 1822 lo2.BaseAsset, lo2.QuoteAsset = mktInfo.Base, mktInfo.Quote 1823 err = archie.StoreOrder(lo2, epochIdx, epochDur, order.OrderStatusBooked) 1824 if err != nil { 1825 t.Fatalf("StoreOrder failed: %v", err) 1826 } 1827 1828 // Revoke the order. 1829 time.Sleep(time.Millisecond * 10) 1830 _, _, err = archie.RevokeOrderUncounted(lo2) 1831 if err != nil { 1832 t.Fatalf("StoreOrder failed: %v", err) 1833 } 1834 1835 user := co.User() 1836 cancels, err := archie.ExecutedCancelsForUser(user, cancelThreshWindow) 1837 if err != nil { 1838 t.Errorf("ExecutedCancelsForUser failed: %v", err) 1839 } 1840 if len(cancels) != 2 { 1841 t.Fatalf("found %d orders, expected 1", len(cancels)) 1842 } 1843 if cancels[0].ID != co.ID() { 1844 t.Errorf("incorrect executed cancel %v, expected %v", cancels[0].ID, co.ID()) 1845 } 1846 if cancels[0].TargetID != targetOrderID { 1847 t.Errorf("incorrect target for executed cancel %v, expected %v", cancels[0].TargetID, targetOrderID) 1848 } 1849 if cancels[0].MatchTime != matchTime { 1850 t.Errorf("incorrect exec time for executed cancel %v, expected %v", cancels[0].MatchTime, matchTime) 1851 } 1852 if cancels[1].ID != coID { 1853 t.Errorf("incorrect executed cancel %v, expected %v", cancels[1].ID, coID) 1854 } 1855 if cancels[1].TargetID != lo.ID() { 1856 t.Errorf("incorrect target for executed cancel %v, expected %v", cancels[1].TargetID, lo.ID()) 1857 } 1858 if coTimeMs := coTime.UnixMilli(); cancels[1].MatchTime != coTimeMs { 1859 t.Errorf("incorrect exec time for executed cancel %v, expected %v", cancels[1].MatchTime, coTimeMs) 1860 } 1861 1862 // test the limit 1863 cancels, err = archie.ExecutedCancelsForUser(user, 0) 1864 if err != nil { 1865 t.Errorf("ExecutedCancelsForUser failed: %v", err) 1866 } 1867 if len(cancels) > 0 { 1868 t.Errorf("found executed orders for user") 1869 } 1870 1871 // Cancel order in epoch status, and with no epochs table entry. 1872 co2 := newCancelOrder(targetOrderID, mktInfo.Base, mktInfo.Quote, 1) 1873 co2.AccountID = randomAccountID() // different user 1874 epochIdx++ 1875 err = archie.StoreOrder(co2, epochIdx, epochDur, order.OrderStatusEpoch) 1876 if err != nil { 1877 t.Fatalf("StoreOrder failed: %v", err) 1878 } 1879 err = archie.FailCancelOrder(co2) 1880 if err != nil { 1881 t.Fatalf("FailCancelOrder failed: %v", err) 1882 } 1883 1884 user2 := co2.User() 1885 cancels, err = archie.ExecutedCancelsForUser(user2, cancelThreshWindow) 1886 if err != nil { 1887 t.Errorf("ExecutedCancelsForUser failed: %v", err) 1888 } 1889 if len(cancels) > 0 { 1890 t.Errorf("found executed orders for user") 1891 } 1892 1893 // Cancel order in failed status, with an epochs table entry. 1894 co3 := newCancelOrder(targetOrderID, mktInfo.Base, mktInfo.Quote, 1) 1895 co3.AccountID = randomAccountID() // different user 1896 epochIdx++ 1897 err = archie.StoreOrder(co3, epochIdx, epochDur, order.OrderStatusEpoch) 1898 if err != nil { 1899 t.Fatalf("StoreOrder failed: %v", err) 1900 } 1901 err = archie.FailCancelOrder(co3) 1902 if err != nil { 1903 t.Fatalf("ExecuteOrder failed: %v", err) 1904 } 1905 1906 // Store the epoch. 1907 matchTime3 := time.Now().UnixMilli() 1908 err = archie.InsertEpoch(&db.EpochResults{ 1909 MktBase: mktInfo.Base, 1910 MktQuote: mktInfo.Quote, 1911 Idx: epochIdx, 1912 Dur: epochDur, 1913 MatchTime: matchTime3, 1914 OrdersRevealed: []order.OrderID{co3.ID()}, // not needed, but would be the case if it were executed in this epoch 1915 }) 1916 if err != nil { 1917 t.Errorf("InsertEpoch failed: %v", err) 1918 } 1919 1920 user3 := co3.User() 1921 cancels, err = archie.ExecutedCancelsForUser(user3, cancelThreshWindow) 1922 if err != nil { 1923 t.Errorf("ExecutedCancelsForUser failed: %v", err) 1924 } 1925 if len(cancels) > 0 { 1926 t.Errorf("found executed orders for user") 1927 } 1928 1929 // Cancel order in executed status, but with no epochs table entry. 1930 co4 := newCancelOrder(targetOrderID, mktInfo.Base, mktInfo.Quote, 1) 1931 co4.AccountID = randomAccountID() // different user 1932 epochIdx++ 1933 err = archie.StoreOrder(co4, epochIdx, epochDur, order.OrderStatusEpoch) 1934 if err != nil { 1935 t.Fatalf("StoreOrder failed: %v", err) 1936 } 1937 err = archie.ExecuteOrder(co4) 1938 if err != nil { 1939 t.Fatalf("ExecuteOrder failed: %v", err) 1940 } 1941 1942 user4 := co4.User() 1943 cancels, err = archie.ExecutedCancelsForUser(user4, cancelThreshWindow) 1944 if err != nil { 1945 t.Errorf("ExecutedCancelsForUser failed: %v", err) 1946 } 1947 if len(cancels) > 0 { 1948 t.Errorf("found executed orders for user") 1949 } 1950 }