#![cfg(all(feature = "listen", feature = "rustls-tls-native-roots"))]
mod common;
use std::path::PathBuf;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex, Weak};
use common::{client, self_signed, self_signed_named, spawn_tls_server, SelfSigned};
use deepgram::{common::options::Options, tls::TlsTrust, DeepgramError};
static SHARED_CERT: Mutex<Weak<SharedCert>> = Mutex::new(Weak::new());
static FIXTURE_SEQ: AtomicUsize = AtomicUsize::new(0);
struct SharedCert {
cert: SelfSigned,
path: PathBuf,
}
impl Drop for SharedCert {
fn drop(&mut self) {
let _ = std::fs::remove_file(&self.path);
}
}
fn shared_cert() -> Arc<SharedCert> {
let mut slot = SHARED_CERT.lock().expect("fixture lock");
if let Some(live) = slot.upgrade() {
return live;
}
let cert = self_signed();
let path = std::env::temp_dir().join(format!(
"deepgram-rust-sdk-test-ca-{}-{}.pem",
std::process::id(),
FIXTURE_SEQ.fetch_add(1, Ordering::Relaxed)
));
std::fs::write(&path, cert.cert_pem.as_bytes()).expect("write test CA");
std::env::set_var("SSL_CERT_FILE", &path);
let fixture = Arc::new(SharedCert { cert, path });
*slot = Arc::downgrade(&fixture);
fixture
}
#[tokio::test]
async fn native_roots_trust_a_certificate_from_the_os_store() {
let fixture = shared_cert();
let cert = &fixture.cert;
let port = spawn_tls_server(cert.cert_der.clone(), cert.key_der.clone()).await;
client(port)
.transcription()
.stream_request_with_options(Options::default())
.handle()
.await
.expect("connect once the certificate is trusted via the OS store");
}
#[tokio::test]
async fn native_roots_apply_to_flux_speech_to_text() {
let fixture = shared_cert();
let cert = &fixture.cert;
let port = spawn_tls_server(cert.cert_der.clone(), cert.key_der.clone()).await;
client(port)
.transcription()
.flux_request()
.handle()
.await
.expect("flux connect once the certificate is trusted via the OS store");
}
#[cfg(feature = "speak")]
#[tokio::test]
async fn native_roots_apply_to_flux_text_to_speech() {
use deepgram::speak::flux::options::{Model, Options as SpeakOptions};
let fixture = shared_cert();
let cert = &fixture.cert;
let port = spawn_tls_server(cert.cert_der.clone(), cert.key_der.clone()).await;
client(port)
.text_to_speech()
.flux_request(SpeakOptions::builder(Model::FluxHaleyEn).build())
.handle()
.await
.expect("flux TTS connect once the certificate is trusted via the OS store");
}
#[tokio::test]
async fn native_roots_still_reject_a_certificate_the_os_store_does_not_have() {
let _fixture = shared_cert();
let other = self_signed_named("Some Other CA");
let port = spawn_tls_server(other.cert_der, other.key_der).await;
let err = client(port)
.transcription()
.stream_request_with_options(Options::default())
.handle()
.await
.expect_err("a certificate in neither webpki nor the OS store must be rejected");
assert!(
matches!(
err,
DeepgramError::UntrustedTlsCertificate {
trust: TlsTrust::WebpkiAndNative,
..
}
),
"{err:?}"
);
let message = err.to_string();
assert!(message.contains("SSL_CERT_FILE"), "{message}");
}
#[cfg(feature = "connect-diagnostics")]
#[tokio::test]
async fn native_roots_apply_to_the_phase_timed_path_and_are_recorded() {
use deepgram::diagnostics::{ConnectOutcome, ConnectRecord};
let fixture = shared_cert();
let cert = &fixture.cert;
let port = spawn_tls_server(cert.cert_der.clone(), cert.key_der.clone()).await;
let (diag_tx, mut diag_rx) = tokio::sync::mpsc::unbounded_channel::<ConnectRecord>();
client(port)
.transcription()
.stream_request_with_options(Options::default())
.diagnostics(diag_tx)
.handle()
.await
.expect("phase-timed connect once the certificate is trusted via the OS store");
let record = diag_rx.try_recv().expect("one record per attempt");
assert_eq!(record.outcome, ConnectOutcome::Completed);
assert_eq!(record.tls_trust, Some(TlsTrust::WebpkiAndNative));
assert_eq!(record.tls_resumed, Some(false));
}