From aa5f160bb6223742ab3c67684ce608a69bd48405 Mon Sep 17 00:00:00 2001 From: adz Date: Fri, 20 Mar 2026 12:33:28 +0100 Subject: [PATCH 1/3] ci: Only run build action when manually dispatched The "build" GitHub action is currently failing and requires a lot of time / resources. Until this is fixed this patch sets it to manual dispatch. It can be triggered now manually from the GitHub UI. --- .github/workflows/build.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 691ac1ea..65cf9f80 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,9 +1,6 @@ name: Build -on: - push: - branches: [main] - pull_request: +on: workflow_dispatch jobs: flatpak: From e748f9f28f825c5623e5a6c3ba119f3ee369dc1f Mon Sep 17 00:00:00 2001 From: adz Date: Fri, 20 Mar 2026 12:33:44 +0100 Subject: [PATCH 2/3] ci: Cargo tasks to check fmt, tests and clippy --- .github/workflows/rust.yml | 104 +++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 .github/workflows/rust.yml diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 00000000..8f9b5092 --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,104 @@ +name: Rust + +on: push + +env: + CARGO_TERM_COLOR: always + RUST_TOOLCHAIN: 1.94.0 + EXCLUDE_PACKAGES: reflection + PKG_CONFIG_PATH: /usr/lib/x86_64-linux-gnu/pkgconfig + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install glib + run: | + sudo apt-get update + sudo apt-get install -y libglib2.0-dev pkg-config + + - name: Setup Rust toolchain + uses: moonrepo/setup-rust@v1 + with: + channel: ${{ env.RUST_TOOLCHAIN }} + + - name: Run tests + run: | + cargo test \ + --workspace \ + --exclude ${{ env.EXCLUDE_PACKAGES }} \ + --all-features + + check: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install glib + run: | + sudo apt-get update + sudo apt-get install -y libglib2.0-dev pkg-config + + - name: Setup Rust toolchain + uses: moonrepo/setup-rust@v1 + with: + channel: ${{ env.RUST_TOOLCHAIN }} + + - name: Check project and dependencies + run: | + cargo check \ + --workspace \ + --exclude ${{ env.EXCLUDE_PACKAGES }} \ + --all-features + + fmt: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Rust toolchain + uses: moonrepo/setup-rust@v1 + with: + components: rustfmt + channel: ${{ env.RUST_TOOLCHAIN }} + + - name: Check formatting + # The `config` module gets auto-generated by the build system and is + # otherwise missing, we create an empty file so at least fmt will not + # wonder where that module went. + run: | + echo "" > ${{ github.workspace }}/reflection-app/src/config.rs + cargo fmt -- --check + + clippy: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install glib + run: | + sudo apt-get update + sudo apt-get install -y libglib2.0-dev pkg-config + + - name: Setup Rust toolchain + uses: moonrepo/setup-rust@v1 + with: + components: clippy + channel: ${{ env.RUST_TOOLCHAIN }} + + - name: Check code with clippy + run: | + cargo clippy \ + --workspace \ + --exclude ${{ env.EXCLUDE_PACKAGES }} \ + --all-features -- -D warnings --no-deps From 074a7f156cf204d7a880f63c7425d34d1e5a2ed0 Mon Sep 17 00:00:00 2001 From: adz Date: Fri, 20 Mar 2026 12:33:54 +0100 Subject: [PATCH 3/3] tests: Fix API changes breaking tests The tests have actually not worked due to API changes which were not updated yet in the tests modules. This was not noticed since we're not running tests in CI. This patch fixes the tests by: 1. Updating the tests to use the latest method signatures 2. Await results due to async behaviour of system --- Cargo.lock | 1 - reflection-doc/Cargo.toml | 16 ++++++------ reflection-doc/src/lib.rs | 46 ++++++++++++++++++++++++++-------- reflection-node/Cargo.toml | 3 +++ reflection-node/src/lib.rs | 21 ++++++++++------ reflection-node/src/network.rs | 17 ++++++++----- 6 files changed, 71 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index efdad07f..cdc663f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4462,7 +4462,6 @@ name = "reflection-doc" version = "0.2.0" dependencies = [ "anyhow", - "async-channel", "gio", "glib", "hex", diff --git a/reflection-doc/Cargo.toml b/reflection-doc/Cargo.toml index 5b06cd14..48cd592f 100644 --- a/reflection-doc/Cargo.toml +++ b/reflection-doc/Cargo.toml @@ -8,16 +8,18 @@ authors = [ ] [dependencies] -reflection-node = { path = "../reflection-node" } anyhow = "1.0.101" -async-channel = "2.5.0" -glib = "0.21" gio = "0.21" +glib = "0.21" +hex = "0.4.3" +indexmap = "2.13.0" loro = { tag = "loro-crdt@1.10.6", git = "https://github.com/loro-dev/loro.git" } +rand = "0.10.0" +reflection-node = { path = "../reflection-node" } +serde = { version = "1.0.228", features = ["derive"] } thiserror = "2.0.18" tracing = "0.1" + +[dev-dependencies] +reflection-node = { path = "../reflection-node", features = ["test_utils"] } test-log = { version = "0.2.19", default-features = false, features = ["trace", "color"] } -serde = { version = "1.0.228", features = ["derive"] } -rand = "0.10.0" -hex = "0.4.3" -indexmap = "2.13.0" diff --git a/reflection-doc/src/lib.rs b/reflection-doc/src/lib.rs index cc653423..8fabd3ac 100644 --- a/reflection-doc/src/lib.rs +++ b/reflection-doc/src/lib.rs @@ -89,7 +89,6 @@ mod tests { let test_string = "Hello World"; let context = glib::MainContext::ref_thread_default(); - println!("Context: {context:?}"); let private_key = PrivateKey::new(); let service = Service::new(&private_key, None); @@ -106,10 +105,9 @@ mod tests { #[test_log::test(glib::async_test)] async fn basic_sync() { - let test_string = "Hello World"; + let expected_string = "Hello World"; let context = glib::MainContext::ref_thread_default(); - println!("Context: {context:?}"); let private_key = PrivateKey::new(); let service = Service::new(&private_key, None); @@ -128,13 +126,22 @@ mod tests { assert_eq!(document.id(), document2.id()); - assert!(document.insert_text(0, test_string).is_ok()); - assert_eq!(document.text(), test_string); + assert!(document.insert_text(0, expected_string).is_ok()); + assert_eq!(document.text(), expected_string); + + // Wait until text got synced. + loop { + glib::timeout_future(std::time::Duration::from_millis(50)).await; + + if document2.text() == expected_string { + break; + } + } service.shutdown().await; service2.shutdown().await; - assert_eq!(document2.text(), test_string); + assert_eq!(document2.text(), expected_string); } #[test_log::test(glib::async_test)] @@ -142,7 +149,6 @@ mod tests { let expected_string = "Hello, World!"; let context = glib::MainContext::ref_thread_default(); - println!("Context: {context:?}"); let private_key = PrivateKey::new(); let service = Service::new(&private_key, None); @@ -167,6 +173,15 @@ mod tests { assert!(document.insert_text(7, "W").is_ok()); assert_eq!(document.text(), expected_string); + // Wait until text got synced. + loop { + glib::timeout_future(std::time::Duration::from_millis(50)).await; + + if document2.text() == expected_string { + break; + } + } + service.shutdown().await; service2.shutdown().await; @@ -175,16 +190,16 @@ mod tests { #[test_log::test(glib::async_test)] async fn sync_longer_text() { - let test_string = "Et aut omnis eos corporis ut. Qui est blanditiis blanditiis. - Sit quia nam maxime accusantium ut voluptatem. Fuga consequuntur animi et et est. - Unde voluptas consequatur mollitia id odit optio harum sint. Fugit quo aut et laborum aut cupiditate."; + let test_string = "Et aut omnis eos corporis ut. Qui est blanditiis blanditiis. Sit quia + nam maxime accusantium ut voluptatem. Fuga consequuntur animi et et est. Unde voluptas + consequatur mollitia id odit optio harum sint. Fugit quo aut et laborum aut cupiditate."; + let expected_string = format!( "{}{}{}{}", test_string, test_string, test_string, test_string ); let context = glib::MainContext::ref_thread_default(); - println!("Context: {context:?}"); let private_key = PrivateKey::new(); let service = Service::new(&private_key, None); @@ -209,6 +224,15 @@ mod tests { assert!(document.insert_text(0, test_string).is_ok()); assert!(document.insert_text(0, test_string).is_ok()); + // Wait until text got synced. + loop { + glib::timeout_future(std::time::Duration::from_millis(50)).await; + + if document2.text() == expected_string { + break; + } + } + service.shutdown().await; service2.shutdown().await; diff --git a/reflection-node/Cargo.toml b/reflection-node/Cargo.toml index 80b936ee..ee817e73 100644 --- a/reflection-node/Cargo.toml +++ b/reflection-node/Cargo.toml @@ -8,6 +8,9 @@ authors = [ "Julian Sparber " ] +[features] +test_utils = [] + [dependencies] thiserror = "2.0.18" chrono = "0.4.43" diff --git a/reflection-node/src/lib.rs b/reflection-node/src/lib.rs index 5eb23d16..d1cddf8a 100644 --- a/reflection-node/src/lib.rs +++ b/reflection-node/src/lib.rs @@ -15,22 +15,23 @@ pub use topic::SubscribableTopic; #[cfg(test)] mod tests { - use crate::SubscribableTopic; - use crate::node::{ConnectionMode, Node}; + use std::sync::Arc; + use p2panda_core::Hash; use p2panda_core::PrivateKey; use p2panda_core::PublicKey; - use std::sync::Arc; use tokio::sync::{Mutex, mpsc}; + use crate::node::ConnectionMode; + use crate::node::Node; + use crate::topic::SubscribableTopic; + #[tokio::test] #[test_log::test] async fn create_topic() { let private_key = PrivateKey::new(); let network_id = Hash::new(b"reflection"); - let node = Node::new(private_key, network_id, None, ConnectionMode::Network) - .await - .unwrap(); + let node = Node::new(private_key, network_id, None).await.unwrap(); let id: [u8; 32] = [0; 32]; let _sub = node.subscribe(id, TestTopic::new()).await; @@ -70,6 +71,7 @@ mod tests { fn author_joined(&self, _author: PublicKey) {} fn author_left(&self, _author: PublicKey) {} fn ephemeral_bytes_received(&self, _author: PublicKey, _data: Vec) {} + fn error(&self, _error: crate::topic::SubscriptionError) {} } #[tokio::test] @@ -77,7 +79,8 @@ mod tests { async fn subscribe_topic() { let private_key = PrivateKey::new(); let network_id = Hash::new(b"reflection"); - let node = Node::new(private_key, network_id, None, ConnectionMode::Network) + let node = Node::new(private_key, network_id, None).await.unwrap(); + node.set_connection_mode(ConnectionMode::Network) .await .unwrap(); @@ -92,7 +95,9 @@ mod tests { let private_key2 = PrivateKey::new(); let network_id2 = Hash::new(b"reflection"); - let node2 = Node::new(private_key2, network_id2, None, ConnectionMode::Network) + let node2 = Node::new(private_key2, network_id2, None).await.unwrap(); + node2 + .set_connection_mode(ConnectionMode::Network) .await .unwrap(); diff --git a/reflection-node/src/network.rs b/reflection-node/src/network.rs index 69f62b09..c9aee3f2 100644 --- a/reflection-node/src/network.rs +++ b/reflection-node/src/network.rs @@ -82,16 +82,21 @@ impl Network { ) -> Result { let address_book = AddressBook::builder().spawn().await?; - if let Err(error) = address_book.insert_node_info(BOOTSTRAP_NODE.clone()).await { + if cfg!(not(any(test, feature = "test_utils"))) + && let Err(error) = address_book.insert_node_info(BOOTSTRAP_NODE.clone()).await + { error!("Failed to add bootstrap node to the address book: {error}"); } - let endpoint = Endpoint::builder(address_book.clone()) + let mut builder = Endpoint::builder(address_book.clone()) .network_id(network_id.into()) - .private_key(private_key.clone()) - .relay_url(RELAY_URL.clone()) - .spawn() - .await?; + .private_key(private_key.clone()); + + if cfg!(not(any(test, feature = "test_utils"))) { + builder = builder.relay_url(RELAY_URL.clone()); + } + + let endpoint = builder.spawn().await?; let mdns_discovery = MdnsDiscovery::builder(address_book.clone(), endpoint.clone()) .mode(MdnsDiscoveryMode::Active)