Skip to content

Commit d59b99c

Browse files
Mateusz Czeladkaclaude
andcommitted
feat: sort operations by index and add unit test
- Sort operations by operation_identifier.index before returning from getOperationsFromTransactionData() to ensure consistent ordering (0, 1, 2, ...) - Add OperationOrdering test class with shouldReturnOperationsSortedByIndex test to verify sorting behavior 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 2662b12 commit d59b99c

File tree

3 files changed

+64
-5
lines changed

3 files changed

+64
-5
lines changed

api/src/main/java/org/cardanofoundation/rosetta/api/construction/service/TransactionOperationParserImpl.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,7 @@
2424
import org.openapitools.client.model.OperationMetadata;
2525
import org.springframework.stereotype.Service;
2626

27-
import java.util.ArrayList;
28-
import java.util.Collections;
29-
import java.util.List;
30-
import java.util.Optional;
27+
import java.util.*;
3128

3229
import static org.cardanofoundation.rosetta.common.enumeration.OperationType.POOL_GOVERNANCE_VOTE;
3330
import static org.cardanofoundation.rosetta.common.enumeration.OperationType.VOTE_DREP_DELEGATION;
@@ -58,8 +55,11 @@ public List<Operation> getOperationsFromTransactionData(TransactionData data, Ne
5855
fillOutputOperations(transactionBody, operations);
5956
fillCertOperations(transactionBody, extraData, network, operations);
6057
fillWithdrawalOperations(transactionBody, extraData, network, operations);
58+
// if more voting operations are added consider to rename this to fillVotingOperations
6159
fillSpoVotingOperations(extraData, operations);
6260

61+
operations.sort(Comparator.comparingLong(op -> op.getOperationIdentifier().getIndex()));
62+
6363
return operations;
6464
}
6565

api/src/main/java/org/cardanofoundation/rosetta/common/model/cardano/transaction/TransactionData.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.bloxbean.cardano.client.transaction.spec.TransactionBody;
44

5-
public record TransactionData(TransactionBody transactionBody, TransactionExtraData transactionExtraData) {
5+
public record TransactionData(TransactionBody transactionBody,
6+
TransactionExtraData transactionExtraData) {
67

78
}

api/src/test/java/org/cardanofoundation/rosetta/common/mapper/TransactionOperationParserImplTest.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -991,6 +991,64 @@ void shouldHandleNullOutputsList()
991991
assertThat(operations).isEmpty();
992992
}
993993
}
994+
995+
/**
996+
* Tests for operation ordering/sorting behavior.
997+
*/
998+
@Nested
999+
class OperationOrdering {
1000+
1001+
@Test
1002+
void shouldReturnOperationsSortedByIndex()
1003+
throws CborException, CborDeserializationException, CborSerializationException {
1004+
// Create operations with non-sequential indices in extraData (index 2 before index 0)
1005+
Operation input1 = Operation.builder()
1006+
.type(OperationType.INPUT.getValue())
1007+
.operationIdentifier(OperationIdentifier.builder().index(2L).build())
1008+
.account(AccountIdentifier.builder().address("addr1").build())
1009+
.amount(Amount.builder().value("-1000000").currency(
1010+
CurrencyResponse.builder().symbol("ADA").decimals(6).build()).build())
1011+
.build();
1012+
1013+
Operation input2 = Operation.builder()
1014+
.type(OperationType.INPUT.getValue())
1015+
.operationIdentifier(OperationIdentifier.builder().index(0L).build())
1016+
.account(AccountIdentifier.builder().address("addr2").build())
1017+
.amount(Amount.builder().value("-2000000").currency(
1018+
CurrencyResponse.builder().symbol("ADA").decimals(6).build()).build())
1019+
.build();
1020+
1021+
Operation input3 = Operation.builder()
1022+
.type(OperationType.INPUT.getValue())
1023+
.operationIdentifier(OperationIdentifier.builder().index(1L).build())
1024+
.account(AccountIdentifier.builder().address("addr3").build())
1025+
.amount(Amount.builder().value("-3000000").currency(
1026+
CurrencyResponse.builder().symbol("ADA").decimals(6).build()).build())
1027+
.build();
1028+
1029+
// Pass operations in non-sorted order: [2, 0, 1]
1030+
TransactionExtraData extraData = new TransactionExtraData(List.of(input1, input2, input3));
1031+
1032+
TransactionBody transactionBody = TransactionBody.builder()
1033+
.inputs(List.of(new TransactionInput(), new TransactionInput(), new TransactionInput()))
1034+
.build();
1035+
1036+
TransactionData transactionData = new TransactionData(transactionBody, extraData);
1037+
1038+
List<Operation> operations = parser
1039+
.getOperationsFromTransactionData(transactionData, NetworkEnum.MAINNET.getNetwork());
1040+
1041+
// Verify operations are sorted by index (0, 1, 2)
1042+
assertThat(operations)
1043+
.extracting(op -> op.getOperationIdentifier().getIndex())
1044+
.isSorted();
1045+
1046+
// Verify the exact order
1047+
assertThat(operations.get(0).getOperationIdentifier().getIndex()).isEqualTo(0L);
1048+
assertThat(operations.get(1).getOperationIdentifier().getIndex()).isEqualTo(1L);
1049+
assertThat(operations.get(2).getOperationIdentifier().getIndex()).isEqualTo(2L);
1050+
}
1051+
}
9941052
}
9951053

9961054
// ========== Helper Methods ==========

0 commit comments

Comments
 (0)