From 6657adae5d2664abb63602a70a5b5e46492dcb00 Mon Sep 17 00:00:00 2001
From: librelois <c@elo.tf>
Date: Wed, 2 Dec 2020 01:25:03 +0100
Subject: [PATCH] [tests] mock db read ops & test some gva queries

---
 Cargo.lock                                    | 29 ++++++-
 Cargo.toml                                    |  1 +
 rust-libs/duniter-dbs-read-ops/src/lib.rs     | 47 +++++++++---
 rust-libs/duniter-dbs-write-ops/Cargo.toml    |  1 +
 .../duniter-dbs-write-ops/src/apply_block.rs  |  8 +-
 rust-libs/duniter-dbs-write-ops/src/lib.rs    |  6 +-
 rust-libs/duniter-dbs/src/lib.rs              | 21 +++---
 rust-libs/duniter-dbs/src/open_dbs.rs         | 18 ++---
 rust-libs/duniter-module/src/lib.rs           | 10 +--
 rust-libs/duniter-server/Cargo.toml           |  3 -
 rust-libs/duniter-server/src/lib.rs           | 56 ++------------
 rust-libs/modules/duniter-gva/Cargo.toml      |  2 +
 rust-libs/modules/duniter-gva/src/lib.rs      | 71 +++++++++++++++++-
 .../src/queries/account_balance.rs            | 47 ++++++++++--
 .../modules/duniter-gva/src/queries/gen_tx.rs | 10 +--
 .../modules/duniter-gva/src/queries/uds.rs    | 36 ++++++++-
 .../modules/duniter-gva/src/queries/utxos.rs  |  5 +-
 rust-libs/modules/duniter-gva/src/schema.rs   | 16 +++-
 .../duniter-integration-tests/Cargo.toml      | 24 ++++++
 .../duniter-integration-tests/src/lib.rs      | 75 +++++++++++++++++++
 20 files changed, 359 insertions(+), 127 deletions(-)
 create mode 100644 rust-libs/tests/duniter-integration-tests/Cargo.toml
 create mode 100644 rust-libs/tests/duniter-integration-tests/src/lib.rs

diff --git a/Cargo.lock b/Cargo.lock
index 38a5c45f3..7da721dbc 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 c671f85a1..09974e444 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 45d94cb20..26ca21089 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 acc4a8ffd..f2eba28e3 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 9e78303c8..fea9f164b 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 d9d74a921..58a0a1066 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 862425f5c..550b9d63c 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 0f99368b5..8a01ae550 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 28f49c53b..d9e3d1ca4 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 770ec868b..0818892ff 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 c1cf343d7..85e5e525d 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 0c0ee93ab..b918aae35 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 24b282bfc..82756eeed 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 5afc02a5a..d8814d6af 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 b3f43ed93..ff9df7e2c 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 4867a37ce..a04fd2fb2 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 90bdc3d94..e184e1bd4 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 c830bb85b..c4540e7bb 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 000000000..275321cf6
--- /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 000000000..4260a8b61
--- /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(())
+    }
+}
-- 
GitLab