Skip to content
Snippets Groups Projects
Commit e85d2552 authored by Éloïs's avatar Éloïs
Browse files

[tests] gva: deep refactor to make the code testable

At the same time, cutting into smaller files
parent ece02832
No related branches found
No related tags found
1 merge request!226Resolve "GVA: create skeleton & implement current Block request"
Showing
with 913 additions and 174 deletions
......@@ -100,7 +100,8 @@ tests:linux64:stable:
script:
- cd bin/dunitrust-server
- RUSTFLAGS="-D warnings" cargo build --features=ssl
- cargo test --all
- cargo test --all --exclude durs-gva
- cargo test --package durs-gva -- --test-threads=1
- cargo test --all -- --ignored
tests:arm-v7-:stable:
......@@ -114,7 +115,6 @@ tests:arm-v7-:stable:
script:
- cd bin/dunitrust-server
- RUSTFLAGS="-D warnings" cargo build --target=armv7-unknown-linux-gnueabihf --features=ssl
- cargo test --all --target=armv7-unknown-linux-gnueabihf
tests:arm-v7:stable:
extends: .rust_stable_armv7
......@@ -124,7 +124,6 @@ tests:arm-v7:stable:
script:
- cd bin/dunitrust-server
- RUSTFLAGS="-D warnings" cargo build --target=armv7-unknown-linux-gnueabihf --features=ssl
- cargo test --all --target=armv7-unknown-linux-gnueabihf
tests:win64:stable:
extends: .rust_stable_win64
......@@ -143,6 +142,7 @@ tests:win64:stable:
- cargo test --package durs-blockchain --target=x86_64-pc-windows-gnu
- cargo test --package durs-dbs-tools --target=x86_64-pc-windows-gnu
#- cargo test --package durs-skeleton-module --target=x86_64-pc-windows-gnu
- cargo test --package durs-gva --target=x86_64-pc-windows-gnu -- --test-threads=1
- cargo test --package durs-ws2p-v1-legacy --target=x86_64-pc-windows-gnu
- cargo test --package durs-ws2p --target=x86_64-pc-windows-gnu
- cargo test --package durs-ws2p-messages --target=x86_64-pc-windows-gnu
......
......@@ -36,8 +36,8 @@ pub mod paging;
pub mod tools;
pub use durs_dbs_tools::kv_db::{
KvFileDbRead as DbReadable, KvFileDbRoHandler as BcDbRo, KvFileDbSchema, KvFileDbStoreType,
KvFileDbValue as DbValue, Readable as DbReader,
KvFileDbRead as DbReadable, KvFileDbReader as Reader, KvFileDbRoHandler as BcDbRo,
KvFileDbSchema, KvFileDbStoreType, KvFileDbValue as DbValue, Readable as DbReader,
};
pub use durs_dbs_tools::DbError;
......
......@@ -12,6 +12,7 @@ path = "src/lib.rs"
[dependencies]
actix-web = "1.0.8"
dubp-block-doc = { path = "../../dubp/block-doc"} #, version = "0.1.0" }
dup-crypto = { path = "../../crypto" }
durs-bc-db-reader = { path = "../../modules-lib/bc-db-reader" }
durs-conf = { path = "../../core/conf" }
durs-message = { path = "../../core/message" }
......@@ -21,6 +22,7 @@ durs-network-documents = { path = "../../dunp/network-documents" }
dubp-common-doc = { path = "../../dubp/common-doc"} #, version = "0.1.0" }
durs-common-tools = { path = "../../tools/common-tools" }
dubp-currency-params = { path = "../../dubp/currency-params" }
cfg-if = "0.1.10"
chrono = "0.4.9"
failure = "0.1.5"
futures = "0.1"
......@@ -34,4 +36,10 @@ serde_derive = "1.0.102"
serde_json = "1.0.41"
structopt= "0.3.4"
[dev-dependencies]
dubp-blocks-tests-tools = { path = "../../tests-tools/blocks-tests-tools" }
dup-crypto-tests-tools = { path = "../../tests-tools/crypto-tests-tools" }
mockall = "0.5.2"
pretty_assertions = "0.5.1"
[features]
......@@ -13,15 +13,23 @@
// 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/>.
#[cfg(not(test))]
use durs_bc_db_reader::BcDbRo;
use durs_common_tools::fatal_error;
#[cfg(test)]
use crate::db::MockBcDbTrait;
/// GVA context (access to database)
static mut CONTEXT: Option<Context> = None;
#[derive(Debug)]
#[cfg(not(test))]
pub type DB = BcDbRo;
#[cfg(test)]
pub(crate) type DB = MockBcDbTrait;
pub struct Context {
db: BcDbRo,
db: DB,
software_name: &'static str,
software_version: &'static str,
}
......@@ -29,7 +37,7 @@ pub struct Context {
impl juniper::Context for Context {}
impl Context {
pub fn new(db: BcDbRo, software_name: &'static str, software_version: &'static str) -> Self {
pub(crate) fn new(db: DB, software_name: &'static str, software_version: &'static str) -> Self {
Context {
db,
software_name,
......@@ -37,7 +45,7 @@ impl Context {
}
}
pub fn get_db(&self) -> &BcDbRo {
pub(crate) fn get_db(&self) -> &DB {
&self.db
}
......@@ -50,7 +58,7 @@ impl Context {
}
}
pub fn init(db: BcDbRo, soft_name: &'static str, soft_version: &'static str) {
pub(crate) fn init(db: DB, soft_name: &'static str, soft_version: &'static str) {
unsafe {
CONTEXT.replace(Context::new(db, soft_name, soft_version));
}
......
// Copyright (C) 2017-2019 The AXIOM TEAM Association.
//
// 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/>.
//! Gva Module: database requests
pub use durs_bc_db_reader::DbError;
use dubp_common_doc::{BlockNumber, Blockstamp};
use durs_bc_db_reader::blocks::DbBlock;
use durs_bc_db_reader::{BcDbRo, DbReadable};
use std::ops::Range;
#[cfg(test)]
use mockall::predicate::*;
#[cfg(test)]
use mockall::*;
#[cfg_attr(test, automock)]
pub(crate) trait BcDbTrait {
fn get_current_blockstamp(&self) -> Result<Option<Blockstamp>, DbError>;
fn get_current_block(&self) -> Result<Option<DbBlock>, DbError>;
fn get_db_block_in_local_blockchain(
&self,
block_number: BlockNumber,
) -> Result<Option<DbBlock>, DbError>;
fn get_db_blocks_in_local_blockchain(&self, range: Range<u32>)
-> Result<Vec<DbBlock>, DbError>;
}
impl<'a> BcDbTrait for BcDbRo {
#[inline]
fn get_current_blockstamp(&self) -> Result<Option<Blockstamp>, DbError> {
self.read(|r| durs_bc_db_reader::current_meta_datas::get_current_blockstamp_(self, r))
}
fn get_current_block(&self) -> Result<Option<DbBlock>, DbError> {
self.read(|r| {
if let Some(current_blockstamp) =
durs_bc_db_reader::current_meta_datas::get_current_blockstamp_(self, r)?
{
durs_bc_db_reader::blocks::get_db_block_in_local_blockchain(
self,
r,
current_blockstamp.id,
)
} else {
Ok(None)
}
})
}
#[inline]
fn get_db_block_in_local_blockchain(
&self,
block_number: BlockNumber,
) -> Result<Option<DbBlock>, DbError> {
self.read(|r| {
durs_bc_db_reader::blocks::get_db_block_in_local_blockchain(self, r, block_number)
})
}
fn get_db_blocks_in_local_blockchain(
&self,
range: Range<u32>,
) -> Result<Vec<DbBlock>, DbError> {
self.read(|r| {
range
.filter_map(|n| {
match durs_bc_db_reader::blocks::get_db_block_in_local_blockchain(
self,
r,
BlockNumber(n),
) {
Ok(Some(db_block)) => Some(Ok(db_block)),
Ok(None) => None,
Err(e) => Some(Err(e)),
}
})
.collect::<Result<Vec<DbBlock>, DbError>>()
})
}
}
// Copyright (C) 2017-2019 The AXIOM TEAM Association.
//
// 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/>.
// web server implementaion based on actix-web
//! Module that execute graphql queries
use crate::schema::Schema;
use actix_web::{web, Error, HttpResponse};
use futures::future::Future;
use juniper::http::GraphQLRequest;
use std::sync::Arc;
pub(crate) fn graphql(
schema: web::Data<Arc<Schema>>,
data: web::Json<GraphQLRequest>,
) -> impl Future<Item = HttpResponse, Error = Error> {
let context = crate::context::get_context();
web::block(move || {
let result = data.execute(&schema, context);
serde_json::to_string(&result)
})
.map_err(Error::from)
.and_then(|user| {
Ok(HttpResponse::Ok()
.content_type("application/json")
.body(user))
})
}
......@@ -14,14 +14,14 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Gva Module
// This module provides a graphql API implementation of the 0003 RFC
//
// /src/schema.gql contains schema description
// /src/schema.rs contains model and resolvers implementation
// /src/webserver.rs contains web server implementaion based on actix-web
//
// Graphiql web client is accessible at
// http://127.0.0.1:3000/graphiql
//! This module provides a graphql API implementation of the 0003 RFC
//!
//! /src/schema.gql contains schema description
//! /src/schema.rs contains model and resolvers implementation
//! /src/webserver.rs contains web server implementaion based on actix-web
//!
//! Graphiql web client is accessible at
//! http://127.0.0.1:10901/graphiql
#![deny(
missing_docs,
......@@ -44,7 +44,9 @@ extern crate structopt;
extern crate juniper;
mod context;
mod db;
mod errors;
mod graphql;
mod schema;
mod webserver;
......
......@@ -13,15 +13,15 @@
// 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/>.
// ! model and resolvers implementation
// ! Module define GraphQl schema
mod block;
mod entities;
mod paging;
mod queries;
use self::block::Block;
use self::entities::block::Block;
use self::entities::node::{Node, Summary};
use crate::context::Context;
use dubp_common_doc::BlockNumber;
use durs_bc_db_reader::{BcDbRo, DbError, DbReadable};
use juniper::Executor;
use juniper::FieldResult;
use juniper_from_schema::graphql_schema_from_file;
......@@ -31,104 +31,40 @@ graphql_schema_from_file!("resources/schema.gql");
pub struct Query;
pub struct Summary {
software: &'static str,
version: &'static str,
}
pub struct Node {
summary: Summary,
}
fn db_err_to_juniper_err(e: DbError) -> juniper::FieldError {
juniper::FieldError::from(format!("Db error: {:?}", e))
}
impl QueryFields for Query {
#[inline]
fn field_node(
&self,
executor: &Executor<'_, Context>,
_trail: &QueryTrail<'_, Node, Walked>,
trail: &QueryTrail<'_, Node, Walked>,
) -> FieldResult<Node> {
Ok(Node {
summary: Summary {
software: executor.context().get_software_name(),
version: executor.context().get_software_version(),
},
})
queries::node::execute(executor, trail)
}
#[inline]
fn field_current(
&self,
executor: &Executor<'_, Context>,
_trail: &QueryTrail<'_, Block, Walked>,
trail: &QueryTrail<'_, Block, Walked>,
) -> FieldResult<Option<Block>> {
let db: &BcDbRo = &executor.context().get_db();
db.read(|r| {
if let Some(current_blockstamp) =
durs_bc_db_reader::current_meta_datas::get_current_blockstamp_(db, r)?
{
block::get_block(db, r, current_blockstamp.id)
} else {
Ok(None)
}
})
.map_err(db_err_to_juniper_err)
queries::current::execute(executor, trail)
}
#[inline]
fn field_block(
&self,
executor: &Executor<'_, Context>,
_trail: &QueryTrail<'_, Block, Walked>,
trail: &QueryTrail<'_, Block, Walked>,
number: i32,
) -> FieldResult<Option<Block>> {
let db: &BcDbRo = &executor.context().get_db();
let block_number = if number >= 0 {
BlockNumber(number as u32)
} else {
return Err(juniper::FieldError::from("Block number must be positive."));
};
db.read(|r| block::get_block(db, r, block_number))
.map_err(db_err_to_juniper_err)
queries::block::execute(executor, trail, number)
}
#[inline]
fn field_blocks(
&self,
executor: &Executor<'_, Context>,
_trail: &QueryTrail<'_, Block, Walked>,
trail: &QueryTrail<'_, Block, Walked>,
paging_opt: Option<Paging>,
) -> FieldResult<Vec<Block>> {
let db: &BcDbRo = &executor.context().get_db();
db.read(|r| {
paging::FilledPaging::new(db, r, paging_opt)?
.get_range()
.filter_map(|n| match block::get_block(db, r, BlockNumber(n)) {
Ok(Some(db_block)) => Some(Ok(db_block)),
Ok(None) => None,
Err(e) => Some(Err(e)),
})
.collect::<Result<Vec<Block>, DbError>>()
})
.map_err(db_err_to_juniper_err)
}
}
impl NodeFields for Node {
fn field_summary(
&self,
_executor: &Executor<'_, Context>,
_trail: &QueryTrail<'_, Summary, Walked>,
) -> &Summary {
&self.summary
}
}
impl SummaryFields for Summary {
fn field_software(&self, _executor: &Executor<'_, Context>) -> String {
self.software.to_owned()
}
fn field_version(&self, _executor: &Executor<'_, Context>) -> String {
self.version.to_owned()
queries::blocks::execute(executor, trail, paging_opt)
}
}
......
// Copyright (C) 2017-2019 The AXIOM TEAM Association.
//
// 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/>.
// ! Module define GraphQl schema entities
pub mod block;
pub mod node;
......@@ -13,14 +13,13 @@
// 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/>.
// ! Block model and resolvers
// ! Module define graphql Block type
use crate::context::Context;
use chrono::NaiveDateTime;
use dubp_block_doc::block::BlockDocumentTrait;
use dubp_common_doc::traits::Document;
use dubp_common_doc::BlockNumber;
use durs_bc_db_reader::{BcDbRo, DbError, DbReader};
use durs_bc_db_reader::blocks::DbBlock;
use durs_common_tools::fatal_error;
use juniper::{Executor, FieldResult};
......@@ -33,7 +32,7 @@ pub struct Block {
common_time: NaiveDateTime,
}
impl super::BlockFields for Block {
impl super::super::BlockFields for Block {
fn field_version(&self, _executor: &Executor<'_, Context>) -> FieldResult<&i32> {
Ok(&self.version)
}
......@@ -59,14 +58,9 @@ impl super::BlockFields for Block {
}
}
pub fn get_block<R: DbReader>(
db: &BcDbRo,
r: &R,
block_number: BlockNumber,
) -> Result<Option<Block>, DbError> {
durs_bc_db_reader::blocks::get_db_block_in_local_blockchain(db, r, block_number).map(
|block_opt| {
block_opt.map(|db_block| Block {
impl Block {
pub fn from_db_block(db_block: DbBlock) -> Block {
Block {
version: db_block.block.version() as i32,
currency: db_block.block.currency().to_string(),
issuer: db_block.block.issuers()[0].to_string(),
......@@ -77,7 +71,6 @@ pub fn get_block<R: DbReader>(
.unwrap_or_else(|| fatal_error!("DbBlock without hash."))
.to_string(),
common_time: NaiveDateTime::from_timestamp(db_block.block.common_time() as i64, 0),
})
},
)
}
}
}
// Copyright (C) 2017-2019 The AXIOM TEAM Association.
//
// 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/>.
// ! Module define graphql Node type and subtypes
use crate::context::Context;
use juniper::Executor;
use juniper_from_schema::{QueryTrail, Walked};
pub struct Summary {
pub software: &'static str,
pub version: &'static str,
}
pub struct Node {
pub summary: Summary,
}
impl super::super::NodeFields for Node {
fn field_summary(
&self,
_executor: &Executor<'_, Context>,
_trail: &QueryTrail<'_, Summary, Walked>,
) -> &Summary {
&self.summary
}
}
impl super::super::SummaryFields for Summary {
fn field_software(&self, _executor: &Executor<'_, Context>) -> String {
self.software.to_owned()
}
fn field_version(&self, _executor: &Executor<'_, Context>) -> String {
self.version.to_owned()
}
}
......@@ -16,16 +16,15 @@
// ! Schema paging input
use super::Paging;
use durs_bc_db_reader::{BcDbRo, DbError, DbReader};
use crate::db::BcDbTrait;
use durs_bc_db_reader::DbError;
use std::ops::Range;
const DEFAULT_PAGE_NUMBER: i32 = 0;
const DEFAULT_PAGE_SIZE: i32 = 50;
const DEFAULT_FROM_BLOCK: i32 = 0;
const MAX_PAGE_NUMBER: i32 = std::i32::MAX;
const MAX_PAGE_SIZE: i32 = 500;
const MAX_FROM_BLOCK: i32 = std::i32::MAX;
/// Paging with all values filled in
pub struct FilledPaging {
......@@ -49,25 +48,16 @@ fn i32_opt_to_positive_i32(int_opt: Option<i32>, default: i32) -> i32 {
}
impl FilledPaging {
pub fn new<R: DbReader>(
db: &BcDbRo,
r: &R,
paging_opt: Option<Paging>,
) -> Result<Self, DbError> {
pub(crate) fn new<DB: BcDbTrait>(db: &DB, paging_opt: Option<Paging>) -> Result<Self, DbError> {
if let Some(paging) = paging_opt {
Ok(FilledPaging {
page_number: std::cmp::min(
MAX_PAGE_NUMBER,
i32_opt_to_positive_i32(paging.page_number, DEFAULT_PAGE_NUMBER),
) as usize,
page_number: i32_opt_to_positive_i32(paging.page_number, DEFAULT_PAGE_NUMBER)
as usize,
page_size: std::cmp::min(
MAX_PAGE_SIZE,
i32_opt_to_positive_i32(paging.page_size, DEFAULT_PAGE_SIZE),
) as usize,
from_block: std::cmp::min(
MAX_FROM_BLOCK,
i32_opt_to_positive_i32(paging.from_block, DEFAULT_FROM_BLOCK),
) as u32,
from_block: i32_opt_to_positive_i32(paging.from_block, DEFAULT_FROM_BLOCK) as u32,
to_block: if let Some(to_block) = paging.to_block {
if to_block < 0 {
0
......@@ -75,7 +65,7 @@ impl FilledPaging {
to_block as u32
}
} else {
Self::get_default_to_block(db, r)?
Self::get_default_to_block(db)?
},
})
} else {
......@@ -83,14 +73,12 @@ impl FilledPaging {
page_number: DEFAULT_PAGE_NUMBER as usize,
page_size: DEFAULT_PAGE_SIZE as usize,
from_block: DEFAULT_FROM_BLOCK as u32,
to_block: Self::get_default_to_block(db, r)?,
to_block: Self::get_default_to_block(db)?,
})
}
}
fn get_default_to_block<R: DbReader>(db: &BcDbRo, r: &R) -> Result<u32, DbError> {
if let Some(current_blockstamp) =
durs_bc_db_reader::current_meta_datas::get_current_blockstamp_(db, r)?
{
fn get_default_to_block<DB: BcDbTrait>(db: &DB) -> Result<u32, DbError> {
if let Some(current_blockstamp) = db.get_current_blockstamp()? {
Ok(current_blockstamp.id.0)
} else {
Ok(0)
......@@ -106,3 +94,102 @@ impl FilledPaging {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::db::MockBcDbTrait;
use dubp_common_doc::{BlockHash, BlockNumber, Blockstamp};
#[test]
fn test_i32_opt_to_positive_i32() {
assert_eq!(3, i32_opt_to_positive_i32(Some(3), 1));
assert_eq!(0, i32_opt_to_positive_i32(Some(-2), 1));
assert_eq!(50, i32_opt_to_positive_i32(None, 50));
assert_eq!(0, i32_opt_to_positive_i32(Some(0), 1));
}
#[test]
fn test_filled_paging_range_with_short_bc() -> Result<(), DbError> {
let mut mock_db = MockBcDbTrait::new();
mock_db
.expect_get_current_blockstamp()
.times(1)
.returning(|| {
Ok(Some(Blockstamp {
id: BlockNumber(42),
hash: BlockHash(dup_crypto::hashs::Hash::default()),
}))
});
let filled_paging = FilledPaging::new(&mock_db, None)?;
assert_eq! {
Range {
start: 0,
end: 43,
},
filled_paging.get_range()
}
Ok(())
}
#[test]
fn test_filled_paging_range() -> Result<(), DbError> {
let mut mock_db = MockBcDbTrait::new();
mock_db
.expect_get_current_blockstamp()
.times(3)
.returning(|| {
Ok(Some(Blockstamp {
id: BlockNumber(750),
hash: BlockHash(dup_crypto::hashs::Hash::default()),
}))
});
let filled_paging = FilledPaging::new(&mock_db, None)?;
assert_eq! {
Range {
start: 0,
end: 50,
},
filled_paging.get_range()
}
let filled_paging = FilledPaging::new(
&mock_db,
Some(Paging {
page_number: Some(1),
page_size: Some(100),
from_block: Some(500),
to_block: None,
}),
)?;
assert_eq! {
Range {
start: 600,
end: 700,
},
filled_paging.get_range()
}
let filled_paging = FilledPaging::new(
&mock_db,
Some(Paging {
page_number: Some(2),
page_size: Some(100),
from_block: Some(500),
to_block: None,
}),
)?;
assert_eq! {
Range {
start: 700,
end: 751,
},
filled_paging.get_range()
}
Ok(())
}
}
// Copyright (C) 2017-2019 The AXIOM TEAM Association.
//
// 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/>.
// ! Module execute GraphQl schema queries
pub mod block;
pub mod blocks;
pub mod current;
pub mod node;
use durs_bc_db_reader::DbError;
pub(crate) fn db_err_to_juniper_err(e: DbError) -> juniper::FieldError {
juniper::FieldError::from(format!("Db error: {:?}", e))
}
#[cfg(test)]
mod tests {
use crate::context;
use crate::db::MockBcDbTrait;
use crate::graphql::graphql;
use crate::schema::{create_schema, Schema};
use actix_web::dev::Body;
use actix_web::http;
use actix_web::test;
use actix_web::web;
use juniper::http::GraphQLRequest;
use pretty_assertions::assert_eq;
use std::str::FromStr;
use std::sync::Arc;
pub(crate) fn setup(mock_db: MockBcDbTrait) -> web::Data<Arc<Schema>> {
context::init(mock_db, "soft_name", "soft_version");
web::Data::new(std::sync::Arc::new(create_schema()))
}
pub(crate) fn test_gql_query(
schema: web::Data<Arc<Schema>>,
gql_query: &str,
expected_response: serde_json::Value,
) {
let resp = test::block_on(graphql(
schema,
web::Json(GraphQLRequest::new(gql_query.to_owned(), None, None)),
))
.unwrap();
assert_eq!(resp.status(), http::StatusCode::OK);
if let Some(Body::Bytes(ref body_bytes)) = resp.body().as_ref() {
assert_eq!(
expected_response,
serde_json::Value::from_str(
&String::from_utf8(body_bytes.to_vec())
.expect("response have invalid utf8 format.")
)
.expect("response have invalid JSON format.")
)
} else {
panic!("Response must contain body in bytes format.")
}
}
}
// Copyright (C) 2017-2019 The AXIOM TEAM Association.
//
// 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/>.
// ! Module execute GraphQl schema block query
use super::db_err_to_juniper_err;
use crate::context::Context;
use crate::db::BcDbTrait;
use crate::schema::entities::block::Block;
use dubp_common_doc::BlockNumber;
use juniper::Executor;
use juniper::FieldResult;
use juniper_from_schema::{QueryTrail, Walked};
pub(crate) fn execute(
executor: &Executor<'_, Context>,
_trail: &QueryTrail<'_, Block, Walked>,
number: i32,
) -> FieldResult<Option<Block>> {
let block_number = if number >= 0 {
BlockNumber(number as u32)
} else {
return Err(juniper::FieldError::from("Block number must be positive."));
};
executor
.context()
.get_db()
.get_db_block_in_local_blockchain(block_number)
.map_err(db_err_to_juniper_err)
.map(|db_block_opt| db_block_opt.map(Block::from_db_block))
}
#[cfg(test)]
mod tests {
use crate::db::MockBcDbTrait;
use crate::schema::queries::tests;
use dubp_block_doc::block::BlockDocument;
use dubp_blocks_tests_tools::mocks::gen_empty_timed_block_v10;
use dubp_common_doc::{BlockHash, BlockNumber, Blockstamp};
use dup_crypto::hashs::Hash;
use dup_crypto_tests_tools::mocks::{hash, pubkey};
use durs_bc_db_reader::blocks::DbBlock;
use mockall::predicate::eq;
use serde_json::json;
#[test]
fn test_graphql_block() {
let mut mock_db = MockBcDbTrait::new();
mock_db
.expect_get_db_block_in_local_blockchain()
.with(eq(BlockNumber(42)))
.returning(|_| {
let mut block = gen_empty_timed_block_v10(
Blockstamp {
id: BlockNumber(42),
hash: BlockHash(hash('A')),
},
1_488_987_127,
Hash::default(),
);
block.issuers = vec![pubkey('B')];
Ok(Some(DbBlock {
block: BlockDocument::V10(block),
expire_certs: None,
}))
});
let schema = tests::setup(mock_db);
tests::test_gql_query(
schema.clone(),
"{ block { commonTime, currency, hash, issuer, number, version } }",
json!({
"errors": [{
"message": "Field \"block\" argument \"number\" of type \"Int!\" is required but not provided",
"locations": [{
"line": 1,
"column": 3,
}]
}]
}),
);
tests::test_gql_query(
schema,
"{ block(number: 42) { commonTime, currency, hash, issuer, number, version } }",
json!({
"data": {
"block": {
"commonTime": 1_488_987_127.0,
"currency": "test_currency",
"hash": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"issuer": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
"number": 42,
"version": 10
}
}
}),
);
}
}
// Copyright (C) 2017-2019 The AXIOM TEAM Association.
//
// 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/>.
// ! Module execute GraphQl schema blocks query
use super::db_err_to_juniper_err;
use crate::context::Context;
use crate::db::BcDbTrait;
use crate::schema::entities::block::Block;
use crate::schema::paging;
use crate::schema::Paging;
use durs_bc_db_reader::blocks::DbBlock;
use juniper::Executor;
use juniper::FieldResult;
use juniper_from_schema::{QueryTrail, Walked};
pub(crate) fn execute(
executor: &Executor<'_, Context>,
_trail: &QueryTrail<'_, Block, Walked>,
paging_opt: Option<Paging>,
) -> FieldResult<Vec<Block>> {
let db = executor.context().get_db();
let blocks: Vec<DbBlock> = db
.get_db_blocks_in_local_blockchain(
paging::FilledPaging::new(db, paging_opt)
.map_err(db_err_to_juniper_err)?
.get_range(),
)
.map_err(db_err_to_juniper_err)?;
Ok(blocks.into_iter().map(Block::from_db_block).collect())
/*let db: &BcDbRo = &executor.context().get_db();
db.read(|r| {
paging::FilledPaging::new(db, paging_opt)?
.get_range()
.filter_map(|n| match block::get_block(db, r, BlockNumber(n)) {
Ok(Some(db_block)) => Some(Ok(db_block)),
Ok(None) => None,
Err(e) => Some(Err(e)),
})
.collect::<Result<Vec<Block>, DbError>>()
})
.map_err(db_err_to_juniper_err)*/
}
#[cfg(test)]
mod tests {
use crate::db::MockBcDbTrait;
use crate::schema::queries::tests;
use dubp_block_doc::block::BlockDocument;
use dubp_blocks_tests_tools::mocks::gen_empty_timed_block_v10;
use dubp_common_doc::traits::Document;
use dubp_common_doc::{BlockHash, BlockNumber, Blockstamp};
use dup_crypto::hashs::Hash;
use dup_crypto_tests_tools::mocks::{hash, pubkey};
use durs_bc_db_reader::blocks::DbBlock;
use mockall::predicate::eq;
use serde_json::json;
use std::ops::Range;
#[test]
fn test_graphql_blocks() {
let mut mock_db = MockBcDbTrait::new();
let mut block_0 = gen_empty_timed_block_v10(
Blockstamp {
id: BlockNumber(0),
hash: BlockHash(hash('A')),
},
1_488_987_127,
Hash::default(),
);
block_0.issuers = vec![pubkey('A')];
let mut block_1 = gen_empty_timed_block_v10(
Blockstamp {
id: BlockNumber(1),
hash: BlockHash(hash('B')),
},
1_488_987_128,
Hash::default(),
);
block_1.issuers = vec![pubkey('B')];
let mut current_block = gen_empty_timed_block_v10(
Blockstamp {
id: BlockNumber(2),
hash: BlockHash(hash('C')),
},
1_488_987_129,
Hash::default(),
);
current_block.issuers = vec![pubkey('C')];
let current_blockstamp = current_block.blockstamp();
mock_db
.expect_get_current_blockstamp()
.times(1)
.returning(move || Ok(Some(current_blockstamp)));
mock_db
.expect_get_db_blocks_in_local_blockchain()
.with(eq(Range { start: 0, end: 3 }))
.returning(move |_| {
Ok(vec![
DbBlock {
block: BlockDocument::V10(block_0.clone()),
expire_certs: None,
},
DbBlock {
block: BlockDocument::V10(block_1.clone()),
expire_certs: None,
},
DbBlock {
block: BlockDocument::V10(current_block.clone()),
expire_certs: None,
},
])
});
let schema = tests::setup(mock_db);
tests::test_gql_query(
schema,
"{ blocks { commonTime, currency, hash, issuer, number, version } }",
json!({
"data": {
"blocks": [{
"commonTime": 1_488_987_127.0,
"currency": "test_currency",
"hash": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"issuer": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"number": 0,
"version": 10
},
{
"commonTime": 1_488_987_128.0,
"currency": "test_currency",
"hash": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
"issuer": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
"number": 1,
"version": 10
},
{
"commonTime": 1_488_987_129.0,
"currency": "test_currency",
"hash": "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC",
"issuer": "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC",
"number": 2,
"version": 10
}]
}
}),
);
}
}
// Copyright (C) 2017-2019 The AXIOM TEAM Association.
//
// 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/>.
// ! Module execute GraphQl schema current query
use super::db_err_to_juniper_err;
use crate::context::Context;
use crate::db::BcDbTrait;
use crate::schema::entities::block::Block;
use juniper::Executor;
use juniper::FieldResult;
use juniper_from_schema::{QueryTrail, Walked};
pub(crate) fn execute(
executor: &Executor<'_, Context>,
_trail: &QueryTrail<'_, Block, Walked>,
) -> FieldResult<Option<Block>> {
executor
.context()
.get_db()
.get_current_block()
.map_err(db_err_to_juniper_err)
.map(|db_block_opt| db_block_opt.map(Block::from_db_block))
}
#[cfg(test)]
mod tests {
use crate::db::MockBcDbTrait;
use crate::schema::queries::tests;
use dubp_block_doc::block::BlockDocument;
use dubp_blocks_tests_tools::mocks::gen_empty_timed_block_v10;
use dubp_common_doc::{BlockHash, BlockNumber, Blockstamp};
use dup_crypto::hashs::Hash;
use dup_crypto_tests_tools::mocks::{hash, pubkey};
use durs_bc_db_reader::blocks::DbBlock;
use serde_json::json;
#[test]
fn test_graphql_current() {
let mut mock_db = MockBcDbTrait::new();
mock_db.expect_get_current_block().returning(|| {
let mut current_block = gen_empty_timed_block_v10(
Blockstamp {
id: BlockNumber(42),
hash: BlockHash(hash('A')),
},
1_488_987_127,
Hash::default(),
);
current_block.issuers = vec![pubkey('B')];
Ok(Some(DbBlock {
block: BlockDocument::V10(current_block),
expire_certs: None,
}))
});
let schema = tests::setup(mock_db);
tests::test_gql_query(
schema,
"{ current { commonTime, currency, hash, issuer, number, version } }",
json!({
"data": {
"current": {
"commonTime": 1_488_987_127.0,
"currency": "test_currency",
"hash": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"issuer": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
"number": 42,
"version": 10
}
}
}),
)
}
}
// Copyright (C) 2017-2019 The AXIOM TEAM Association.
//
// 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/>.
// ! Module execute GraphQl schema node query
use crate::context::Context;
use crate::schema::entities::node::{Node, Summary};
use juniper::Executor;
use juniper::FieldResult;
use juniper_from_schema::{QueryTrail, Walked};
pub(crate) fn execute(
executor: &Executor<'_, Context>,
_trail: &QueryTrail<'_, Node, Walked>,
) -> FieldResult<Node> {
Ok(Node {
summary: Summary {
software: executor.context().get_software_name(),
version: executor.context().get_software_version(),
},
})
}
#[cfg(test)]
mod tests {
use crate::db::MockBcDbTrait;
use crate::schema::queries::tests;
use serde_json::json;
#[test]
fn test_graphql_current() {
let schema = tests::setup(MockBcDbTrait::new());
tests::test_gql_query(
schema,
"{ node { summary { software, version } } }",
json!({
"data": {
"node": {
"summary": {
"software": "soft_name",
"version": "soft_version"
}
}
}
}),
)
}
}
......@@ -15,18 +15,20 @@
// web server implementaion based on actix-web
use crate::context;
use crate::schema::{create_schema, Schema};
use actix_web::{middleware, web, App, Error, HttpResponse, HttpServer};
use crate::graphql::graphql;
use crate::schema::create_schema;
use actix_web::{middleware, web, App, HttpResponse, HttpServer};
#[cfg(not(test))]
use durs_common_tools::fatal_error;
use durs_conf::DuRsConf;
use durs_module::SoftwareMetaDatas;
use durs_network_documents::host::Host;
use durs_network_documents::url::Url;
use futures::future::Future;
use juniper::http::graphiql::graphiql_source;
use juniper::http::GraphQLRequest;
use std::net::SocketAddr;
use std::sync::Arc;
#[cfg(test)]
use crate::db::MockBcDbTrait;
fn graphiql() -> HttpResponse {
let html = graphiql_source("/graphql");
......@@ -35,23 +37,6 @@ fn graphiql() -> HttpResponse {
.body(html)
}
fn graphql(
schema: web::Data<Arc<Schema>>,
data: web::Json<GraphQLRequest>,
) -> impl Future<Item = HttpResponse, Error = Error> {
let context = crate::context::get_context();
web::block(move || {
let result = data.execute(&schema, context);
serde_json::to_string(&result)
})
.map_err(Error::from)
.and_then(|user| {
Ok(HttpResponse::Ok()
.content_type("application/json")
.body(user))
})
}
pub fn start_web_server(
soft_meta_datas: &SoftwareMetaDatas<DuRsConf>,
host: Host,
......@@ -65,13 +50,29 @@ pub fn start_web_server(
// Create Juniper schema
let schema = std::sync::Arc::new(create_schema());
// Instanciate the context
// Get DB
#[cfg(not(test))]
let db = {
let db_path = durs_conf::get_blockchain_db_path(soft_meta_datas.profile_path.clone());
if let Ok(db) = durs_bc_db_reader::open_db_ro(&std::path::Path::new(&db_path)) {
context::init(db, soft_meta_datas.soft_name, soft_meta_datas.soft_version);
db
} else {
fatal_error!("GVA: fail to open DB.");
}
};
#[cfg(test)]
let db = MockBcDbTrait::new();
cfg_if::cfg_if! {
if #[cfg(test)] {
MockBcDbTrait::new()
} else {
}
};
// Instanciate the context
context::init(db, soft_meta_datas.soft_name, soft_meta_datas.soft_version);
// Start http server
HttpServer::new(move || {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment