наблюдаемость: ввести безопасный контракт метрик
CI / Rust Checks (pull_request) Successful in 6m15s
CI / UI Checks (pull_request) Successful in 5s
CI / Community Image Smoke (pull_request) Successful in 4m25s
CI / Frontend E2E (pull_request) Successful in 5m17s
CI / Deploy (pull_request) Has been skipped
CI / Rust Checks (push) Successful in 6m9s
CI / UI Checks (push) Successful in 5s
CI / Community Image Smoke (push) Successful in 1m3s
CI / Frontend E2E (push) Successful in 3m47s
CI / Deploy (push) Failing after 3s

This commit is contained in:
2026-07-31 05:04:01 +03:00
parent ec2453c00f
commit 9b1a739e39
50 changed files with 3066 additions and 433 deletions
@@ -0,0 +1,76 @@
use std::{sync::Arc, time::Duration};
use crank_community_mcp::session::{
ActiveSessionMetrics, InMemorySessionStore, SharedSessionStore, TransportSessionStore,
spawn_session_cleanup,
};
use metrics_util::debugging::{DebugValue, DebuggingRecorder, Snapshotter};
use time::OffsetDateTime;
#[tokio::test]
async fn active_session_sampler_tracks_create_delete_and_expiry() {
let recorder = DebuggingRecorder::new();
let snapshotter = recorder.snapshotter();
recorder
.install()
.expect("isolated integration test recorder");
let store = Arc::new(InMemorySessionStore::default());
let sessions: SharedSessionStore = store.clone();
let sampler = ActiveSessionMetrics::start(Arc::clone(&sessions));
assert_gauge(&sampler, &snapshotter, 0.0).await;
let now = OffsetDateTime::now_utc();
store
.create(
"2025-11-25",
"default",
"agent",
false,
now,
Some(now + time::Duration::milliseconds(100)),
)
.await
.unwrap();
assert_gauge(&sampler, &snapshotter, 1.0).await;
spawn_session_cleanup(sessions, sampler, Duration::from_millis(10));
assert_gauge_without_refresh(&snapshotter, 0.0).await;
}
async fn assert_gauge_without_refresh(snapshotter: &Snapshotter, expected: f64) {
for _ in 0..100 {
tokio::time::sleep(Duration::from_millis(10)).await;
if gauge_value(snapshotter) == Some(expected) {
return;
}
}
panic!("active session gauge did not become {expected} through cleanup");
}
async fn assert_gauge(sampler: &ActiveSessionMetrics, snapshotter: &Snapshotter, expected: f64) {
for _ in 0..50 {
sampler.refresh();
tokio::time::sleep(Duration::from_millis(10)).await;
if gauge_value(snapshotter) == Some(expected) {
return;
}
}
panic!("active session gauge did not become {expected}");
}
fn gauge_value(snapshotter: &Snapshotter) -> Option<f64> {
snapshotter
.snapshot()
.into_vec()
.into_iter()
.find_map(|(key, _, _, value)| {
if key.key().name() != "crank_mcp_active_sessions" {
return None;
}
match value {
DebugValue::Gauge(value) => Some(value.into_inner()),
_ => None,
}
})
}