commit 4d3a511087635e2b4d8389c861f1d3b75f158f78 parent 2b1907ef57b429d3e42fdeeba62298dfdb405643 Author: Antoine A <> Date: Fri, 12 Jun 2026 13:12:31 +0200 common: fix setup, clean code and improve testbench Diffstat:
27 files changed, 434 insertions(+), 373 deletions(-)
diff --git a/.gitignore b/.gitignore @@ -1,15 +1,7 @@ -.idea/* -/.version -.vscode libeufin-nexus/test -common/tmp testbench/test testbench/config.json configure -build/ -.gradle -.kotlin -out *.sqlite3 *.swp presentation/*.log @@ -18,9 +10,6 @@ presentation/*.aux presentation/*.out presentation/*.snm presentation/*.toc -.idea/misc.xml -.idea/modules/ -__pycache__ *.log .DS_Store *.mk diff --git a/Cargo.lock b/Cargo.lock @@ -1315,7 +1315,6 @@ dependencies = [ [[package]] name = "http-client" version = "1.5.0" -source = "git+git://git.taler.net/taler-rust.git/#4e0704b3732a7a139f9ec7eef8a5adfdfa391aaf" dependencies = [ "compact_str", "futures-util", @@ -3470,7 +3469,6 @@ dependencies = [ [[package]] name = "taler-api" version = "1.5.0" -source = "git+git://git.taler.net/taler-rust.git/#4e0704b3732a7a139f9ec7eef8a5adfdfa391aaf" dependencies = [ "aws-lc-rs", "axum", @@ -3497,12 +3495,10 @@ dependencies = [ [[package]] name = "taler-build" version = "1.5.0" -source = "git+git://git.taler.net/taler-rust.git/#4e0704b3732a7a139f9ec7eef8a5adfdfa391aaf" [[package]] name = "taler-common" version = "1.5.0" -source = "git+git://git.taler.net/taler-rust.git/#4e0704b3732a7a139f9ec7eef8a5adfdfa391aaf" dependencies = [ "anyhow", "aws-lc-rs", @@ -3531,7 +3527,6 @@ dependencies = [ [[package]] name = "taler-macros" version = "1.5.0" -source = "git+git://git.taler.net/taler-rust.git/#4e0704b3732a7a139f9ec7eef8a5adfdfa391aaf" dependencies = [ "proc-macro2", "quote", @@ -3541,16 +3536,19 @@ dependencies = [ [[package]] name = "taler-test-utils" version = "1.5.0" -source = "git+git://git.taler.net/taler-rust.git/#4e0704b3732a7a139f9ec7eef8a5adfdfa391aaf" dependencies = [ "aws-lc-rs", "axum", + "clap", "flate2", "http-body-util", "jiff", + "nu-ansi-term", + "reedline", "serde", "serde_json", "serde_urlencoded", + "shlex", "sqlx", "taler-api", "taler-common", @@ -3582,12 +3580,12 @@ dependencies = [ "clap", "jiff", "libeufin-ebics", + "libeufin-ebisync", "libeufin-nexus", "nix", "owo-colors", - "reedline", - "shlex", "taler-common", + "taler-test-utils", "tokio", "tracing", "tracing-subscriber", diff --git a/Cargo.toml b/Cargo.toml @@ -47,15 +47,15 @@ zip = { version = "8.5", default-features = false, features = [ ] } tower-http = { version = "0.6", features = ["fs"]} shlex = "2.0" -taler-common = { git = "git://git.taler.net/taler-rust.git/" } -taler-api = { git = "git://git.taler.net/taler-rust.git/" } -taler-build = { git = "git://git.taler.net/taler-rust.git/" } -taler-test-utils = { git = "git://git.taler.net/taler-rust.git/" } -taler-macros = { git = "git://git.taler.net/taler-rust.git/" } -http-client = { git = "git://git.taler.net/taler-rust.git/" } -#taler-common = { path = "../taler-rust/common/taler-common" } -#taler-api = { path = "../taler-rust/common/taler-api" } -#taler-build = { path = "../taler-rust/common/taler-build" } -#taler-test-utils = { path = "../taler-rust/common/taler-test-utils" } -#taler-macros = { path = "../taler-rust/common/taler-macros" } -#http-client = { path = "../taler-rust/common/http-client" } -\ No newline at end of file +#taler-common = { git = "git://git.taler.net/taler-rust.git/" } +#taler-api = { git = "git://git.taler.net/taler-rust.git/" } +#taler-build = { git = "git://git.taler.net/taler-rust.git/" } +#taler-test-utils = { git = "git://git.taler.net/taler-rust.git/" } +#taler-macros = { git = "git://git.taler.net/taler-rust.git/" } +#http-client = { git = "git://git.taler.net/taler-rust.git/" } +taler-common = { path = "../taler-rust/common/taler-common" } +taler-api = { path = "../taler-rust/common/taler-api" } +taler-build = { path = "../taler-rust/common/taler-build" } +taler-test-utils = { path = "../taler-rust/common/taler-test-utils" } +taler-macros = { path = "../taler-rust/common/taler-macros" } +http-client = { path = "../taler-rust/common/http-client" } +\ No newline at end of file diff --git a/Makefile b/Makefile @@ -27,6 +27,12 @@ install-nobuild-files: install -m 644 -D -t $(share_dir)/libeufin-ebisync/config.d libeufin-ebisync/ebisync.conf install -m 644 -D -t $(share_dir)/libeufin-ebisync/sql database-versioning/versioning.sql install -m 644 -D -t $(share_dir)/libeufin-ebisync/sql database-versioning/ebisync*.sql + install -m 644 -D -t $(man_dir)/man1 doc/prebuilt/man/libeufin-bank.1 + install -m 644 -D -t $(man_dir)/man5 doc/prebuilt/man/libeufin-bank.conf.5 + install -m 644 -D -t $(man_dir)/man1 doc/prebuilt/man/libeufin-nexus.1 + install -m 644 -D -t $(man_dir)/man5 doc/prebuilt/man/libeufin-nexus.conf.5 + install -m 644 -D -t $(man_dir)/man1 doc/prebuilt/man/libeufin-ebisync.1 + install -m 644 -D -t $(man_dir)/man5 doc/prebuilt/man/libeufin-ebisync.conf.5 install -D -t $(bin_dir) contrib/libeufin-dbconfig install -D -t $(bin_dir) contrib/libeufin-ebisync-dbconfig install -D -t $(bin_dir) contrib/libeufin-tan-*.sh @@ -37,18 +43,9 @@ install-nobuild-files: .PHONY: install install: build install-nobuild-files -# Install libeufin-bank install -D -t $(bin_dir) target/release/libeufin-bank - install -m 644 -D -t $(man_dir)/man1 doc/prebuilt/man/libeufin-bank.1 - install -m 644 -D -t $(man_dir)/man5 doc/prebuilt/man/libeufin-bank.conf.5 -# Install libeufin-nexus install -D -t $(bin_dir) target/release/libeufin-nexus - install -m 644 -D -t $(man_dir)/man1 doc/prebuilt/man/libeufin-nexus.1 - install -m 644 -D -t $(man_dir)/man5 doc/prebuilt/man/libeufin-nexus.conf.5 -# Install libeufin-ebisync install -D -t $(bin_dir) target/release/libeufin-ebisync - install -m 644 -D -t $(man_dir)/man1 doc/prebuilt/man/libeufin-ebisync.1 - install -m 644 -D -t $(man_dir)/man5 doc/prebuilt/man/libeufin-ebisync.conf.5 .PHONY: check check: install-nobuild-files diff --git a/libeufin-bank/src/api.rs b/libeufin-bank/src/api.rs @@ -311,7 +311,7 @@ pub mod test { impl BankTestCtx { pub async fn new(conf: &str) -> Self { let cfg = Config::from_file(CONFIG_SOURCE, Some(format!("conf/{conf}"))).unwrap(); - let cfg = BankCfg::parse(cfg).unwrap(); + let cfg = BankCfg::parse(&cfg).unwrap(); let dir = cfg.db_cfg.sql_dir.as_ref(); let (mut conn, db) = db_test_setup_manual(dir, "libeufin-bank").await; dbinit(&mut conn, dir, "libeufin-bank", true).await.unwrap(); @@ -443,7 +443,7 @@ pub mod test { let cfg = Config::from_file(CONFIG_SOURCE, Some(format!("conf/{conf}"))).unwrap(); let state = Arc::new( - BankState::start(self.state.db.clone(), BankCfg::parse(cfg).unwrap()).await, + BankState::start(self.state.db.clone(), BankCfg::parse(&cfg).unwrap()).await, ); self.server = bank_api(state.clone()); self.state = state; diff --git a/libeufin-bank/src/config.rs b/libeufin-bank/src/config.rs @@ -121,7 +121,7 @@ impl BankCfg { }) } - pub fn parse(cfg: Config) -> Result<Self, ValueErr> { + pub fn parse(cfg: &Config) -> Result<Self, ValueErr> { let s = cfg.section("libeufin-bank"); let base_url = s.base_url("base_url").require()?; let hostname = base_url.host_str().unwrap_or_default().into(); @@ -134,7 +134,7 @@ impl BankCfg { let allow_conversion = s.boolean("allow_conversion").default(false)?; let fiat = if allow_conversion { let currency = s.currency("fiat_currency").require()?; - let spec = Self::currency_specification(&cfg, ¤cy)?; + let spec = Self::currency_specification(cfg, ¤cy)?; Some((currency, spec)) } else { None @@ -160,7 +160,7 @@ impl BankCfg { .default(CompactString::const_new("Taler Bank"))?, base_url, regional_currency: currency, - regional_currency_spec: Self::currency_specification(&cfg, ¤cy)?, + regional_currency_spec: Self::currency_specification(cfg, ¤cy)?, wire_transfer_fees: s.amount("wire_transfer_fees", ¤cy).default(zero)?, min_amount: s .amount("min_wire_transfer_amount", ¤cy) @@ -194,7 +194,7 @@ impl BankCfg { basic_auth_compat: s.boolean("pwd_auth_compat").require()?, serve: Serve::parse(&s)?, db_cfg: DbCfg::parse(cfg.section("libeufin-bankdb-postgres"))?, - cfg, + cfg: cfg.clone(), }) } diff --git a/libeufin-bank/src/lib.rs b/libeufin-bank/src/lib.rs @@ -251,7 +251,7 @@ pub enum TanChannel { email, } -pub async fn run(cfg: Config, cmd: Cmd) -> anyhow::Result<()> { +pub async fn run(cfg: &Config, cmd: Cmd) -> anyhow::Result<()> { match cmd { Cmd::Dbinit { reset } => { let cfg = BankCfg::parse(cfg)?; @@ -554,7 +554,7 @@ pub async fn run(cfg: Config, cmd: Cmd) -> anyhow::Result<()> { } } } - Cmd::Config(cmd) => cmd.run(&cfg)?, + Cmd::Config(cmd) => cmd.run(cfg)?, } Ok(()) } diff --git a/libeufin-bank/src/main.rs b/libeufin-bank/src/main.rs @@ -23,5 +23,7 @@ use taler_common::taler_main; fn main() { let args = Args::parse(); - taler_main(CONFIG_SOURCE, args.common, |cfg| run(cfg, args.cmd)) + taler_main(CONFIG_SOURCE, args.common, async |cfg| { + run(cfg, args.cmd).await + }) } diff --git a/libeufin-ebics/src/ebics/key_management.rs b/libeufin-ebics/src/ebics/key_management.rs @@ -126,7 +126,7 @@ impl EbicsClient<'_> { .. } = &self.cfg; let ctx = EbicsCtx::new(order); - info!("Doing key request {order}"); + info!(target: "ebics", "Doing key request {order}"); let (name, security_medium) = match order { Order::INI | Order::HIA => ("ebicsUnsecuredRequest", "0200"), diff --git a/libeufin-ebics/src/setup.rs b/libeufin-ebics/src/setup.rs @@ -103,7 +103,7 @@ pub async fn ebics_setup( } let new = ebics.hpb(&client).await?; - if let Some(current) = bank { + let mut bank = if let Some(current) = bank { // Check current bank keys if current.enc.as_ref() != new.enc.as_ref() { bail!( @@ -120,12 +120,13 @@ pub async fn ebics_setup( hex_chunk_by_two(ebics_pub_key_hash(&new.auth)) ) } + current } else { // Accept bank keys info!("Bank keys stored at {}", cfg.keys.bank); persist_bank_keys(&new, cfg.keys.bank.as_ref())?; + new }; - let mut bank = new; if !bank.accepted { // Finishing the setup by accepting the bank keys. let enc_hash = ebics_pub_key_hash(&bank.enc); diff --git a/libeufin-ebics/src/ws.rs b/libeufin-ebics/src/ws.rs @@ -252,7 +252,7 @@ pub async fn listen_for_notification( #[cfg(test)] mod test { - use std::{fmt::Debug, fs::Permissions, os::unix::fs::PermissionsExt as _}; + use std::{assert_matches, fmt::Debug, fs::Permissions, os::unix::fs::PermissionsExt as _}; use axum::{ extract::{ @@ -427,9 +427,9 @@ mod test { // Check message number and type assert!(count <= 3); if count == 3 { - assert!(matches!(msg, WssNotification::GeneralInfo { .. })) + assert_matches!(msg, WssNotification::GeneralInfo { .. }) } else { - assert!(matches!(msg, WssNotification::NewData { .. })) + assert_matches!(msg, WssNotification::NewData { .. }) } }) .await diff --git a/libeufin-ebisync/docker-compose.yml b/libeufin-ebisync/docker-compose.yml @@ -0,0 +1,13 @@ +services: + azurite: + image: mcr.microsoft.com/azure-storage/azurite + command: azurite --blobHost 0.0.0.0 --debug /data/debug.log + container_name: azurite + ports: + - 10000:10000 + volumes: + - ../azurite:/data + restart: unless-stopped + +volumes: + azurite-data: +\ No newline at end of file diff --git a/libeufin-ebisync/src/azure.rs b/libeufin-ebisync/src/azure.rs @@ -187,9 +187,19 @@ impl<'a> AzureBlobStorage<'a> { } } - pub async fn create_container(&self, name: &'static str) -> AzureResult { + pub async fn create_container(&self, name: &str) -> AzureResult { self.req( - Req::new(self.client, Method::PUT, &self.base_url, name).query("restype", "container"), + Req::new(self.client, Method::PUT, &self.base_url, name.to_owned()) + .query("restype", "container"), + ) + .await + } + + pub async fn container_metadata(&self, name: &str) -> AzureResult { + self.req( + Req::new(self.client, Method::GET, &self.base_url, name.to_owned()) + .query("restype", "container") + .query("comp", "metadata"), ) .await } diff --git a/libeufin-ebisync/src/config.rs b/libeufin-ebisync/src/config.rs @@ -102,9 +102,9 @@ pub struct EbisyncSubmitCfg { impl EbisyncSubmitCfg { pub fn parse(cfg: &Config) -> Result<Self, ValueErr> { - let s = cfg.section("ebisync-fetch"); + let s = cfg.section("ebisync-submit"); Ok(Self { - source: map_config!(s, "ebics file destination", "destination", + source: map_config!(s, "ebics file source", "source", "none" => { Source::None }, "ebisync-api" => { Source::SyncAPI(AuthCfg::parse(&s)?) } ) @@ -159,8 +159,7 @@ impl EbisyncHostCfg { } } -pub struct EbisyncCfg { - pub cfg: Config, +pub struct EbisyncCfg<'a> { pub bank: String, pub client: String, pub host: EbisyncHostCfg, @@ -169,10 +168,11 @@ pub struct EbisyncCfg { pub setup: OnceCell<EbisyncSetupCfg>, pub serve: OnceCell<EbisyncServeCfg>, pub db_cfg: DbCfg, + pub cfg: &'a Config, } -impl EbisyncCfg { - pub fn parse(cfg: Config) -> Result<Self, ValueErr> { +impl<'a> EbisyncCfg<'a> { + pub fn parse(cfg: &'a Config) -> Result<Self, ValueErr> { let s = cfg.section("ebisync"); Ok(Self { host: EbisyncHostCfg::parse(&s)?, @@ -182,7 +182,7 @@ impl EbisyncCfg { submit: OnceCell::new(), setup: OnceCell::new(), serve: OnceCell::new(), - db_cfg: parse_db_cfg(&cfg)?, + db_cfg: parse_db_cfg(cfg)?, cfg, }) } @@ -192,7 +192,7 @@ impl EbisyncCfg { if let Some(fetch) = self.fetch.get() { return Ok(fetch); } - let fetch = EbisyncFetchCfg::parse(&self.cfg)?; + let fetch = EbisyncFetchCfg::parse(self.cfg)?; self.fetch.set(fetch).ok(); Ok(self.fetch.get().unwrap()) } @@ -202,7 +202,7 @@ impl EbisyncCfg { if let Some(submit) = self.submit.get() { return Ok(submit); } - let submit = EbisyncSubmitCfg::parse(&self.cfg)?; + let submit = EbisyncSubmitCfg::parse(self.cfg)?; self.submit.set(submit).ok(); Ok(self.submit.get().unwrap()) } @@ -212,17 +212,17 @@ impl EbisyncCfg { if let Some(serve) = self.serve.get() { return Ok(serve); } - let serve = EbisyncServeCfg::parse(&self.cfg)?; + let serve = EbisyncServeCfg::parse(self.cfg)?; self.serve.set(serve).ok(); Ok(self.serve.get().unwrap()) } - pub fn ebics_setup<'a>(&'a self) -> Result<EbicsSetupCfg<'a>, ValueErr> { + pub fn ebics_setup(&'a self) -> Result<EbicsSetupCfg<'a>, ValueErr> { let setup = { if let Some(setup) = self.setup.get() { setup } else { - let setup = EbisyncSetupCfg::parse(&self.cfg)?; + let setup = EbisyncSetupCfg::parse(self.cfg)?; self.setup.set(setup).ok(); self.setup.get().unwrap() } diff --git a/libeufin-ebisync/src/lib.rs b/libeufin-ebisync/src/lib.rs @@ -26,7 +26,7 @@ use std::{ time::Duration, }; -use anyhow::anyhow; +use anyhow::{anyhow, bail}; use axum::{body::Bytes, http::HeaderValue}; use compact_str::CompactStringExt; use http_client::Client; @@ -48,7 +48,7 @@ use taler_api::api::TalerRouter; use taler_build::long_version; use taler_common::{CommonArgs, cli::ConfigCmd, config::Config, types::utils::date_to_utc_ts}; use tokio::{time::timeout, try_join}; -use tracing::{debug, error, info, trace}; +use tracing::{debug, error, info, trace, warn}; use crate::{ api::{EbisyncState, sync_api}, @@ -66,6 +66,13 @@ pub mod db; const CHECKPOINT_KEY: &str = "checkpoint"; const FETCH_TASK_KEY: &str = "fetch_task"; +/// Testing helper commands +#[derive(clap::Subcommand, Debug)] +pub enum TestingCmd { + // Create the container + CreateContainer, +} + #[derive(clap::Subcommand, Debug)] pub enum Cmd { /// Initialize libeufin-ebisync database @@ -119,6 +126,8 @@ pub enum Cmd { }, #[command(subcommand)] Config(ConfigCmd), + #[command(subcommand)] + Testing(TestingCmd), } #[derive(clap::Parser, Debug)] @@ -133,7 +142,7 @@ pub struct Args { pub async fn ebics_setup( ebics: &EbicsClient<'_>, - cfg: &EbisyncCfg, + cfg: &EbisyncCfg<'_>, db: &PgPool, force_keys_resubmission: bool, generate_registration_pdf: bool, @@ -177,6 +186,20 @@ pub async fn ebics_setup( debug!(target: "setup", "{fmt}"); info!(target: "setup", "EBICS ready"); + info!(target: "setup", "Check fetch destination setup"); + if let Some(dest) = + DestinationClient::prepare(&cfg.fetch()?.destination, &http_client::client())? + { + match dest { + DestinationClient::AzureBlobStorage { client, container } => { + client.container_metadata(container).await?; + } + } + } else { + warn!(target: "setup", "No destination configured"); + } + info!(target: "setup", "Fetch destination ready"); + eprintln!("setup ready"); Ok(()) } @@ -223,7 +246,7 @@ impl<'a> DestinationClient<'a> { pub async fn ebics_fetch( ebics: &EbicsClient<'_>, - cfg: &EbisyncCfg, + cfg: &EbisyncCfg<'_>, client: &ClientKeys, bank: &BankKeys, db: &PgPool, @@ -418,10 +441,10 @@ pub async fn ebics_fetch( Ok(()) } -pub async fn run(cfg: Config, cmd: Cmd) -> anyhow::Result<()> { +pub async fn run(cfg: &Config, cmd: Cmd) -> anyhow::Result<()> { match cmd { Cmd::Dbinit { reset } => { - let cfg = parse_db_cfg(&cfg)?; + let cfg = parse_db_cfg(cfg)?; dbinit(&cfg, reset).await?; } Cmd::Setup { @@ -494,7 +517,20 @@ pub async fn run(cfg: Config, cmd: Cmd) -> anyhow::Result<()> { .await?; } } - Cmd::Config(cmd) => cmd.run(&cfg)?, + Cmd::Config(cmd) => cmd.run(cfg)?, + Cmd::Testing(cmd) => match cmd { + TestingCmd::CreateContainer => { + let cfg = EbisyncCfg::parse(cfg)?; + let http = http_client::client(); + let dest = DestinationClient::prepare(&cfg.fetch()?.destination, &http)?; + if let Some(DestinationClient::AzureBlobStorage { client, container }) = dest { + client.create_container(container).await?; + info!("Created container {container}"); + } else { + bail!("Destination is not azure-blob-storage") + } + } + }, } Ok(()) } @@ -513,7 +549,7 @@ mod ebics { let args = std::iter::once("libeufin-ebisync").chain(parts.iter().map(|it| it.as_str())); let cmd = Args::try_parse_from(args).unwrap(); - run(cfg.clone(), cmd.cmd).await + run(cfg, cmd.cmd).await } async fn test_setup() -> (TestBank, Config, PgPool) { diff --git a/libeufin-ebisync/src/main.rs b/libeufin-ebisync/src/main.rs @@ -23,5 +23,7 @@ use taler_common::taler_main; fn main() { let args = Args::parse(); - taler_main(CONFIG_SOURCE, args.common, |cfg| run(cfg, args.cmd)) + taler_main(CONFIG_SOURCE, args.common, async |cfg| { + run(cfg, args.cmd).await + }) } diff --git a/libeufin-nexus/src/config.rs b/libeufin-nexus/src/config.rs @@ -222,8 +222,8 @@ impl NexusSetupConfig { } } -pub struct NexusCfg { - pub cfg: Config, +pub struct NexusCfg<'a> { + pub cfg: &'a Config, pub currency: Currency, pub account_type: AccountType, pub keys: OnceCell<NexusKeysCfg>, @@ -238,8 +238,8 @@ pub struct NexusCfg { pub serve_cfg: Serve, } -impl NexusCfg { - pub fn parse(cfg: Config) -> Result<Self, ValueErr> { +impl<'a> NexusCfg<'a> { + pub fn parse(cfg: &'a Config) -> Result<Self, ValueErr> { let s = cfg.section("nexus-ebics"); Ok(Self { currency: s.currency("currency").require()?, @@ -253,7 +253,7 @@ impl NexusCfg { submit: OnceCell::new(), ebics: OnceCell::new(), setup: OnceCell::new(), - db_cfg: parse_db_cfg(&cfg)?, + db_cfg: parse_db_cfg(cfg)?, cfg, }) } @@ -263,7 +263,7 @@ impl NexusCfg { if let Some(keys) = self.keys.get() { return Ok(keys); } - let keys = NexusKeysCfg::parse(&self.cfg)?; + let keys = NexusKeysCfg::parse(self.cfg)?; self.keys.set(keys).ok(); Ok(self.keys.get().unwrap()) } @@ -273,7 +273,7 @@ impl NexusCfg { if let Some(host) = self.host.get() { return Ok(host); } - let host = NexusHostCfg::parse(&self.cfg)?; + let host = NexusHostCfg::parse(self.cfg)?; self.host.set(host).ok(); Ok(self.host.get().unwrap()) } @@ -283,7 +283,7 @@ impl NexusCfg { if let Some(fetch) = self.fetch.get() { return Ok(fetch); } - let fetch = NexusFetchCfg::parse(&self.cfg, &self.currency)?; + let fetch = NexusFetchCfg::parse(self.cfg, &self.currency)?; self.fetch.set(fetch).ok(); Ok(self.fetch.get().unwrap()) } @@ -293,7 +293,7 @@ impl NexusCfg { if let Some(submit) = self.submit.get() { return Ok(submit); } - let submit = NexusSubmitCfg::parse(&self.cfg)?; + let submit = NexusSubmitCfg::parse(self.cfg)?; self.submit.set(submit).ok(); Ok(self.submit.get().unwrap()) } @@ -303,7 +303,7 @@ impl NexusCfg { if let Some(ebics) = self.ebics.get() { return Ok(ebics); } - let ebics = NexusEbicsConfig::parse(&self.cfg)?; + let ebics = NexusEbicsConfig::parse(self.cfg)?; self.ebics.set(ebics).ok(); Ok(self.ebics.get().unwrap()) } @@ -313,7 +313,7 @@ impl NexusCfg { if let Some(setup) = self.setup.get() { return Ok(setup); } - let setup = NexusSetupConfig::parse(&self.cfg)?; + let setup = NexusSetupConfig::parse(self.cfg)?; self.setup.set(setup).ok(); Ok(self.setup.get().unwrap()) } @@ -331,7 +331,7 @@ impl NexusCfg { }) } - pub fn ebics_setup<'a>(&'a self) -> Result<EbicsSetupCfg<'a>, ValueErr> { + pub fn ebics_setup(&'a self) -> Result<EbicsSetupCfg<'a>, ValueErr> { let setup = self.setup()?; Ok(EbicsSetupCfg { keys: self.keys()?.ebics(), diff --git a/libeufin-nexus/src/db/initiated.rs b/libeufin-nexus/src/db/initiated.rs @@ -465,7 +465,7 @@ pub async fn tx_status_update( #[cfg(test)] mod test { - use std::str::FromStr as _; + use std::{assert_matches, str::FromStr as _}; use jiff::{Span, Timestamp, civil::Date}; use libeufin_ebics::{ebics::rand_ebics_id, iso20022::model::Tx}; @@ -493,7 +493,7 @@ mod test { pub async fn initiated_skip() { let (_, db) = db_setup().await; let cfg = Config::from_file(CONFIG_SOURCE, Some("conf/skip.conf")).unwrap(); - let cfg = NexusCfg::parse(cfg).unwrap(); + let cfg = NexusCfg::parse(&cfg).unwrap(); let cfg = cfg.ingest().unwrap(); let millis = Span::new().milliseconds(10); @@ -621,10 +621,10 @@ mod test { .unwrap(); // Create a test batch with three transactions for id in ["TX", "TX_SETTLED"] { - assert!(matches!( + assert_matches!( gen_initiate(db, id, "lol").await, PaymentInitiationResult::Success(_) - )); + ); } batch_initiated(db, &Timestamp::now(), "BATCH", false) .await @@ -632,19 +632,19 @@ mod test { // Create witness transactions and batch for id in ["WITNESS_1", "WITNESS_2"] { - assert!(matches!( + assert_matches!( gen_initiate(db, id, "lol").await, PaymentInitiationResult::Success(_) - )); + ); } batch_initiated(db, &Timestamp::now(), "BATCH_WITNESS", false) .await .unwrap(); for id in ["WITNESS_3", "WITNESS_4"] { - assert!(matches!( + assert_matches!( gen_initiate(db, id, "lol").await, PaymentInitiationResult::Success(_) - )); + ); } // Check everything is unsubmitted sqlx::query( @@ -859,10 +859,10 @@ mod test { let (_, db) = db_setup().await; let now = Timestamp::now(); for i in 0..6 { - assert!(matches!( + assert_matches!( gen_initiate(&db, format!("PAY{i}"), "").await, PaymentInitiationResult::Success(_) - )); + ); batch_initiated(&db, &now, &rand_ebics_id(), false) .await .unwrap(); diff --git a/libeufin-nexus/src/db/payment.rs b/libeufin-nexus/src/db/payment.rs @@ -307,6 +307,7 @@ pub async fn register_in_malformed( #[cfg(test)] mod test { + use std::assert_matches; use jiff::Timestamp; use libeufin_ebics::{ @@ -347,10 +348,10 @@ mod test { format!("{} https://exchange.com/", ShortHashCode::rand()), ] { let payment = gen_out_pay(subject.clone()); - assert!(matches!( + assert_matches!( gen_initiate(&db, payment.id.e2e_id.clone().unwrap(), subject).await, PaymentInitiationResult::Success(_) - )); + ); let first = register_outgoing(&db, &payment).await.unwrap(); assert_eq!( first, @@ -461,10 +462,10 @@ mod test { format!("{wtid} https://exchange.com/"), format!("{wtid} https://exchange.com/"), ] { - assert!(matches!( + assert_matches!( gen_initiate(&db, rand_ebics_id(), subject).await, PaymentInitiationResult::Success(_) - )); + ); } batch_initiated(&db, &Timestamp::now(), "BATCH", false) .await @@ -549,17 +550,15 @@ mod test { ) .await .unwrap(); - assert!( - matches!( - res, - IncomingBounceRegistrationResult::Success(InResult { - new: true, - id: _, - completed: false, - pending: false, - ref bounce_id - }) if bounce_id.as_ref() == Some(&id) - ), + assert_matches!( + res, + IncomingBounceRegistrationResult::Success(InResult { + new: true, + id: _, + completed: false, + pending: false, + ref bounce_id + }) if bounce_id.as_ref() == Some(&id), "{res:?}" ); // Idempotent @@ -573,17 +572,15 @@ mod test { ) .await .unwrap(); - assert!( - matches!( - res, - IncomingBounceRegistrationResult::Success(InResult { - new: false, - id: _, - completed: false, - pending: false, - ref bounce_id - }) if bounce_id.as_ref() == Some(&id) - ), + assert_matches!( + res, + IncomingBounceRegistrationResult::Success(InResult { + new: false, + id: _, + completed: false, + pending: false, + ref bounce_id + }) if bounce_id.as_ref() == Some(&id), "{res:?}" ); diff --git a/libeufin-nexus/src/fetch.rs b/libeufin-nexus/src/fetch.rs @@ -72,7 +72,7 @@ use crate::{ pub async fn ebics_fetch( ebics: &EbicsClient<'_>, - cfg: &NexusCfg, + cfg: &NexusCfg<'_>, client: &ClientKeys, bank: &BankKeys, db: &PgPool, @@ -395,7 +395,7 @@ pub async fn ebics_fetch( Ok(()) } -pub async fn register_camt(db: &PgPool, cfg: &NexusCfg, xml: &[u8]) -> anyhow::Result<usize> { +pub async fn register_camt(db: &PgPool, cfg: &NexusCfg<'_>, xml: &[u8]) -> anyhow::Result<usize> { let account = &cfg.ebics()?.account; let ingest_cfg = cfg.ingest()?; let mut nb_tx = 0; diff --git a/libeufin-nexus/src/lib.rs b/libeufin-nexus/src/lib.rs @@ -204,7 +204,7 @@ pub fn batch_pain001( pub async fn ebics_submit( ebics: &EbicsClient<'_>, - cfg: &NexusCfg, + cfg: &NexusCfg<'_>, client: &ClientKeys, bank: &BankKeys, db: &PgPool, @@ -311,7 +311,7 @@ pub async fn ebics_submit( pub async fn ebics_setup( ebics: &EbicsClient<'_>, - cfg: &NexusCfg, + cfg: &NexusCfg<'_>, db: &PgPool, force_keys_resubmission: bool, generate_registration_pdf: bool, @@ -443,10 +443,10 @@ pub async fn ebics_setup( Ok(()) } -pub async fn run(cfg: Config, cmd: Cmd) -> anyhow::Result<()> { +pub async fn run(cfg: &Config, cmd: Cmd) -> anyhow::Result<()> { match cmd { Cmd::Dbinit { reset } => { - let cfg = parse_db_cfg(&cfg)?; + let cfg = parse_db_cfg(cfg)?; dbinit(&cfg, reset).await?; } Cmd::EbicsSetup { @@ -585,7 +585,7 @@ pub async fn run(cfg: Config, cmd: Cmd) -> anyhow::Result<()> { let pool = pool(&cfg.db_cfg).await?; cmd.run(&pool, &cfg.currency).await?; } - Cmd::Config(cmd) => cmd.run(&cfg)?, + Cmd::Config(cmd) => cmd.run(cfg)?, Cmd::Testing(cmd) => cmd.run(cfg).await?, } Ok(()) diff --git a/libeufin-nexus/src/main.rs b/libeufin-nexus/src/main.rs @@ -23,5 +23,7 @@ use taler_common::taler_main; fn main() { let args = Args::parse(); - taler_main(CONFIG_SOURCE, args.common, |cfg| run(cfg, args.cmd)) + taler_main(CONFIG_SOURCE, args.common, async |cfg| { + run(cfg, args.cmd).await + }) } diff --git a/libeufin-nexus/src/manual.rs b/libeufin-nexus/src/manual.rs @@ -68,7 +68,7 @@ pub enum ManualCmd { } impl ManualCmd { - pub async fn run(self, db: &PgPool, cfg: &NexusCfg) -> anyhow::Result<()> { + pub async fn run(self, db: &PgPool, cfg: &NexusCfg<'_>) -> anyhow::Result<()> { match self { ManualCmd::Export { out } => { let out: &mut dyn Write = if out == "-" { diff --git a/libeufin-nexus/src/test.rs b/libeufin-nexus/src/test.rs @@ -299,7 +299,7 @@ mod ebics { let args = std::iter::once("libeufin_nexus").chain(parts.iter().map(|it| it.as_str())); let cmd = Args::try_parse_from(args).unwrap(); - run(cfg.clone(), cmd.cmd).await + run(cfg, cmd.cmd).await } async fn test_setup() -> (TestBank, Config, PgPool) { diff --git a/libeufin-nexus/src/testing.rs b/libeufin-nexus/src/testing.rs @@ -126,7 +126,7 @@ pub enum TestingCmd { } impl TestingCmd { - pub async fn run(self, cfg: Config) -> anyhow::Result<()> { + pub async fn run(self, cfg: &Config) -> anyhow::Result<()> { match self { TestingCmd::Iban(cmd) => cmd.run()?, TestingCmd::FakeIncoming { diff --git a/testbench/Cargo.toml b/testbench/Cargo.toml @@ -14,10 +14,10 @@ anyhow.workspace = true jiff.workspace = true clap.workspace = true taler-common.workspace = true +taler-test-utils.workspace = true libeufin-nexus = { path = "../libeufin-nexus"} +libeufin-ebisync = { path = "../libeufin-ebisync"} libeufin-ebics = { path = "../libeufin-ebics"} -reedline = "0.48" -shlex.workspace = true owo-colors = "4.3" tracing-subscriber = "0.3" diff --git a/testbench/src/main.rs b/testbench/src/main.rs @@ -17,16 +17,16 @@ * <http://www.gnu.org/licenses/> */ -use std::{borrow::Cow, fmt::Display, str::FromStr}; +use std::{fmt::Display, str::FromStr}; use anyhow::bail; use clap::{Parser, ValueEnum}; use jiff::Timestamp; use libeufin_ebics::keys::{load_bank_keys, load_client_keys}; -use libeufin_nexus::{CONFIG_SOURCE, config::NexusCfg, run}; +use libeufin_nexus::config::NexusCfg; use owo_colors::OwoColorize as _; -use reedline::{FileBackedHistory, Prompt, Reedline, Signal}; use taler_common::{config::Config, log::taler_logger, types::payto::TransferIbanPayto}; +use taler_test_utils::{cli::clap_parse, repl::test_repl}; use tracing::Level; use tracing_subscriber::util::SubscriberInitExt as _; @@ -36,6 +36,37 @@ enum Component { Ebisync, } +impl Component { + pub async fn run(&self, cfg: &Config, cmd: &str) -> bool { + match self { + Component::Nexus => match clap_parse::<libeufin_nexus::Args>(cmd) { + Ok(cmd) => { + tokio::select! { + res = libeufin_nexus::run(cfg, cmd.cmd) => check(res), + _ = tokio::signal::ctrl_c() => false + } + } + Err(e) => { + println!("Error: {}", e); + false + } + }, + Component::Ebisync => match clap_parse::<libeufin_ebisync::Args>(cmd) { + Ok(cmd) => { + tokio::select! { + res = libeufin_ebisync::run(cfg, cmd.cmd) => check(res), + _ = tokio::signal::ctrl_c() => false + } + } + Err(e) => { + println!("Error: {}", e); + false + } + }, + } + } +} + #[derive(Parser)] /// Run integration tests on banks provider pub struct TestbenchCmd { @@ -52,6 +83,7 @@ pub enum NexusCmd { Setup, ResetKeys, ResetDb, + Exit, Tx, Fetch { #[arg(trailing_var_arg = true, allow_hyphen_values = true)] @@ -67,7 +99,26 @@ pub enum NexusCmd { }, Wss, TxCheck, +} + +#[derive(Parser)] +#[command(name = "shell", no_binary_name = true)] +/// Run integration tests on banks provider +pub enum SyncCmd { + #[clap(alias = "ebics-setup")] + Setup, + ResetKeys, + ResetDb, Exit, + Fetch { + #[arg(trailing_var_arg = true, allow_hyphen_values = true)] + raw_args: Vec<String>, + }, + Testing { + #[arg(trailing_var_arg = true, allow_hyphen_values = true)] + raw_args: Vec<String>, + }, + Serve, } fn step(name: impl Display) { @@ -93,28 +144,11 @@ fn check<R, E: Display>(res: Result<R, E>) -> bool { res.is_ok() } -pub async fn nexus_cmd(cfg: &Config, cmd: &str) -> bool { - let parts = shlex::split(cmd).unwrap(); - let args = std::iter::once("libeufin_nexus").chain(parts.iter().map(|it| it.as_str())); - - match libeufin_nexus::Args::try_parse_from(args) { - Ok(cmd) => { - tokio::select! { - res = run(cfg.clone(), cmd.cmd) => check(res), - _ = tokio::signal::ctrl_c() => false - } - } - Err(e) => { - println!("Error: {}", e); - false - } - } -} - #[tokio::main] async fn main() -> anyhow::Result<()> { taler_logger(Some(Level::DEBUG), false).init(); let cmd = TestbenchCmd::parse(); + // List available platform let platforms: Vec<_> = std::fs::read_dir("testbench/test/platform") .unwrap() @@ -145,69 +179,64 @@ async fn main() -> anyhow::Result<()> { // Augment config let simple_cfg = std::fs::read_to_string(format!("testbench/test/platform/{}.conf", cmd.platform)).unwrap(); - let cfg = format!( - r#" - {simple_cfg} - {} - [paths] - LIBEUFIN_NEXUS_HOME = testbench/test/{} - EBISYNC_HOME = testbench/test/{} - - [nexus-fetch] - FREQUENCY = 1h - CHECKPOINT_TIME_OF_DAY = 16:52 - - [ebisync-fetch] - FREQUENCY = 1h - CHECKPOINT_TIME_OF_DAY = 16:52 - DESTINATION = azure-blob-storage - AZURE_API_URL = http://localhost:10000/devstoreaccount1/ - AZURE_ACCOUNT_NAME = devstoreaccount1 - AZURE_ACCOUNT_KEY = Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw== - AZURE_CONTAINER = test - - [ebisync-submit] - SOURCE = ebisync-api - AUTH_METHOD = none - - [libeufin-nexusdb-postgres] - CONFIG = postgres:///libeufintestbench - - [ebisyncdb-postgres] - CONFIG = postgres:///libeufintestbench - "#, - simple_cfg - .replace("[nexus-ebics]", "[ebisync]") - .replace("[nexus-setup]", "[ebisync-setup]"), - cmd.platform, - cmd.platform - ); - - let history = Box::new( - FileBackedHistory::with_file( - 1000, - match cmd.component { - Component::Ebisync => ".ebisync_history", - Component::Nexus => ".nexus_history", - } - .into(), - ) - .expect("Error configuring history with file"), - ); - let cfg = Config::from_mem_with_env(CONFIG_SOURCE, &cfg).unwrap(); - let cfg = NexusCfg::parse(cfg).unwrap(); + let cfg = { + let mut parser = taler_common::config::parser::Parser::empty(); + parser.load_env(libeufin_nexus::CONFIG_SOURCE).unwrap(); + parser + .load_env(libeufin_ebisync::config::CONFIG_SOURCE) + .unwrap(); + parser.parse_str(&format!( + r#" + {simple_cfg} + {} + [paths] + LIBEUFIN_NEXUS_HOME = testbench/test/{} + EBISYNC_HOME = testbench/test/{} + + [nexus-fetch] + FREQUENCY = 1h + CHECKPOINT_TIME_OF_DAY = 16:52 + + [ebisync-fetch] + FREQUENCY = 1h + CHECKPOINT_TIME_OF_DAY = 16:52 + DESTINATION = azure-blob-storage + AZURE_API_URL = http://localhost:10000/devstoreaccount1/ + AZURE_ACCOUNT_NAME = devstoreaccount1 + AZURE_ACCOUNT_KEY = Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw== + AZURE_CONTAINER = test + + [ebisync-submit] + SOURCE = ebisync-api + AUTH_METHOD = none + + [libeufin-nexusdb-postgres] + CONFIG = postgres:///libeufintestbench + + [ebisyncdb-postgres] + CONFIG = postgres:///libeufintestbench + "#, + simple_cfg + .replace("[nexus-ebics]", "[ebisync]") + .replace("[nexus-setup]", "[ebisync-setup]"), + cmd.platform, + cmd.platform + )).unwrap(); + parser.finish() + }; + let cfg = NexusCfg::parse(&cfg).unwrap(); let ebics = cfg.keys().unwrap(); let (name, settings) = match cfg.host().unwrap().base_url.as_str() { "https://isotest.postfinance.ch/ebicsweb/ebicsweb" => ( - Some("PostFinance IsoTest"), + "PostFinance IsoTest", Some("https://isotest.postfinance.ch/corporates/user/settings/ebics"), ), "https://iso20022test.credit-suisse.com/ebicsweb/ebicsweb" => ( - Some("Credit Suisse isoTest"), + "Credit Suisse isoTest", Some("https://iso20022test.credit-suisse.com/user/settings/ebics"), ), - "https://ebics.postfinance.ch/ebics/ebics.aspx" => (Some("PostFinance"), None), - _ => (None, None), + "https://ebics.postfinance.ch/ebics/ebics.aspx" => ("PostFinance", None), + _ => ("Unknown", None), }; let test = settings.is_some(); let payto = match cfg.currency.as_ref() { @@ -221,186 +250,154 @@ async fn main() -> anyhow::Result<()> { }; let payto = TransferIbanPayto::from_str(payto).unwrap(); let ebics_log = format!("--debug-ebics testbench/test/{}", cmd.platform); - - let mut line_editor = Reedline::create().with_history(history); - let prompt = BenchPrompt { - prompt: format!( - "{:?} {}{}", - cmd.component, - cmd.platform, - std::fmt::from_fn(|f| { - if let Some(name) = &name { - write!(f, " {name}") + let run = async |args: &str| cmd.component.run(cfg.cfg, args).await; + let auto_setup = async || { + let client = load_client_keys(ebics.client.as_ref()).unwrap(); + let bank = load_bank_keys(ebics.bank.as_ref()).unwrap(); + if settings.is_none() && client.is_none() { + msg("Manual setup is required for non test environment") + } else if client + .map(|it| !it.submitted_ini || !it.submitted_hia) + .unwrap_or(true) + || bank.map(|it| !it.accepted).unwrap_or(true) + { + step("Run EBICS setup"); + if !run(&format!("ebics-setup {ebics_log}")).await { + if let Some(settings) = settings { + let client = load_client_keys(ebics.client.as_ref()).unwrap(); + if client + .map(|it| !it.submitted_ini || !it.submitted_hia) + .unwrap_or(true) + { + msg(format_args!( + "Got to {settings} and click on 'Reset EBICS user'" + )) + } else { + msg(format_args!( + "Got to {settings} and click on 'Activate EBICS user'" + )) + } } else { - Ok(()) + msg("Activate your keys at your bank") } - }) - ), + } + } }; - loop { - // Automatic setup - { - let client = load_client_keys(ebics.client.as_ref()).unwrap(); - let bank = load_bank_keys(ebics.bank.as_ref()).unwrap(); - if settings.is_none() && client.is_none() { - msg("Manual setup is required for non test environment") - } else if client - .map(|it| !it.submitted_ini || !it.submitted_hia) - .unwrap_or(true) - || bank.map(|it| !it.accepted).unwrap_or(true) - { - step("Run EBICS setup"); - if !nexus_cmd(&cfg.cfg, &format!("ebics-setup {ebics_log}")).await { - if let Some(settings) = settings { - let client = load_client_keys(ebics.client.as_ref()).unwrap(); - if client - .map(|it| !it.submitted_ini || !it.submitted_hia) - .unwrap_or(true) - { - msg(format_args!( - "Got to {settings} and click on 'Reset EBICS user'" - )) - } else { - msg(format_args!( - "Got to {settings} and click on 'Activate EBICS user'" + msg(format!("Init {name}")); + run("dbinit").await; + + let prompt = format!("{:?} {} {name}", cmd.component, cmd.platform); + match cmd.component { + Component::Nexus => { + test_repl( + ".nexus_history", + format!("{:?} {} {name}", cmd.component, cmd.platform), + auto_setup, + async |cmd| { + match cmd { + NexusCmd::Setup => { + run(&format!("ebics-setup {ebics_log}")).await; + } + NexusCmd::ResetDb => { + run("dbinit -r").await; + } + NexusCmd::Fetch { raw_args } => { + run(&format!("ebics-fetch {ebics_log} {}", raw_args.join(" "))).await; + } + NexusCmd::Submit { raw_args } => { + run(&format!("ebics-submit {ebics_log} {}", raw_args.join(" "))).await; + } + NexusCmd::Tx => { + run(&format!( + "initiate-payment --amount={}:0.1 --subject=\"single {}\" {payto}", + cfg.currency, + Timestamp::now() )) + .await; } - } else { - msg("Activate your keys at your bank") + NexusCmd::List { raw_args } => { + run(&format!("list {}", raw_args.join(" "))).await; + } + NexusCmd::ResetKeys => { + if test { + std::fs::remove_file(&ebics.client).unwrap(); + } + std::fs::remove_file(&ebics.bank).unwrap(); + } + NexusCmd::TxCheck => { + run(&format!("testing tx-check {ebics_log}")).await; + } + NexusCmd::Wss => { + run(&format!("testing wss {ebics_log}")).await; + } + NexusCmd::Exit => return true, } - } - } + false + }, + ) + .await; } - let Signal::Success(buf) = line_editor.read_line(&prompt).unwrap() else { - break; - }; - match NexusCmd::try_parse_from(buf.split_whitespace()) { - Ok(cmd) => match cmd { - NexusCmd::Setup => { - nexus_cmd(&cfg.cfg, &format!("ebics-setup {ebics_log}")).await; - } - NexusCmd::ResetDb => { - nexus_cmd(&cfg.cfg, "dbinit -r").await; - } - NexusCmd::Fetch { raw_args } => { - nexus_cmd( - &cfg.cfg, - &format!( - "ebics-fetch {ebics_log} {}", - shlex::try_join(raw_args.iter().map(|it| it.as_str())).unwrap() - ), - ) - .await; - } - NexusCmd::Submit { raw_args } => { - nexus_cmd( - &cfg.cfg, - &format!( - "ebics-submit {ebics_log} {}", - shlex::try_join(raw_args.iter().map(|it| it.as_str())).unwrap() - ), - ) - .await; - } - NexusCmd::Tx => { - nexus_cmd( - &cfg.cfg, - &format!( - "initiate-payment --amount={}:0.1 --subject=\"single {}\" {payto}", - cfg.currency, - Timestamp::now() - ), - ) - .await; - } - NexusCmd::List { raw_args } => { - nexus_cmd( - &cfg.cfg, - &format!( - "list {}", - shlex::try_join(raw_args.iter().map(|it| it.as_str())).unwrap() - ), - ) - .await; - } - NexusCmd::ResetKeys => { - if test { - std::fs::remove_file(&ebics.client)?; + Component::Ebisync => { + test_repl(".ebisync_history", prompt, auto_setup, async |cmd| { + match cmd { + SyncCmd::Setup => { + run(&format!("setup {ebics_log}")).await; } - std::fs::remove_file(&ebics.bank)?; - } - NexusCmd::TxCheck => { - nexus_cmd(&cfg.cfg, &format!("testing tx-check {ebics_log}")).await; - } - NexusCmd::Wss => { - nexus_cmd(&cfg.cfg, &format!("testing wss {ebics_log}")).await; + SyncCmd::ResetDb => { + run("dbinit -r").await; + } + SyncCmd::Fetch { raw_args } => { + run(&format!("fetch {ebics_log} {}", raw_args.join(" "))).await; + } + SyncCmd::Testing { raw_args } => { + run(&format!("testing {}", raw_args.join(" "))).await; + } + SyncCmd::Serve => { + run("serve").await; + } + SyncCmd::ResetKeys => { + if test { + std::fs::remove_file(&ebics.client).unwrap(); + } + std::fs::remove_file(&ebics.bank).unwrap(); + } + SyncCmd::Exit => return true, } - NexusCmd::Exit => return Ok(()), - }, - Err(e) => { - println!("{e}"); - } + false + }) + .await; } } Ok(()) } -struct BenchPrompt { - prompt: String, -} - -impl Prompt for BenchPrompt { - fn render_prompt_left(&self) -> Cow<'_, str> { - Cow::Borrowed(&self.prompt) - } - - fn render_prompt_right(&self) -> Cow<'_, str> { - Cow::Borrowed("") - } - - fn render_prompt_indicator(&self, _: reedline::PromptEditMode) -> Cow<'_, str> { - Cow::Borrowed(">") - } - - fn render_prompt_multiline_indicator(&self) -> Cow<'_, str> { - Cow::Borrowed(":") - } - - fn render_prompt_history_search_indicator( - &self, - _: reedline::PromptHistorySearch, - ) -> Cow<'_, str> { - Cow::Borrowed(">") - } -} - #[cfg(test)] mod test { use std::{fs::Permissions, io::Write, os::unix::fs::PermissionsExt as _, path::PathBuf}; use libeufin_ebics::keys::{BankKeys, ClientKeys, persist_bank_keys, persist_client_keys}; - use libeufin_nexus::{ - Args, CONFIG_SOURCE, - config::{NexusCfg, NexusKeysCfg}, - run, - }; - use taler_common::config::Config; + use libeufin_nexus::config::{NexusCfg, NexusKeysCfg}; - pub fn clap_parse<T: clap::Parser>(cmd: &str) -> T { - let parts = shlex::split(cmd).unwrap(); - let args = std::iter::once("bin").chain(parts.iter().map(|it| it.as_str())); - - T::try_parse_from(args).unwrap() - } + use crate::clap_parse; /** Test error format related to the keying process */ #[tokio::test] pub async fn keys() { let nexus_cmds = ["ebics-submit", "ebics-fetch"]; let nexus_all_cmds = ["ebics-submit", "ebics-fetch", "ebics-setup"]; + let sync_cmds = ["fetch"]; + let sync_all_cmds = ["fetch", "setup"]; let conf = "conf/cli.conf"; - let cfg = Config::from_file(CONFIG_SOURCE, Some(conf)).unwrap(); - let cfg = NexusCfg::parse(cfg).unwrap(); + let cfg = { + let mut parser = taler_common::config::parser::Parser::empty(); + parser.load_env(libeufin_nexus::CONFIG_SOURCE).unwrap(); + parser + .load_env(libeufin_ebisync::config::CONFIG_SOURCE) + .unwrap(); + parser.parse_file(conf.into(), 0).unwrap(); + parser.finish() + }; + let cfg = NexusCfg::parse(&cfg).unwrap(); let NexusKeysCfg { client, bank } = cfg.keys().unwrap(); let c_path = &PathBuf::from(client); @@ -412,23 +409,39 @@ mod test { let check_cmds = async |msg: &str| { for cmd in nexus_cmds { - let tmp: Args = clap_parse(cmd); - let res = run(cfg.cfg.clone(), tmp.cmd).await.unwrap_err(); + let tmp: libeufin_nexus::Args = clap_parse(cmd).unwrap(); + let res = libeufin_nexus::run(cfg.cfg, tmp.cmd).await.unwrap_err(); assert_eq!( res.to_string(), msg.replace("SETUPCMD", "libeufin-nexus ebics-setup") ); } + for cmd in sync_cmds { + let tmp: libeufin_ebisync::Args = clap_parse(cmd).unwrap(); + let res = libeufin_ebisync::run(cfg.cfg, tmp.cmd).await.unwrap_err(); + assert_eq!( + res.to_string(), + msg.replace("SETUPCMD", "libeufin-ebisync setup") + ); + } }; let check_all_cmds = async |msg: &str| { for cmd in nexus_all_cmds { - let tmp: Args = clap_parse(cmd); - let res = run(cfg.cfg.clone(), tmp.cmd).await.unwrap_err(); + let tmp: libeufin_nexus::Args = clap_parse(cmd).unwrap(); + let res = libeufin_nexus::run(cfg.cfg, tmp.cmd).await.unwrap_err(); assert_eq!( res.to_string(), msg.replace("SETUPCMD", "libeufin-nexus ebics-setup") ); } + for cmd in sync_all_cmds { + let tmp: libeufin_ebisync::Args = clap_parse(cmd).unwrap(); + let res = libeufin_ebisync::run(cfg.cfg, tmp.cmd).await.unwrap_err(); + assert_eq!( + res.to_string(), + msg.replace("SETUPCMD", "libeufin-ebisync setup") + ); + } }; // Missing client keys