diff --git a/.github/workflows/demo.yml b/.github/workflows/demo.yml index f16560f..50f0b92 100644 --- a/.github/workflows/demo.yml +++ b/.github/workflows/demo.yml @@ -14,11 +14,15 @@ jobs: - name: Check out the SDK repo uses: actions/checkout@v4 - - name: Setup Go, Rust and Golem Base + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@main + + - name: Setup Go and Golem Base uses: ./.github/workflows/setup + # TODO: This could probably just be a derivation - name: Run Rust demo example - run: cargo run + run: nix develop --command cargo run working-directory: ./demo run-tests-and-docs: @@ -30,17 +34,20 @@ jobs: - name: Setup Go, Rust and Golem Base uses: ./.github/workflows/setup + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@main + - name: Run tests - run: cargo test + run: nix flake check --keep-going -L - name: Build docs - run: cargo doc --no-deps --workspace + run: nix build .#checks.x86_64-linux.cargo-doc - name: Deploy docs to branch folder uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./target/doc + publish_dir: ./result/share/doc publish_branch: gh-pages destination_dir: ${{ github.head_ref }} diff --git a/.github/workflows/setup/action.yml b/.github/workflows/setup/action.yml index be6ac77..d0d34ac 100644 --- a/.github/workflows/setup/action.yml +++ b/.github/workflows/setup/action.yml @@ -3,7 +3,6 @@ # - Starts a `gb-op-geth` container for Ethereum-compatible RPC. # - Checks out the `gb-op-geth` repo and sets up Go. # - Creates and funds a test account. -# - Reads the `./rust-toolchain` version and install it. # # To use, reference this action in your workflow with `uses:`. @@ -35,12 +34,3 @@ runs: go run ./cmd/golembase account fund working-directory: ./gb-op-geth shell: bash - - - name: Set `env.rust_stable` - run: echo "RUST_STABLE=$(cat ./rust-toolchain)" >> $GITHUB_ENV - shell: bash - - - name: Install Rust - uses: dtolnay/rust-toolchain@v1 - with: - toolchain: ${{ env.RUST_STABLE }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fa07d96..b1aba9f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,43 +1,34 @@ -# `pre-commit` configuration for Rust projects. +# `pre-commit` hook configuration for running checks from the nix flake. # -# Defines formatting, linting, and TOML validation hooks. +# This hook will run `nix flake check` before every commit to +# ensure your project passes all flake-defined checks. # -# To enable these checks before every commit, install `pre-commit` and run: +# You can view which checks will be run for your system with `nix flake show`. # -# pre-commit install +# Usage: +# 1. Make sure you are inside the dev shell: # -# This will set up the hooks as git `pre-commit` hooks. +# `nix develop` +# +# (Otherwise, `nix flake check` may fail due to missing dependencies.) +# +# 2. Install the pre-commit hook (only once per repo): +# +# `pre-commit install` +# +# 3. From now on, every `git commit` will automatically run `nix flake check`. +# +# To bypass these checks (not recommended), commit with: +# +# `git commit --no-verify` +# +# ------------------------------------------------------------ repos: - repo: local hooks: - - id: rustfmt - name: rustfmt - entry: rustfmt - args: ["--edition", "2024", "--check"] - language: system - types: [rust] - files: \.rs$ - - - id: cargo-clippy - name: cargo clippy - entry: cargo - args: ["clippy", "--all-targets", "--all-features", "--", "-D", "warnings"] + - id: nix-flake-check + name: nix flake check + entry: nix flake check language: system - types: [rust] pass_filenames: false - - # - id: cargo-audit - # name: cargo audit - # entry: cargo - # args: ["audit"] - # language: system - # files: ^(Cargo\.toml|Cargo\.lock)$ - # pass_filenames: false - - - id: taplo-validate - name: Taplo TOML validate - entry: taplo - args: ["check"] - language: system - files: \.toml$ diff --git a/.rust-toolchain.toml b/.rust-toolchain.toml new file mode 100644 index 0000000..e1da042 --- /dev/null +++ b/.rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "1.88.0" +components = ["rust-src", "rust-analyzer", "rustfmt", "clippy"] diff --git a/taplo.toml b/.taplo.toml similarity index 100% rename from taplo.toml rename to .taplo.toml diff --git a/demo/src/main.rs b/demo/src/main.rs index 0c6aca6..19f4c0f 100644 --- a/demo/src/main.rs +++ b/demo/src/main.rs @@ -25,15 +25,14 @@ async fn main() -> Result<(), Box> { private_key_path.push("golembase/private.key"); let private_key_bytes = fs::read(&private_key_path).map_err(|e| { format!( - "Failed to read private key at {}: {}", + "Failed to read private key at {}: {e}", private_key_path.display(), - e ) })?; let private_key = Hash::from_slice(&private_key_bytes); let signer = PrivateKeySigner::from_bytes(&private_key) - .map_err(|e| format!("Failed to parse private key: {}", e))?; + .map_err(|e| format!("Failed to parse private key: {e}"))?; let url = Url::parse("http://localhost:8545").unwrap(); let client = GolemBaseClient::builder() .wallet(signer) diff --git a/flake.lock b/flake.lock index 7f5fb0a..ea9076d 100644 --- a/flake.lock +++ b/flake.lock @@ -15,6 +15,27 @@ "type": "github" } }, + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": [] + }, + "locked": { + "lastModified": 1754980813, + "narHash": "sha256-Wr9ei2V4rfr3HR5eJUA7pjMIrHH5o4DtWazQC5UwxHA=", + "owner": "nix-community", + "repo": "fenix", + "rev": "a1ce805b08279ee4e697b47aa3aa28fe2b335de6", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, "flake-utils": { "inputs": { "systems": "systems" @@ -49,29 +70,9 @@ "root": { "inputs": { "crane": "crane", + "fenix": "fenix", "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs", - "rust-overlay": "rust-overlay" - } - }, - "rust-overlay": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1750646674, - "narHash": "sha256-gHg6QUjMi1ObrocQUAoEhhbIfop14UNae4QDSHoKsRU=", - "owner": "oxalica", - "repo": "rust-overlay", - "rev": "65162ae665154e0eddb395166bd4956358981dd0", - "type": "github" - }, - "original": { - "owner": "oxalica", - "repo": "rust-overlay", - "type": "github" + "nixpkgs": "nixpkgs" } }, "systems": { diff --git a/flake.nix b/flake.nix index 163d7cb..35c0d20 100644 --- a/flake.nix +++ b/flake.nix @@ -1,76 +1,112 @@ -# Nix flake for reproducible Rust development and builds. -# - Uses `crane` for Rust builds and incremental caching. -# - Uses `rust-overlay` for toolchain from `rust-toolchain` file. -# - Provides a `devShell` with all dev dependencies and `pre-commit`. -# - Exposes the built crate as the default package. - { - # Flake inputs: build helpers, overlays, and package set + description = "A Rust SDK for interacting with GolemBase."; + inputs = { - crane.url = "github:ipetkov/crane"; - flake-utils.url = "github:numtide/flake-utils"; nixpkgs.url = "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz"; - rust-overlay = { - url = "github:oxalica/rust-overlay"; + + crane.url = "github:ipetkov/crane"; + + fenix = { + url = "github:nix-community/fenix"; inputs.nixpkgs.follows = "nixpkgs"; + inputs.rust-analyzer-src.follows = ""; }; + + flake-utils.url = "github:numtide/flake-utils"; }; - # Flake outputs: build and dev environments for each system outputs = - { - nixpkgs, - crane, - rust-overlay, - flake-utils, - ... + { self + , nixpkgs + , crane + , fenix + , flake-utils + , ... }: - flake-utils.lib.eachDefaultSystem ( - system: - let - - # Import nixpkgs with Rust overlay - overlays = [ (import rust-overlay) ]; - pkgs = import nixpkgs { inherit system overlays; }; - - # Rust toolchain and build dependencies - rustToolchain = pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain; - nativeBuildInputs = with pkgs; [ - rustToolchain - pkg-config - ]; - buildInputs = with pkgs; [ openssl ]; + flake-utils.lib.eachDefaultSystem (system: + let + inherit (pkgs) lib; + pkgs = nixpkgs.legacyPackages.${system}; + + rustToolchain = fenix.packages.${system}.fromToolchainFile { + file = ./.rust-toolchain.toml; + sha256 = "sha256-Qxt8XAuaUR2OMdKbN4u8dBJOhSHxS+uS06Wl9+flVEk="; + }; - # Prepare source and build args using crane - craneLib = crane.mkLib pkgs; - src = craneLib.cleanCargoSource ./.; - commonArgs = { - inherit src nativeBuildInputs buildInputs; - strictDeps = true; - # The tests don't work in the nix sandbox - doCheck = false; + craneLib = (crane.mkLib pkgs).overrideToolchain rustToolchain; + src = + let + markdownFilter = path: _type: builtins.match ".*md$" path != null; + markdownOrCargo = path: type: + (markdownFilter path type) || (craneLib.filterCargoSources path type); + in + lib.cleanSourceWith { + src = ./.; + filter = markdownOrCargo; + name = "source"; }; - # Build Rust dependencies and crate - cargoArtifacts = craneLib.buildDepsOnly commonArgs; - rustSdk = craneLib.buildPackage (commonArgs // { inherit cargoArtifacts; }); + commonArgs = { + inherit src; + strictDeps = true; - in - { - # Expose built crate and devShell - packages = { - default = rustSdk; + # TODO: Check dependencies for rustls, we can potentially + # remove the dependency on pkg-config and openssl + nativeBuildInputs = with pkgs; [ pkg-config ]; + + buildInputs = with pkgs; [ openssl ]; + }; + + cargoArtifacts = craneLib.buildDepsOnly commonArgs; + + golem-base-sdk = craneLib.buildPackage (commonArgs // { + inherit cargoArtifacts; + doCheck = false; # The tests don't work in the nix sandbox + }); + in + { + # Additional cargo checks can be found at https://github.com/ipetkov/crane. + # + # These derivations are inherited by the `devShell`. + checks = { + inherit golem-base-sdk; + + cargo-clippy = craneLib.cargoClippy (commonArgs // { + inherit cargoArtifacts; + cargoClippyExtraArgs = "--all-targets -- --deny warnings"; + }); + + cargo-fmt = craneLib.cargoFmt { + inherit src; }; - devShells.default = - with pkgs; - mkShell { - inherit nativeBuildInputs; - buildInputs = buildInputs ++ [ pre-commit ]; - shellHook = '' - export PATH=${rustToolchain}/bin:$PATH - ''; - }; - } - ); + taplo-fmt = craneLib.taploFmt { + src = pkgs.lib.sources.sourceFilesBySuffices src [ ".toml" ]; + }; + + cargo-doc = craneLib.cargoDoc (commonArgs // { + inherit cargoArtifacts; + cargoDocExtraArgs = "--no-deps --workspace"; + # This can be commented out or tweaked as necessary, e.g. set to + # `--deny rustdoc::broken-intra-doc-links` to only enforce that lint + env.RUSTDOCFLAGS = "--deny warnings"; + }); + }; + + packages = { + inherit golem-base-sdk; + default = golem-base-sdk; + }; + + devShells.default = craneLib.devShell { + checks = self.checks.${system}; + packages = with pkgs; [ + pre-commit + nil + nixpkgs-fmt + ]; + }; + + formatter = pkgs.nixpkgs-fmt; + }); } diff --git a/rust-toolchain b/rust-toolchain deleted file mode 100644 index f634271..0000000 --- a/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -1.87.0 diff --git a/src/entity.rs b/src/entity.rs index 40697a1..047b623 100644 --- a/src/entity.rs +++ b/src/entity.rs @@ -182,7 +182,7 @@ impl TryFrom for TransactionResult { receipt.logs().iter().cloned().try_for_each(|log| { let log: alloy::primitives::Log = log.into(); let parsed = GolemBaseABI::GolemBaseABIEvents::decode_log(&log).map_err(|e| { - Self::Error::UnexpectedLogDataError(format!("Error decoding event log: {}", e)) + Self::Error::UnexpectedLogDataError(format!("Error decoding event log: {e}")) })?; match parsed.data { GolemBaseABI::GolemBaseABIEvents::GolemBaseStorageEntityCreated(data) => { diff --git a/src/eth.rs b/src/eth.rs index 49e8e1a..e15aa4d 100644 --- a/src/eth.rs +++ b/src/eth.rs @@ -166,7 +166,7 @@ impl GolemBaseClient { nm.base_nonce = on_chain_nonce; } Err(e) => { - log::warn!("Failed to fetch on-chain nonce: {}", e); + log::warn!("Failed to fetch on-chain nonce: {e}"); } } nm.next_nonce().await @@ -178,9 +178,9 @@ impl GolemBaseClient { &self, payload: GolemBaseTransaction, ) -> Result { - log::debug!("payload: {:?}", payload); + log::debug!("payload: {payload:?}"); let encoded = payload.encoded(); - log::debug!("buffer: {:?}", encoded); + log::debug!("buffer: {encoded:?}"); let nonce = self.next_nonce().await; @@ -196,14 +196,15 @@ impl GolemBaseClient { nonce: Some(nonce), ..Default::default() }; - log::debug!("transaction: {:?}", tx); + log::debug!("transaction: {tx:?}"); let gas_limit = if let Some(gas_limit) = payload.gas_limit { gas_limit } else { - self.provider.estimate_gas(tx.clone()).await.map_err(|e| { - Error::TransactionSendError(format!("Failed to estimate gas: {}", e)) - })? + self.provider + .estimate_gas(tx.clone()) + .await + .map_err(|e| Error::TransactionSendError(format!("Failed to estimate gas: {e}")))? }; tx = tx.with_gas_limit(gas_limit); @@ -220,12 +221,12 @@ impl GolemBaseClient { .send_transaction(tx.clone()) .await .map_err(|e| Error::TransactionSendError(e.to_string()))?; - log::debug!("pending transaction: {:?}", pending_tx); + log::debug!("pending transaction: {pending_tx:?}"); let receipt = pending_tx .get_receipt() .await .map_err(|e| Error::TransactionReceiptError(e.to_string()))?; - log::debug!("receipt: {:?}", receipt); + log::debug!("receipt: {receipt:?}"); { let mut nm = self.nonce_manager.lock().await; nm.complete().await; diff --git a/src/events.rs b/src/events.rs index 62d7e27..9edaa2f 100644 --- a/src/events.rs +++ b/src/events.rs @@ -126,14 +126,14 @@ impl EventsClient { /// Creates a new `EventsClient` by connecting to the given websocket `Url`. /// Establishes a connection to the blockchain node for event streaming. pub async fn new(url: Url) -> anyhow::Result { - log::debug!("Connecting to websocket provider: {}", url); + log::debug!("Connecting to websocket provider: {url}"); let provider = ProviderBuilder::new() .connect_ws(WsConnect::new(url.clone())) .await? .erased(); - log::info!("Connected to websocket provider: {}", url); + log::info!("Connected to websocket provider: {url}"); Ok(Self { provider }) } diff --git a/src/lib.rs b/src/lib.rs index 6652d18..26d2751 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,33 +1,11 @@ -// # GolemBase SDK -//! -//! This is part of the [Golem Base](https://github.com/Golem-Base) project, which is designed as a Layer2 Network deployed on Ethereum, acting as a gateway to various Layer 3 Database Chains (DB-Chains). -//! For an overview of Golem Base, **check out our [Litepaper](https://golem-base.io/wp-content/uploads/2025/03/GolemBase-Litepaper.pdf)**. -//! -//! This SDK allows you to use [GolemBase](https://github.com/Golem-Base) from Rust, it is available on [crates.io](https://crates.io/crates/golem-base-sdk), alng with its [generated documentation](https://docs.rs/golem-base-sdk). We provide an [example application](https://github.com/Golem-Base/rust-sdk/tree/main/demo) to showcase how you can use this SDK. -//! -//! For **getting up and running quickly**, we recommend the following two steps: -//! 1. Start golembase-op-geth through its [`docker-compose`](https://github.com/Golem-Base/golembase-op-geth/blob/main/RUN_LOCALLY.md) ; -//! 2. [Install the demo CLI](https://github.com/Golem-Base/golembase-demo-cli?tab=readme-ov-file#installation) and [create a user](https://github.com/Golem-Base/golembase-demo-cli?tab=readme-ov-file#quickstart), or build the [actual CLI](https://github.com/Golem-Base/golembase-op-geth/blob/main/cmd/golembase/README.md) as it's included in the `golembase-op-geth` repository. -//! -//! When you create a user, it will generate a private key file called `private.key` and store it in the standard folder as per the [XDG specification](https://specifications.freedesktop.org/basedir-spec/latest/): -//! - `~/.config/golembase/` on **Linux** -//! - `~/Library/Application Support/golembase/` on **macOS** -//! - `%LOCALAPPDATA%\golembase\` on **Windows** -//! -//! You will also need to fund the account, you can do it with: `golembase-demo-cli account fund 10` -//! -//! # Transaction Abstractions -//! -//! This SDK provides multiple layers for sending transactions: -//! - Use [`GolemBaseClient`] for high-level operations such as creating, updating, or deleting entities. -//! - Use [`Account`](crate::account::Account) for account-centric and lower-level transaction control. -//! - Advanced users can construct and submit raw Ethereum transactions directly using the types and helpers re-exported from `Alloy`. +#![doc = include_str!("../README.md")] /// Re-export commonly used types from `alloy`. -pub use alloy::primitives::{Address, keccak256}; -pub use alloy::signers::Signature; -pub use alloy::signers::local::PrivateKeySigner; -pub use alloy::transports::http::reqwest::Url; +pub use alloy::{ + primitives::{Address, keccak256}, + signers::{Signature, local::PrivateKeySigner}, + transports::http::reqwest::Url, +}; pub use client::{GolemBaseClient, GolemBaseRoClient}; pub use entity::{Annotation, Hash, NumericAnnotation, StringAnnotation}; diff --git a/src/rpc.rs b/src/rpc.rs index 9d4427f..fb0765a 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -85,15 +85,15 @@ impl GolemBaseRoClient { params: S, ) -> Result { let method = method.into(); - log::debug!("RPC Call - Method: {}, Params: {:?}", method, params); + log::debug!("RPC Call - Method: {method}, Params: {params:?}"); self.provider .client() .request(method.clone(), params) .await - .inspect(|res| log::debug!("RPC Response: {:?}", res)) + .inspect(|res| log::debug!("RPC Response: {res:?}")) .map_err(|e| match e { RpcError::ErrorResp(err) => { - anyhow!("Error response from RPC service: {}", err) + anyhow!("Error response from RPC service: {err}") } RpcError::SerError(err) => { anyhow!("Serialization error: {err}") diff --git a/test-utils/Cargo.toml b/test-utils/Cargo.toml index 07472a8..f750d4d 100644 --- a/test-utils/Cargo.toml +++ b/test-utils/Cargo.toml @@ -10,6 +10,6 @@ bigdecimal = "0.4" env_logger = "0.11.8" log = "0.4" -golem-base-sdk = { workspace = true } dirs = "6.0.0" +golem-base-sdk = { workspace = true } url = "2.5.4" diff --git a/tests/test_entity_operations.rs b/tests/test_entity_operations.rs index f65dbc9..cec55da 100644 --- a/tests/test_entity_operations.rs +++ b/tests/test_entity_operations.rs @@ -153,7 +153,7 @@ async fn test_concurrent_entity_creation_batch() -> Result<()> { async move { let mut creates = Vec::with_capacity(ENTITIES_PER_TASK); for i in 0..ENTITIES_PER_TASK { - let payload = format!("task1_entity_{}", i).into_bytes(); + let payload = format!("task1_entity_{i}").into_bytes(); let entry = Create::new(payload, 300) .annotate_string("task", "task1") .annotate_number("index", i as u64); @@ -169,7 +169,7 @@ async fn test_concurrent_entity_creation_batch() -> Result<()> { async move { let mut creates = Vec::with_capacity(ENTITIES_PER_TASK); for i in 0..ENTITIES_PER_TASK { - let payload = format!("task2_entity_{}", i).into_bytes(); + let payload = format!("task2_entity_{i}").into_bytes(); let entry = Create::new(payload, 300) .annotate_string("task", "task2") .annotate_number("index", i as u64); @@ -193,7 +193,7 @@ async fn test_concurrent_entity_creation_batch() -> Result<()> { .await?, ) .unwrap(); - assert_eq!(entry_str, format!("task1_entity_{}", i)); + assert_eq!(entry_str, format!("task1_entity_{i}")); let metadata = client.get_entity_metadata(result.entity_key).await?; assert_eq!(metadata.string_annotations[0].value, "task1"); @@ -207,7 +207,7 @@ async fn test_concurrent_entity_creation_batch() -> Result<()> { .await?, ) .unwrap(); - assert_eq!(entry_str, format!("task2_entity_{}", i)); + assert_eq!(entry_str, format!("task2_entity_{i}")); let metadata = client.get_entity_metadata(result.entity_key).await?; assert_eq!(metadata.string_annotations[0].value, "task2"); diff --git a/tests/test_query.rs b/tests/test_query.rs index a226913..07b703e 100644 --- a/tests/test_query.rs +++ b/tests/test_query.rs @@ -25,57 +25,42 @@ async fn test_query_entities() -> Result<()> { { // Test queries let type_test_entries = client.query_entity_keys("type = \"test\"").await?; - log::info!("Entries with type = \"test\": {:?}", type_test_entries); - //assert_eq!(type_test_entries.len(), 2); + log::info!("Entries with type = \"test\": {type_test_entries:?}"); assert!(type_test_entries.contains(&entity1.entity_key)); assert!(type_test_entries.contains(&entity2.entity_key)); let category_alpha_entries = client.query_entity_keys("category = \"alpha\"").await?; - log::info!( - "Entries with category = \"alpha\": {:?}", - category_alpha_entries - ); - //assert_eq!(category_alpha_entries.len(), 2); + log::info!("Entries with category = \"alpha\": {category_alpha_entries:?}"); assert!(category_alpha_entries.contains(&entity1.entity_key)); assert!(category_alpha_entries.contains(&entity3.entity_key)); let type_demo_entries = client.query_entity_keys("type = \"demo\"").await?; - log::info!("Entries with type = \"demo\": {:?}", type_demo_entries); - //assert_eq!(type_demo_entries.len(), 1); + log::info!("Entries with type = \"demo\": {type_demo_entries:?}"); assert!(type_demo_entries.contains(&entity3.entity_key)); let combined_and = client .query_entity_keys("type = \"test\" && category = \"beta\"") .await?; - log::info!( - "Entries with type = \"test\" && category = \"beta\": {:?}", - combined_and - ); - //assert_eq!(combined_and.len(), 1); + log::info!("Entries with type = \"test\" && category = \"beta\": {combined_and:?}"); assert!(combined_and.contains(&entity2.entity_key)); let combined_or = client .query_entity_keys("type = \"demo\" || category = \"beta\"") .await?; - log::info!( - "Entries with type = \"demo\" || category = \"beta\": {:?}", - combined_or - ); - //assert_eq!(combined_or.len(), 2); + log::info!("Entries with type = \"demo\" || category = \"beta\": {combined_or:?}"); assert!(combined_or.contains(&entity2.entity_key)); assert!(combined_or.contains(&entity3.entity_key)); // Test empty result let no_results = client.query_entity_keys("type = \"nonexistent\"").await?; - log::info!("Entries with type = \"nonexistent\": {:?}", no_results); + log::info!("Entries with type = \"nonexistent\": {no_results:?}"); assert_eq!(no_results.len(), 0); // Test selecting all entries let all_entries = client .query_entity_keys("type = \"test\" || type = \"demo\"") .await?; - log::info!("All entries: {:?}", all_entries); - //assert_eq!(all_entries.len(), 3); + log::info!("All entries: {all_entries:?}"); assert!(all_entries.contains(&entity1.entity_key)); assert!(all_entries.contains(&entity2.entity_key)); assert!(all_entries.contains(&entity3.entity_key));