use std::cell::{Cell, RefCell};
use std::error::Error;
use std::sync::Arc;
use liminal::durability::{CountingStore, DurabilityError, DurableStore};
use super::LogAppender;
use crate::server::participant::production::log::{
OperationLog, OperationLogError, StoredOperation,
};
use crate::server::participant::production::operation_batch::OperationBatch;
use crate::server::participant::production::outbox_log::OutboxLog;
use crate::server::participant::production::registry::ConversationRegistry;
use crate::server::participant::production::restore_checkpoint_locator::checkpoint_conversation_route;
use crate::server::participant::production::routed_stream::RoutedStreamRefusal;
use crate::server::participant::production::state::DurableAppend;
use crate::server::participant::production::tests_memory_store::MemoryStore;
#[test]
fn appender_commits_once_and_cannot_append_or_commit_after_consumption()
-> Result<(), Box<dyn Error>> {
let store = Arc::new(CountingStore::new(MemoryStore::default()));
let shared = Arc::clone(&store) as Arc<dyn DurableStore>;
let registry = ConversationRegistry::new(Arc::clone(&shared));
let log = OperationLog::new(Arc::clone(&shared), 55);
let outbox_log = OutboxLog::new(shared, 55);
let batch = RefCell::new(Some(OperationBatch::new(&checkpoint_conversation_route(
55,
))));
let appender = LogAppender {
log: &log,
registry: ®istry,
conversation_id: 55,
outbox_log: &outbox_log,
pruning: None,
outstanding_extension_rows: Cell::new(0),
batch: Some(&batch),
timestamp: 10,
};
appender.append(&StoredOperation::Genesis { event: vec![1] }, 0)?;
let before = store.snapshot();
assert_eq!(before.apply_routed_batch_calls, 1);
assert_eq!(before.flushes, 0);
assert!(appender.has_staged()?);
appender.commit_staged()?;
assert_eq!(store.snapshot().since(&before).apply_routed_batch_calls, 1);
assert!(matches!(
appender.commit_staged(),
Err(OperationLogError::RoutedStream(
RoutedStreamRefusal::BatchRefused
))
));
assert!(matches!(
appender.append(&StoredOperation::Genesis { event: vec![2] }, 1),
Err(OperationLogError::RoutedStream(
RoutedStreamRefusal::BatchRefused
))
));
assert_eq!(store.snapshot().since(&before).apply_routed_batch_calls, 1);
Ok(())
}
#[test]
fn an_empty_appender_commit_preserves_the_named_refusal_without_issuance()
-> Result<(), Box<dyn Error>> {
let store = Arc::new(CountingStore::new(MemoryStore::default()));
let shared = Arc::clone(&store) as Arc<dyn DurableStore>;
let registry = ConversationRegistry::new(Arc::clone(&shared));
let log = OperationLog::new(Arc::clone(&shared), 56);
let outbox_log = OutboxLog::new(shared, 56);
let batch = RefCell::new(Some(OperationBatch::new(&checkpoint_conversation_route(
56,
))));
let appender = LogAppender {
log: &log,
registry: ®istry,
conversation_id: 56,
outbox_log: &outbox_log,
pruning: None,
outstanding_extension_rows: Cell::new(0),
batch: Some(&batch),
timestamp: 10,
};
assert!(!appender.has_staged()?);
assert!(matches!(
appender.commit_staged(),
Err(OperationLogError::Durability(
DurabilityError::EmptyOperationBatch
))
));
assert!(!log.issuance().was_submitted());
assert_eq!(store.snapshot().apply_routed_batch_calls, 0);
assert_eq!(store.snapshot().append_calls, 0);
Ok(())
}