Merge branch 'master' into age-withdraw

This commit is contained in:
Özgür Kesim 2023-08-25 13:24:30 +02:00
commit a58f73ecdb
Signed by: oec
GPG Key ID: 3D76A56D79EDD9D7
44 changed files with 1251 additions and 537 deletions

View File

@ -15,7 +15,7 @@
*/ */
import { import {
globalLogLevel, getGlobalLogLevel,
setGlobalLogLevelFromString, setGlobalLogLevelFromString,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { TranslationProvider } from "@gnu-taler/web-util/browser"; import { TranslationProvider } from "@gnu-taler/web-util/browser";
@ -62,9 +62,7 @@ const App: FunctionalComponent = () => {
); );
}; };
(window as any).setGlobalLogLevelFromString = setGlobalLogLevelFromString; (window as any).setGlobalLogLevelFromString = setGlobalLogLevelFromString;
(window as any).getGlobaLevel = () => { (window as any).getGlobalLevel = getGlobalLogLevel;
return globalLogLevel;
};
function localStorageProvider(): Map<unknown, unknown> { function localStorageProvider(): Map<unknown, unknown> {
const map = new Map(JSON.parse(localStorage.getItem("app-cache") || "[]")); const map = new Map(JSON.parse(localStorage.getItem("app-cache") || "[]"));

View File

@ -467,12 +467,29 @@ export async function setupDb(t: GlobalTestState): Promise<DbInfo> {
}; };
} }
/**
* Make sure that the taler-integrationtest-shared database exists.
* Don't delete it if it already exists.
*/
export async function setupSharedDb(t: GlobalTestState): Promise<DbInfo> {
const dbname = "taler-integrationtest-shared";
const databases = await runCommand(t, "list-dbs", "psql", ["-Aqtl"]);
if (databases.indexOf("taler-integrationtest-shared") < 0) {
await runCommand(t, "createdb", "createdb", [dbname]);
}
return {
connStr: `postgres:///${dbname}`,
dbname,
};
}
export interface BankConfig { export interface BankConfig {
currency: string; currency: string;
httpPort: number; httpPort: number;
database: string; database: string;
allowRegistrations: boolean; allowRegistrations: boolean;
maxDebt?: string; maxDebt?: string;
overrideTestDir?: string;
} }
export interface FakeBankConfig { export interface FakeBankConfig {
@ -518,6 +535,14 @@ function setCoin(config: Configuration, c: CoinConfig) {
} }
} }
function backoffStart(): number {
return 10;
}
function backoffIncrement(n: number): number {
return Math.min(Math.floor(n * 1.5), 1000);
}
/** /**
* Send an HTTP request until it succeeds or the process dies. * Send an HTTP request until it succeeds or the process dies.
*/ */
@ -529,6 +554,7 @@ export async function pingProc(
if (!proc || proc.proc.exitCode !== null) { if (!proc || proc.proc.exitCode !== null) {
throw Error(`service process ${serviceName} not started, can't ping`); throw Error(`service process ${serviceName} not started, can't ping`);
} }
let nextDelay = backoffStart();
while (true) { while (true) {
try { try {
logger.trace(`pinging ${serviceName} at ${url}`); logger.trace(`pinging ${serviceName} at ${url}`);
@ -537,8 +563,9 @@ export async function pingProc(
return; return;
} catch (e: any) { } catch (e: any) {
logger.warn(`service ${serviceName} not ready:`, e.toString()); logger.warn(`service ${serviceName} not ready:`, e.toString());
//console.log(e); logger.info(`waiting ${nextDelay}ms on ${serviceName}`);
await delayMs(1000); await delayMs(nextDelay);
nextDelay = backoffIncrement(nextDelay);
} }
if (!proc || proc.proc.exitCode != null || proc.proc.signalCode != null) { if (!proc || proc.proc.exitCode != null || proc.proc.signalCode != null) {
throw Error(`service process ${serviceName} stopped unexpectedly`); throw Error(`service process ${serviceName} stopped unexpectedly`);
@ -857,31 +884,57 @@ export class FakebankService
accountPassword: string; accountPassword: string;
}[] = []; }[] = [];
/**
* Create a new fakebank service handle.
*
* First generates the configuration for the fakebank and
* then creates a fakebank handle, but doesn't start the fakebank
* service yet.
*/
static async create( static async create(
gc: GlobalTestState, gc: GlobalTestState,
bc: BankConfig, bc: BankConfig,
): Promise<FakebankService> { ): Promise<FakebankService> {
const config = new Configuration(); const config = new Configuration();
setTalerPaths(config, gc.testDir + "/talerhome"); const testDir = bc.overrideTestDir ?? gc.testDir;
setTalerPaths(config, testDir + "/talerhome");
config.setString("taler", "currency", bc.currency); config.setString("taler", "currency", bc.currency);
config.setString("bank", "http_port", `${bc.httpPort}`); config.setString("bank", "http_port", `${bc.httpPort}`);
config.setString("bank", "serve", "http"); config.setString("bank", "serve", "http");
config.setString("bank", "max_debt_bank", `${bc.currency}:999999`); config.setString("bank", "max_debt_bank", `${bc.currency}:999999`);
config.setString("bank", "max_debt", bc.maxDebt ?? `${bc.currency}:100`); config.setString("bank", "max_debt", bc.maxDebt ?? `${bc.currency}:100`);
config.setString("bank", "ram_limit", `${1024}`); config.setString("bank", "ram_limit", `${1024}`);
const cfgFilename = gc.testDir + "/bank.conf"; const cfgFilename = testDir + "/bank.conf";
config.write(cfgFilename); config.write(cfgFilename, { excludeDefaults: true });
return new FakebankService(gc, bc, cfgFilename); return new FakebankService(gc, bc, cfgFilename);
} }
static fromExistingConfig(
gc: GlobalTestState,
opts: { overridePath?: string },
): FakebankService {
const testDir = opts.overridePath ?? gc.testDir;
const cfgFilename = testDir + `/bank.conf`;
const config = Configuration.load(cfgFilename);
const bc: BankConfig = {
allowRegistrations:
config.getYesNo("bank", "allow_registrations").orUndefined() ?? true,
currency: config.getString("taler", "currency").required(),
database: "none",
httpPort: config.getNumber("bank", "http_port").required(),
maxDebt: config.getString("bank", "max_debt").required(),
};
return new FakebankService(gc, bc, cfgFilename);
}
setSuggestedExchange(e: ExchangeServiceInterface, exchangePayto: string) { setSuggestedExchange(e: ExchangeServiceInterface, exchangePayto: string) {
if (!!this.proc) { if (!!this.proc) {
throw Error("Can't set suggested exchange while bank is running."); throw Error("Can't set suggested exchange while bank is running.");
} }
const config = Configuration.load(this.configFile); const config = Configuration.load(this.configFile);
config.setString("bank", "suggested_exchange", e.baseUrl); config.setString("bank", "suggested_exchange", e.baseUrl);
config.write(this.configFile); config.write(this.configFile, { excludeDefaults: true });
} }
get baseUrl(): string { get baseUrl(): string {
@ -958,6 +1011,7 @@ export interface ExchangeConfig {
roundUnit?: string; roundUnit?: string;
httpPort: number; httpPort: number;
database: string; database: string;
overrideTestDir?: string;
} }
export interface ExchangeServiceInterface { export interface ExchangeServiceInterface {
@ -968,8 +1022,13 @@ export interface ExchangeServiceInterface {
} }
export class ExchangeService implements ExchangeServiceInterface { export class ExchangeService implements ExchangeServiceInterface {
static fromExistingConfig(gc: GlobalTestState, exchangeName: string) { static fromExistingConfig(
const cfgFilename = gc.testDir + `/exchange-${exchangeName}.conf`; gc: GlobalTestState,
exchangeName: string,
opts: { overridePath?: string },
) {
const testDir = opts.overridePath ?? gc.testDir;
const cfgFilename = testDir + `/exchange-${exchangeName}.conf`;
const config = Configuration.load(cfgFilename); const config = Configuration.load(cfgFilename);
const ec: ExchangeConfig = { const ec: ExchangeConfig = {
currency: config.getString("taler", "currency").required(), currency: config.getString("taler", "currency").required(),
@ -978,7 +1037,9 @@ export class ExchangeService implements ExchangeServiceInterface {
name: exchangeName, name: exchangeName,
roundUnit: config.getString("taler", "currency_round_unit").required(), roundUnit: config.getString("taler", "currency_round_unit").required(),
}; };
const privFile = config.getPath("exchange", "master_priv_file").required(); const privFile = config
.getPath("exchange-offline", "master_priv_file")
.required();
const eddsaPriv = fs.readFileSync(privFile); const eddsaPriv = fs.readFileSync(privFile);
const keyPair: EddsaKeyPair = { const keyPair: EddsaKeyPair = {
eddsaPriv, eddsaPriv,
@ -1076,11 +1137,13 @@ export class ExchangeService implements ExchangeServiceInterface {
changeConfig(f: (config: Configuration) => void) { changeConfig(f: (config: Configuration) => void) {
const config = Configuration.load(this.configFilename); const config = Configuration.load(this.configFilename);
f(config); f(config);
config.write(this.configFilename); config.write(this.configFilename, { excludeDefaults: true });
} }
static create(gc: GlobalTestState, e: ExchangeConfig) { static create(gc: GlobalTestState, e: ExchangeConfig) {
const testDir = e.overrideTestDir ?? gc.testDir;
const config = new Configuration(); const config = new Configuration();
setTalerPaths(config, testDir + "/talerhome");
config.setString("taler", "currency", e.currency); config.setString("taler", "currency", e.currency);
// Required by the exchange but not really used yet. // Required by the exchange but not really used yet.
config.setString("exchange", "aml_threshold", `${e.currency}:1000000`); config.setString("exchange", "aml_threshold", `${e.currency}:1000000`);
@ -1089,7 +1152,6 @@ export class ExchangeService implements ExchangeServiceInterface {
"currency_round_unit", "currency_round_unit",
e.roundUnit ?? `${e.currency}:0.01`, e.roundUnit ?? `${e.currency}:0.01`,
); );
setTalerPaths(config, gc.testDir + "/talerhome");
config.setString( config.setString(
"exchange", "exchange",
"revocation_dir", "revocation_dir",
@ -1124,10 +1186,16 @@ export class ExchangeService implements ExchangeServiceInterface {
fs.mkdirSync(path.dirname(masterPrivFile), { recursive: true }); fs.mkdirSync(path.dirname(masterPrivFile), { recursive: true });
if (fs.existsSync(masterPrivFile)) {
throw new Error(
"master priv file already exists, can't create new exchange config",
);
}
fs.writeFileSync(masterPrivFile, Buffer.from(exchangeMasterKey.eddsaPriv)); fs.writeFileSync(masterPrivFile, Buffer.from(exchangeMasterKey.eddsaPriv));
const cfgFilename = gc.testDir + `/exchange-${e.name}.conf`; const cfgFilename = testDir + `/exchange-${e.name}.conf`;
config.write(cfgFilename); config.write(cfgFilename, { excludeDefaults: true });
return new ExchangeService(gc, e, cfgFilename, exchangeMasterKey); return new ExchangeService(gc, e, cfgFilename, exchangeMasterKey);
} }
@ -1136,13 +1204,13 @@ export class ExchangeService implements ExchangeServiceInterface {
offeredCoins.forEach((cc) => offeredCoins.forEach((cc) =>
setCoin(config, cc(this.exchangeConfig.currency)), setCoin(config, cc(this.exchangeConfig.currency)),
); );
config.write(this.configFilename); config.write(this.configFilename, { excludeDefaults: true });
} }
addCoinConfigList(ccs: CoinConfig[]) { addCoinConfigList(ccs: CoinConfig[]) {
const config = Configuration.load(this.configFilename); const config = Configuration.load(this.configFilename);
ccs.forEach((cc) => setCoin(config, cc)); ccs.forEach((cc) => setCoin(config, cc));
config.write(this.configFilename); config.write(this.configFilename, { excludeDefaults: true });
} }
enableAgeRestrictions(maskStr: string) { enableAgeRestrictions(maskStr: string) {
@ -1153,7 +1221,7 @@ export class ExchangeService implements ExchangeServiceInterface {
"age_groups", "age_groups",
maskStr, maskStr,
); );
config.write(this.configFilename); config.write(this.configFilename, { excludeDefaults: true });
} }
get masterPub() { get masterPub() {
@ -1174,7 +1242,7 @@ export class ExchangeService implements ExchangeServiceInterface {
): Promise<void> { ): Promise<void> {
const config = Configuration.load(this.configFilename); const config = Configuration.load(this.configFilename);
await f(config); await f(config);
config.write(this.configFilename); config.write(this.configFilename, { excludeDefaults: true });
} }
async addBankAccount( async addBankAccount(
@ -1214,7 +1282,7 @@ export class ExchangeService implements ExchangeServiceInterface {
"password", "password",
exchangeBankAccount.accountPassword, exchangeBankAccount.accountPassword,
); );
config.write(this.configFilename); config.write(this.configFilename, { excludeDefaults: true });
} }
exchangeHttpProc: ProcessWrapper | undefined; exchangeHttpProc: ProcessWrapper | undefined;
@ -1475,15 +1543,26 @@ export class ExchangeService implements ExchangeServiceInterface {
); );
} }
async start(): Promise<void> { async dbinit() {
if (this.isRunning()) {
throw Error("exchange is already running");
}
await sh( await sh(
this.globalState, this.globalState,
"exchange-dbinit", "exchange-dbinit",
`taler-exchange-dbinit -c "${this.configFilename}"`, `taler-exchange-dbinit -c "${this.configFilename}"`,
); );
}
async start(
opts: { skipDbinit?: boolean; skipKeyup?: boolean } = {},
): Promise<void> {
if (this.isRunning()) {
throw Error("exchange is already running");
}
const skipDbinit = opts.skipDbinit ?? false;
if (!skipDbinit) {
await this.dbinit();
}
this.helperCryptoEddsaProc = this.globalState.spawnService( this.helperCryptoEddsaProc = this.globalState.spawnService(
"taler-exchange-secmod-eddsa", "taler-exchange-secmod-eddsa",
@ -1514,7 +1593,14 @@ export class ExchangeService implements ExchangeServiceInterface {
); );
await this.pingUntilAvailable(); await this.pingUntilAvailable();
await this.keyup();
const skipKeyup = opts.skipKeyup ?? false;
if (!skipKeyup) {
await this.keyup();
} else {
logger.info("skipping keyup");
}
} }
async pingUntilAvailable(): Promise<void> { async pingUntilAvailable(): Promise<void> {
@ -1530,6 +1616,7 @@ export interface MerchantConfig {
currency: string; currency: string;
httpPort: number; httpPort: number;
database: string; database: string;
overrideTestDir?: string;
} }
export interface PrivateOrderStatusQuery { export interface PrivateOrderStatusQuery {
@ -1775,8 +1862,13 @@ export interface CreateMerchantTippingReserveRequest {
} }
export class MerchantService implements MerchantServiceInterface { export class MerchantService implements MerchantServiceInterface {
static fromExistingConfig(gc: GlobalTestState, name: string) { static fromExistingConfig(
const cfgFilename = gc.testDir + `/merchant-${name}.conf`; gc: GlobalTestState,
name: string,
opts: { overridePath?: string },
) {
const testDir = opts.overridePath ?? gc.testDir;
const cfgFilename = testDir + `/merchant-${name}.conf`;
const config = Configuration.load(cfgFilename); const config = Configuration.load(cfgFilename);
const mc: MerchantConfig = { const mc: MerchantConfig = {
currency: config.getString("taler", "currency").required(), currency: config.getString("taler", "currency").required(),
@ -1846,13 +1938,24 @@ export class MerchantService implements MerchantServiceInterface {
} }
} }
async start(): Promise<void> { async dbinit() {
await runCommand( await runCommand(
this.globalState, this.globalState,
"merchant-dbinit", "merchant-dbinit",
"taler-merchant-dbinit", "taler-merchant-dbinit",
["-c", this.configFilename], ["-c", this.configFilename],
); );
}
/**
* Start the merchant,
*/
async start(opts: { skipDbinit?: boolean } = {}): Promise<void> {
const skipSetup = opts.skipDbinit ?? false;
if (!skipSetup) {
await this.dbinit();
}
this.proc = this.globalState.spawnService( this.proc = this.globalState.spawnService(
"taler-merchant-httpd", "taler-merchant-httpd",
@ -1871,11 +1974,12 @@ export class MerchantService implements MerchantServiceInterface {
gc: GlobalTestState, gc: GlobalTestState,
mc: MerchantConfig, mc: MerchantConfig,
): Promise<MerchantService> { ): Promise<MerchantService> {
const testDir = mc.overrideTestDir ?? gc.testDir;
const config = new Configuration(); const config = new Configuration();
config.setString("taler", "currency", mc.currency); config.setString("taler", "currency", mc.currency);
const cfgFilename = gc.testDir + `/merchant-${mc.name}.conf`; const cfgFilename = testDir + `/merchant-${mc.name}.conf`;
setTalerPaths(config, gc.testDir + "/talerhome"); setTalerPaths(config, testDir + "/talerhome");
config.setString("merchant", "serve", "tcp"); config.setString("merchant", "serve", "tcp");
config.setString("merchant", "port", `${mc.httpPort}`); config.setString("merchant", "port", `${mc.httpPort}`);
config.setString( config.setString(
@ -1884,7 +1988,7 @@ export class MerchantService implements MerchantServiceInterface {
"${TALER_DATA_HOME}/merchant/merchant.priv", "${TALER_DATA_HOME}/merchant/merchant.priv",
); );
config.setString("merchantdb-postgres", "config", mc.database); config.setString("merchantdb-postgres", "config", mc.database);
config.write(cfgFilename); config.write(cfgFilename, { excludeDefaults: true });
return new MerchantService(gc, mc, cfgFilename); return new MerchantService(gc, mc, cfgFilename);
} }
@ -1902,7 +2006,7 @@ export class MerchantService implements MerchantServiceInterface {
this.merchantConfig.currency, this.merchantConfig.currency,
); );
config.setString(`merchant-exchange-${e.name}`, "master_key", e.masterPub); config.setString(`merchant-exchange-${e.name}`, "master_key", e.masterPub);
config.write(this.configFilename); config.write(this.configFilename, { excludeDefaults: true });
} }
async addDefaultInstance(): Promise<void> { async addDefaultInstance(): Promise<void> {
@ -1935,14 +2039,8 @@ export class MerchantService implements MerchantServiceInterface {
name: instanceConfig.name, name: instanceConfig.name,
address: instanceConfig.address ?? {}, address: instanceConfig.address ?? {},
jurisdiction: instanceConfig.jurisdiction ?? {}, jurisdiction: instanceConfig.jurisdiction ?? {},
default_max_wire_fee: // FIXME: In some tests, we might want to make this configurable
instanceConfig.defaultMaxWireFee ?? use_stefan: true,
`${this.merchantConfig.currency}:1.0`,
default_wire_fee_amortization:
instanceConfig.defaultWireFeeAmortization ?? 3,
default_max_deposit_fee:
instanceConfig.defaultMaxDepositFee ??
`${this.merchantConfig.currency}:1.0`,
default_wire_transfer_delay: default_wire_transfer_delay:
instanceConfig.defaultWireTransferDelay ?? instanceConfig.defaultWireTransferDelay ??
Duration.toTalerProtocolDuration( Duration.toTalerProtocolDuration(
@ -1984,9 +2082,6 @@ export interface PartialMerchantInstanceConfig {
paytoUris: string[]; paytoUris: string[];
address?: unknown; address?: unknown;
jurisdiction?: unknown; jurisdiction?: unknown;
defaultMaxWireFee?: string;
defaultMaxDepositFee?: string;
defaultWireFeeAmortization?: number;
defaultWireTransferDelay?: TalerProtocolDuration; defaultWireTransferDelay?: TalerProtocolDuration;
defaultPayDelay?: TalerProtocolDuration; defaultPayDelay?: TalerProtocolDuration;
} }
@ -2029,9 +2124,7 @@ export interface MerchantInstanceConfig {
name: string; name: string;
address: unknown; address: unknown;
jurisdiction: unknown; jurisdiction: unknown;
default_max_wire_fee: string; use_stefan: boolean;
default_max_deposit_fee: string;
default_wire_fee_amortization: number;
default_wire_transfer_delay: TalerProtocolDuration; default_wire_transfer_delay: TalerProtocolDuration;
default_pay_delay: TalerProtocolDuration; default_pay_delay: TalerProtocolDuration;
} }
@ -2229,12 +2322,15 @@ export class WalletService {
} }
async pingUntilAvailable(): Promise<void> { async pingUntilAvailable(): Promise<void> {
let nextDelay = backoffStart();
while (1) { while (1) {
try { try {
await tryUnixConnect(this.socketPath); await tryUnixConnect(this.socketPath);
} catch (e) { } catch (e) {
logger.info(`connection attempt failed: ${e}`); logger.info(`wallet connection attempt failed: ${e}`);
await delayMs(200); logger.info(`waiting on wallet for ${nextDelay}ms`);
await delayMs(nextDelay);
nextDelay = backoffIncrement(nextDelay);
continue; continue;
} }
logger.info("connection to wallet-core succeeded"); logger.info("connection to wallet-core succeeded");

View File

@ -32,6 +32,7 @@ import {
NotificationType, NotificationType,
WalletNotification, WalletNotification,
TransactionMajorState, TransactionMajorState,
Logger,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { import {
BankAccessApi, BankAccessApi,
@ -49,18 +50,24 @@ import {
DbInfo, DbInfo,
ExchangeService, ExchangeService,
ExchangeServiceInterface, ExchangeServiceInterface,
FakebankService,
getPayto, getPayto,
GlobalTestState, GlobalTestState,
MerchantPrivateApi, MerchantPrivateApi,
MerchantService, MerchantService,
MerchantServiceInterface, MerchantServiceInterface,
setupDb, setupDb,
setupSharedDb,
WalletCli, WalletCli,
WalletClient, WalletClient,
WalletService, WalletService,
WithAuthorization, WithAuthorization,
} from "./harness.js"; } from "./harness.js";
import * as fs from "fs";
const logger = new Logger("helpers.ts");
/** /**
* @deprecated * @deprecated
*/ */
@ -94,6 +101,10 @@ export interface EnvOptions {
ageMaskSpec?: string; ageMaskSpec?: string;
mixedAgeRestriction?: boolean; mixedAgeRestriction?: boolean;
additionalExchangeConfig?(e: ExchangeService): void;
additionalMerchantConfig?(m: MerchantService): void;
additionalBankConfig?(b: BankService): void;
} }
/** /**
@ -204,6 +215,179 @@ export async function createSimpleTestkudosEnvironment(
}; };
} }
export async function useSharedTestkudosEnvironment(t: GlobalTestState) {
const coinConfig: CoinConfig[] = defaultCoinConfig.map((x) => x("TESTKUDOS"));
const sharedDir = `/tmp/taler-harness@${process.env.USER}`;
fs.mkdirSync(sharedDir, { recursive: true });
const db = await setupSharedDb(t);
let bank: FakebankService;
const prevSetupDone = fs.existsSync(sharedDir + "/setup-done");
logger.info(`previous setup done: ${prevSetupDone}`);
// Wallet has longer startup-time and no dependencies,
// so we start it rather early.
const walletStartProm = createWalletDaemonWithClient(t, { name: "wallet" })
if (fs.existsSync(sharedDir + "/bank.conf")) {
logger.info("reusing existing bank");
bank = BankService.fromExistingConfig(t, {
overridePath: sharedDir,
});
} else {
logger.info("creating new bank config");
bank = await BankService.create(t, {
allowRegistrations: true,
currency: "TESTKUDOS",
database: db.connStr,
httpPort: 8082,
overrideTestDir: sharedDir,
});
}
logger.info("setting up exchange");
const exchangeName = "testexchange-1";
const exchangeConfigFilename = sharedDir + `/exchange-${exchangeName}.conf`;
logger.info(`exchange config filename: ${exchangeConfigFilename}`);
let exchange: ExchangeService;
if (fs.existsSync(exchangeConfigFilename)) {
logger.info("reusing existing exchange config");
exchange = ExchangeService.fromExistingConfig(t, exchangeName, {
overridePath: sharedDir,
});
} else {
logger.info("creating new exchange config");
exchange = ExchangeService.create(t, {
name: "testexchange-1",
currency: "TESTKUDOS",
httpPort: 8081,
database: db.connStr,
overrideTestDir: sharedDir,
});
}
logger.info("setting up merchant");
let merchant: MerchantService;
const merchantName = "testmerchant-1";
const merchantConfigFilename = sharedDir + `/merchant-${merchantName}}`;
if (fs.existsSync(merchantConfigFilename)) {
merchant = MerchantService.fromExistingConfig(t, merchantName, {
overridePath: sharedDir,
});
} else {
merchant = await MerchantService.create(t, {
name: "testmerchant-1",
currency: "TESTKUDOS",
httpPort: 8083,
database: db.connStr,
overrideTestDir: sharedDir,
});
}
logger.info("creating bank account for exchange");
const exchangeBankAccount = await bank.createExchangeAccount(
"myexchange",
"x",
);
logger.info("creating exchange bank account");
await exchange.addBankAccount("1", exchangeBankAccount);
bank.setSuggestedExchange(exchange, exchangeBankAccount.accountPaytoUri);
exchange.addCoinConfigList(coinConfig);
merchant.addExchange(exchange);
logger.info("basic setup done, starting services");
if (!prevSetupDone) {
// Must be done sequentially, due to a concurrency
// issue in the *-dbinit tools.
await exchange.dbinit();
await merchant.dbinit();
}
const bankStart = async () => {
await bank.start();
await bank.pingUntilAvailable();
};
const exchangeStart = async () => {
await exchange.start({
skipDbinit: true,
skipKeyup: prevSetupDone,
});
await exchange.pingUntilAvailable();
};
const merchStart = async () => {
await merchant.start({
skipDbinit: true,
});
await merchant.pingUntilAvailable();
if (!prevSetupDone) {
await merchant.addInstance({
id: "default",
name: "Default Instance",
paytoUris: [getPayto("merchant-default")],
defaultWireTransferDelay: Duration.toTalerProtocolDuration(
Duration.fromSpec({ minutes: 1 }),
),
});
await merchant.addInstance({
id: "minst1",
name: "minst1",
paytoUris: [getPayto("minst1")],
defaultWireTransferDelay: Duration.toTalerProtocolDuration(
Duration.fromSpec({ minutes: 1 }),
),
});
}
};
await bankStart()
const res = await Promise.all([
exchangeStart(),
merchStart(),
undefined,
walletStartProm,
]);
const walletClient = res[3].walletClient;
const walletService = res[3].walletService;
fs.writeFileSync(sharedDir + "/setup-done", "OK");
logger.info("setup done!");
return {
commonDb: db,
exchange,
merchant,
walletClient,
walletService,
bank,
exchangeBankAccount,
};
}
/** /**
* Run a test case with a simple TESTKUDOS Taler environment, consisting * Run a test case with a simple TESTKUDOS Taler environment, consisting
* of one exchange, one bank and one merchant. * of one exchange, one bank and one merchant.
@ -246,6 +430,9 @@ export async function createSimpleTestkudosEnvironmentV2(
bank.setSuggestedExchange(exchange, exchangeBankAccount.accountPaytoUri); bank.setSuggestedExchange(exchange, exchangeBankAccount.accountPaytoUri);
if (opts.additionalBankConfig) {
opts.additionalBankConfig(bank);
}
await bank.start(); await bank.start();
await bank.pingUntilAvailable(); await bank.pingUntilAvailable();
@ -272,11 +459,17 @@ export async function createSimpleTestkudosEnvironmentV2(
exchange.addCoinConfigList(coinConfig); exchange.addCoinConfigList(coinConfig);
} }
if (opts.additionalExchangeConfig) {
opts.additionalExchangeConfig(exchange);
}
await exchange.start(); await exchange.start();
await exchange.pingUntilAvailable(); await exchange.pingUntilAvailable();
merchant.addExchange(exchange); merchant.addExchange(exchange);
if (opts.additionalMerchantConfig) {
opts.additionalMerchantConfig(merchant);
}
await merchant.start(); await merchant.start();
await merchant.pingUntilAvailable(); await merchant.pingUntilAvailable();
@ -336,7 +529,7 @@ export async function createWalletDaemonWithClient(
const walletClient = new WalletClient({ const walletClient = new WalletClient({
unixPath: walletService.socketPath, unixPath: walletService.socketPath,
onNotification(n) { onNotification(n) {
console.log("got notification", n); console.log(`got ${args.name} notification`, n);
if (args.handleNotification) { if (args.handleNotification) {
args.handleNotification(n); args.handleNotification(n);
} }
@ -610,6 +803,8 @@ export async function applyTimeTravel(
/** /**
* Make a simple payment and check that it succeeded. * Make a simple payment and check that it succeeded.
*
* @deprecated
*/ */
export async function makeTestPayment( export async function makeTestPayment(
t: GlobalTestState, t: GlobalTestState,

View File

@ -170,6 +170,39 @@ const sandcastleCli = testingCli.subcommand("sandcastleArgs", "sandcastle", {
help: "Subcommands for handling GNU Taler sandcastle deployments.", help: "Subcommands for handling GNU Taler sandcastle deployments.",
}); });
const configCli = testingCli.subcommand("configArgs", "config", {
help: "Subcommands for handling the Taler configuration.",
});
configCli.subcommand("show", "show").action(async (args) => {
const config = Configuration.load();
const cfgStr = config.stringify({
diagnostics: true,
});
console.log(cfgStr);
});
configCli
.subcommand("get", "get")
.requiredArgument("section", clk.STRING)
.requiredArgument("option", clk.STRING)
.flag("file", ["-f"])
.action(async (args) => {
const config = Configuration.load();
let res;
if (args.get.file) {
res = config.getString(args.get.section, args.get.option);
} else {
res = config.getPath(args.get.section, args.get.option);
}
if (res.isDefined()) {
console.log(res.value);
} else {
console.warn("not found");
process.exit(1);
}
});
const deploymentCli = testingCli.subcommand("deploymentArgs", "deployment", { const deploymentCli = testingCli.subcommand("deploymentArgs", "deployment", {
help: "Subcommands for handling GNU Taler deployments.", help: "Subcommands for handling GNU Taler deployments.",
}); });
@ -208,8 +241,10 @@ deploymentCli
const bankAccessApiClient = new BankAccessApiClient({ const bankAccessApiClient = new BankAccessApiClient({
baseUrl: args.tipTopup.bankAccessUrl, baseUrl: args.tipTopup.bankAccessUrl,
username: args.tipTopup.bankAccount, auth: {
password: args.tipTopup.bankPassword, username: args.tipTopup.bankAccount,
password: args.tipTopup.bankPassword,
},
allowHttp: true, allowHttp: true,
}); });
@ -219,10 +254,12 @@ deploymentCli
console.log("payto URI:", paytoUri); console.log("payto URI:", paytoUri);
const transactions = await bankAccessApiClient.getTransactions(); const transactions = await bankAccessApiClient.getTransactions(
args.tipTopup.bankAccount,
);
console.log("transactions:", j2s(transactions)); console.log("transactions:", j2s(transactions));
await bankAccessApiClient.createTransaction({ await bankAccessApiClient.createTransaction(args.tipTopup.bankAccount, {
amount, amount,
paytoUri, paytoUri,
}); });

View File

@ -17,12 +17,14 @@
/** /**
* Imports. * Imports.
*/ */
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { defaultCoinConfig } from "../harness/denomStructures.js"; import { defaultCoinConfig } from "../harness/denomStructures.js";
import { GlobalTestState, WalletCli } from "../harness/harness.js"; import { GlobalTestState } from "../harness/harness.js";
import { import {
createSimpleTestkudosEnvironment, createSimpleTestkudosEnvironmentV2,
withdrawViaBank, createWalletDaemonWithClient,
makeTestPayment, makeTestPaymentV2,
withdrawViaBankV2,
} from "../harness/helpers.js"; } from "../harness/helpers.js";
/** /**
@ -32,11 +34,11 @@ export async function runAgeRestrictionsMixedMerchantTest(t: GlobalTestState) {
// Set up test environment // Set up test environment
const { const {
wallet: walletOne, walletClient: walletOne,
bank, bank,
exchange, exchange,
merchant, merchant,
} = await createSimpleTestkudosEnvironment( } = await createSimpleTestkudosEnvironmentV2(
t, t,
defaultCoinConfig.map((x) => x("TESTKUDOS")), defaultCoinConfig.map((x) => x("TESTKUDOS")),
{ {
@ -45,20 +47,27 @@ export async function runAgeRestrictionsMixedMerchantTest(t: GlobalTestState) {
}, },
); );
const walletTwo = new WalletCli(t, "walletTwo"); const { walletClient: walletTwo } = await createWalletDaemonWithClient(t, {
const walletThree = new WalletCli(t, "walletThree"); name: "w2",
});
const { walletClient: walletThree } = await createWalletDaemonWithClient(t, {
name: "w3",
});
{ {
const wallet = walletOne; const walletClient = walletOne;
await withdrawViaBank(t, { const wres = await withdrawViaBankV2(t, {
wallet, walletClient,
bank, bank,
exchange, exchange,
amount: "TESTKUDOS:20", amount: "TESTKUDOS:20",
restrictAge: 13, restrictAge: 13,
}); });
await wres.withdrawalFinishedCond;
const order = { const order = {
summary: "Buy me!", summary: "Buy me!",
amount: "TESTKUDOS:5", amount: "TESTKUDOS:5",
@ -66,41 +75,46 @@ export async function runAgeRestrictionsMixedMerchantTest(t: GlobalTestState) {
minimum_age: 9, minimum_age: 9,
}; };
await makeTestPayment(t, { wallet, merchant, order }); await makeTestPaymentV2(t, { walletClient, merchant, order });
await wallet.runUntilDone(); await walletClient.call(
WalletApiOperation.TestingWaitTransactionsFinal,
{},
);
} }
{ {
const wallet = walletTwo; const wres = await withdrawViaBankV2(t, {
walletClient: walletTwo,
await withdrawViaBank(t, {
wallet,
bank, bank,
exchange, exchange,
amount: "TESTKUDOS:20", amount: "TESTKUDOS:20",
restrictAge: 13, restrictAge: 13,
}); });
await wres.withdrawalFinishedCond;
const order = { const order = {
summary: "Buy me!", summary: "Buy me!",
amount: "TESTKUDOS:5", amount: "TESTKUDOS:5",
fulfillment_url: "taler://fulfillment-success/thx", fulfillment_url: "taler://fulfillment-success/thx",
}; };
await makeTestPayment(t, { wallet, merchant, order }); await makeTestPaymentV2(t, { walletClient: walletTwo, merchant, order });
await wallet.runUntilDone(); await walletTwo.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
} }
{ {
const wallet = walletThree; const wres = await withdrawViaBankV2(t, {
walletClient: walletThree,
await withdrawViaBank(t, {
wallet,
bank, bank,
exchange, exchange,
amount: "TESTKUDOS:20", amount: "TESTKUDOS:20",
}); });
await wres.withdrawalFinishedCond;
const order = { const order = {
summary: "Buy me!", summary: "Buy me!",
amount: "TESTKUDOS:5", amount: "TESTKUDOS:5",
@ -108,8 +122,8 @@ export async function runAgeRestrictionsMixedMerchantTest(t: GlobalTestState) {
minimum_age: 9, minimum_age: 9,
}; };
await makeTestPayment(t, { wallet, merchant, order }); await makeTestPaymentV2(t, { walletClient: walletThree, merchant, order });
await wallet.runUntilDone(); await walletThree.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
} }
} }

View File

@ -17,23 +17,22 @@
/** /**
* Imports. * Imports.
*/ */
import {
GlobalTestState,
WalletCli,
ExchangeService,
setupDb,
BankService,
MerchantService,
getPayto,
} from "../harness/harness.js";
import { createEddsaKeyPair, encodeCrock } from "@gnu-taler/taler-util"; import { createEddsaKeyPair, encodeCrock } from "@gnu-taler/taler-util";
import { defaultCoinConfig } from "../harness/denomStructures.js";
import { import {
BankApi,
BankAccessApi, BankAccessApi,
BankApi,
CreditDebitIndicator, CreditDebitIndicator,
WireGatewayApiClient, WireGatewayApiClient,
} from "@gnu-taler/taler-wallet-core"; } from "@gnu-taler/taler-wallet-core";
import { defaultCoinConfig } from "../harness/denomStructures.js";
import {
BankService,
ExchangeService,
GlobalTestState,
MerchantService,
getPayto,
setupDb,
} from "../harness/harness.js";
/** /**
* Run test for basic, bank-integrated withdrawal. * Run test for basic, bank-integrated withdrawal.

View File

@ -17,13 +17,13 @@
/** /**
* Imports. * Imports.
*/ */
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { URL } from "url";
import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js"; import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
import { import {
createSimpleTestkudosEnvironment, createSimpleTestkudosEnvironmentV2,
withdrawViaBank, withdrawViaBankV2,
} from "../harness/helpers.js"; } from "../harness/helpers.js";
import { URL } from "url";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
/** /**
* Run test for the merchant's order lifecycle. * Run test for the merchant's order lifecycle.
@ -34,10 +34,15 @@ import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
export async function runClaimLoopTest(t: GlobalTestState) { export async function runClaimLoopTest(t: GlobalTestState) {
// Set up test environment // Set up test environment
const { wallet, bank, exchange, merchant } = const { walletClient, bank, exchange, merchant } =
await createSimpleTestkudosEnvironment(t); await createSimpleTestkudosEnvironmentV2(t);
await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); await withdrawViaBankV2(t, {
walletClient,
bank,
exchange,
amount: "TESTKUDOS:20",
});
// Set up order. // Set up order.
const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", { const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
@ -61,7 +66,7 @@ export async function runClaimLoopTest(t: GlobalTestState) {
// Make wallet claim the unpaid order. // Make wallet claim the unpaid order.
t.assertTrue(orderStatusBefore.order_status === "unpaid"); t.assertTrue(orderStatusBefore.order_status === "unpaid");
const talerPayUri = orderStatusBefore.taler_pay_uri; const talerPayUri = orderStatusBefore.taler_pay_uri;
await wallet.client.call(WalletApiOperation.PreparePayForUri, { await walletClient.call(WalletApiOperation.PreparePayForUri, {
talerPayUri, talerPayUri,
}); });

View File

@ -18,22 +18,29 @@
* Imports. * Imports.
*/ */
import { PreparePayResultType, TalerErrorCode } from "@gnu-taler/taler-util"; import { PreparePayResultType, TalerErrorCode } from "@gnu-taler/taler-util";
import { Wallet, WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js"; import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
import { import {
createSimpleTestkudosEnvironment, createSimpleTestkudosEnvironmentV2,
withdrawViaBank, withdrawViaBankV2,
} from "../harness/helpers.js"; } from "../harness/helpers.js";
export async function runDenomUnofferedTest(t: GlobalTestState) { export async function runDenomUnofferedTest(t: GlobalTestState) {
// Set up test environment // Set up test environment
const { wallet, bank, exchange, merchant } = const { walletClient, bank, exchange, merchant } =
await createSimpleTestkudosEnvironment(t); await createSimpleTestkudosEnvironmentV2(t);
// Withdraw digital cash into the wallet. // Withdraw digital cash into the wallet.
await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); const wres = await withdrawViaBankV2(t, {
walletClient,
bank,
exchange,
amount: "TESTKUDOS:20",
});
await wres.withdrawalFinishedCond;
// Make the exchange forget the denomination. // Make the exchange forget the denomination.
// Effectively we completely reset the exchange, // Effectively we completely reset the exchange,
@ -67,7 +74,7 @@ export async function runDenomUnofferedTest(t: GlobalTestState) {
// Make wallet pay for the order // Make wallet pay for the order
const preparePayResult = await wallet.client.call( const preparePayResult = await walletClient.call(
WalletApiOperation.PreparePayForUri, WalletApiOperation.PreparePayForUri,
{ {
talerPayUri: orderStatus.taler_pay_uri, talerPayUri: orderStatus.taler_pay_uri,
@ -78,11 +85,11 @@ export async function runDenomUnofferedTest(t: GlobalTestState) {
preparePayResult.status === PreparePayResultType.PaymentPossible, preparePayResult.status === PreparePayResultType.PaymentPossible,
); );
const confirmResp = await wallet.client.call(WalletApiOperation.ConfirmPay, { const confirmResp = await walletClient.call(WalletApiOperation.ConfirmPay, {
proposalId: preparePayResult.proposalId, proposalId: preparePayResult.proposalId,
}); });
const tx = await wallet.client.call(WalletApiOperation.GetTransactionById, { const tx = await walletClient.call(WalletApiOperation.GetTransactionById, {
transactionId: confirmResp.transactionId, transactionId: confirmResp.transactionId,
}); });
@ -96,21 +103,26 @@ export async function runDenomUnofferedTest(t: GlobalTestState) {
TalerErrorCode.MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_KEY_NOT_FOUND, TalerErrorCode.MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_KEY_NOT_FOUND,
); );
await wallet.client.call(WalletApiOperation.AddExchange, { await walletClient.call(WalletApiOperation.AddExchange, {
exchangeBaseUrl: exchange.baseUrl, exchangeBaseUrl: exchange.baseUrl,
forceUpdate: true, forceUpdate: true,
}); });
await wallet.client.call(WalletApiOperation.DeleteTransaction, { await walletClient.call(WalletApiOperation.DeleteTransaction, {
transactionId: confirmResp.transactionId, transactionId: confirmResp.transactionId,
}); });
// Now withdrawal should work again. // Now withdrawal should work again.
await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); await withdrawViaBankV2(t, {
walletClient,
bank,
exchange,
amount: "TESTKUDOS:20",
});
await wallet.runUntilDone(); await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
const txs = await wallet.client.call(WalletApiOperation.GetTransactions, {}); const txs = await walletClient.call(WalletApiOperation.GetTransactions, {});
console.log(JSON.stringify(txs, undefined, 2)); console.log(JSON.stringify(txs, undefined, 2));
} }

View File

@ -30,14 +30,15 @@ import {
depositCoin, depositCoin,
downloadExchangeInfo, downloadExchangeInfo,
findDenomOrThrow, findDenomOrThrow,
refreshCoin,
SynchronousCryptoWorkerFactoryPlain, SynchronousCryptoWorkerFactoryPlain,
topupReserveWithDemobank, topupReserveWithDemobank,
Wallet, Wallet,
withdrawCoin, withdrawCoin,
} from "@gnu-taler/taler-wallet-core"; } from "@gnu-taler/taler-wallet-core";
import { GlobalTestState } from "../harness/harness.js"; import { GlobalTestState } from "../harness/harness.js";
import { createSimpleTestkudosEnvironment } from "../harness/helpers.js"; import {
createSimpleTestkudosEnvironmentV2,
} from "../harness/helpers.js";
/** /**
* Run test for basic, bank-integrated withdrawal and payment. * Run test for basic, bank-integrated withdrawal and payment.
@ -45,7 +46,7 @@ import { createSimpleTestkudosEnvironment } from "../harness/helpers.js";
export async function runExchangeDepositTest(t: GlobalTestState) { export async function runExchangeDepositTest(t: GlobalTestState) {
// Set up test environment // Set up test environment
const { bank, exchange } = await createSimpleTestkudosEnvironment(t); const { bank, exchange } = await createSimpleTestkudosEnvironmentV2(t);
const http = createPlatformHttpLib({ const http = createPlatformHttpLib({
allowHttp: true, allowHttp: true,

View File

@ -19,18 +19,18 @@
*/ */
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { import {
GlobalTestState,
BankService, BankService,
ExchangeService, ExchangeService,
GlobalTestState,
MerchantService, MerchantService,
setupDb,
WalletCli,
getPayto, getPayto,
setupDb,
} from "../harness/harness.js"; } from "../harness/harness.js";
import { import {
withdrawViaBank, SimpleTestEnvironmentNg,
makeTestPayment, createWalletDaemonWithClient,
SimpleTestEnvironment, makeTestPaymentV2,
withdrawViaBankV2,
} from "../harness/helpers.js"; } from "../harness/helpers.js";
/** /**
@ -39,7 +39,7 @@ import {
*/ */
export async function createMyTestkudosEnvironment( export async function createMyTestkudosEnvironment(
t: GlobalTestState, t: GlobalTestState,
): Promise<SimpleTestEnvironment> { ): Promise<SimpleTestEnvironmentNg> {
const db = await setupDb(t); const db = await setupDb(t);
const bank = await BankService.create(t, { const bank = await BankService.create(t, {
@ -147,13 +147,19 @@ export async function createMyTestkudosEnvironment(
console.log("setup done!"); console.log("setup done!");
const wallet = new WalletCli(t); const { walletClient, walletService } = await createWalletDaemonWithClient(
t,
{
name: "w1",
},
);
return { return {
commonDb: db, commonDb: db,
exchange, exchange,
merchant, merchant,
wallet, walletClient,
walletService,
bank, bank,
exchangeBankAccount, exchangeBankAccount,
}; };
@ -165,19 +171,21 @@ export async function createMyTestkudosEnvironment(
export async function runFeeRegressionTest(t: GlobalTestState) { export async function runFeeRegressionTest(t: GlobalTestState) {
// Set up test environment // Set up test environment
const { wallet, bank, exchange, merchant } = const { walletClient, bank, exchange, merchant } =
await createMyTestkudosEnvironment(t); await createMyTestkudosEnvironment(t);
// Withdraw digital cash into the wallet. // Withdraw digital cash into the wallet.
await withdrawViaBank(t, { const wres = await withdrawViaBankV2(t, {
wallet, walletClient,
bank, bank,
exchange, exchange,
amount: "TESTKUDOS:1.92", amount: "TESTKUDOS:1.92",
}); });
const coins = await wallet.client.call(WalletApiOperation.DumpCoins, {}); await wres.withdrawalFinishedCond;
const coins = await walletClient.call(WalletApiOperation.DumpCoins, {});
// Make sure we really withdraw one 0.64 and one 1.28 coin. // Make sure we really withdraw one 0.64 and one 1.28 coin.
t.assertTrue(coins.coins.length === 2); t.assertTrue(coins.coins.length === 2);
@ -188,11 +196,11 @@ export async function runFeeRegressionTest(t: GlobalTestState) {
fulfillment_url: "taler://fulfillment-success/thx", fulfillment_url: "taler://fulfillment-success/thx",
}; };
await makeTestPayment(t, { wallet, merchant, order }); await makeTestPaymentV2(t, { walletClient, merchant, order });
await wallet.runUntilDone(); await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
const txs = await wallet.client.call(WalletApiOperation.GetTransactions, {}); const txs = await walletClient.call(WalletApiOperation.GetTransactions, {});
t.assertAmountEquals(txs.transactions[1].amountEffective, "TESTKUDOS:1.30"); t.assertAmountEquals(txs.transactions[1].amountEffective, "TESTKUDOS:1.30");
console.log(txs); console.log(txs);
} }

View File

@ -20,7 +20,7 @@
import { j2s } from "@gnu-taler/taler-util"; import { j2s } from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { GlobalTestState } from "../harness/harness.js"; import { GlobalTestState } from "../harness/harness.js";
import { createSimpleTestkudosEnvironment } from "../harness/helpers.js"; import { createSimpleTestkudosEnvironmentV2 } from "../harness/helpers.js";
/** /**
* Run test for forced denom/coin selection. * Run test for forced denom/coin selection.
@ -28,14 +28,14 @@ import { createSimpleTestkudosEnvironment } from "../harness/helpers.js";
export async function runForcedSelectionTest(t: GlobalTestState) { export async function runForcedSelectionTest(t: GlobalTestState) {
// Set up test environment // Set up test environment
const { wallet, bank, exchange, merchant } = const { walletClient, bank, exchange, merchant } =
await createSimpleTestkudosEnvironment(t); await createSimpleTestkudosEnvironmentV2(t);
await wallet.client.call(WalletApiOperation.AddExchange, { await walletClient.call(WalletApiOperation.AddExchange, {
exchangeBaseUrl: exchange.baseUrl, exchangeBaseUrl: exchange.baseUrl,
}); });
await wallet.client.call(WalletApiOperation.WithdrawTestBalance, { await walletClient.call(WalletApiOperation.WithdrawTestBalance, {
exchangeBaseUrl: exchange.baseUrl, exchangeBaseUrl: exchange.baseUrl,
amount: "TESTKUDOS:10", amount: "TESTKUDOS:10",
bankAccessApiBaseUrl: bank.bankAccessApiBaseUrl, bankAccessApiBaseUrl: bank.bankAccessApiBaseUrl,
@ -49,13 +49,13 @@ export async function runForcedSelectionTest(t: GlobalTestState) {
}, },
}); });
await wallet.runUntilDone(); await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
const coinDump = await wallet.client.call(WalletApiOperation.DumpCoins, {}); const coinDump = await walletClient.call(WalletApiOperation.DumpCoins, {});
console.log(coinDump); console.log(coinDump);
t.assertDeepEqual(coinDump.coins.length, 3); t.assertDeepEqual(coinDump.coins.length, 3);
const payResp = await wallet.client.call(WalletApiOperation.TestPay, { const payResp = await walletClient.call(WalletApiOperation.TestPay, {
amount: "TESTKUDOS:3", amount: "TESTKUDOS:3",
merchantBaseUrl: merchant.makeInstanceBaseUrl(), merchantBaseUrl: merchant.makeInstanceBaseUrl(),
summary: "bla", summary: "bla",

View File

@ -19,7 +19,6 @@
*/ */
import { GlobalTestState } from "../harness/harness.js"; import { GlobalTestState } from "../harness/harness.js";
import { import {
NexusUserBundle,
LibeufinNexusApi, LibeufinNexusApi,
LibeufinNexusService, LibeufinNexusService,
LibeufinSandboxService, LibeufinSandboxService,

View File

@ -23,7 +23,6 @@ import {
NexusUserBundle, NexusUserBundle,
launchLibeufinServices, launchLibeufinServices,
LibeufinNexusApi, LibeufinNexusApi,
LibeufinCli,
} from "../harness/libeufin.js"; } from "../harness/libeufin.js";
/** /**

View File

@ -67,12 +67,10 @@ export async function runMerchantInstancesUrlsTest(t: GlobalTestState) {
await clientForDefault.createInstance({ await clientForDefault.createInstance({
id: "default", id: "default",
address: {}, address: {},
default_max_deposit_fee: "TESTKUDOS:1", use_stefan: true,
default_max_wire_fee: "TESTKUDOS:1",
default_pay_delay: Duration.toTalerProtocolDuration( default_pay_delay: Duration.toTalerProtocolDuration(
Duration.fromSpec({ seconds: 60 }), Duration.fromSpec({ seconds: 60 }),
), ),
default_wire_fee_amortization: 1,
default_wire_transfer_delay: Duration.toTalerProtocolDuration( default_wire_transfer_delay: Duration.toTalerProtocolDuration(
Duration.fromSpec({ seconds: 60 }), Duration.fromSpec({ seconds: 60 }),
), ),
@ -92,12 +90,10 @@ export async function runMerchantInstancesUrlsTest(t: GlobalTestState) {
await clientForDefault.createInstance({ await clientForDefault.createInstance({
id: "myinst", id: "myinst",
address: {}, address: {},
default_max_deposit_fee: "TESTKUDOS:1",
default_max_wire_fee: "TESTKUDOS:1",
default_pay_delay: Duration.toTalerProtocolDuration( default_pay_delay: Duration.toTalerProtocolDuration(
Duration.fromSpec({ seconds: 60 }), Duration.fromSpec({ seconds: 60 }),
), ),
default_wire_fee_amortization: 1, use_stefan: true,
default_wire_transfer_delay: Duration.toTalerProtocolDuration( default_wire_transfer_delay: Duration.toTalerProtocolDuration(
Duration.fromSpec({ seconds: 60 }), Duration.fromSpec({ seconds: 60 }),
), ),

View File

@ -17,20 +17,20 @@
/** /**
* Imports. * Imports.
*/ */
import {
ConfirmPayResultType,
PreparePayResultType,
URL,
codecForMerchantOrderStatusUnpaid,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import axiosImp from "axios";
import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js"; import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
import { import {
createSimpleTestkudosEnvironment, createSimpleTestkudosEnvironmentV2,
withdrawViaBank, withdrawViaBankV2
} from "../harness/helpers.js"; } from "../harness/helpers.js";
import {
PreparePayResultType,
codecForMerchantOrderStatusUnpaid,
ConfirmPayResultType,
URL,
} from "@gnu-taler/taler-util";
import axiosImp from "axios";
const axios = axiosImp.default; const axios = axiosImp.default;
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
/** /**
* Run test for basic, bank-integrated withdrawal. * Run test for basic, bank-integrated withdrawal.
@ -38,12 +38,19 @@ import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
export async function runMerchantLongpollingTest(t: GlobalTestState) { export async function runMerchantLongpollingTest(t: GlobalTestState) {
// Set up test environment // Set up test environment
const { wallet, bank, exchange, merchant } = const { walletClient, bank, exchange, merchant } =
await createSimpleTestkudosEnvironment(t); await createSimpleTestkudosEnvironmentV2(t);
// Withdraw digital cash into the wallet. // Withdraw digital cash into the wallet.
await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); const wres = await withdrawViaBankV2(t, {
walletClient,
bank,
exchange,
amount: "TESTKUDOS:20",
});
await wres.withdrawalFinishedCond;
/** /**
* ========================================================================= * =========================================================================
@ -113,7 +120,7 @@ export async function runMerchantLongpollingTest(t: GlobalTestState) {
* ========================================================================= * =========================================================================
*/ */
let preparePayResp = await wallet.client.call( let preparePayResp = await walletClient.call(
WalletApiOperation.PreparePayForUri, WalletApiOperation.PreparePayForUri,
{ {
talerPayUri: pubUnpaidStatus.taler_pay_uri, talerPayUri: pubUnpaidStatus.taler_pay_uri,
@ -148,7 +155,7 @@ export async function runMerchantLongpollingTest(t: GlobalTestState) {
publicOrderStatusResp.data, publicOrderStatusResp.data,
); );
const confirmPayRes = await wallet.client.call( const confirmPayRes = await walletClient.call(
WalletApiOperation.ConfirmPay, WalletApiOperation.ConfirmPay,
{ {
proposalId: proposalId, proposalId: proposalId,

View File

@ -24,7 +24,6 @@ import {
} from "../harness/helpers.js"; } from "../harness/helpers.js";
import { import {
FaultInjectionRequestContext, FaultInjectionRequestContext,
FaultInjectionResponseContext,
} from "../harness/faultInjection.js"; } from "../harness/faultInjection.js";
import { import {
codecForMerchantOrderStatusUnpaid, codecForMerchantOrderStatusUnpaid,

View File

@ -17,18 +17,17 @@
/** /**
* Imports. * Imports.
*/ */
import { PreparePayResultType, TalerErrorCode } from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { import {
GlobalTestState, GlobalTestState,
MerchantPrivateApi, MerchantPrivateApi,
WalletCli, WalletCli,
} from "../harness/harness.js"; } from "../harness/harness.js";
import { import {
createSimpleTestkudosEnvironment, createSimpleTestkudosEnvironmentV2,
withdrawViaBank, withdrawViaBankV2,
} from "../harness/helpers.js"; } from "../harness/helpers.js";
import { PreparePayResultType } from "@gnu-taler/taler-util";
import { TalerErrorCode } from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
/** /**
* Run test for basic, bank-integrated withdrawal. * Run test for basic, bank-integrated withdrawal.
@ -36,14 +35,19 @@ import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
export async function runPaymentClaimTest(t: GlobalTestState) { export async function runPaymentClaimTest(t: GlobalTestState) {
// Set up test environment // Set up test environment
const { wallet, bank, exchange, merchant } = const { walletClient, bank, exchange, merchant } =
await createSimpleTestkudosEnvironment(t); await createSimpleTestkudosEnvironmentV2(t);
const walletTwo = new WalletCli(t, "two"); const walletTwo = new WalletCli(t, "two");
// Withdraw digital cash into the wallet. // Withdraw digital cash into the wallet.
await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); await withdrawViaBankV2(t, {
walletClient,
bank,
exchange,
amount: "TESTKUDOS:20",
});
// Set up order. // Set up order.
@ -65,7 +69,7 @@ export async function runPaymentClaimTest(t: GlobalTestState) {
// Make wallet pay for the order // Make wallet pay for the order
const preparePayResult = await wallet.client.call( const preparePayResult = await walletClient.call(
WalletApiOperation.PreparePayForUri, WalletApiOperation.PreparePayForUri,
{ {
talerPayUri, talerPayUri,
@ -82,7 +86,7 @@ export async function runPaymentClaimTest(t: GlobalTestState) {
}); });
}); });
await wallet.client.call(WalletApiOperation.ConfirmPay, { await walletClient.call(WalletApiOperation.ConfirmPay, {
proposalId: preparePayResult.proposalId, proposalId: preparePayResult.proposalId,
}); });

View File

@ -17,11 +17,12 @@
/** /**
* Imports. * Imports.
*/ */
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { GlobalTestState } from "../harness/harness.js"; import { GlobalTestState } from "../harness/harness.js";
import { import {
createSimpleTestkudosEnvironment, createSimpleTestkudosEnvironmentV2,
withdrawViaBank, makeTestPaymentV2,
makeTestPayment, withdrawViaBankV2,
} from "../harness/helpers.js"; } from "../harness/helpers.js";
/** /**
@ -30,12 +31,19 @@ import {
export async function runPaymentForgettableTest(t: GlobalTestState) { export async function runPaymentForgettableTest(t: GlobalTestState) {
// Set up test environment // Set up test environment
const { wallet, bank, exchange, merchant } = const { walletClient, bank, exchange, merchant } =
await createSimpleTestkudosEnvironment(t); await createSimpleTestkudosEnvironmentV2(t);
// Withdraw digital cash into the wallet. // Withdraw digital cash into the wallet.
await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); const wres = await withdrawViaBankV2(t, {
walletClient,
bank,
exchange,
amount: "TESTKUDOS:20",
});
await wres.withdrawalFinishedCond;
{ {
const order = { const order = {
@ -50,7 +58,7 @@ export async function runPaymentForgettableTest(t: GlobalTestState) {
}, },
}; };
await makeTestPayment(t, { wallet, merchant, order }); await makeTestPaymentV2(t, { walletClient, merchant, order });
} }
console.log("testing with forgettable field without hash"); console.log("testing with forgettable field without hash");
@ -68,10 +76,10 @@ export async function runPaymentForgettableTest(t: GlobalTestState) {
}, },
}; };
await makeTestPayment(t, { wallet, merchant, order }); await makeTestPaymentV2(t, { walletClient, merchant, order });
} }
await wallet.runUntilDone(); await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
} }
runPaymentForgettableTest.suites = ["wallet", "merchant"]; runPaymentForgettableTest.suites = ["wallet", "merchant"];

View File

@ -17,13 +17,13 @@
/** /**
* Imports. * Imports.
*/ */
import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
import {
createSimpleTestkudosEnvironment,
withdrawViaBank,
} from "../harness/helpers.js";
import { PreparePayResultType } from "@gnu-taler/taler-util"; import { PreparePayResultType } from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
import {
createSimpleTestkudosEnvironmentV2,
withdrawViaBankV2,
} from "../harness/helpers.js";
/** /**
* Test the wallet-core payment API, especially that repeated operations * Test the wallet-core payment API, especially that repeated operations
@ -32,12 +32,17 @@ import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
export async function runPaymentIdempotencyTest(t: GlobalTestState) { export async function runPaymentIdempotencyTest(t: GlobalTestState) {
// Set up test environment // Set up test environment
const { wallet, bank, exchange, merchant } = const { walletClient, bank, exchange, merchant } =
await createSimpleTestkudosEnvironment(t); await createSimpleTestkudosEnvironmentV2(t);
// Withdraw digital cash into the wallet. // Withdraw digital cash into the wallet.
await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); await withdrawViaBankV2(t, {
walletClient,
bank,
exchange,
amount: "TESTKUDOS:20",
});
// Set up order. // Set up order.
@ -59,14 +64,14 @@ export async function runPaymentIdempotencyTest(t: GlobalTestState) {
// Make wallet pay for the order // Make wallet pay for the order
const preparePayResult = await wallet.client.call( const preparePayResult = await walletClient.call(
WalletApiOperation.PreparePayForUri, WalletApiOperation.PreparePayForUri,
{ {
talerPayUri: orderStatus.taler_pay_uri, talerPayUri: orderStatus.taler_pay_uri,
}, },
); );
const preparePayResultRep = await wallet.client.call( const preparePayResultRep = await walletClient.call(
WalletApiOperation.PreparePayForUri, WalletApiOperation.PreparePayForUri,
{ {
talerPayUri: orderStatus.taler_pay_uri, talerPayUri: orderStatus.taler_pay_uri,
@ -82,7 +87,7 @@ export async function runPaymentIdempotencyTest(t: GlobalTestState) {
const proposalId = preparePayResult.proposalId; const proposalId = preparePayResult.proposalId;
const confirmPayResult = await wallet.client.call( const confirmPayResult = await walletClient.call(
WalletApiOperation.ConfirmPay, WalletApiOperation.ConfirmPay,
{ {
proposalId: proposalId, proposalId: proposalId,
@ -91,7 +96,7 @@ export async function runPaymentIdempotencyTest(t: GlobalTestState) {
console.log("confirm pay result", confirmPayResult); console.log("confirm pay result", confirmPayResult);
await wallet.runUntilDone(); await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
// Check if payment was successful. // Check if payment was successful.
@ -101,7 +106,7 @@ export async function runPaymentIdempotencyTest(t: GlobalTestState) {
t.assertTrue(orderStatus.order_status === "paid"); t.assertTrue(orderStatus.order_status === "paid");
const preparePayResultAfter = await wallet.client.call( const preparePayResultAfter = await walletClient.call(
WalletApiOperation.PreparePayForUri, WalletApiOperation.PreparePayForUri,
{ {
talerPayUri, talerPayUri,

View File

@ -17,21 +17,17 @@
/** /**
* Imports. * Imports.
*/ */
import {
ConfirmPayResultType,
PreparePayResultType,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js"; import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
import { import {
createSimpleTestkudosEnvironmentV2, createSimpleTestkudosEnvironmentV2,
withdrawViaBankV2,
makeTestPaymentV2,
createWalletDaemonWithClient, createWalletDaemonWithClient,
withdrawViaBankV2,
} from "../harness/helpers.js"; } from "../harness/helpers.js";
import {
ConfirmPayResultType,
PreparePayResultType,
j2s,
parsePayUri,
stringifyPayUri,
} from "@gnu-taler/taler-util";
/** /**
* Run test for basic, bank-integrated withdrawal and payment. * Run test for basic, bank-integrated withdrawal and payment.

View File

@ -17,13 +17,12 @@
/** /**
* Imports. * Imports.
*/ */
import { ConfirmPayResultType, Duration, PreparePayResultType, TalerProtocolTimestamp } from "@gnu-taler/taler-util"; import { ConfirmPayResultType, Duration, PreparePayResultType } from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js"; import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
import { import {
createSimpleTestkudosEnvironment, createSimpleTestkudosEnvironmentV2,
withdrawViaBank, withdrawViaBankV2
makeTestPayment,
} from "../harness/helpers.js"; } from "../harness/helpers.js";
/** /**
@ -32,8 +31,8 @@ import {
export async function runPaymentTemplateTest(t: GlobalTestState) { export async function runPaymentTemplateTest(t: GlobalTestState) {
// Set up test environment // Set up test environment
const { wallet, bank, exchange, merchant } = const { walletClient, bank, exchange, merchant } =
await createSimpleTestkudosEnvironment(t); await createSimpleTestkudosEnvironmentV2(t);
await MerchantPrivateApi.createTemplate(merchant, "default", { await MerchantPrivateApi.createTemplate(merchant, "default", {
template_id: "template1", template_id: "template1",
@ -51,11 +50,11 @@ export async function runPaymentTemplateTest(t: GlobalTestState) {
// Withdraw digital cash into the wallet. // Withdraw digital cash into the wallet.
await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); await withdrawViaBankV2(t, { walletClient, bank, exchange, amount: "TESTKUDOS:20" });
// Request a template payment // Request a template payment
const preparePayResult = await wallet.client.call( const preparePayResult = await walletClient.call(
WalletApiOperation.PreparePayForTemplate, WalletApiOperation.PreparePayForTemplate,
{ {
talerPayTemplateUri: `taler+http://pay-template/localhost:${merchant.port}/template1?amount=TESTKUDOS:1`, talerPayTemplateUri: `taler+http://pay-template/localhost:${merchant.port}/template1?amount=TESTKUDOS:1`,
@ -71,7 +70,7 @@ export async function runPaymentTemplateTest(t: GlobalTestState) {
// Pay for it // Pay for it
const r2 = await wallet.client.call(WalletApiOperation.ConfirmPay, { const r2 = await walletClient.call(WalletApiOperation.ConfirmPay, {
proposalId: preparePayResult.proposalId, proposalId: preparePayResult.proposalId,
}); });
@ -88,8 +87,7 @@ export async function runPaymentTemplateTest(t: GlobalTestState) {
); );
t.assertTrue(orderStatus.order_status === "paid"); t.assertTrue(orderStatus.order_status === "paid");
await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
await wallet.runUntilDone();
} }
runPaymentTemplateTest.suites = ["wallet"]; runPaymentTemplateTest.suites = ["wallet"];

View File

@ -20,9 +20,9 @@
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { GlobalTestState } from "../harness/harness.js"; import { GlobalTestState } from "../harness/harness.js";
import { import {
createSimpleTestkudosEnvironment, createSimpleTestkudosEnvironmentV2,
withdrawViaBank, withdrawViaBankV2,
makeTestPayment, makeTestPaymentV2,
} from "../harness/helpers.js"; } from "../harness/helpers.js";
import { TransactionMajorState } from "@gnu-taler/taler-util"; import { TransactionMajorState } from "@gnu-taler/taler-util";
@ -33,19 +33,19 @@ import { TransactionMajorState } from "@gnu-taler/taler-util";
export async function runPaymentZeroTest(t: GlobalTestState) { export async function runPaymentZeroTest(t: GlobalTestState) {
// Set up test environment // Set up test environment
const { wallet, bank, exchange, merchant } = const { walletClient, bank, exchange, merchant } =
await createSimpleTestkudosEnvironment(t); await createSimpleTestkudosEnvironmentV2(t);
// First, make a "free" payment when we don't even have // First, make a "free" payment when we don't even have
// any money in the // any money in the
// Withdraw digital cash into the wallet. // Withdraw digital cash into the wallet.
await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); await withdrawViaBankV2(t, { walletClient, bank, exchange, amount: "TESTKUDOS:20" });
await wallet.runUntilDone(); await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
await makeTestPayment(t, { await makeTestPaymentV2(t, {
wallet, walletClient,
merchant, merchant,
order: { order: {
summary: "I am free!", summary: "I am free!",
@ -54,9 +54,9 @@ export async function runPaymentZeroTest(t: GlobalTestState) {
}, },
}); });
await wallet.runUntilDone(); await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
const transactions = await wallet.client.call( const transactions = await walletClient.call(
WalletApiOperation.GetTransactions, WalletApiOperation.GetTransactions,
{}, {},
); );

View File

@ -18,19 +18,23 @@
* Imports. * Imports.
*/ */
import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js"; import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
import {
createSimpleTestkudosEnvironment,
withdrawViaBank,
} from "../harness/helpers.js";
import { import {
PreparePayResultType, PreparePayResultType,
codecForMerchantOrderStatusUnpaid, codecForMerchantOrderStatusUnpaid,
ConfirmPayResultType, ConfirmPayResultType,
URL, URL,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import axiosImp from "axios";
const axios = axiosImp.default;
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import {
createSimpleTestkudosEnvironmentV2,
withdrawViaBankV2,
} from "../harness/helpers.js";
import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
const httpLib = createPlatformHttpLib({
allowHttp: true,
enableThrottling: false,
});
/** /**
* Run test for basic, bank-integrated withdrawal. * Run test for basic, bank-integrated withdrawal.
@ -38,12 +42,17 @@ import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
export async function runPaywallFlowTest(t: GlobalTestState) { export async function runPaywallFlowTest(t: GlobalTestState) {
// Set up test environment // Set up test environment
const { wallet, bank, exchange, merchant } = const { walletClient, bank, exchange, merchant } =
await createSimpleTestkudosEnvironment(t); await createSimpleTestkudosEnvironmentV2(t);
// Withdraw digital cash into the wallet. // Withdraw digital cash into the wallet.
await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); await withdrawViaBankV2(t, {
walletClient,
bank,
exchange,
amount: "TESTKUDOS:20",
});
/** /**
* ========================================================================= * =========================================================================
@ -77,9 +86,7 @@ export async function runPaywallFlowTest(t: GlobalTestState) {
t.assertTrue(orderStatus.already_paid_order_id === undefined); t.assertTrue(orderStatus.already_paid_order_id === undefined);
let publicOrderStatusUrl = new URL(orderStatus.order_status_url); let publicOrderStatusUrl = new URL(orderStatus.order_status_url);
let publicOrderStatusResp = await axios.get(publicOrderStatusUrl.href, { let publicOrderStatusResp = await httpLib.fetch(publicOrderStatusUrl.href);
validateStatus: () => true,
});
if (publicOrderStatusResp.status != 402) { if (publicOrderStatusResp.status != 402) {
throw Error( throw Error(
@ -88,12 +95,12 @@ export async function runPaywallFlowTest(t: GlobalTestState) {
} }
let pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode( let pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode(
publicOrderStatusResp.data, publicOrderStatusResp.json(),
); );
console.log(pubUnpaidStatus); console.log(pubUnpaidStatus);
let preparePayResp = await wallet.client.call( let preparePayResp = await walletClient.call(
WalletApiOperation.PreparePayForUri, WalletApiOperation.PreparePayForUri,
{ {
talerPayUri: pubUnpaidStatus.taler_pay_uri, talerPayUri: pubUnpaidStatus.taler_pay_uri,
@ -105,10 +112,8 @@ export async function runPaywallFlowTest(t: GlobalTestState) {
const proposalId = preparePayResp.proposalId; const proposalId = preparePayResp.proposalId;
console.log("requesting", publicOrderStatusUrl.href); console.log("requesting", publicOrderStatusUrl.href);
publicOrderStatusResp = await axios.get(publicOrderStatusUrl.href, { publicOrderStatusResp = await httpLib.fetch(publicOrderStatusUrl.href);
validateStatus: () => true, console.log("response body", publicOrderStatusResp.json());
});
console.log("response body", publicOrderStatusResp.data);
if (publicOrderStatusResp.status != 402) { if (publicOrderStatusResp.status != 402) {
throw Error( throw Error(
`expected status 402 (after claiming), but got ${publicOrderStatusResp.status}`, `expected status 402 (after claiming), but got ${publicOrderStatusResp.status}`,
@ -116,26 +121,20 @@ export async function runPaywallFlowTest(t: GlobalTestState) {
} }
pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode( pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode(
publicOrderStatusResp.data, publicOrderStatusResp.json(),
); );
const confirmPayRes = await wallet.client.call( const confirmPayRes = await walletClient.call(WalletApiOperation.ConfirmPay, {
WalletApiOperation.ConfirmPay, proposalId: proposalId,
{
proposalId: proposalId,
},
);
t.assertTrue(confirmPayRes.type === ConfirmPayResultType.Done);
publicOrderStatusResp = await axios.get(publicOrderStatusUrl.href, {
validateStatus: () => true,
}); });
console.log(publicOrderStatusResp.data); t.assertTrue(confirmPayRes.type === ConfirmPayResultType.Done);
publicOrderStatusResp = await httpLib.fetch(publicOrderStatusUrl.href);
console.log(publicOrderStatusResp.json());
if (publicOrderStatusResp.status != 200) { if (publicOrderStatusResp.status != 200) {
console.log(publicOrderStatusResp.data); console.log(publicOrderStatusResp.json());
throw Error( throw Error(
`expected status 200 (after paying), but got ${publicOrderStatusResp.status}`, `expected status 200 (after paying), but got ${publicOrderStatusResp.status}`,
); );
@ -158,7 +157,7 @@ export async function runPaywallFlowTest(t: GlobalTestState) {
// Pay with new taler://pay URI, which should // Pay with new taler://pay URI, which should
// have the new session ID! // have the new session ID!
// Wallet should now automatically re-play payment. // Wallet should now automatically re-play payment.
preparePayResp = await wallet.client.call( preparePayResp = await walletClient.call(
WalletApiOperation.PreparePayForUri, WalletApiOperation.PreparePayForUri,
{ {
talerPayUri: talerPayUriOne, talerPayUri: talerPayUriOne,
@ -199,7 +198,7 @@ export async function runPaywallFlowTest(t: GlobalTestState) {
// Here the re-purchase detection should kick in, // Here the re-purchase detection should kick in,
// and the wallet should re-pay for the old order // and the wallet should re-pay for the old order
// under the new session ID (mysession-three). // under the new session ID (mysession-three).
preparePayResp = await wallet.client.call( preparePayResp = await walletClient.call(
WalletApiOperation.PreparePayForUri, WalletApiOperation.PreparePayForUri,
{ {
talerPayUri: orderStatus.taler_pay_uri, talerPayUri: orderStatus.taler_pay_uri,
@ -232,19 +231,17 @@ export async function runPaywallFlowTest(t: GlobalTestState) {
console.log("requesting public status", publicOrderStatusUrl); console.log("requesting public status", publicOrderStatusUrl);
// Ask the order status of the claimed-but-unpaid order // Ask the order status of the claimed-but-unpaid order
publicOrderStatusResp = await axios.get(publicOrderStatusUrl.href, { publicOrderStatusResp = await httpLib.fetch(publicOrderStatusUrl.href);
validateStatus: () => true,
});
if (publicOrderStatusResp.status != 402) { if (publicOrderStatusResp.status != 402) {
throw Error(`expected status 402, but got ${publicOrderStatusResp.status}`); throw Error(`expected status 402, but got ${publicOrderStatusResp.status}`);
} }
pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode( pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode(
publicOrderStatusResp.data, publicOrderStatusResp.json(),
); );
console.log(publicOrderStatusResp.data); console.log(publicOrderStatusResp.json());
t.assertTrue(pubUnpaidStatus.already_paid_order_id === firstOrderId); t.assertTrue(pubUnpaidStatus.already_paid_order_id === firstOrderId);
} }

View File

@ -17,13 +17,13 @@
/** /**
* Imports. * Imports.
*/ */
import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
import {
createSimpleTestkudosEnvironment,
withdrawViaBank,
} from "../harness/helpers.js";
import { Duration, durationFromSpec } from "@gnu-taler/taler-util"; import { Duration, durationFromSpec } from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
import {
createSimpleTestkudosEnvironmentV2,
withdrawViaBankV2,
} from "../harness/helpers.js";
/** /**
* Run test for basic, bank-integrated withdrawal. * Run test for basic, bank-integrated withdrawal.
@ -31,12 +31,17 @@ import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
export async function runRefundAutoTest(t: GlobalTestState) { export async function runRefundAutoTest(t: GlobalTestState) {
// Set up test environment // Set up test environment
const { wallet, bank, exchange, merchant } = const { walletClient, bank, exchange, merchant } =
await createSimpleTestkudosEnvironment(t); await createSimpleTestkudosEnvironmentV2(t);
// Withdraw digital cash into the wallet. // Withdraw digital cash into the wallet.
await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); await withdrawViaBankV2(t, {
walletClient,
bank,
exchange,
amount: "TESTKUDOS:20",
});
// Set up order. // Set up order.
const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", { const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
@ -61,13 +66,12 @@ export async function runRefundAutoTest(t: GlobalTestState) {
// Make wallet pay for the order // Make wallet pay for the order
const r1 = await wallet.client.call(WalletApiOperation.PreparePayForUri, { const r1 = await walletClient.call(WalletApiOperation.PreparePayForUri, {
talerPayUri: orderStatus.taler_pay_uri, talerPayUri: orderStatus.taler_pay_uri,
}); });
await wallet.client.call(WalletApiOperation.ConfirmPay, { await walletClient.call(WalletApiOperation.ConfirmPay, {
// FIXME: should be validated, don't cast! transactionId: r1.transactionId,
proposalId: r1.proposalId,
}); });
// Check if payment was successful. // Check if payment was successful.
@ -88,9 +92,9 @@ export async function runRefundAutoTest(t: GlobalTestState) {
console.log(ref); console.log(ref);
// The wallet should now automatically pick up the refund. // The wallet should now automatically pick up the refund.
await wallet.runUntilDone(); await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
const transactions = await wallet.client.call( const transactions = await walletClient.call(
WalletApiOperation.GetTransactions, WalletApiOperation.GetTransactions,
{}, {},
); );

View File

@ -18,21 +18,21 @@
* Imports. * Imports.
*/ */
import { import {
GlobalTestState,
delayMs,
MerchantPrivateApi,
} from "../harness/harness.js";
import {
createSimpleTestkudosEnvironment,
withdrawViaBank,
} from "../harness/helpers.js";
import {
TransactionType,
Amounts, Amounts,
durationFromSpec,
Duration, Duration,
TransactionType,
durationFromSpec,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import {
GlobalTestState,
MerchantPrivateApi,
delayMs,
} from "../harness/harness.js";
import {
createSimpleTestkudosEnvironmentV2,
withdrawViaBankV2,
} from "../harness/helpers.js";
/** /**
* Run test for basic, bank-integrated withdrawal. * Run test for basic, bank-integrated withdrawal.
@ -40,12 +40,17 @@ import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
export async function runRefundIncrementalTest(t: GlobalTestState) { export async function runRefundIncrementalTest(t: GlobalTestState) {
// Set up test environment // Set up test environment
const { wallet, bank, exchange, merchant } = const { walletClient, bank, exchange, merchant } =
await createSimpleTestkudosEnvironment(t); await createSimpleTestkudosEnvironmentV2(t);
// Withdraw digital cash into the wallet. // Withdraw digital cash into the wallet.
await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); await withdrawViaBankV2(t, {
walletClient,
bank,
exchange,
amount: "TESTKUDOS:20",
});
// Set up order. // Set up order.
@ -68,11 +73,11 @@ export async function runRefundIncrementalTest(t: GlobalTestState) {
// Make wallet pay for the order // Make wallet pay for the order
const r1 = await wallet.client.call(WalletApiOperation.PreparePayForUri, { const r1 = await walletClient.call(WalletApiOperation.PreparePayForUri, {
talerPayUri: orderStatus.taler_pay_uri, talerPayUri: orderStatus.taler_pay_uri,
}); });
await wallet.client.call(WalletApiOperation.ConfirmPay, { await walletClient.call(WalletApiOperation.ConfirmPay, {
proposalId: r1.proposalId, proposalId: r1.proposalId,
}); });
@ -94,15 +99,15 @@ export async function runRefundIncrementalTest(t: GlobalTestState) {
console.log("first refund increase response", ref); console.log("first refund increase response", ref);
{ {
let wr = await wallet.client.call(WalletApiOperation.StartRefundQuery, { let wr = await walletClient.call(WalletApiOperation.StartRefundQuery, {
transactionId: r1.transactionId, transactionId: r1.transactionId,
}); });
await wallet.runUntilDone(); await walletClient.call(
console.log(wr); WalletApiOperation.TestingWaitTransactionsFinal,
const txs = await wallet.client.call(
WalletApiOperation.GetTransactions,
{}, {},
); );
console.log(wr);
const txs = await walletClient.call(WalletApiOperation.GetTransactions, {});
console.log( console.log(
"transactions after applying first refund:", "transactions after applying first refund:",
JSON.stringify(txs, undefined, 2), JSON.stringify(txs, undefined, 2),
@ -136,10 +141,13 @@ export async function runRefundIncrementalTest(t: GlobalTestState) {
console.log("third refund increase response", ref); console.log("third refund increase response", ref);
{ {
let wr = await wallet.client.call(WalletApiOperation.StartRefundQuery, { let wr = await walletClient.call(WalletApiOperation.StartRefundQuery, {
transactionId: r1.transactionId, transactionId: r1.transactionId,
}); });
await wallet.runUntilDone(); await walletClient.call(
WalletApiOperation.TestingWaitTransactionsFinal,
{},
);
console.log(wr); console.log(wr);
} }
@ -153,25 +161,17 @@ export async function runRefundIncrementalTest(t: GlobalTestState) {
console.log(JSON.stringify(orderStatus, undefined, 2)); console.log(JSON.stringify(orderStatus, undefined, 2));
await wallet.runUntilDone(); await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
const bal = await wallet.client.call(WalletApiOperation.GetBalances, {}); const bal = await walletClient.call(WalletApiOperation.GetBalances, {});
console.log(JSON.stringify(bal, undefined, 2)); console.log(JSON.stringify(bal, undefined, 2));
{ {
const txs = await wallet.client.call( const txs = await walletClient.call(WalletApiOperation.GetTransactions, {});
WalletApiOperation.GetTransactions,
{},
);
console.log(JSON.stringify(txs, undefined, 2)); console.log(JSON.stringify(txs, undefined, 2));
const txTypes = txs.transactions.map((x) => x.type); const txTypes = txs.transactions.map((x) => x.type);
t.assertDeepEqual(txTypes, [ t.assertDeepEqual(txTypes, ["withdrawal", "payment", "refund", "refund"]);
"withdrawal",
"payment",
"refund",
"refund",
]);
for (const tx of txs.transactions) { for (const tx of txs.transactions) {
if (tx.type !== TransactionType.Refund) { if (tx.type !== TransactionType.Refund) {

View File

@ -213,3 +213,4 @@ export async function runRevocationTest(t: GlobalTestState) {
runRevocationTest.timeoutMs = 120000; runRevocationTest.timeoutMs = 120000;
runRevocationTest.suites = ["wallet"]; runRevocationTest.suites = ["wallet"];
runRevocationTest.excludeByDefault = true;

View File

@ -1,6 +1,6 @@
/* /*
This file is part of GNU Taler This file is part of GNU Taler
(C) 2020 Taler Systems S.A. (C) 2023 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the 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 terms of the GNU General Public License as published by the Free Software
@ -20,11 +20,10 @@
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { GlobalTestState } from "../harness/harness.js"; import { GlobalTestState } from "../harness/harness.js";
import { import {
createSimpleTestkudosEnvironmentV2,
withdrawViaBankV2, withdrawViaBankV2,
makeTestPaymentV2, makeTestPaymentV2,
useSharedTestkudosEnvironment,
} from "../harness/helpers.js"; } from "../harness/helpers.js";
import { j2s } from "@gnu-taler/taler-util";
/** /**
* Run test for basic, bank-integrated withdrawal and payment. * Run test for basic, bank-integrated withdrawal and payment.
@ -33,7 +32,7 @@ export async function runSimplePaymentTest(t: GlobalTestState) {
// Set up test environment // Set up test environment
const { walletClient, bank, exchange, merchant } = const { walletClient, bank, exchange, merchant } =
await createSimpleTestkudosEnvironmentV2(t); await useSharedTestkudosEnvironment(t);
// Withdraw digital cash into the wallet. // Withdraw digital cash into the wallet.

View File

@ -18,8 +18,8 @@
* Imports. * Imports.
*/ */
import { import {
BankAccessApiClient,
WalletApiOperation, WalletApiOperation,
BankApi,
WireGatewayApiClient, WireGatewayApiClient,
} from "@gnu-taler/taler-wallet-core"; } from "@gnu-taler/taler-wallet-core";
import { import {
@ -28,7 +28,7 @@ import {
MerchantPrivateApi, MerchantPrivateApi,
getWireMethodForTest, getWireMethodForTest,
} from "../harness/harness.js"; } from "../harness/harness.js";
import { createSimpleTestkudosEnvironment } from "../harness/helpers.js"; import { createSimpleTestkudosEnvironmentV2 } from "../harness/helpers.js";
import { TransactionMajorState } from "@gnu-taler/taler-util"; import { TransactionMajorState } from "@gnu-taler/taler-util";
/** /**
@ -37,10 +37,14 @@ import { TransactionMajorState } from "@gnu-taler/taler-util";
export async function runTippingTest(t: GlobalTestState) { export async function runTippingTest(t: GlobalTestState) {
// Set up test environment // Set up test environment
const { wallet, bank, exchange, merchant, exchangeBankAccount } = const { walletClient, bank, exchange, merchant, exchangeBankAccount } =
await createSimpleTestkudosEnvironment(t); await createSimpleTestkudosEnvironmentV2(t);
const mbu = await BankApi.createRandomBankUser(bank); const bankAccessApiClient = new BankAccessApiClient({
allowHttp: true,
baseUrl: bank.bankAccessApiBaseUrl,
});
const mbu = await bankAccessApiClient.createRandomBankUser();
const merchantClient = new MerchantApiClient( const merchantClient = new MerchantApiClient(
merchant.makeInstanceBaseUrl("default"), merchant.makeInstanceBaseUrl("default"),
@ -99,7 +103,7 @@ export async function runTippingTest(t: GlobalTestState) {
console.log("created tip", tip); console.log("created tip", tip);
const doTip = async (): Promise<void> => { const doTip = async (): Promise<void> => {
const ptr = await wallet.client.call(WalletApiOperation.PrepareReward, { const ptr = await walletClient.call(WalletApiOperation.PrepareReward, {
talerRewardUri: tip.taler_reward_uri, talerRewardUri: tip.taler_reward_uri,
}); });
@ -108,19 +112,22 @@ export async function runTippingTest(t: GlobalTestState) {
t.assertAmountEquals(ptr.rewardAmountRaw, "TESTKUDOS:5"); t.assertAmountEquals(ptr.rewardAmountRaw, "TESTKUDOS:5");
t.assertAmountEquals(ptr.rewardAmountEffective, "TESTKUDOS:4.85"); t.assertAmountEquals(ptr.rewardAmountEffective, "TESTKUDOS:4.85");
await wallet.client.call(WalletApiOperation.AcceptReward, { await walletClient.call(WalletApiOperation.AcceptReward, {
walletRewardId: ptr.walletRewardId, walletRewardId: ptr.walletRewardId,
}); });
await wallet.runUntilDone(); await walletClient.call(
WalletApiOperation.TestingWaitTransactionsFinal,
{},
);
const bal = await wallet.client.call(WalletApiOperation.GetBalances, {}); const bal = await walletClient.call(WalletApiOperation.GetBalances, {});
console.log(bal); console.log(bal);
t.assertAmountEquals(bal.balances[0].available, "TESTKUDOS:4.85"); t.assertAmountEquals(bal.balances[0].available, "TESTKUDOS:4.85");
const txns = await wallet.client.call( const txns = await walletClient.call(
WalletApiOperation.GetTransactions, WalletApiOperation.GetTransactions,
{}, {},
); );
@ -128,7 +135,10 @@ export async function runTippingTest(t: GlobalTestState) {
console.log("Transactions:", JSON.stringify(txns, undefined, 2)); console.log("Transactions:", JSON.stringify(txns, undefined, 2));
t.assertDeepEqual(txns.transactions[0].type, "reward"); t.assertDeepEqual(txns.transactions[0].type, "reward");
t.assertDeepEqual(txns.transactions[0].txState.major, TransactionMajorState.Done); t.assertDeepEqual(
txns.transactions[0].txState.major,
TransactionMajorState.Done,
);
t.assertAmountEquals( t.assertAmountEquals(
txns.transactions[0].amountEffective, txns.transactions[0].amountEffective,
"TESTKUDOS:4.85", "TESTKUDOS:4.85",

View File

@ -0,0 +1,101 @@
/*
This file is part of GNU Taler
(C) 2020 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/>
*/
/**
* Imports.
*/
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { GlobalTestState } from "../harness/harness.js";
import {
createSimpleTestkudosEnvironmentV2,
} from "../harness/helpers.js";
import * as fs from "fs";
/**
* Run test for basic, bank-integrated withdrawal and payment.
*/
export async function runTermOfServiceFormatTest(t: GlobalTestState) {
// Set up test environment
const tosDir = t.testDir + `/tos/`;
const langs = ["es", "en", "de"]
langs.forEach(l => {
const langDir = tosDir + l + "/"
fs.mkdirSync(langDir, { recursive: true });
fs.writeFileSync(langDir + "v1.txt", "text content");
fs.writeFileSync(langDir + "v1.md", "markdown content");
fs.writeFileSync(langDir + "v1.html", "html content");
});
const { walletClient, exchange, } =
await createSimpleTestkudosEnvironmentV2(t, undefined, {
additionalExchangeConfig: (ex) => {
ex.changeConfig((cfg) => {
cfg.setString("exchange", "terms_etag", "v1")
cfg.setString("exchange", "terms_dir", tosDir)
})
}
});
{
const tos = await walletClient.client.call(WalletApiOperation.GetExchangeTos, {
exchangeBaseUrl: exchange.baseUrl,
})
t.assertDeepEqual(tos.content, "text content");
}
{
const tos = await walletClient.client.call(WalletApiOperation.GetExchangeTos, {
exchangeBaseUrl: exchange.baseUrl,
acceptedFormat: ["text/html"]
})
t.assertDeepEqual(tos.content, "html content");
}
{
const tos = await walletClient.client.call(WalletApiOperation.GetExchangeTos, {
exchangeBaseUrl: exchange.baseUrl,
acceptedFormat: ["text/markdown"]
})
t.assertDeepEqual(tos.content, "markdown content");
}
{
const tos = await walletClient.client.call(WalletApiOperation.GetExchangeTos, {
exchangeBaseUrl: exchange.baseUrl,
acceptedFormat: ["text/markdown", "text/html"]
})
// prefer markdown since its the first one in the list
t.assertDeepEqual(tos.content, "markdown content");
}
{
const tos = await walletClient.client.call(WalletApiOperation.GetExchangeTos, {
exchangeBaseUrl: exchange.baseUrl,
acceptedFormat: ["text/pdf", "text/html"]
})
// there is no pdf so fallback in html
t.assertDeepEqual(tos.content, "html content");
}
}
runTermOfServiceFormatTest.suites = ["wallet"];

View File

@ -22,7 +22,9 @@ import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { GlobalTestState, WalletCli } from "../harness/harness.js"; import { GlobalTestState, WalletCli } from "../harness/harness.js";
import { import {
createSimpleTestkudosEnvironment, createSimpleTestkudosEnvironment,
createSimpleTestkudosEnvironmentV2,
withdrawViaBank, withdrawViaBank,
withdrawViaBankV2,
} from "../harness/helpers.js"; } from "../harness/helpers.js";
import { SyncService } from "../harness/sync.js"; import { SyncService } from "../harness/sync.js";
@ -32,8 +34,8 @@ import { SyncService } from "../harness/sync.js";
export async function runWalletBackupBasicTest(t: GlobalTestState) { export async function runWalletBackupBasicTest(t: GlobalTestState) {
// Set up test environment // Set up test environment
const { commonDb, merchant, wallet, bank, exchange } = const { commonDb, merchant, walletClient, bank, exchange } =
await createSimpleTestkudosEnvironment(t); await createSimpleTestkudosEnvironmentV2(t);
const sync = await SyncService.create(t, { const sync = await SyncService.create(t, {
currency: "TESTKUDOS", currency: "TESTKUDOS",
@ -49,32 +51,32 @@ export async function runWalletBackupBasicTest(t: GlobalTestState) {
await sync.start(); await sync.start();
await sync.pingUntilAvailable(); await sync.pingUntilAvailable();
await wallet.client.call(WalletApiOperation.AddBackupProvider, { await walletClient.call(WalletApiOperation.AddBackupProvider, {
backupProviderBaseUrl: sync.baseUrl, backupProviderBaseUrl: sync.baseUrl,
activate: false, activate: false,
name: sync.baseUrl, name: sync.baseUrl,
}); });
{ {
const bi = await wallet.client.call(WalletApiOperation.GetBackupInfo, {}); const bi = await walletClient.call(WalletApiOperation.GetBackupInfo, {});
t.assertDeepEqual(bi.providers[0].active, false); t.assertDeepEqual(bi.providers[0].active, false);
} }
await wallet.client.call(WalletApiOperation.AddBackupProvider, { await walletClient.call(WalletApiOperation.AddBackupProvider, {
backupProviderBaseUrl: sync.baseUrl, backupProviderBaseUrl: sync.baseUrl,
activate: true, activate: true,
name: sync.baseUrl, name: sync.baseUrl,
}); });
{ {
const bi = await wallet.client.call(WalletApiOperation.GetBackupInfo, {}); const bi = await walletClient.call(WalletApiOperation.GetBackupInfo, {});
t.assertDeepEqual(bi.providers[0].active, true); t.assertDeepEqual(bi.providers[0].active, true);
} }
await wallet.client.call(WalletApiOperation.RunBackupCycle, {}); await walletClient.call(WalletApiOperation.RunBackupCycle, {});
{ {
const bi = await wallet.client.call(WalletApiOperation.GetBackupInfo, {}); const bi = await walletClient.call(WalletApiOperation.GetBackupInfo, {});
console.log(bi); console.log(bi);
t.assertDeepEqual( t.assertDeepEqual(
bi.providers[0].paymentStatus.type, bi.providers[0].paymentStatus.type,
@ -82,32 +84,32 @@ export async function runWalletBackupBasicTest(t: GlobalTestState) {
); );
} }
await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:10" }); await withdrawViaBankV2(t, { walletClient, bank, exchange, amount: "TESTKUDOS:10" });
await wallet.runUntilDone(); await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
await wallet.client.call(WalletApiOperation.RunBackupCycle, {}); await walletClient.call(WalletApiOperation.RunBackupCycle, {});
{ {
const bi = await wallet.client.call(WalletApiOperation.GetBackupInfo, {}); const bi = await walletClient.call(WalletApiOperation.GetBackupInfo, {});
console.log(bi); console.log(bi);
} }
await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:5" }); await withdrawViaBankV2(t, { walletClient, bank, exchange, amount: "TESTKUDOS:5" });
await wallet.client.call(WalletApiOperation.RunBackupCycle, {}); await walletClient.call(WalletApiOperation.RunBackupCycle, {});
{ {
const bi = await wallet.client.call(WalletApiOperation.GetBackupInfo, {}); const bi = await walletClient.call(WalletApiOperation.GetBackupInfo, {});
console.log(bi); console.log(bi);
} }
const backupRecovery = await wallet.client.call( const backupRecovery = await walletClient.call(
WalletApiOperation.ExportBackupRecovery, WalletApiOperation.ExportBackupRecovery,
{}, {},
); );
const txs = await wallet.client.call(WalletApiOperation.GetTransactions, {}); const txs = await walletClient.call(WalletApiOperation.GetTransactions, {});
console.log(`backed up transactions ${j2s(txs)}`); console.log(`backed up transactions ${j2s(txs)}`);
const wallet2 = new WalletCli(t, "wallet2"); const wallet2 = new WalletCli(t, "wallet2");

View File

@ -21,21 +21,22 @@ import { PreparePayResultType } from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { import {
GlobalTestState, GlobalTestState,
WalletCli,
MerchantPrivateApi, MerchantPrivateApi,
WalletCli,
} from "../harness/harness.js"; } from "../harness/harness.js";
import { import {
createSimpleTestkudosEnvironment, createSimpleTestkudosEnvironmentV2,
makeTestPayment, makeTestPaymentV2,
withdrawViaBank, withdrawViaBank,
withdrawViaBankV2
} from "../harness/helpers.js"; } from "../harness/helpers.js";
import { SyncService } from "../harness/sync.js"; import { SyncService } from "../harness/sync.js";
export async function runWalletBackupDoublespendTest(t: GlobalTestState) { export async function runWalletBackupDoublespendTest(t: GlobalTestState) {
// Set up test environment // Set up test environment
const { commonDb, merchant, wallet, bank, exchange } = const { commonDb, merchant, walletClient, bank, exchange } =
await createSimpleTestkudosEnvironment(t); await createSimpleTestkudosEnvironmentV2(t);
const sync = await SyncService.create(t, { const sync = await SyncService.create(t, {
currency: "TESTKUDOS", currency: "TESTKUDOS",
@ -51,21 +52,21 @@ export async function runWalletBackupDoublespendTest(t: GlobalTestState) {
await sync.start(); await sync.start();
await sync.pingUntilAvailable(); await sync.pingUntilAvailable();
await wallet.client.call(WalletApiOperation.AddBackupProvider, { await walletClient.call(WalletApiOperation.AddBackupProvider, {
backupProviderBaseUrl: sync.baseUrl, backupProviderBaseUrl: sync.baseUrl,
activate: true, activate: true,
name: sync.baseUrl, name: sync.baseUrl,
}); });
await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:10" }); await withdrawViaBankV2(t, { walletClient, bank, exchange, amount: "TESTKUDOS:10" });
await wallet.runUntilDone(); await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
await wallet.client.call(WalletApiOperation.RunBackupCycle, {}); await walletClient.call(WalletApiOperation.RunBackupCycle, {});
await wallet.runUntilDone(); await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
await wallet.client.call(WalletApiOperation.RunBackupCycle, {}); await walletClient.call(WalletApiOperation.RunBackupCycle, {});
const backupRecovery = await wallet.client.call( const backupRecovery = await walletClient.call(
WalletApiOperation.ExportBackupRecovery, WalletApiOperation.ExportBackupRecovery,
{}, {},
); );
@ -80,23 +81,23 @@ export async function runWalletBackupDoublespendTest(t: GlobalTestState) {
console.log( console.log(
"wallet1 balance before spend:", "wallet1 balance before spend:",
await wallet.client.call(WalletApiOperation.GetBalances, {}), await walletClient.call(WalletApiOperation.GetBalances, {}),
); );
await makeTestPayment(t, { await makeTestPaymentV2(t, {
merchant, merchant,
wallet, walletClient,
order: { order: {
summary: "foo", summary: "foo",
amount: "TESTKUDOS:7", amount: "TESTKUDOS:7",
}, },
}); });
await wallet.runUntilDone(); await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
console.log( console.log(
"wallet1 balance after spend:", "wallet1 balance after spend:",
await wallet.client.call(WalletApiOperation.GetBalances, {}), await walletClient.call(WalletApiOperation.GetBalances, {}),
); );
{ {

View File

@ -30,7 +30,11 @@ import {
setupDb, setupDb,
WalletCli, WalletCli,
} from "../harness/harness.js"; } from "../harness/harness.js";
import { withdrawViaBank } from "../harness/helpers.js"; import {
createSimpleTestkudosEnvironmentV2,
withdrawViaBank,
withdrawViaBankV2,
} from "../harness/helpers.js";
/** /**
* Test for wallet balance error messages / different types of insufficient balance. * Test for wallet balance error messages / different types of insufficient balance.
@ -41,72 +45,17 @@ import { withdrawViaBank } from "../harness/helpers.js";
export async function runWalletBalanceTest(t: GlobalTestState) { export async function runWalletBalanceTest(t: GlobalTestState) {
// Set up test environment // Set up test environment
const db = await setupDb(t); const { merchant, walletClient, exchange, bank } =
await createSimpleTestkudosEnvironmentV2(t);
const bank = await FakebankService.create(t, {
allowRegistrations: true,
currency: "TESTKUDOS",
database: db.connStr,
httpPort: 8082,
});
const exchange = ExchangeService.create(t, {
name: "testexchange-1",
currency: "TESTKUDOS",
httpPort: 8081,
database: db.connStr,
});
const merchant = await MerchantService.create(t, {
name: "testmerchant-1",
currency: "TESTKUDOS",
httpPort: 8083,
database: db.connStr,
});
const exchangeBankAccount = await bank.createExchangeAccount(
"myexchange",
"x",
);
exchange.addBankAccount("1", exchangeBankAccount);
bank.setSuggestedExchange(exchange, exchangeBankAccount.accountPaytoUri);
await bank.start();
await bank.pingUntilAvailable();
const coinConfig: CoinConfig[] = defaultCoinConfig.map((x) => x("TESTKUDOS"));
exchange.addCoinConfigList(coinConfig);
await exchange.start();
await exchange.pingUntilAvailable();
merchant.addExchange(exchange);
await merchant.start();
await merchant.pingUntilAvailable();
// Fakebank uses x-taler-bank, but merchant is configured to only accept sepa!
const label = "mymerchant";
await merchant.addInstance({
id: "default",
name: "Default Instance",
paytoUris: [
`payto://iban/SANDBOXX/${getRandomIban(label)}?receiver-name=${label}`,
],
defaultWireTransferDelay: Duration.toTalerProtocolDuration(
Duration.fromSpec({ minutes: 1 }),
),
});
console.log("setup done!");
const wallet = new WalletCli(t);
// Withdraw digital cash into the wallet. // Withdraw digital cash into the wallet.
await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); await withdrawViaBankV2(t, {
walletClient,
bank,
exchange,
amount: "TESTKUDOS:20",
});
const order = { const order = {
summary: "Buy me!", summary: "Buy me!",
@ -126,7 +75,7 @@ export async function runWalletBalanceTest(t: GlobalTestState) {
// Make wallet pay for the order // Make wallet pay for the order
const preparePayResult = await wallet.client.call( const preparePayResult = await walletClient.call(
WalletApiOperation.PreparePayForUri, WalletApiOperation.PreparePayForUri,
{ {
talerPayUri: orderStatus.taler_pay_uri, talerPayUri: orderStatus.taler_pay_uri,
@ -152,7 +101,7 @@ export async function runWalletBalanceTest(t: GlobalTestState) {
Amounts.isZero(preparePayResult.balanceDetails.balanceMerchantDepositable), Amounts.isZero(preparePayResult.balanceDetails.balanceMerchantDepositable),
); );
await wallet.runUntilDone(); await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
} }
runWalletBalanceTest.suites = ["wallet"]; runWalletBalanceTest.suites = ["wallet"];

View File

@ -37,7 +37,7 @@ import {
withdrawCoin, withdrawCoin,
} from "@gnu-taler/taler-wallet-core"; } from "@gnu-taler/taler-wallet-core";
import { GlobalTestState } from "../harness/harness.js"; import { GlobalTestState } from "../harness/harness.js";
import { createSimpleTestkudosEnvironment } from "../harness/helpers.js"; import { createSimpleTestkudosEnvironmentV2 } from "../harness/helpers.js";
/** /**
* Run test for basic, bank-integrated withdrawal and payment. * Run test for basic, bank-integrated withdrawal and payment.
@ -45,7 +45,7 @@ import { createSimpleTestkudosEnvironment } from "../harness/helpers.js";
export async function runWalletDblessTest(t: GlobalTestState) { export async function runWalletDblessTest(t: GlobalTestState) {
// Set up test environment // Set up test environment
const { bank, exchange } = await createSimpleTestkudosEnvironment(t); const { bank, exchange } = await createSimpleTestkudosEnvironmentV2(t);
const http = createPlatformHttpLib({ const http = createPlatformHttpLib({
allowHttp: true, allowHttp: true,

View File

@ -18,27 +18,24 @@
* Imports. * Imports.
*/ */
import { import {
Amounts,
Duration, Duration,
NotificationType, NotificationType,
PreparePayResultType,
TransactionMajorState, TransactionMajorState,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { import {
BankAccessApi, BankAccessApiClient,
BankApi,
WalletApiOperation, WalletApiOperation,
} from "@gnu-taler/taler-wallet-core"; } from "@gnu-taler/taler-wallet-core";
import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js"; import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js";
import { import {
ExchangeService, ExchangeService,
FakebankService, FakebankService,
getRandomIban,
GlobalTestState, GlobalTestState,
MerchantService, MerchantService,
setupDb,
WalletClient, WalletClient,
WalletService, WalletService,
getRandomIban,
setupDb,
} from "../harness/harness.js"; } from "../harness/harness.js";
/** /**
@ -126,10 +123,14 @@ export async function runWalletNotificationsTest(t: GlobalTestState) {
skipDefaults: true, skipDefaults: true,
}); });
const user = await BankApi.createRandomBankUser(bank); const bankAccessApiClient = new BankAccessApiClient({
const wop = await BankAccessApi.createWithdrawalOperation( allowHttp: true,
bank, baseUrl: bank.bankAccessApiBaseUrl,
user, });
const user = await bankAccessApiClient.createRandomBankUser();
bankAccessApiClient.setAuth(user);
const wop = await bankAccessApiClient.createWithdrawalOperation(
user.username,
"TESTKUDOS:20", "TESTKUDOS:20",
); );
@ -163,7 +164,7 @@ export async function runWalletNotificationsTest(t: GlobalTestState) {
// Confirm it // Confirm it
await BankApi.confirmWithdrawalOperation(bank, user, wop); await bankAccessApiClient.confirmWithdrawalOperation(user.username, wop);
await withdrawalFinishedReceivedPromise; await withdrawalFinishedReceivedPromise;
} }

View File

@ -20,8 +20,7 @@
import { TalerErrorCode } from "@gnu-taler/taler-util"; import { TalerErrorCode } from "@gnu-taler/taler-util";
import { import {
WalletApiOperation, WalletApiOperation,
BankApi, BankAccessApiClient,
BankAccessApi,
} from "@gnu-taler/taler-wallet-core"; } from "@gnu-taler/taler-wallet-core";
import { GlobalTestState } from "../harness/harness.js"; import { GlobalTestState } from "../harness/harness.js";
import { createSimpleTestkudosEnvironment } from "../harness/helpers.js"; import { createSimpleTestkudosEnvironment } from "../harness/helpers.js";
@ -36,10 +35,14 @@ export async function runWithdrawalAbortBankTest(t: GlobalTestState) {
// Create a withdrawal operation // Create a withdrawal operation
const user = await BankApi.createRandomBankUser(bank); const bankAccessApiClient = new BankAccessApiClient({
const wop = await BankAccessApi.createWithdrawalOperation( allowHttp: true,
bank, baseUrl: bank.bankAccessApiBaseUrl,
user, });
const user = await bankAccessApiClient.createRandomBankUser();
bankAccessApiClient.setAuth(user);
const wop = await bankAccessApiClient.createWithdrawalOperation(
user.username,
"TESTKUDOS:10", "TESTKUDOS:10",
); );
@ -53,8 +56,7 @@ export async function runWithdrawalAbortBankTest(t: GlobalTestState) {
// Abort it // Abort it
await BankApi.abortWithdrawalOperation(bank, user, wop); await bankAccessApiClient.abortWithdrawalOperation(user.username, wop);
//await BankApi.confirmWithdrawalOperation(bank, user, wop);
// Withdraw // Withdraw

View File

@ -19,11 +19,7 @@
*/ */
import { GlobalTestState } from "../harness/harness.js"; import { GlobalTestState } from "../harness/harness.js";
import { createSimpleTestkudosEnvironmentV2 } from "../harness/helpers.js"; import { createSimpleTestkudosEnvironmentV2 } from "../harness/helpers.js";
import { import { BankAccessApiClient, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
WalletApiOperation,
BankApi,
BankAccessApi,
} from "@gnu-taler/taler-wallet-core";
import { import {
j2s, j2s,
NotificationType, NotificationType,
@ -44,10 +40,14 @@ export async function runWithdrawalBankIntegratedTest(t: GlobalTestState) {
// Create a withdrawal operation // Create a withdrawal operation
const user = await BankApi.createRandomBankUser(bank); const bankAccessApiClient = new BankAccessApiClient({
const wop = await BankAccessApi.createWithdrawalOperation( allowHttp: true,
bank, baseUrl: bank.bankAccessApiBaseUrl,
user, });
const user = await bankAccessApiClient.createRandomBankUser();
bankAccessApiClient.setAuth(user);
const wop = await bankAccessApiClient.createWithdrawalOperation(
user.username,
"TESTKUDOS:10", "TESTKUDOS:10",
); );
@ -129,7 +129,7 @@ export async function runWithdrawalBankIntegratedTest(t: GlobalTestState) {
// Confirm it // Confirm it
await BankApi.confirmWithdrawalOperation(bank, user, wop); await bankAccessApiClient.confirmWithdrawalOperation(user.username, wop);
await withdrawalBankConfirmedCond; await withdrawalBankConfirmedCond;

View File

@ -26,6 +26,7 @@ import {
} from "../harness/harness.js"; } from "../harness/harness.js";
import { import {
BankAccessApi, BankAccessApi,
BankAccessApiClient,
BankApi, BankApi,
WalletApiOperation, WalletApiOperation,
} from "@gnu-taler/taler-wallet-core"; } from "@gnu-taler/taler-wallet-core";
@ -112,8 +113,13 @@ export async function runWithdrawalFeesTest(t: GlobalTestState) {
const amount = "TESTKUDOS:7.5"; const amount = "TESTKUDOS:7.5";
const user = await BankApi.createRandomBankUser(bank); const bankAccessApiClient = new BankAccessApiClient({
const wop = await BankAccessApi.createWithdrawalOperation(bank, user, amount); allowHttp: true,
baseUrl: bank.bankAccessApiBaseUrl,
});
const user = await bankAccessApiClient.createRandomBankUser();
bankAccessApiClient.setAuth(user);
const wop = await bankAccessApiClient.createWithdrawalOperation(user.username, amount);
// Hand it to the wallet // Hand it to the wallet
@ -150,10 +156,7 @@ export async function runWithdrawalFeesTest(t: GlobalTestState) {
// Confirm it // Confirm it
await BankApi.confirmWithdrawalOperation(bank, user, wop); await bankAccessApiClient.confirmWithdrawalOperation(user.username, wop);
await exchange.runWirewatchOnce();
await wallet.runUntilDone(); await wallet.runUntilDone();
// Check balance // Check balance

View File

@ -27,7 +27,11 @@ import {
} from "../harness/harness.js"; } from "../harness/harness.js";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js"; import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js";
import { NotificationType, TransactionMajorState, URL } from "@gnu-taler/taler-util"; import {
NotificationType,
TransactionMajorState,
URL,
} from "@gnu-taler/taler-util";
/** /**
* Withdraw a high amount. Mostly intended as a perf test. * Withdraw a high amount. Mostly intended as a perf test.
@ -84,7 +88,8 @@ export async function runWithdrawalHugeTest(t: GlobalTestState) {
await wallet.connect(); await wallet.connect();
const withdrawalFinishedCond = wallet.waitForNotificationCond( const withdrawalFinishedCond = wallet.waitForNotificationCond(
(wn) => wn.type === NotificationType.TransactionStateTransition && (wn) =>
wn.type === NotificationType.TransactionStateTransition &&
wn.transactionId.startsWith("txn:withdrawal:") && wn.transactionId.startsWith("txn:withdrawal:") &&
wn.newTxState.major === TransactionMajorState.Done, wn.newTxState.major === TransactionMajorState.Done,
); );

View File

@ -17,14 +17,14 @@
/** /**
* Imports. * Imports.
*/ */
import { GlobalTestState } from "../harness/harness.js"; import { AbsoluteTime, Logger, j2s } from "@gnu-taler/taler-util";
import { createSimpleTestkudosEnvironment } from "../harness/helpers.js";
import { import {
BankAccessApiClient,
WalletApiOperation, WalletApiOperation,
BankApi,
WireGatewayApiClient, WireGatewayApiClient,
} from "@gnu-taler/taler-wallet-core"; } from "@gnu-taler/taler-wallet-core";
import { AbsoluteTime, j2s, Logger } from "@gnu-taler/taler-util"; import { GlobalTestState } from "../harness/harness.js";
import { createSimpleTestkudosEnvironmentV2 } from "../harness/helpers.js";
const logger = new Logger("test-withdrawal-manual.ts"); const logger = new Logger("test-withdrawal-manual.ts");
@ -34,14 +34,19 @@ const logger = new Logger("test-withdrawal-manual.ts");
export async function runWithdrawalManualTest(t: GlobalTestState) { export async function runWithdrawalManualTest(t: GlobalTestState) {
// Set up test environment // Set up test environment
const { wallet, bank, exchange, exchangeBankAccount } = const { walletClient, bank, exchange, exchangeBankAccount } =
await createSimpleTestkudosEnvironment(t); await createSimpleTestkudosEnvironmentV2(t);
// Create a withdrawal operation // Create a withdrawal operation
const user = await BankApi.createRandomBankUser(bank); const bankAccessApiClient = new BankAccessApiClient({
baseUrl: bank.bankAccessApiBaseUrl,
allowHttp: true,
});
await wallet.client.call(WalletApiOperation.AddExchange, { const user = await bankAccessApiClient.createRandomBankUser();
await walletClient.call(WalletApiOperation.AddExchange, {
exchangeBaseUrl: exchange.baseUrl, exchangeBaseUrl: exchange.baseUrl,
}); });
@ -50,7 +55,7 @@ export async function runWithdrawalManualTest(t: GlobalTestState) {
logger.info("starting AcceptManualWithdrawal request"); logger.info("starting AcceptManualWithdrawal request");
// We expect this to return immediately. // We expect this to return immediately.
const wres = await wallet.client.call( const wres = await walletClient.call(
WalletApiOperation.AcceptManualWithdrawal, WalletApiOperation.AcceptManualWithdrawal,
{ {
exchangeBaseUrl: exchange.baseUrl, exchangeBaseUrl: exchange.baseUrl,
@ -84,11 +89,11 @@ export async function runWithdrawalManualTest(t: GlobalTestState) {
await exchange.runWirewatchOnce(); await exchange.runWirewatchOnce();
await wallet.runUntilDone(); await walletClient.call(WalletApiOperation.TestingWaitTransactionsFinal, {});
// Check balance // Check balance
const balResp = await wallet.client.call(WalletApiOperation.GetBalances, {}); const balResp = await walletClient.call(WalletApiOperation.GetBalances, {});
t.assertAmountEquals("TESTKUDOS:9.72", balResp.balances[0].available); t.assertAmountEquals("TESTKUDOS:9.72", balResp.balances[0].available);
await t.shutdown(); await t.shutdown();

View File

@ -14,7 +14,7 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
import { CancellationToken, Logger, minimatch } from "@gnu-taler/taler-util"; import { CancellationToken, Logger, minimatch, setGlobalLogLevelFromString } from "@gnu-taler/taler-util";
import * as child_process from "child_process"; import * as child_process from "child_process";
import * as fs from "fs"; import * as fs from "fs";
import * as os from "os"; import * as os from "os";
@ -104,6 +104,7 @@ import { runExchangeDepositTest } from "./test-exchange-deposit.js";
import { runPeerRepairTest } from "./test-peer-repair.js"; import { runPeerRepairTest } from "./test-peer-repair.js";
import { runPaymentShareTest } from "./test-payment-share.js"; import { runPaymentShareTest } from "./test-payment-share.js";
import { runSimplePaymentTest } from "./test-simple-payment.js"; import { runSimplePaymentTest } from "./test-simple-payment.js";
import { runTermOfServiceFormatTest } from "./test-tos-format.js";
/** /**
* Test runner. * Test runner.
@ -200,6 +201,7 @@ const allTests: TestMainFunction[] = [
runWithdrawalFakebankTest, runWithdrawalFakebankTest,
runWithdrawalFeesTest, runWithdrawalFeesTest,
runWithdrawalHugeTest, runWithdrawalHugeTest,
runTermOfServiceFormatTest,
]; ];
export interface TestRunSpec { export interface TestRunSpec {

View File

@ -0,0 +1,10 @@
// Work in progress.
// TS-based schema for the sandcastle configuration.
export interface SandcastleConfig {
currency: string;
merchant: {
apiKey: string;
baseUrl: string;
};
}

View File

@ -32,13 +32,17 @@ export enum LogLevel {
None = "none", None = "none",
} }
export let globalLogLevel = LogLevel.Info; let globalLogLevel = LogLevel.Info;
const byTagLogLevel: Record<string, LogLevel> = {};
export function getGlobalLogLevel(): string {
return globalLogLevel;
}
export function setGlobalLogLevelFromString(logLevelStr: string): void { export function setGlobalLogLevelFromString(logLevelStr: string): void {
globalLogLevel = getLevelForString(logLevelStr); globalLogLevel = getLevelForString(logLevelStr);
} }
export const byTagLogLevel: Record<string, LogLevel> = {};
export function setLogLevelFromString(tag: string, logLevelStr: string): void { export function setLogLevelFromString(tag: string, logLevelStr: string): void {
byTagLogLevel[tag] = getLevelForString(logLevelStr); byTagLogLevel[tag] = getLevelForString(logLevelStr);
} }
@ -98,7 +102,7 @@ function writeNodeLog(
* and uses the corresponding console.* method to log in the browser. * and uses the corresponding console.* method to log in the browser.
*/ */
export class Logger { export class Logger {
constructor(private tag: string) {} constructor(private tag: string) { }
shouldLogTrace(): boolean { shouldLogTrace(): boolean {
const level = byTagLogLevel[this.tag] ?? globalLogLevel; const level = byTagLogLevel[this.tag] ?? globalLogLevel;

View File

@ -1,6 +1,6 @@
/* /*
This file is part of GNU Taler This file is part of GNU Taler
(C) 2020 Taler Systems S.A. (C) 2020-2023 Taler Systems S.A.
GNU Taler is free software; you can redistribute it and/or modify it under the 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 terms of the GNU General Public License as published by the Free Software
@ -39,10 +39,30 @@ export class ConfigError extends Error {
} }
} }
enum EntryOrigin {
/**
* From a default file.
*/
DefaultFile = 1,
/**
* From a system/installation specific default value.
*/
DefaultSystem = 2,
/**
* Loaded from file or string
*/
Loaded = 3,
/**
* Changed after loading
*/
Changed = 4,
}
interface Entry { interface Entry {
value: string; value: string;
sourceLine: number; sourceLine: number;
sourceFile: string; sourceFile: string;
origin: EntryOrigin;
} }
interface Section { interface Section {
@ -159,12 +179,12 @@ export function pathsub(
const r = lookup(inner, depth + 1); const r = lookup(inner, depth + 1);
if (r !== undefined) { if (r !== undefined) {
s = s.substr(0, start) + r + s.substr(p + 1); s = s.substring(0, start) + r + s.substring(p + 1);
l = start + r.length; l = start + r.length;
continue; continue;
} else if (defaultValue !== undefined) { } else if (defaultValue !== undefined) {
const resolvedDefault = pathsub(defaultValue, lookup, depth + 1); const resolvedDefault = pathsub(defaultValue, lookup, depth + 1);
s = s.substr(0, start) + resolvedDefault + s.substr(p + 1); s = s.substring(0, start) + resolvedDefault + s.substring(p + 1);
l = start + resolvedDefault.length; l = start + resolvedDefault.length;
continue; continue;
} }
@ -176,7 +196,7 @@ export function pathsub(
if (m && m[0]) { if (m && m[0]) {
const r = lookup(m[0], depth + 1); const r = lookup(m[0], depth + 1);
if (r !== undefined) { if (r !== undefined) {
s = s.substr(0, l) + r + s.substr(l + 1 + m[0].length); s = s.substring(0, l) + r + s.substring(l + 1 + m[0].length);
l = l + r.length; l = l + r.length;
continue; continue;
} }
@ -195,6 +215,7 @@ export interface LoadOptions {
export interface StringifyOptions { export interface StringifyOptions {
diagnostics?: boolean; diagnostics?: boolean;
excludeDefaults?: boolean;
} }
export interface LoadedFile { export interface LoadedFile {
@ -282,7 +303,11 @@ export class Configuration {
private nestLevel = 0; private nestLevel = 0;
private loadFromFilename(filename: string, opts: LoadOptions = {}): void { private loadFromFilename(
filename: string,
isDefaultSource: boolean,
opts: LoadOptions = {},
): void {
filename = expandPath(filename); filename = expandPath(filename);
const checkCycle = () => { const checkCycle = () => {
@ -309,7 +334,7 @@ export class Configuration {
const oldNestLevel = this.nestLevel; const oldNestLevel = this.nestLevel;
this.nestLevel += 1; this.nestLevel += 1;
try { try {
this.loadFromString(s, { this.internalLoadFromString(s, isDefaultSource, {
...opts, ...opts,
filename: filename, filename: filename,
}); });
@ -318,7 +343,11 @@ export class Configuration {
} }
} }
private loadGlob(parentFilename: string, fileglob: string): void { private loadGlob(
parentFilename: string,
isDefaultSource: boolean,
fileglob: string,
): void {
const resolvedParent = nodejs_fs.realpathSync(parentFilename); const resolvedParent = nodejs_fs.realpathSync(parentFilename);
const parentDir = nodejs_path.dirname(resolvedParent); const parentDir = nodejs_path.dirname(resolvedParent);
@ -339,12 +368,16 @@ export class Configuration {
for (const f of files) { for (const f of files) {
if (globMatch(tail, f)) { if (globMatch(tail, f)) {
const fullPath = nodejs_path.join(head, f); const fullPath = nodejs_path.join(head, f);
this.loadFromFilename(fullPath); this.loadFromFilename(fullPath, isDefaultSource);
} }
} }
} }
private loadSecret(sectionName: string, filename: string): void { private loadSecret(
sectionName: string,
filename: string,
isDefaultSource: boolean,
): void {
const sec = this.provideSection(sectionName); const sec = this.provideSection(sectionName);
sec.secretFilename = filename; sec.secretFilename = filename;
const otherCfg = new Configuration(); const otherCfg = new Configuration();
@ -354,7 +387,7 @@ export class Configuration {
sec.inaccessible = true; sec.inaccessible = true;
return; return;
} }
otherCfg.loadFromFilename(filename, { otherCfg.loadFromFilename(filename, isDefaultSource, {
banDirectives: true, banDirectives: true,
}); });
const otherSec = otherCfg.provideSection(sectionName); const otherSec = otherCfg.provideSection(sectionName);
@ -363,7 +396,11 @@ export class Configuration {
} }
} }
loadFromString(s: string, opts: LoadOptions = {}): void { private internalLoadFromString(
s: string,
isDefaultSource: boolean,
opts: LoadOptions = {},
): void {
let lineNo = 0; let lineNo = 0;
const fn = opts.filename ?? "<input>"; const fn = opts.filename ?? "<input>";
const reComment = /^\s*#.*$/; const reComment = /^\s*#.*$/;
@ -399,7 +436,10 @@ export class Configuration {
); );
} }
const arg = directiveMatch[2].trim(); const arg = directiveMatch[2].trim();
this.loadFromFilename(normalizeInlineFilename(opts.filename, arg)); this.loadFromFilename(
normalizeInlineFilename(opts.filename, arg),
isDefaultSource,
);
break; break;
} }
case "inline-secret": { case "inline-secret": {
@ -419,7 +459,7 @@ export class Configuration {
opts.filename, opts.filename,
sp[1], sp[1],
); );
this.loadSecret(sp[0], secretFilename); this.loadSecret(sp[0], secretFilename, isDefaultSource);
break; break;
} }
case "inline-matching": { case "inline-matching": {
@ -429,7 +469,7 @@ export class Configuration {
`invalid configuration, @inline-matching@ directive in ${fn}:${lineNo} can only be used from a file`, `invalid configuration, @inline-matching@ directive in ${fn}:${lineNo} can only be used from a file`,
); );
} }
this.loadGlob(opts.filename, arg); this.loadGlob(opts.filename, isDefaultSource, arg);
break; break;
} }
default: default:
@ -462,6 +502,9 @@ export class Configuration {
value: val, value: val,
sourceFile: opts.filename ?? "<unknown>", sourceFile: opts.filename ?? "<unknown>",
sourceLine: lineNo, sourceLine: lineNo,
origin: isDefaultSource
? EntryOrigin.DefaultFile
: EntryOrigin.Loaded,
}; };
continue; continue;
} }
@ -471,6 +514,10 @@ export class Configuration {
} }
} }
loadFromString(s: string, opts: LoadOptions = {}): void {
return this.internalLoadFromString(s, false, opts);
}
private provideSection(section: string): Section { private provideSection(section: string): Section {
const secNorm = section.toUpperCase(); const secNorm = section.toUpperCase();
if (this.sectionMap[secNorm]) { if (this.sectionMap[secNorm]) {
@ -496,6 +543,24 @@ export class Configuration {
value, value,
sourceLine: 0, sourceLine: 0,
sourceFile: "<unknown>", sourceFile: "<unknown>",
origin: EntryOrigin.Changed,
};
}
/**
* Set a string value to a value from default locations.
*/
private setStringSystemDefault(
section: string,
option: string,
value: string,
): void {
const sec = this.provideSection(section);
sec.entries[option.toUpperCase()] = {
value,
sourceLine: 0,
sourceFile: "<unknown>",
origin: EntryOrigin.DefaultSystem,
}; };
} }
@ -559,7 +624,7 @@ export class Configuration {
lookupVariable(x: string, depth: number = 0): string | undefined { lookupVariable(x: string, depth: number = 0): string | undefined {
// We loop up options in PATHS in upper case, as option names // We loop up options in PATHS in upper case, as option names
// are case insensitive // are case insensitive
const val = this.findEntry("PATHS", x)?.value; let val = this.findEntry("PATHS", x)?.value;
if (val !== undefined) { if (val !== undefined) {
return pathsub(val, (v, d) => this.lookupVariable(v, d), depth); return pathsub(val, (v, d) => this.lookupVariable(v, d), depth);
} }
@ -578,30 +643,63 @@ export class Configuration {
); );
} }
loadFrom(dirname: string): void { loadDefaultsFromDir(dirname: string): void {
const files = nodejs_fs.readdirSync(dirname); const files = nodejs_fs.readdirSync(dirname);
for (const f of files) { for (const f of files) {
const fn = nodejs_path.join(dirname, f); const fn = nodejs_path.join(dirname, f);
this.loadFromFilename(fn); this.loadFromFilename(fn, true);
} }
} }
private loadDefaults(): void { private loadDefaults(): void {
let bc = process.env["TALER_BASE_CONFIG"]; let baseConfigDir = process.env["TALER_BASE_CONFIG"];
if (!bc) { if (!baseConfigDir) {
/* Try to locate the configuration based on the location /* Try to locate the configuration based on the location
* of the taler-config binary. */ * of the taler-config binary. */
const path = which("taler-config"); const path = which("taler-config");
if (path) { if (path) {
bc = nodejs_fs.realpathSync( baseConfigDir = nodejs_fs.realpathSync(
nodejs_path.dirname(path) + "/../share/taler/config.d", nodejs_path.dirname(path) + "/../share/taler/config.d",
); );
} }
} }
if (!bc) { if (!baseConfigDir) {
bc = "/usr/share/taler/config.d"; baseConfigDir = "/usr/share/taler/config.d";
} }
this.loadFrom(bc);
let installPrefix = process.env["TALER_PREFIX"];
if (!installPrefix) {
/* Try to locate install path based on the location
* of the taler-config binary. */
const path = which("taler-config");
if (path) {
installPrefix = nodejs_fs.realpathSync(
nodejs_path.dirname(path) + "/..",
);
}
}
if (!installPrefix) {
installPrefix = "/usr";
}
this.setStringSystemDefault(
"PATHS",
"LIBEXECDIR",
`${installPrefix}/taler/libexec/`,
);
this.setStringSystemDefault(
"PATHS",
"DOCDIR",
`${installPrefix}/share/doc/taler/`,
);
this.setStringSystemDefault("PATHS", "ICONDIR", `${installPrefix}/share/icons/`);
this.setStringSystemDefault("PATHS", "LOCALEDIR", `${installPrefix}/share/locale/`);
this.setStringSystemDefault("PATHS", "PREFIX", `${installPrefix}/`);
this.setStringSystemDefault("PATHS", "BINDIR", `${installPrefix}/bin`);
this.setStringSystemDefault("PATHS", "LIBDIR", `${installPrefix}/lib/taler/`);
this.setStringSystemDefault("PATHS", "DATADIR", `${installPrefix}/share/taler/`);
this.loadDefaultsFromDir(baseConfigDir);
} }
getDefaultConfigFilename(): string | undefined { getDefaultConfigFilename(): string | undefined {
@ -631,11 +729,13 @@ export class Configuration {
const cfg = new Configuration(); const cfg = new Configuration();
cfg.loadDefaults(); cfg.loadDefaults();
if (filename) { if (filename) {
cfg.loadFromFilename(filename); cfg.loadFromFilename(filename, false);
} else { } else {
const fn = cfg.getDefaultConfigFilename(); const fn = cfg.getDefaultConfigFilename();
if (fn) { if (fn) {
cfg.loadFromFilename(fn); // It's the default filename for the main config file,
// but we don't consider the values default values.
cfg.loadFromFilename(fn, false);
} }
} }
cfg.hintEntrypoint = filename; cfg.hintEntrypoint = filename;
@ -657,26 +757,51 @@ export class Configuration {
} }
for (const sectionName of Object.keys(this.sectionMap)) { for (const sectionName of Object.keys(this.sectionMap)) {
const sec = this.sectionMap[sectionName]; const sec = this.sectionMap[sectionName];
if (opts.diagnostics && sec.secretFilename) { let headerWritten = false;
s += `# Secret section from ${sec.secretFilename}\n`;
s += `# Secret accessible: ${!sec.inaccessible}\n`;
}
s += `[${sectionName}]\n`;
for (const optionName of Object.keys(sec.entries)) { for (const optionName of Object.keys(sec.entries)) {
const entry = this.sectionMap[sectionName].entries[optionName]; const entry = this.sectionMap[sectionName].entries[optionName];
if (
opts.excludeDefaults &&
(entry.origin === EntryOrigin.DefaultSystem ||
entry.origin === EntryOrigin.DefaultFile)
) {
continue;
}
if (!headerWritten) {
if (opts.diagnostics && sec.secretFilename) {
s += `# Secret section from ${sec.secretFilename}\n`;
s += `# Secret accessible: ${!sec.inaccessible}\n`;
}
s += `[${sectionName}]\n`;
headerWritten = true;
}
if (entry !== undefined) { if (entry !== undefined) {
if (opts.diagnostics) { if (opts.diagnostics) {
s += `# ${entry.sourceFile}:${entry.sourceLine}\n`; switch (entry.origin) {
case EntryOrigin.DefaultFile:
case EntryOrigin.Changed:
case EntryOrigin.Loaded:
s += `# ${entry.sourceFile}:${entry.sourceLine}\n`;
break;
case EntryOrigin.DefaultSystem:
s += `# (system/installation default)\n`;
break;
}
} }
s += `${optionName} = ${entry.value}\n`; s += `${optionName} = ${entry.value}\n`;
} }
} }
s += "\n"; if (headerWritten) {
s += "\n";
}
} }
return s; return s;
} }
write(filename: string): void { write(filename: string, opts: { excludeDefaults?: boolean } = {}): void {
nodejs_fs.writeFileSync(filename, this.stringify()); nodejs_fs.writeFileSync(
filename,
this.stringify({ excludeDefaults: opts.excludeDefaults }),
);
} }
} }

View File

@ -267,8 +267,7 @@ export namespace BankAccessApi {
export interface BankAccessApiClientArgs { export interface BankAccessApiClientArgs {
baseUrl: string; baseUrl: string;
username: string; auth?: { username: string; password: string };
password: string;
enableThrottling?: boolean; enableThrottling?: boolean;
allowHttp?: boolean; allowHttp?: boolean;
} }
@ -330,7 +329,7 @@ export class WireGatewayApiClient {
* but it will be nice to have in utils to be used by others * but it will be nice to have in utils to be used by others
*/ */
export class BankAccessApiClient { export class BankAccessApiClient {
httpLib; httpLib: HttpRequestLibrary;
constructor(private args: BankAccessApiClientArgs) { constructor(private args: BankAccessApiClientArgs) {
this.httpLib = createPlatformHttpLib({ this.httpLib = createPlatformHttpLib({
@ -339,19 +338,33 @@ export class BankAccessApiClient {
}); });
} }
async getTransactions(): Promise<void> { setAuth(auth: { username: string; password: string }) {
const reqUrl = new URL( this.args.auth = auth;
`accounts/${this.args.username}/transactions`, }
this.args.baseUrl,
); private makeAuthHeader(): Record<string, string> {
if (!this.args.auth) {
return {};
}
const authHeaderValue = makeBasicAuthHeader( const authHeaderValue = makeBasicAuthHeader(
this.args.username, this.args.auth.username,
this.args.password, this.args.auth.password,
);
return {
Authorization: authHeaderValue,
};
}
async getTransactions(username: string): Promise<void> {
const auth = this.args.auth;
const reqUrl = new URL(
`accounts/${username}/transactions`,
this.args.baseUrl,
); );
const resp = await this.httpLib.fetch(reqUrl.href, { const resp = await this.httpLib.fetch(reqUrl.href, {
method: "GET", method: "GET",
headers: { headers: {
Authorization: authHeaderValue, ...this.makeAuthHeader(),
}, },
}); });
@ -360,24 +373,128 @@ export class BankAccessApiClient {
} }
async createTransaction( async createTransaction(
username: string,
req: BankAccessApiCreateTransactionRequest, req: BankAccessApiCreateTransactionRequest,
): Promise<any> { ): Promise<any> {
const reqUrl = new URL( const reqUrl = new URL(
`accounts/${this.args.username}/transactions`, `accounts/${username}/transactions`,
this.args.baseUrl, this.args.baseUrl,
); );
const authHeaderValue = makeBasicAuthHeader(
this.args.username,
this.args.password,
);
const resp = await this.httpLib.fetch(reqUrl.href, { const resp = await this.httpLib.fetch(reqUrl.href, {
method: "POST", method: "POST",
body: req, body: req,
headers: { headers: this.makeAuthHeader(),
Authorization: authHeaderValue,
},
}); });
return await readSuccessResponseJsonOrThrow(resp, codecForAny()); return await readSuccessResponseJsonOrThrow(resp, codecForAny());
} }
async registerAccount(
username: string,
password: string,
options: {
iban?: string;
},
): Promise<BankUser> {
const url = new URL("testing/register", this.args.baseUrl);
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
body: {
username,
password,
iban: options?.iban,
},
});
let paytoUri = `payto://x-taler-bank/localhost/${username}`;
if (resp.status !== 200 && resp.status !== 202 && resp.status !== 204) {
logger.error(`${j2s(await resp.json())}`);
throw TalerError.fromDetail(
TalerErrorCode.GENERIC_UNEXPECTED_REQUEST_ERROR,
{
httpStatusCode: resp.status,
},
);
}
try {
// Pybank has no body, thus this might throw.
const respJson = await resp.json();
// LibEuFin demobank returns payto URI in response
if (respJson.paytoUri) {
paytoUri = respJson.paytoUri;
}
} catch (e) {
// Do nothing
}
return {
password,
username,
accountPaytoUri: paytoUri,
};
}
async createRandomBankUser(): Promise<BankUser> {
const username = "user-" + encodeCrock(getRandomBytes(10)).toLowerCase();
const password = "pw-" + encodeCrock(getRandomBytes(10)).toLowerCase();
// FIXME: This is just a temporary workaround, because demobank is running out of short IBANs
const iban = generateIban("DE", 15);
return await this.registerAccount(username, password, {
iban,
});
}
async createWithdrawalOperation(
user: string,
amount: string,
): Promise<WithdrawalOperationInfo> {
const url = new URL(`accounts/${user}/withdrawals`, this.args.baseUrl);
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
body: {
amount,
},
headers: this.makeAuthHeader(),
});
return readSuccessResponseJsonOrThrow(
resp,
codecForWithdrawalOperationInfo(),
);
}
async confirmWithdrawalOperation(
username: string,
wopi: WithdrawalOperationInfo,
): Promise<void> {
const url = new URL(
`accounts/${username}/withdrawals/${wopi.withdrawal_id}/confirm`,
this.args.baseUrl,
);
logger.info(`confirming withdrawal operation via ${url.href}`);
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
body: {},
headers: this.makeAuthHeader(),
});
logger.info(`response status ${resp.status}`);
const respJson = await readSuccessResponseJsonOrThrow(resp, codecForAny());
// FIXME: We don't check the status here!
}
async abortWithdrawalOperation(
accountName: string,
wopi: WithdrawalOperationInfo,
): Promise<void> {
const url = new URL(
`accounts/${accountName}/withdrawals/${wopi.withdrawal_id}/abort`,
this.args.baseUrl,
);
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
body: {},
headers: this.makeAuthHeader(),
});
await readSuccessResponseJsonOrThrow(resp, codecForAny());
}
} }