From 74963fbf29ff332552631ae7ee509b5e7116b6b6 Mon Sep 17 00:00:00 2001 From: AryanGh-imp Date: Fri, 5 Jun 2026 21:53:35 +0330 Subject: [PATCH] feat(tests): add redesigned concurrent banking test suite aligned with TransactionProcessor architecture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Introduce BankAccount-focused concurrency tests based on Transaction → Processor → Account flow - Add stress tests for high-contention deposits, withdrawals, and bidirectional transfers - Validate atomicity and consistency across multi-threaded execution scenarios - Include deadlock detection tests under heavy transfer contention - Align all test cases with updated module structure and BankAccount concurrency contract --- .../BankAccountConcurrentDepositTest.java | 37 +++++++ ...> BankAccountDepositWithdrawRaceTest.java} | 30 ++---- .../BankAccountTransferConsistencyTest.java | 45 +++++++++ .../BankAccountTransferDeadlockTest.java | 35 +++++++ .../BankingSystemEndToEndStressTest.java | 42 ++++++++ src/test/java/banking/TestA.java | 49 ---------- src/test/java/banking/TestC.java | 98 ------------------- ...cessorMixedTransactionIntegrationTest.java | 47 +++++++++ 8 files changed, 214 insertions(+), 169 deletions(-) create mode 100644 src/test/java/banking/BankAccountConcurrentDepositTest.java rename src/test/java/banking/{TestB.java => BankAccountDepositWithdrawRaceTest.java} (51%) create mode 100644 src/test/java/banking/BankAccountTransferConsistencyTest.java create mode 100644 src/test/java/banking/BankAccountTransferDeadlockTest.java create mode 100644 src/test/java/banking/BankingSystemEndToEndStressTest.java delete mode 100644 src/test/java/banking/TestA.java delete mode 100644 src/test/java/banking/TestC.java create mode 100644 src/test/java/banking/TransactionProcessorMixedTransactionIntegrationTest.java 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