#![cfg(all(feature = "http-tracing", not(target_arch = "wasm32")))]
use std::fmt::Write as _;
use std::sync::{Arc, Mutex};
use allama::ChatRequest;
use allama_conformance::{MockHttpResponse, TestHttpServer};
use allama_http::{
Credential, CredentialPlacement, CredentialSlot, HttpRequest, HttpRequestOptions,
HttpTransport, ReqwestTransport,
};
use tracing::field::{Field, Visit};
use tracing::span::{Attributes, Id, Record};
use tracing::{Event, Subscriber};
use tracing_subscriber::layer::{Context, Layer, SubscriberExt};
#[derive(Clone, Default)]
struct Capture(Arc<Mutex<String>>);
impl Visit for Capture {
fn record_debug(&mut self, field: &Field, value: &dyn std::fmt::Debug) {
let _ = writeln!(self.0.lock().unwrap(), "{}={value:?}", field.name());
}
}
impl<S: Subscriber> Layer<S> for Capture {
fn on_new_span(&self, attrs: &Attributes<'_>, _: &Id, _: Context<'_, S>) {
attrs.record(&mut self.clone());
}
fn on_record(&self, _: &Id, values: &Record<'_>, _: Context<'_, S>) {
values.record(&mut self.clone());
}
fn on_event(&self, event: &Event<'_>, _: Context<'_, S>) {
event.record(&mut self.clone());
}
}
#[tokio::test]
async fn spans_never_record_header_values() {
let server = TestHttpServer::spawn([MockHttpResponse::text(200, "ok")]).await;
let capture = Capture::default();
let _guard =
tracing::subscriber::set_default(tracing_subscriber::registry().with(capture.clone()));
let request = ChatRequest::new("m").with_option(
HttpRequestOptions::new()
.credential("gateway", Credential::api_key("TENANT-SECRET"))
.credential("gateway", Credential::header("x-route-key", "ROUTE-SECRET"))
.header("x-gateway-tag", "TAG-VALUE"),
);
let slot = CredentialSlot::new("gateway", CredentialPlacement::Bearer);
let http = HttpRequest::post(format!("{}/v1/chat", server.url()), "{}")
.header("authorization", "Bearer provider-key")
.apply_options(&request.options, &slot)
.unwrap();
let response = ReqwestTransport::new().send(http).await.unwrap();
assert_eq!(response.status, 200);
let recorded = server.recorded_requests().await;
assert_eq!(
recorded[0].header("authorization"),
Some("Bearer TENANT-SECRET")
);
assert_eq!(recorded[0].header("x-route-key"), Some("ROUTE-SECRET"));
let fields = capture.0.lock().unwrap().clone();
assert!(
fields.contains("/v1/chat"),
"no span was recorded: {fields}"
);
assert!(!fields.contains("TENANT-SECRET"), "{fields}");
assert!(!fields.contains("ROUTE-SECRET"), "{fields}");
assert!(!fields.contains("TAG-VALUE"), "{fields}");
}