Skip to content

Commit

Permalink
Issue #44 coverage (#85)
Browse files Browse the repository at this point in the history
* issue #44 - test coverage improvements

- removed unused HTTPStatus class
- added tests for first batch of transaction types
- added missing getters on transactions
- added hash/Code/equals to couple of places
- added resources with serialized transaction data

* more tests for transactions and associated objects

* private key hex string is now always 64 characters long even when small

private key is random and as such can start by 0. This was not handled
properly and resulting hexa string was shorter than expected

* fixed typo in log message

* fixing typo in property name
  • Loading branch information
tonowie authored Jun 26, 2019
1 parent e49c12a commit d6f7a72
Show file tree
Hide file tree
Showing 53 changed files with 1,281 additions and 769 deletions.
4 changes: 2 additions & 2 deletions src/e2e/java/io/proximax/sdk/E2EContractTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ void prepare() {

@Test
void test02CreateContract() {
logger.info("Creating constract");
logger.info("Creating contract");
// prepare transaction
ModifyContractTransaction trans = ModifyContractTransaction.create(getDeadline(),
BigInteger.ZERO,
Expand Down Expand Up @@ -119,7 +119,7 @@ void test02CreateContract() {

@Test
void test03ChangeExistingContract() {
logger.info("Changing constract");
logger.info("Changing contract");
// prepare transaction - add one block to duration, move executor2 to verifiers
ModifyContractTransaction trans = ModifyContractTransaction.create(getDeadline(),
BigInteger.ZERO,
Expand Down
2 changes: 1 addition & 1 deletion src/e2e/resources/config.properties
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ xpxsdk.conf.timeout=30

#xpxsdk.conf.url=http://bctestnet1.xpxsirius.io:3000
#xpxsdk.conf.seed.private_key=
#vsdk.conf.network_type=TEST_NET
#xpxsdk.conf.network_type=TEST_NET
#xpxsdk.conf.timeout=60
2 changes: 1 addition & 1 deletion src/main/java/io/proximax/core/crypto/PrivateKey.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,6 @@ public boolean equals(final Object obj) {

@Override
public String toString() {
return HexEncoder.getString(this.value.toByteArray());
return HexEncoder.getString(this.value.toByteArray(), 32);
}
}
15 changes: 15 additions & 0 deletions src/main/java/io/proximax/core/utils/HexEncoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,21 @@ public static byte[] getBytes(final String hexString) {
}
}

/**
* <p>Converts a byte array to a hex string of at minimum specified length prefixed by 0 characters as needed</p>
*
* <p>This implementation is specifically intended to make sure that if key is 32 bytes then output is 64
* hexadecimal characters even if first bytes were 0 and input was shorter</p>
*
* @param bytes The input byte array
* @param targetByteCount prefix 0 to make the source desired size
* @return The output hex string.
*/
public static String getString(final byte[] bytes, int targetByteCount) {
String hexString = getString(bytes);
return StringUtils.repeat("00", targetByteCount - bytes.length) + hexString;
}

/**
* Converts a byte array to a hex string.
*
Expand Down
25 changes: 25 additions & 0 deletions src/main/java/io/proximax/core/utils/StringUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
* Static class that contains string utility functions.
*/
public class StringUtils {

private StringUtils() {
// hiding implicit constructor for utility class
}

/**
* Determines if the specified string is null or empty.
Expand Down Expand Up @@ -51,6 +55,27 @@ public static boolean isNullOrWhitespace(final String str) {
return true;
}

/**
* <p>Repeat a String {@code repeat} times to form a new String.</p>
*
* <pre>
* StringUtils.repeat(null, 2) = null
* StringUtils.repeat("", 0) = ""
* StringUtils.repeat("", 2) = ""
* StringUtils.repeat("a", 3) = "aaa"
* StringUtils.repeat("ab", 2) = "abab"
* StringUtils.repeat("a", -2) = ""
* </pre>
*
* @param str the String to repeat, may be null
* @param repeat number of times to repeat str, negative treated as zero
* @return a new String consisting of the original String repeated,
* {@code null} if null String input
*/
public static String repeat(String str, int repeat) {
return org.apache.commons.lang3.StringUtils.repeat(str, repeat);
}

/**
* Replaces a variable contained in a string with a value. A variable is defined as ${variable}.
* This pattern is replaced by the given value.
Expand Down
96 changes: 17 additions & 79 deletions src/main/java/io/proximax/sdk/infrastructure/AccountHttp.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,8 @@
package io.proximax.sdk.infrastructure;

import static io.proximax.sdk.utils.GsonUtils.stream;
import static io.proximax.sdk.utils.dto.UInt64Utils.toBigInt;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

Expand All @@ -36,21 +33,15 @@
import io.proximax.sdk.gen.model.AccountPropertiesInfoDTO;
import io.proximax.sdk.gen.model.MultisigAccountGraphInfoDTO;
import io.proximax.sdk.gen.model.MultisigAccountInfoDTO;
import io.proximax.sdk.gen.model.MultisigDTO;
import io.proximax.sdk.model.account.AccountInfo;
import io.proximax.sdk.model.account.Address;
import io.proximax.sdk.model.account.MultisigAccountGraphInfo;
import io.proximax.sdk.model.account.MultisigAccountInfo;
import io.proximax.sdk.model.account.PublicAccount;
import io.proximax.sdk.model.account.props.AccountProperties;
import io.proximax.sdk.model.blockchain.NetworkType;
import io.proximax.sdk.model.mosaic.Mosaic;
import io.proximax.sdk.model.mosaic.MosaicId;
import io.proximax.sdk.model.transaction.AggregateTransaction;
import io.proximax.sdk.model.transaction.Transaction;
import io.proximax.sdk.utils.dto.AccountDTOUtils;
import io.reactivex.Observable;
import io.reactivex.functions.Function;

/**
* Account http repository.
Expand All @@ -61,25 +52,15 @@ public class AccountHttp extends Http implements AccountRepository {

private static final String ROUTE = "/account/";
private static final String PROPERTIES_SUFFIX = "/properties";

public AccountHttp(BlockchainApi api) {
super(api);
}

@Override
public Observable<AccountInfo> getAccountInfo(Address address) {
return this.client.get(ROUTE + address.plain())
.map(Http::mapStringOrError)
.map(str -> objectMapper.readValue(str, AccountInfoDTO.class))
.map(AccountInfoDTO::getAccount)
.map(accountDTO -> new AccountInfo(
Address.createFromRawAddress(AccountDTOUtils.getAddressEncoded(accountDTO)),
toBigInt(accountDTO.getAddressHeight()), accountDTO.getPublicKey(),
toBigInt(accountDTO.getPublicKeyHeight()),
accountDTO.getMosaics().stream()
.map(mosaicDTO -> new Mosaic(new MosaicId(toBigInt(mosaicDTO.getId())),
toBigInt(mosaicDTO.getAmount())))
.collect(Collectors.toList())));
return this.client.get(ROUTE + address.plain()).map(Http::mapStringOrError)
.map(str -> objectMapper.readValue(str, AccountInfoDTO.class)).map(AccountInfo::fromDto);
}

@Override
Expand All @@ -92,58 +73,32 @@ public Observable<List<AccountInfo>> getAccountsInfo(List<Address> addresses) {
requestBody.add("addresses", arr);
return this.client.post(ROUTE, requestBody).map(Http::mapStringOrError)
.map(str -> objectMapper.<List<AccountInfoDTO>>readValue(str, new TypeReference<List<AccountInfoDTO>>() {
})).flatMapIterable(item -> item).map(AccountInfoDTO::getAccount).map(
accountDTO -> new AccountInfo(
Address.createFromRawAddress(AccountDTOUtils.getAddressEncoded(accountDTO)),
toBigInt(accountDTO.getAddressHeight()), accountDTO.getPublicKey(),
toBigInt(accountDTO.getPublicKeyHeight()),
accountDTO.getMosaics().stream()
.map(mosaicDTO -> new Mosaic(new MosaicId(toBigInt(mosaicDTO.getId())),
toBigInt(mosaicDTO.getAmount())))
.collect(Collectors.toList())))
.toList().toObservable();
})).flatMapIterable(item -> item).map(AccountInfo::fromDto).toList().toObservable();
}

@Override
public Observable<MultisigAccountInfo> getMultisigAccountInfo(Address address) {
return this.client.get(ROUTE + address.plain() + "/multisig").map(Http::mapStringOrError)
.map(str -> objectMapper.readValue(str, MultisigAccountInfoDTO.class))
.map(MultisigAccountInfoDTO::getMultisig).map(transfromMultisigAccountInfoDTO(api.getNetworkType()));
.map(dto -> MultisigAccountInfo.fromDto(dto, api.getNetworkType()));
}

@Override
public Observable<MultisigAccountGraphInfo> getMultisigAccountGraphInfo(Address address) {
final NetworkType networkType = api.getNetworkType();
return this.client.get(ROUTE + address.plain() + "/multisig/graph").map(Http::mapStringOrError)
.map(str -> objectMapper.<List<MultisigAccountGraphInfoDTO>>readValue(str,
new TypeReference<List<MultisigAccountGraphInfoDTO>>() {
}))
.map(multisigAccountGraphInfoDTOList -> {
Map<Integer, List<MultisigAccountInfo>> multisigAccountInfoMap = new HashMap<>();
multisigAccountGraphInfoDTOList.forEach(item -> multisigAccountInfoMap.put(item.getLevel(),
item.getMultisigEntries().stream().map(MultisigAccountInfoDTO::getMultisig)
.map(item2 -> new MultisigAccountInfo(new PublicAccount(item2.getAccount(), networkType),
item2.getMinApproval(), item2.getMinRemoval(),
item2.getCosignatories().stream()
.map(cosigner -> new PublicAccount(cosigner, networkType))
.collect(Collectors.toList()),
item2.getMultisigAccounts().stream()
.map(multisigAccount -> new PublicAccount(multisigAccount, networkType))
.collect(Collectors.toList())))
.collect(Collectors.toList())));
return new MultisigAccountGraphInfo(multisigAccountInfoMap);
});
.map(dto -> MultisigAccountGraphInfo.fromDto(dto, api.getNetworkType()));
}

@Override
public Observable<AccountProperties> getAccountProperties(Address address) {
return this.client.get(ROUTE + address.plain() + PROPERTIES_SUFFIX)
.map(Http::mapStringOrError)
return this.client.get(ROUTE + address.plain() + PROPERTIES_SUFFIX).map(Http::mapStringOrError)
.map(str -> objectMapper.readValue(str, AccountPropertiesInfoDTO.class))
.map(AccountPropertiesInfoDTO::getAccountProperties)
.map(AccountProperties::fromDto);
.map(AccountPropertiesInfoDTO::getAccountProperties).map(AccountProperties::fromDto);
}

@Override
public Observable<List<AccountProperties>> getAccountProperties(List<Address> addresses) {
// prepare JSON array with addresses
Expand All @@ -153,16 +108,15 @@ public Observable<List<AccountProperties>> getAccountProperties(List<Address> ad
JsonObject requestBody = new JsonObject();
requestBody.add("addresses", arr);
// post to the API
return this.client.post(ROUTE + PROPERTIES_SUFFIX, requestBody)
.map(Http::mapStringOrError)
.map(str -> objectMapper.<List<AccountPropertiesInfoDTO>>readValue(str, new TypeReference<List<AccountPropertiesInfoDTO>>() {}))
.flatMapIterable(item -> item)
.map(AccountPropertiesInfoDTO::getAccountProperties)
.map(AccountProperties::fromDto)
.toList().toObservable();

return this.client.post(ROUTE + PROPERTIES_SUFFIX, requestBody).map(Http::mapStringOrError)
.map(str -> objectMapper.<List<AccountPropertiesInfoDTO>>readValue(str,
new TypeReference<List<AccountPropertiesInfoDTO>>() {
}))
.flatMapIterable(item -> item).map(AccountPropertiesInfoDTO::getAccountProperties)
.map(AccountProperties::fromDto).toList().toObservable();

}

@Override
public Observable<List<Transaction>> transactions(PublicAccount publicAccount) {
return this.transactions(publicAccount, Optional.empty());
Expand Down Expand Up @@ -249,20 +203,4 @@ private Observable<List<Transaction>> findTransactions(PublicAccount publicAccou
.collect(Collectors.toList()))
.flatMapIterable(item -> item).map(new TransactionMapping()).toList().toObservable();
}

/**
* return function that transforms MultisigDTO to MultisigAccountInfo
*
* @param networkType
* @return
*/
private Function<MultisigDTO, MultisigAccountInfo> transfromMultisigAccountInfoDTO(NetworkType networkType) {
return multisig -> new MultisigAccountInfo(new PublicAccount(multisig.getAccount(), networkType),
multisig.getMinApproval(), multisig.getMinRemoval(),
multisig.getCosignatories().stream().map(cosigner -> new PublicAccount(cosigner, networkType))
.collect(Collectors.toList()),
multisig.getMultisigAccounts().stream()
.map(multisigAccount -> new PublicAccount(multisigAccount, networkType))
.collect(Collectors.toList()));
}
}
19 changes: 19 additions & 0 deletions src/main/java/io/proximax/sdk/model/account/AccountInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,17 @@

package io.proximax.sdk.model.account;

import static io.proximax.sdk.utils.dto.UInt64Utils.toBigInt;

import java.math.BigInteger;
import java.util.List;
import java.util.stream.Collectors;

import io.proximax.sdk.gen.model.AccountDTO;
import io.proximax.sdk.gen.model.AccountInfoDTO;
import io.proximax.sdk.model.mosaic.Mosaic;
import io.proximax.sdk.model.mosaic.MosaicId;
import io.proximax.sdk.utils.dto.AccountDTOUtils;

/**
* The account info structure describes basic information for an account.
Expand Down Expand Up @@ -94,4 +101,16 @@ public List<Mosaic> getMosaics() {
public PublicAccount getPublicAccount() {
return PublicAccount.createFromPublicKey(this.publicKey, this.address.getNetworkType());
}

public static AccountInfo fromDto(AccountInfoDTO dto) {
AccountDTO accountDTO = dto.getAccount();
return new AccountInfo(
Address.createFromRawAddress(AccountDTOUtils.getAddressEncoded(accountDTO)),
toBigInt(accountDTO.getAddressHeight()), accountDTO.getPublicKey(),
toBigInt(accountDTO.getPublicKeyHeight()),
accountDTO.getMosaics().stream()
.map(mosaicDTO -> new Mosaic(new MosaicId(toBigInt(mosaicDTO.getId())),
toBigInt(mosaicDTO.getAmount())))
.collect(Collectors.toList()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,37 +16,51 @@

package io.proximax.sdk.model.account;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import io.proximax.sdk.gen.model.MultisigAccountGraphInfoDTO;
import io.proximax.sdk.model.blockchain.NetworkType;

/**
* The multisig account graph info structure describes the information of all the mutlisig levels an account is involved in.
* The multisig account graph info structure describes the information of all the mutlisig levels an account is involved
* in.
*
* @since 1.0
*/
public class MultisigAccountGraphInfo {
private final Map<Integer, List<MultisigAccountInfo>> multisigAccounts;

public MultisigAccountGraphInfo(Map<Integer, List<MultisigAccountInfo>> multisigAccounts) {
this.multisigAccounts = multisigAccounts;
}

/**
* Returns multisig accounts levels number.
*
* @return levels in the graph
*/
public Set<Integer> getLevelsNumber() {
return this.multisigAccounts.keySet();
}

/**
* Returns multisig accounts.
*
* @return list of multisig accounts for each level
*/
public Map<Integer, List<MultisigAccountInfo>> getMultisigAccounts() {
return multisigAccounts;
}
private final Map<Integer, List<MultisigAccountInfo>> multisigAccounts;

public MultisigAccountGraphInfo(Map<Integer, List<MultisigAccountInfo>> multisigAccounts) {
this.multisigAccounts = multisigAccounts;
}

/**
* Returns multisig accounts levels number.
*
* @return levels in the graph
*/
public Set<Integer> getLevelsNumber() {
return this.multisigAccounts.keySet();
}

/**
* Returns multisig accounts.
*
* @return list of multisig accounts for each level
*/
public Map<Integer, List<MultisigAccountInfo>> getMultisigAccounts() {
return multisigAccounts;
}

public static MultisigAccountGraphInfo fromDto(List<MultisigAccountGraphInfoDTO> dto, NetworkType networkType) {
Map<Integer, List<MultisigAccountInfo>> multisigAccountInfoMap = new HashMap<>();
dto.forEach(item -> multisigAccountInfoMap.put(item.getLevel(),
item.getMultisigEntries().stream().map(item2 -> MultisigAccountInfo.fromDto(item2, networkType))
.collect(Collectors.toList())));
return new MultisigAccountGraphInfo(multisigAccountInfoMap);
}
}
Loading

0 comments on commit d6f7a72

Please sign in to comment.