1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
//! A doc-only module explaining how to run core library tests.
//!
//! > **Note**: the library is compatible with both `tokio` and `async-std` runtimes, however all
//! > tests are written to use the `tokio` executor.
//! > Therefore, to run the tests you must specify the runtime feature flag e.g. `cargo test
//! > --features=tokio-runtime` unless it is already set as the default runtime in Cargo.toml.
//!
//! Tests are organised into the following modules:
//!
//! - `node_behaviour` tests for single node setup and behaviour.
//! - `layer_communication` tests involving the synchronization between two nodes.
//! - `replication` tests for integration tests involving replication configuration and behavior.
//! - `sharding` tests for integration tests involving sharding configuration and behavior.
//!
//!
//! # Node behaviour testing
//!
//! These are simple unit tests that check the behaviour of a single node. To run these tests,
//! simply run the following command:
//!
//! ```bash
//! cargo test node_ --features=tokio-runtime
//! ```
//!
//! # Layer communication testing
//!
//! In order to create tests for communication between two nodes, we used the Rust conditional
//! compilation feature to be able to setup different nodes and test their communication.
//! All commands for running these tests should be run with `-- --nocapture` to verify the expected
//! results.
//!
//! For these tests, we've created two test nodes: `node1` and `node2`.
//!
//! - Node 1 is setup by calling the `setup_node_1` function which uses a pre-configured
//! cryptographic keypair and the `setup_core_builder_1` function to configure a default node.
//! This keeps its identity consistent across tests.
//!
//! - Node 2 is setup by calling the `setup_node_2` function which creates a new node identity every
//! time it is called.
//! It then adds Node 1 as its bootnode and establishes a connection by dialing Node 1.
//!
//! ### Peer dialing tests
//!
//! The peer dialing tests checks if a node can dial another node by using a `listening` node and a
//! `dialing` node. To run these tests, start the listening node by running the following command in
//! one terminal:
//!
//! ```bash
//! cargo test dialing_peer_works --features=test-listening-node --features=tokio-runtime -- --nocapture
//! ```
//!
//! Then, in another terminal run the dialing node:
//!
//! ```bash
//! cargo test dialing_peer_works --features=test-dialing-node --features=tokio-runtime -- --nocapture
//! ```
//!
//! The application event handler will log the dialing node's peer id and the listening node's peer
//! id.
//! ## Fetching tests
//!
//! The fetching test checks if a node can fetch a value from another node.
//! These tests use a `server` node and a `client` node.
//!
//! To run these tests first start the server node in one terminal:
//!
//! ```bash
//! cargo test rpc_fetch_works --features=test-server-node --features=tokio-runtime -- --nocapture
//! ```
//!
//! And in another terminal, run the client node:
//!
//! ```bash
//! cargo test rpc_fetch_works --features=test-client-node --features=tokio-runtime -- --nocapture
//! ```
//!
//! Then you can check that the server node prints out a _"Recvd incoming RPC:"_ message with the
//! data sent by the client node.
//!
//! ## Kademlia tests
//!
//! For Kademlia tests, we have a `reading` node and a `writing` node.
//! We use a time delay to simulate the reading node "sleeping" so as to allow the writing node to
//! make changes to the DHT.
//!
//! When the reading node "wakes up" it then tries to read the value from the DHT. If the value is
//! what it expects, the tests passes successfully.
//!
//! To run this test, run the following command in one terminal to launch the "reading" node:
//!
//! ```bash
//! cargo test kademlia_record_store_itest_works --features=test-reading-node --features=tokio-runtime -- --nocapture
//! ```
//!
//! And then run the following command in another terminal to launch the "writing node":
//!
//! ```bash
//! cargo test kademlia_record_store_itest_works --features=test-writing-node --features=tokio-runtime -- --nocapture
//! ```
//!
//! ### Record providers tests
//!
//! To run the providers tests, we have a `reading` node and a `writing` node.
//!
//! We first run the "writing" node to store a record in the DHT. Then we run a "reading" node to
//! fetch the list of providers of the record that's been written.
//!
//! Then we simply assert that node 1 is a provider of the record.
//!
//! To run this test, first run the "writing" node:
//!
//! ```bash
//! cargo test kademlia_provider_records_itest_works --features=test-writing-node --features=tokio-runtime -- --nocapture
//! ```
//!
//! Then, in another terminal, run the "reading" node:
//!
//! ```bash
//! cargo test kademlia_provider_records_itest_works --features=test-reading-node --features=tokio-runtime -- --nocapture
//! ```
//!
//! ### Gossipsub tests
//!
//! **Join/Exit tests**
//!
//! For Gossipsub tests, we have a `subscribe` node and a `query` node.
//!
//! When the "subscribe" node is set up, it joins a mesh network. Then node 2 is setup and connects
//! to node 1, sleeps for a while (to allow propagtion of data from node 1) and then joins the
//! network. After joining, it then queries the network layer for gossipping information. This
//! information contains topics the node is currently subscribed to such as the peers that node 2
//! knows (which is node 1) and the network they are a part of. The peers that have been blacklisted
//! are also returned.
//!
//! In this test, we test that node 1 is a part of the mesh network that node 2 is subscribed to.
//!
//! To run this test, first run the "subscribe" node:
//!
//! ```bash
//! cargo test gossipsub_join_exit_itest_works --features=test-subscribe-node --features=tokio-runtime -- --nocapture
//! ```
//!
//! Then, in another terminal, run the "query" node:
//!
//! ```bash
//! cargo test gossipsub_join_exit_itest_works --features=test-query-node --features=tokio-runtime -- --nocapture
//! ```
//!
//! **Publish/Subscribe tests**
//!
//! For this test we have a `listening` node and a `broadcast` node. The first node is setup which
//! joins a mesh network. Then, node 2 is setup and connects to node 1, sleeps for a few seconds (to
//! allow propagtion of data from node 1) and then joins the network. It then joins the network that
//! node 1 was already a part of and sends a broadcast message to every peer in the mesh network.
//!
//! The indicator of the success of this test is revealed in the application's event handler
//! function which logs the message received from node 2.
//!
//! To run this test, first run the "listening" node in one terminal:
//!
//! ```bash
//! cargo test gossipsub_message_itest_works --features=test-listening-node --features=tokio-runtime -- --nocapture
//! ```
//!
//! Then run the "broadcast" node in another terminal:
//!
//! ```bash
//! cargo test gossipsub_message_itest_works --features=test-broadcast-node --features=tokio-runtime -- --nocapture
//! ```
//!
//! ## Replication tests
//!
//! For each Replication test, we setup nodes as separate async tasks that dial each other to form a replica network.
//!
//! The `setup_node` function builds each node with replication configured.
//!
//! For basic replication tests we test the network behaves as expected for:
//! - Joining and exiting the network
//! - Replicating and fetching data from the network
//! - Fully replicating a node from the replica network
//!
//! For Strong Consistency tests, we test the network behaves as expected for:
//! - Number of confirmations are correct in a network with only 2 nodes
//! - Number of confirmations are correct in a network with 3 nodes
//! - Number of confirmations are correct in a network with `MinPeers` set to 2 nodes
//!
//! For Eventual Consistency tests, we test the network behaves as expected for:
//! - A node is updated upon newly joining a replica network
//! - Lamport ordering
//! - Replicating a value across the network
//!
//! ## Sharding tests
//!
//! To setup the testing environment for Sharding, we implement `ShardStorage` to define the behavior of `fetch_data()`
//! which fetches data separated by `-->`. We also implement the `Sharding` trait for range-based sharding to test the behavior of the network.
//!
//! In each test, we setup nodes as separate async tasks that forms the sharded network.
//! The `setup_node` function builds each node with replication and sharding configured.
//!
//! For Sharding, we test the network behaves as expected for:
//!
//! - Joining and exiting a sharded network
//! - Data forwarding between shards
//! - Replication between nodes in a shard
//! - Sharding and fetching from local storage
//! - Fetching sharded data from the network
//!