#![allow(dead_code, unused_macros)]
use std::net::SocketAddr;
use apollo_http_client::{HttpBody, HttpClient, HttpClientConfig};
use apollo_opentelemetry::metrics::Clock;
use apollo_opentelemetry_test::TelemetryContext;
use axum::Router;
use bytes::Bytes;
use http_body_util::{BodyExt as _, Full};
use indoc::indoc;
use opentelemetry_sdk::metrics::{Aggregation, Instrument, StreamBuilder};
pub fn integration_context() -> TelemetryContext {
TelemetryContext::builder()
.with_view(|instrument: &Instrument| {
if instrument.name() == "http.client.connection.duration" {
Some(
StreamBuilder::default()
.with_aggregation(Aggregation::Drop)
.build()
.unwrap(),
)
} else {
None
}
})
.build()
}
macro_rules! snapshot {
($ctx:expr, @$snapshot:literal) => {{
insta::with_settings!({
filters => [
(r#"server\.port: "\d+""#, r#"server.port: "<port>""#),
(r#"network\.peer\.port: "\d+""#, r#"network.peer.port: "<peer_port>""#),
]
}, {
apollo_opentelemetry_test::assert_metrics_snapshot!($ctx, @$snapshot);
});
}};
}
macro_rules! span_snapshot {
($ctx:expr, @$snapshot:literal) => {{
insta::with_settings!({
filters => [
(r#"server\.port: "\d+""#, r#"server.port: "<port>""#),
(r#"url\.full: "http://127\.0\.0\.1:\d+/"#, r#"url.full: "http://127.0.0.1:<port>/"#),
]
}, {
apollo_opentelemetry_test::assert_spans_snapshot!($ctx, @$snapshot);
});
}};
}
pub async fn spawn_server(router: Router) -> SocketAddr {
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
tokio::spawn(async move {
axum::serve(listener, router).await.unwrap();
});
addr
}
#[cfg(unix)]
pub struct UnixTestServer {
socket_path: String,
_tmp: tempfile::TempDir,
task: tokio::task::JoinHandle<()>,
}
#[cfg(unix)]
impl UnixTestServer {
pub fn path(&self) -> &str {
&self.socket_path
}
}
#[cfg(unix)]
impl Drop for UnixTestServer {
fn drop(&mut self) {
self.task.abort();
}
}
#[cfg(unix)]
pub async fn spawn_unix_server(router: Router) -> UnixTestServer {
let tmp = tempfile::tempdir().unwrap();
let socket_path = tmp
.path()
.join("test.sock")
.into_os_string()
.into_string()
.expect("tempdir path is UTF-8");
let listener = tokio::net::UnixListener::bind(&socket_path).unwrap();
let task = tokio::spawn(async move {
axum::serve(listener, router).await.unwrap();
});
UnixTestServer {
socket_path,
_tmp: tmp,
task,
}
}
pub async fn spawn_drop_server() -> SocketAddr {
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
tokio::spawn(async move {
while let Ok((socket, _)) = listener.accept().await {
drop(socket);
}
});
addr
}
pub async fn spawn_stall_server() -> SocketAddr {
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
tokio::spawn(async move {
while let Ok((socket, _)) = listener.accept().await {
tokio::spawn(async move {
tokio::time::sleep(std::time::Duration::from_secs(60)).await;
drop(socket);
});
}
});
addr
}
pub async fn spawn_tls_h2_server(router: Router) -> SocketAddr {
use std::sync::Arc as SyncArc;
use hyper::server::conn::http2;
use hyper_util::rt::{TokioExecutor, TokioIo};
use hyper_util::service::TowerToHyperService;
use rcgen::{CertificateParams, KeyPair};
use rustls::ServerConfig;
use rustls::crypto::aws_lc_rs as aws_lc_rs_crypto;
use tokio_rustls::TlsAcceptor;
let key_pair = KeyPair::generate().unwrap();
let params = CertificateParams::new(vec!["127.0.0.1".to_string()]).unwrap();
let cert = params.self_signed(&key_pair).unwrap();
let cert_der = cert.der().clone();
let key_der = rustls::pki_types::PrivateKeyDer::Pkcs8(key_pair.serialize_der().into());
let mut server_config =
ServerConfig::builder_with_provider(SyncArc::new(aws_lc_rs_crypto::default_provider()))
.with_safe_default_protocol_versions()
.unwrap()
.with_no_client_auth()
.with_single_cert(vec![cert_der], key_der)
.unwrap();
server_config.alpn_protocols = vec![b"h2".to_vec()];
let acceptor = TlsAcceptor::from(SyncArc::new(server_config));
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
tokio::spawn(async move {
loop {
let Ok((stream, _)) = listener.accept().await else {
break;
};
let acceptor = acceptor.clone();
let router = router.clone();
tokio::spawn(async move {
let Ok(tls_stream) = acceptor.accept(stream).await else {
return;
};
let svc = TowerToHyperService::new(router);
http2::Builder::new(TokioExecutor::new())
.serve_connection(TokioIo::new(tls_stream), svc)
.await
.ok();
});
}
});
addr
}
pub fn new_client(config: &HttpClientConfig) -> HttpClient {
let (clock, _mock) = Clock::mock();
HttpClient::with_clock(config, clock).expect("valid config")
}
pub fn parse_config(yaml: &str) -> HttpClientConfig {
apollo_configuration::parse_yaml(yaml, &Default::default()).expect("valid config")
}
pub fn parse_config_with_vars(yaml: &str, vars: &[(&str, &str)]) -> HttpClientConfig {
use std::collections::HashMap;
use apollo_configuration::ParseYamlOptions;
use apollo_configuration::expansion::MapVariables;
let variables: HashMap<String, String> = vars
.iter()
.map(|(k, v)| ((*k).to_string(), (*v).to_string()))
.collect();
apollo_configuration::parse_yaml(
yaml,
&ParseYamlOptions::default().variables(MapVariables(variables)),
)
.expect("valid config")
}
pub fn default_config() -> HttpClientConfig {
parse_config("{}")
}
pub fn config_with_body_size() -> HttpClientConfig {
parse_config(indoc! {"
telemetry:
metrics:
request_body_size: true
response_body_size: true
url_scheme: true
"})
}
pub fn empty_body() -> HttpBody {
Full::new(Bytes::new())
.map_err(|e: std::convert::Infallible| match e {})
.boxed()
}
pub fn bytes_body(b: Bytes) -> HttpBody {
Full::new(b)
.map_err(|e: std::convert::Infallible| match e {})
.boxed()
}