diff --git a/src/main/java/storage/Blocks.java b/src/main/java/storage/Blocks.java index bd645a59..81748635 100644 --- a/src/main/java/storage/Blocks.java +++ b/src/main/java/storage/Blocks.java @@ -57,4 +57,9 @@ public interface Blocks { * @return all stored blocks in database. */ ArrayList all(); + + /** + * Closes the database. + */ + void close(); } diff --git a/src/main/java/storage/mapdb/BlocksMapDb.java b/src/main/java/storage/mapdb/BlocksMapDb.java index 4cb807e0..d4bec831 100644 --- a/src/main/java/storage/mapdb/BlocksMapDb.java +++ b/src/main/java/storage/mapdb/BlocksMapDb.java @@ -169,7 +169,7 @@ public ArrayList all() { /** * Close the db. */ - public void closeDb() { + public void close() { dbId.close(); dbHeight.close(); } diff --git a/src/test/java/protocol/engines/IngestEngineTest.java b/src/test/java/protocol/engines/IngestEngineTest.java index 5723c6b2..46129c1a 100644 --- a/src/test/java/protocol/engines/IngestEngineTest.java +++ b/src/test/java/protocol/engines/IngestEngineTest.java @@ -1,5 +1,10 @@ package protocol.engines; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -16,6 +21,7 @@ import model.lightchain.*; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.testcontainers.shaded.org.apache.commons.io.FileUtils; import protocol.Parameters; import protocol.assigner.AssignerInf; import state.Snapshot; @@ -23,105 +29,207 @@ import storage.Blocks; import storage.Identifiers; import storage.Transactions; +import storage.mapdb.BlocksMapDb; import unittest.fixtures.AccountFixture; import unittest.fixtures.BlockFixture; import unittest.fixtures.EntityFixture; import unittest.fixtures.ValidatedTransactionFixture; +import unittest.storage.TempBlocksMapDB; /** * Encapsulates tests for ingest engine implementation. */ public class IngestEngineTest { + private static final String TEMP_DIR = "tempdir"; + private static final String TEMP_FILE_ID = "tempfileID.db"; + private static final String TEMP_FILE_HEIGHT = "tempfileHEIGHT.db"; + + /** + * Get the temporary path. + * + * @param label dir name, height or id. + * @return temporary path. + */ + private static String getTemporaryPath(String label) { + Path temporaryDir = null; + Path currentRelativePath = Paths.get(""); + try { + temporaryDir = Files.createTempDirectory(currentRelativePath, TEMP_DIR); + } catch (IOException e) { + Assertions.fail(); + } + return temporaryDir.toAbsolutePath() + "/" + label; + } + /** * Evaluates that when a new validated block arrives at ingest engine, - * the engine adds the block to its block storage database. + * the engine adds the block to its mocked block storage database. * The engine also adds hash of all the transactions of block into its "transactions" database. + *

+ * This test is using a mocked block storage database. */ @Test - public void testValidatedSingleBlock() { + public void testValidatedSingleBlock_MockedStorage() { Blocks blocks = mock(Blocks.class); - Identifiers seenEntities = mock(Identifiers.class); - Identifiers transactionIds = mock(Identifiers.class); - Transactions pendingTransactions = mock(Transactions.class); - Block block = BlockFixture.newBlock(); - - // mocks block as new to ingest engine. - when(seenEntities.has(block.id())).thenReturn(false); when(blocks.has(block.id())).thenReturn(false); + testValidatedSingleBlock(blocks, block); + verify(blocks, times(1)).add(block); + } + + /** + * Evaluates that when a new validated block arrives at ingest engine, + * the engine adds the block to its real block storage database. + * The engine also adds hash of all the transactions of block into its "transactions" database. + *

+ * This test is using a real block storage database. + */ + @Test + public void testValidatedSingleBlock_RealStorage() { + Blocks blocks = null; + try { + blocks = new TempBlocksMapDB(); + Block block = BlockFixture.newBlock(); + testValidatedSingleBlock(blocks, block); + Assertions.assertTrue(blocks.has(block.id())); + } finally { + if (blocks != null) { + blocks.close(); + } + } + } - IngestEngine ingestEngine = this.mockIngestEngineForEntities( - new ArrayList<>(List.of(block)), - seenEntities, - transactionIds, - pendingTransactions, - blocks); + /** + * Evaluates that when two validated blocks arrive at ingest engine SEQUENTIALLY, + * the engine adds the blocks to its mocked block storage database. + * The engine also adds hash of all the transactions of blocks into its "transactions" database. + *

+ * This test is using a mocked blocks storage database. + */ + @Test + public void testValidatedTwoBlocks_MockedStorage() { + Blocks blocks = mock(Blocks.class); - // action - ingestEngine.process(block); + Block block1 = BlockFixture.newBlock(); + Block block2 = BlockFixture.newBlock(); - // verify - verifyBlockHappyPathCalled(block, blocks, pendingTransactions, transactionIds, seenEntities); + when(blocks.has(block1.id())).thenReturn(false); + when(blocks.has(block2.id())).thenReturn(false); + + testValidatedTwoBlocks(blocks, block1, block2); + + verify(blocks, times(1)).add(block1); + verify(blocks, times(1)).add(block2); } /** * Evaluates that when two validated blocks arrive at ingest engine SEQUENTIALLY, - * the engine adds the blocks to its block storage database. + * the engine adds the blocks to its real block storage database. * The engine also adds hash of all the transactions of blocks into its "transactions" database. + *

+ * This test is using a real block storage database. */ @Test - public void testValidatedTwoBlocks() { - Blocks blocks = mock(Blocks.class); + public void testValidatedTwoBlocks_RealStorage() { + Blocks blocks = null; + try { + blocks = new TempBlocksMapDB(); + Block block1 = BlockFixture.newBlock(); + Block block2 = BlockFixture.newBlock(); + testValidatedTwoBlocks(blocks, block1, block2); + + Assertions.assertTrue(blocks.has(block1.id())); + Assertions.assertTrue(blocks.has(block2.id())); + } finally { + if (blocks != null) { + blocks.close(); + } + } + + } + + /** + * Test validated single block for mocked and real versions. + * + * @param blocks mocked or real block. + */ + // TODO: this method should be refactored with parameters option. + public void testValidatedTwoBlocks(Blocks blocks, Block block1, Block block2) { Identifiers seenEntities = mock(Identifiers.class); Identifiers transactionIds = mock(Identifiers.class); Transactions pendingTransactions = mock(Transactions.class); - Block block1 = BlockFixture.newBlock(); - Block block2 = BlockFixture.newBlock(); - - IngestEngine ingestEngine = this.mockIngestEngineForEntities( - new ArrayList<>(Arrays.asList(block1, block2)), - seenEntities, - transactionIds, - pendingTransactions, - blocks); + IngestEngine ingestEngine = this.mockIngestEngineForEntities(new ArrayList<>(Arrays.asList(block1, block2)), seenEntities, transactionIds, pendingTransactions, blocks); - // action ingestEngine.process(block1); ingestEngine.process(block2); - // verification for block 1 verifyBlockHappyPathCalled(block1, blocks, pendingTransactions, transactionIds, seenEntities); - // verification for block 2 verifyBlockHappyPathCalled(block2, blocks, pendingTransactions, transactionIds, seenEntities); } /** * Evaluates that when two validated blocks arrive at ingest engine concurrently, - * the engine adds the blocks to its block storage database. + * the engine adds the blocks to its mocked block storage database. * The engine also adds hash of all the transactions of blocks into its "transactions" database. + *

+ * This test is using a mocked block storage database. */ @Test - public void testValidatedTwoBlocksConcurrently() { + public void testValidatedTwoBlocks_Concurrently_MockedBlocks() { Blocks blocks = mock(Blocks.class); + Block block1 = BlockFixture.newBlock(); + Block block2 = BlockFixture.newBlock(); + when(blocks.has(block1.id())).thenReturn(false); + when(blocks.has(block2.id())).thenReturn(false); + testValidatedTwoBlocks_Concurrently(blocks, block1, block2); + + verify(blocks, times(1)).add(block1); + verify(blocks, times(1)).add(block2); + + } + + /** + * Evaluates that when two validated blocks arrive at ingest engine concurrently, + * the engine adds the blocks to its real block storage database. + * The engine also adds hash of all the transactions of blocks into its "transactions" database. + *

+ * This test is using a real block storage database. + */ + @Test + public void testValidatedTwoBlocks_Concurrently_RealBlocks() { + Blocks blocks = null; + try { + blocks = new TempBlocksMapDB(); + + Block block1 = BlockFixture.newBlock(); + Block block2 = BlockFixture.newBlock(); + + testValidatedTwoBlocks_Concurrently(blocks, block1, block2); + Assertions.assertTrue(blocks.has(block1.id())); + Assertions.assertTrue(blocks.has(block2.id())); + } finally { + if (blocks != null) { + blocks.close(); + } + } + } + + /** + * Test validated two blocks for mocked and real versions concurrently. + * + * @param blocks mocked or real block. + */ + // TODO: this method should be refactored with parameters option. + public void testValidatedTwoBlocks_Concurrently(Blocks blocks, Block block1, Block block2) { Identifiers seenEntities = mock(Identifiers.class); Identifiers transactionIds = mock(Identifiers.class); Transactions pendingTransactions = mock(Transactions.class); - Block block1 = BlockFixture.newBlock(); - Block block2 = BlockFixture.newBlock(); - - IngestEngine ingestEngine = this.mockIngestEngineForEntities( - new ArrayList<>(Arrays.asList(block1, block2)), - seenEntities, - transactionIds, - pendingTransactions, - blocks); + IngestEngine ingestEngine = this.mockIngestEngineForEntities(new ArrayList<>(Arrays.asList(block1, block2)), seenEntities, transactionIds, pendingTransactions, blocks); - this.processEntitiesConcurrently( - ingestEngine, - new ArrayList<>(Arrays.asList(block1, block2))); + this.processEntitiesConcurrently(ingestEngine, new ArrayList<>(Arrays.asList(block1, block2))); // verification for block 1 verifyBlockHappyPathCalled(block1, blocks, pendingTransactions, transactionIds, seenEntities); @@ -132,63 +240,120 @@ public void testValidatedTwoBlocksConcurrently() { /** * Evaluates that when two same validated blocks arrive at ingest engine (second one should be ignored), - * the engine adds the blocks to its block storage database. + * the engine adds the blocks to its mocked block storage database. * The engine also adds hash of all the transactions of blocks into its "transactions" database. + *

+ * This test is using a mocked block storage database. */ @Test - public void testValidatedSameTwoBlocks() { + public void testValidated_SameTwoBlocks_MockedStorage() { Blocks blocks = mock(Blocks.class); + Block block = BlockFixture.newBlock(); + when(blocks.has(block.id())).thenReturn(false); + testValidated_SameTwoBlocks(blocks, block); + verify(blocks, times(1)).add(block); + } + + /** + * Evaluates that when two same validated blocks arrive at ingest engine (second one should be ignored), + * the engine adds the blocks to its real block storage database. + * The engine also adds hash of all the transactions of blocks into its "transactions" database. + *

+ * This test is using a real block storage database. + */ + @Test + public void testValidated_SameTwoBlocks_RealStorage() { + Blocks blocks = null; + try { + blocks = new TempBlocksMapDB(); + Block block = BlockFixture.newBlock(); + testValidated_SameTwoBlocks(blocks, block); + Assertions.assertTrue(blocks.has(block.id())); + } finally { + if (blocks != null) { + blocks.close(); + } + } + } + + /** + * Test validated two blocks for mocked and real versions concurrently. + * + * @param blocks mocked or real blocks. + */ + // TODO: this method should be refactored with parameters option. + public void testValidated_SameTwoBlocks(Blocks blocks, Block block) { Identifiers seenEntities = mock(Identifiers.class); Identifiers transactionIds = mock(Identifiers.class); Transactions pendingTransactions = mock(Transactions.class); - Block block = BlockFixture.newBlock(); - when(seenEntities.has(block.id())).thenReturn(false); - when(blocks.has(block.id())).thenReturn(false); - IngestEngine ingestEngine = this.mockIngestEngineForEntities( - new ArrayList<>(List.of(block)), - seenEntities, - transactionIds, - pendingTransactions, - blocks); + IngestEngine ingestEngine = this.mockIngestEngineForEntities(new ArrayList<>(List.of(block)), seenEntities, transactionIds, pendingTransactions, blocks); - // action ingestEngine.process(block); when(seenEntities.has(block.id())).thenReturn(true); // block is already seen ingestEngine.process(block); - // verification verifyBlockHappyPathCalled(block, blocks, pendingTransactions, transactionIds, seenEntities); verify(seenEntities, times(2)).has(block.id()); } /** * Evaluates that when a new validated block (with shared transactions in pendingTx) arrive at ingest engine, - * the engine adds the blocks to its block storage database. + * the engine adds the blocks to its real block storage database. * The engine also removes hash of the transactions of blocks from pendingTransactions. + *

+ * This test is using a mocked block storage database. */ @Test - public void testValidatedBlockContainingPendingTransaction() { + public void testValidatedBlock_PendingTransaction_MockedStorage() { Blocks blocks = mock(Blocks.class); + Block block = BlockFixture.newBlock(); + when(blocks.has(block.id())).thenReturn(false); + testValidatedBlock_PendingTransaction(blocks, block); + verify(blocks, times(1)).add(block); + } + + /** + * Evaluates that when a new validated block (with shared transactions in pendingTx) arrive at ingest engine, + * the engine adds the blocks to its real block storage database. + * The engine also removes hash of the transactions of blocks from pendingTransactions. + *

+ * This test is using a real block storage database. + */ + @Test + public void testValidatedBlock_PendingTransaction_RealStorage() { + Blocks blocks = null; + try { + blocks = new TempBlocksMapDB(); + Block block = BlockFixture.newBlock(); + testValidatedBlock_PendingTransaction(blocks, block); + Assertions.assertTrue(blocks.has(block.id())); + } finally { + if (blocks != null) { + blocks.close(); + } + } + } + + /** + * Test validated blocks containing pending transactions for mocked and real versions concurrently. + * + * @param blocks mocked or real block. + */ + // TODO: this method should be refactored with parameters option. + public void testValidatedBlock_PendingTransaction(Blocks blocks, Block block) { Identifiers seenEntities = mock(Identifiers.class); Identifiers transactionIds = mock(Identifiers.class); Transactions pendingTransactions = mock(Transactions.class); - Block block = BlockFixture.newBlock(); - IngestEngine ingestEngine = this.mockIngestEngineForEntities( - new ArrayList<>(List.of(block)), - seenEntities, - transactionIds, - pendingTransactions, - blocks); + IngestEngine ingestEngine = this.mockIngestEngineForEntities(new ArrayList<>(List.of(block)), seenEntities, transactionIds, pendingTransactions, blocks); for (Transaction tx : block.getTransactions()) { - when(pendingTransactions.has(tx.id())).thenReturn(true); // pendingTx contains all txs of block + when(pendingTransactions.has(tx.id())).thenReturn(true); } - // action ingestEngine.process(block); // verification @@ -201,35 +366,66 @@ public void testValidatedBlockContainingPendingTransaction() { /** * Evaluates that when two new validated blocks (with a shared transactions in pendingTx, disjoint set) - * arrive at ingest engine, the engine adds the blocks to its block storage database. + * arrive at ingest engine, the engine adds the blocks to its real block storage database. * The engine also removes the hash of the single shared transaction among blocks from pending transactions. + *

+ * This test is using a mocked block storage database. */ @Test - public void testConcurrentBlockIngestionContainingSeenTransactionDisjointSet() { - // R + public void testConcurrentBlock_DisjointSet_MockedStorage() { Blocks blocks = mock(Blocks.class); + Block block1 = BlockFixture.newBlock(); + Block block2 = BlockFixture.newBlock(); + testConcurrentBlock_SeenTransaction_DisjointSet(blocks, block1, block2); + verify(blocks, times(1)).add(block1); + verify(blocks, times(1)).add(block2); + } + + /** + * Evaluates that when two new validated blocks (with a shared transactions in pendingTx, disjoint set) + * arrive at ingest engine, the engine adds the blocks to its real block storage database. + * The engine also removes the hash of the single shared transaction among blocks from pending transactions. + *

+ * This test is using a real block storage database. + */ + @Test + public void testConcurrentBlock_DisjointSet_RealStorage() { + Blocks blocks = null; + try { + blocks = new TempBlocksMapDB(); + + Block block1 = BlockFixture.newBlock(); + Block block2 = BlockFixture.newBlock(); + testConcurrentBlock_SeenTransaction_DisjointSet(blocks, block1, block2); + Assertions.assertTrue(blocks.has(block1.id())); + Assertions.assertTrue(blocks.has(block2.id())); + } finally { + if (blocks != null) { + blocks.close(); + } + } + } + + /** + * Test concurrent block ingestion containing seen transaction disjoint set mocked and real versions. + * + * @param blocks mocked or real block. + */ + // TODO: this method should be refactored with parameters option. + private void testConcurrentBlock_SeenTransaction_DisjointSet(Blocks blocks, Block block1, Block block2) { Identifiers seenEntities = mock(Identifiers.class); Identifiers transactionIds = mock(Identifiers.class); Transactions pendingTransactions = mock(Transactions.class); ArrayList accounts = new ArrayList<>(AccountFixture.newAccounts(10, 10).values()); - Block block1 = BlockFixture.newBlock(); - Block block2 = BlockFixture.newBlock(); - IngestEngine ingestEngine = this.mockIngestEngineForEntities( - new ArrayList<>(Arrays.asList(block1, block2)), - seenEntities, - transactionIds, - pendingTransactions, - blocks); + IngestEngine ingestEngine = this.mockIngestEngineForEntities(new ArrayList<>(Arrays.asList(block1, block2)), seenEntities, transactionIds, pendingTransactions, blocks); // simulates 1 shared transaction for each block when(pendingTransactions.has(block1.getTransactions()[0].id())).thenReturn(true); when(pendingTransactions.has(block2.getTransactions()[0].id())).thenReturn(true); - processEntitiesConcurrently( - ingestEngine, - new ArrayList<>(Arrays.asList(block1, block2))); + processEntitiesConcurrently(ingestEngine, new ArrayList<>(Arrays.asList(block1, block2))); // verification for block1 verifyBlockHappyPathCalled(block1, blocks, pendingTransactions, transactionIds, seenEntities); @@ -243,32 +439,22 @@ public void testConcurrentBlockIngestionContainingSeenTransactionDisjointSet() { } /** - * Evaluates that when two new validated blocks (with shared transactions in pendingTx, overlapping set) - * arrive at ingest engine, the engine adds the blocks to its block storage database. - * The engine also removes hash of the transactions of blocks from pendingTransactions. + * Test concurrent block ingestion containing seen transaction overlapping set mocked and real versions. + * + * @param blocks mocked or real block. */ - @Test - public void testConcurrentBlockIngestionContainingSeenTransactionOverlappingSet() { - Blocks blocks = mock(Blocks.class); + // TODO: this method should be refactored with parameters option. + private void testConcurrentBlock_OverlappingSet(Blocks blocks, Block block1, Block block2) { Identifiers seenEntities = mock(Identifiers.class); Identifiers transactionIds = mock(Identifiers.class); Transactions pendingTransactions = mock(Transactions.class); - Block block1 = BlockFixture.newBlock(); - Block block2 = BlockFixture.newBlock(); - IngestEngine ingestEngine = this.mockIngestEngineForEntities( - new ArrayList<>(Arrays.asList(block1, block2)), - seenEntities, - transactionIds, - pendingTransactions, - blocks); + IngestEngine ingestEngine = this.mockIngestEngineForEntities(new ArrayList<>(Arrays.asList(block1, block2)), seenEntities, transactionIds, pendingTransactions, blocks); // simulates an overlapping set of shared transactions when(pendingTransactions.has(any(Identifier.class))).thenReturn(true); - processEntitiesConcurrently( - ingestEngine, - new ArrayList<>(Arrays.asList(block1, block2))); + processEntitiesConcurrently(ingestEngine, new ArrayList<>(Arrays.asList(block1, block2))); // verification for block1 verifyBlockHappyPathCalled(block1, blocks, pendingTransactions, transactionIds, seenEntities); @@ -284,33 +470,62 @@ public void testConcurrentBlockIngestionContainingSeenTransactionOverlappingSet( } /** - * Evaluates that when an already ingested validated block arrives at ingest engine, - * the engine discards the block right away. + * Evaluates that when two new validated blocks (with shared transactions in pendingTx, overlapping set) + * arrive at ingest engine, the engine adds the blocks to its mocked block storage database. + * The engine also removes hash of the transactions of blocks from pendingTransactions. + *

+ * This test is using a mocked block storage database. */ @Test - public void testValidatedAlreadyIngestedBlock() { + public void testConcurrentBlock_OverlappingSet_MockedStorage() { Blocks blocks = mock(Blocks.class); + Block block1 = BlockFixture.newBlock(); + Block block2 = BlockFixture.newBlock(); + when(blocks.has(block1.id())).thenReturn(false); + when(blocks.has(block2.id())).thenReturn(false); + testConcurrentBlock_OverlappingSet(blocks, block1, block2); + verify(blocks, times(1)).add(block1); + verify(blocks, times(1)).add(block2); + } + + /** + * Evaluates that when two new validated blocks (with shared transactions in pendingTx, overlapping set) + * arrive at ingest engine, the engine adds the blocks to its real block storage database. + * The engine also removes hash of the transactions of blocks from pendingTransactions. + *

+ * This test is using a real block storage database. + */ + @Test + public void testConcurrentBlock_OverlappingSet_RealStorage() { + Blocks blocks = null; + try { + blocks = new TempBlocksMapDB(); + + Block block1 = BlockFixture.newBlock(); + Block block2 = BlockFixture.newBlock(); + testConcurrentBlock_OverlappingSet(blocks, block1, block2); + Assertions.assertTrue(blocks.has(block1.id())); + Assertions.assertTrue(blocks.has(block2.id())); + } finally { + if (blocks != null) { + blocks.close(); + } + } + + } + + // TODO: this method should be refactored with parameters option. + private void testValidated_AlreadyIngestedBlock(Blocks blocks, Block block) { Identifiers seenEntities = mock(Identifiers.class); Identifiers transactionIds = mock(Identifiers.class); Transactions pendingTransactions = mock(Transactions.class); - Block block = BlockFixture.newBlock(); - - IngestEngine ingestEngine = this.mockIngestEngineForEntities( - new ArrayList<>(List.of(block)), - seenEntities, - transactionIds, - pendingTransactions, - blocks); + IngestEngine ingestEngine = this.mockIngestEngineForEntities(new ArrayList<>(List.of(block)), seenEntities, transactionIds, pendingTransactions, blocks); when(seenEntities.has(block.id())).thenReturn(true); // block is already ingested - when(blocks.has(block.id())).thenReturn(true); // block is already ingested - // action ingestEngine.process(block); - // verification - verify(blocks, times(0)).add(block); verify(seenEntities, times(0)).add(block.id()); verify(seenEntities, times(1)).has(block.id()); for (Transaction tx : block.getTransactions()) { @@ -320,44 +535,100 @@ public void testValidatedAlreadyIngestedBlock() { } /** - * Evaluates that when a new validated transaction arrives at ingest engine, - * the engine adds hash of the transaction into its pending transactions' database. + * Evaluates that when an already ingested validated block arrives at ingest engine, + * the engine discards the block right away. + *

+ * This test is using a mocked block storage database. */ @Test - public void testValidatedTransaction() { - // R + public void testValidated_AlreadyIngestedBlock_MockedStorage() { + Blocks blocks = mock(Blocks.class); + Block block = BlockFixture.newBlock(); + when(blocks.has(block.id())).thenReturn(false); + testValidated_AlreadyIngestedBlock(blocks, block); + } + + /** + * Evaluates that when an already ingested validated block arrives at ingest engine, + * the engine discards the block right away. + *

+ * This test is using a real block storage database. + */ + @Test + public void testValidated_AlreadyIngestedBlock_RealStorage() { + Blocks blocks = null; + try { + blocks = new TempBlocksMapDB(); + Block block = BlockFixture.newBlock(); + testValidated_AlreadyIngestedBlock(blocks, block); + } finally { + if (blocks != null) { + blocks.close(); + } + } + + } + + /** + * Test validated transaction for mocked and real blokcs storage. + * + * @param blocks mocked or real block. + */ + private void testValidatedTransaction(Blocks blocks) { Identifiers seenEntities = mock(Identifiers.class); Identifiers transactionIds = mock(Identifiers.class); Transactions pendingTransactions = mock(Transactions.class); - Blocks blocks = mock(Blocks.class); ValidatedTransaction tx = ValidatedTransactionFixture.newValidatedTransaction(); - IngestEngine ingestEngine = this.mockIngestEngineForEntities( - new ArrayList<>(List.of(tx)), - seenEntities, - transactionIds, - pendingTransactions, - blocks); + IngestEngine ingestEngine = this.mockIngestEngineForEntities(new ArrayList<>(List.of(tx)), seenEntities, transactionIds, pendingTransactions, blocks); - // action ingestEngine.process(tx); - // verification verifyTransactionHappyPathCalled(tx, seenEntities, transactionIds, pendingTransactions); } /** - * Evaluates that when two validated transactions arrives at ingest engine sequentially, - * the engine adds the hashes of the transactions into its pending transactions' database. + * Evaluates that when a new validated transaction arrives at ingest engine, with mocked blocks storage, + * the engine adds hash of the transaction into its pending transactions' database. + *

+ * This test is using a mocked block storage database. */ @Test - public void testValidatedTwoTransactions() { - // R + public void testValidatedTransaction_MockedStorage() { + Blocks blocks = mock(Blocks.class); + testValidatedTransaction(blocks); + } + + /** + * Evaluates that when a new validated transaction arrives at ingest engine, with real blocks storage, + * the engine adds hash of the transaction into its pending transactions' database. + *

+ * This test is using a real block storage database. + */ + @Test + public void testValidatedTransaction_RealStorage() { + Blocks blocks = null; + try { + blocks = new TempBlocksMapDB(); + testValidatedTransaction(blocks); + } finally { + if (blocks != null) { + blocks.close(); + } + } + } + + /** + * Test validated two transactions for mocked and real blocks storage. + * + * @param blocks mocked or real block. + */ + // TODO: this method should be refactored with parameters option. + private void testValidated_TwoTransactions(Blocks blocks) { Identifiers seenEntities = mock(Identifiers.class); Identifiers transactionIds = mock(Identifiers.class); Transactions pendingTransactions = mock(Transactions.class); - Blocks blocks = mock(Blocks.class); ValidatedTransaction tx1 = ValidatedTransactionFixture.newValidatedTransaction(); ValidatedTransaction tx2 = ValidatedTransactionFixture.newValidatedTransaction(); @@ -365,49 +636,60 @@ public void testValidatedTwoTransactions() { when(transactionIds.has(any(Identifier.class))).thenReturn(false); when(pendingTransactions.has(any(Identifier.class))).thenReturn(false); - IngestEngine ingestEngine = this.mockIngestEngineForEntities( - new ArrayList<>(Arrays.asList(tx1, tx2)), - seenEntities, - transactionIds, - pendingTransactions, - blocks); + IngestEngine ingestEngine = this.mockIngestEngineForEntities(new ArrayList<>(Arrays.asList(tx1, tx2)), seenEntities, transactionIds, pendingTransactions, blocks); - // action ingestEngine.process(tx1); ingestEngine.process(tx2); - // verification of tx1 verifyTransactionHappyPathCalled(tx1, seenEntities, transactionIds, pendingTransactions); - // verification of tx2 verifyTransactionHappyPathCalled(tx2, seenEntities, transactionIds, pendingTransactions); } /** - * Evaluates that when two validated transactions arrive at ingest engine concurrently, + * Evaluates that when two validated transactions arrives at ingest engine sequentially, + * the engine adds the hashes of the transactions into its pending transactions' database. + */ + @Test + public void testValidated_TwoTransactions_MockedBlocksStorage() { + Blocks blocks = mock(Blocks.class); + testValidated_TwoTransactions(blocks); + } + + /** + * Evaluates that when two validated transactions arrives at ingest engine sequentially, * the engine adds the hashes of the transactions into its pending transactions' database. */ @Test - public void testConcurrentValidatedTwoTransactions() { - // R + public void testValidated_TwoTransactions_RealBlocksStorage() { + Blocks blocks = null; + try { + blocks = new TempBlocksMapDB(); + testValidated_TwoTransactions(blocks); + } finally { + if (blocks != null) { + blocks.close(); + } + } + } + + /** + * Test validated two transactions concurrently for mocked and real blocks storage. + * + * @param blocks mocked or real block. + */ + // TODO: this method should be refactored with parameters option. + private void testConcurrent_ValidatedTwoTransactions(Blocks blocks) { Identifiers seenEntities = mock(Identifiers.class); Identifiers transactionIds = mock(Identifiers.class); Transactions pendingTransactions = mock(Transactions.class); - Blocks blocks = mock(Blocks.class); ValidatedTransaction tx1 = ValidatedTransactionFixture.newValidatedTransaction(); ValidatedTransaction tx2 = ValidatedTransactionFixture.newValidatedTransaction(); - IngestEngine ingestEngine = this.mockIngestEngineForEntities( - new ArrayList<>(Arrays.asList(tx1, tx2)), - seenEntities, - transactionIds, - pendingTransactions, - blocks); + IngestEngine ingestEngine = this.mockIngestEngineForEntities(new ArrayList<>(Arrays.asList(tx1, tx2)), seenEntities, transactionIds, pendingTransactions, blocks); - processEntitiesConcurrently( - ingestEngine, - new ArrayList<>(List.of(tx1, tx2))); + processEntitiesConcurrently(ingestEngine, new ArrayList<>(List.of(tx1, tx2))); // verification of tx1 verifyTransactionHappyPathCalled(tx1, seenEntities, transactionIds, pendingTransactions); @@ -417,130 +699,229 @@ public void testConcurrentValidatedTwoTransactions() { } /** - * Evaluates that when two same validated transactions arrive at ingest engine sequentially, - * the engine adds the hash of the first transaction into its pending transactions' database. - * The engine also discards the second transaction right away. + * Evaluates that when two validated transactions arrive at ingest engine concurrently, with mocked blocks storage, + * the engine adds the hashes of the transactions into its pending transactions' database. */ @Test - public void testValidatedSameTwoTransactions() { - // R + public void testConcurrent_TwoTransactions_MockedBlockStorage() { + Blocks blocks = mock(Blocks.class); + testConcurrent_ValidatedTwoTransactions(blocks); + } + + /** + * Evaluates that when two validated transactions arrive at ingest engine concurrently, with real blocks storage, + * the engine adds the hashes of the transactions into its pending transactions' database. + */ + @Test + public void testConcurrent_TwoTransactions_RealBlockStorage() { + Blocks blocks = null; + try { + blocks = new TempBlocksMapDB(); + testConcurrent_ValidatedTwoTransactions(blocks); + } finally { + if (blocks != null) { + blocks.close(); + } + } + } + + /** + * Test validated same two transactions for mocked and real blocks storage. + * + * @param blocks mocked or real blocks + */ + private void testValidated_SameTwoTransactions(Blocks blocks) { Identifiers seenEntities = mock(Identifiers.class); Identifiers transactionIds = mock(Identifiers.class); Transactions pendingTransactions = mock(Transactions.class); - Blocks blocks = mock(Blocks.class); ValidatedTransaction tx = ValidatedTransactionFixture.newValidatedTransaction(); when(seenEntities.has(tx.id())).thenReturn(false); when(transactionIds.has(tx.id())).thenReturn(false); when(pendingTransactions.has(tx.id())).thenReturn(false); - IngestEngine ingestEngine = this.mockIngestEngineForEntities( - new ArrayList<>(List.of(tx)), - seenEntities, - transactionIds, - pendingTransactions, - blocks); + IngestEngine ingestEngine = this.mockIngestEngineForEntities(new ArrayList<>(List.of(tx)), seenEntities, transactionIds, pendingTransactions, blocks); - // action ingestEngine.process(tx); when(seenEntities.has(tx.id())).thenReturn(true); when(pendingTransactions.has(tx.id())).thenReturn(true); - // process the same transaction again. + ingestEngine.process(tx); - // verification verifyTransactionHappyPathCalled(tx, seenEntities, transactionIds, pendingTransactions); verify(seenEntities, times(2)).has(tx.id()); } /** - * Evaluates that when a validated transaction which is already in transaction identifiers arrives at ingest engine, - * the engine discards the transaction. + * Evaluates that when two same validated transactions arrive at ingest engine sequentially, with mocked blocks, + * the engine adds the hash of the first transaction into its pending transactions' database. + * The engine also discards the second transaction right away. + */ + @Test + public void testValidated_SameTwoTransactions_MockedBlocks() { + Blocks blocks = mock(Blocks.class); + testValidated_SameTwoTransactions(blocks); + } + + /** + * Evaluates that when two same validated transactions arrive at ingest engine sequentially, with real blocks, + * the engine adds the hash of the first transaction into its pending transactions' database. + * The engine also discards the second transaction right away. */ @Test - public void testValidatedTransactionAlreadyInTransactionIdStorage() { - // R + public void testValidated_SameTwoTransactions_RealBlocks() { + Path tempdir = null; + BlocksMapDb db = null; + try { + Path currentRelativePath = Paths.get(""); + tempdir = Files.createTempDirectory(currentRelativePath, TEMP_DIR); + db = new BlocksMapDb(getTemporaryPath(TEMP_FILE_ID), getTemporaryPath(TEMP_FILE_HEIGHT)); + Blocks blocks = db; + testValidated_SameTwoTransactions(blocks); + } catch (IOException e) { + Assertions.fail(); + } finally { + db.close(); + try { + FileUtils.deleteDirectory(new File(tempdir.toString())); + } catch (IOException e) { + Assertions.fail(); + } + } + } + + /** + * Test validated transactions already in transaction id storage for mocked and real blocks. + * + * @param blocks mocked or real blocks. + */ + private void testAlreadyInTransaction_IdStorage(Blocks blocks) { Identifiers seenEntities = mock(Identifiers.class); Identifiers transactionIds = mock(Identifiers.class); Transactions pendingTransactions = mock(Transactions.class); - Blocks blocks = mock(Blocks.class); ValidatedTransaction tx = ValidatedTransactionFixture.newValidatedTransaction(); - final IngestEngine ingestEngine = this.mockIngestEngineForEntities( - new ArrayList<>(List.of(tx)), - seenEntities, - transactionIds, - pendingTransactions, - blocks); + final IngestEngine ingestEngine = this.mockIngestEngineForEntities(new ArrayList<>(List.of(tx)), seenEntities, transactionIds, pendingTransactions, blocks); // transaction is not seen, but it is in transaction ids storage (as the result of processing a validated block). when(seenEntities.has(tx.id())).thenReturn(false); when(transactionIds.has(tx.id())).thenReturn(true); when(pendingTransactions.has(tx.id())).thenReturn(false); - // action ingestEngine.process(tx); - // verification verify(seenEntities, times(1)).add(tx.id()); verify(pendingTransactions, times(1)).has(tx.id()); verify(transactionIds, times(1)).has(tx.id()); verify(pendingTransactions, times(0)).add(tx); } + /** + * Evaluates that when a validated transaction which is already in transaction identifiers arrives at ingest engine, + * with mocked blocks storage, the engine discards the transaction. + */ + @Test + public void testAlreadyInTransaction_IdStorage_MockedBlocks() { + Blocks blocks = mock(Blocks.class); + testAlreadyInTransaction_IdStorage(blocks); + } + + /** + * Evaluates that when a validated transaction which is already in transaction identifiers arrives at ingest engine, + * with real blocks storage, the engine discards the transaction. + */ + @Test + public void testAlreadyInTransaction_IdStorage_RealBlocks() { + Path tempdir = null; + BlocksMapDb db = null; + try { + Path currentRelativePath = Paths.get(""); + tempdir = Files.createTempDirectory(currentRelativePath, TEMP_DIR); + db = new BlocksMapDb(getTemporaryPath(TEMP_FILE_ID), getTemporaryPath(TEMP_FILE_HEIGHT)); + Blocks blocks = db; + testAlreadyInTransaction_IdStorage(blocks); + } catch (IOException e) { + Assertions.fail(); + } finally { + db.close(); + try { + FileUtils.deleteDirectory(new File(tempdir.toString())); + } catch (IOException e) { + Assertions.fail(); + } + } + } + /** * Evaluates that when an entity that is neither a validated block nor a validated transaction - * arrives at ingest engine, the engine throws IllegalArgumentException. + * arrives at ingest engine, with mocked block storage, the engine throws IllegalArgumentException. */ @Test - public void testNeitherBlockNorTransaction() { - // R + public void testNeitherBlockNorTransaction_MockedBlocks() { + Blocks blocks = mock(Blocks.class); + testNeitherBlockNorTransaction(blocks); + } + + /** + * Evaluates that when an entity that is neither a validated block nor a validated transaction + * arrives at ingest engine, with real block storage, the engine throws IllegalArgumentException. + */ + @Test + public void testNeitherBlockNorTransaction_RealBlocks() { + Path tempdir = null; + BlocksMapDb db = null; + try { + Path currentRelativePath = Paths.get(""); + tempdir = Files.createTempDirectory(currentRelativePath, TEMP_DIR); + db = new BlocksMapDb(getTemporaryPath(TEMP_FILE_ID), getTemporaryPath(TEMP_FILE_HEIGHT)); + Blocks blocks = db; + testNeitherBlockNorTransaction(blocks); + } catch (IOException e) { + Assertions.fail(); + } finally { + db.close(); + try { + FileUtils.deleteDirectory(new File(tempdir.toString())); + } catch (IOException e) { + Assertions.fail(); + } + } + } + + /** + * TestNeitherBlockNorTransaction for mocked and real versions. + * + * @param blocks mocked or real blocks + */ + public void testNeitherBlockNorTransaction(Blocks blocks) { Transactions pendingTransactions = mock(Transactions.class); AssignerInf assigner = mock(AssignerInf.class); Identifiers transactionIds = mock(Identifiers.class); - Blocks blocks = mock(Blocks.class); State state = mock(State.class); Identifiers seenEntities = mock(Identifiers.class); Entity e = new EntityFixture(); // not a block nor a transaction - IngestEngine ingestEngine = new IngestEngine( - state, - blocks, - transactionIds, - pendingTransactions, - seenEntities, - assigner); + IngestEngine ingestEngine = new IngestEngine(state, blocks, transactionIds, pendingTransactions, seenEntities, assigner); Assertions.assertThrows(IllegalArgumentException.class, () -> ingestEngine.process(e)); } /** - * Evaluates that when a validated block and a validated transaction arrives at ingest engine concurrently, - * the engine adds the block to its block storage database. - * The engine also adds hash of all the transactions of block - * and the hash of the validated transaction into its "transactions" database. + * Test concurrent validated transaction and block nonoverlapping for mocked and real versions. + * + * @param blocks mocked or real blocks */ - @Test - public void testConcurrentValidatedTransactionAndBlockNonOverlapping() { - // R + private void testConcurrent_TransactionAndBlock_NonOverlapping(Blocks blocks) { Identifiers seenEntities = mock(Identifiers.class); Identifiers transactionIds = mock(Identifiers.class); Transactions pendingTransactions = mock(Transactions.class); - Blocks blocks = mock(Blocks.class); ValidatedTransaction validatedTx = ValidatedTransactionFixture.newValidatedTransaction(); Block block = BlockFixture.newBlock(); - IngestEngine ingestEngine = this.mockIngestEngineForEntities( - new ArrayList<>(Arrays.asList(block, validatedTx)), - seenEntities, - transactionIds, - pendingTransactions, - blocks); + IngestEngine ingestEngine = this.mockIngestEngineForEntities(new ArrayList<>(Arrays.asList(block, validatedTx)), seenEntities, transactionIds, pendingTransactions, blocks); - processEntitiesConcurrently( - ingestEngine, - new ArrayList<>(List.of(block, validatedTx))); + processEntitiesConcurrently(ingestEngine, new ArrayList<>(List.of(block, validatedTx))); // verification for block verifyBlockHappyPathCalled(block, blocks, pendingTransactions, transactionIds, seenEntities); @@ -552,28 +933,57 @@ public void testConcurrentValidatedTransactionAndBlockNonOverlapping() { } /** - * Evaluates that when a validated block and a validated transaction (which the block contains) - * arrive at ingest engine (block first), the engine adds the block to its block storage database. - * The engine also adds hash of all the transactions of block into its "transactions" database. - * Hence, when the transaction that also included in the block comes next, it is never added to pending - * transactions' database. + * Evaluates that when a validated block and a validated transaction arrives at ingest engine concurrently, + * the engine adds the block to its block storage database. + * The engine also adds hash of all the transactions of block + * and the hash of the validated transaction into its "transactions" database. + */ + @Test + public void testConcurrent_TransactionAndBlock_NonOverlapping_MockedBlocks() { + Blocks blocks = mock(Blocks.class); + testConcurrent_TransactionAndBlock_NonOverlapping(blocks); + } + + /** + * Evaluates that when a validated block and a validated transaction arrives at ingest engine concurrently, + * the engine adds the block to its block storage database. + * The engine also adds hash of all the transactions of block + * and the hash of the validated transaction into its "transactions" database. */ @Test - public void testProcessBlockAndIncludedTransaction_BlockFirst() { + public void testConcurrent_TransactionAndBlock_NonOverlapping_RealBlocks() { + Path tempdir = null; + BlocksMapDb db = null; + try { + Path currentRelativePath = Paths.get(""); + tempdir = Files.createTempDirectory(currentRelativePath, TEMP_DIR); + db = new BlocksMapDb(getTemporaryPath(TEMP_FILE_ID), getTemporaryPath(TEMP_FILE_HEIGHT)); + Blocks blocks = db; + testConcurrent_TransactionAndBlock_NonOverlapping(blocks); + } catch (IOException e) { + Assertions.fail(); + } finally { + db.close(); + try { + FileUtils.deleteDirectory(new File(tempdir.toString())); + } catch (IOException e) { + Assertions.fail(); + } + } + } + + /** + * Tests processBlockAndIncludedTransaction_BlockFirst for mocked and real block storages. + * + * @param blocks mocked or real blocks. + */ + private void testIncludedTransaction_BlockFirst(Blocks blocks, Block block) { Identifiers seenEntities = mock(Identifiers.class); Identifiers transactionIds = mock(Identifiers.class); Transactions pendingTransactions = mock(Transactions.class); - Blocks blocks = mock(Blocks.class); - - Block block = BlockFixture.newBlock(); ValidatedTransaction validatedTx = block.getTransactions()[0]; // the transaction is in the block - IngestEngine ingestEngine = this.mockIngestEngineForEntities( - new ArrayList<>(Arrays.asList(block, validatedTx)), - seenEntities, - transactionIds, - pendingTransactions, - blocks); + IngestEngine ingestEngine = this.mockIngestEngineForEntities(new ArrayList<>(Arrays.asList(block, validatedTx)), seenEntities, transactionIds, pendingTransactions, blocks); // process block ingestEngine.process(block); @@ -583,8 +993,6 @@ public void testProcessBlockAndIncludedTransaction_BlockFirst() { // process transaction ingestEngine.process(validatedTx); - // verification for block - verify(blocks, times(1)).add(block); verify(seenEntities, times(1)).add(block.id()); for (Transaction tx : block.getTransactions()) { verify(transactionIds, times(1)).add(tx.id()); @@ -598,28 +1006,64 @@ public void testProcessBlockAndIncludedTransaction_BlockFirst() { /** * Evaluates that when a validated block and a validated transaction (which the block contains) - * arrive at ingest engine (transaction first), the engine adds the block to its block storage database. + * arrive at ingest engine (block first), the engine adds the block to its mocked block storage database. + * The engine also adds hash of all the transactions of block into its "transactions" database. + * Hence, when the transaction that also included in the block comes next, it is never added to pending + * transactions' database. + */ + @Test + public void testIncludedTransaction_BlockFirst_MockedBlocks() { + Blocks blocks = mock(Blocks.class); + Block block = BlockFixture.newBlock(); + when(blocks.has(block.id())).thenReturn(false); + testIncludedTransaction_BlockFirst(blocks, block); + verify(blocks, times(1)).add(block); + } + + /** + * Evaluates that when a validated block and a validated transaction (which the block contains) + * arrive at ingest engine (block first), the engine adds the block to its real block storage database. * The engine also adds hash of all the transactions of block into its "transactions" database. * Hence, when the transaction that also included in the block comes next, it is never added to pending * transactions' database. */ @Test - public void testProcessBlockAndIncludedTransaction_TransactionFirst() { - // R + public void testIncludedTransaction_BlockFirst_RealBlocks() { + Path tempdir = null; + BlocksMapDb db = null; + try { + Path currentRelativePath = Paths.get(""); + tempdir = Files.createTempDirectory(currentRelativePath, TEMP_DIR); + db = new BlocksMapDb(getTemporaryPath(TEMP_FILE_ID), getTemporaryPath(TEMP_FILE_HEIGHT)); + Blocks blocks = db; + Block block = BlockFixture.newBlock(); + testIncludedTransaction_BlockFirst(blocks, block); + Assertions.assertTrue(blocks.has(block.id())); + } catch (IOException e) { + Assertions.fail(); + } finally { + db.close(); + try { + FileUtils.deleteDirectory(new File(tempdir.toString())); + } catch (IOException e) { + Assertions.fail(); + } + } + } + + /** + * Tests process block and included transaction and transaction first for mocked and real blocks. + * + * @param blocks real or mocked blocks. + */ + private void testIncludedTransaction_TransactionFirst(Blocks blocks, Block block) { Identifiers seenEntities = mock(Identifiers.class); Identifiers transactionIds = mock(Identifiers.class); Transactions pendingTransactions = mock(Transactions.class); - Blocks blocks = mock(Blocks.class); - Block block = BlockFixture.newBlock(); ValidatedTransaction validatedTx = block.getTransactions()[0]; // the transaction is in the block - IngestEngine ingestEngine = this.mockIngestEngineForEntities( - new ArrayList<>(Arrays.asList(block, validatedTx)), - seenEntities, - transactionIds, - pendingTransactions, - blocks); + IngestEngine ingestEngine = this.mockIngestEngineForEntities(new ArrayList<>(Arrays.asList(block, validatedTx)), seenEntities, transactionIds, pendingTransactions, blocks); // process transaction first. ingestEngine.process(validatedTx); @@ -630,8 +1074,6 @@ public void testProcessBlockAndIncludedTransaction_TransactionFirst() { // process block next. ingestEngine.process(block); - // verification for block - verify(blocks, times(1)).add(block); verify(seenEntities, times(1)).add(block.id()); for (Transaction tx : block.getTransactions()) { verify(transactionIds, times(1)).add(tx.id()); @@ -645,9 +1087,57 @@ public void testProcessBlockAndIncludedTransaction_TransactionFirst() { verify(pendingTransactions, times(1)).remove(validatedTx.id()); } + /** + * Evaluates that when a validated block and a validated transaction (which the block contains) + * arrive at ingest engine (transaction first), the engine adds the block to its mocked block storage database. + * The engine also adds hash of all the transactions of block into its "transactions" database. + * Hence, when the transaction that also included in the block comes next, it is never added to pending + * transactions' database. + */ + @Test + public void testIncludedTransaction_TransactionFirst_MockedBlocks() { + Blocks blocks = mock(Blocks.class); + Block block = BlockFixture.newBlock(); + when(blocks.has(block.id())).thenReturn(false); + testIncludedTransaction_TransactionFirst(blocks, block); + verify(blocks, times(1)).add(block); + } + + /** + * Evaluates that when a validated block and a validated transaction (which the block contains) + * arrive at ingest engine (transaction first), the engine adds the block to its real block storage database. + * The engine also adds hash of all the transactions of block into its "transactions" database. + * Hence, when the transaction that also included in the block comes next, it is never added to pending + * transactions' database. + */ + @Test + public void testIncludedTransaction_TransactionFirst_RealBlocks() { + Path tempdir = null; + BlocksMapDb db = null; + try { + Path currentRelativePath = Paths.get(""); + tempdir = Files.createTempDirectory(currentRelativePath, TEMP_DIR); + db = new BlocksMapDb(getTemporaryPath(TEMP_FILE_ID), getTemporaryPath(TEMP_FILE_HEIGHT)); + Blocks blocks = db; + Block block = BlockFixture.newBlock(); + testIncludedTransaction_TransactionFirst(blocks, block); + Assertions.assertTrue(blocks.has(block.id())); + } catch (IOException e) { + Assertions.fail(); + } finally { + db.close(); + try { + FileUtils.deleteDirectory(new File(tempdir.toString())); + } catch (IOException e) { + Assertions.fail(); + } + } + + } + /** * Verifies mocked storage components have been called with expected parameters on an - * expected number of times for block happy path, i.e., block is added to the blocks storage, and its id is + * expected number of times for block happy path, i.e., block is added to the block's storage, and its id is * added to seenEntities storage. Also, all its transactions ids are added to the pendingTx and * txIds. * @@ -657,14 +1147,7 @@ public void testProcessBlockAndIncludedTransaction_TransactionFirst() { * @param txIds the transaction identifiers. * @param seenEntities identifiers of processed entities by engine. */ - private void verifyBlockHappyPathCalled( - Block block, - Blocks blocks, - Transactions pendingTx, - Identifiers txIds, - Identifiers seenEntities) { - - verify(blocks, times(1)).add(block); + private void verifyBlockHappyPathCalled(Block block, Blocks blocks, Transactions pendingTx, Identifiers txIds, Identifiers seenEntities) { verify(seenEntities, times(1)).add(block.id()); for (Transaction tx : block.getTransactions()) { verify(pendingTx, times(1)).has(tx.id()); @@ -681,11 +1164,7 @@ private void verifyBlockHappyPathCalled( * @param txIds the transaction identifiers. * @param pendingTx the pending transactions identifiers. */ - private void verifyTransactionHappyPathCalled( - Transaction transaction, - Identifiers seenEntities, - Identifiers txIds, - Transactions pendingTx) { + private void verifyTransactionHappyPathCalled(Transaction transaction, Identifiers seenEntities, Identifiers txIds, Transactions pendingTx) { verify(seenEntities, times(1)).add(transaction.id()); verify(txIds, times(1)).has(transaction.id()); @@ -702,12 +1181,7 @@ private void verifyTransactionHappyPathCalled( * @param blocks the blocks storage component. * @return mocked ingest engine with mocked components. */ - private IngestEngine mockIngestEngineForEntities( - ArrayList entities, - Identifiers seenEntities, - Identifiers txIds, - Transactions pendingTx, - Blocks blocks) { + private IngestEngine mockIngestEngineForEntities(ArrayList entities, Identifiers seenEntities, Identifiers txIds, Transactions pendingTx, Blocks blocks) { Snapshot snapshot = mock(Snapshot.class); AssignerInf assigner = mock(AssignerInf.class); @@ -731,7 +1205,6 @@ private IngestEngine mockIngestEngineForEntities( Block block = (Block) e; when(state.atBlockId(block.getPreviousBlockId())).thenReturn(snapshot); - when(blocks.has(block.id())).thenReturn(false); for (Transaction tx : block.getTransactions()) { when(pendingTx.has(tx.id())).thenReturn(false); } @@ -742,13 +1215,7 @@ private IngestEngine mockIngestEngineForEntities( } - return new IngestEngine( - state, - blocks, - txIds, - pendingTx, - seenEntities, - assigner); + return new IngestEngine(state, blocks, txIds, pendingTx, seenEntities, assigner); } /** @@ -757,9 +1224,7 @@ private IngestEngine mockIngestEngineForEntities( * @param ingestEngine ingest engine. * @param entities the entities to be processed. */ - private void processEntitiesConcurrently( - IngestEngine ingestEngine, - ArrayList entities) { + private void processEntitiesConcurrently(IngestEngine ingestEngine, ArrayList entities) { AtomicInteger threadError = new AtomicInteger(); int concurrencyDegree = entities.size(); @@ -810,4 +1275,22 @@ private void mockAssignment(AssignerInf assigner, Entity e, Snapshot snapshot) { // returns the mock account for all identifiers when(snapshot.getAccount(any(Identifier.class))).thenReturn(account); } + + /** + * Test validated single block for mocked and real versions. + * + * @param blocks mocked or real block storage. + * @param block the real block + */ + private void testValidatedSingleBlock(Blocks blocks, Block block) { + Identifiers seenEntities = mock(Identifiers.class); + Identifiers transactionIds = mock(Identifiers.class); + Transactions pendingTransactions = mock(Transactions.class); + + when(seenEntities.has(block.id())).thenReturn(false); + + IngestEngine ingestEngine = this.mockIngestEngineForEntities(new ArrayList<>(List.of(block)), seenEntities, transactionIds, pendingTransactions, blocks); + ingestEngine.process(block); + verifyBlockHappyPathCalled(block, blocks, pendingTransactions, transactionIds, seenEntities); + } } \ No newline at end of file diff --git a/src/test/java/storage/BlocksTest.java b/src/test/java/storage/BlocksTest.java index 50e990f7..f0f2746e 100644 --- a/src/test/java/storage/BlocksTest.java +++ b/src/test/java/storage/BlocksTest.java @@ -55,7 +55,7 @@ void setup() throws IOException { */ @AfterEach void cleanup() throws IOException { - db.closeDb(); + db.close(); FileUtils.deleteDirectory(new File(tempdir.toString())); } diff --git a/src/test/java/unittest/storage/TempBlocksMapDB.java b/src/test/java/unittest/storage/TempBlocksMapDB.java new file mode 100644 index 00000000..b9b41543 --- /dev/null +++ b/src/test/java/unittest/storage/TempBlocksMapDB.java @@ -0,0 +1,81 @@ +package unittest.storage; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; + +import model.lightchain.Block; +import model.lightchain.Identifier; +import storage.Blocks; +import storage.mapdb.BlocksMapDb; + +public class TempBlocksMapDB implements Blocks { + private static final String TEMP_DIR = "tempdir"; + private static final String TEMP_FILE_ID = "tempfileID.db"; + private static final String TEMP_FILE_HEIGHT = "tempfileHEIGHT.db"; + + private final Blocks blocks; + private final Path tempDir; + + public TempBlocksMapDB() { + Path currentRelativePath = Paths.get(""); + try { + this.tempDir = Files.createTempDirectory(currentRelativePath, TEMP_DIR); + } catch (IOException e) { + throw new RuntimeException(e); + } + this.blocks = new BlocksMapDb(getTemporaryPath(TEMP_FILE_ID), getTemporaryPath(TEMP_FILE_HEIGHT)); + } + + @Override + public boolean has(Identifier blockId) { + return this.blocks.has(blockId); + } + + @Override + public boolean add(Block block) { + return this.blocks.add(block); + } + + @Override + public boolean remove(Identifier blockId) { + return this.blocks.remove(blockId); + } + + @Override + public Block byId(Identifier blockId) { + return this.blocks.byId(blockId); + } + + @Override + public Block atHeight(long height) { + return this.blocks.atHeight(height); + } + + @Override + public ArrayList all() { + return this.blocks.all(); + } + + public void close() { + this.blocks.close(); + try { + Files.deleteIfExists(this.tempDir); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private String getTemporaryPath(String label) { + Path temporaryDir; + Path currentRelativePath = Paths.get(""); + try { + temporaryDir = Files.createTempDirectory(currentRelativePath, TEMP_DIR); + } catch (IOException e) { + throw new RuntimeException(e); + } + return temporaryDir.toAbsolutePath() + "/" + label; + } +}