use async_trait::async_trait;
use turnframe_core::ids::{AccountId, ConversationId, TurnId};
use turnframe_core::replay::ReplayRecord;
use crate::error::StoreError;
#[async_trait]
pub trait ReplayReader: Send + Sync {
async fn get(&self, account: &AccountId, turn_id: &TurnId) -> Result<ReplayRecord, StoreError>;
async fn list_for_conversation(
&self,
account: &AccountId,
conversation: &ConversationId,
limit: usize,
) -> Result<Vec<ReplayRecord>, StoreError>;
}
#[async_trait]
pub trait ReplayWriter: Send + Sync {
async fn put(&self, record: ReplayRecord) -> Result<(), StoreError>;
}
pub trait ReplayStore: ReplayReader + ReplayWriter {}
impl<T: ReplayReader + ReplayWriter + ?Sized> ReplayStore for T {}
#[cfg(test)]
mod tests {
use chrono::{DateTime, TimeDelta, Utc};
use turnframe_core::ids::EventId;
use turnframe_core::replay::TurnPhase;
use super::*;
use crate::memory::MemoryStores;
fn record(
account: &AccountId,
conversation: ConversationId,
turn: TurnId,
at: DateTime<Utc>,
) -> ReplayRecord {
ReplayRecord::received(turn, conversation, account.clone(), at)
}
#[tokio::test]
async fn put_replaces_and_get_is_account_scoped() {
let store = MemoryStores::new();
let account = AccountId::from("a");
let conversation = ConversationId::new();
let turn = TurnId::new();
let epoch = DateTime::<Utc>::UNIX_EPOCH;
store
.put(record(&account, conversation, turn, epoch))
.await
.unwrap();
let mut advanced = record(&account, conversation, turn, epoch + TimeDelta::seconds(1));
advanced.phase = TurnPhase::Composed;
advanced.event_ids = vec![EventId::new()];
store.put(advanced.clone()).await.unwrap();
assert_eq!(
ReplayReader::get(&store, &account, &turn).await.unwrap(),
advanced
);
assert_eq!(
store
.list_for_conversation(&account, &conversation, 10)
.await
.unwrap(),
vec![advanced]
);
assert_eq!(
ReplayReader::get(&store, &AccountId::from("b"), &turn).await,
Err(StoreError::NotFound)
);
assert!(
store
.list_for_conversation(&AccountId::from("b"), &conversation, 10)
.await
.unwrap()
.is_empty()
);
}
#[tokio::test]
async fn list_returns_the_most_recent_records_chronologically() {
let store = MemoryStores::new();
let account = AccountId::from("a");
let conversation = ConversationId::new();
let epoch = DateTime::<Utc>::UNIX_EPOCH;
let mut turns = Vec::new();
for seconds in 0..4 {
let turn = TurnId::new();
turns.push(turn);
store
.put(record(
&account,
conversation,
turn,
epoch + TimeDelta::seconds(seconds),
))
.await
.unwrap();
}
let recent = store
.list_for_conversation(&account, &conversation, 2)
.await
.unwrap();
assert_eq!(
recent
.iter()
.map(|record| record.turn_id)
.collect::<Vec<_>>(),
vec![turns[2], turns[3]]
);
}
}