lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

lifecycle.rs (5008B)


      1 #[cfg(feature = "memory")]
      2 use radroots_sdk::{ClientBuilder, capability::CapabilityId, error::ErrorKind};
      3 
      4 const CLIENT_SOURCE: &str = include_str!("../src/client.rs");
      5 const MANIFEST: &str = include_str!("../Cargo.toml");
      6 
      7 #[test]
      8 fn clean_default_path_contains_no_implicit_resource_or_worker_authority() {
      9     let production = CLIENT_SOURCE
     10         .split_once("#[cfg(all(test, feature = \"memory\"))]")
     11         .expect("production client boundary")
     12         .0;
     13     for forbidden in [
     14         "keyring",
     15         "reqwest",
     16         "radrootsd",
     17         "std::fs",
     18         "tokio::fs",
     19         "tokio::spawn",
     20         "std::thread::spawn",
     21         "Runtime::new",
     22         "File::create",
     23         "impl Drop for Client",
     24     ] {
     25         assert!(
     26             !production.contains(forbidden),
     27             "clean client path contains implicit authority `{forbidden}`"
     28         );
     29     }
     30     assert!(MANIFEST.contains("default = [\"memory\"]"));
     31     assert!(!MANIFEST.contains("default = [\"native\"]"));
     32     assert!(!MANIFEST.contains("default = [\"full\"]"));
     33 }
     34 
     35 #[cfg(feature = "memory")]
     36 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
     37 async fn memory_client_is_passive_clone_shared_and_explicitly_closed() {
     38     let generation =
     39         radroots_storage::event::SourceGeneration::new([41; 32]).expect("non-zero generation");
     40     let client = ClientBuilder::memory(generation).build().expect("client");
     41 
     42     let first_report = client.capabilities();
     43     let second_report = client.capabilities();
     44     assert_eq!(first_report, second_report);
     45     let storage = first_report
     46         .get(CapabilityId::CANONICAL_STORAGE)
     47         .expect("canonical storage status");
     48     assert!(storage.is_compiled());
     49     assert!(storage.is_configured());
     50 
     51     let unpolled_close = client.close();
     52     drop(unpolled_close);
     53     assert!(client.storage().is_ok());
     54 
     55     let first = client.clone();
     56     let second = client.clone();
     57     let first_close = tokio::spawn(async move { first.close().await });
     58     let second_close = tokio::spawn(async move { second.close().await });
     59     let outcomes = [
     60         first_close.await.expect("first close task"),
     61         second_close.await.expect("second close task"),
     62     ];
     63     assert!(outcomes.iter().all(|outcome| {
     64         outcome.is_ok()
     65             || outcome
     66                 .as_ref()
     67                 .is_err_and(|error| error.kind() == ErrorKind::CloseInProgress)
     68     }));
     69     if !client.is_closed() {
     70         client.close().await.expect("complete close");
     71     }
     72 
     73     assert!(client.is_closed());
     74     assert_eq!(
     75         client
     76             .storage()
     77             .err()
     78             .expect("closed client rejects storage")
     79             .kind(),
     80         ErrorKind::ClientClosed
     81     );
     82     assert_eq!(
     83         client
     84             .capabilities()
     85             .get(CapabilityId::CANONICAL_STORAGE)
     86             .expect("closed storage status")
     87             .availability(),
     88         radroots_sdk::capability::Availability::Unavailable
     89     );
     90 }
     91 
     92 #[cfg(all(feature = "memory", feature = "sqlite"))]
     93 #[tokio::test]
     94 async fn sqlite_resources_exist_only_after_explicit_open_and_close_across_clones() {
     95     use radroots_sdk::storage::{SqliteOpenMode, SqliteOptions, SqlitePaths};
     96     use radroots_storage::status::{ShutdownState, StorageBackend};
     97 
     98     let directory = tempfile::tempdir().expect("temporary directory");
     99     let empty_builder = ClientBuilder::new();
    100     assert!(
    101         directory
    102             .path()
    103             .read_dir()
    104             .expect("read tempdir")
    105             .next()
    106             .is_none()
    107     );
    108     drop(empty_builder);
    109 
    110     let paths = SqlitePaths::from_directory(directory.path()).expect("SQLite paths");
    111     let generation =
    112         radroots_storage::event::SourceGeneration::new([42; 32]).expect("non-zero generation");
    113     let options = SqliteOptions::new(paths, SqliteOpenMode::Create)
    114         .with_source_generation(generation, 1)
    115         .expect("source generation");
    116     assert!(
    117         directory
    118             .path()
    119             .read_dir()
    120             .expect("read tempdir")
    121             .next()
    122             .is_none()
    123     );
    124 
    125     let client = ClientBuilder::sqlite(options)
    126         .await
    127         .expect("explicit SQLite open")
    128         .build()
    129         .expect("client");
    130     assert!(
    131         directory
    132             .path()
    133             .read_dir()
    134             .expect("read tempdir")
    135             .next()
    136             .is_some()
    137     );
    138     let status = client.storage_status().await.expect("storage status");
    139     assert_eq!(status.backend(), StorageBackend::Sqlite);
    140     assert_eq!(status.shutdown(), ShutdownState::Open);
    141 
    142     let clone = client.clone();
    143     let unpolled_close = clone.close();
    144     drop(unpolled_close);
    145     assert!(client.storage().is_ok());
    146     client.close().await.expect("explicit close");
    147     assert!(clone.is_closed());
    148     assert_eq!(
    149         clone
    150             .storage_status()
    151             .await
    152             .expect_err("closed clone rejects inspection")
    153             .kind(),
    154         ErrorKind::ClientClosed
    155     );
    156 }