Full-depth rename per the approved plan; the 'claw' product vocabulary (claws, /claws routes, clawId, Claw Chat) stays — it is now the brand. - Display brand: Clawmates (manifest, titles, hero, login/rail logo 'clawmates'); default host app.clawmates.work; registry ghcr.io/clawmates - Crates tc-* -> cm-* (16 crates + all imports); binaries clawmates-server/broker/bundler; images clawmates/*; env prefix CLAWMATES_* (+ CM_TEST_DATABASE_URL / CM_LIVE_LLM); config clawmates.toml; helm chart deploy/helm/clawmates with clawmates-* resources; db names clawmates*; sockets /run/clawmates; cookie cm_session; kind cluster clawmates-test; seccomp node profile clawmates-agent-profile.json - All 9 Playwright brand assertions updated in lockstep; historical spec document left untouched as the only remaining 'TeamClaw' - Local env migrated: dev pg clawmates-dev-pg/clawmates_dev, shared test server clawmates-test-pg, kind cluster recreated with image + profile, compose images rebuilt under clawmates/* Verified end to end: 161 Rust + 68 frontend tests, 29 Playwright journeys, 4 live kind tests, helm/install/LOC/placeholder gates, and the clean-room install rehearsal serving the clawmates login page from a signed bundle of the rebuilt images. Co-Authored-By: Claude Fable 5 <[email protected]>
82 lines
2.9 KiB
Rust
82 lines
2.9 KiB
Rust
//! Spans reach a REAL OTLP/HTTP receiver: a live server accepting
|
|
//! protobuf at /v1/traces, decoded with the official proto types — the
|
|
//! exact wire contract any collector speaks.
|
|
|
|
use std::sync::Arc;
|
|
|
|
use opentelemetry_proto::tonic::collector::trace::v1::ExportTraceServiceRequest;
|
|
use prost::Message;
|
|
use tokio::sync::Mutex;
|
|
|
|
type Received = Arc<Mutex<Vec<ExportTraceServiceRequest>>>;
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn spans_export_to_a_real_otlp_receiver() {
|
|
let received: Received = Arc::new(Mutex::new(Vec::new()));
|
|
let state = received.clone();
|
|
let app = axum::Router::new()
|
|
.route(
|
|
"/v1/traces",
|
|
axum::routing::post(
|
|
|axum::extract::State(state): axum::extract::State<Received>,
|
|
body: axum::body::Bytes| async move {
|
|
let request =
|
|
ExportTraceServiceRequest::decode(body.as_ref()).expect("otlp protobuf");
|
|
state.lock().await.push(request);
|
|
// An empty ExportTraceServiceResponse.
|
|
(
|
|
[("content-type", "application/x-protobuf")],
|
|
Vec::<u8>::new(),
|
|
)
|
|
},
|
|
),
|
|
)
|
|
.with_state(state);
|
|
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
|
|
let endpoint = format!("http://{}", listener.local_addr().unwrap());
|
|
tokio::spawn(async move {
|
|
axum::serve(listener, app).await.unwrap();
|
|
});
|
|
|
|
let guard = cm_telemetry::init("clawmates-test", Some(&endpoint)).expect("init");
|
|
|
|
{
|
|
let span = tracing::info_span!("approval_decision", approval_id = "ap_123");
|
|
let _entered = span.enter();
|
|
tracing::info!("decided");
|
|
}
|
|
guard.flush();
|
|
|
|
// Find our span among everything exported.
|
|
let requests = received.lock().await;
|
|
assert!(!requests.is_empty(), "no OTLP export arrived");
|
|
let mut span_names = Vec::new();
|
|
let mut service_names = Vec::new();
|
|
for request in requests.iter() {
|
|
for resource in &request.resource_spans {
|
|
if let Some(res) = &resource.resource {
|
|
for attr in &res.attributes {
|
|
if attr.key == "service.name" {
|
|
if let Some(value) = &attr.value {
|
|
service_names.push(format!("{value:?}"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for scope in &resource.scope_spans {
|
|
for span in &scope.spans {
|
|
span_names.push(span.name.clone());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
assert!(
|
|
span_names.iter().any(|name| name == "approval_decision"),
|
|
"spans: {span_names:?}"
|
|
);
|
|
assert!(
|
|
service_names.iter().any(|s| s.contains("clawmates-test")),
|
|
"services: {service_names:?}"
|
|
);
|
|
}
|