mod common;
use apify_client::models::RequestQueueRequest;
use serde_json::json;
#[tokio::test(flavor = "multi_thread")]
async fn list_request_queues() {
let client = require_client!();
let page = client
.request_queues()
.list(Default::default())
.await
.expect("listing request queues should succeed");
assert!(page.total >= 0);
}
#[tokio::test(flavor = "multi_thread")]
async fn get_request_queue() {
let client = require_client!();
let name = common::unique_name("rq-get");
let queue = client
.request_queues()
.get_or_create(Some(&name))
.await
.expect("create queue");
let cleanup_client = client.clone();
let id = queue.id.clone();
let _guard = common::Cleanup::new(move || async move {
let _ = cleanup_client.request_queue(&id).delete().await;
});
let fetched = client
.request_queue(&queue.id)
.get()
.await
.expect("get queue by id")
.expect("queue should exist");
assert_eq!(fetched.id, queue.id);
}
#[tokio::test(flavor = "multi_thread")]
async fn request_queue_crud_flow() {
let client = require_client!();
let name = common::unique_name("rq");
let queue = client
.request_queues()
.get_or_create(Some(&name))
.await
.expect("create queue");
assert_eq!(queue.name.as_deref(), Some(name.as_str()));
let cleanup_client = client.clone();
let cleanup_id = queue.id.clone();
let _guard = common::Cleanup::new(move || async move {
let _ = cleanup_client.request_queue(&cleanup_id).delete().await;
});
let queue_client = client.request_queue(&queue.id);
assert!(queue_client.get().await.expect("get queue").is_some());
let request = RequestQueueRequest {
id: None,
url: "https://example.com/".to_string(),
unique_key: Some("example".to_string()),
method: Some("GET".to_string()),
user_data: Some(json!({ "label": "START" })),
extra: Default::default(),
};
let added = queue_client
.add_request(&request, false)
.await
.expect("add request");
assert!(!added.request_id.is_empty());
let fetched = queue_client
.get_request(&added.request_id)
.await
.expect("get request")
.expect("request should exist");
assert_eq!(fetched.url, "https://example.com/");
let head = queue_client.list_head(Some(10)).await.expect("list head");
assert!(head
.items
.iter()
.any(|r| r.id.as_deref() == Some(added.request_id.as_str())));
let renamed = common::unique_name("rq-renamed");
let updated = queue_client
.update(&json!({ "name": renamed }))
.await
.expect("update queue");
assert_eq!(updated.name.as_deref(), Some(renamed.as_str()));
queue_client
.delete_request(&added.request_id)
.await
.expect("delete request");
queue_client.delete().await.expect("delete queue");
assert!(queue_client
.get()
.await
.expect("get after delete")
.is_none());
}
#[tokio::test(flavor = "multi_thread")]
async fn request_queue_paginate_multiple_pages() {
let client = require_client!();
let name = common::unique_name("rq-page");
let queue = client
.request_queues()
.get_or_create(Some(&name))
.await
.expect("create queue");
let cleanup_client = client.clone();
let cleanup_id = queue.id.clone();
let _guard = common::Cleanup::new(move || async move {
let _ = cleanup_client.request_queue(&cleanup_id).delete().await;
});
let queue_client = client.request_queue(&queue.id);
const TOTAL: usize = 5;
let mut expected_urls = std::collections::HashSet::new();
for i in 0..TOTAL {
let url = format!("https://example.com/page/{i}");
let request = RequestQueueRequest {
id: None,
url: url.clone(),
unique_key: Some(format!("page-{i}")),
method: Some("GET".to_string()),
user_data: None,
extra: Default::default(),
};
queue_client
.add_request(&request, false)
.await
.expect("add request");
expected_urls.insert(url);
}
let mut iter = queue_client.paginate_requests(Some(2));
let mut seen = std::collections::HashSet::new();
while let Some(req) = iter.next().await.expect("paginate page") {
assert!(
seen.insert(req.url.clone()),
"request {} yielded more than once (pagination cursor bug)",
req.url
);
}
assert_eq!(
seen, expected_urls,
"pagination must yield every added request exactly once across pages"
);
queue_client.delete().await.expect("delete queue");
}
#[tokio::test(flavor = "multi_thread")]
async fn request_queue_lock_lifecycle() {
let client = require_client!();
let name = common::unique_name("rq-lock");
let queue = client
.request_queues()
.get_or_create(Some(&name))
.await
.expect("create queue");
let cleanup_client = client.clone();
let cleanup_id = queue.id.clone();
let _guard = common::Cleanup::new(move || async move {
let _ = cleanup_client.request_queue(&cleanup_id).delete().await;
});
let queue_client = client
.request_queue(&queue.id)
.with_client_key("rust-test-client");
let request = RequestQueueRequest {
id: None,
url: "https://example.com/lock".to_string(),
unique_key: Some("lock-example".to_string()),
method: Some("GET".to_string()),
user_data: None,
extra: Default::default(),
};
let added = queue_client
.add_request(&request, false)
.await
.expect("add request");
let listed = queue_client
.list_requests(apify_client::ListRequestsOptions {
limit: Some(10),
..Default::default()
})
.await
.expect("list requests");
assert!(listed.get("items").is_some());
let filtered = queue_client
.list_requests(apify_client::ListRequestsOptions {
limit: Some(10),
filter: Some(vec!["locked".to_string(), "pending".to_string()]),
..Default::default()
})
.await
.expect("list requests with filter");
assert!(filtered.get("items").is_some());
let mut iter = queue_client.paginate_requests(Some(10));
let first = iter.next().await.expect("paginate requests");
assert!(first.is_some(), "pagination should yield the added request");
let locked = queue_client
.list_and_lock_head(30, Some(5))
.await
.expect("lock head");
assert!(locked.get("items").is_some());
queue_client
.prolong_request_lock(&added.request_id, 60, false)
.await
.expect("prolong lock");
queue_client
.delete_request_lock(&added.request_id, false)
.await
.expect("delete lock");
queue_client
.unlock_requests()
.await
.expect("unlock requests");
queue_client.delete().await.expect("delete queue");
}