Files
lez-programs/apps/shared/wallet/tests/cpp/WalletPortfolioServiceTest.cpp

207 lines
7.5 KiB
C++
Raw Permalink Normal View History

#include "WalletPortfolioService.h"
#include <QtTest>
#include <utility>
// The service normally links this symbol from wallet-idl-decoder. Every test
// injects a decoder, so keep this target independent from the Rust FFI library.
WalletDecodeResult WalletIdlDecoder::decode(const QByteArray&,
const QVector<WalletAccountRead>&)
{
return {
QStringLiteral("error"),
QStringLiteral("unexpected_default_decoder"),
{},
};
}
namespace {
const QString DEFINITION_ID(64, QLatin1Char('a'));
const QString DEFINITION_BASE58 = QStringLiteral("base58-definition");
const QString HOLDING_ID(64, QLatin1Char('b'));
const QString TOKEN_PROGRAM_ID(64, QLatin1Char('c'));
const QString AMM_ACCOUNT_ID(64, QLatin1Char('d'));
const QString AMM_PROGRAM_ID(64, QLatin1Char('e'));
WalletAccountRead read(const QString& accountId,
const QString& programOwner,
const QString& data)
{
WalletAccountRead result;
result.accountId = accountId;
result.status = QStringLiteral("ok");
result.programOwner = programOwner;
result.dataHex = data;
return result;
}
WalletPortfolioRequest request(const QString& name = QStringLiteral("Test token"))
{
WalletSnapshot snapshot;
snapshot.publicAccountReads = {
read(DEFINITION_ID, TOKEN_PROGRAM_ID, QStringLiteral("definition")),
read(HOLDING_ID, TOKEN_PROGRAM_ID, QStringLiteral("holding")),
read(AMM_ACCOUNT_ID, AMM_PROGRAM_ID, QStringLiteral("amm")),
};
WalletPortfolioRequest result(snapshot);
result.tokenDefinitionIds = { DEFINITION_BASE58 };
result.tokens = { QVariantMap {
{ QStringLiteral("definitionId"), DEFINITION_BASE58 },
{ QStringLiteral("definitionIdHex"), DEFINITION_ID },
{ QStringLiteral("name"), name },
} };
result.tokenProgramId = TOKEN_PROGRAM_ID;
result.tokenIdl = QByteArrayLiteral("token-idl");
return result;
}
WalletDecodedAccount tokenDefinition()
{
WalletDecodedAccount account;
account.id = DEFINITION_ID;
account.status = QStringLiteral("decoded");
account.typeName = QStringLiteral("TokenDefinition");
account.value = QJsonObject {
{ QStringLiteral("Fungible"), QJsonObject {
{ QStringLiteral("name"), QStringLiteral("Test token") },
} },
};
return account;
}
WalletDecodedAccount tokenHolding(bool decoded = true)
{
WalletDecodedAccount account;
account.id = HOLDING_ID;
account.status = decoded ? QStringLiteral("decoded") : QStringLiteral("error");
account.typeName = QStringLiteral("TokenHolding");
account.value = QJsonObject {
{ QStringLiteral("Fungible"), QJsonObject {
{ QStringLiteral("definition_id"), QStringLiteral("definition") },
{ QStringLiteral("balance"), QStringLiteral("25") },
} },
};
account.accountIds.insert(QStringLiteral("definition"), DEFINITION_ID);
return account;
}
WalletDecodedAccount ammAccount()
{
WalletDecodedAccount account;
account.id = AMM_ACCOUNT_ID;
account.status = QStringLiteral("decoded");
account.typeName = QStringLiteral("Pool");
account.value = QJsonObject {
{ QStringLiteral("Pool"), QJsonObject {} },
};
return account;
}
WalletPortfolioService::Decoder decoder(int* calls, bool failHolding = false)
{
return [calls, failHolding](const QByteArray&, const QVector<WalletAccountRead>& reads) {
++*calls;
WalletDecodeResult result;
result.status = QStringLiteral("ok");
for (const WalletAccountRead& item : reads) {
if (item.accountId == DEFINITION_ID)
result.accounts.append(tokenDefinition());
else if (item.accountId == HOLDING_ID)
result.accounts.append(tokenHolding(!failHolding));
else if (item.accountId == AMM_ACCOUNT_ID)
result.accounts.append(ammAccount());
}
return result;
};
}
}
class WalletPortfolioServiceTest : public QObject {
Q_OBJECT
private slots:
void acceptsBase58DefinitionsAndReusesUnchangedDecodes();
void exposesHoldingDecodeFailureWithoutZeroBalance();
void exposesUnreadPublicAccountWithoutZeroBalance();
void exposesUnresolvedDefinitions();
};
void WalletPortfolioServiceTest::acceptsBase58DefinitionsAndReusesUnchangedDecodes()
{
int decodeCalls = 0;
WalletPortfolioService service(decoder(&decodeCalls));
service.registerProgram(AMM_PROGRAM_ID, QStringLiteral("AMM"), QByteArrayLiteral("amm-idl"));
const WalletPortfolioResult first = service.refresh(request());
QCOMPARE(first.status, QStringLiteral("ready"));
QCOMPARE(first.assets.size(), 1);
const QVariantMap asset = first.assets.first().toMap();
QCOMPARE(asset.value(QStringLiteral("definitionId")).toString(), DEFINITION_ID);
QCOMPARE(asset.value(QStringLiteral("displayDefinitionId")).toString(), DEFINITION_BASE58);
QCOMPARE(asset.value(QStringLiteral("balance")).toString(), QStringLiteral("25"));
QCOMPARE(first.presentations.size(), 3);
const int firstDecodeCalls = decodeCalls;
QVERIFY(firstDecodeCalls > 0);
const WalletPortfolioResult renamed = service.refresh(request(QStringLiteral("Renamed")));
QCOMPARE(decodeCalls, firstDecodeCalls);
QCOMPARE(renamed.status, QStringLiteral("ready"));
QCOMPARE(renamed.assets.first().toMap().value(QStringLiteral("name")).toString(),
QStringLiteral("Renamed"));
}
void WalletPortfolioServiceTest::exposesHoldingDecodeFailureWithoutZeroBalance()
{
int decodeCalls = 0;
WalletPortfolioService service(decoder(&decodeCalls, true));
const WalletPortfolioResult result = service.refresh(request());
QCOMPARE(result.status, QStringLiteral("partial"));
QCOMPARE(result.error, QStringLiteral("holding_decode_failed"));
const QVariantMap asset = result.assets.first().toMap();
QCOMPARE(asset.value(QStringLiteral("status")).toString(), QStringLiteral("unavailable"));
QVERIFY(asset.value(QStringLiteral("balance")).toString().isEmpty());
}
void WalletPortfolioServiceTest::exposesUnreadPublicAccountWithoutZeroBalance()
{
int decodeCalls = 0;
WalletPortfolioService service(decoder(&decodeCalls));
WalletPortfolioRequest input = request();
for (WalletAccountRead& account : input.publicAccountReads) {
if (account.accountId == HOLDING_ID)
account = WalletAccountRead { HOLDING_ID };
}
const WalletPortfolioResult result = service.refresh(input);
QCOMPARE(result.status, QStringLiteral("partial"));
QCOMPARE(result.error, QStringLiteral("public_account_read_failed"));
const QVariantMap asset = result.assets.first().toMap();
QCOMPARE(asset.value(QStringLiteral("status")).toString(), QStringLiteral("unavailable"));
QVERIFY(asset.value(QStringLiteral("balance")).toString().isEmpty());
}
void WalletPortfolioServiceTest::exposesUnresolvedDefinitions()
{
int decodeCalls = 0;
WalletPortfolioService service(decoder(&decodeCalls));
WalletPortfolioRequest input = request();
input.tokens.clear();
const WalletPortfolioResult result = service.refresh(input);
QCOMPARE(result.status, QStringLiteral("error"));
QCOMPARE(result.error, QStringLiteral("definitions_unavailable"));
QCOMPARE(result.assets.size(), 1);
QCOMPARE(result.assets.first().toMap().value(QStringLiteral("status")).toString(),
QStringLiteral("unavailable"));
}
QTEST_GUILESS_MAIN(WalletPortfolioServiceTest)
#include "WalletPortfolioServiceTest.moc"