diff --git a/common/src/testFixtures/kotlin/org/onflow/flow/common/test/FlowTestUtil.kt b/common/src/testFixtures/kotlin/org/onflow/flow/common/test/FlowTestUtil.kt index a0daab1..1f91057 100644 --- a/common/src/testFixtures/kotlin/org/onflow/flow/common/test/FlowTestUtil.kt +++ b/common/src/testFixtures/kotlin/org/onflow/flow/common/test/FlowTestUtil.kt @@ -161,7 +161,6 @@ object FlowTestUtil { val fileLocation = flowJsonLocation?.let(::File) if (fileLocation != null && fileLocation.exists() && fileLocation.isFile) { flowJson = fileLocation.absolutePath - println("Flow JSON found as file: $flowJson") } // Check if flowJsonLocation is a classpath resource diff --git a/java-example/src/main/java/org/onflow/examples/java/AccessAPIConnector.java b/java-example/src/main/java/org/onflow/examples/java/AccessAPIConnector.java index 59c4f4e..16ee671 100644 --- a/java-example/src/main/java/org/onflow/examples/java/AccessAPIConnector.java +++ b/java-example/src/main/java/org/onflow/examples/java/AccessAPIConnector.java @@ -82,13 +82,14 @@ private FlowTransactionResult waitForSeal(FlowId txID) { private FlowAddress getAccountCreatedAddress(FlowTransactionResult txResult) { FlowEvent event = txResult.getEvents().stream() - .filter(it -> "flow.AccountCreated".equals(it.getType())) + .filter(it -> "flow.AccountCreated".equals(it.getType())) .findFirst() .orElseThrow(() -> new RuntimeException("account created event not found")); EventField field = (EventField) event.getPayload().getJsonCadence(); String address = (String) field.getValue().getRequiredField("address").getValue(); + assert address != null; return new FlowAddress(address); } @@ -103,18 +104,10 @@ private byte[] loadScript(String name) { } } - public FlowAddress createAccount(FlowAddress payerAddress, PublicKey publicKey) { - - FlowAccountKey payerAccountKey = getAccountKey(payerAddress, 0); - - FlowScript script = new FlowScript(loadScript("cadence/create_account.cdc")); - - FlowTransaction tx = new FlowTransaction( + private FlowTransaction createTransaction(FlowAddress payerAddress, FlowAccountKey payerAccountKey, FlowScript script, List arguments) { + return new FlowTransaction( script, - List.of( - new FlowArgument(new StringField(publicKey.getHex())), - new FlowArgument(new UInt8NumberField(Integer.toString(publicKey.getAlgo().getIndex()))) - ), + arguments, getLatestBlockID(), 500L, new FlowTransactionProposalKey( @@ -127,50 +120,52 @@ public FlowAddress createAccount(FlowAddress payerAddress, PublicKey publicKey) Collections.emptyList(), Collections.emptyList() ); + } + private FlowId signAndSendTransaction(FlowTransaction tx, FlowAddress payerAddress, FlowAccountKey payerAccountKey) { Signer signer = Crypto.getSigner(privateKey, payerAccountKey.getHashAlgo()); tx = tx.addEnvelopeSignature(payerAddress, payerAccountKey.getId(), signer); + return sendTransaction(tx); + } - FlowId txID = sendTransaction(tx); - + public FlowAddress createAccount(FlowAddress payerAddress, PublicKey publicKey) { + FlowAccountKey payerAccountKey = getAccountKey(payerAddress, 0); + FlowScript script = new FlowScript(loadScript("cadence/create_account.cdc")); + List arguments = List.of( + new FlowArgument(new StringField(publicKey.getHex())), + new FlowArgument(new UInt8NumberField(Integer.toString(publicKey.getAlgo().getIndex()))) + ); + FlowTransaction tx = createTransaction(payerAddress, payerAccountKey, script, arguments); + FlowId txID = signAndSendTransaction(tx, payerAddress, payerAccountKey); FlowTransactionResult txResult = waitForSeal(txID); return getAccountCreatedAddress(txResult); } + public FlowId sendSampleTransaction(FlowAddress payerAddress, PublicKey publicKey) { + FlowAccountKey payerAccountKey = getAccountKey(payerAddress, 0); + FlowScript script = new FlowScript(loadScript("cadence/create_account.cdc")); + List arguments = List.of( + new FlowArgument(new StringField(publicKey.getHex())), + new FlowArgument(new UInt8NumberField(Integer.toString(publicKey.getAlgo().getIndex()))) + ); + FlowTransaction tx = createTransaction(payerAddress, payerAccountKey, script, arguments); + return signAndSendTransaction(tx, payerAddress, payerAccountKey); + } + public void transferTokens(FlowAddress senderAddress, FlowAddress recipientAddress, BigDecimal amount) { if (amount.scale() != 8) { throw new RuntimeException("FLOW amount must have exactly 8 decimal places of precision (e.g. 10.00000000)"); } FlowAccountKey senderAccountKey = getAccountKey(senderAddress, 0); - FlowScript script = new FlowScript(loadScript("cadence/transfer_flow.cdc")); - List arguments = List.of( new FlowArgument(new UFix64NumberField(amount.toPlainString())), new FlowArgument(new AddressField(recipientAddress.getBase16Value())) ); - FlowTransaction tx = new FlowTransaction( - script, - arguments, - getLatestBlockID(), - 500L, - new FlowTransactionProposalKey( - senderAddress, - senderAccountKey.getId(), - senderAccountKey.getSequenceNumber() - ), - senderAddress, - Collections.singletonList(senderAddress), - Collections.emptyList(), - Collections.emptyList() - ); - - Signer signer = Crypto.getSigner(privateKey, senderAccountKey.getHashAlgo()); - tx = tx.addEnvelopeSignature(senderAddress, senderAccountKey.getId(), signer); - - FlowId txID = sendTransaction(tx); + FlowTransaction tx = createTransaction(senderAddress, senderAccountKey, script, arguments); + FlowId txID = signAndSendTransaction(tx, senderAddress, senderAccountKey); waitForSeal(txID); } diff --git a/java-example/src/main/java/org/onflow/examples/java/getAccount/GetAccountAccessAPIConnector.java b/java-example/src/main/java/org/onflow/examples/java/getAccount/GetAccountAccessAPIConnector.java new file mode 100644 index 0000000..ea07e0f --- /dev/null +++ b/java-example/src/main/java/org/onflow/examples/java/getAccount/GetAccountAccessAPIConnector.java @@ -0,0 +1,38 @@ +package org.onflow.examples.java.getAccount; + +import org.onflow.flow.sdk.*; +import java.math.BigDecimal; + +public class GetAccountAccessAPIConnector { + + private final FlowAccessApi accessAPI; + + public GetAccountAccessAPIConnector(FlowAccessApi accessAPI) { + this.accessAPI = accessAPI; + } + + public FlowAccount getAccountAtLatestBlock(FlowAddress address) { + FlowAccessApi.AccessApiCallResponse response = accessAPI.getAccountAtLatestBlock(address); + if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) { + return ((FlowAccessApi.AccessApiCallResponse.Success) response).getData(); + } else { + FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response; + throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable()); + } + } + + public FlowAccount getAccountAtBlockHeight(FlowAddress address, long height) { + FlowAccessApi.AccessApiCallResponse response = accessAPI.getAccountByBlockHeight(address, height); + if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) { + return ((FlowAccessApi.AccessApiCallResponse.Success) response).getData(); + } else { + FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response; + throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable()); + } + } + + public BigDecimal getAccountBalance(FlowAddress address) { + FlowAccount account = getAccountAtLatestBlock(address); + return account.getBalance(); + } +} diff --git a/java-example/src/main/java/org/onflow/examples/java/getBlock/GetBlockAccessAPIConnector.java b/java-example/src/main/java/org/onflow/examples/java/getBlock/GetBlockAccessAPIConnector.java new file mode 100644 index 0000000..bd10fb6 --- /dev/null +++ b/java-example/src/main/java/org/onflow/examples/java/getBlock/GetBlockAccessAPIConnector.java @@ -0,0 +1,46 @@ +package org.onflow.examples.java.getBlock; + +import org.onflow.flow.sdk.*; + +public class GetBlockAccessAPIConnector { + + private final FlowAccessApi accessAPI; + + public GetBlockAccessAPIConnector(FlowAccessApi accessAPI) { + this.accessAPI = accessAPI; + } + + public FlowBlock getLatestSealedBlock() { + boolean isSealed = true; + FlowAccessApi.AccessApiCallResponse response = accessAPI.getLatestBlock(isSealed); + + if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) { + return ((FlowAccessApi.AccessApiCallResponse.Success) response).getData(); + } else { + FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response; + throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable()); + } + } + + public FlowBlock getBlockByID(FlowId blockID) { + FlowAccessApi.AccessApiCallResponse response = accessAPI.getBlockById(blockID); + + if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) { + return ((FlowAccessApi.AccessApiCallResponse.Success) response).getData(); + } else { + FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response; + throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable()); + } + } + + public FlowBlock getBlockByHeight(long height) { + FlowAccessApi.AccessApiCallResponse response = accessAPI.getBlockByHeight(height); + + if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) { + return ((FlowAccessApi.AccessApiCallResponse.Success) response).getData(); + } else { + FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response; + throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable()); + } + } +} diff --git a/java-example/src/main/java/org/onflow/examples/java/getEvent/GetEventAccessAPIConnector.java b/java-example/src/main/java/org/onflow/examples/java/getEvent/GetEventAccessAPIConnector.java new file mode 100644 index 0000000..c62c9fc --- /dev/null +++ b/java-example/src/main/java/org/onflow/examples/java/getEvent/GetEventAccessAPIConnector.java @@ -0,0 +1,55 @@ +package org.onflow.examples.java.getEvent; + +import kotlin.ranges.LongRange; +import org.onflow.flow.sdk.*; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class GetEventAccessAPIConnector { + private final FlowAccessApi accessAPI; + + public GetEventAccessAPIConnector(FlowAccessApi accessAPI) { + this.accessAPI = accessAPI; + } + + public List getEventsForHeightRange(String eventType, long startHeight, long endHeight) { + LongRange range = new LongRange(startHeight, endHeight); + FlowAccessApi.AccessApiCallResponse> response = accessAPI.getEventsForHeightRange(eventType, range); + if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) { + return ((FlowAccessApi.AccessApiCallResponse.Success>) response).getData(); + } else if (response instanceof FlowAccessApi.AccessApiCallResponse.Error) { + FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response; + throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable()); + } else { + throw new RuntimeException("Unknown response type"); + } + } + + public List getEventsForBlockIds(String eventType, List blockIds) { + Set blockIdSet = new HashSet<>(blockIds); + FlowAccessApi.AccessApiCallResponse> response = accessAPI.getEventsForBlockIds(eventType, blockIdSet); + if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) { + return ((FlowAccessApi.AccessApiCallResponse.Success>) response).getData(); + } else if (response instanceof FlowAccessApi.AccessApiCallResponse.Error errorResponse) { + throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable()); + } else { + throw new RuntimeException("Unknown response type"); + } + } + + public FlowTransactionResult getTransactionResult(FlowId txID) { + FlowAccessApi.AccessApiCallResponse response = accessAPI.getTransactionResultById(txID); + if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) { + return ((FlowAccessApi.AccessApiCallResponse.Success) response).getData(); + } else if (response instanceof FlowAccessApi.AccessApiCallResponse.Error errorResponse) { + throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable()); + } else { + throw new RuntimeException("Unknown response type"); + } + } + + public List getAccountCreatedEvents(long startHeight, long endHeight) { + return getEventsForHeightRange("flow.AccountCreated", startHeight, endHeight); + } +} \ No newline at end of file diff --git a/java-example/src/main/java/org/onflow/examples/java/getNetworkParams/GetNetworkParametersAccessAPIConnector.java b/java-example/src/main/java/org/onflow/examples/java/getNetworkParams/GetNetworkParametersAccessAPIConnector.java new file mode 100644 index 0000000..9611ac2 --- /dev/null +++ b/java-example/src/main/java/org/onflow/examples/java/getNetworkParams/GetNetworkParametersAccessAPIConnector.java @@ -0,0 +1,24 @@ +package org.onflow.examples.java.getNetworkParams; + +import org.onflow.flow.sdk.FlowAccessApi; +import org.onflow.flow.sdk.FlowChainId; + +public class GetNetworkParametersAccessAPIConnector { + + private final FlowAccessApi accessAPI; + + public GetNetworkParametersAccessAPIConnector(FlowAccessApi accessAPI) { + this.accessAPI = accessAPI; + } + + public FlowChainId getNetworkParameters() { + FlowAccessApi.AccessApiCallResponse response = accessAPI.getNetworkParameters(); + if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) { + return ((FlowAccessApi.AccessApiCallResponse.Success) response).getData(); + } else { + FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response; + throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable()); + } + } +} + diff --git a/java-example/src/main/java/org/onflow/examples/java/getTransaction/GetTransactionAccessAPIConnector.java b/java-example/src/main/java/org/onflow/examples/java/getTransaction/GetTransactionAccessAPIConnector.java new file mode 100644 index 0000000..048a091 --- /dev/null +++ b/java-example/src/main/java/org/onflow/examples/java/getTransaction/GetTransactionAccessAPIConnector.java @@ -0,0 +1,35 @@ +package org.onflow.examples.java.getTransaction; + +import org.onflow.flow.sdk.FlowAccessApi; +import org.onflow.flow.sdk.FlowId; +import org.onflow.flow.sdk.FlowTransaction; +import org.onflow.flow.sdk.FlowTransactionResult; + +public class GetTransactionAccessAPIConnector { + + private final FlowAccessApi accessAPI; + + public GetTransactionAccessAPIConnector(FlowAccessApi accessAPI) { + this.accessAPI = accessAPI; + } + + public FlowTransaction getTransaction(FlowId txID) { + FlowAccessApi.AccessApiCallResponse response = accessAPI.getTransactionById(txID); + if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) { + return ((FlowAccessApi.AccessApiCallResponse.Success) response).getData(); + } else { + FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response; + throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable()); + } + } + + public FlowTransactionResult getTransactionResult(FlowId txID) { + FlowAccessApi.AccessApiCallResponse response = accessAPI.getTransactionResultById(txID); + if (response instanceof FlowAccessApi.AccessApiCallResponse.Success) { + return ((FlowAccessApi.AccessApiCallResponse.Success) response).getData(); + } else { + FlowAccessApi.AccessApiCallResponse.Error errorResponse = (FlowAccessApi.AccessApiCallResponse.Error) response; + throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable()); + } + } +} diff --git a/java-example/src/test/java/org/onflow/examples/java/getAccount/GetAccountAccessAPIConnectorTest.java b/java-example/src/test/java/org/onflow/examples/java/getAccount/GetAccountAccessAPIConnectorTest.java new file mode 100644 index 0000000..1c50835 --- /dev/null +++ b/java-example/src/test/java/org/onflow/examples/java/getAccount/GetAccountAccessAPIConnectorTest.java @@ -0,0 +1,59 @@ +package org.onflow.examples.java.getAccount; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.onflow.flow.common.test.FlowEmulatorProjectTest; +import org.onflow.flow.common.test.FlowServiceAccountCredentials; +import org.onflow.flow.common.test.FlowTestClient; +import org.onflow.flow.common.test.TestAccount; +import org.onflow.flow.sdk.FlowAccessApi; +import org.onflow.flow.sdk.FlowAccount; +import org.onflow.flow.sdk.FlowAddress; + +import java.math.BigDecimal; + +@FlowEmulatorProjectTest(flowJsonLocation = "../flow/flow.json") +public class GetAccountAccessAPIConnectorTest { + @FlowServiceAccountCredentials + private TestAccount testAccount; + + @FlowTestClient + private FlowAccessApi accessAPI; + + private GetAccountAccessAPIConnector accountConnector; + + @BeforeEach + public void setup() { + accountConnector = new GetAccountAccessAPIConnector(accessAPI); + } + + @Test + public void testCanFetchAccountFromLatestBlock() { + FlowAddress address = testAccount.getFlowAddress(); + FlowAccount account = accountConnector.getAccountAtLatestBlock(address); + + Assertions.assertNotNull(account, "Account should not be null"); + Assertions.assertEquals(address.getBase16Value(), account.getAddress().getBase16Value(), "Address should match"); + Assertions.assertTrue(account.getBalance().compareTo(BigDecimal.ZERO) >= 0, "Account balance should be non-negative"); + } + + @Test + public void testCanFetchAccountFromBlockByHeight0() { + FlowAddress address = testAccount.getFlowAddress(); + FlowAccount account = accountConnector.getAccountAtBlockHeight(address, 0); + + Assertions.assertNotNull(account, "Account should not be null"); + Assertions.assertEquals(address.getBase16Value(), account.getAddress().getBase16Value(), "Address should match"); + Assertions.assertTrue(account.getBalance().compareTo(BigDecimal.ZERO) >= 0, "Account balance should be non-negative"); + } + + @Test + public void testCanFetchAccountBalance() { + FlowAddress address = testAccount.getFlowAddress(); + BigDecimal balance = accountConnector.getAccountBalance(address); + + Assertions.assertNotNull(balance, "Balance should not be null"); + Assertions.assertTrue(balance.compareTo(BigDecimal.ZERO) >= 0, "Balance should be non-negative"); + } +} diff --git a/java-example/src/test/java/org/onflow/examples/java/getBlock/GetBlockAccessAPIConnectorTest.java b/java-example/src/test/java/org/onflow/examples/java/getBlock/GetBlockAccessAPIConnectorTest.java new file mode 100644 index 0000000..7f8423a --- /dev/null +++ b/java-example/src/test/java/org/onflow/examples/java/getBlock/GetBlockAccessAPIConnectorTest.java @@ -0,0 +1,55 @@ +package org.onflow.examples.java.getBlock; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.onflow.flow.common.test.FlowEmulatorProjectTest; +import org.onflow.flow.common.test.FlowTestClient; +import org.onflow.flow.sdk.FlowAccessApi; +import org.onflow.flow.sdk.FlowBlock; + +import java.time.LocalDateTime; + +@FlowEmulatorProjectTest(flowJsonLocation = "../flow/flow.json") +public class GetBlockAccessAPIConnectorTest { + + @FlowTestClient + private FlowAccessApi accessAPI; + + private GetBlockAccessAPIConnector blockAPIConnector; + + @BeforeEach + public void setup() { + blockAPIConnector = new GetBlockAccessAPIConnector(accessAPI); + } + + @Test + public void testCanFetchLatestSealedBlock() { + FlowBlock block = blockAPIConnector.getLatestSealedBlock(); + + Assertions.assertNotNull(block.getId(), "Block ID should not be null"); + Assertions.assertTrue(block.getHeight() >= 0, "Block height should be non-negative"); + Assertions.assertTrue(block.getTimestamp().isBefore(LocalDateTime.now()), "Block timestamp should be in the past"); + } + + @Test + public void testCanFetchBlockByID() { + FlowBlock latestBlock = blockAPIConnector.getLatestSealedBlock(); + FlowBlock blockByID = blockAPIConnector.getBlockByID(latestBlock.getId()); + + Assertions.assertEquals(latestBlock.getId(), blockByID.getId(), "Block IDs should match"); + Assertions.assertEquals(latestBlock.getHeight(), blockByID.getHeight(), "Block heights should match"); + Assertions.assertEquals(latestBlock.getTimestamp(), blockByID.getTimestamp(), "Block timestamps should match"); + Assertions.assertEquals(latestBlock.getCollectionGuarantees().size(), blockByID.getCollectionGuarantees().size(), "Block should have the same number of collection guarantees"); + } + + @Test + public void testCanFetchBlockByHeight() { + FlowBlock latestBlock = blockAPIConnector.getLatestSealedBlock(); + FlowBlock blockByHeight = blockAPIConnector.getBlockByHeight(latestBlock.getHeight()); + + Assertions.assertEquals(latestBlock.getId(), blockByHeight.getId(), "Block ID fetched by height should match the latest block ID"); + Assertions.assertEquals(latestBlock.getTimestamp(), blockByHeight.getTimestamp(), "Block timestamps should match for the latest block and the block fetched by height"); + Assertions.assertEquals(latestBlock.getCollectionGuarantees().size(), blockByHeight.getCollectionGuarantees().size(), "Block should have the same number of collection guarantees"); + } +} diff --git a/java-example/src/test/java/org/onflow/examples/java/getEvent/GetEventAccessAPIConnectorTest.java b/java-example/src/test/java/org/onflow/examples/java/getEvent/GetEventAccessAPIConnectorTest.java new file mode 100644 index 0000000..a7a935a --- /dev/null +++ b/java-example/src/test/java/org/onflow/examples/java/getEvent/GetEventAccessAPIConnectorTest.java @@ -0,0 +1,101 @@ +package org.onflow.examples.java.getEvent; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.onflow.examples.java.AccessAPIConnector; +import org.onflow.flow.common.test.*; +import org.onflow.flow.sdk.*; +import org.onflow.flow.sdk.crypto.Crypto; +import org.onflow.flow.sdk.crypto.PublicKey; + +import java.util.List; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.*; + +@FlowEmulatorProjectTest(flowJsonLocation = "../flow/flow.json") +public class GetEventAccessAPIConnectorTest { + @FlowServiceAccountCredentials + private TestAccount serviceAccount; + + @FlowTestAccount + private TestAccount testAccount; + + @FlowTestClient + private FlowAccessApi accessAPI; + + private GetEventAccessAPIConnector connector; + + private FlowId txID; + + @BeforeEach + void setup() { + AccessAPIConnector accessAPIConnector = new AccessAPIConnector(serviceAccount.getPrivateKey(), accessAPI); + connector = new GetEventAccessAPIConnector(accessAPI); + + // Send a sample transaction to create an account and capture the transaction ID + PublicKey publicKey = Crypto.generateKeyPair(SignatureAlgorithm.ECDSA_P256).getPublic(); + txID = accessAPIConnector.sendSampleTransaction( + serviceAccount.getFlowAddress(), + publicKey + ); + } + + @Test + void testGetEventsForHeightRange() { + List events = connector.getEventsForHeightRange("flow.AccountCreated", 0, 30); + assertNotNull(events, "Events should not be null"); + assertFalse(events.isEmpty(), "Expected account created events but found none."); + assertEquals(3, events.size(), "Expected 3 account created events."); + } + + @Test + void testGetEventsForBlockIds() { + FlowBlockHeader latestBlock; + + // Get the latest block header from the access API + FlowAccessApi.AccessApiCallResponse latestBlockResponse = accessAPI.getLatestBlockHeader(true); + + // Handle the response + if (latestBlockResponse instanceof FlowAccessApi.AccessApiCallResponse.Success) { + latestBlock = ((FlowAccessApi.AccessApiCallResponse.Success) latestBlockResponse).getData(); + } else if (latestBlockResponse instanceof FlowAccessApi.AccessApiCallResponse.Error errorResponse) { + throw new RuntimeException(errorResponse.getMessage(), errorResponse.getThrowable()); + } else { + throw new RuntimeException("Unknown response type"); + } + + List blockIds = List.of(latestBlock.getId()); + List events = connector.getEventsForBlockIds("flow.AccountCreated", blockIds); + + assertNotNull(events, "Events should not be null"); + assertFalse(events.isEmpty(), "Expected events for the provided block IDs but found none."); + assertEquals(1, events.size(), "Expected 1 account created event."); + } + + @Test + void testAccountCreatedEvents() { + List events = connector.getAccountCreatedEvents(0, 30); + assertNotNull(events, "Events should not be null"); + assertFalse(events.isEmpty(), "Expected account created events but found none."); + for (FlowEventResult block : events) { + for (FlowEvent event : block.getEvents()) { + assertEquals("flow.AccountCreated", event.getType()); + } + } + } + + @Test + void testTransactionResultEvents() { + FlowTransactionResult txResult = connector.getTransactionResult(txID); + assertNotNull(txResult, "Transaction result should not be null"); + assertFalse(txResult.getEvents().isEmpty(), "Expected events in transaction result but found none."); + + Set expectedEventTypes = Set.of("Withdrawn", "TokensDeposited", "Deposited", "AccountCreated", "AccountKeyAdded", "TokensWithdrawn", "StorageCapabilityControllerIssued", "CapabilityPublished"); + + for (FlowEvent event : txResult.getEvents()) { + String eventType = event.getType().split("\\.")[event.getType().split("\\.").length - 1]; + assertTrue(expectedEventTypes.contains(eventType), "Unexpected event type: " + eventType); + } + } +} diff --git a/java-example/src/test/java/org/onflow/examples/java/getNetworkParams/GetNetworkParametersAccessAPIConnectorTest.java b/java-example/src/test/java/org/onflow/examples/java/getNetworkParams/GetNetworkParametersAccessAPIConnectorTest.java new file mode 100644 index 0000000..0478b8d --- /dev/null +++ b/java-example/src/test/java/org/onflow/examples/java/getNetworkParams/GetNetworkParametersAccessAPIConnectorTest.java @@ -0,0 +1,33 @@ +package org.onflow.examples.java.getNetworkParams; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.onflow.flow.common.test.FlowEmulatorProjectTest; +import org.onflow.flow.common.test.FlowTestClient; +import org.onflow.flow.sdk.FlowAccessApi; +import org.onflow.flow.sdk.FlowChainId; + +import static org.junit.jupiter.api.Assertions.*; + +@FlowEmulatorProjectTest(flowJsonLocation = "../flow/flow.json") +public class GetNetworkParametersAccessAPIConnectorTest { + + @FlowTestClient + private FlowAccessApi accessAPI; + + private GetNetworkParametersAccessAPIConnector networkParametersConnector; + + @BeforeEach + public void setup() { + networkParametersConnector = new GetNetworkParametersAccessAPIConnector(accessAPI); + } + + @Test + public void canFetchNetworkParameters() { + FlowChainId networkParams = networkParametersConnector.getNetworkParameters(); + assertNotNull(networkParams, "Network parameters should not be null"); + assertFalse(networkParams.getId().isEmpty(), "Network parameters should have a valid ID"); + assertEquals(FlowChainId.EMULATOR, networkParams, "Network parameters should match EMULATOR"); + assertEquals("flow-emulator", networkParams.getId(), "Network ID should be 'flow-emulator'"); + } +} diff --git a/java-example/src/test/java/org/onflow/examples/java/getTransaction/GetTransactionAccessAPIConnectorTest.java b/java-example/src/test/java/org/onflow/examples/java/getTransaction/GetTransactionAccessAPIConnectorTest.java new file mode 100644 index 0000000..8d6e2de --- /dev/null +++ b/java-example/src/test/java/org/onflow/examples/java/getTransaction/GetTransactionAccessAPIConnectorTest.java @@ -0,0 +1,62 @@ +package org.onflow.examples.java.getTransaction; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.onflow.examples.java.AccessAPIConnector; +import org.onflow.flow.common.test.FlowEmulatorProjectTest; +import org.onflow.flow.common.test.FlowServiceAccountCredentials; +import org.onflow.flow.common.test.FlowTestClient; +import org.onflow.flow.common.test.TestAccount; +import org.onflow.flow.sdk.FlowAccessApi; +import org.onflow.flow.sdk.FlowId; +import org.onflow.flow.sdk.FlowTransaction; +import org.onflow.flow.sdk.FlowTransactionResult; +import org.onflow.flow.sdk.FlowTransactionStatus; +import org.onflow.flow.sdk.crypto.Crypto; +import org.onflow.flow.sdk.crypto.PublicKey; +import org.onflow.flow.sdk.SignatureAlgorithm; + +import static org.junit.jupiter.api.Assertions.*; + +@FlowEmulatorProjectTest(flowJsonLocation = "../flow/flow.json") +public class GetTransactionAccessAPIConnectorTest { + + @FlowServiceAccountCredentials + private TestAccount serviceAccount; + + @FlowTestClient + private FlowAccessApi accessAPI; + + private GetTransactionAccessAPIConnector connector; + + private FlowId txID; + + @BeforeEach + public void setup() { + AccessAPIConnector accessAPIConnector = new AccessAPIConnector(serviceAccount.getPrivateKey(), accessAPI); + connector = new GetTransactionAccessAPIConnector(accessAPI); + + // Send a sample transaction to create an account and capture the transaction ID + PublicKey publicKey = Crypto.generateKeyPair(SignatureAlgorithm.ECDSA_P256).getPublic(); + txID = accessAPIConnector.sendSampleTransaction( + serviceAccount.getFlowAddress(), + publicKey + ); + } + + @Test + public void canFetchTransaction() { + FlowTransaction transaction = connector.getTransaction(txID); + + assertNotNull(transaction, "Transaction should not be null"); + assertEquals(txID, transaction.getId(), "Transaction ID should match"); + } + + @Test + public void canFetchTransactionResult() { + FlowTransactionResult transactionResult = connector.getTransactionResult(txID); + + assertNotNull(transactionResult, "Transaction result should not be null"); + assertSame(transactionResult.getStatus(), FlowTransactionStatus.SEALED, "Transaction should be sealed"); + } +} diff --git a/kotlin-example/src/main/kotlin/org/onflow/examples/kotlin/AccessAPIConnector.kt b/kotlin-example/src/main/kotlin/org/onflow/examples/kotlin/AccessAPIConnector.kt index c3a2e30..522791a 100644 --- a/kotlin-example/src/main/kotlin/org/onflow/examples/kotlin/AccessAPIConnector.kt +++ b/kotlin-example/src/main/kotlin/org/onflow/examples/kotlin/AccessAPIConnector.kt @@ -101,12 +101,48 @@ internal class AccessAPIConnector( return getAccountCreatedAddress(txResult) } + fun sendSampleTransaction( + payerAddress: FlowAddress, + publicKey: PublicKey, + scriptName: String = "cadence/create_account.cdc", + gasLimit: Long = 500 + ): FlowId { + val payerAccountKey = getAccountKey(payerAddress, 0) + + var tx = FlowTransaction( + script = FlowScript(loadScript(scriptName)), + arguments = listOf( + FlowArgument(StringField(publicKey.hex)), + FlowArgument(UInt8NumberField(publicKey.algo.index.toString())) + ), + referenceBlockId = latestBlockID, + gasLimit = gasLimit, + proposalKey = FlowTransactionProposalKey( + address = payerAddress, + keyIndex = payerAccountKey.id, + sequenceNumber = payerAccountKey.sequenceNumber.toLong() + ), + payerAddress = payerAddress, + authorizers = listOf(payerAddress) + ) + + val signer = Crypto.getSigner(privateKey, payerAccountKey.hashAlgo) + tx = tx.addEnvelopeSignature(payerAddress, payerAccountKey.id, signer) + + val txID = when (val response = accessAPI.sendTransaction(tx)) { + is FlowAccessApi.AccessApiCallResponse.Success -> response.data + is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable) + } + + return txID + } + fun transferTokens(senderAddress: FlowAddress, recipientAddress: FlowAddress, amount: BigDecimal) { if (amount.scale() != 8) { throw Exception("FLOW amount must have exactly 8 decimal places of precision (e.g. 10.00000000)") } val senderAccountKey = getAccountKey(senderAddress, 0) - val pkHex = senderAccountKey.publicKey.bytes.bytesToHex() + senderAccountKey.publicKey.bytes.bytesToHex() var tx = FlowTransaction( script = FlowScript(loadScript("cadence/transfer_flow.cdc")), diff --git a/kotlin-example/src/main/kotlin/org/onflow/examples/kotlin/getAccount/GetAccountAccessAPIConnector.kt b/kotlin-example/src/main/kotlin/org/onflow/examples/kotlin/getAccount/GetAccountAccessAPIConnector.kt new file mode 100644 index 0000000..6b769c9 --- /dev/null +++ b/kotlin-example/src/main/kotlin/org/onflow/examples/kotlin/getAccount/GetAccountAccessAPIConnector.kt @@ -0,0 +1,27 @@ +package org.onflow.examples.kotlin.getAccount + +import org.onflow.flow.sdk.* +import java.math.BigDecimal + +internal class GetAccountAccessAPIConnector( + private val accessAPI: FlowAccessApi +) { + fun getAccountAtLatestBlock(address: FlowAddress): FlowAccount { + return when (val response = accessAPI.getAccountAtLatestBlock(address)) { + is FlowAccessApi.AccessApiCallResponse.Success -> response.data + is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable) + } + } + + fun getAccountAtBlockHeight(address: FlowAddress, height: Long): FlowAccount { + return when (val response = accessAPI.getAccountByBlockHeight(address, height)) { + is FlowAccessApi.AccessApiCallResponse.Success -> response.data + is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable) + } + } + + fun getAccountBalance(address: FlowAddress): BigDecimal { + val account = getAccountAtLatestBlock(address) + return account.balance + } +} diff --git a/kotlin-example/src/main/kotlin/org/onflow/examples/kotlin/getBlock/GetBlockAccessAPIConnector.kt b/kotlin-example/src/main/kotlin/org/onflow/examples/kotlin/getBlock/GetBlockAccessAPIConnector.kt new file mode 100644 index 0000000..3368812 --- /dev/null +++ b/kotlin-example/src/main/kotlin/org/onflow/examples/kotlin/getBlock/GetBlockAccessAPIConnector.kt @@ -0,0 +1,29 @@ +package org.onflow.examples.kotlin.getBlock + +import org.onflow.flow.sdk.* + +internal class GetBlockAccessAPIConnector( + private val accessAPI: FlowAccessApi +) { + fun getLatestSealedBlock(): FlowBlock { + val isSealed = true + return when (val response = accessAPI.getLatestBlock(isSealed)) { + is FlowAccessApi.AccessApiCallResponse.Success -> response.data + is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable) + } + } + + fun getBlockByID(blockID: FlowId): FlowBlock { + return when (val response = accessAPI.getBlockById(blockID)) { + is FlowAccessApi.AccessApiCallResponse.Success -> response.data + is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable) + } + } + + fun getBlockByHeight(height: Long): FlowBlock { + return when (val response = accessAPI.getBlockByHeight(height)) { + is FlowAccessApi.AccessApiCallResponse.Success -> response.data + is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable) + } + } +} diff --git a/kotlin-example/src/main/kotlin/org/onflow/examples/kotlin/getEvent/GetEventAccessAPIConnector.kt b/kotlin-example/src/main/kotlin/org/onflow/examples/kotlin/getEvent/GetEventAccessAPIConnector.kt new file mode 100644 index 0000000..6a1121c --- /dev/null +++ b/kotlin-example/src/main/kotlin/org/onflow/examples/kotlin/getEvent/GetEventAccessAPIConnector.kt @@ -0,0 +1,37 @@ +package org.onflow.examples.kotlin.getEvent + +import org.onflow.flow.sdk.* + +class GetEventAccessAPIConnector( + private val accessAPI: FlowAccessApi +) { + fun getEventsForHeightRange(eventType: String, startHeight: Long, endHeight: Long): List { + val range = startHeight..endHeight + val response = accessAPI.getEventsForHeightRange(eventType, range) + return when (response) { + is FlowAccessApi.AccessApiCallResponse.Success -> response.data + is FlowAccessApi.AccessApiCallResponse.Error -> throw RuntimeException(response.message, response.throwable) + } + } + + fun getEventsForBlockIds(eventType: String, blockIds: List): List { + val blockIdSet = blockIds.toSet() + val response = accessAPI.getEventsForBlockIds(eventType, blockIdSet) + return when (response) { + is FlowAccessApi.AccessApiCallResponse.Success -> response.data + is FlowAccessApi.AccessApiCallResponse.Error -> throw RuntimeException(response.message, response.throwable) + } + } + + fun getTransactionResult(txID: FlowId): FlowTransactionResult { + val response = accessAPI.getTransactionResultById(txID) + return when (response) { + is FlowAccessApi.AccessApiCallResponse.Success -> response.data + is FlowAccessApi.AccessApiCallResponse.Error -> throw RuntimeException(response.message, response.throwable) + } + } + + fun getAccountCreatedEvents(startHeight: Long, endHeight: Long): List { + return getEventsForHeightRange("flow.AccountCreated", startHeight, endHeight) + } +} diff --git a/kotlin-example/src/main/kotlin/org/onflow/examples/kotlin/getNetworkParams/GetNetworkParametersAccessAPIConnector.kt b/kotlin-example/src/main/kotlin/org/onflow/examples/kotlin/getNetworkParams/GetNetworkParametersAccessAPIConnector.kt new file mode 100644 index 0000000..8f7f169 --- /dev/null +++ b/kotlin-example/src/main/kotlin/org/onflow/examples/kotlin/getNetworkParams/GetNetworkParametersAccessAPIConnector.kt @@ -0,0 +1,15 @@ +package org.onflow.examples.kotlin.getNetworkParams + +import org.onflow.flow.sdk.FlowAccessApi +import org.onflow.flow.sdk.FlowChainId + +internal class GetNetworkParametersAccessAPIConnector( + private val accessAPI: FlowAccessApi +) { + fun getNetworkParameters(): FlowChainId { + return when (val response = accessAPI.getNetworkParameters()) { + is FlowAccessApi.AccessApiCallResponse.Success -> response.data + is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable) + } + } +} diff --git a/kotlin-example/src/main/kotlin/org/onflow/examples/kotlin/getTransaction/GetTransactionAccessAPIConnector.kt b/kotlin-example/src/main/kotlin/org/onflow/examples/kotlin/getTransaction/GetTransactionAccessAPIConnector.kt new file mode 100644 index 0000000..38dc3f2 --- /dev/null +++ b/kotlin-example/src/main/kotlin/org/onflow/examples/kotlin/getTransaction/GetTransactionAccessAPIConnector.kt @@ -0,0 +1,21 @@ +package org.onflow.examples.kotlin.getTransaction + +import org.onflow.flow.sdk.* + +class GetTransactionAccessAPIConnector( + private val accessAPI: FlowAccessApi +) { + fun getTransaction(txID: FlowId): FlowTransaction { + return when (val response = accessAPI.getTransactionById(txID)) { + is FlowAccessApi.AccessApiCallResponse.Success -> response.data + is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable) + } + } + + fun getTransactionResult(txID: FlowId): FlowTransactionResult { + return when (val response = accessAPI.getTransactionResultById(txID)) { + is FlowAccessApi.AccessApiCallResponse.Success -> response.data + is FlowAccessApi.AccessApiCallResponse.Error -> throw Exception(response.message, response.throwable) + } + } +} diff --git a/kotlin-example/src/test/kotlin/org/onflow/examples/kotlin/getAccount/GetAccountAccessAPIConnectorTest.kt b/kotlin-example/src/test/kotlin/org/onflow/examples/kotlin/getAccount/GetAccountAccessAPIConnectorTest.kt new file mode 100644 index 0000000..c0f23ee --- /dev/null +++ b/kotlin-example/src/test/kotlin/org/onflow/examples/kotlin/getAccount/GetAccountAccessAPIConnectorTest.kt @@ -0,0 +1,53 @@ +package org.onflow.examples.kotlin.getAccount + +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.onflow.flow.common.test.* +import org.onflow.flow.sdk.FlowAccessApi +import java.math.BigDecimal + +@FlowEmulatorProjectTest(flowJsonLocation = "../flow/flow.json") +internal class GetAccountAccessAPIConnectorTest { + @FlowServiceAccountCredentials + lateinit var testAccount: TestAccount + + @FlowTestClient + lateinit var accessAPI: FlowAccessApi + + private lateinit var accountConnector: GetAccountAccessAPIConnector + + @BeforeEach + fun setup() { + accountConnector = GetAccountAccessAPIConnector(accessAPI) + } + + @Test + fun `Can fetch account from the latest block`() { + val address = testAccount.flowAddress + val account = accountConnector.getAccountAtLatestBlock(address) + + assertNotNull(account, "Account should not be null") + assertEquals(address.base16Value, account.address.base16Value, "Address should match") + assertTrue(account.balance >= BigDecimal.ZERO, "Account balance should be non-negative") + } + + @Test + fun `Can fetch account from block by height 0`() { + val address = testAccount.flowAddress + val account = accountConnector.getAccountAtBlockHeight(address, 0) + + assertNotNull(account, "Account should not be null") + assertEquals(address.base16Value, account.address.base16Value, "Address should match") + assertTrue(account.balance >= BigDecimal.ZERO, "Account balance should be non-negative") + } + + @Test + fun `Can fetch account balance`() { + val address = testAccount.flowAddress + val balance = accountConnector.getAccountBalance(address) + + assertNotNull(balance, "Balance should not be null") + assertTrue(balance >= BigDecimal.ZERO, "Balance should be non-negative") + } +} diff --git a/kotlin-example/src/test/kotlin/org/onflow/examples/kotlin/getBlock/GetBlockAccessAPIConnectorTest.kt b/kotlin-example/src/test/kotlin/org/onflow/examples/kotlin/getBlock/GetBlockAccessAPIConnectorTest.kt new file mode 100644 index 0000000..1f506fc --- /dev/null +++ b/kotlin-example/src/test/kotlin/org/onflow/examples/kotlin/getBlock/GetBlockAccessAPIConnectorTest.kt @@ -0,0 +1,52 @@ +package org.onflow.examples.kotlin.getBlock + +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.onflow.flow.common.test.FlowEmulatorProjectTest +import org.onflow.flow.common.test.FlowTestClient +import org.onflow.flow.sdk.FlowAccessApi +import java.time.LocalDateTime + +@FlowEmulatorProjectTest(flowJsonLocation = "../flow/flow.json") +internal class GetBlockAccessAPIConnectorTest { + @FlowTestClient + lateinit var accessAPI: FlowAccessApi + + private lateinit var blockAPIConnector: GetBlockAccessAPIConnector + + @BeforeEach + fun setup() { + blockAPIConnector = GetBlockAccessAPIConnector(accessAPI) + } + + @Test + fun `Can fetch the latest sealed block`() { + val block = blockAPIConnector.getLatestSealedBlock() + + Assertions.assertNotNull(block.id, "Block ID should not be null") + Assertions.assertTrue(block.height >= 0, "Block height should be non-negative") + Assertions.assertTrue(block.timestamp.isBefore(LocalDateTime.now()), "Block timestamp should be in the past") + } + + @Test + fun `Can fetch a block by ID`() { + val latestBlock = blockAPIConnector.getLatestSealedBlock() + val blockByID = blockAPIConnector.getBlockByID(latestBlock.id) + + Assertions.assertEquals(latestBlock.id, blockByID.id, "Block IDs should match") + Assertions.assertEquals(latestBlock.height, blockByID.height, "Block heights should match") + Assertions.assertEquals(latestBlock.timestamp, blockByID.timestamp, "Block timestamps should match") + Assertions.assertEquals(latestBlock.collectionGuarantees.size, blockByID.collectionGuarantees.size, "Block should have the same number of collection guarantees") + } + + @Test + fun `Can fetch a block by height`() { + val latestBlock = blockAPIConnector.getLatestSealedBlock() + val blockByHeight = blockAPIConnector.getBlockByHeight(latestBlock.height) + + Assertions.assertEquals(latestBlock.id, blockByHeight.id, "Block ID fetched by height should match the latest block ID") + Assertions.assertEquals(latestBlock.timestamp, blockByHeight.timestamp, "Block timestamps should match for the latest block and the block fetched by height") + Assertions.assertEquals(latestBlock.collectionGuarantees.size, blockByHeight.collectionGuarantees.size, "Block should have the same number of collection guarantees") + } +} diff --git a/kotlin-example/src/test/kotlin/org/onflow/examples/kotlin/getEvent/GetEventAccessAPIConnectorTest.kt b/kotlin-example/src/test/kotlin/org/onflow/examples/kotlin/getEvent/GetEventAccessAPIConnectorTest.kt new file mode 100644 index 0000000..5c91528 --- /dev/null +++ b/kotlin-example/src/test/kotlin/org/onflow/examples/kotlin/getEvent/GetEventAccessAPIConnectorTest.kt @@ -0,0 +1,89 @@ +package org.onflow.examples.kotlin.getEvent + +import kotlinx.coroutines.runBlocking +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.onflow.examples.kotlin.AccessAPIConnector +import org.onflow.flow.common.test.* +import org.onflow.flow.sdk.* +import org.onflow.flow.sdk.crypto.Crypto + +@FlowEmulatorProjectTest(flowJsonLocation = "../flow/flow.json") +class GetEventsAccessAPIConnectorTest { + @FlowServiceAccountCredentials + lateinit var serviceAccount: TestAccount + + @FlowTestAccount + lateinit var testAccount: TestAccount + + @FlowTestClient + lateinit var accessAPI: FlowAccessApi + + private lateinit var connector: GetEventAccessAPIConnector + private lateinit var accessAPIConnector: AccessAPIConnector + + private lateinit var txID: FlowId + + @BeforeEach + fun setup() { + accessAPIConnector = AccessAPIConnector(serviceAccount.privateKey, accessAPI) + connector = GetEventAccessAPIConnector(accessAPI) + + // Send a sample transaction to create an account and capture the transaction ID + val publicKey = Crypto.generateKeyPair(SignatureAlgorithm.ECDSA_P256).public + txID = accessAPIConnector.sendSampleTransaction( + serviceAccount.flowAddress, + publicKey + ) + } + + @Test + fun testGetEventsForHeightRange() = runBlocking { + val events = connector.getEventsForHeightRange("flow.AccountCreated", 0, 30) + assertNotNull(events, "Events should not be null") + assert(events.isNotEmpty()) { "Expected account created events but found none." } + assert(events.size == 3) { "Expected 3 account created events." } + } + + @Test + fun testGetEventsForBlockIds() = runBlocking { + val latestBlock = when (val latestBlockResponse = accessAPI.getLatestBlockHeader()) { + is FlowAccessApi.AccessApiCallResponse.Success -> latestBlockResponse.data + is FlowAccessApi.AccessApiCallResponse.Error -> throw RuntimeException(latestBlockResponse.message, latestBlockResponse.throwable) + } + + val blockIds = listOf(latestBlock.id) + val events = connector.getEventsForBlockIds("flow.AccountCreated", blockIds) + + assertNotNull(events, "Events should not be null") + assert(events.isNotEmpty()) { "Expected events for the provided block IDs but found none." } + assert(events.size == 1) { "Expected 1 account created event." } + } + + @Test + fun testAccountCreatedEvents() = runBlocking { + val events = connector.getAccountCreatedEvents(0, 30) + assertNotNull(events, "Events should not be null") + assert(events.isNotEmpty()) { "Expected account created events but found none." } + events.forEach { block -> + block.events.forEach { event -> + assertEquals("flow.AccountCreated", event.type) + } + } + } + + @Test + fun testTransactionResultEvents() = runBlocking { + val txResult = connector.getTransactionResult(txID) + assertNotNull(txResult, "Transaction result should not be null") + assert(txResult.events.isNotEmpty()) { "Expected events in transaction result but found none." } + + val expectedEventTypes = setOf("Withdrawn", "TokensDeposited", "Deposited", "AccountCreated", "AccountKeyAdded", "TokensWithdrawn", "StorageCapabilityControllerIssued", "CapabilityPublished") + + txResult.events.forEach { event -> + val eventType = event.type.split(".").last() + assertTrue(expectedEventTypes.contains(eventType), "Unexpected event type: $eventType") + } + } +} diff --git a/kotlin-example/src/test/kotlin/org/onflow/examples/kotlin/getNetworkParams/GetNetworkParametersAccessAPIConnectorTest.kt b/kotlin-example/src/test/kotlin/org/onflow/examples/kotlin/getNetworkParams/GetNetworkParametersAccessAPIConnectorTest.kt new file mode 100644 index 0000000..c89ff30 --- /dev/null +++ b/kotlin-example/src/test/kotlin/org/onflow/examples/kotlin/getNetworkParams/GetNetworkParametersAccessAPIConnectorTest.kt @@ -0,0 +1,31 @@ +package org.onflow.examples.kotlin.getNetworkParams + +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.onflow.flow.common.test.FlowEmulatorProjectTest +import org.onflow.flow.common.test.FlowTestClient +import org.onflow.flow.sdk.FlowAccessApi +import org.onflow.flow.sdk.FlowChainId + +@FlowEmulatorProjectTest(flowJsonLocation = "../flow/flow.json") +internal class GetNetworkParametersAccessAPIConnectorTest { + @FlowTestClient + lateinit var accessAPI: FlowAccessApi + + private lateinit var networkParametersConnector: GetNetworkParametersAccessAPIConnector + + @BeforeEach + fun setup() { + networkParametersConnector = GetNetworkParametersAccessAPIConnector(accessAPI) + } + + @Test + fun `Can fetch network parameters`() { + val networkParams: FlowChainId = networkParametersConnector.getNetworkParameters() + assertNotNull(networkParams, "Network parameters should not be null") + assertTrue(networkParams.id.isNotEmpty(), "Network parameters should have a valid ID") + assertEquals(networkParams, FlowChainId.EMULATOR) + assertEquals(networkParams.id, "flow-emulator") + } +} diff --git a/kotlin-example/src/test/kotlin/org/onflow/examples/kotlin/getTransaction/GetTransactionAccessAPIConnectorTest.kt b/kotlin-example/src/test/kotlin/org/onflow/examples/kotlin/getTransaction/GetTransactionAccessAPIConnectorTest.kt new file mode 100644 index 0000000..a33d73e --- /dev/null +++ b/kotlin-example/src/test/kotlin/org/onflow/examples/kotlin/getTransaction/GetTransactionAccessAPIConnectorTest.kt @@ -0,0 +1,55 @@ +package org.onflow.examples.kotlin.getTransaction + +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.onflow.examples.kotlin.AccessAPIConnector +import org.onflow.flow.common.test.FlowEmulatorProjectTest +import org.onflow.flow.common.test.FlowServiceAccountCredentials +import org.onflow.flow.common.test.FlowTestClient +import org.onflow.flow.common.test.TestAccount +import org.onflow.flow.sdk.* +import org.onflow.flow.sdk.crypto.Crypto + +@FlowEmulatorProjectTest(flowJsonLocation = "../flow/flow.json") +internal class GetTransactionAccessAPIConnectorTest { + @FlowServiceAccountCredentials + lateinit var serviceAccount: TestAccount + + @FlowTestClient + lateinit var accessAPI: FlowAccessApi + + private lateinit var connector: GetTransactionAccessAPIConnector + private lateinit var accessAPIConnector: AccessAPIConnector + + private lateinit var txID: FlowId + + @BeforeEach + fun setup() { + accessAPIConnector = AccessAPIConnector(serviceAccount.privateKey, accessAPI) + connector = GetTransactionAccessAPIConnector(accessAPI) + + // Send a sample transaction to create an account and capture the transaction ID + val publicKey = Crypto.generateKeyPair(SignatureAlgorithm.ECDSA_P256).public + txID = accessAPIConnector.sendSampleTransaction( + serviceAccount.flowAddress, + publicKey + ) + } + + @Test + fun `Can fetch a transaction`() { + val transaction = connector.getTransaction(txID) + + assertNotNull(transaction, "Transaction should not be null") + assertEquals(txID, transaction.id, "Transaction ID should match") + } + + @Test + fun `Can fetch a transaction result`() { + val transactionResult = connector.getTransactionResult(txID) + + assertNotNull(transactionResult, "Transaction result should not be null") + assertTrue(transactionResult.status === FlowTransactionStatus.SEALED, "Transaction should be sealed") + } +} diff --git a/sdk/src/intTest/org/onflow/flow/sdk/transaction/TransactionIntegrationTest.kt b/sdk/src/intTest/org/onflow/flow/sdk/transaction/TransactionIntegrationTest.kt index 2d854ec..d296d8b 100644 --- a/sdk/src/intTest/org/onflow/flow/sdk/transaction/TransactionIntegrationTest.kt +++ b/sdk/src/intTest/org/onflow/flow/sdk/transaction/TransactionIntegrationTest.kt @@ -24,7 +24,7 @@ class TransactionIntegrationTest { try { handleResult(accessAPI.ping(), "Failed to ping") } catch (e: Exception) { - fail("Failed to ping mainnet: ${e.message}") + fail("Failed to ping emulator: ${e.message}") } val address = serviceAccount.flowAddress diff --git a/sdk/src/test/kotlin/org/onflow/flow/sdk/impl/FlowAccessApiImplTest.kt b/sdk/src/test/kotlin/org/onflow/flow/sdk/impl/FlowAccessApiImplTest.kt index 1011ef4..baa02f3 100644 --- a/sdk/src/test/kotlin/org/onflow/flow/sdk/impl/FlowAccessApiImplTest.kt +++ b/sdk/src/test/kotlin/org/onflow/flow/sdk/impl/FlowAccessApiImplTest.kt @@ -288,7 +288,7 @@ class FlowAccessApiImplTest { `when`(mockApi.getEventsForHeightRange(any())).thenReturn(response) - val result = flowAccessApiImpl.getEventsForHeightRange(type, range) + val result = flowAccessApiImpl.getEventsForHeightRange(type = type, range = range) assertResultSuccess(result) { assertEquals(2, it.size) } verify(mockApi).getEventsForHeightRange(Access.GetEventsForHeightRangeRequest.newBuilder().setType(type).setStartHeight(range.first).setEndHeight(range.last).build())