use faucet_conformance::{assert_config_schema_valid_value, assert_errors_not_panics};
use faucet_source_sqs::{SqsCredentials, SqsSource, SqsSourceConfig};
use testcontainers::{ContainerAsync, runners::AsyncRunner};
use testcontainers_modules::localstack::LocalStack;
#[test]
fn conformance_config_schema_valid() {
let schema = serde_json::to_value(schemars::schema_for!(SqsSourceConfig)).unwrap();
assert_config_schema_valid_value(&schema, "faucet-source-sqs");
}
#[tokio::test(flavor = "multi_thread")]
async fn conformance_errors_not_panics() {
let mut cfg = SqsSourceConfig::new("https://sqs.us-east-1.amazonaws.com/1/does-not-exist");
cfg.region = Some("us-east-1".into());
cfg.endpoint_url = Some("http://127.0.0.1:1".into());
cfg.credentials = test_credentials();
cfg.wait_time_seconds = 0;
cfg.idle_timeout_secs = Some(1);
cfg.max_messages = Some(10);
let source = SqsSource::new(cfg).await.expect("source builds lazily");
assert_errors_not_panics(&source).await;
}
async fn start_localstack() -> (ContainerAsync<LocalStack>, String) {
use testcontainers::ImageExt;
let image = LocalStack::default().with_env_var("SERVICES", "sqs");
let container = image.start().await.expect("localstack start");
let port = container
.get_host_port_ipv4(4566)
.await
.expect("localstack port");
(container, format!("http://127.0.0.1:{port}"))
}
fn test_credentials() -> SqsCredentials {
SqsCredentials::AccessKey {
access_key_id: "test".into(),
secret_access_key: "test".into(),
session_token: None,
}
}
async fn raw_client(endpoint: &str) -> aws_sdk_sqs::Client {
faucet_source_sqs::build_client(Some("us-east-1"), Some(endpoint), &test_credentials())
.await
.expect("client")
}
async fn create_queue(client: &aws_sdk_sqs::Client, name: &str) -> String {
use aws_sdk_sqs::types::QueueAttributeName;
for _ in 0..120 {
match client
.create_queue()
.queue_name(name)
.attributes(QueueAttributeName::VisibilityTimeout, "300")
.send()
.await
{
Ok(out) => return out.queue_url().expect("queue url").to_string(),
Err(_) => tokio::time::sleep(std::time::Duration::from_millis(500)).await,
}
}
panic!("localstack sqs never became ready");
}
async fn seed(client: &aws_sdk_sqs::Client, queue_url: &str, n: usize) {
use aws_sdk_sqs::types::SendMessageBatchRequestEntry;
let mut i = 0usize;
while i < n {
let end = (i + 10).min(n);
let entries: Vec<SendMessageBatchRequestEntry> = (i..end)
.map(|j| {
SendMessageBatchRequestEntry::builder()
.id(format!("m{j}"))
.message_body(format!("{{\"i\":{j}}}"))
.build()
.expect("entry")
})
.collect();
let out = client
.send_message_batch()
.queue_url(queue_url)
.set_entries(Some(entries))
.send()
.await
.expect("send_message_batch");
assert!(out.failed().is_empty(), "seeding must not fail");
i = end;
}
}
#[tokio::test(flavor = "multi_thread")]
async fn conformance_bounded_memory() {
let (_container, endpoint) = start_localstack().await;
let client = raw_client(&endpoint).await;
let queue_url = create_queue(&client, "conformance").await;
seed(&client, &queue_url, 5_000).await;
let mut cfg = SqsSourceConfig::new(&queue_url);
cfg.region = Some("us-east-1".into());
cfg.endpoint_url = Some(endpoint.clone());
cfg.credentials = test_credentials();
cfg.wait_time_seconds = 1;
cfg.idle_timeout_secs = Some(15);
cfg.batch_size = 250;
let source = SqsSource::new(cfg).await.expect("source");
faucet_conformance::assert_bounded_memory(&source, 250, 5_000).await;
}