From eaf8aeaae4813f4fa3641d0b4154e7af5ded2862 Mon Sep 17 00:00:00 2001 From: CATS Date: Sun, 5 Apr 2026 19:16:06 +0000 Subject: [PATCH] refactor: extract shared BeaconChain test suite Factor out common BeaconChain contract tests into BeaconChainTestSuite in core testFixtures. Both InMemoryBeaconChainTest and KvDatabaseTest now extend this shared suite, eliminating duplicated test logic for getSealedBeaconBlocks, state/block CRUD, rollback, and null lookups. Implementation-specific tests remain in their respective test classes: - InMemory: uncommitted visibility, byte-array equality, state-by-number - KvDatabase: persistence across restarts, repeated writes, P2P state Closes #198 --- core/build.gradle | 3 + .../maru/core/InMemoryBeaconChainTest.kt | 182 ++--------------- .../maru/database/BeaconChainTestSuite.kt | 193 ++++++++++++++++++ .../kotlin/maru/database/kv/KvDatabaseTest.kt | 160 ++------------- 4 files changed, 227 insertions(+), 311 deletions(-) create mode 100644 core/src/testFixtures/kotlin/maru/database/BeaconChainTestSuite.kt diff --git a/core/build.gradle b/core/build.gradle index aa8acda6f..9f1b9eda4 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -37,4 +37,7 @@ dependencies { testFixturesImplementation(project(":observability")) testFixturesImplementation(project(":p2p")) testFixturesImplementation(project(":crypto")) + testFixturesImplementation platform("org.junit:junit-bom") + testFixturesImplementation "org.junit.jupiter:junit-jupiter" + testFixturesImplementation "org.assertj:assertj-core" } diff --git a/core/src/test/kotlin/maru/core/InMemoryBeaconChainTest.kt b/core/src/test/kotlin/maru/core/InMemoryBeaconChainTest.kt index b86cb748e..dd8a282b5 100644 --- a/core/src/test/kotlin/maru/core/InMemoryBeaconChainTest.kt +++ b/core/src/test/kotlin/maru/core/InMemoryBeaconChainTest.kt @@ -8,18 +8,23 @@ */ package maru.core -import kotlin.random.Random import maru.core.ext.DataGenerators +import maru.database.BeaconChain +import maru.database.BeaconChainTestSuite import maru.database.InMemoryBeaconChain import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -class InMemoryBeaconChainTest { +class InMemoryBeaconChainTest : BeaconChainTestSuite() { private lateinit var initialBeaconState: BeaconState private lateinit var inMemoryBeaconChain: InMemoryBeaconChain + override fun createBeaconChain(initialBeaconState: BeaconState): BeaconChain { + val chain = InMemoryBeaconChain(initialBeaconState) + return chain + } + @BeforeEach fun setUp() { initialBeaconState = DataGenerators.randomBeaconState(2UL) @@ -27,81 +32,10 @@ class InMemoryBeaconChainTest { } @Test - fun `getLatestBeaconState returns initial state`() { - val latestBeaconState = inMemoryBeaconChain.getLatestBeaconState() - assertThat(latestBeaconState).isEqualTo(initialBeaconState) - } - - @Test - fun `getBeaconState returns null for unknown block root`() { - val beaconState = inMemoryBeaconChain.getBeaconState(Random.nextBytes(32)) - assertThat(beaconState).isNull() - } - - @Test - fun `getSealedBeaconBlock returns null for unknown block root`() { - val sealedBeaconBlock = inMemoryBeaconChain.getSealedBeaconBlock(Random.nextBytes(32)) - assertThat(sealedBeaconBlock).isNull() - } - - @Test - fun `getSealedBeaconBlock returns null for unknown block number`() { - val sealedBeaconBlock = inMemoryBeaconChain.getSealedBeaconBlock(100uL) - assertThat(sealedBeaconBlock).isNull() - } - - @Test - fun `newBeaconChainUpdater can put and commit beacon state`() { - val newBeaconState = DataGenerators.randomBeaconState(2UL) - val updater = inMemoryBeaconChain.newBeaconChainUpdater() - updater.putBeaconState(newBeaconState).commit() - - val latestBeaconState = inMemoryBeaconChain.getLatestBeaconState() - assertThat(latestBeaconState).isEqualTo(newBeaconState) - - val retrievedBeaconState = inMemoryBeaconChain.getBeaconState(newBeaconState.beaconBlockHeader.hash) - assertThat(retrievedBeaconState).isEqualTo(newBeaconState) - } - - @Test - fun `newBeaconChainUpdater can put and commit sealed beacon block`() { - val sealedBeaconBlock = DataGenerators.randomSealedBeaconBlock(3UL) - val beaconBlockRoot = sealedBeaconBlock.beaconBlock.beaconBlockHeader.hash - val updater = inMemoryBeaconChain.newBeaconChainUpdater() - updater.putSealedBeaconBlock(sealedBeaconBlock).commit() - - val retrievedSealedBeaconBlockByBlockRoot = inMemoryBeaconChain.getSealedBeaconBlock(beaconBlockRoot) - assertThat(retrievedSealedBeaconBlockByBlockRoot).isEqualTo(sealedBeaconBlock) - - val retrievedSealedBeaconBlockByBlockNumber = - inMemoryBeaconChain - .getSealedBeaconBlock(sealedBeaconBlock.beaconBlock.beaconBlockHeader.number) - assertThat(retrievedSealedBeaconBlockByBlockNumber).isEqualTo(sealedBeaconBlock) - } - - @Test - fun `newBeaconChainUpdater can rollback changes`() { - val newBeaconState = DataGenerators.randomBeaconState(4UL) - val sealedBeaconBlock = DataGenerators.randomSealedBeaconBlock(5UL) - val beaconBlockRoot = sealedBeaconBlock.beaconBlock.beaconBlockHeader.hash - val updater = inMemoryBeaconChain.newBeaconChainUpdater() - updater.putBeaconState(newBeaconState) - updater.putSealedBeaconBlock(sealedBeaconBlock) - updater.rollback() - - val latestBeaconState = inMemoryBeaconChain.getLatestBeaconState() - assertThat(latestBeaconState).isEqualTo(initialBeaconState) - - val retrievedBeaconState = inMemoryBeaconChain.getBeaconState(newBeaconState.beaconBlockHeader.hash) - assertThat(retrievedBeaconState).isNull() - - val retrievedSealedBeaconBlockByBlockRoot = inMemoryBeaconChain.getSealedBeaconBlock(beaconBlockRoot) - assertThat(retrievedSealedBeaconBlockByBlockRoot).isNull() - - val retrievedSealedBeaconBlockByBlockNumber = - inMemoryBeaconChain - .getSealedBeaconBlock(sealedBeaconBlock.beaconBlock.beaconBlockHeader.number) - assertThat(retrievedSealedBeaconBlockByBlockNumber).isNull() + fun `initial state can be found by number`() { + val initialBeaconStateByNumber = + inMemoryBeaconChain.getBeaconState(initialBeaconState.beaconBlockHeader.number) + assertThat(initialBeaconStateByNumber).isEqualTo(initialBeaconState) } @Test @@ -128,98 +62,6 @@ class InMemoryBeaconChainTest { assertThat(retrievedSealedBeaconBlockByBlockNumber).isNull() } - @Test - fun `initial state can be found by hash`() { - val initialBeaconStateByHash = inMemoryBeaconChain.getBeaconState(initialBeaconState.beaconBlockHeader.hash) - assertThat(initialBeaconStateByHash).isEqualTo(initialBeaconState) - } - - @Test - fun `initial state can be found by number`() { - val initialBeaconStateByNumber = - inMemoryBeaconChain.getBeaconState(initialBeaconState.beaconBlockHeader.number) - assertThat(initialBeaconStateByNumber).isEqualTo(initialBeaconState) - } - - @Test - fun `getSealedBeaconBlocks returns consecutive blocks`() { - val testBlocks = (0uL..5uL).map { DataGenerators.randomSealedBeaconBlock(it) } - - val updater = inMemoryBeaconChain.newBeaconChainUpdater() - testBlocks.forEach { block -> - updater.putSealedBeaconBlock(block) - } - updater.putBeaconState( - BeaconState( - beaconBlockHeader = testBlocks.last().beaconBlock.beaconBlockHeader, - validators = DataGenerators.randomValidators(), - ), - ) - updater.commit() - - val startBlockNumber = 2uL - val count = 3uL - val blocks = inMemoryBeaconChain.getSealedBeaconBlocks(startBlockNumber, count) - assertThat(blocks).hasSize(3) - assertThat(blocks).isEqualTo(testBlocks.subList(startBlockNumber.toInt(), (startBlockNumber + count).toInt())) - } - - @Test - fun `getSealedBeaconBlocks returns empty list when count is zero`() { - val testBlock = DataGenerators.randomSealedBeaconBlock(1uL) - - val updater = inMemoryBeaconChain.newBeaconChainUpdater() - updater.putSealedBeaconBlock(testBlock).commit() - - val blocks = inMemoryBeaconChain.getSealedBeaconBlocks(startBlockNumber = 1uL, count = 0uL) - assertThat(blocks).isEmpty() - } - - @Test - fun `getSealedBeaconBlocks stops at gap in sequence`() { - val block1 = DataGenerators.randomSealedBeaconBlock(1uL) - val block2 = DataGenerators.randomSealedBeaconBlock(2uL) - val block4 = DataGenerators.randomSealedBeaconBlock(4uL) - - val updater = inMemoryBeaconChain.newBeaconChainUpdater() - updater - .putSealedBeaconBlock(block1) - .putSealedBeaconBlock(block2) - .putSealedBeaconBlock(block4) - .putBeaconState( - BeaconState( - beaconBlockHeader = block4.beaconBlock.beaconBlockHeader, - validators = DataGenerators.randomValidators(), - ), - ).commit() - - assertThatThrownBy { - inMemoryBeaconChain.getSealedBeaconBlocks(startBlockNumber = 1uL, count = 5uL) - }.isInstanceOf(IllegalStateException::class.java) - .hasMessage("Missing sealed beacon block 3") - } - - @Test - fun `getSealedBeaconBlocks returns available blocks when count exceeds available`() { - val testBlocks = (1uL..3uL).map { DataGenerators.randomSealedBeaconBlock(it) } - - val updater = inMemoryBeaconChain.newBeaconChainUpdater() - testBlocks.forEach { block -> - updater.putSealedBeaconBlock(block) - } - updater.putBeaconState( - BeaconState( - beaconBlockHeader = testBlocks.last().beaconBlock.beaconBlockHeader, - validators = DataGenerators.randomValidators(), - ), - ) - updater.commit() - - val blocks = inMemoryBeaconChain.getSealedBeaconBlocks(startBlockNumber = 1uL, count = 10uL) - assertThat(blocks).hasSize(3) - assertThat(blocks).isEqualTo(testBlocks) - } - @Test fun `getBeaconState finds state by block root with different byte arrays`() { val newBeaconState = DataGenerators.randomBeaconState(3UL) diff --git a/core/src/testFixtures/kotlin/maru/database/BeaconChainTestSuite.kt b/core/src/testFixtures/kotlin/maru/database/BeaconChainTestSuite.kt new file mode 100644 index 000000000..37693f399 --- /dev/null +++ b/core/src/testFixtures/kotlin/maru/database/BeaconChainTestSuite.kt @@ -0,0 +1,193 @@ +/* + * Copyright Consensys Software Inc. + * + * This file is dual-licensed under either the MIT license or Apache License 2.0. + * See the LICENSE-MIT and LICENSE-APACHE files in the repository root for details. + * + * SPDX-License-Identifier: MIT OR Apache-2.0 + */ +package maru.database + +import kotlin.random.Random +import maru.core.BeaconState +import maru.core.ext.DataGenerators +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.assertThatThrownBy +import org.junit.jupiter.api.Test + +/** + * Shared test suite for [BeaconChain] implementations. + * + * Subclasses must implement [createBeaconChain] to provide a fresh, initialized instance + * for each test. The instance should already contain an initial beacon state so that + * [BeaconChain.getLatestBeaconState] returns a valid result. + */ +abstract class BeaconChainTestSuite { + /** + * Creates a new [BeaconChain] instance pre-loaded with [initialBeaconState]. + * Called once per test method. + */ + abstract fun createBeaconChain(initialBeaconState: BeaconState): BeaconChain + + private fun withBeaconChain( + initialBlockNumber: ULong = 2UL, + block: (BeaconChain, BeaconState) -> Unit, + ) { + val initialBeaconState = DataGenerators.randomBeaconState(initialBlockNumber) + val beaconChain = createBeaconChain(initialBeaconState) + beaconChain.use { block(it, initialBeaconState) } + } + + @Test + fun `getLatestBeaconState returns initial state`() = withBeaconChain { db, initialState -> + assertThat(db.getLatestBeaconState()).isEqualTo(initialState) + } + + @Test + fun `getBeaconState returns null for unknown block root`() = withBeaconChain { db, _ -> + assertThat(db.getBeaconState(Random.nextBytes(32))).isNull() + } + + @Test + fun `getSealedBeaconBlock returns null for unknown block root`() = withBeaconChain { db, _ -> + assertThat(db.getSealedBeaconBlock(Random.nextBytes(32))).isNull() + } + + @Test + fun `getSealedBeaconBlock returns null for unknown block number`() = withBeaconChain { db, _ -> + assertThat(db.getSealedBeaconBlock(100uL)).isNull() + } + + @Test + fun `newBeaconChainUpdater can put and commit beacon state`() = withBeaconChain { db, _ -> + val newBeaconState = DataGenerators.randomBeaconState(3UL) + db.newBeaconChainUpdater().use { + it.putBeaconState(newBeaconState).commit() + } + + assertThat(db.getLatestBeaconState()).isEqualTo(newBeaconState) + assertThat(db.getBeaconState(newBeaconState.beaconBlockHeader.hash)).isEqualTo(newBeaconState) + } + + @Test + fun `newBeaconChainUpdater can put and commit sealed beacon block`() = withBeaconChain { db, _ -> + val sealedBeaconBlock = DataGenerators.randomSealedBeaconBlock(3UL) + val beaconBlockRoot = sealedBeaconBlock.beaconBlock.beaconBlockHeader.hash + db.newBeaconChainUpdater().use { + it.putSealedBeaconBlock(sealedBeaconBlock).commit() + } + + assertThat(db.getSealedBeaconBlock(beaconBlockRoot)).isEqualTo(sealedBeaconBlock) + assertThat( + db.getSealedBeaconBlock(sealedBeaconBlock.beaconBlock.beaconBlockHeader.number), + ).isEqualTo(sealedBeaconBlock) + } + + @Test + fun `newBeaconChainUpdater can rollback changes`() = withBeaconChain { db, initialState -> + val newBeaconState = DataGenerators.randomBeaconState(4UL) + val sealedBeaconBlock = DataGenerators.randomSealedBeaconBlock(5UL) + val beaconBlockRoot = sealedBeaconBlock.beaconBlock.beaconBlockHeader.hash + db.newBeaconChainUpdater().use { + it.putBeaconState(newBeaconState) + it.putSealedBeaconBlock(sealedBeaconBlock) + it.rollback() + } + + assertThat(db.getLatestBeaconState()).isEqualTo(initialState) + assertThat(db.getBeaconState(newBeaconState.beaconBlockHeader.hash)).isNull() + assertThat(db.getSealedBeaconBlock(beaconBlockRoot)).isNull() + assertThat( + db.getSealedBeaconBlock(sealedBeaconBlock.beaconBlock.beaconBlockHeader.number), + ).isNull() + } + + @Test + fun `initial state can be found by hash`() = withBeaconChain { db, initialState -> + assertThat(db.getBeaconState(initialState.beaconBlockHeader.hash)).isEqualTo(initialState) + } + + @Test + fun `getSealedBeaconBlocks returns consecutive blocks`() = withBeaconChain { db, _ -> + val testBlocks = (0uL..5uL).map { DataGenerators.randomSealedBeaconBlock(it) } + + db.newBeaconChainUpdater().use { updater -> + testBlocks.forEach { block -> updater.putSealedBeaconBlock(block) } + updater.putBeaconState( + BeaconState( + beaconBlockHeader = testBlocks.last().beaconBlock.beaconBlockHeader, + validators = DataGenerators.randomValidators(), + ), + ) + updater.commit() + } + + val startBlockNumber = 2uL + val count = 3uL + val blocks = db.getSealedBeaconBlocks(startBlockNumber, count) + assertThat(blocks).hasSize(3) + assertThat(blocks).isEqualTo(testBlocks.subList(startBlockNumber.toInt(), (startBlockNumber + count).toInt())) + } + + @Test + fun `getSealedBeaconBlocks returns empty list when count is zero`() = withBeaconChain { db, _ -> + val testBlock = DataGenerators.randomSealedBeaconBlock(1uL) + db.newBeaconChainUpdater().use { updater -> + updater.putSealedBeaconBlock(testBlock) + updater.putBeaconState( + BeaconState( + beaconBlockHeader = testBlock.beaconBlock.beaconBlockHeader, + validators = DataGenerators.randomValidators(), + ), + ) + updater.commit() + } + + assertThat(db.getSealedBeaconBlocks(startBlockNumber = 1uL, count = 0uL)).isEmpty() + } + + @Test + fun `getSealedBeaconBlocks stops at gap in sequence`() = withBeaconChain { db, _ -> + val block1 = DataGenerators.randomSealedBeaconBlock(1uL) + val block2 = DataGenerators.randomSealedBeaconBlock(2uL) + val block4 = DataGenerators.randomSealedBeaconBlock(4uL) + + db.newBeaconChainUpdater().use { updater -> + updater + .putSealedBeaconBlock(block1) + .putSealedBeaconBlock(block2) + .putSealedBeaconBlock(block4) + .putBeaconState( + BeaconState( + beaconBlockHeader = block4.beaconBlock.beaconBlockHeader, + validators = DataGenerators.randomValidators(), + ), + ).commit() + } + + assertThatThrownBy { + db.getSealedBeaconBlocks(startBlockNumber = 1uL, count = 5uL) + }.isInstanceOf(IllegalStateException::class.java) + .hasMessage("Missing sealed beacon block 3") + } + + @Test + fun `getSealedBeaconBlocks returns available blocks when count exceeds available`() = withBeaconChain { db, _ -> + val testBlocks = (1uL..3uL).map { DataGenerators.randomSealedBeaconBlock(it) } + + db.newBeaconChainUpdater().use { updater -> + testBlocks.forEach { block -> updater.putSealedBeaconBlock(block) } + updater.putBeaconState( + BeaconState( + beaconBlockHeader = testBlocks.last().beaconBlock.beaconBlockHeader, + validators = DataGenerators.randomValidators(), + ), + ) + updater.commit() + } + + val blocks = db.getSealedBeaconBlocks(startBlockNumber = 1uL, count = 10uL) + assertThat(blocks).hasSize(3) + assertThat(blocks).isEqualTo(testBlocks) + } +} diff --git a/storage/src/test/kotlin/maru/database/kv/KvDatabaseTest.kt b/storage/src/test/kotlin/maru/database/kv/KvDatabaseTest.kt index 44157d0e0..4b88be1e9 100644 --- a/storage/src/test/kotlin/maru/database/kv/KvDatabaseTest.kt +++ b/storage/src/test/kotlin/maru/database/kv/KvDatabaseTest.kt @@ -9,18 +9,21 @@ package maru.database.kv import java.nio.file.Path -import kotlin.random.Random import maru.core.BeaconState import maru.core.ext.DataGenerators import maru.core.ext.metrics.TestMetrics +import maru.database.BeaconChain +import maru.database.BeaconChainTestSuite import maru.metrics.BesuMetricsCategoryAdapter import maru.metrics.MaruMetricsCategory import org.assertj.core.api.Assertions.assertThat -import org.assertj.core.api.Assertions.assertThatThrownBy import org.junit.jupiter.api.Test import org.junit.jupiter.api.io.TempDir -class KvDatabaseTest { +class KvDatabaseTest : BeaconChainTestSuite() { + @field:TempDir + lateinit var sharedDatabasePath: Path + private fun createDatabase(databasePath: Path): KvDatabase = KvDatabaseFactory.createRocksDbDatabase( databasePath = databasePath, @@ -28,8 +31,16 @@ class KvDatabaseTest { metricCategory = BesuMetricsCategoryAdapter.from(MaruMetricsCategory.STORAGE), ) + override fun createBeaconChain(initialBeaconState: BeaconState): BeaconChain { + val db = createDatabase(sharedDatabasePath) + db.newBeaconChainUpdater().use { + it.putBeaconState(initialBeaconState).commit() + } + return db + } + @Test - fun `test read and write beacon state`( + fun `test beacon state persists across database restarts`( @TempDir databasePath: Path, ) { val testBeaconStates = (1..10).map { DataGenerators.randomBeaconState(it.toULong()) } @@ -52,7 +63,7 @@ class KvDatabaseTest { } @Test - fun `test read and write latest beacon state`( + fun `test latest beacon state persists across database restarts`( @TempDir databasePath: Path, ) { val testBeaconStates = (1..10).map { DataGenerators.randomBeaconState(it.toULong()) } @@ -73,19 +84,7 @@ class KvDatabaseTest { } @Test - fun `test invalid key read`( - @TempDir databasePath: Path, - ) { - val randomKey = Random.nextBytes(32) - createDatabase(databasePath).use { db -> - assertThat(db.getBeaconState(randomKey)).isNull() - assertThat(db.getSealedBeaconBlock(randomKey)).isNull() - assertThat(db.getSealedBeaconBlock(100uL)).isNull() - } - } - - @Test - fun `test read and write beacon blocks`( + fun `test beacon blocks persist across database restarts`( @TempDir databasePath: Path, ) { val testBeaconBlocks = @@ -115,7 +114,7 @@ class KvDatabaseTest { } @Test - fun `test repeated write`( + fun `test repeated write persists correctly`( @TempDir databasePath: Path, ) { val testBeaconBlock = DataGenerators.randomSealedBeaconBlock(1uL) @@ -138,7 +137,7 @@ class KvDatabaseTest { } @Test - fun `test update rollback`( + fun `test rollback persists correctly across database restarts`( @TempDir databasePath: Path, ) { val testBeaconBlock1 = DataGenerators.randomSealedBeaconBlock(1uL) @@ -175,127 +174,6 @@ class KvDatabaseTest { } } - @Test - fun `test getSealedBeaconBlocks returns consecutive blocks`( - @TempDir databasePath: Path, - ) { - val testBlocks = (0uL..5uL).map { DataGenerators.randomSealedBeaconBlock(it) } - - createDatabase(databasePath).use { db -> - // Store blocks - testBlocks.forEach { block -> - db.newBeaconChainUpdater().use { - it.putSealedBeaconBlock(block).commit() - } - } - db.newBeaconChainUpdater().use { - it - .putBeaconState( - BeaconState( - beaconBlockHeader = testBlocks.last().beaconBlock.beaconBlockHeader, - validators = DataGenerators.randomValidators(), - ), - ).commit() - } - - // Get blocks 2-4 - val startBlockNumber = 2uL - val count = 3uL - val blocks = db.getSealedBeaconBlocks(startBlockNumber, count) - assertThat(blocks).hasSize(3) - assertThat(blocks).isEqualTo(testBlocks.subList(startBlockNumber.toInt(), (startBlockNumber + count).toInt())) - } - } - - @Test - fun `test getSealedBeaconBlocks returns empty list when count is zero`( - @TempDir databasePath: Path, - ) { - val testBlock = DataGenerators.randomSealedBeaconBlock(1uL) - - createDatabase(databasePath).use { db -> - db.newBeaconChainUpdater().use { - it.putSealedBeaconBlock(testBlock).commit() - } - db.newBeaconChainUpdater().use { - it - .putBeaconState( - BeaconState( - beaconBlockHeader = testBlock.beaconBlock.beaconBlockHeader, - validators = DataGenerators.randomValidators(), - ), - ).commit() - } - - val blocks = db.getSealedBeaconBlocks(startBlockNumber = 1uL, count = 0uL) - assertThat(blocks).isEmpty() - } - } - - @Test - fun `test getSealedBeaconBlocks stops at gap in sequence`( - @TempDir databasePath: Path, - ) { - val block1 = DataGenerators.randomSealedBeaconBlock(1uL) - val block2 = DataGenerators.randomSealedBeaconBlock(2uL) - // Skip block 3 - val block4 = DataGenerators.randomSealedBeaconBlock(4uL) - - createDatabase(databasePath).use { db -> - db.newBeaconChainUpdater().use { - it - .putSealedBeaconBlock(block1) - .putSealedBeaconBlock(block2) - .putSealedBeaconBlock(block4) - .commit() - } - db.newBeaconChainUpdater().use { - it - .putBeaconState( - BeaconState( - beaconBlockHeader = block4.beaconBlock.beaconBlockHeader, - validators = DataGenerators.randomValidators(), - ), - ).commit() - } - - // Request 5 blocks starting from 1, should throw exception at gap - assertThatThrownBy { - db.getSealedBeaconBlocks(startBlockNumber = 1uL, count = 5uL) - }.isInstanceOf(IllegalStateException::class.java) - .hasMessage("Missing sealed beacon block 3") - } - } - - @Test - fun `test getSealedBeaconBlocks returns available blocks when count exceeds available`( - @TempDir databasePath: Path, - ) { - val testBlocks = (1uL..3uL).map { DataGenerators.randomSealedBeaconBlock(it) } - - createDatabase(databasePath).use { db -> - testBlocks.forEach { block -> - db.newBeaconChainUpdater().use { - it.putSealedBeaconBlock(block).commit() - } - } - db.newBeaconChainUpdater().use { - it - .putBeaconState( - BeaconState( - beaconBlockHeader = testBlocks.last().beaconBlock.beaconBlockHeader, - validators = DataGenerators.randomValidators(), - ), - ).commit() - } - - // Request 10 blocks but only 3 exist - val blocks = db.getSealedBeaconBlocks(startBlockNumber = 1uL, count = 10uL) - assertThat(blocks).hasSize(3) - assertThat(blocks).isEqualTo(testBlocks) - } - } - @Test fun `test read and increment discovery sequence number`( @TempDir databasePath: Path,