#![cfg(feature = "read")]
use std::time::Duration;
use deepgram::read::options::{CallbackMethod, Options};
use deepgram::Deepgram;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpListener;
use tokio::sync::oneshot;
struct Captured {
target: String,
body: String,
}
const READ_RESPONSE: &str = r#"{"metadata":{"request_id":"0193b1c8-6d3f-7a4e-b8f0-1234567890ab","created":"2026-01-01T00:00:00.000Z","language":"en"},"results":{}}"#;
const CALLBACK_RESPONSE: &str = r#"{"request_id":"0193b1c8-6d3f-7a4e-b8f0-1234567890ab"}"#;
async fn spawn_capturing_server(response_body: &'static str) -> (u16, oneshot::Receiver<Captured>) {
let listener = TcpListener::bind("127.0.0.1:0").await.expect("bind");
let port = listener.local_addr().expect("local addr").port();
let (captured_tx, captured_rx) = oneshot::channel();
tokio::spawn(async move {
let (mut stream, _) = listener.accept().await.expect("accept");
let mut buf = Vec::new();
let header_end = loop {
if let Some(at) = buf.windows(4).position(|w| w == b"\r\n\r\n") {
break at + 4;
}
let mut chunk = [0u8; 1024];
let read = stream.read(&mut chunk).await.expect("read headers");
assert_ne!(read, 0, "connection closed before the headers ended");
buf.extend_from_slice(&chunk[..read]);
};
let headers = String::from_utf8(buf[..header_end].to_vec()).expect("utf-8 headers");
let target = headers
.lines()
.next()
.and_then(|line| line.split_whitespace().nth(1))
.expect("request target")
.to_owned();
assert!(
headers.starts_with("POST "),
"expected a POST, got: {headers}"
);
let content_length: usize = headers
.lines()
.find_map(|line| {
line.split_once(':').and_then(|(name, value)| {
name.eq_ignore_ascii_case("content-length")
.then(|| value.trim().parse().expect("content-length"))
})
})
.expect("content-length header");
let mut body = buf[header_end..].to_vec();
while body.len() < content_length {
let mut chunk = [0u8; 1024];
let read = stream.read(&mut chunk).await.expect("read body");
assert_ne!(read, 0, "connection closed before the body ended");
body.extend_from_slice(&chunk[..read]);
}
body.truncate(content_length);
let _ = captured_tx.send(Captured {
target,
body: String::from_utf8(body).expect("utf-8 body"),
});
let response = format!(
"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
response_body.len(),
response_body
);
stream
.write_all(response.as_bytes())
.await
.expect("write response");
stream.flush().await.expect("flush");
});
(port, captured_rx)
}
fn client(port: u16) -> Deepgram {
Deepgram::with_base_url_and_api_key(format!("http://127.0.0.1:{port}").as_str(), "fake-key")
.expect("client")
}
async fn captured(rx: oneshot::Receiver<Captured>) -> Captured {
tokio::time::timeout(Duration::from_secs(5), rx)
.await
.expect("server captured a request")
.expect("server did not panic")
}
fn options() -> Options {
Options::builder().sentiment(true).build()
}
#[tokio::test]
async fn analyze_text_posts_a_text_body() {
let (port, rx) = spawn_capturing_server(READ_RESPONSE).await;
client(port)
.text_intelligence()
.analyze_text("The weather today is lovely.", &options())
.await
.expect("analyze_text");
let captured = captured(rx).await;
assert_eq!(captured.body, r#"{"text":"The weather today is lovely."}"#);
assert!(
captured.target.starts_with("/v1/read?"),
"target was: {}",
captured.target
);
assert!(
captured.target.contains("sentiment=true") && captured.target.contains("language=en"),
"target was: {}",
captured.target
);
}
#[tokio::test]
async fn analyze_url_posts_a_url_body() {
let (port, rx) = spawn_capturing_server(READ_RESPONSE).await;
client(port)
.text_intelligence()
.analyze_url("https://example.com/doc.txt", &options())
.await
.expect("analyze_url");
let captured = captured(rx).await;
assert_eq!(captured.body, r#"{"url":"https://example.com/doc.txt"}"#);
}
#[tokio::test]
async fn analyze_text_callback_posts_a_text_body_and_the_callback_query() {
let (port, rx) = spawn_capturing_server(CALLBACK_RESPONSE).await;
let options = Options::builder()
.sentiment(true)
.callback_method(CallbackMethod::PUT)
.build();
client(port)
.text_intelligence()
.analyze_text_callback("hello", &options, "https://example.com/hook")
.await
.expect("analyze_text_callback");
let captured = captured(rx).await;
assert_eq!(captured.body, r#"{"text":"hello"}"#);
assert!(
captured
.target
.contains("callback=https%3A%2F%2Fexample.com%2Fhook"),
"target was: {}",
captured.target
);
assert!(
captured.target.contains("callback_method=put"),
"target was: {}",
captured.target
);
}
#[tokio::test]
async fn analyze_url_callback_posts_a_url_body() {
let (port, rx) = spawn_capturing_server(CALLBACK_RESPONSE).await;
client(port)
.text_intelligence()
.analyze_url_callback(
"https://example.com/doc.txt",
&options(),
"https://example.com/hook",
)
.await
.expect("analyze_url_callback");
let captured = captured(rx).await;
assert_eq!(captured.body, r#"{"url":"https://example.com/doc.txt"}"#);
assert!(
captured
.target
.contains("callback=https%3A%2F%2Fexample.com%2Fhook"),
"target was: {}",
captured.target
);
}