use super::*;
use std::{mem, sync::Arc};
use assert_matches::assert_matches;
use codec::Encode;
use tet_core::{
ed25519, sr25519,
H256, blake2_256, hexdisplay::HexDisplay, testing::{ED25519, SR25519},
crypto::{CryptoTypePublicPair, Pair, Public},
};
use tp_keystore::testing::KeyStore;
use rpc::futures::Stream as _;
use tetcore_test_runtime_client::{
self, AccountKeyring, runtime::{Extrinsic, Transfer, SessionKeys, Block},
DefaultTestClientBuilderExt, TestClientBuilderExt, Backend, Client,
};
use tc_transaction_pool::{BasicPool, FullChainApi};
use futures::{executor, compat::Future01CompatExt};
fn uxt(sender: AccountKeyring, nonce: u64) -> Extrinsic {
let tx = Transfer {
amount: Default::default(),
nonce,
from: sender.into(),
to: Default::default(),
};
tx.into_signed_tx()
}
type FullTransactionPool = BasicPool<
FullChainApi<Client<Backend>, Block>,
Block,
>;
struct TestSetup {
pub client: Arc<Client<Backend>>,
pub keystore: Arc<KeyStore>,
pub pool: Arc<FullTransactionPool>,
}
impl Default for TestSetup {
fn default() -> Self {
let keystore = Arc::new(KeyStore::new());
let client_builder = tetcore_test_runtime_client::TestClientBuilder::new();
let client = Arc::new(client_builder.set_keystore(keystore.clone()).build());
let spawner = tet_core::testing::TaskExecutor::new();
let pool = BasicPool::new_full(
Default::default(),
None,
spawner,
client.clone(),
);
TestSetup {
client,
keystore,
pool,
}
}
}
impl TestSetup {
fn author(&self) -> Author<FullTransactionPool, Client<Backend>> {
Author {
client: self.client.clone(),
pool: self.pool.clone(),
subscriptions: SubscriptionManager::new(Arc::new(crate::testing::TaskExecutor)),
keystore: self.keystore.clone(),
deny_unsafe: DenyUnsafe::No,
}
}
}
#[test]
fn submit_transaction_should_not_cause_error() {
let p = TestSetup::default().author();
let xt = uxt(AccountKeyring::Alice, 1).encode();
let h: H256 = blake2_256(&xt).into();
assert_matches!(
AuthorApi::submit_extrinsic(&p, xt.clone().into()).wait(),
Ok(h2) if h == h2
);
assert!(
AuthorApi::submit_extrinsic(&p, xt.into()).wait().is_err()
);
}
#[test]
fn submit_rich_transaction_should_not_cause_error() {
let p = TestSetup::default().author();
let xt = uxt(AccountKeyring::Alice, 0).encode();
let h: H256 = blake2_256(&xt).into();
assert_matches!(
AuthorApi::submit_extrinsic(&p, xt.clone().into()).wait(),
Ok(h2) if h == h2
);
assert!(
AuthorApi::submit_extrinsic(&p, xt.into()).wait().is_err()
);
}
#[test]
fn should_watch_extrinsic() {
let setup = TestSetup::default();
let p = setup.author();
let (subscriber, id_rx, data) = tetsy_jsonrpc_pubsub::typed::Subscriber::new_test("test");
p.watch_extrinsic(
Default::default(),
subscriber,
uxt(AccountKeyring::Alice, 0).encode().into(),
);
let id = executor::block_on(id_rx.compat()).unwrap().unwrap();
assert_matches!(id, SubscriptionId::String(_));
let id = match id {
SubscriptionId::String(id) => id,
_ => unreachable!(),
};
let replacement = {
let tx = Transfer {
amount: 5,
nonce: 0,
from: AccountKeyring::Alice.into(),
to: Default::default(),
};
tx.into_signed_tx()
};
AuthorApi::submit_extrinsic(&p, replacement.encode().into()).wait().unwrap();
let (res, data) = executor::block_on(data.into_future().compat()).unwrap();
let expected = Some(format!(
r#"{{"jsonrpc":"2.0","method":"test","params":{{"result":"ready","subscription":"{}"}}}}"#,
id,
));
assert_eq!(res, expected);
let h = blake2_256(&replacement.encode());
let expected = Some(format!(
r#"{{"jsonrpc":"2.0","method":"test","params":{{"result":{{"usurped":"0x{}"}},"subscription":"{}"}}}}"#,
HexDisplay::from(&h),
id,
));
let res = executor::block_on(data.into_future().compat()).unwrap().0;
assert_eq!(res, expected);
}
#[test]
fn should_return_watch_validation_error() {
let setup = TestSetup::default();
let p = setup.author();
let (subscriber, id_rx, _data) = tetsy_jsonrpc_pubsub::typed::Subscriber::new_test("test");
p.watch_extrinsic(Default::default(), subscriber, uxt(AccountKeyring::Alice, 179).encode().into());
let res = executor::block_on(id_rx.compat()).unwrap();
assert!(res.is_err(), "Expected the transaction to be rejected as invalid.");
}
#[test]
fn should_return_pending_extrinsics() {
let p = TestSetup::default().author();
let ex = uxt(AccountKeyring::Alice, 0);
AuthorApi::submit_extrinsic(&p, ex.encode().into()).wait().unwrap();
assert_matches!(
p.pending_extrinsics(),
Ok(ref expected) if *expected == vec![Bytes(ex.encode())]
);
}
#[test]
fn should_remove_extrinsics() {
let setup = TestSetup::default();
let p = setup.author();
let ex1 = uxt(AccountKeyring::Alice, 0);
p.submit_extrinsic(ex1.encode().into()).wait().unwrap();
let ex2 = uxt(AccountKeyring::Alice, 1);
p.submit_extrinsic(ex2.encode().into()).wait().unwrap();
let ex3 = uxt(AccountKeyring::Bob, 0);
let hash3 = p.submit_extrinsic(ex3.encode().into()).wait().unwrap();
assert_eq!(setup.pool.status().ready, 3);
let removed = p.remove_extrinsic(vec![
hash::ExtrinsicOrHash::Hash(hash3),
hash::ExtrinsicOrHash::Extrinsic(ex1.encode().into()),
]).unwrap();
assert_eq!(removed.len(), 3);
}
#[test]
fn should_insert_key() {
let setup = TestSetup::default();
let p = setup.author();
let suri = "//Alice";
let key_pair = ed25519::Pair::from_string(suri, None).expect("Generates keypair");
p.insert_key(
String::from_utf8(ED25519.0.to_vec()).expect("Keytype is a valid string"),
suri.to_string(),
key_pair.public().0.to_vec().into(),
).expect("Insert key");
let public_keys = SyncCryptoStore::keys(&*setup.keystore, ED25519).unwrap();
assert!(public_keys.contains(&CryptoTypePublicPair(ed25519::CRYPTO_ID, key_pair.public().to_raw_vec())));
}
#[test]
fn should_rotate_keys() {
let setup = TestSetup::default();
let p = setup.author();
let new_public_keys = p.rotate_keys().expect("Rotates the keys");
let session_keys = SessionKeys::decode(&mut &new_public_keys[..])
.expect("SessionKeys decode successfully");
let ed25519_public_keys = SyncCryptoStore::keys(&*setup.keystore, ED25519).unwrap();
let sr25519_public_keys = SyncCryptoStore::keys(&*setup.keystore, SR25519).unwrap();
assert!(ed25519_public_keys.contains(&CryptoTypePublicPair(ed25519::CRYPTO_ID, session_keys.ed25519.to_raw_vec())));
assert!(sr25519_public_keys.contains(&CryptoTypePublicPair(sr25519::CRYPTO_ID, session_keys.sr25519.to_raw_vec())));
}
#[test]
fn test_has_session_keys() {
let setup = TestSetup::default();
let p = setup.author();
let non_existent_public_keys = TestSetup::default()
.author()
.rotate_keys()
.expect("Rotates the keys");
let public_keys = p.rotate_keys().expect("Rotates the keys");
let test_vectors = vec![
(public_keys, Ok(true)),
(vec![1, 2, 3].into(), Err(Error::InvalidSessionKeys)),
(non_existent_public_keys, Ok(false)),
];
for (keys, result) in test_vectors {
assert_eq!(
result.map_err(|e| mem::discriminant(&e)),
p.has_session_keys(keys).map_err(|e| mem::discriminant(&e)),
);
}
}
#[test]
fn test_has_key() {
let setup = TestSetup::default();
let p = setup.author();
let suri = "//Alice";
let alice_key_pair = ed25519::Pair::from_string(suri, None).expect("Generates keypair");
p.insert_key(
String::from_utf8(ED25519.0.to_vec()).expect("Keytype is a valid string"),
suri.to_string(),
alice_key_pair.public().0.to_vec().into(),
).expect("Insert key");
let bob_key_pair = ed25519::Pair::from_string("//Bob", None).expect("Generates keypair");
let test_vectors = vec![
(alice_key_pair.public().to_raw_vec().into(), ED25519, Ok(true)),
(alice_key_pair.public().to_raw_vec().into(), SR25519, Ok(false)),
(bob_key_pair.public().to_raw_vec().into(), ED25519, Ok(false)),
];
for (key, key_type, result) in test_vectors {
assert_eq!(
result.map_err(|e| mem::discriminant(&e)),
p.has_key(
key,
String::from_utf8(key_type.0.to_vec()).expect("Keytype is a valid string"),
).map_err(|e| mem::discriminant(&e)),
);
}
}