bullshit
This commit is contained in:
@@ -191,22 +191,22 @@ class ConcurrentCurrencyExchangeTest {
|
||||
|
||||
@Test
|
||||
void testConcurrentOrderPlacement() throws InterruptedException {
|
||||
var numSellers = 5;
|
||||
var numBuyers = 5;
|
||||
var numSellers = 10;
|
||||
var numBuyers = 10;
|
||||
var ordersPerClient = 20;
|
||||
var latch = new CountDownLatch(numSellers + numBuyers);
|
||||
|
||||
var sellers = new ArrayList<Client>();
|
||||
for (int i = 0; i < numSellers; i++) {
|
||||
var seller = exchange.createClient("Seller " + i).join();
|
||||
exchange.deposit(seller.id(), CNY, 1000.0).join();
|
||||
exchange.deposit(seller.id(), CNY, 1000000.0).join();
|
||||
sellers.add(seller);
|
||||
}
|
||||
|
||||
var buyers = new ArrayList<Client>();
|
||||
for (int i = 0; i < numBuyers; i++) {
|
||||
var buyer = exchange.createClient("Buyer " + i).join();
|
||||
exchange.deposit(buyer.id(), RUB, 1200.0).join();
|
||||
exchange.deposit(buyer.id(), RUB, 1200000.0).join();
|
||||
buyers.add(buyer);
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ class ConcurrentCurrencyExchangeTest {
|
||||
.map(seller -> CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
for (int i = 0; i < ordersPerClient; i++) {
|
||||
exchange.placeOrder(seller.id(), RUB_CNY, OrderType.SELL, 1.2, 10.0).join();
|
||||
exchange.placeOrder(seller.id(), RUB_CNY, OrderType.SELL, 10.0, 10.0).join();
|
||||
Thread.sleep(10);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
@@ -350,20 +350,22 @@ class ConcurrentCurrencyExchangeTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBalanceConsistencyWithConcurrentTradesAndWithdrawals() throws InterruptedException {
|
||||
var numTraders = 5;
|
||||
void testBalanceConsistencyWithConcurrentTrades() throws InterruptedException {
|
||||
var numTraders = 10;
|
||||
var numOrders = 1000;
|
||||
var latch = new CountDownLatch(numTraders * 2);
|
||||
|
||||
var sellers = new ArrayList<Client>();
|
||||
var buyers = new ArrayList<Client>();
|
||||
var initialAmount = 1000.0;
|
||||
|
||||
var sellAmount = 100.0;
|
||||
var buyAmount = 100.0;
|
||||
|
||||
for (var i = 0; i < numTraders; i++) {
|
||||
var seller = exchange.createClient("Seller " + i).join();
|
||||
var buyer = exchange.createClient("Buyer " + i).join();
|
||||
|
||||
exchange.deposit(seller.id(), CNY, initialAmount).join();
|
||||
exchange.deposit(buyer.id(), RUB, initialAmount).join();
|
||||
exchange.deposit(seller.id(), CNY, sellAmount * numOrders).join();
|
||||
exchange.deposit(buyer.id(), RUB, buyAmount * numOrders).join();
|
||||
|
||||
sellers.add(seller);
|
||||
buyers.add(buyer);
|
||||
@@ -374,17 +376,10 @@ class ConcurrentCurrencyExchangeTest {
|
||||
for (var seller : sellers) {
|
||||
var future = CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
exchange.placeOrder(seller.id(), RUB_CNY, OrderType.SELL, 1.2, 10.0).join();
|
||||
try {
|
||||
exchange.withdraw(seller.id(), CNY, 1.0).join();
|
||||
} catch (CompletionException ignored) {
|
||||
|
||||
}
|
||||
Thread.sleep(10);
|
||||
for (var i = 0; i < numOrders; i++)
|
||||
{
|
||||
exchange.placeOrder(seller.id(), RUB_CNY, OrderType.SELL, buyAmount, sellAmount).join();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
} finally {
|
||||
latch.countDown();
|
||||
}
|
||||
@@ -395,16 +390,10 @@ class ConcurrentCurrencyExchangeTest {
|
||||
for (var buyer : buyers) {
|
||||
var future = CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
exchange.placeOrder(buyer.id(), RUB_CNY, OrderType.BUY, 1.2, 10.0).join();
|
||||
try {
|
||||
exchange.withdraw(buyer.id(), RUB, 1.0).join();
|
||||
} catch (CompletionException ignored) {
|
||||
}
|
||||
Thread.sleep(10);
|
||||
for (var i = 0; i < numOrders; i++)
|
||||
{
|
||||
exchange.placeOrder(buyer.id(), RUB_CNY, OrderType.BUY, sellAmount, buyAmount).join();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
} finally {
|
||||
latch.countDown();
|
||||
}
|
||||
@@ -414,12 +403,22 @@ class ConcurrentCurrencyExchangeTest {
|
||||
|
||||
latch.await(30, TimeUnit.SECONDS);
|
||||
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
|
||||
|
||||
for (var client : Stream.concat(sellers.stream(), buyers.stream()).toList()) {
|
||||
var balances = exchange.getBalances(client.id()).join();
|
||||
assertTrue(balances.get(RUB) >= 0, "RUB balance should not be negative");
|
||||
assertTrue(balances.get(CNY) >= 0, "CNY balance should not be negative");
|
||||
var totalRubMoney = 0.0;
|
||||
var totalCnyMoney = 0.0;
|
||||
|
||||
for (var seller : sellers) {
|
||||
var balances = exchange.getBalances(seller.id()).join();
|
||||
totalCnyMoney += balances.get(CNY);
|
||||
totalRubMoney += balances.get(RUB);
|
||||
}
|
||||
for (var buyer : buyers) {
|
||||
var balances = exchange.getBalances(buyer.id()).join();
|
||||
totalCnyMoney += balances.get(CNY);
|
||||
totalRubMoney += balances.get(RUB);
|
||||
}
|
||||
|
||||
assertEquals(totalRubMoney, sellAmount * numOrders * numTraders, 0.001);
|
||||
assertEquals(totalCnyMoney, sellAmount * numOrders * numTraders, 0.001);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
|
||||
@@ -30,8 +30,7 @@ class CurrencyExchangeTest {
|
||||
Arrays.asList(RUB_CNY)
|
||||
);
|
||||
}
|
||||
|
||||
// Client Tests
|
||||
|
||||
@Test
|
||||
void testCreateClient() throws ExecutionException, InterruptedException {
|
||||
var client = exchange.createClient("Trader").get();
|
||||
@@ -52,7 +51,7 @@ class CurrencyExchangeTest {
|
||||
assertThrows(Exception.class, () -> exchange.getBalances(nonexistentId).get());
|
||||
}
|
||||
|
||||
// Deposit Tests
|
||||
|
||||
@Test
|
||||
void testDeposit() throws ExecutionException, InterruptedException {
|
||||
var client = exchange.createClient("Trader").get();
|
||||
@@ -77,7 +76,7 @@ class CurrencyExchangeTest {
|
||||
assertThrows(ExecutionException.class, () -> exchange.deposit(client.id(), null, 100.0).get());
|
||||
}
|
||||
|
||||
// Withdraw Tests
|
||||
|
||||
@Test
|
||||
void testWithdraw() throws ExecutionException, InterruptedException {
|
||||
var client = exchange.createClient("Trader").get();
|
||||
@@ -104,16 +103,16 @@ class CurrencyExchangeTest {
|
||||
|
||||
exchange.deposit(client.id(), RUB, 500.0).get();
|
||||
var balancesAfterDeposit = exchange.getBalances(client.id()).get();
|
||||
assertEquals(500.0, balancesAfterDeposit.get(RUB)); // Trader has 500 RUB
|
||||
assertEquals(500.0, balancesAfterDeposit.get(RUB));
|
||||
|
||||
exchange.withdraw(client.id(), RUB, 200.0).get();
|
||||
var balancesAfterWithdraw = exchange.getBalances(client.id()).get();
|
||||
assertEquals(300.0, balancesAfterWithdraw.get(RUB)); // Trader has 300 RUB
|
||||
assertEquals(300.0, balancesAfterWithdraw.get(RUB));
|
||||
|
||||
assertThrows(ExecutionException.class, () -> exchange.withdraw(client.id(), RUB, 400.0).get()); // Trader has only 300 RUB left
|
||||
assertThrows(ExecutionException.class, () -> exchange.withdraw(client.id(), RUB, 400.0).get());
|
||||
}
|
||||
|
||||
// Order Tests
|
||||
|
||||
@Test
|
||||
void testPlaceOrder() throws ExecutionException, InterruptedException {
|
||||
var client = exchange.createClient("Trader").get();
|
||||
@@ -156,7 +155,7 @@ class CurrencyExchangeTest {
|
||||
);
|
||||
}
|
||||
|
||||
// Order Matching Tests
|
||||
|
||||
@Test
|
||||
void testMatchingOrders() throws ExecutionException, InterruptedException {
|
||||
var buyer = exchange.createClient("Buyer").get();
|
||||
@@ -171,10 +170,10 @@ class CurrencyExchangeTest {
|
||||
Map<Currency, Double> buyerBalances = exchange.getBalances(buyer.id()).get();
|
||||
Map<Currency, Double> sellerBalances = exchange.getBalances(seller.id()).get();
|
||||
|
||||
assertEquals(100.0, buyerBalances.get(CNY)); // Buyer bought 100 CNY
|
||||
assertEquals(0.0, buyerBalances.get(RUB)); // Buyer sold 120 RUB so he has no RUB left
|
||||
assertEquals(120.0, sellerBalances.get(RUB)); // Seller bought 120 RUB
|
||||
assertEquals(0.0, sellerBalances.get(CNY)); // Seller sold 100 CNY so he has no CNY left
|
||||
assertEquals(100.0, buyerBalances.get(CNY));
|
||||
assertEquals(0.0, buyerBalances.get(RUB));
|
||||
assertEquals(120.0, sellerBalances.get(RUB));
|
||||
assertEquals(0.0, sellerBalances.get(CNY));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -191,13 +190,13 @@ class CurrencyExchangeTest {
|
||||
var buyerBalances = exchange.getBalances(buyer.id()).get();
|
||||
var sellerBalances = exchange.getBalances(seller.id()).get();
|
||||
|
||||
assertEquals(50.0, buyerBalances.get(CNY)); // Buyer bought 50 CNY
|
||||
assertEquals(60.0, sellerBalances.get(RUB)); // Seller bought 60 RUB
|
||||
assertEquals(50.0, buyerBalances.get(CNY));
|
||||
assertEquals(60.0, sellerBalances.get(RUB));
|
||||
|
||||
var sellerOrders = exchange.getActiveOrders(seller.id()).get();
|
||||
var remainingOrder = sellerOrders.iterator().next();
|
||||
|
||||
// Seller has 60/50 RUB/CNY order left
|
||||
|
||||
assertEquals(RUB_CNY, remainingOrder.pair());
|
||||
assertEquals(50.0, remainingOrder.quantity());
|
||||
assertEquals(60.0, remainingOrder.price());
|
||||
@@ -221,12 +220,12 @@ class CurrencyExchangeTest {
|
||||
var buyer2Balances = exchange.getBalances(buyer2.id()).get();
|
||||
var sellerBalances = exchange.getBalances(seller.id()).get();
|
||||
|
||||
assertEquals(50.0, buyer1Balances.get(CNY)); // Buyer1 bought 50 CNY
|
||||
assertEquals(50.0, buyer2Balances.get(CNY)); // Buyer2 bought 50 CNY
|
||||
assertEquals(120.0, sellerBalances.get(RUB)); // Seller bought 120 RUB
|
||||
assertEquals(50.0, buyer1Balances.get(CNY));
|
||||
assertEquals(50.0, buyer2Balances.get(CNY));
|
||||
assertEquals(120.0, sellerBalances.get(RUB));
|
||||
|
||||
var sellerOrders = exchange.getActiveOrders(seller.id()).get();
|
||||
assertFalse(sellerOrders.iterator().hasNext()); // Seller has no orders left
|
||||
assertFalse(sellerOrders.iterator().hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -243,10 +242,10 @@ class CurrencyExchangeTest {
|
||||
var buyerBalances = exchange.getBalances(buyer.id()).get();
|
||||
var sellerBalances = exchange.getBalances(seller.id()).get();
|
||||
|
||||
assertEquals(100.0, buyerBalances.get(CNY)); // Buyer bought 100 CNY
|
||||
assertEquals(180.0, buyerBalances.get(RUB)); // Buyer spends 120 RUB
|
||||
assertEquals(120.0, sellerBalances.get(RUB)); // Seller bought 120 RUB
|
||||
assertEquals(100.0, sellerBalances.get(CNY)); // Seller retains remaining CNY
|
||||
assertEquals(100.0, buyerBalances.get(CNY));
|
||||
assertEquals(180.0, buyerBalances.get(RUB));
|
||||
assertEquals(120.0, sellerBalances.get(RUB));
|
||||
assertEquals(100.0, sellerBalances.get(CNY));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -268,18 +267,18 @@ class CurrencyExchangeTest {
|
||||
var buyer2Balances = exchange.getBalances(buyer2.id()).get();
|
||||
var sellerBalances = exchange.getBalances(seller.id()).get();
|
||||
|
||||
assertEquals(30.0, buyer1Balances.get(CNY)); // Buyer1 bought 30 CNY
|
||||
assertEquals(24.0, buyer1Balances.get(RUB)); // Buyer1 sold 36 CNY
|
||||
assertEquals(30.0, buyer1Balances.get(CNY));
|
||||
assertEquals(24.0, buyer1Balances.get(RUB));
|
||||
|
||||
assertEquals(30.0, buyer2Balances.get(CNY)); // Buyer2 bought 30 CNY
|
||||
assertEquals(24.0, buyer2Balances.get(RUB)); // Buyer2 sold 36 CNY
|
||||
assertEquals(30.0, buyer2Balances.get(CNY));
|
||||
assertEquals(24.0, buyer2Balances.get(RUB));
|
||||
|
||||
var sellerOrders = exchange.getActiveOrders(seller.id()).get();
|
||||
var sellerOrder = sellerOrders.iterator().next();
|
||||
|
||||
assertEquals(40.0, sellerOrder.quantity()); // 40 CNY reserved
|
||||
assertEquals(48.0, sellerOrder.price()); // 1.2 RUB per CNY
|
||||
assertEquals(72.0, sellerBalances.get(RUB)); // 72 RUB earned
|
||||
assertEquals(40.0, sellerOrder.quantity());
|
||||
assertEquals(48.0, sellerOrder.price());
|
||||
assertEquals(72.0, sellerBalances.get(RUB));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -314,8 +313,8 @@ class CurrencyExchangeTest {
|
||||
exchange.deposit(seller1.id(), CNY, 50.0).get();
|
||||
exchange.deposit(seller2.id(), CNY, 50.0).get();
|
||||
|
||||
exchange.placeOrder(seller1.id(), RUB_CNY, OrderType.SELL, 75, 50.0).get(); // Higher price
|
||||
exchange.placeOrder(seller2.id(), RUB_CNY, OrderType.SELL, 60, 50.0).get(); // Lower price
|
||||
exchange.placeOrder(seller1.id(), RUB_CNY, OrderType.SELL, 75, 50.0).get();
|
||||
exchange.placeOrder(seller2.id(), RUB_CNY, OrderType.SELL, 60, 50.0).get();
|
||||
|
||||
exchange.placeOrder(buyer.id(), RUB_CNY, OrderType.BUY, 75, 50.0).get();
|
||||
|
||||
@@ -323,14 +322,14 @@ class CurrencyExchangeTest {
|
||||
var seller1Balances = exchange.getBalances(seller1.id()).get();
|
||||
var seller2Balances = exchange.getBalances(seller2.id()).get();
|
||||
|
||||
assertEquals(50.0, buyerBalances.get(CNY)); // Buyer gets 50 CNY from the lower-priced seller
|
||||
assertEquals(140.0, buyerBalances.get(RUB)); // Buyer spends 60 RUB
|
||||
assertEquals(50.0, buyerBalances.get(CNY));
|
||||
assertEquals(140.0, buyerBalances.get(RUB));
|
||||
|
||||
assertEquals(60.0, seller2Balances.get(RUB)); // Seller2 sells at 60 price
|
||||
assertEquals(0.0, seller2Balances.get(CNY)); // Seller2 has no CNY left
|
||||
assertEquals(60.0, seller2Balances.get(RUB));
|
||||
assertEquals(0.0, seller2Balances.get(CNY));
|
||||
|
||||
assertNull(seller1Balances.get(RUB)); // Seller1 remains untouched
|
||||
assertEquals(0.0, seller1Balances.get(CNY)); // Seller1 still has their CNY reserved
|
||||
assertNull(seller1Balances.get(RUB));
|
||||
assertEquals(0.0, seller1Balances.get(CNY));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -345,9 +344,9 @@ class CurrencyExchangeTest {
|
||||
exchange.deposit(seller2.id(), CNY, 50.0).get();
|
||||
exchange.deposit(seller3.id(), CNY, 50.0).get();
|
||||
|
||||
exchange.placeOrder(seller1.id(), RUB_CNY, OrderType.SELL, 75.0, 50.0).get(); // Highest price
|
||||
exchange.placeOrder(seller2.id(), RUB_CNY, OrderType.SELL, 65.0, 50.0).get(); // Medium price
|
||||
exchange.placeOrder(seller3.id(), RUB_CNY, OrderType.SELL, 60.0, 50.0).get(); // Lowest price
|
||||
exchange.placeOrder(seller1.id(), RUB_CNY, OrderType.SELL, 75.0, 50.0).get();
|
||||
exchange.placeOrder(seller2.id(), RUB_CNY, OrderType.SELL, 65.0, 50.0).get();
|
||||
exchange.placeOrder(seller3.id(), RUB_CNY, OrderType.SELL, 60.0, 50.0).get();
|
||||
|
||||
exchange.placeOrder(buyer.id(), RUB_CNY, OrderType.BUY, 150.0, 100.0).get();
|
||||
|
||||
@@ -356,17 +355,17 @@ class CurrencyExchangeTest {
|
||||
var seller2Balances = exchange.getBalances(seller2.id()).get();
|
||||
var seller3Balances = exchange.getBalances(seller3.id()).get();
|
||||
|
||||
assertEquals(100.0, buyerBalances.get(CNY)); // Buyer receives 100 CNY (50 from seller3 and 50 from seller2)
|
||||
assertEquals(175.0, buyerBalances.get(RUB)); // Buyer spends 125 RUB (60 + 65)
|
||||
assertEquals(100.0, buyerBalances.get(CNY));
|
||||
assertEquals(175.0, buyerBalances.get(RUB));
|
||||
|
||||
assertEquals(60.0, seller3Balances.get(RUB)); // Seller3 sells at 60 price
|
||||
assertEquals(0.0, seller3Balances.get(CNY)); // Seller3's CNY is reduced to 0
|
||||
assertEquals(60.0, seller3Balances.get(RUB));
|
||||
assertEquals(0.0, seller3Balances.get(CNY));
|
||||
|
||||
assertEquals(65.0, seller2Balances.get(RUB)); // Seller2 sells at 65 price
|
||||
assertEquals(0.0, seller2Balances.get(CNY)); // Seller2's CNY is reduced to 0
|
||||
assertEquals(65.0, seller2Balances.get(RUB));
|
||||
assertEquals(0.0, seller2Balances.get(CNY));
|
||||
|
||||
assertNull(seller1Balances.get(RUB)); // Seller1 remains untouched
|
||||
assertEquals(0.0, seller1Balances.get(CNY)); // Seller1 still has their CNY reserved
|
||||
assertNull(seller1Balances.get(RUB));
|
||||
assertEquals(0.0, seller1Balances.get(CNY));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -379,8 +378,8 @@ class CurrencyExchangeTest {
|
||||
exchange.deposit(seller1.id(), CNY, 50.0).get();
|
||||
exchange.deposit(seller2.id(), CNY, 50.0).get();
|
||||
|
||||
exchange.placeOrder(seller1.id(), RUB_CNY, OrderType.SELL, 60, 50.0).get(); // Placed first
|
||||
exchange.placeOrder(seller2.id(), RUB_CNY, OrderType.SELL, 60, 50.0).get(); // Placed second
|
||||
exchange.placeOrder(seller1.id(), RUB_CNY, OrderType.SELL, 60, 50.0).get();
|
||||
exchange.placeOrder(seller2.id(), RUB_CNY, OrderType.SELL, 60, 50.0).get();
|
||||
|
||||
exchange.placeOrder(buyer.id(), RUB_CNY, OrderType.BUY, 60, 50.0).get();
|
||||
|
||||
@@ -388,13 +387,13 @@ class CurrencyExchangeTest {
|
||||
var seller1Balances = exchange.getBalances(seller1.id()).get();
|
||||
var seller2Balances = exchange.getBalances(seller2.id()).get();
|
||||
|
||||
assertEquals(50.0, buyerBalances.get(CNY)); // Buyer gets 50 CNY
|
||||
assertEquals(140.0, buyerBalances.get(RUB)); // Buyer spends 60 RUB
|
||||
assertEquals(50.0, buyerBalances.get(CNY));
|
||||
assertEquals(140.0, buyerBalances.get(RUB));
|
||||
|
||||
assertEquals(60.0, seller1Balances.get(RUB)); // Seller1 sells first
|
||||
assertEquals(0.0, seller1Balances.get(CNY)); // Seller1's CNY is reduced to 0
|
||||
assertEquals(60.0, seller1Balances.get(RUB));
|
||||
assertEquals(0.0, seller1Balances.get(CNY));
|
||||
|
||||
assertNull(seller2Balances.get(RUB)); // Seller2 remains untouched
|
||||
assertNull(seller2Balances.get(RUB));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -416,13 +415,13 @@ class CurrencyExchangeTest {
|
||||
var buyer2Balances = exchange.getBalances(buyer2.id()).get();
|
||||
var sellerBalances = exchange.getBalances(seller.id()).get();
|
||||
|
||||
assertNull(sellerBalances.get(RUB)); // Seller did not sell anything
|
||||
assertEquals(0.0, sellerBalances.get(CNY)); // Seller has no CNY left
|
||||
assertNull(sellerBalances.get(RUB));
|
||||
assertEquals(0.0, sellerBalances.get(CNY));
|
||||
|
||||
assertNull(buyer1Balances.get(CNY)); // Buyer1 did not buy anything
|
||||
assertNull(buyer1Balances.get(CNY));
|
||||
assertEquals(120.0, buyer1Balances.get(RUB));
|
||||
|
||||
assertNull(buyer2Balances.get(CNY)); // Buyer2 did not buy anything
|
||||
assertNull(buyer2Balances.get(CNY));
|
||||
assertEquals(125.0, buyer2Balances.get(RUB));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user