fix: harden typed metrics review findings
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use crank_metrics::{
|
||||
HTTP_ROUTE_DOMAIN, HttpRoute, MAX_LOGICAL_SERIES_PER_PROCESS, MAX_PRODUCT_LABELS_PER_FAMILY,
|
||||
MAX_RENDERED_SERIES_PER_PROCESS, METRIC_SCHEMA_VERSION, metric_schema,
|
||||
render_metric_schema_json, schema_budget, validate_budget,
|
||||
HTTP_ROUTE_DOMAIN, HttpRoute, LabelClass, LabelDomain, MAX_LOGICAL_SERIES_PER_PROCESS,
|
||||
MAX_PRODUCT_LABELS_PER_FAMILY, MAX_RENDERED_SERIES_PER_PROCESS, METRIC_SCHEMA_VERSION,
|
||||
MetricDefinition, MetricKind, MetricProcess, MetricService, MetricUnit, metric_schema,
|
||||
metric_schema_for_service, render_metric_schema_json, schema_budget, validate_budget,
|
||||
validate_schema,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -98,3 +100,89 @@ fn exact_budget_math_counts_histogram_bucket_sum_and_count_series() {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prometheus_reserved_and_digit_prefixed_label_names_are_rejected() {
|
||||
static DOMAIN: LabelDomain = LabelDomain {
|
||||
name: "valid",
|
||||
class: LabelClass::ProductClosed,
|
||||
values: &["value"],
|
||||
};
|
||||
for label in ["1invalid", "__reserved"] {
|
||||
let mut domain = DOMAIN;
|
||||
domain.name = label;
|
||||
let definition = MetricDefinition {
|
||||
name: "crank_test_total",
|
||||
kind: MetricKind::Counter,
|
||||
unit: MetricUnit::Count,
|
||||
labels: Box::leak(vec![label].into_boxed_slice()),
|
||||
label_domains: Box::leak(vec![domain].into_boxed_slice()),
|
||||
buckets: &[],
|
||||
processes: &[MetricProcess::AdminApi],
|
||||
max_logical_series: 1,
|
||||
max_rendered_series: 1,
|
||||
description: "test metric",
|
||||
};
|
||||
assert!(validate_schema(&[definition]).is_err(), "accepted {label}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn process_projection_excludes_mcp_only_metrics_from_admin() {
|
||||
let admin = metric_schema_for_service(MetricService::AdminApi)
|
||||
.map(|definition| definition.name)
|
||||
.collect::<BTreeSet<_>>();
|
||||
let mcp = metric_schema_for_service(MetricService::McpServer)
|
||||
.map(|definition| definition.name)
|
||||
.collect::<BTreeSet<_>>();
|
||||
assert!(!admin.contains("crank_mcp_active_sessions"));
|
||||
assert!(!admin.contains("crank_catalog_tools"));
|
||||
assert!(mcp.contains("crank_mcp_active_sessions"));
|
||||
assert!(admin.contains("crank_http_requests_total"));
|
||||
assert!(mcp.contains("crank_http_requests_total"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn registered_admin_and_mcp_routes_are_exactly_the_schema_domain() {
|
||||
let admin_source = include_str!("../../../apps/admin-api/src/app.rs");
|
||||
let mcp_source = include_str!("../../crank-community-mcp/src/app.rs");
|
||||
let auth = ["/login", "/logout", "/session", "/profile", "/password"];
|
||||
let admin_root = ["/capabilities", "/workspaces", "/workspaces/{workspace_id}"];
|
||||
let mut actual = BTreeSet::from(["unmatched".to_owned()]);
|
||||
for route in route_literals(admin_source) {
|
||||
let canonical = if matches!(route.as_str(), "/health" | "/ready") {
|
||||
route
|
||||
} else if auth.contains(&route.as_str()) {
|
||||
format!("/api/auth{route}")
|
||||
} else if admin_root.contains(&route.as_str()) {
|
||||
format!("/api/admin{route}")
|
||||
} else {
|
||||
format!("/api/admin/workspaces/{{workspace_id}}{route}")
|
||||
};
|
||||
actual.insert(canonical);
|
||||
}
|
||||
actual.extend(route_literals(mcp_source));
|
||||
let expected = HTTP_ROUTE_DOMAIN
|
||||
.iter()
|
||||
.map(|route| (*route).to_owned())
|
||||
.collect::<BTreeSet<_>>();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
fn route_literals(source: &str) -> Vec<String> {
|
||||
let mut routes = Vec::new();
|
||||
let mut remaining = source;
|
||||
while let Some(offset) = remaining.find(".route(") {
|
||||
remaining = &remaining[offset + ".route(".len()..];
|
||||
let Some(start) = remaining.find('"') else {
|
||||
break;
|
||||
};
|
||||
let after_start = &remaining[start + 1..];
|
||||
let Some(end) = after_start.find('"') else {
|
||||
break;
|
||||
};
|
||||
routes.push(after_start[..end].to_owned());
|
||||
remaining = &after_start[end + 1..];
|
||||
}
|
||||
routes
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user