util `fake-fetch` (#8363)

* start

* hash-fetch

* rpc

* revert price-info

* remove used file

* adapt to changes in fetch

* make fake-fetch generic over

* fix tests to comply with the new `fake-fetch`
This commit is contained in:
Niklas Adolfsson 2018-04-11 11:59:04 +02:00 committed by Marek Kotewicz
parent 6cf441fc9e
commit 9fe991db1c
13 changed files with 82 additions and 128 deletions

13
Cargo.lock generated
View File

@ -1025,6 +1025,15 @@ dependencies = [
"vm 0.1.0", "vm 0.1.0",
] ]
[[package]]
name = "fake-fetch"
version = "0.0.1"
dependencies = [
"fetch 0.1.0",
"futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.11.24 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "fdlimit" name = "fdlimit"
version = "0.1.1" version = "0.1.1"
@ -1940,6 +1949,7 @@ dependencies = [
"ethcore-transaction 0.1.0", "ethcore-transaction 0.1.0",
"ethereum-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethereum-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ethkey 0.3.0", "ethkey 0.3.0",
"fake-fetch 0.0.1",
"fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2063,6 +2073,7 @@ dependencies = [
"ethabi-derive 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "ethabi-derive 5.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ethcore-bytes 0.1.0", "ethcore-bytes 0.1.0",
"ethereum-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "ethereum-types 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"fake-fetch 0.0.1",
"fetch 0.1.0", "fetch 0.1.0",
"futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2148,6 +2159,7 @@ dependencies = [
"ethjson 0.1.0", "ethjson 0.1.0",
"ethkey 0.3.0", "ethkey 0.3.0",
"ethstore 0.2.0", "ethstore 0.2.0",
"fake-fetch 0.0.1",
"fetch 0.1.0", "fetch 0.1.0",
"futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2465,6 +2477,7 @@ dependencies = [
name = "price-info" name = "price-info"
version = "1.11.0" version = "1.11.0"
dependencies = [ dependencies = [
"fake-fetch 0.0.1",
"fetch 0.1.0", "fetch 0.1.0",
"futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -82,6 +82,7 @@ rustc_version = "0.2"
pretty_assertions = "0.1" pretty_assertions = "0.1"
ipnetwork = "0.12.6" ipnetwork = "0.12.6"
tempdir = "0.3" tempdir = "0.3"
fake-fetch = { path = "util/fake-fetch" }
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
winapi = "0.2" winapi = "0.2"

View File

@ -28,3 +28,4 @@ ethabi-contract = "5.0"
[dev-dependencies] [dev-dependencies]
hyper = "0.11" hyper = "0.11"
parking_lot = "0.5" parking_lot = "0.5"
fake-fetch = { path = "../util/fake-fetch" }

View File

@ -193,53 +193,14 @@ fn random_temp_path() -> PathBuf {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
extern crate hyper; use fake_fetch::FakeFetch;
use rustc_hex::FromHex; use rustc_hex::FromHex;
use std::sync::{Arc, mpsc}; use std::sync::{Arc, mpsc};
use parking_lot::Mutex; use parking_lot::Mutex;
use futures::future;
use futures_cpupool::CpuPool; use futures_cpupool::CpuPool;
use fetch::{self, Fetch, Url, Request};
use parity_reactor::Remote; use parity_reactor::Remote;
use urlhint::tests::{FakeRegistrar, URLHINT}; use urlhint::tests::{FakeRegistrar, URLHINT};
use super::{Error, Client, HashFetch, random_temp_path}; use super::{Error, Client, HashFetch, random_temp_path};
use self::hyper::StatusCode;
#[derive(Clone)]
struct FakeFetch {
return_success: bool
}
impl Fetch for FakeFetch {
type Result = future::Ok<fetch::Response, fetch::Error>;
fn fetch(&self, request: Request, abort: fetch::Abort) -> Self::Result {
assert_eq!(request.url().as_str(), "https://parity.io/assets/images/ethcore-black-horizontal.png");
let u = request.url().clone();
future::ok(if self.return_success {
fetch::client::Response::new(u, hyper::Response::new().with_body(&b"result"[..]), abort)
} else {
fetch::client::Response::new(u, hyper::Response::new().with_status(StatusCode::NotFound), abort)
})
}
fn get(&self, url: &str, abort: fetch::Abort) -> Self::Result {
let url: Url = match url.parse() {
Ok(u) => u,
Err(e) => return future::err(e.into())
};
self.fetch(Request::get(url), abort)
}
fn post(&self, url: &str, abort: fetch::Abort) -> Self::Result {
let url: Url = match url.parse() {
Ok(u) => u,
Err(e) => return future::err(e.into())
};
self.fetch(Request::post(url), abort)
}
}
fn registrar() -> FakeRegistrar { fn registrar() -> FakeRegistrar {
let mut registrar = FakeRegistrar::new(); let mut registrar = FakeRegistrar::new();
@ -254,7 +215,7 @@ mod tests {
fn should_return_error_if_hash_not_found() { fn should_return_error_if_hash_not_found() {
// given // given
let contract = Arc::new(FakeRegistrar::new()); let contract = Arc::new(FakeRegistrar::new());
let fetch = FakeFetch { return_success: false }; let fetch = FakeFetch::new(None::<usize>);
let client = Client::with_fetch(contract.clone(), CpuPool::new(1), fetch, Remote::new_sync()); let client = Client::with_fetch(contract.clone(), CpuPool::new(1), fetch, Remote::new_sync());
// when // when
@ -272,7 +233,7 @@ mod tests {
fn should_return_error_if_response_is_not_successful() { fn should_return_error_if_response_is_not_successful() {
// given // given
let registrar = Arc::new(registrar()); let registrar = Arc::new(registrar());
let fetch = FakeFetch { return_success: false }; let fetch = FakeFetch::new(None::<usize>);
let client = Client::with_fetch(registrar.clone(), CpuPool::new(1), fetch, Remote::new_sync()); let client = Client::with_fetch(registrar.clone(), CpuPool::new(1), fetch, Remote::new_sync());
// when // when
@ -290,7 +251,7 @@ mod tests {
fn should_return_hash_mismatch() { fn should_return_hash_mismatch() {
// given // given
let registrar = Arc::new(registrar()); let registrar = Arc::new(registrar());
let fetch = FakeFetch { return_success: true }; let fetch = FakeFetch::new(Some(1));
let mut client = Client::with_fetch(registrar.clone(), CpuPool::new(1), fetch, Remote::new_sync()); let mut client = Client::with_fetch(registrar.clone(), CpuPool::new(1), fetch, Remote::new_sync());
let path = random_temp_path(); let path = random_temp_path();
let path2 = path.clone(); let path2 = path.clone();
@ -304,7 +265,7 @@ mod tests {
// then // then
let result = rx.recv().unwrap(); let result = rx.recv().unwrap();
let hash = "0x06b0a4f426f6713234b2d4b2468640bc4e0bb72657a920ad24c5087153c593c8".into(); let hash = "0x2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e".into();
assert_eq!(result.unwrap_err(), Error::HashMismatch { expected: 2.into(), got: hash }); assert_eq!(result.unwrap_err(), Error::HashMismatch { expected: 2.into(), got: hash });
assert!(!path.exists(), "Temporary file should be removed."); assert!(!path.exists(), "Temporary file should be removed.");
} }
@ -313,12 +274,12 @@ mod tests {
fn should_return_path_if_hash_matches() { fn should_return_path_if_hash_matches() {
// given // given
let registrar = Arc::new(registrar()); let registrar = Arc::new(registrar());
let fetch = FakeFetch { return_success: true }; let fetch = FakeFetch::new(Some(1));
let client = Client::with_fetch(registrar.clone(), CpuPool::new(1), fetch, Remote::new_sync()); let client = Client::with_fetch(registrar.clone(), CpuPool::new(1), fetch, Remote::new_sync());
// when // when
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
client.fetch("0x06b0a4f426f6713234b2d4b2468640bc4e0bb72657a920ad24c5087153c593c8".into(), client.fetch("0x2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e".into(),
Default::default(), Default::default(),
Box::new(move |result| { tx.send(result).unwrap(); })); Box::new(move |result| { tx.send(result).unwrap(); }));

View File

@ -42,6 +42,10 @@ extern crate ethabi_derive;
extern crate ethabi_contract; extern crate ethabi_contract;
#[cfg(test)] #[cfg(test)]
extern crate parking_lot; extern crate parking_lot;
#[cfg(test)]
extern crate hyper;
#[cfg(test)]
extern crate fake_fetch;
mod client; mod client;

View File

@ -16,3 +16,4 @@ serde_json = "1.0"
[dev-dependencies] [dev-dependencies]
hyper = "0.11" hyper = "0.11"
parking_lot = "0.5" parking_lot = "0.5"
fake-fetch = { path = "../util/fake-fetch" }

View File

@ -25,6 +25,9 @@ extern crate serde_json;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
#[cfg(test)]
extern crate fake_fetch;
pub extern crate fetch; pub extern crate fetch;
use std::cmp; use std::cmp;
@ -133,68 +136,18 @@ impl<F: Fetch> Client<F> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
extern crate hyper;
extern crate parking_lot;
use self::parking_lot::Mutex;
use std::sync::Arc; use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use fetch;
use fetch::{Fetch, Url, Request};
use futures_cpupool::CpuPool; use futures_cpupool::CpuPool;
use futures::future::{self, FutureResult};
use Client; use Client;
use self::hyper::StatusCode; use std::sync::atomic::{AtomicBool, Ordering};
use fake_fetch::FakeFetch;
#[derive(Clone)] fn price_info_ok(response: &str) -> Client<FakeFetch<String>> {
struct FakeFetch(Option<String>, Arc<Mutex<u64>>); Client::new(FakeFetch::new(Some(response.to_owned())), CpuPool::new(1))
impl FakeFetch {
fn new() -> Result<Self, fetch::Error> {
Ok(FakeFetch(None, Default::default()))
}
} }
impl Fetch for FakeFetch { fn price_info_not_found() -> Client<FakeFetch<String>> {
type Result = FutureResult<fetch::Response, fetch::Error>; Client::new(FakeFetch::new(None::<String>), CpuPool::new(1))
fn fetch(&self, request: Request, abort: fetch::Abort) -> Self::Result {
assert_eq!(request.url().as_str(), "https://api.etherscan.io/api?module=stats&action=ethprice");
let u = request.url().clone();
let mut val = self.1.lock();
*val = *val + 1;
if let Some(ref response) = self.0 {
let r = hyper::Response::new().with_body(response.clone());
future::ok(fetch::client::Response::new(u, r, abort))
} else {
let r = hyper::Response::new().with_status(StatusCode::NotFound);
future::ok(fetch::client::Response::new(u, r, abort))
}
}
fn get(&self, url: &str, abort: fetch::Abort) -> Self::Result {
let url: Url = match url.parse() {
Ok(u) => u,
Err(e) => return future::err(e.into())
};
self.fetch(Request::get(url), abort)
}
fn post(&self, url: &str, abort: fetch::Abort) -> Self::Result {
let url: Url = match url.parse() {
Ok(u) => u,
Err(e) => return future::err(e.into())
};
self.fetch(Request::post(url), abort)
}
}
fn price_info_ok(response: &str) -> Client<FakeFetch> {
Client::new(FakeFetch(Some(response.to_owned()), Default::default()), CpuPool::new(1))
}
fn price_info_not_found() -> Client<FakeFetch> {
Client::new(FakeFetch::new().unwrap(), CpuPool::new(1))
} }
#[test] #[test]

View File

@ -70,3 +70,4 @@ pretty_assertions = "0.1"
macros = { path = "../util/macros" } macros = { path = "../util/macros" }
ethcore-network = { path = "../util/network" } ethcore-network = { path = "../util/network" }
kvdb-memorydb = { path = "../util/kvdb-memorydb" } kvdb-memorydb = { path = "../util/kvdb-memorydb" }
fake-fetch = { path = "../util/fake-fetch" }

View File

@ -91,6 +91,9 @@ extern crate macros;
#[cfg(test)] #[cfg(test)]
extern crate kvdb_memorydb; extern crate kvdb_memorydb;
#[cfg(test)]
extern crate fake_fetch;
extern crate tempdir; extern crate tempdir;
pub extern crate jsonrpc_ws_server as ws; pub extern crate jsonrpc_ws_server as ws;

View File

@ -17,14 +17,12 @@
//! Test rpc services. //! Test rpc services.
mod dapps; mod dapps;
mod fetch;
mod miner_service; mod miner_service;
mod snapshot_service; mod snapshot_service;
mod sync_provider; mod sync_provider;
mod update_service; mod update_service;
pub use self::dapps::TestDappsService; pub use self::dapps::TestDappsService;
pub use self::fetch::TestFetch;
pub use self::miner_service::TestMinerService; pub use self::miner_service::TestMinerService;
pub use self::snapshot_service::TestSnapshotService; pub use self::snapshot_service::TestSnapshotService;
pub use self::sync_provider::{Config, TestSyncProvider}; pub use self::sync_provider::{Config, TestSyncProvider};

View File

@ -26,9 +26,11 @@ use futures_cpupool::CpuPool;
use jsonrpc_core::IoHandler; use jsonrpc_core::IoHandler;
use v1::{ParitySet, ParitySetClient}; use v1::{ParitySet, ParitySetClient};
use v1::tests::helpers::{TestMinerService, TestFetch, TestUpdater, TestDappsService}; use v1::tests::helpers::{TestMinerService, TestUpdater, TestDappsService};
use super::manage_network::TestManageNetwork; use super::manage_network::TestManageNetwork;
use fake_fetch::FakeFetch;
fn miner_service() -> Arc<TestMinerService> { fn miner_service() -> Arc<TestMinerService> {
Arc::new(TestMinerService::default()) Arc::new(TestMinerService::default())
} }
@ -45,7 +47,7 @@ fn updater_service() -> Arc<TestUpdater> {
Arc::new(TestUpdater::default()) Arc::new(TestUpdater::default())
} }
pub type TestParitySetClient = ParitySetClient<TestBlockChainClient, TestMinerService, TestUpdater, TestFetch>; pub type TestParitySetClient = ParitySetClient<TestBlockChainClient, TestMinerService, TestUpdater, FakeFetch<usize>>;
fn parity_set_client( fn parity_set_client(
client: &Arc<TestBlockChainClient>, client: &Arc<TestBlockChainClient>,
@ -55,7 +57,7 @@ fn parity_set_client(
) -> TestParitySetClient { ) -> TestParitySetClient {
let dapps_service = Arc::new(TestDappsService); let dapps_service = Arc::new(TestDappsService);
let pool = CpuPool::new(1); let pool = CpuPool::new(1);
ParitySetClient::new(client, miner, updater, &(net.clone() as Arc<ManageNetwork>), Some(dapps_service), TestFetch::default(), pool) ParitySetClient::new(client, miner, updater, &(net.clone() as Arc<ManageNetwork>), Some(dapps_service), FakeFetch::new(Some(1)), pool)
} }
#[test] #[test]

View File

@ -0,0 +1,11 @@
[package]
description = "Mock fetcher for testing"
name = "fake-fetch"
version = "0.0.1"
license = "GPL-3.0"
authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
fetch = { path = "../fetch" }
futures = "0.1"
hyper = "0.11"

View File

@ -1,4 +1,4 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd. // Copyright 2015-2018 Parity Technologies (UK) Ltd.
// This file is part of Parity. // This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify // Parity is free software: you can redistribute it and/or modify
@ -14,37 +14,42 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Test implementation of fetch client. extern crate fetch;
extern crate hyper;
extern crate futures;
use std::thread; use hyper::StatusCode;
use std::boxed::Box; use futures::{future, future::FutureResult};
use jsonrpc_core::futures::{self, Future}; use fetch::{Fetch, Url, Request};
use fetch::{self, Fetch, Url, Request};
use futures::future;
use hyper;
/// Test implementation of fetcher. Will always return the same file. #[derive(Clone, Default)]
#[derive(Default, Clone)] pub struct FakeFetch<T> where T: Clone + Send + Sync {
pub struct TestFetch; val: Option<T>,
}
impl Fetch for TestFetch { impl<T> FakeFetch<T> where T: Clone + Send + Sync {
type Result = Box<Future<Item = fetch::Response, Error = fetch::Error> + Send + 'static>; pub fn new(t: Option<T>) -> Self {
FakeFetch { val : t }
}
}
impl<T: 'static> Fetch for FakeFetch<T> where T: Clone + Send+ Sync {
type Result = FutureResult<fetch::Response, fetch::Error>;
fn fetch(&self, request: Request, abort: fetch::Abort) -> Self::Result { fn fetch(&self, request: Request, abort: fetch::Abort) -> Self::Result {
let u = request.url().clone(); let u = request.url().clone();
let (tx, rx) = futures::oneshot(); future::ok(if self.val.is_some() {
thread::spawn(move || {
let r = hyper::Response::new().with_body(&b"Some content"[..]); let r = hyper::Response::new().with_body(&b"Some content"[..]);
tx.send(fetch::Response::new(u, r, abort)).unwrap(); fetch::client::Response::new(u, r, abort)
}); } else {
fetch::client::Response::new(u, hyper::Response::new().with_status(StatusCode::NotFound), abort)
Box::new(rx.map_err(|_| fetch::Error::Aborted)) })
} }
fn get(&self, url: &str, abort: fetch::Abort) -> Self::Result { fn get(&self, url: &str, abort: fetch::Abort) -> Self::Result {
let url: Url = match url.parse() { let url: Url = match url.parse() {
Ok(u) => u, Ok(u) => u,
Err(e) => return Box::new(future::err(e.into())) Err(e) => return future::err(e.into())
}; };
self.fetch(Request::get(url), abort) self.fetch(Request::get(url), abort)
} }
@ -52,7 +57,7 @@ impl Fetch for TestFetch {
fn post(&self, url: &str, abort: fetch::Abort) -> Self::Result { fn post(&self, url: &str, abort: fetch::Abort) -> Self::Result {
let url: Url = match url.parse() { let url: Url = match url.parse() {
Ok(u) => u, Ok(u) => u,
Err(e) => return Box::new(future::err(e.into())) Err(e) => return future::err(e.into())
}; };
self.fetch(Request::post(url), abort) self.fetch(Request::post(url), abort)
} }