diff --git a/src/test/java/banking/BankAccountConcurrentDepositTest.java b/src/test/java/banking/BankAccountConcurrentDepositTest.java new file mode 100644 index 0000000..5ad50cb --- /dev/null +++ b/src/test/java/banking/BankAccountConcurrentDepositTest.java @@ -0,0 +1,37 @@ +package banking; + +import dev.banking.model.BankAccount; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.*; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class BankAccountConcurrentDepositTest { + + @Test + void concurrentDeposits() throws Exception { + + BankAccount account = new BankAccount(1, 0); + + int threads = 100; + int perThread = 1000; + + ExecutorService executor = Executors.newFixedThreadPool(threads); + CountDownLatch latch = new CountDownLatch(threads); + + for (int i = 0; i < threads; i++) { + executor.submit(() -> { + for (int j = 0; j < perThread; j++) { + account.deposit(1); + } + latch.countDown(); + }); + } + + latch.await(); + executor.shutdown(); + + assertEquals(threads * perThread, account.getBalance()); + } +} \ No newline at end of file diff --git a/src/test/java/banking/TestB.java b/src/test/java/banking/BankAccountDepositWithdrawRaceTest.java similarity index 51% rename from src/test/java/banking/TestB.java rename to src/test/java/banking/BankAccountDepositWithdrawRaceTest.java index 66daa64..28e2cf8 100644 --- a/src/test/java/banking/TestB.java +++ b/src/test/java/banking/BankAccountDepositWithdrawRaceTest.java @@ -3,54 +3,40 @@ package banking; import dev.banking.model.BankAccount; import org.junit.jupiter.api.Test; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +import java.util.concurrent.*; import static org.junit.jupiter.api.Assertions.assertEquals; -public class TestB { +public class BankAccountDepositWithdrawRaceTest { @Test void concurrentDepositWithdraw() throws Exception { - BankAccount account = - new BankAccount(1, 1_000_000); + BankAccount account = new BankAccount(1, 1_000_000); - ExecutorService executor = - Executors.newFixedThreadPool(100); - - CountDownLatch latch = - new CountDownLatch(100); + ExecutorService executor = Executors.newFixedThreadPool(100); + CountDownLatch latch = new CountDownLatch(100); for (int i = 0; i < 50; i++) { executor.submit(() -> { - - for (int j = 0; j < 10000; j++) { + for (int j = 0; j < 10_000; j++) { account.deposit(1); } - latch.countDown(); }); executor.submit(() -> { - - for (int j = 0; j < 10000; j++) { + for (int j = 0; j < 10_000; j++) { account.withdraw(1); } - latch.countDown(); }); } latch.await(); - executor.shutdown(); - assertEquals( - 1_000_000, - account.getBalance() - ); + assertEquals(1_000_000, account.getBalance()); } } \ No newline at end of file diff --git a/src/test/java/banking/BankAccountTransferConsistencyTest.java b/src/test/java/banking/BankAccountTransferConsistencyTest.java new file mode 100644 index 0000000..cf70df6 --- /dev/null +++ b/src/test/java/banking/BankAccountTransferConsistencyTest.java @@ -0,0 +1,45 @@ +package banking; + +import dev.banking.model.BankAccount; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.*; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class BankAccountTransferConsistencyTest { + + @Test + void transferConservation() throws Exception { + + BankAccount a = new BankAccount(1, 100_000); + BankAccount b = new BankAccount(2, 100_000); + + ExecutorService executor = Executors.newFixedThreadPool(50); + CountDownLatch latch = new CountDownLatch(100); + + for (int i = 0; i < 50; i++) { + + executor.submit(() -> { + for (int j = 0; j < 1000; j++) { + a.transfer(b, 1); + } + latch.countDown(); + }); + + executor.submit(() -> { + for (int j = 0; j < 1000; j++) { + b.transfer(a, 1); + } + latch.countDown(); + }); + } + + latch.await(); + executor.shutdown(); + + long total = a.getBalance() + b.getBalance(); + + assertEquals(200_000, total); + } +} \ No newline at end of file diff --git a/src/test/java/banking/BankAccountTransferDeadlockTest.java b/src/test/java/banking/BankAccountTransferDeadlockTest.java new file mode 100644 index 0000000..5d6556f --- /dev/null +++ b/src/test/java/banking/BankAccountTransferDeadlockTest.java @@ -0,0 +1,35 @@ +package banking; + +import dev.banking.model.BankAccount; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.*; + +import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively; + +import java.time.Duration; + +public class BankAccountTransferDeadlockTest { + + @Test + void deadlockFreeTransfers() { + + assertTimeoutPreemptively(Duration.ofSeconds(5), () -> { + + BankAccount a = new BankAccount(1, 1_000_000); + BankAccount b = new BankAccount(2, 1_000_000); + + ExecutorService executor = Executors.newFixedThreadPool(50); + + for (int i = 0; i < 50_000; i++) { + + executor.submit(() -> a.transfer(b, 1)); + executor.submit(() -> b.transfer(a, 1)); + } + + executor.shutdown(); + + executor.awaitTermination(5, TimeUnit.SECONDS); + }); + } +} \ No newline at end of file diff --git a/src/test/java/banking/BankingSystemEndToEndStressTest.java b/src/test/java/banking/BankingSystemEndToEndStressTest.java new file mode 100644 index 0000000..c9f8efa --- /dev/null +++ b/src/test/java/banking/BankingSystemEndToEndStressTest.java @@ -0,0 +1,42 @@ +package banking; + +import dev.banking.model.*; +import dev.banking.processor.TransactionProcessor; +import dev.banking.service.BankingSystem; +import org.junit.jupiter.api.Test; + +import java.util.*; +import java.util.concurrent.*; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class BankingSystemEndToEndStressTest { + + @Test + void fullSystemStressTest() throws Exception { + + BankAccount a = new BankAccount(1, 1_000_000); + BankAccount b = new BankAccount(2, 1_000_000); + + Map accounts = new HashMap<>(); + accounts.put(1, a); + accounts.put(2, b); + + ExecutorService executor = Executors.newFixedThreadPool(8); + TransactionProcessor processor = new TransactionProcessor(accounts); + BankingSystem system = new BankingSystem(executor, processor); + + List txs = new ArrayList<>(); + + for (int i = 0; i < 50_000; i++) { + txs.add(new TransferTransaction(1, 2, 1)); + txs.add(new TransferTransaction(2, 1, 1)); + } + + system.processTransactions(txs); + + executor.shutdown(); + + assertTrue(executor.awaitTermination(5, TimeUnit.SECONDS)); + } +} \ No newline at end of file diff --git a/src/test/java/banking/TestA.java b/src/test/java/banking/TestA.java deleted file mode 100644 index 35659eb..0000000 --- a/src/test/java/banking/TestA.java +++ /dev/null @@ -1,49 +0,0 @@ -package banking; - -import dev.banking.model.BankAccount; -import org.junit.jupiter.api.Test; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class TestA { - - @Test - void concurrentDeposits() throws Exception { - - BankAccount account = new BankAccount(1, 0); - - int threadCount = 100; - int depositsPerThread = 1000; - - ExecutorService executor = - Executors.newFixedThreadPool(threadCount); - - CountDownLatch latch = - new CountDownLatch(threadCount); - - for (int i = 0; i < threadCount; i++) { - - executor.submit(() -> { - - for (int j = 0; j < depositsPerThread; j++) { - account.deposit(1); - } - - latch.countDown(); - }); - } - - latch.await(); - - executor.shutdown(); - - assertEquals( - threadCount * depositsPerThread, - account.getBalance() - ); - } -} \ No newline at end of file diff --git a/src/test/java/banking/TestC.java b/src/test/java/banking/TestC.java deleted file mode 100644 index 93ea4a2..0000000 --- a/src/test/java/banking/TestC.java +++ /dev/null @@ -1,98 +0,0 @@ -package banking; - -import dev.banking.model.BankAccount; -import org.junit.jupiter.api.Test; - -import java.time.Duration; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.*; - -public class TestC { - - @Test - void transferConservation() throws Exception { - - BankAccount a = - new BankAccount(1, 100_000); - - BankAccount b = - new BankAccount(2, 100_000); - - ExecutorService executor = - Executors.newFixedThreadPool(50); - - CountDownLatch latch = - new CountDownLatch(100); - - for (int i = 0; i < 50; i++) { - - executor.submit(() -> { - - for (int j = 0; j < 1000; j++) { - a.transfer(b, 1); - } - - latch.countDown(); - }); - - executor.submit(() -> { - - for (int j = 0; j < 1000; j++) { - b.transfer(a, 1); - } - - latch.countDown(); - }); - } - - latch.await(); - - executor.shutdown(); - - long total = - a.getBalance() + b.getBalance(); - - assertEquals(200_000, total); - } - - @Test - void deadlockFreeTransfers() { - - assertTimeoutPreemptively( - Duration.ofSeconds(5), - () -> { - - BankAccount a = - new BankAccount(1, 1_000_000); - - BankAccount b = - new BankAccount(2, 1_000_000); - - ExecutorService executor = - Executors.newFixedThreadPool(50); - - for (int i = 0; i < 50000; i++) { - - executor.submit(() -> - a.transfer(b, 1)); - - executor.submit(() -> - b.transfer(a, 1)); - } - - executor.shutdown(); - - assertTrue( - executor.awaitTermination( - 5, - TimeUnit.SECONDS - ) - ); - } - ); - } -} \ No newline at end of file diff --git a/src/test/java/banking/TransactionProcessorMixedTransactionIntegrationTest.java b/src/test/java/banking/TransactionProcessorMixedTransactionIntegrationTest.java new file mode 100644 index 0000000..37992f1 --- /dev/null +++ b/src/test/java/banking/TransactionProcessorMixedTransactionIntegrationTest.java @@ -0,0 +1,47 @@ +package banking; + +import dev.banking.model.*; +import dev.banking.processor.TransactionProcessor; +import org.junit.jupiter.api.Test; + +import java.util.*; +import java.util.concurrent.*; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +public class TransactionProcessorMixedTransactionIntegrationTest { + + @Test + void mixedTransactionsShouldExecuteCorrectly() { + + BankAccount a = new BankAccount(1, 1000); + BankAccount b = new BankAccount(2, 2000); + BankAccount c = new BankAccount(3, 1500); + + Map accounts = new HashMap<>(); + accounts.put(1, a); + accounts.put(2, b); + accounts.put(3, c); + + TransactionProcessor processor = new TransactionProcessor(accounts); + + ExecutorService executor = Executors.newFixedThreadPool(8); + + List txs = List.of( + new DepositTransaction(1, 100), + new WithdrawTransaction(2, 50), + new TransferTransaction(1, 2, 30), + new TransferTransaction(2, 3, 70), + new DepositTransaction(3, 200) + ); + + assertDoesNotThrow(() -> { + for (Transaction tx : txs) { + executor.submit(() -> processor.process(tx)); + } + + executor.shutdown(); + executor.awaitTermination(5, TimeUnit.SECONDS); + }); + } +} \ No newline at end of file