mod common; use serde_json::json; use wiremock::matchers::{body_json, method, path}; use wiremock::{Mock, MockServer, ResponseTemplate}; fn doc(id: &str) -> serde_json::Value { json!({ "id": id, "title": id }) } #[tokio::test] async fn paginator_advances_offset_and_stops_on_short_page() { let server = MockServer::start().await; let client = common::client_for(&server).await; Mock::given(method("POST")) .and(path("/api/documents.list")) .and(body_json(json!({ "limit": 2, "offset": 0 }))) .respond_with(ResponseTemplate::new(200).set_body_json(json!({ "ok": true, "data": [doc("doc-1"), doc("doc-2")], "pagination": { "limit": 2, "offset": 0 } }))) .mount(&server) .await; Mock::given(method("POST")) .and(path("/api/documents.list")) .and(body_json(json!({ "limit": 2, "offset": 2 }))) .respond_with(ResponseTemplate::new(200).set_body_json(json!({ "ok": true, "data": [doc("doc-3")], "pagination": { "limit": 2, "offset": 2 } }))) .mount(&server) .await; let items = client .documents() .list() .limit(2) .paginate() .collect_all() .await .unwrap(); let ids: Vec<_> = items .into_iter() .map(|d| d.id.as_str().to_string()) .collect(); assert_eq!(ids, vec!["doc-1", "doc-2", "doc-3"]); } #[tokio::test] async fn paginator_stops_immediately_on_empty_first_page() { let server = MockServer::start().await; let client = common::client_for(&server).await; Mock::given(method("POST")) .and(path("/api/documents.list")) .and(body_json(json!({ "limit": 25, "offset": 0 }))) .respond_with(ResponseTemplate::new(200).set_body_json(json!({ "ok": true, "data": [], "pagination": { "limit": 25, "offset": 0 } }))) .mount(&server) .await; let mut paginator = client.documents().list().paginate(); let page = paginator .next_page() .await .unwrap() .expect("first page always returned"); assert!(page.items.is_empty()); assert!(paginator.next_page().await.unwrap().is_none()); } #[cfg(feature = "stream")] #[tokio::test] async fn into_stream_yields_items_across_pages() { use futures_util::StreamExt; let server = MockServer::start().await; let client = common::client_for(&server).await; Mock::given(method("POST")) .and(path("/api/documents.list")) .and(body_json(json!({ "limit": 2, "offset": 0 }))) .respond_with(ResponseTemplate::new(200).set_body_json(json!({ "ok": true, "data": [doc("doc-1"), doc("doc-2")], "pagination": { "limit": 2, "offset": 0 } }))) .mount(&server) .await; Mock::given(method("POST")) .and(path("/api/documents.list")) .and(body_json(json!({ "limit": 2, "offset": 2 }))) .respond_with(ResponseTemplate::new(200).set_body_json(json!({ "ok": true, "data": [doc("doc-3")], "pagination": { "limit": 2, "offset": 2 } }))) .mount(&server) .await; let stream = client.documents().list().limit(2).paginate().into_stream(); let items: Vec<_> = stream.collect().await; assert_eq!(items.len(), 3); assert!(items.iter().all(|item| item.is_ok())); }