diff --git a/Cargo.lock b/Cargo.lock index 38a5c45f34778a41ca766461a002b465e856a363..7da721dbca9382c3d6637651afa8b21ad7980650 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1147,14 +1147,38 @@ dependencies = [ "futures", "http", "log", + "mockall", "resiter", "serde", + "serde_json", "serde_urlencoded 0.7.0", "tokio", "unwrap", "warp", ] +[[package]] +name = "duniter-integration-tests" +version = "0.1.0" +dependencies = [ + "anyhow", + "dubp", + "duniter-conf", + "duniter-dbs", + "duniter-dbs-read-ops", + "duniter-dbs-write-ops", + "duniter-gva", + "duniter-mempools", + "duniter-module", + "duniter-server", + "fast-threadpool", + "flume", + "log", + "paste", + "resiter", + "tokio", +] + [[package]] name = "duniter-launcher" version = "1.8.1" @@ -1218,7 +1242,6 @@ dependencies = [ "paste", "resiter", "tokio", - "unwrap", ] [[package]] @@ -2420,9 +2443,9 @@ checksum = "37fd5004feb2ce328a52b0b3d01dbf4ffff72583493900ed15f22d4111c51693" [[package]] name = "once_cell" -version = "1.4.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" +checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" [[package]] name = "oorandom" diff --git a/Cargo.toml b/Cargo.toml index c671f85a12b45215f0b35d8185a459439e0cced6..09974e44493494d7dab706e8770cd626667e5201 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ members = [ "rust-libs/duniter-module", "rust-libs/duniter-server", "rust-libs/modules/duniter-gva", + "rust-libs/tests/duniter-integration-tests", "rust-libs/tools/kv_typed" ] diff --git a/rust-libs/duniter-dbs-read-ops/src/lib.rs b/rust-libs/duniter-dbs-read-ops/src/lib.rs index 45d94cb20460f33e5ebaca79757f5b9e470dce0d..26ca21089c652cad033abfbba93199e9a9abba21 100644 --- a/rust-libs/duniter-dbs-read-ops/src/lib.rs +++ b/rust-libs/duniter-dbs-read-ops/src/lib.rs @@ -34,25 +34,50 @@ use crate::pagination::{has_next_page, has_previous_page}; use dubp::common::crypto::hashs::Hash; use dubp::common::crypto::keys::ed25519::PublicKey; use dubp::documents::transaction::TransactionDocumentV10; -use dubp::{common::prelude::BlockNumber, wallet::prelude::SourceAmount}; +use dubp::{common::prelude::BlockNumber, wallet::prelude::*}; use duniter_dbs::bc_v2::BcV2DbReadable; use duniter_dbs::{ - kv_typed::prelude::*, BlockMetaV2, GvaV1DbReadable, HashKeyV2, PubKeyKeyV2, TxDbV2, - TxsMpV2DbReadable, UtxoIdDbV2, + kv_typed::prelude::*, BlockMetaV2, GvaV1DbReadable, HashKeyV2, PubKeyKeyV2, SourceAmountValV2, + TxDbV2, TxsMpV2DbReadable, UtxoIdDbV2, }; use resiter::filter::Filter; use resiter::filter_map::FilterMap; use resiter::map::Map; use std::collections::BTreeSet; -pub fn get_current_block_meta<BcDb: BcV2DbReadable>(bc_db: &BcDb) -> KvResult<Option<BlockMetaV2>> { - bc_db - .blocks_meta() - .iter(.., |it| it.reverse().values().next_res()) +#[derive(Clone, Copy, Debug)] +pub struct DbsReader; + +pub fn create_dbs_reader() -> DbsReader { + DbsReader } -pub fn get_current_ud<BcDb: BcV2DbReadable>(bc_db: &BcDb) -> KvResult<Option<SourceAmount>> { - bc_db - .uds_reval() - .iter(.., |it| it.reverse().values().map_ok(|v| v.0).next_res()) +impl DbsReader { + pub fn get_account_balance<GvaDb: GvaV1DbReadable>( + &self, + gva_db: &GvaDb, + account_script: &WalletScriptV10, + ) -> KvResult<Option<SourceAmountValV2>> { + gva_db + .balances() + .get(duniter_dbs::WalletConditionsV2::from_ref(account_script)) + } + + pub fn get_current_block_meta<BcDb: BcV2DbReadable>( + &self, + bc_db: &BcDb, + ) -> KvResult<Option<BlockMetaV2>> { + bc_db + .blocks_meta() + .iter(.., |it| it.reverse().values().next_res()) + } + + pub fn get_current_ud<BcDb: BcV2DbReadable>( + &self, + bc_db: &BcDb, + ) -> KvResult<Option<SourceAmount>> { + bc_db + .uds_reval() + .iter(.., |it| it.reverse().values().map_ok(|v| v.0).next_res()) + } } diff --git a/rust-libs/duniter-dbs-write-ops/Cargo.toml b/rust-libs/duniter-dbs-write-ops/Cargo.toml index acc4a8ffd6d70af3520780ade15e8c99a664e53b..f2eba28e3563bfe248653150f88f5f61c0db4104 100644 --- a/rust-libs/duniter-dbs-write-ops/Cargo.toml +++ b/rust-libs/duniter-dbs-write-ops/Cargo.toml @@ -21,6 +21,7 @@ resiter = "0.4.0" [dev-dependencies] anyhow = "1.0.34" +duniter-dbs = { path = "../duniter-dbs", features = ["mem"] } serde_json = "1.0.53" [features] diff --git a/rust-libs/duniter-dbs-write-ops/src/apply_block.rs b/rust-libs/duniter-dbs-write-ops/src/apply_block.rs index 9e78303c896d53fb6d9f901fe31320164df4d001..fea9f164bc350d8e491714e25c5f60abae5ecb7d 100644 --- a/rust-libs/duniter-dbs-write-ops/src/apply_block.rs +++ b/rust-libs/duniter-dbs-write-ops/src/apply_block.rs @@ -18,7 +18,7 @@ use crate::*; pub fn apply_block( block: DubpBlockV10, current_opt: Option<BlockMetaV2>, - dbs_pool: &fast_threadpool::ThreadPoolSyncHandler<DuniterDbs>, + dbs_pool: &fast_threadpool::ThreadPoolSyncHandler<DuniterDbs<FileBackend>>, gva: bool, throw_chainability: bool, ) -> KvResult<BlockMetaV2> { @@ -49,7 +49,7 @@ pub fn apply_block( #[inline(always)] pub fn apply_chunk( current_opt: Option<BlockMetaV2>, - dbs_pool: &fast_threadpool::ThreadPoolSyncHandler<DuniterDbs>, + dbs_pool: &fast_threadpool::ThreadPoolSyncHandler<DuniterDbs<FileBackend>>, blocks: Vec<DubpBlockV10>, gva: bool, ) -> KvResult<BlockMetaV2> { @@ -102,7 +102,7 @@ fn verify_chunk_chainability( } fn apply_block_inner( - dbs_pool: &fast_threadpool::ThreadPoolSyncHandler<DuniterDbs>, + dbs_pool: &fast_threadpool::ThreadPoolSyncHandler<DuniterDbs<FileBackend>>, block: Arc<DubpBlockV10>, gva: bool, ) -> KvResult<BlockMetaV2> { @@ -134,7 +134,7 @@ fn apply_block_inner( } fn apply_chunk_inner( - dbs_pool: &fast_threadpool::ThreadPoolSyncHandler<DuniterDbs>, + dbs_pool: &fast_threadpool::ThreadPoolSyncHandler<DuniterDbs<FileBackend>>, blocks: Arc<Vec<DubpBlockV10>>, gva: bool, ) -> KvResult<BlockMetaV2> { diff --git a/rust-libs/duniter-dbs-write-ops/src/lib.rs b/rust-libs/duniter-dbs-write-ops/src/lib.rs index d9d74a921faa4374847f32b316431ad4b8e747f0..58a0a10662d2ac5492d7f8e4dcffe88b66577d70 100644 --- a/rust-libs/duniter-dbs-write-ops/src/lib.rs +++ b/rust-libs/duniter-dbs-write-ops/src/lib.rs @@ -39,9 +39,9 @@ use dubp::documents::{ use dubp::wallet::prelude::*; use duniter_dbs::gva_v1::{TxsByIssuerEvent, TxsByRecipientEvent, TxsEvent}; use duniter_dbs::{ - kv_typed::prelude::*, BlockMetaV2, DuniterDbs, GvaV1Db, GvaV1DbReadable, GvaV1DbWritable, - HashKeyV2, PendingTxDbV2, PubKeyKeyV2, PubKeyValV2, SourceAmountValV2, TxDbV2, TxsMpV2Db, - TxsMpV2DbReadable, TxsMpV2DbWritable, UtxoValV2, WalletConditionsV2, + kv_typed::prelude::*, BlockMetaV2, DuniterDbs, FileBackend, GvaV1Db, GvaV1DbReadable, + GvaV1DbWritable, HashKeyV2, PendingTxDbV2, PubKeyKeyV2, PubKeyValV2, SourceAmountValV2, TxDbV2, + TxsMpV2Db, TxsMpV2DbReadable, TxsMpV2DbWritable, UtxoValV2, WalletConditionsV2, }; use resiter::filter::Filter; use resiter::filter_map::FilterMap; diff --git a/rust-libs/duniter-dbs/src/lib.rs b/rust-libs/duniter-dbs/src/lib.rs index 862425f5c145aaf9858eb30520ef7b34456fcb1e..550b9d63c94c2ab9c2482e2b271686c1d614270c 100644 --- a/rust-libs/duniter-dbs/src/lib.rs +++ b/rust-libs/duniter-dbs/src/lib.rs @@ -44,7 +44,7 @@ pub use kv_typed; // Prelude pub mod prelude { - pub use crate::{DbsBackend, DuniterDbs}; + pub use crate::DuniterDbs; #[cfg(feature = "explorer")] pub use kv_typed::explorer::{ DbExplorable, EntryFound, ExplorerAction, ExplorerActionResponse, ValueCaptures, @@ -114,21 +114,20 @@ pub trait ToDumpString { fn to_dump_string(&self) -> String; } -#[cfg(feature = "mem")] -pub type DbsBackend = kv_typed::backend::memory::Mem; -#[cfg(not(feature = "mem"))] -pub type DbsBackend = Sled; +#[cfg(all(not(feature = "mem"), not(test)))] +pub type FileBackend = kv_typed::backend::sled::Sled; +#[cfg(any(feature = "mem", test))] +pub type FileBackend = kv_typed::backend::memory::Mem; #[derive(Clone, Debug)] -pub struct DuniterDbs { - pub bc_db: bc_v2::BcV2Db<DbsBackend>, +pub struct DuniterDbs<B: Backend> { + pub bc_db: bc_v2::BcV2Db<B>, pub cm_db: cm_v1::CmV1Db<MemSingleton>, - pub gva_db: GvaV1Db<DbsBackend>, - pub txs_mp_db: TxsMpV2Db<DbsBackend>, + pub gva_db: GvaV1Db<B>, + pub txs_mp_db: TxsMpV2Db<B>, } -#[cfg(feature = "mem")] -impl DuniterDbs { +impl DuniterDbs<Mem> { pub fn mem() -> KvResult<Self> { use bc_v2::BcV2DbWritable as _; use cm_v1::CmV1DbWritable as _; diff --git a/rust-libs/duniter-dbs/src/open_dbs.rs b/rust-libs/duniter-dbs/src/open_dbs.rs index 0f99368b5237bc3266fdb63f66ff90be3146a4db..8a01ae5507262bfeb57a5dcf9fd66008e42a8c61 100644 --- a/rust-libs/duniter-dbs/src/open_dbs.rs +++ b/rust-libs/duniter-dbs/src/open_dbs.rs @@ -17,22 +17,16 @@ use crate::bc_v2::BcV2DbWritable as _; use crate::cm_v1::CmV1DbWritable as _; use crate::*; -pub fn open_dbs(home_path_opt: Option<&Path>) -> DuniterDbs { +pub fn open_dbs<B: BackendConf>(home_path_opt: Option<&Path>) -> DuniterDbs<B> { DuniterDbs { - bc_db: crate::bc_v2::BcV2Db::<DbsBackend>::open(DbsBackend::gen_backend_conf( - "bc_v2", - home_path_opt, - )) - .expect("fail to open BcV2 DB"), + bc_db: crate::bc_v2::BcV2Db::<B>::open(B::gen_backend_conf("bc_v2", home_path_opt)) + .expect("fail to open BcV2 DB"), cm_db: crate::cm_v1::CmV1Db::<MemSingleton>::open(MemSingletonConf::default()) .expect("fail to open CmV1 DB"), - gva_db: GvaV1Db::<DbsBackend>::open(DbsBackend::gen_backend_conf("gva_v1", home_path_opt)) + gva_db: GvaV1Db::<B>::open(B::gen_backend_conf("gva_v1", home_path_opt)) .expect("fail to open Gva DB"), - txs_mp_db: TxsMpV2Db::<DbsBackend>::open(DbsBackend::gen_backend_conf( - "txs_mp_v2", - home_path_opt, - )) - .expect("fail to open TxsMp DB"), + txs_mp_db: TxsMpV2Db::<B>::open(B::gen_backend_conf("txs_mp_v2", home_path_opt)) + .expect("fail to open TxsMp DB"), } } diff --git a/rust-libs/duniter-module/src/lib.rs b/rust-libs/duniter-module/src/lib.rs index 28f49c53bfe5e1552957b1e675c2606a00111d23..d9e3d1ca45ff91eb4f263068464bef13d08145c1 100644 --- a/rust-libs/duniter-module/src/lib.rs +++ b/rust-libs/duniter-module/src/lib.rs @@ -23,7 +23,7 @@ )] use duniter_conf::DuniterConf; -use duniter_dbs::DuniterDbs; +use duniter_dbs::{DuniterDbs, FileBackend}; use duniter_mempools::Mempools; use std::path::Path; @@ -34,7 +34,7 @@ pub trait DuniterModule: 'static + Sized { fn init( conf: &DuniterConf, currency: &str, - dbs_pool: &fast_threadpool::ThreadPoolAsyncHandler<DuniterDbs>, + dbs_pool: &fast_threadpool::ThreadPoolAsyncHandler<DuniterDbs<FileBackend>>, mempools: Mempools, profile_path_opt: Option<&Path>, software_version: &'static str, @@ -51,7 +51,7 @@ macro_rules! plug_duniter_modules { async fn start_duniter_modules( conf: &DuniterConf, currency: String, - dbs_pool: fast_threadpool::ThreadPoolAsyncHandler<DuniterDbs>, + dbs_pool: fast_threadpool::ThreadPoolAsyncHandler<DuniterDbs<FileBackend>>, mempools: duniter_mempools::Mempools, profile_path_opt: Option<std::path::PathBuf>, software_version: &'static str, @@ -105,7 +105,7 @@ mod tests { fn init( _conf: &DuniterConf, _currency: &str, - _dbs_pool: &fast_threadpool::ThreadPoolAsyncHandler<DuniterDbs>, + _dbs_pool: &fast_threadpool::ThreadPoolAsyncHandler<DuniterDbs<FileBackend>>, _mempools: Mempools, profile_path_opt: Option<&Path>, _software_version: &'static str, @@ -128,7 +128,7 @@ mod tests { fn init( _conf: &DuniterConf, _currency: &str, - _dbs_pool: &fast_threadpool::ThreadPoolAsyncHandler<DuniterDbs>, + _dbs_pool: &fast_threadpool::ThreadPoolAsyncHandler<DuniterDbs<FileBackend>>, _mempools: Mempools, _profile_path_opt: Option<&Path>, _software_version: &'static str, diff --git a/rust-libs/duniter-server/Cargo.toml b/rust-libs/duniter-server/Cargo.toml index 770ec868ba9370e78eb943c1ab57494e5f95f1f5..0818892ff646b740eb76c3a656186d3d0b700d9c 100644 --- a/rust-libs/duniter-server/Cargo.toml +++ b/rust-libs/duniter-server/Cargo.toml @@ -21,6 +21,3 @@ log = "0.4.11" paste = "1.0.2" resiter = "0.4.0" tokio = { version = "0.2.22", features = ["io-util", "rt-threaded"] } - -[dev-dependencies] -unwrap = "1.2.1" diff --git a/rust-libs/duniter-server/src/lib.rs b/rust-libs/duniter-server/src/lib.rs index c1cf343d735becdf915c52913409e2a317adfd06..85e5e525d0e11f589ffbbc41eecdd839cb4d5f7f 100644 --- a/rust-libs/duniter-server/src/lib.rs +++ b/rust-libs/duniter-server/src/lib.rs @@ -37,7 +37,7 @@ use duniter_dbs::cm_v1::{CmV1DbReadable, CmV1DbWritable}; use duniter_dbs::{ kv_typed::prelude::*, GvaV1DbReadable, HashKeyV2, PendingTxDbV2, TxsMpV2DbReadable, }; -use duniter_dbs::{prelude::*, BlockMetaV2}; +use duniter_dbs::{prelude::*, BlockMetaV2, FileBackend}; use duniter_dbs_read_ops::txs_history::TxsHistory; use duniter_mempools::{Mempools, TxMpError, TxsMempool}; use duniter_module::{plug_duniter_modules, DuniterModule as _, Endpoint}; @@ -54,7 +54,7 @@ pub enum DuniterCommand { pub struct DuniterServer { conf: DuniterConf, current: Option<BlockMetaV2>, - dbs_pool: fast_threadpool::ThreadPoolSyncHandler<DuniterDbs>, + dbs_pool: fast_threadpool::ThreadPoolSyncHandler<DuniterDbs<FileBackend>>, pending_txs_subscriber: flume::Receiver<Arc<Events<duniter_dbs::txs_mp_v2::TxsEvent>>>, txs_mempool: TxsMempool, } @@ -74,12 +74,14 @@ impl DuniterServer { _ => DuniterCommand::Start, }; + let dbs_reader = duniter_dbs_read_ops::create_dbs_reader(); let txs_mempool = TxsMempool::new(conf.txs_mempool_size); log::info!("open duniter databases..."); let dbs = duniter_dbs::open_dbs(home_path_opt); log::info!("Databases successfully opened."); - let current = duniter_dbs_read_ops::get_current_block_meta(&dbs.bc_db) + let current = dbs_reader + .get_current_block_meta(&dbs.bc_db) .context("Fail to get current")?; if let Some(current) = current { log::info!("Current block: #{}-{}", current.number, current.hash); @@ -333,51 +335,3 @@ impl DuniterServer { .expect("dbs pool disconnected") } } - -#[cfg(test)] -mod tests { - use super::*; - use dubp::documents::transaction::TransactionDocumentV10Builder; - use dubp::{crypto::keys::ed25519::Ed25519KeyPair, documents::smallvec::smallvec}; - - #[test] - fn test_txs_history() -> anyhow::Result<()> { - let server = DuniterServer::start( - None, - DuniterConf { - gva: None, - self_key_pair: Ed25519KeyPair::generate_random() - .expect("fail to gen random keypair"), - txs_mempool_size: 200, - }, - "currency_test".to_owned(), - None, - "test", - )?; - - let tx = TransactionDocumentV10Builder { - currency: "duniter_unit_test_currency", - blockstamp: Blockstamp::default(), - locktime: 0, - issuers: smallvec![PublicKey::default()], - inputs: &[], - unlocks: &[], - outputs: smallvec![], - comment: "test", - hash: None, - } - .build_with_signature(smallvec![]); - server.add_pending_tx_force(tx.clone())?; - - let txs_history = server.get_transactions_history(PublicKey::default())?; - - tx.get_hash(); - assert_eq!(txs_history.sending, vec![tx]); - - server.remove_all_pending_txs()?; - - assert_eq!(server.get_pending_txs(0, 0)?.len(), 0); - - Ok(()) - } -} diff --git a/rust-libs/modules/duniter-gva/Cargo.toml b/rust-libs/modules/duniter-gva/Cargo.toml index 0c0ee93ab8105f0c58487c6e9e5ae6789e755e57..b918aae3569e46b8f9dffa5c4e43a02a7fc382b3 100644 --- a/rust-libs/modules/duniter-gva/Cargo.toml +++ b/rust-libs/modules/duniter-gva/Cargo.toml @@ -29,5 +29,7 @@ warp = "0.2" [dev-dependencies] duniter-dbs = { path = "../../duniter-dbs", features = ["mem"] } +mockall = "0.8.0" +serde_json = "1.0.53" tokio = { version = "0.2.22", features = ["macros", "rt-threaded"] } unwrap = "1.2.1" diff --git a/rust-libs/modules/duniter-gva/src/lib.rs b/rust-libs/modules/duniter-gva/src/lib.rs index 24b282bfc168dcb6587381d3d8ec6ce393d22a6f..82756eeed6119f7c28419798a4b634cc7802b738 100644 --- a/rust-libs/modules/duniter-gva/src/lib.rs +++ b/rust-libs/modules/duniter-gva/src/lib.rs @@ -43,6 +43,10 @@ use crate::inputs::{TxIssuer, TxRecipient, UdsFilter}; use crate::inputs_validators::TxCommentValidator; use crate::pagination::{PaginationWithIntCursor, PaginationWithStrCursor}; use crate::schema::{GraphQlSchema, SchemaData}; +#[cfg(test)] +use crate::tests::create_dbs_reader; +#[cfg(test)] +use crate::tests::DbsReader; use async_graphql::http::GraphQLPlaygroundConfig; use async_graphql::validators::{IntGreaterThan, ListMinLength, StringMaxLength, StringMinLength}; use dubp::common::crypto::keys::{ed25519::PublicKey, KeyPair as _, PublicKey as _}; @@ -52,7 +56,11 @@ use dubp::documents::transaction::{TransactionDocumentTrait, TransactionDocument use dubp::documents_parser::prelude::*; use dubp::wallet::prelude::*; use duniter_dbs::prelude::*; -use duniter_dbs::{kv_typed::prelude::*, TxDbV2, TxsMpV2DbReadable}; +use duniter_dbs::{kv_typed::prelude::*, FileBackend, TxDbV2, TxsMpV2DbReadable}; +#[cfg(not(test))] +use duniter_dbs_read_ops::create_dbs_reader; +#[cfg(not(test))] +use duniter_dbs_read_ops::DbsReader; use duniter_mempools::{Mempools, TxsMempool}; use futures::{StreamExt, TryStreamExt}; use resiter::map::Map; @@ -66,7 +74,7 @@ use warp::{http::Response as HttpResponse, Filter as _, Rejection, Stream}; pub struct GvaModule { conf: Option<GvaConf>, currency: String, - dbs_pool: fast_threadpool::ThreadPoolAsyncHandler<DuniterDbs>, + dbs_pool: fast_threadpool::ThreadPoolAsyncHandler<DuniterDbs<FileBackend>>, mempools: Mempools, self_pubkey: PublicKey, software_version: &'static str, @@ -77,7 +85,7 @@ impl duniter_module::DuniterModule for GvaModule { fn init( conf: &duniter_conf::DuniterConf, currency: &str, - dbs_pool: &fast_threadpool::ThreadPoolAsyncHandler<DuniterDbs>, + dbs_pool: &fast_threadpool::ThreadPoolAsyncHandler<DuniterDbs<FileBackend>>, mempools: Mempools, _profile_path_opt: Option<&std::path::Path>, software_version: &'static str, @@ -150,7 +158,7 @@ impl GvaModule { async fn start_inner( conf: GvaConf, currency: String, - dbs_pool: fast_threadpool::ThreadPoolAsyncHandler<DuniterDbs>, + dbs_pool: fast_threadpool::ThreadPoolAsyncHandler<DuniterDbs<FileBackend>>, mempools: Mempools, self_pubkey: PublicKey, software_version: &'static str, @@ -163,6 +171,7 @@ impl GvaModule { ) .data(schema::SchemaData { dbs_pool, + dbs_reader: create_dbs_reader(), server_meta_data: ServerMetaData { currency, self_pubkey, @@ -278,11 +287,65 @@ impl From<duniter_dbs::PeerCardDbV1> for PeerCardStringified { mod tests { use super::*; use duniter_conf::DuniterConf; + use duniter_dbs::bc_v2::BcV2DbReadable; + use duniter_dbs::gva_v1::GvaV1DbReadable; + use duniter_dbs::{BlockMetaV2, SourceAmountValV2}; use duniter_mempools::Mempools; use duniter_module::DuniterModule; use fast_threadpool::ThreadPoolConfig; use unwrap::unwrap; + mockall::mock! { + pub DbsReader { + fn get_account_balance<GvaDb: 'static + GvaV1DbReadable>( + &self, + gva_db: &GvaDb, + account_script: &WalletScriptV10, + ) -> KvResult<Option<SourceAmountValV2>>; + fn get_current_block_meta<BcDb: 'static + BcV2DbReadable>( + &self, + bc_db: &BcDb, + ) -> KvResult<Option<BlockMetaV2>>; + fn get_current_ud<BcDb: 'static + BcV2DbReadable>( + &self, + bc_db: &BcDb, + ) -> KvResult<Option<SourceAmount>>; + } + } + pub type DbsReader = duniter_dbs::kv_typed::prelude::Arc<MockDbsReader>; + pub fn create_dbs_reader() -> DbsReader { + Arc::new(MockDbsReader::new()) + } + + pub(crate) fn create_schema(dbs_ops: MockDbsReader) -> KvResult<GraphQlSchema> { + let dbs = DuniterDbs::mem()?; + let threadpool = fast_threadpool::ThreadPool::start(ThreadPoolConfig::default(), dbs); + Ok(async_graphql::Schema::build( + queries::QueryRoot::default(), + mutations::MutationRoot::default(), + subscriptions::SubscriptionRoot::default(), + ) + .data(schema::SchemaData { + dbs_pool: threadpool.into_async_handler(), + dbs_reader: Arc::new(dbs_ops), + server_meta_data: ServerMetaData { + currency: "test_currency".to_owned(), + self_pubkey: PublicKey::default(), + software_version: "test", + }, + txs_mempool: TxsMempool::new(10), + }) + .extension(async_graphql::extensions::Logger) + .finish()) + } + + pub(crate) async fn exec_graphql_request( + schema: &GraphQlSchema, + request: &str, + ) -> anyhow::Result<serde_json::Value> { + Ok(serde_json::to_value(schema.execute(request).await)?) + } + #[tokio::test] #[ignore] async fn launch_mem_gva() -> anyhow::Result<()> { diff --git a/rust-libs/modules/duniter-gva/src/queries/account_balance.rs b/rust-libs/modules/duniter-gva/src/queries/account_balance.rs index 5afc02a5a55a2a3d648b8ec126d4cf55af911733..d8814d6af963ef12525260a2bf086cf5f3701da4 100644 --- a/rust-libs/modules/duniter-gva/src/queries/account_balance.rs +++ b/rust-libs/modules/duniter-gva/src/queries/account_balance.rs @@ -14,7 +14,6 @@ // along with this program. If not, see <https://www.gnu.org/licenses/>. use crate::*; -use duniter_dbs::{GvaV1DbReadable, WalletConditionsV2}; #[derive(Default)] pub(crate) struct AccountBalanceQuery; @@ -33,14 +32,11 @@ impl AccountBalanceQuery { }; let data = ctx.data::<SchemaData>()?; + let dbs_reader = data.dbs_reader(); let balance = data .dbs_pool - .execute(move |dbs| { - dbs.gva_db - .balances() - .get(&WalletConditionsV2(account_script)) - }) + .execute(move |dbs| dbs_reader.get_account_balance(&dbs.gva_db, &account_script)) .await?? .unwrap_or_default() .0; @@ -51,3 +47,42 @@ impl AccountBalanceQuery { }) } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::tests::*; + use duniter_dbs::SourceAmountValV2; + + #[tokio::test] + async fn query_balance() -> anyhow::Result<()> { + let mut dbs_reader = MockDbsReader::new(); + use duniter_dbs::gva_v1::GvaV1Db; + dbs_reader + .expect_get_account_balance::<GvaV1Db<FileBackend>>() + .withf(|_, s| { + s == &WalletScriptV10::single_sig( + PublicKey::from_base58("DnjL6hYA1k7FavGHbbir79PKQbmzw63d6bsamBBdUULP") + .expect("wrong pubkey"), + ) + }) + .times(1) + .returning(|_, _| Ok(Some(SourceAmountValV2(SourceAmount::with_base0(38))))); + let schema = create_schema(dbs_reader)?; + assert_eq!( + exec_graphql_request( + &schema, + r#"{ balance(script: "DnjL6hYA1k7FavGHbbir79PKQbmzw63d6bsamBBdUULP") {amount} }"# + ) + .await?, + serde_json::json!({ + "data": { + "balance": { + "amount": 38 + } + } + }) + ); + Ok(()) + } +} diff --git a/rust-libs/modules/duniter-gva/src/queries/gen_tx.rs b/rust-libs/modules/duniter-gva/src/queries/gen_tx.rs index b3f43ed93550d8321891c19e64c7837f1777f624..ff9df7e2c9936ecf0f2a23b9ed0980f8b0f7bbec 100644 --- a/rust-libs/modules/duniter-gva/src/queries/gen_tx.rs +++ b/rust-libs/modules/duniter-gva/src/queries/gen_tx.rs @@ -112,14 +112,13 @@ impl GenTxsQuery { let recipient = PublicKey::from_base58(&recipient)?; let data = ctx.data::<SchemaData>()?; + let dbs_reader = data.dbs_reader(); let currency = data.server_meta_data.currency.clone(); let (current_block, (inputs, inputs_sum)) = data .dbs_pool .execute(move |dbs| { - if let Some(current_block) = - duniter_dbs_read_ops::get_current_block_meta(&dbs.bc_db)? - { + if let Some(current_block) = dbs_reader.get_current_block_meta(&dbs.bc_db)? { Ok(( current_block, duniter_dbs_read_ops::find_inputs::find_inputs( @@ -187,14 +186,13 @@ impl GenTxsQuery { } let data = ctx.data::<SchemaData>()?; + let dbs_reader = data.dbs_reader(); let currency = data.server_meta_data.currency.clone(); let (current_block, issuers_inputs_with_sum) = data .dbs_pool .execute(move |dbs| { - if let Some(current_block) = - duniter_dbs_read_ops::get_current_block_meta(&dbs.bc_db)? - { + if let Some(current_block) = dbs_reader.get_current_block_meta(&dbs.bc_db)? { let mut issuers_inputs_with_sum = Vec::new(); for issuer in issuers { issuers_inputs_with_sum.push(( diff --git a/rust-libs/modules/duniter-gva/src/queries/uds.rs b/rust-libs/modules/duniter-gva/src/queries/uds.rs index 4867a37ce4278b009c7744c431a983965e849826..a04fd2fb2c5ab8bf15d6a75a6c2d27ff4c82a486 100644 --- a/rust-libs/modules/duniter-gva/src/queries/uds.rs +++ b/rust-libs/modules/duniter-gva/src/queries/uds.rs @@ -28,10 +28,11 @@ impl UdsQuery { ctx: &async_graphql::Context<'_>, ) -> async_graphql::Result<Option<CurrentUdGva>> { let data = ctx.data::<SchemaData>()?; + let dbs_reader = data.dbs_reader(); Ok(data .dbs_pool - .execute(move |dbs| duniter_dbs_read_ops::get_current_ud(&dbs.bc_db)) + .execute(move |dbs| dbs_reader.get_current_ud(&dbs.bc_db)) .await?? .map(|sa| CurrentUdGva { amount: sa.amount(), @@ -53,6 +54,7 @@ impl UdsQuery { let pubkey = PublicKey::from_base58(&pubkey)?; let data = ctx.data::<SchemaData>()?; + let dbs_reader = data.dbs_reader(); let ( PagedData { @@ -64,9 +66,7 @@ impl UdsQuery { ) = data .dbs_pool .execute(move |dbs| { - if let Some(current_block) = - duniter_dbs_read_ops::get_current_block_meta(&dbs.bc_db)? - { + if let Some(current_block) = dbs_reader.get_current_block_meta(&dbs.bc_db)? { let paged_data = match filter { UdsFilter::All => duniter_dbs_read_ops::uds_of_pubkey::all_uds_of_pubkey( &dbs.bc_db, @@ -156,3 +156,31 @@ impl UdsQuery { .await??) } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::tests::*; + + #[tokio::test] + async fn query_current_ud() -> anyhow::Result<()> { + let mut dbs_reader = MockDbsReader::new(); + use duniter_dbs::bc_v2::BcV2Db; + dbs_reader + .expect_get_current_ud::<BcV2Db<FileBackend>>() + .times(1) + .returning(|_| Ok(Some(SourceAmount::with_base0(100)))); + let schema = create_schema(dbs_reader)?; + assert_eq!( + exec_graphql_request(&schema, r#"{ currentUd {amount} }"#).await?, + serde_json::json!({ + "data": { + "currentUd": { + "amount": 100 + } + } + }) + ); + Ok(()) + } +} diff --git a/rust-libs/modules/duniter-gva/src/queries/utxos.rs b/rust-libs/modules/duniter-gva/src/queries/utxos.rs index 90bdc3d94c25fe5436c42ed6151d31a437ece6da..e184e1bd4392e0cebbc87bf230496fb1e992e6be 100644 --- a/rust-libs/modules/duniter-gva/src/queries/utxos.rs +++ b/rust-libs/modules/duniter-gva/src/queries/utxos.rs @@ -38,6 +38,7 @@ impl UtxosQuery { let script = dubp::documents_parser::wallet_script_from_str(&script)?; let data = ctx.data::<SchemaData>()?; + let dbs_reader = data.dbs_reader(); let ( PagedData { @@ -49,9 +50,7 @@ impl UtxosQuery { ) = data .dbs_pool .execute(move |dbs| { - if let Some(current_block) = - duniter_dbs_read_ops::get_current_block_meta(&dbs.bc_db)? - { + if let Some(current_block) = dbs_reader.get_current_block_meta(&dbs.bc_db)? { let paged_data = duniter_dbs_read_ops::utxos::find_script_utxos( &dbs.gva_db, &dbs.txs_mp_db, diff --git a/rust-libs/modules/duniter-gva/src/schema.rs b/rust-libs/modules/duniter-gva/src/schema.rs index c830bb85ba6715882c372d48061db5ef7e225214..c4540e7bb64d7cc500d2aae09697c20ad5f7be87 100644 --- a/rust-libs/modules/duniter-gva/src/schema.rs +++ b/rust-libs/modules/duniter-gva/src/schema.rs @@ -21,7 +21,21 @@ pub(crate) type GraphQlSchema = async_graphql::Schema< crate::subscriptions::SubscriptionRoot, >; pub(crate) struct SchemaData { - pub(crate) dbs_pool: fast_threadpool::ThreadPoolAsyncHandler<DuniterDbs>, + pub(crate) dbs_pool: fast_threadpool::ThreadPoolAsyncHandler<DuniterDbs<FileBackend>>, + pub(crate) dbs_reader: DbsReader, pub(crate) server_meta_data: ServerMetaData, pub(crate) txs_mempool: TxsMempool, } + +#[cfg(not(test))] +impl SchemaData { + pub fn dbs_reader(&self) -> DbsReader { + self.dbs_reader + } +} +#[cfg(test)] +impl SchemaData { + pub fn dbs_reader(&self) -> DbsReader { + self.dbs_reader.clone() + } +} diff --git a/rust-libs/tests/duniter-integration-tests/Cargo.toml b/rust-libs/tests/duniter-integration-tests/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..275321cf6bd8d1fab7a0076805c615ced35ce918 --- /dev/null +++ b/rust-libs/tests/duniter-integration-tests/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "duniter-integration-tests" +version = "0.1.0" +authors = ["librelois <elois@duniter.org>"] +license = "AGPL-3.0" +edition = "2018" + +[dependencies] +anyhow = "1.0.34" +dubp = { version = "0.32.2" } +duniter-conf = { path = "../../duniter-conf" } +duniter-dbs = { path = "../../duniter-dbs" } +duniter-dbs-read-ops = { path = "../../duniter-dbs-read-ops" } +duniter-dbs-write-ops = { path = "../../duniter-dbs-write-ops" } +duniter-gva = { path = "../../modules/duniter-gva" } +duniter-mempools = { path = "../../duniter-mempools" } +duniter-module = { path = "../../duniter-module" } +duniter-server = { path = "../../duniter-server" } +fast-threadpool = "0.2.1" +flume = "0.9.1" +log = "0.4.11" +paste = "1.0.2" +resiter = "0.4.0" +tokio = { version = "0.2.22", features = ["io-util", "rt-threaded"] } diff --git a/rust-libs/tests/duniter-integration-tests/src/lib.rs b/rust-libs/tests/duniter-integration-tests/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..4260a8b612d0e6fcf02500bd997dfec13a2aa6c9 --- /dev/null +++ b/rust-libs/tests/duniter-integration-tests/src/lib.rs @@ -0,0 +1,75 @@ +// Copyright (C) 2020 Éloïs SANCHEZ. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +#![deny( + missing_copy_implementations, + trivial_casts, + trivial_numeric_casts, + unstable_features, + unused_import_braces +)] + +#[cfg(test)] +mod tests { + use dubp::documents::transaction::TransactionDocumentV10Builder; + use dubp::{ + common::prelude::*, + crypto::keys::ed25519::{Ed25519KeyPair, PublicKey}, + documents::prelude::*, + documents::smallvec::smallvec, + }; + use duniter_server::*; + + #[test] + fn test_txs_history() -> anyhow::Result<()> { + let server = DuniterServer::start( + None, + DuniterConf { + gva: None, + self_key_pair: Ed25519KeyPair::generate_random() + .expect("fail to gen random keypair"), + txs_mempool_size: 200, + }, + "currency_test".to_owned(), + None, + "test", + )?; + + let tx = TransactionDocumentV10Builder { + currency: "duniter_unit_test_currency", + blockstamp: Blockstamp::default(), + locktime: 0, + issuers: smallvec![PublicKey::default()], + inputs: &[], + unlocks: &[], + outputs: smallvec![], + comment: "test", + hash: None, + } + .build_with_signature(smallvec![]); + server.add_pending_tx_force(tx.clone())?; + + let txs_history = server.get_transactions_history(PublicKey::default())?; + + tx.get_hash(); + assert_eq!(txs_history.sending, vec![tx]); + + server.remove_all_pending_txs()?; + + assert_eq!(server.get_pending_txs(0, 0)?.len(), 0); + + Ok(()) + } +}