wallet-core/packages/taler-wallet-core/src/util/coinSelection.test.ts
2023-06-15 13:07:31 -03:00

221 lines
5.6 KiB
TypeScript

/*
This file is part of GNU Taler
(C) 2022 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import test, { ExecutionContext } from "ava";
import {
calculatePlanFormAvailableCoins,
selectCoinForOperation,
} from "./coinSelection.js";
import {
AbsoluteTime,
AgeRestriction,
AmountJson,
Amounts,
Duration,
TransactionType,
} from "@gnu-taler/taler-util";
function expect(t: ExecutionContext, thing: any): any {
return {
deep: {
equal: (another: any) => t.deepEqual(thing, another),
equals: (another: any) => t.deepEqual(thing, another),
},
};
}
function kudos(v: number): AmountJson {
return Amounts.fromFloat(v, "KUDOS");
}
function defaultFeeConfig(value: AmountJson, totalAvailable: number) {
return {
id: Amounts.stringify(value),
denomDeposit: kudos(0.01),
denomRefresh: kudos(0.01),
denomWithdraw: kudos(0.01),
duration: Duration.getForever(),
exchangePurse: undefined,
exchangeWire: undefined,
maxAge: AgeRestriction.AGE_UNRESTRICTED,
totalAvailable,
value,
};
}
type Coin = [AmountJson, number];
/**
* selectCoinForOperation test
*
* Test here should check that the correct coins are selected
*/
test("get effective 2", (t) => {
const coinList: Coin[] = [
[kudos(2), 5],
[kudos(5), 5],
];
const result = selectCoinForOperation("credit", kudos(2), "net", {
list: coinList.map(([v, t]) => defaultFeeConfig(v, t)),
exchanges: {},
});
expect(t, result.coins).deep.equal(["KUDOS:2"]);
t.assert(result.refresh === undefined);
});
test("get raw 4", (t) => {
const coinList: Coin[] = [
[kudos(2), 5],
[kudos(5), 5],
];
const result = selectCoinForOperation("credit", kudos(4), "gross", {
list: coinList.map(([v, t]) => defaultFeeConfig(v, t)),
exchanges: {},
});
expect(t, result.coins).deep.equal(["KUDOS:2", "KUDOS:2"]);
t.assert(result.refresh === undefined);
});
test("send effective 6", (t) => {
const coinList: Coin[] = [
[kudos(2), 5],
[kudos(5), 5],
];
const result = selectCoinForOperation("debit", kudos(6), "gross", {
list: coinList.map(([v, t]) => defaultFeeConfig(v, t)),
exchanges: {},
});
expect(t, result.coins).deep.equal(["KUDOS:5"]);
t.assert(result.refresh?.selected === "KUDOS:2");
});
test("send raw 6", (t) => {
const coinList: Coin[] = [
[kudos(2), 5],
[kudos(5), 5],
];
const result = selectCoinForOperation("debit", kudos(6), "gross", {
list: coinList.map(([v, t]) => defaultFeeConfig(v, t)),
exchanges: {},
});
expect(t, result.coins).deep.equal(["KUDOS:5"]);
t.assert(result.refresh?.selected === "KUDOS:2");
});
test("send raw 20 (not enough)", (t) => {
const coinList: Coin[] = [
[kudos(2), 1],
[kudos(5), 2],
];
const result = selectCoinForOperation("debit", kudos(20), "gross", {
list: coinList.map(([v, t]) => defaultFeeConfig(v, t)),
exchanges: {},
});
expect(t, result.coins).deep.equal(["KUDOS:5", "KUDOS:5", "KUDOS:2"]);
t.assert(result.refresh === undefined);
});
/**
* calculatePlanFormAvailableCoins test
*
* Test here should check that the plan summary for a transaction is correct
* * effective amount
* * raw amount
*/
test("deposit effective 2 ", (t) => {
const coinList: Coin[] = [
[kudos(2), 1],
[kudos(5), 2],
];
const result = calculatePlanFormAvailableCoins(
TransactionType.Deposit,
kudos(2),
"effective",
{
list: coinList.map(([v, t]) => defaultFeeConfig(v, t)),
exchanges: {
"2": {
creditDeadline: AbsoluteTime.never(),
debitDeadline: AbsoluteTime.never(),
wireFee: kudos(0.01),
purseFee: kudos(0.01),
},
},
},
);
t.deepEqual(result.rawAmount, "KUDOS:1.98");
t.deepEqual(result.effectiveAmount, "KUDOS:2");
});
test("deposit raw 2 ", (t) => {
const coinList: Coin[] = [
[kudos(2), 1],
[kudos(5), 2],
];
const result = calculatePlanFormAvailableCoins(
TransactionType.Deposit,
kudos(2),
"raw",
{
list: coinList.map(([v, t]) => defaultFeeConfig(v, t)),
exchanges: {
"2": {
creditDeadline: AbsoluteTime.never(),
debitDeadline: AbsoluteTime.never(),
wireFee: kudos(0.01),
purseFee: kudos(0.01),
},
},
},
);
t.deepEqual(result.rawAmount, "KUDOS:2");
t.deepEqual(result.effectiveAmount, "KUDOS:2.04");
});
test("withdraw raw 21 ", (t) => {
const coinList: Coin[] = [
[kudos(2), 1],
[kudos(5), 2],
];
const result = calculatePlanFormAvailableCoins(
TransactionType.Withdrawal,
kudos(21),
"raw",
{
list: coinList.map(([v, t]) => defaultFeeConfig(v, t)),
exchanges: {
"2": {
creditDeadline: AbsoluteTime.never(),
debitDeadline: AbsoluteTime.never(),
wireFee: kudos(0.01),
purseFee: kudos(0.01),
},
},
},
);
// denominations configuration is not suitable
// for greedy algorithm
t.deepEqual(result.rawAmount, "KUDOS:20");
t.deepEqual(result.effectiveAmount, "KUDOS:19.96");
});