Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 70 additions & 29 deletions integration/tests/access/cohort1/access_api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,50 @@ type AccessAPISuite struct {
serviceClient *testnet.Client
}

func (s *AccessAPISuite) TestAccessAPIs() {
// Deploy the test contract once for both AN1 and AN2 tests
_ = s.deployContract(lib.CounterContract, false)
txResult := s.deployCounter()
targetHeight := txResult.BlockHeight + 1
s.waitUntilIndexed(targetHeight)

s.T().Run("Script execution and get accounts using execution nodes", func(t *testing.T) {
s.testScriptExecutionAndGetAccountsAN1(targetHeight)
})

s.T().Run("Script execution and get accounts using local data", func(t *testing.T) {
s.testScriptExecutionAndGetAccountsAN2(targetHeight)
})

s.T().Run("MVP script execution with local storage", func(t *testing.T) {
s.testMVPScriptExecutionLocalStorage()
})

s.T().Run("Send and subscribe transaction statuses", func(t *testing.T) {
s.testSendAndSubscribeTransactionStatuses()
})

s.T().Run("Contract update", func(t *testing.T) {
s.testContractUpdate()
})

s.T().Run("Transaction signature with plain extension data", func(t *testing.T) {
s.testTransactionSignaturePlainExtensionData()
})

s.T().Run("Transaction signature with WebAuthn extension data", func(t *testing.T) {
s.testTransactionSignatureWebAuthnExtensionData()
})

s.T().Run("Extension data preservation", func(t *testing.T) {
s.testExtensionDataPreservation()
})

s.T().Run("Rejected invalid signature format", func(t *testing.T) {
s.testRejectedInvalidSignatureFormat()
})
}

func (s *AccessAPISuite) TearDownTest() {
s.log.Info().Msg("================> Start TearDownTest")
s.net.Remove()
Expand Down Expand Up @@ -193,50 +237,38 @@ func (s *AccessAPISuite) SetupTest() {
}, 30*time.Second, 1*time.Second)
}

// TestScriptExecutionAndGetAccountsAN1 test the Access API endpoints for executing scripts and getting
// testScriptExecutionAndGetAccountsAN1 test the Access API endpoints for executing scripts and getting
// accounts using execution nodes.
//
// Note: not combining AN1, AN2 tests together because that causes a drastic increase in test run times. test cases are read-only
// and should not interfere with each other.
func (s *AccessAPISuite) TestScriptExecutionAndGetAccountsAN1() {
// deploy the test contract
_ = s.deployContract(lib.CounterContract, false)
txResult := s.deployCounter()
targetHeight := txResult.BlockHeight + 1
s.waitUntilIndexed(targetHeight)

func (s *AccessAPISuite) testScriptExecutionAndGetAccountsAN1(targetHeight uint64) {
// Run tests against Access 1, which uses the execution node
s.testGetAccount(s.an1Client)
s.testExecuteScriptWithSimpleScript(s.an1Client)
s.testExecuteScriptWithSimpleContract(s.an1Client, targetHeight)
}

// TestScriptExecutionAndGetAccountsAN2 test the Access API endpoints for executing scripts and getting
// testScriptExecutionAndGetAccountsAN2 test the Access API endpoints for executing scripts and getting
// accounts using local storage.
//
// Note: not combining AN1, AN2 tests together because that causes a drastic increase in test run times. test cases are read-only
// and should not interfere with each other.
func (s *AccessAPISuite) TestScriptExecutionAndGetAccountsAN2() {
// deploy the test contract
_ = s.deployContract(lib.CounterContract, false)
txResult := s.deployCounter()
targetHeight := txResult.BlockHeight + 1
s.waitUntilIndexed(targetHeight)

func (s *AccessAPISuite) testScriptExecutionAndGetAccountsAN2(targetHeight uint64) {
// Run tests against Access 2, which uses local storage
s.testGetAccount(s.an2Client)
s.testExecuteScriptWithSimpleScript(s.an2Client)
s.testExecuteScriptWithSimpleContract(s.an2Client, targetHeight)
}

func (s *AccessAPISuite) TestMVPScriptExecutionLocalStorage() {
func (s *AccessAPISuite) testMVPScriptExecutionLocalStorage() {
// this is a specialized test that creates accounts, deposits funds, deploys contracts, etc, and
// uses the provided access node to handle the Access API calls. there is an existing test that
// covers the default config, so we only need to test with local storage.
mvp.RunMVPTest(s.T(), s.ctx, s.net, s.accessNode2)
}

// TestSendAndSubscribeTransactionStatuses tests the functionality of sending and subscribing to transaction statuses.
// testSendAndSubscribeTransactionStatuses tests the functionality of sending and subscribing to transaction statuses.
//
// This test verifies that a transaction can be created, signed, sent to the access API, and then the status of the transaction
// can be subscribed to. It performs the following steps:
Expand All @@ -245,7 +277,7 @@ func (s *AccessAPISuite) TestMVPScriptExecutionLocalStorage() {
// 3. Signs the transaction.
// 4. Sends and subscribes to the transaction status using the access API.
// 5. Verifies the received transaction statuses, ensuring they are received in order and the final status is "SEALED".
func (s *AccessAPISuite) TestSendAndSubscribeTransactionStatuses() {
func (s *AccessAPISuite) testSendAndSubscribeTransactionStatuses() {
accessNodeContainer := s.net.ContainerByName(testnet.PrimaryAN)

// Establish a gRPC connection to the access API
Expand Down Expand Up @@ -350,11 +382,20 @@ func (s *AccessAPISuite) TestSendAndSubscribeTransactionStatuses() {

// Check, if the final transaction status is sealed.
s.Assert().Equal(entities.TransactionStatus_SEALED, lastReportedTxStatus)

// Refresh the suite's service client to sync sequence number.
// This test created a fresh local serviceClient and submitted a transaction,
// which incremented the on-chain sequence number. The suite's s.serviceClient
// still has the old cached sequence number, so we refresh it here.
s.Require().Eventually(func() bool {
s.serviceClient, err = s.accessNode2.TestnetClient()
return err == nil
}, 30*time.Second, 1*time.Second)
}

// TestContractUpdate tests that the Access API can index contract updates, and that the program cache
// testContractUpdate tests that the Access API can index contract updates, and that the program cache
// is invalidated when a contract is updated.
func (s *AccessAPISuite) TestContractUpdate() {
func (s *AccessAPISuite) testContractUpdate() {
txResult := s.deployContract(OriginalContract, false)
targetHeight := txResult.BlockHeight + 1
s.waitUntilIndexed(targetHeight)
Expand Down Expand Up @@ -610,9 +651,9 @@ func convertToMessageSigWithExtensionData(sigs []sdk.TransactionSignature, exten
return msgSigs
}

// TestTransactionSignaturePlainExtensionData tests that the Access API properly handles the ExtensionData field
// testTransactionSignaturePlainExtensionData tests that the Access API properly handles the ExtensionData field
// in transaction signatures for different authentication schemes.
func (s *AccessAPISuite) TestTransactionSignaturePlainExtensionData() {
func (s *AccessAPISuite) testTransactionSignaturePlainExtensionData() {
accessNodeContainer := s.net.ContainerByName(testnet.PrimaryAN)

// Establish a gRPC connection to the access API
Expand Down Expand Up @@ -757,8 +798,8 @@ func (s *AccessAPISuite) TestTransactionSignaturePlainExtensionData() {
}
}

// TestTransactionSignatureWebAuthnExtensionData tests the WebAuthn authentication scheme with properly constructed extension data.
func (s *AccessAPISuite) TestTransactionSignatureWebAuthnExtensionData() {
// testTransactionSignatureWebAuthnExtensionData tests the WebAuthn authentication scheme with properly constructed extension data.
func (s *AccessAPISuite) testTransactionSignatureWebAuthnExtensionData() {
accessNodeContainer := s.net.ContainerByName(testnet.PrimaryAN)

// Establish a gRPC connection to the access API
Expand Down Expand Up @@ -920,9 +961,9 @@ func (s *AccessAPISuite) TestTransactionSignatureWebAuthnExtensionData() {
}
}

// TestExtensionDataPreservation tests that the ExtensionData field is properly preserved
// testExtensionDataPreservation tests that the ExtensionData field is properly preserved
// when transactions are submitted and retrieved through the Access API.
func (s *AccessAPISuite) TestExtensionDataPreservation() {
func (s *AccessAPISuite) testExtensionDataPreservation() {
accessNodeContainer := s.net.ContainerByName(testnet.PrimaryAN)

// Establish a gRPC connection to the access API
Expand Down Expand Up @@ -1063,9 +1104,9 @@ func (s *AccessAPISuite) TestExtensionDataPreservation() {
}
}

// TestInvalidTransactionSignature tests that the access API performs sanity checks
// testRejectedInvalidSignatureFormat tests that the access API performs sanity checks
// on the transaction signature format and rejects invalid formats
func (s *AccessAPISuite) TestRejectedInvalidSignatureFormat() {
func (s *AccessAPISuite) testRejectedInvalidSignatureFormat() {
accessNodeContainer := s.net.ContainerByName(testnet.PrimaryAN)

// Establish a gRPC connection to the access API
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ type ObserverIndexerEnabledSuite struct {
ObserverSuite
}

func (s *ObserverIndexerEnabledSuite) TestObserverIndexerEnabled() {
s.T().Run("Observer indexed RPCs happy path", s.testObserverIndexedRPCsHappyPath)
s.T().Run("All observer indexed RPCs happy path", s.testAllObserverIndexedRPCsHappyPath)
}

// SetupTest sets up the test suite by starting the network and preparing the observers client.
// By overriding this function, we can ensure that the observers are started with correct parameters and select
// the RPCs and REST endpoints that are tested.
Expand Down Expand Up @@ -141,17 +146,15 @@ func (s *ObserverIndexerEnabledSuite) SetupTest() {
s.net.Start(ctx)
}

// TestObserverIndexedRPCsHappyPath tests RPCs that are handled by the observer by using a dedicated indexer for the events.
// testObserverIndexedRPCsHappyPath tests RPCs that are handled by the observer by using a dedicated indexer for the events.
// To ensure that the observer is handling these RPCs, we stop the upstream access node and verify that the observer client
// returns success for valid requests and errors for invalid ones.
func (s *ObserverIndexerEnabledSuite) TestObserverIndexedRPCsHappyPath() {
unittest.SkipUnless(s.T(), unittest.TEST_FLAKY, "flaky")
func (s *ObserverIndexerEnabledSuite) testObserverIndexedRPCsHappyPath(t *testing.T) {
unittest.SkipUnless(t, unittest.TEST_FLAKY, "flaky")

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

t := s.T()

// prepare environment to create a new account
serviceAccountClient, err := s.net.ContainerByName(testnet.PrimaryAN).TestnetClient()
require.NoError(t, err)
Expand Down Expand Up @@ -271,7 +274,7 @@ func (s *ObserverIndexerEnabledSuite) TestObserverIndexedRPCsHappyPath() {
require.True(t, found)
}

// TestAllObserverIndexedRPCsHappyPath tests the observer with the indexer enabled,
// testAllObserverIndexedRPCsHappyPath tests the observer with the indexer enabled,
// observer configured to proxy requests to an access node and access node itself. All responses are compared
// to ensure all of the endpoints are working as expected.
// For now the observer only supports the following RPCs:
Expand All @@ -288,12 +291,10 @@ func (s *ObserverIndexerEnabledSuite) TestObserverIndexedRPCsHappyPath() {
// -GetTransaction
// -GetTransactionResult
// -GetTransactionResultByIndex
func (s *ObserverIndexerEnabledSuite) TestAllObserverIndexedRPCsHappyPath() {
func (s *ObserverIndexerEnabledSuite) testAllObserverIndexedRPCsHappyPath(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

t := s.T()

// prepare environment to create a new account
serviceAccountClient, err := s.net.ContainerByName(testnet.PrimaryAN).TestnetClient()
require.NoError(t, err)
Expand Down
12 changes: 12 additions & 0 deletions integration/tests/access/cohort3/access_circuit_breaker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,18 @@ func (s *AccessCircuitBreakerSuite) TearDownTest() {
s.log.Info().Msg("================> Finish TearDownTest")
}

// SetupTest initializes the test suite with a minimal network configuration for circuit breaker testing.
//
// Network Configuration:
// - 1 Access node with circuit breaker enabled (max-requests=1, max-failures=1, restore-timeout=6s, request-timeout=1.5s)
// - 1 Execution node (standard configuration)
// - 1 Verification node (ghost - lightweight, unused)
// - 1 Collection node (controllable, with 100ms proposal duration)
// - 3 Consensus nodes (ghosts - lightweight, unused)
//
// The access node is configured with aggressive circuit breaker settings to allow testing the breaker's
// open/close behavior within a short test timeframe. The collection node can be disconnected to trigger
// circuit breaker activation.
func (s *AccessCircuitBreakerSuite) SetupTest() {
s.log = unittest.LoggerForTest(s.Suite.T(), zerolog.InfoLevel)
s.log.Info().Msg("================> SetupTest")
Expand Down
14 changes: 14 additions & 0 deletions integration/tests/access/cohort3/collection_indexing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,20 @@ type CollectionIndexingSuite struct {
cancel context.CancelFunc
}

// SetupTest initializes the test suite with a network configuration for testing collection indexing.
//
// Network Configuration:
// - 2 Access nodes with different collection indexing strategies:
// - access_1: Uses ingestion engine for collection indexing (execution-data-sync-enabled=true, NO indexer)
// - access_2: Uses execution data indexer for collection indexing (execution-data-indexing-enabled=true)
// - 2 Collection nodes (standard configuration)
// - 2 Execution nodes (standard configuration)
// - 3 Consensus nodes (standard configuration)
// - 1 Verification node (standard configuration)
//
// This setup allows testing that both collection indexing approaches (ingestion engine vs dedicated indexer)
// work correctly. The test verifies that access_2 can catch up on collections by syncing from the indexer
// even when collection nodes are stopped.
func (s *CollectionIndexingSuite) SetupTest() {
// access_1 is not running the indexer, so all collections are indexed using the ingestion engine
defaultAccessOpts := []func(config *testnet.NodeConfig){
Expand Down
14 changes: 14 additions & 0 deletions integration/tests/access/cohort3/consensus_follower_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,20 @@ func (s *ConsensusFollowerSuite) TearDownTest() {
s.log.Info().Msgf("================> Finish TearDownTest")
}

// SetupTest initializes the test suite with a network configuration for testing consensus followers.
//
// Network Configuration:
// - 1 Staked Access node (supports-observer=true) that consensus followers connect to
// - 1 Standard Access node (standard configuration)
// - 2 Collection nodes (ghosts - lightweight, unused)
// - 3 Consensus nodes (with custom timing: 100ms proposal duration, reduced seal approvals)
// - 2 Consensus Followers (unstaked nodes with generated networking keys)
// - 2 Execution nodes (standard configuration)
// - 1 Verification node (standard configuration)
//
// The staked access node acts as the entry point for consensus followers. The two consensus followers
// are configured to follow the chain through this staked node, allowing testing of chain following
// and catch-up behavior.
func (s *ConsensusFollowerSuite) SetupTest() {
s.log = unittest.LoggerForTest(s.Suite.T(), zerolog.InfoLevel)
s.log.Info().Msg("================> SetupTest")
Expand Down
17 changes: 15 additions & 2 deletions integration/tests/access/cohort3/execution_state_sync_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,19 @@ type ExecutionStateSyncSuite struct {
executionDataDBMode execution_data.ExecutionDataDBMode
}

// SetupTest initializes the test suite with a network configuration for testing execution state sync.
//
// Network Configuration:
// - 1 Bridge Access node (supports-observer=true, execution-data-sync-enabled, public-network-sync-enabled, Pebble DB)
// - 1 Ghost Access node (lightweight, for tracking block state)
// - 1 Observer node (execution-data-sync-enabled, execution-data-indexing-enabled, event-query-mode=execution-nodes-only)
// - 2 Collection nodes (standard configuration)
// - 2 Execution nodes (standard configuration)
// - 3 Consensus nodes (with custom timing: 100ms proposal duration, reduced seal approvals)
// - 1 Verification node (standard configuration)
//
// The bridge access node and observer both sync execution data from execution nodes and verify that
// execution state is properly synced and can be retrieved. Uses Pebble DB as the execution data store.
func (s *ExecutionStateSyncSuite) SetupTest() {
s.setup(execution_data.ExecutionDataDBModePebble)
}
Expand Down Expand Up @@ -143,9 +156,9 @@ func (s *ExecutionStateSyncSuite) buildNetworkConfig() {
s.net = testnet.PrepareFlowNetwork(s.T(), conf, flow.Localnet)
}

// TestBadgerDBHappyPath tests that Execution Nodes generate execution data, and Access Nodes are able to
// TestPebbleDBHappyPath tests that Execution Nodes generate execution data, and Access Nodes are able to
// successfully sync the data to badger DB
func (s *ExecutionStateSyncSuite) TestBadgerDBHappyPath() {
func (s *ExecutionStateSyncSuite) TestPebbleDBHappyPath() {
s.executionStateSyncTest()
}

Expand Down

This file was deleted.

Loading
Loading