Skip to content

Commit 82451d7

Browse files
devin-ai-integration[bot]sanityiduartgomez
authored
tests: reproduce update propagation issues with peer blocking tests (#1592)
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Ian Clarke <github@ian.33mail.com> Co-authored-by: Ignacio Duart <iduartgomez@gmail.com>
1 parent 9b01894 commit 82451d7

File tree

17 files changed

+2828
-414
lines changed

17 files changed

+2828
-414
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/freenet-ping/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/freenet-ping/app/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ tokio = { version = "1.0", features = ["full"] }
1919
tokio-tungstenite = "0.26.1"
2020
tracing = { version = "0.1", features = ["log"] }
2121
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
22+
humantime = "2.2.0"
2223

2324
[dev-dependencies]
2425
freenet = { path = "../../../crates/core" }

apps/freenet-ping/app/src/ping_client.rs

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,11 @@ impl PingStats {
3333
self.sent_count += 1;
3434
}
3535

36-
pub fn record_received(&mut self, peer: String, time: DateTime<Utc>) {
36+
pub fn record_received(&mut self, peer: String, time: Vec<DateTime<Utc>>) {
3737
*self.received_counts.entry(peer.clone()).or_insert(0) += 1;
38-
self.last_updates.insert(peer, time);
38+
if let Some(latest) = time.first() {
39+
self.last_updates.insert(peer, *latest);
40+
}
3941
}
4042
}
4143

@@ -54,6 +56,21 @@ pub async fn wait_for_put_response(
5456
return Err("unexpected key".into());
5557
}
5658
}
59+
Ok(Ok(HostResponse::ContractResponse(ContractResponse::UpdateResponse {
60+
key,
61+
summary,
62+
}))) => {
63+
if &key == expected_key {
64+
tracing::info!(
65+
"Received update response for key: {}, summary: {:?}",
66+
key,
67+
summary
68+
);
69+
return Ok(key);
70+
} else {
71+
return Err("unexpected key".into());
72+
}
73+
}
5774
Ok(Ok(other)) => {
5875
tracing::warn!("Unexpected response while waiting for put: {}", other);
5976
}
@@ -85,9 +102,17 @@ pub async fn wait_for_get_response(
85102
return Err("unexpected key".into());
86103
}
87104

88-
let old_ping = serde_json::from_slice::<Ping>(&state)?;
89-
tracing::info!(num_entries = %old_ping.len(), "old state fetched successfully!");
90-
return Ok(old_ping);
105+
match serde_json::from_slice::<Ping>(&state) {
106+
Ok(ping) => {
107+
tracing::info!(num_entries = %ping.len(), "old state fetched successfully!");
108+
return Ok(ping);
109+
}
110+
Err(e) => {
111+
tracing::error!("Failed to deserialize Ping: {}", e);
112+
tracing::error!("Raw state data: {:?}", String::from_utf8_lossy(&state));
113+
return Err(Box::new(e));
114+
}
115+
};
91116
}
92117
Ok(Ok(other)) => {
93118
tracing::warn!("Unexpected response while waiting for get: {}", other);
@@ -218,9 +243,13 @@ pub async fn run_ping_client(
218243

219244
let updates = local_state.merge(new_ping, parameters.ttl);
220245

221-
for (name, update_time) in updates.into_iter() {
222-
tracing::info!("{} last updated at {}", name, update_time);
223-
stats.record_received(name, update_time);
246+
for (name, timestamps) in updates.into_iter() {
247+
if !timestamps.is_empty() {
248+
if let Some(last) = timestamps.first() {
249+
tracing::info!("{} last updated at {}", name, last);
250+
}
251+
stats.record_received(name, timestamps);
252+
}
224253
}
225254
Ok(())
226255
};

apps/freenet-ping/app/tests/README.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Freenet Ping App Test Suite
2+
3+
This directory contains integration tests for the Freenet Ping application, focusing on contract state propagation in various network topologies and connectivity scenarios.
4+
5+
## Test Organization
6+
7+
| Test File | Scenario |
8+
| ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
9+
| `run_app.rs` | **Multi-node and client functionality tests.** Contains tests for basic multi-node functionality and client application. |
10+
| `run_app_blocked_peers.rs` | **Parameterized blocked peers tests.** A unified implementation of blocked peers tests with different functional behaviors. |
11+
| `run_app_partially_connected_network.rs` | **Partial connectivity test.** Large-scale network test with controlled connectivity between nodes. |
12+
13+
## Parameterized "Blocked Peers" Tests
14+
15+
The `run_app_blocked_peers.rs` file contains a parameterized implementation that consolidates blocked peers testing into a single file. The file provides the following test variants:
16+
17+
| Test Function | Description |
18+
| ---------------------------------- | -------------------------------------------------------------------------------- |
19+
| `test_ping_blocked_peers` | **Baseline implementation.** Standard test for indirect propagation via gateway. |
20+
| `test_ping_blocked_peers_simple` | **Minimal variant.** One round of updates, simplified verification. |
21+
| `test_ping_blocked_peers_solution` | **Reference implementation.** Best practices for indirect propagation. |
22+
23+
## Test Structure
24+
25+
### In `run_app.rs`
26+
27+
| Test Function | Description |
28+
| ---------------------------- | -------------------------------------------------------------- |
29+
| `test_ping_multi_node` | Tests basic contract propagation in a fully-connected network. |
30+
| `test_ping_application_loop` | Tests the complete client application running over time. |
31+
32+
### In `run_app_partially_connected_network.rs`
33+
34+
| Test Function | Description |
35+
| --------------------------------------- | ---------------------------------------------------------------- |
36+
| `test_ping_partially_connected_network` | Tests propagation in a larger network with partial connectivity. |
37+
38+
### Multi-node vs. Partially Connected Tests
39+
40+
- **Multi-node test**: Uses a fully connected network where all nodes can directly communicate with each other
41+
- **Partially connected test**: Creates a larger network with controlled connectivity ratio (50%) between nodes, testing how updates propagate in a more realistic, constrained network topology
42+
43+
## The "Blocked Peers" Test Scenario
44+
45+
The "blocked peers" tests verify that contract state updates can propagate correctly even when direct peer-to-peer connections are blocked:
46+
47+
1. Two regular nodes (Node1 and Node2) are configured to block each other's network addresses
48+
2. A gateway node is connected to both regular nodes
49+
3. All nodes subscribe to the ping contract
50+
4. Each node sends state updates with its own unique identifier
51+
5. Updates must route through the gateway to reach nodes with blocked direct connections
52+
6. The test verifies that all nodes eventually have the same consistent state
53+
54+
This tests Freenet's ability to maintain contract state consistency even when the network topology prevents direct communication between some peers.
55+
56+
## Common Test Infrastructure
57+
58+
The `common/mod.rs` module provides shared infrastructure for tests, including:
59+
60+
- Node and gateway configuration
61+
- Contract deployment helpers
62+
- State comparison utilities
63+
- WebSocket connection management
64+
- State update and verification functions
65+
66+
## Parameterized Testing Approach
67+
68+
The unified approach in `run_app_blocked_peers.rs` offers several advantages:
69+
70+
- **Reduced duplication**: Core test logic is defined once and reused
71+
- **Consistent methodology**: All variants follow the same testing pattern
72+
- **Functional variants**: Tests focus on different functional behaviors (baseline, simple, solution)
73+
- **Easier maintenance**: Changes to the core test logic only need to be made in one place
74+
- **Focused test coverage**: Each variant tests a specific functional aspect of the system
75+
76+
## Running the Tests
77+
78+
Run all tests with:
79+
80+
```bash
81+
cd apps/freenet-ping
82+
cargo test
83+
```
84+
85+
Run a specific blocked peers test variant:
86+
87+
```bash
88+
cargo test test_ping_blocked_peers_simple
89+
```
90+
91+
Run the large-scale partial connectivity network test:
92+
93+
```bash
94+
cargo test -p freenet-ping-app --test run_app_partially_connected_network
95+
```
96+
97+
---

0 commit comments

Comments
 (0)