use std::sync::Arc;
use std::time::Instant;
use synaptic_core::{ChatModel, ChatRequest, ChatResponse, Message, SynapticError};
use synaptic_models::TokenBucketChatModel;
struct InstantModel;
#[async_trait::async_trait]
impl ChatModel for InstantModel {
async fn chat(&self, _request: ChatRequest) -> Result<ChatResponse, SynapticError> {
Ok(ChatResponse {
message: Message::ai("ok"),
usage: None,
})
}
}
#[tokio::test]
async fn token_bucket_allows_immediate_when_available() {
let inner = Arc::new(InstantModel);
let model = TokenBucketChatModel::new(inner, 5.0, 10.0);
let start = Instant::now();
for _ in 0..3 {
let result = model
.chat(ChatRequest::new(vec![Message::human("hi")]))
.await;
assert!(result.is_ok());
}
assert!(start.elapsed().as_millis() < 100);
}
#[tokio::test]
async fn token_bucket_limits_rate() {
let inner = Arc::new(InstantModel);
let model = Arc::new(TokenBucketChatModel::new(inner, 1.0, 10.0));
let start = Instant::now();
model
.chat(ChatRequest::new(vec![Message::human("a")]))
.await
.unwrap();
model
.chat(ChatRequest::new(vec![Message::human("b")]))
.await
.unwrap();
assert!(start.elapsed().as_millis() >= 50);
}
#[tokio::test]
async fn token_bucket_refills() {
let inner = Arc::new(InstantModel);
let model = TokenBucketChatModel::new(inner, 1.0, 20.0);
model
.chat(ChatRequest::new(vec![Message::human("first")]))
.await
.unwrap();
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
let start = Instant::now();
model
.chat(ChatRequest::new(vec![Message::human("second")]))
.await
.unwrap();
assert!(start.elapsed().as_millis() < 100);
}