use std::error::Error;
use std::sync::Arc;
use liminal::durability::bridge::block_on;
use liminal::durability::{CountingStore, DurableStore};
use super::OperationBatch;
use crate::server::participant::production::log::{OperationLog, StoredOperation};
use crate::server::participant::production::outbox_log::{OutboxLog, OutboxRow, decode_row};
use crate::server::participant::production::routed_stream::{read_head, read_page};
use crate::server::participant::production::tests_memory_store::MemoryStore;
#[test]
fn base_and_extension_stage_without_issuing_and_commit_together() -> Result<(), Box<dyn Error>> {
let store = Arc::new(CountingStore::new(MemoryStore::default()));
let shared = Arc::clone(&store) as Arc<dyn DurableStore>;
let log = OperationLog::new(Arc::clone(&shared), 41);
let outbox = OutboxLog::new(shared, 41);
let mut batch = OperationBatch::new("conversation-route");
let operation = StoredOperation::Genesis {
event: vec![1, 2, 3],
};
let extension = OutboxRow::AckAdvanced {
source_log_sequence: 0,
participant_id: 1,
through_seq: 9,
};
block_on(log.stage_into(&mut batch, &operation, 0, 17))??;
block_on(outbox.stage_into(&mut batch, &extension, 0, 17))??;
assert!(!log.issuance().was_submitted());
let before = store.snapshot();
assert_eq!(before.apply_routed_batch_calls, 0);
assert_eq!(before.append_calls, 0);
assert_eq!(before.append_all_calls, 0);
assert_eq!(before.flushes, 0);
block_on(batch.commit(store.as_ref(), || log.issuance().mark_submitted()))??;
assert!(log.issuance().was_submitted());
let counts = store.snapshot();
assert_eq!(counts.apply_routed_batch_calls, 1);
assert_eq!(counts.apply_routed_batch_items, 4);
assert_eq!(counts.append_calls, 0);
assert_eq!(counts.append_all_calls, 0);
assert_eq!(counts.flushes, 0);
let head = block_on(read_head(
store.as_ref(),
"conversation-route",
"liminal:participant-production-unit2:41",
))??
.ok_or("extension head missing")?;
let rows = block_on(read_page(
store.as_ref(),
"conversation-route",
"liminal:participant-production-unit2:41",
&head,
0,
1,
))??;
assert_eq!(rows.len(), 1);
assert_eq!(rows[0].timestamp, 17);
assert_eq!(decode_row(&rows[0].payload)?, extension);
Ok(())
}
#[test]
fn a_staging_refusal_does_not_mark_or_publish_the_base_row() -> Result<(), Box<dyn Error>> {
let store = Arc::new(CountingStore::new(MemoryStore::default()));
let shared = Arc::clone(&store) as Arc<dyn DurableStore>;
let log = OperationLog::new(Arc::clone(&shared), 42);
let outbox = OutboxLog::new(shared, 42);
let mut batch = OperationBatch::new("conversation-route");
block_on(log.stage_into(
&mut batch,
&StoredOperation::Genesis { event: vec![1] },
0,
20,
))??;
let extension = OutboxRow::AckAdvanced {
source_log_sequence: 0,
participant_id: 1,
through_seq: 9,
};
assert!(block_on(outbox.stage_into(&mut batch, &extension, 1, 20))?.is_err());
assert!(!log.issuance().was_submitted());
assert!(block_on(batch.commit(store.as_ref(), || log.issuance().mark_submitted()))?.is_err());
assert!(!log.issuance().was_submitted());
let counts = store.snapshot();
assert_eq!(counts.apply_routed_batch_calls, 0);
assert_eq!(counts.append_calls, 0);
assert_eq!(counts.flushes, 0);
assert!(
block_on(read_head(
store.as_ref(),
"conversation-route",
"liminal:participant-production:42"
))??
.is_none()
);
Ok(())
}