open_lifecycle.rs (3681B)
1 use std::time::Duration; 2 3 use radroots_storage::{EventStore, event::SourceGeneration, status::EventStoreMode}; 4 use radroots_storage_sqlite::{Error, OpenMode, OpenOptions, Paths, SqliteStorage}; 5 6 fn generation(byte: u8) -> SourceGeneration { 7 SourceGeneration::new([byte; 32]).expect("source generation") 8 } 9 10 #[tokio::test] 11 async fn public_open_creates_both_databases_and_reuses_the_durable_generation() { 12 let directory = tempfile::tempdir().expect("temporary directory"); 13 let paths = Paths::from_directory(directory.path()).expect("owned paths"); 14 let expected = generation(31); 15 let store = SqliteStorage::open( 16 OpenOptions::new(paths.clone(), OpenMode::Create) 17 .with_busy_timeout(Duration::from_millis(250)) 18 .expect("busy timeout") 19 .with_source_generation(expected, 1_000) 20 .expect("source generation"), 21 ) 22 .await 23 .expect("create storage"); 24 assert!(paths.runtime().is_file()); 25 assert!(paths.private().is_file()); 26 let status = EventStore::status(&store).await.expect("event status"); 27 assert_eq!(status.generation(), expected); 28 assert_eq!(status.mode(), EventStoreMode::ReadWrite); 29 30 let reader = SqliteStorage::open(OpenOptions::new(paths.clone(), OpenMode::ReadOnly)) 31 .await 32 .expect("concurrent reader"); 33 let reader_status = EventStore::status(&reader).await.expect("reader status"); 34 assert_eq!(reader_status.generation(), expected); 35 assert_eq!(reader_status.mode(), EventStoreMode::ReadOnly); 36 drop(reader); 37 38 assert!(matches!( 39 SqliteStorage::open(OpenOptions::new(paths.clone(), OpenMode::ReadWriteExisting,)).await, 40 Err(Error::WriterAlreadyActive { .. }) 41 )); 42 let cloned = store.clone(); 43 drop(store); 44 assert!(matches!( 45 SqliteStorage::open(OpenOptions::new(paths.clone(), OpenMode::ReadWriteExisting,)).await, 46 Err(Error::WriterAlreadyActive { .. }) 47 )); 48 drop(cloned); 49 50 let reopened = SqliteStorage::open(OpenOptions::new(paths, OpenMode::ReadWriteExisting)) 51 .await 52 .expect("reopen after guard release"); 53 assert_eq!( 54 EventStore::status(&reopened) 55 .await 56 .expect("reopened status") 57 .generation(), 58 expected 59 ); 60 } 61 62 #[tokio::test] 63 async fn fresh_store_requires_explicit_generation_and_exact_expectations() { 64 let directory = tempfile::tempdir().expect("temporary directory"); 65 let paths = Paths::from_directory(directory.path()).expect("owned paths"); 66 assert!(matches!( 67 SqliteStorage::open(OpenOptions::new(paths.clone(), OpenMode::Create)).await, 68 Err(Error::SourceGenerationRequired) 69 )); 70 assert!(!paths.runtime().exists()); 71 assert!(!paths.private().exists()); 72 73 let expected = generation(41); 74 let store = SqliteStorage::open( 75 OpenOptions::new(paths.clone(), OpenMode::Create) 76 .with_source_generation(expected, 2_000) 77 .expect("source generation"), 78 ) 79 .await 80 .expect("complete fresh store"); 81 drop(store); 82 83 let exact_reopen = SqliteStorage::open( 84 OpenOptions::new(paths.clone(), OpenMode::ReadWriteExisting) 85 .with_source_generation(expected, 2_000) 86 .expect("exact expectation"), 87 ) 88 .await 89 .expect("reopen with exact generation expectation"); 90 drop(exact_reopen); 91 92 assert!(matches!( 93 SqliteStorage::open( 94 OpenOptions::new(paths, OpenMode::ReadWriteExisting) 95 .with_source_generation(generation(42), 2_000) 96 .expect("wrong expectation"), 97 ) 98 .await, 99 Err(Error::SourceGenerationMismatch) 100 )); 101 }