From d38721e1a0ff79b5ad25099ce49dbe97da884b04 Mon Sep 17 00:00:00 2001 From: Dave Hrycyszyn Date: Mon, 17 Jun 2024 15:25:22 +0100 Subject: [PATCH] Splitting side-node crate into lib/bin for integration tests --- side-node/src/crdt.rs | 10 +++---- side-node/src/lib.rs | 56 ++++++++++++++++++++++++++++++++++++++ side-node/src/main.rs | 57 +++------------------------------------ side-node/src/node.rs | 2 +- side-node/src/utils.rs | 4 +-- side-node/tests/crdt.rs | 59 ++++++++++++++++++++++++++++++++--------- 6 files changed, 113 insertions(+), 75 deletions(-) create mode 100644 side-node/src/lib.rs diff --git a/side-node/src/crdt.rs b/side-node/src/crdt.rs index 5962b3d..48ff2f3 100644 --- a/side-node/src/crdt.rs +++ b/side-node/src/crdt.rs @@ -7,20 +7,20 @@ use serde::{Deserialize, Serialize}; #[add_crdt_fields] #[derive(Clone, CrdtNode, Serialize, Deserialize)] -pub(crate) struct TransactionList { - pub(crate) list: ListCrdt, +pub struct TransactionList { + pub list: ListCrdt, } impl TransactionList { - pub(crate) fn view_sha(&self) -> String { + pub fn view_sha(&self) -> String { sha256::digest(serde_json::to_string(&self.list.view()).unwrap().as_bytes()).to_string() } } /// A fake Transaction struct we can use as a simulated payload #[add_crdt_fields] -#[derive(Clone, CrdtNode, Serialize, Deserialize)] -pub(crate) struct Transaction { +#[derive(Clone, CrdtNode, Serialize, Deserialize, PartialEq)] +pub struct Transaction { from: String, to: String, amount: f64, diff --git a/side-node/src/lib.rs b/side-node/src/lib.rs new file mode 100644 index 0000000..713c800 --- /dev/null +++ b/side-node/src/lib.rs @@ -0,0 +1,56 @@ +use bft_json_crdt::json_crdt::{BaseCrdt, SignedOp}; +use cli::{parse_args, Commands}; +use crdt::TransactionList; +use node::SideNode; +use tokio::{sync::mpsc, task}; +use websocket::WebSocketClient; + +pub(crate) mod cli; +pub mod crdt; +pub(crate) mod init; +pub(crate) mod keys; +pub(crate) mod node; +pub(crate) mod stdin; +pub mod utils; +pub(crate) mod websocket; + +#[tokio::main] +pub async fn run() { + let args = parse_args(); + + match &args.command { + Some(Commands::Init { name }) => { + let config = init::config::SideNodeConfig { + name: name.to_string(), + }; + + let _ = init::init(utils::home(name), config); + } + Some(Commands::Run { name }) => { + let mut node = setup(name).await; + node.start().await; + } + None => println!("No command provided. Exiting. See --help for more information."), + } +} + +/// Wire everything up outside the application so we can test more easily later +async fn setup(name: &String) -> SideNode { + // First, load up the keys and create a bft-crdt + let side_dir = utils::home(name); + let keys = keys::load_from_file(side_dir); + let crdt = BaseCrdt::::new(&keys); + + // Channels for internal communication, and a tokio task for stdin input + let (incoming_sender, incoming_receiver) = mpsc::channel::(32); + let (stdin_sender, stdin_receiver) = std::sync::mpsc::channel(); + task::spawn(async move { + stdin::input(stdin_sender); + }); + + // Finally, create the node and return it + let handle = WebSocketClient::new(incoming_sender).await; + let node = SideNode::new(crdt, keys, incoming_receiver, stdin_receiver, handle); + println!("Node setup complete."); + node +} diff --git a/side-node/src/main.rs b/side-node/src/main.rs index 29d2950..f82e664 100644 --- a/side-node/src/main.rs +++ b/side-node/src/main.rs @@ -1,56 +1,5 @@ -use bft_json_crdt::json_crdt::{BaseCrdt, SignedOp}; -use cli::{parse_args, Commands}; -use crdt::TransactionList; -use node::SideNode; -use tokio::{sync::mpsc, task}; -use websocket::WebSocketClient; +use side_node; -pub(crate) mod cli; -pub(crate) mod crdt; -pub(crate) mod init; -pub(crate) mod keys; -pub(crate) mod node; -pub(crate) mod stdin; -pub(crate) mod utils; -pub(crate) mod websocket; - -#[tokio::main] -async fn main() { - let args = parse_args(); - - match &args.command { - Some(Commands::Init { name }) => { - let config = init::config::SideNodeConfig { - name: name.to_string(), - }; - - let _ = init::init(utils::home(name), config); - } - Some(Commands::Run { name }) => { - let mut node = setup(name).await; - node.start().await; - } - None => println!("No command provided. Exiting. See --help for more information."), - } -} - -/// Wire everything up outside the application so we can test more easily later -async fn setup(name: &String) -> SideNode { - // First, load up the keys and create a bft-crdt - let side_dir = utils::home(name); - let keys = keys::load_from_file(side_dir); - let crdt = BaseCrdt::::new(&keys); - - // Channels for internal communication, and a tokio task for stdin input - let (incoming_sender, incoming_receiver) = mpsc::channel::(32); - let (stdin_sender, stdin_receiver) = std::sync::mpsc::channel(); - task::spawn(async move { - stdin::input(stdin_sender); - }); - - // Finally, create the node and return it - let handle = WebSocketClient::new(incoming_sender).await; - let node = SideNode::new(crdt, keys, incoming_receiver, stdin_receiver, handle); - println!("Node setup complete."); - node +fn main() { + side_node::run(); } diff --git a/side-node/src/node.rs b/side-node/src/node.rs index 18a1b3a..c69e9c1 100644 --- a/side-node/src/node.rs +++ b/side-node/src/node.rs @@ -36,7 +36,7 @@ impl SideNode { loop { match self.stdin_receiver.try_recv() { Ok(stdin) => { - let transaction = utils::fake_transaction(stdin); + let transaction = utils::fake_transaction_json(stdin); let json = serde_json::to_value(transaction).unwrap(); let signed_op = self.add_transaction_local(json); self.send_to_network(signed_op).await; diff --git a/side-node/src/utils.rs b/side-node/src/utils.rs index d847d37..4bd2818 100644 --- a/side-node/src/utils.rs +++ b/side-node/src/utils.rs @@ -24,9 +24,9 @@ pub(crate) fn home(name: &String) -> std::path::PathBuf { } /// Generate a fake transaction with customizable from_pubkey String -pub(crate) fn fake_transaction(from_pubkey: String) -> Value { +pub fn fake_transaction_json(from: String) -> Value { json!({ - "from": from_pubkey, + "from": from, "to": "Bob", "amount": 1 }) diff --git a/side-node/tests/crdt.rs b/side-node/tests/crdt.rs index bc70e06..169ed86 100644 --- a/side-node/tests/crdt.rs +++ b/side-node/tests/crdt.rs @@ -1,13 +1,9 @@ use bft_crdt_derive::add_crdt_fields; -use bft_json_crdt::json_crdt::{BaseCrdt, CrdtNode, IntoCrdtNode}; +use bft_json_crdt::json_crdt::{BaseCrdt, CrdtNode, IntoCrdtNode, SignedOp}; use bft_json_crdt::op::ROOT_ID; use bft_json_crdt::{keypair::make_keypair, list_crdt::ListCrdt}; - -#[add_crdt_fields] -#[derive(Clone, CrdtNode)] -struct ListExample { - list: ListCrdt, -} +use serde_json::Value; +use side_node::crdt::{Transaction, TransactionList}; // case 1 - send valid updates #[test] @@ -15,15 +11,35 @@ fn test_valid_updates() { // Insert to crdt.doc on local node, test applying the same operation to a remote node // and check that the view is the same let keypair1 = make_keypair(); - let mut crdt1 = BaseCrdt::::new(&keypair1); - let _a = crdt1.doc.list.insert(ROOT_ID, 'a').sign(&keypair1); - let _b = crdt1.doc.list.insert(_a.id(), 'b').sign(&keypair1); - let _c = crdt1.doc.list.insert(_b.id(), 'c').sign(&keypair1); + let mut crdt1 = BaseCrdt::::new(&keypair1); - assert_eq!(crdt1.doc.list.view(), vec!['a', 'b', 'c']); + let val_a = side_node::utils::fake_transaction_json(String::from("a")); + let val_b = side_node::utils::fake_transaction_json(String::from("b")); + let val_c = side_node::utils::fake_transaction_json(String::from("c")); + + let _a = crdt1 + .doc + .list + .insert(ROOT_ID, val_a.clone()) + .sign(&keypair1); + let _b = crdt1 + .doc + .list + .insert(_a.id(), val_b.clone()) + .sign(&keypair1); + let _c = crdt1 + .doc + .list + .insert(_b.id(), val_c.clone()) + .sign(&keypair1); + + assert_eq!( + crdt1.doc.list.view(), + vec![to_tx(_a.clone()), to_tx(_b.clone()), to_tx(_c.clone())] + ); let keypair2 = make_keypair(); - let mut crdt2 = BaseCrdt::::new(&keypair2); + let mut crdt2 = BaseCrdt::::new(&keypair2); crdt2.apply(_a.clone()); crdt2.apply(_b); crdt2.apply(_c.clone()); @@ -43,3 +59,20 @@ fn test_valid_updates() { "views are still equal after repeated applies" ); } + +fn to_tx(op: SignedOp) -> Transaction { + let val = op.inner.content; + serde_json::from_value(val.into()).unwrap() +} + +// pub fn iter(&self) -> impl Iterator { +// self.ops +// .iter() +// .filter(|op| !op.is_deleted && op.content.is_some()) +// .map(|op| op.content.as_ref().unwrap()) +// } + +// /// Convenience function to get a vector of visible list elements +// pub fn view(&self) -> Vec { +// self.iter().map(|i| i.to_owned()).collect() +// }