refactor: 移除独立 hub/proxy/executor/gateway crate,统一为 gateway tunnel 架构

- 删除 aether-hub、aether-proxy 独立项目及其 Dockerfile/配置
- 删除 crates/aether-executor 和 crates/aether-gateway 全部模块
- 新增 apps/ 目录作为应用入口
- 将 hub 概念重构为 gateway tunnel transport
- 将 executor 重构为 execution runtime
- 新增 tunnel.rs 合约定义和 testkit tunnel/execution_runtime 模块
- 更新 Python 服务层和测试适配新架构命名
This commit is contained in:
fawney19
2026-04-03 14:59:58 +08:00
parent ddf18fed9a
commit 8f26e1a31f
983 changed files with 103098 additions and 105837 deletions

View File

@@ -0,0 +1,628 @@
use std::time::{SystemTime, UNIX_EPOCH};
use axum::body::Bytes;
use axum::http::Uri;
use sha2::{Digest, Sha256};
use url::form_urlencoded;
use crate::gateway::headers::{header_value_str, is_json_request};
use super::super::GatewayControlDecision;
use super::types::{
GatewayCredentialBundle, GatewayCredentialCarrier, GatewayExtractedCredentials,
GatewayPrimaryCredential, GatewayTrustedAdminHeaders, GatewayTrustedAuthHeaders,
};
pub(crate) fn extract_requested_model(
decision: &GatewayControlDecision,
uri: &Uri,
headers: &http::HeaderMap,
body: &Bytes,
) -> Option<String> {
if decision.route_family.as_deref() == Some("gemini") {
if let Some(model) = extract_gemini_model_from_path(uri.path()) {
return Some(model);
}
}
if !is_json_request(headers) || body.is_empty() {
return None;
}
serde_json::from_slice::<serde_json::Value>(body)
.ok()
.and_then(|payload| {
payload
.get("model")
.and_then(|value| value.as_str())
.map(|value| value.trim().to_string())
})
.filter(|value| !value.is_empty())
}
pub(super) fn extract_request_credentials(
headers: &http::HeaderMap,
uri: &Uri,
auth_endpoint_signature: &str,
) -> GatewayExtractedCredentials {
let bundle = GatewayCredentialBundle {
authorization_bearer: header_value_str(headers, http::header::AUTHORIZATION.as_str())
.as_deref()
.and_then(extract_bearer_token)
.map(ToOwned::to_owned),
x_api_key: header_value_str(headers, "x-api-key"),
api_key: header_value_str(headers, "api-key"),
x_goog_api_key: header_value_str(headers, "x-goog-api-key"),
query_key: extract_query_api_key(uri),
cookie_header: header_value_str(headers, http::header::COOKIE.as_str()),
};
let trusted_headers = extract_trusted_auth_headers(headers);
let trusted_admin_headers = extract_trusted_admin_headers(headers);
let primary = select_primary_credential(auth_endpoint_signature, &bundle);
GatewayExtractedCredentials {
trusted_headers,
trusted_admin_headers,
bundle,
primary,
}
}
fn has_trusted_gateway_marker(headers: &http::HeaderMap) -> bool {
header_value_str(headers, crate::gateway::constants::GATEWAY_HEADER)
.unwrap_or_default()
.trim()
.to_ascii_lowercase()
.starts_with("rust-phase3")
}
pub(super) fn build_auth_context_cache_key(
headers: &http::HeaderMap,
uri: &Uri,
auth_endpoint_signature: &str,
) -> Option<String> {
let signature = auth_endpoint_signature.trim();
if signature.is_empty() {
return None;
}
let extracted = extract_request_credentials(headers, uri, signature);
let bundle = extracted.bundle;
if bundle.authorization_bearer.is_none()
&& bundle.x_api_key.is_none()
&& bundle.api_key.is_none()
&& bundle.x_goog_api_key.is_none()
&& bundle.query_key.is_none()
&& bundle.cookie_header.is_none()
{
return None;
}
Some(format!(
"{signature}\n{}\n{}\n{}\n{}\n{}\n{}",
bundle.authorization_bearer.unwrap_or_default(),
bundle.x_api_key.unwrap_or_default(),
bundle.api_key.unwrap_or_default(),
bundle.x_goog_api_key.unwrap_or_default(),
bundle.query_key.unwrap_or_default(),
bundle.cookie_header.unwrap_or_default(),
))
}
fn extract_gemini_model_from_path(path: &str) -> Option<String> {
let (_, suffix) = path.split_once("/models/")?;
let model = suffix
.split_once(':')
.map(|(value, _)| value)
.unwrap_or(suffix);
let model = model.trim();
if model.is_empty() {
None
} else {
Some(model.to_string())
}
}
fn extract_trusted_auth_headers(headers: &http::HeaderMap) -> Option<GatewayTrustedAuthHeaders> {
if !has_trusted_gateway_marker(headers) {
return None;
}
let user_id = header_value_str(
headers,
crate::gateway::constants::TRUSTED_AUTH_USER_ID_HEADER,
)
.filter(|value| !value.is_empty())?;
let api_key_id = header_value_str(
headers,
crate::gateway::constants::TRUSTED_AUTH_API_KEY_ID_HEADER,
)
.filter(|value| !value.is_empty())?;
let balance_remaining = header_value_str(
headers,
crate::gateway::constants::TRUSTED_AUTH_BALANCE_HEADER,
)
.as_deref()
.and_then(parse_f64_header);
let access_allowed = header_value_str(
headers,
crate::gateway::constants::TRUSTED_AUTH_ACCESS_ALLOWED_HEADER,
)
.as_deref()
.and_then(parse_bool_header);
Some(GatewayTrustedAuthHeaders {
user_id,
api_key_id,
balance_remaining,
access_allowed,
})
}
pub(super) fn extract_trusted_admin_headers(
headers: &http::HeaderMap,
) -> Option<GatewayTrustedAdminHeaders> {
if !has_trusted_gateway_marker(headers) {
return None;
}
let user_id = header_value_str(
headers,
crate::gateway::constants::TRUSTED_ADMIN_USER_ID_HEADER,
)?
.trim()
.to_string();
if user_id.is_empty() {
return None;
}
let user_role = header_value_str(
headers,
crate::gateway::constants::TRUSTED_ADMIN_USER_ROLE_HEADER,
)?
.trim()
.to_string();
if !user_role.eq_ignore_ascii_case("admin") {
return None;
}
let session_id = header_value_str(
headers,
crate::gateway::constants::TRUSTED_ADMIN_SESSION_ID_HEADER,
)
.map(|value| value.trim().to_string())
.filter(|value| !value.is_empty());
let management_token_id = header_value_str(
headers,
crate::gateway::constants::TRUSTED_ADMIN_MANAGEMENT_TOKEN_ID_HEADER,
)
.map(|value| value.trim().to_string())
.filter(|value| !value.is_empty());
if session_id.is_none() && management_token_id.is_none() {
return None;
}
Some(GatewayTrustedAdminHeaders {
user_id,
user_role: "admin".to_string(),
session_id,
management_token_id,
})
}
fn select_primary_credential(
auth_endpoint_signature: &str,
bundle: &GatewayCredentialBundle,
) -> Option<GatewayPrimaryCredential> {
let signature = auth_endpoint_signature.trim().to_ascii_lowercase();
if signature.starts_with("gemini:") {
return select_gemini_credential(bundle);
}
if signature == "claude:cli" {
return select_claude_cli_credential(bundle);
}
if signature.starts_with("claude:") {
return select_claude_chat_credential(bundle);
}
if signature.starts_with("openai:") {
return select_openai_credential(bundle);
}
select_generic_credential(bundle)
}
fn select_openai_credential(bundle: &GatewayCredentialBundle) -> Option<GatewayPrimaryCredential> {
first_provider_api_key(
bundle,
&[
GatewayCredentialCarrier::AuthorizationBearer,
GatewayCredentialCarrier::XApiKey,
GatewayCredentialCarrier::ApiKey,
GatewayCredentialCarrier::XGoogApiKey,
GatewayCredentialCarrier::QueryKey,
],
)
.or_else(|| select_cookie_credential(bundle))
}
fn select_claude_cli_credential(
bundle: &GatewayCredentialBundle,
) -> Option<GatewayPrimaryCredential> {
first_provider_api_key(
bundle,
&[
GatewayCredentialCarrier::AuthorizationBearer,
GatewayCredentialCarrier::XApiKey,
GatewayCredentialCarrier::ApiKey,
],
)
.or_else(|| first_bearer_token(bundle))
.or_else(|| select_cookie_credential(bundle))
}
fn select_claude_chat_credential(
bundle: &GatewayCredentialBundle,
) -> Option<GatewayPrimaryCredential> {
first_provider_api_key(
bundle,
&[
GatewayCredentialCarrier::XApiKey,
GatewayCredentialCarrier::ApiKey,
],
)
.or_else(|| first_bearer_token(bundle))
.or_else(|| select_cookie_credential(bundle))
}
fn select_gemini_credential(bundle: &GatewayCredentialBundle) -> Option<GatewayPrimaryCredential> {
first_provider_api_key(
bundle,
&[
GatewayCredentialCarrier::QueryKey,
GatewayCredentialCarrier::XGoogApiKey,
GatewayCredentialCarrier::XApiKey,
GatewayCredentialCarrier::ApiKey,
],
)
.or_else(|| first_bearer_token(bundle))
.or_else(|| select_cookie_credential(bundle))
}
fn select_generic_credential(bundle: &GatewayCredentialBundle) -> Option<GatewayPrimaryCredential> {
first_bearer_token(bundle)
.or_else(|| {
first_provider_api_key(
bundle,
&[
GatewayCredentialCarrier::XApiKey,
GatewayCredentialCarrier::ApiKey,
GatewayCredentialCarrier::XGoogApiKey,
GatewayCredentialCarrier::QueryKey,
],
)
})
.or_else(|| select_cookie_credential(bundle))
}
fn first_provider_api_key(
bundle: &GatewayCredentialBundle,
carriers: &[GatewayCredentialCarrier],
) -> Option<GatewayPrimaryCredential> {
for carrier in carriers {
if let Some(raw) = credential_value(bundle, *carrier) {
return Some(GatewayPrimaryCredential::ProviderApiKey {
raw,
carrier: *carrier,
});
}
}
None
}
fn first_bearer_token(bundle: &GatewayCredentialBundle) -> Option<GatewayPrimaryCredential> {
credential_value(bundle, GatewayCredentialCarrier::AuthorizationBearer).map(|raw| {
GatewayPrimaryCredential::BearerToken {
raw,
carrier: GatewayCredentialCarrier::AuthorizationBearer,
}
})
}
fn select_cookie_credential(bundle: &GatewayCredentialBundle) -> Option<GatewayPrimaryCredential> {
credential_value(bundle, GatewayCredentialCarrier::CookieHeader).map(|raw| {
GatewayPrimaryCredential::CookieHeader {
raw,
carrier: GatewayCredentialCarrier::CookieHeader,
}
})
}
fn credential_value(
bundle: &GatewayCredentialBundle,
carrier: GatewayCredentialCarrier,
) -> Option<String> {
match carrier {
GatewayCredentialCarrier::AuthorizationBearer => bundle.authorization_bearer.clone(),
GatewayCredentialCarrier::XApiKey => bundle.x_api_key.clone(),
GatewayCredentialCarrier::ApiKey => bundle.api_key.clone(),
GatewayCredentialCarrier::XGoogApiKey => bundle.x_goog_api_key.clone(),
GatewayCredentialCarrier::QueryKey => bundle.query_key.clone(),
GatewayCredentialCarrier::CookieHeader => bundle.cookie_header.clone(),
}
}
fn extract_query_api_key(uri: &Uri) -> Option<String> {
let query = uri.query()?;
form_urlencoded::parse(query.as_bytes())
.find(|(key, value)| key == "key" && !value.trim().is_empty())
.map(|(_, value)| value.into_owned())
}
fn extract_bearer_token(value: &str) -> Option<&str> {
let trimmed = value.trim();
let (scheme, token) = trimmed.split_once(' ')?;
if !scheme.eq_ignore_ascii_case("bearer") {
return None;
}
let token = token.trim();
if token.is_empty() {
None
} else {
Some(token)
}
}
pub(super) fn hash_api_key(value: &str) -> String {
let mut hasher = Sha256::new();
hasher.update(value.as_bytes());
format!("{:x}", hasher.finalize())
}
pub(super) fn contains_string(items: &[String], target: &str) -> bool {
items
.iter()
.any(|item| item.trim().eq_ignore_ascii_case(target.trim()))
}
pub(super) fn parse_bool_header(value: &str) -> Option<bool> {
match value.trim().to_ascii_lowercase().as_str() {
"true" | "1" | "yes" => Some(true),
"false" | "0" | "no" => Some(false),
_ => None,
}
}
pub(super) fn parse_f64_header(value: &str) -> Option<f64> {
value.trim().parse::<f64>().ok()
}
pub(super) fn current_unix_secs() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs()
}
#[cfg(test)]
mod tests {
use super::*;
fn uri(path: &str) -> Uri {
path.parse().expect("uri should parse")
}
#[test]
fn selects_openai_bearer_as_provider_api_key() {
let mut headers = http::HeaderMap::new();
headers.insert(
http::header::AUTHORIZATION,
"Bearer sk-openai".parse().unwrap(),
);
let extracted =
extract_request_credentials(&headers, &uri("/v1/chat/completions"), "openai:chat");
assert_eq!(
extracted.primary,
Some(GatewayPrimaryCredential::ProviderApiKey {
raw: "sk-openai".to_string(),
carrier: GatewayCredentialCarrier::AuthorizationBearer,
})
);
}
#[test]
fn prefers_claude_chat_x_api_key_over_bearer() {
let mut headers = http::HeaderMap::new();
headers.insert(
http::header::AUTHORIZATION,
"Bearer cli-token".parse().unwrap(),
);
headers.insert("x-api-key", "claude-key".parse().unwrap());
let extracted = extract_request_credentials(&headers, &uri("/v1/messages"), "claude:chat");
assert_eq!(
extracted.primary,
Some(GatewayPrimaryCredential::ProviderApiKey {
raw: "claude-key".to_string(),
carrier: GatewayCredentialCarrier::XApiKey,
})
);
}
#[test]
fn selects_claude_cli_bearer_as_provider_api_key() {
let mut headers = http::HeaderMap::new();
headers.insert(
http::header::AUTHORIZATION,
"Bearer cli-token".parse().unwrap(),
);
let extracted = extract_request_credentials(&headers, &uri("/v1/messages"), "claude:cli");
assert_eq!(
extracted.primary,
Some(GatewayPrimaryCredential::ProviderApiKey {
raw: "cli-token".to_string(),
carrier: GatewayCredentialCarrier::AuthorizationBearer,
})
);
}
#[test]
fn prefers_gemini_query_key_over_header_key() {
let mut headers = http::HeaderMap::new();
headers.insert("x-goog-api-key", "gemini-header".parse().unwrap());
let extracted = extract_request_credentials(
&headers,
&uri("/v1beta/models?key=gemini-query"),
"gemini:chat",
);
assert_eq!(
extracted.primary,
Some(GatewayPrimaryCredential::ProviderApiKey {
raw: "gemini-query".to_string(),
carrier: GatewayCredentialCarrier::QueryKey,
})
);
}
#[test]
fn extracts_cookie_as_fallback_credential() {
let mut headers = http::HeaderMap::new();
headers.insert(http::header::COOKIE, "session=abc123".parse().unwrap());
let extracted =
extract_request_credentials(&headers, &uri("/v1/chat/completions"), "internal:session");
assert_eq!(
extracted.primary,
Some(GatewayPrimaryCredential::CookieHeader {
raw: "session=abc123".to_string(),
carrier: GatewayCredentialCarrier::CookieHeader,
})
);
}
#[test]
fn cache_key_includes_cookie_header() {
let mut headers = http::HeaderMap::new();
headers.insert(http::header::COOKIE, "session=abc123".parse().unwrap());
let cache_key = build_auth_context_cache_key(
&headers,
&uri("/v1/chat/completions"),
"internal:session",
)
.expect("cache key should exist");
assert!(cache_key.contains("session=abc123"));
}
#[test]
fn extracts_trusted_auth_headers() {
let mut headers = http::HeaderMap::new();
headers.insert(
crate::gateway::constants::GATEWAY_HEADER,
"rust-phase3b".parse().unwrap(),
);
headers.insert(
crate::gateway::constants::TRUSTED_AUTH_USER_ID_HEADER,
"user-1".parse().unwrap(),
);
headers.insert(
crate::gateway::constants::TRUSTED_AUTH_API_KEY_ID_HEADER,
"key-1".parse().unwrap(),
);
headers.insert(
crate::gateway::constants::TRUSTED_AUTH_BALANCE_HEADER,
"1.5".parse().unwrap(),
);
headers.insert(
crate::gateway::constants::TRUSTED_AUTH_ACCESS_ALLOWED_HEADER,
"true".parse().unwrap(),
);
let extracted =
extract_request_credentials(&headers, &uri("/v1/chat/completions"), "openai:chat");
assert_eq!(
extracted.trusted_headers,
Some(GatewayTrustedAuthHeaders {
user_id: "user-1".to_string(),
api_key_id: "key-1".to_string(),
balance_remaining: Some(1.5),
access_allowed: Some(true),
})
);
assert_eq!(extracted.trusted_admin_headers, None);
}
#[test]
fn ignores_trusted_auth_headers_without_gateway_marker() {
let mut headers = http::HeaderMap::new();
headers.insert(
crate::gateway::constants::TRUSTED_AUTH_USER_ID_HEADER,
"user-1".parse().unwrap(),
);
headers.insert(
crate::gateway::constants::TRUSTED_AUTH_API_KEY_ID_HEADER,
"key-1".parse().unwrap(),
);
let extracted =
extract_request_credentials(&headers, &uri("/v1/chat/completions"), "openai:chat");
assert_eq!(extracted.trusted_headers, None);
}
#[test]
fn extracts_trusted_admin_headers() {
let mut headers = http::HeaderMap::new();
headers.insert(
crate::gateway::constants::GATEWAY_HEADER,
"rust-phase3b".parse().unwrap(),
);
headers.insert(
crate::gateway::constants::TRUSTED_ADMIN_USER_ID_HEADER,
"admin-user-1".parse().unwrap(),
);
headers.insert(
crate::gateway::constants::TRUSTED_ADMIN_USER_ROLE_HEADER,
"admin".parse().unwrap(),
);
headers.insert(
crate::gateway::constants::TRUSTED_ADMIN_SESSION_ID_HEADER,
"sess-1".parse().unwrap(),
);
let extracted = extract_request_credentials(
&headers,
&uri("/api/admin/endpoints/health/api-formats"),
"admin:endpoints_health",
);
assert_eq!(
extracted.trusted_admin_headers,
Some(GatewayTrustedAdminHeaders {
user_id: "admin-user-1".to_string(),
user_role: "admin".to_string(),
session_id: Some("sess-1".to_string()),
management_token_id: None,
})
);
}
#[test]
fn ignores_trusted_admin_headers_without_gateway_marker() {
let mut headers = http::HeaderMap::new();
headers.insert(
crate::gateway::constants::TRUSTED_ADMIN_USER_ID_HEADER,
"admin-user-1".parse().unwrap(),
);
headers.insert(
crate::gateway::constants::TRUSTED_ADMIN_USER_ROLE_HEADER,
"admin".parse().unwrap(),
);
headers.insert(
crate::gateway::constants::TRUSTED_ADMIN_SESSION_ID_HEADER,
"sess-1".parse().unwrap(),
);
let extracted = extract_request_credentials(
&headers,
&uri("/api/admin/endpoints/health/api-formats"),
"admin:endpoints_health",
);
assert_eq!(extracted.trusted_admin_headers, None);
}
}

View File

@@ -0,0 +1,65 @@
use axum::body::Bytes;
use axum::http::Uri;
use super::super::GatewayControlDecision;
use super::credentials::{contains_string, extract_requested_model};
#[derive(Debug, Clone, PartialEq)]
pub(crate) enum GatewayLocalAuthRejection {
InvalidApiKey,
LockedApiKey,
WalletUnavailable,
BalanceDenied { remaining: Option<f64> },
ProviderNotAllowed { provider: String },
ApiFormatNotAllowed { api_format: String },
ModelNotAllowed { model: String },
}
pub(crate) fn trusted_auth_local_rejection(
decision: Option<&GatewayControlDecision>,
_headers: &http::HeaderMap,
) -> Option<GatewayLocalAuthRejection> {
let decision = decision?;
if decision.route_class.as_deref() != Some("ai_public") {
return None;
}
decision
.local_auth_rejection
.clone()
.or_else(|| decision.auth_context.as_ref()?.local_rejection.clone())
}
pub(crate) fn should_buffer_request_for_local_auth(
decision: Option<&GatewayControlDecision>,
headers: &http::HeaderMap,
) -> bool {
let Some(decision) = decision else {
return false;
};
decision.route_class.as_deref() == Some("ai_public")
&& decision.route_kind.as_deref() != Some("files")
&& crate::gateway::headers::is_json_request(headers)
}
pub(crate) fn request_model_local_rejection(
decision: Option<&GatewayControlDecision>,
uri: &Uri,
headers: &http::HeaderMap,
body: &Bytes,
) -> Option<GatewayLocalAuthRejection> {
let decision = decision?;
if decision.route_class.as_deref() != Some("ai_public") {
return None;
}
let auth_context = decision.auth_context.as_ref()?;
let allowed_models = auth_context.allowed_models.as_deref()?;
let requested_model = extract_requested_model(decision, uri, headers, body)?;
if contains_string(allowed_models, &requested_model) {
return None;
}
Some(GatewayLocalAuthRejection::ModelNotAllowed {
model: requested_model,
})
}

View File

@@ -0,0 +1,15 @@
mod credentials;
mod gate;
mod principal;
mod resolution;
mod types;
pub(crate) use credentials::extract_requested_model;
pub(crate) use gate::{
request_model_local_rejection, should_buffer_request_for_local_auth,
trusted_auth_local_rejection, GatewayLocalAuthRejection,
};
pub(super) use resolution::{resolve_control_decision_auth, ControlDecisionAuthResolution};
pub(crate) use resolution::{
resolve_execution_runtime_auth_context, GatewayAdminPrincipalContext, GatewayControlAuthContext,
};

View File

@@ -0,0 +1,110 @@
use super::credentials::hash_api_key;
use super::types::{
GatewayExtractedCredentials, GatewayPrimaryCredential, GatewayPrincipalCandidate,
};
pub(super) fn derive_principal_candidate(
credentials: &GatewayExtractedCredentials,
) -> Option<GatewayPrincipalCandidate> {
if let Some(trusted_headers) = credentials.trusted_headers.clone() {
return Some(GatewayPrincipalCandidate::TrustedHeaders(trusted_headers));
}
match credentials.primary.as_ref()? {
GatewayPrimaryCredential::ProviderApiKey { raw, carrier } => {
Some(GatewayPrincipalCandidate::ApiKeyHash {
key_hash: hash_api_key(raw),
carrier: *carrier,
})
}
GatewayPrimaryCredential::BearerToken { raw, carrier } => {
Some(GatewayPrincipalCandidate::DeferredBearerToken {
raw: raw.clone(),
carrier: *carrier,
})
}
GatewayPrimaryCredential::CookieHeader { raw, carrier } => {
Some(GatewayPrincipalCandidate::DeferredCookieHeader {
raw: raw.clone(),
carrier: *carrier,
})
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::gateway::control::auth::types::{
GatewayCredentialBundle, GatewayCredentialCarrier, GatewayExtractedCredentials,
GatewayPrimaryCredential, GatewayTrustedAuthHeaders,
};
#[test]
fn derives_trusted_headers_candidate_before_primary_credential() {
let candidate = derive_principal_candidate(&GatewayExtractedCredentials {
trusted_headers: Some(GatewayTrustedAuthHeaders {
user_id: "user-1".to_string(),
api_key_id: "key-1".to_string(),
balance_remaining: Some(1.5),
access_allowed: Some(true),
}),
trusted_admin_headers: None,
bundle: GatewayCredentialBundle::default(),
primary: Some(GatewayPrimaryCredential::ProviderApiKey {
raw: "sk-test".to_string(),
carrier: GatewayCredentialCarrier::AuthorizationBearer,
}),
});
assert!(matches!(
candidate,
Some(GatewayPrincipalCandidate::TrustedHeaders(_))
));
}
#[test]
fn derives_api_key_hash_candidate_for_provider_api_key() {
let candidate = derive_principal_candidate(&GatewayExtractedCredentials {
trusted_headers: None,
trusted_admin_headers: None,
bundle: GatewayCredentialBundle::default(),
primary: Some(GatewayPrimaryCredential::ProviderApiKey {
raw: "sk-test".to_string(),
carrier: GatewayCredentialCarrier::XApiKey,
}),
});
match candidate {
Some(GatewayPrincipalCandidate::ApiKeyHash { key_hash, carrier }) => {
assert_eq!(
key_hash,
"f3abf2a6cc4f00987743db5f544ba345b4899ae31f326d8ee9c4816de153c9e0"
);
assert_eq!(carrier, GatewayCredentialCarrier::XApiKey);
}
other => panic!("unexpected candidate: {other:?}"),
}
}
#[test]
fn derives_deferred_cookie_candidate_for_cookie_credentials() {
let candidate = derive_principal_candidate(&GatewayExtractedCredentials {
trusted_headers: None,
trusted_admin_headers: None,
bundle: GatewayCredentialBundle::default(),
primary: Some(GatewayPrimaryCredential::CookieHeader {
raw: "session=test".to_string(),
carrier: GatewayCredentialCarrier::CookieHeader,
}),
});
assert_eq!(
candidate,
Some(GatewayPrincipalCandidate::DeferredCookieHeader {
raw: "session=test".to_string(),
carrier: GatewayCredentialCarrier::CookieHeader,
})
);
}
}

View File

@@ -0,0 +1,631 @@
use std::time::Duration;
use axum::http::Uri;
use base64::Engine as _;
use hmac::Mac;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::gateway::{AppState, GatewayError};
use super::super::GatewayControlDecision;
use super::credentials::{
build_auth_context_cache_key, contains_string, current_unix_secs, extract_request_credentials,
extract_trusted_admin_headers,
};
use super::gate::GatewayLocalAuthRejection;
use super::principal::derive_principal_candidate;
use super::types::{GatewayPrincipalCandidate, GatewayTrustedAuthHeaders};
use crate::gateway::headers::header_value_str;
use crate::gateway::{local_rejection_from_wallet_access, resolve_wallet_auth_gate};
const AUTH_CONTEXT_CACHE_TTL: Duration = Duration::from_secs(60);
const AUTH_CONTEXT_CACHE_MAX_ENTRIES: usize = 256;
#[derive(Debug, Clone, Deserialize, Serialize)]
pub(crate) struct GatewayControlAuthContext {
pub(crate) user_id: String,
pub(crate) api_key_id: String,
pub(crate) balance_remaining: Option<f64>,
pub(crate) access_allowed: bool,
#[serde(skip)]
pub(crate) user_rate_limit: Option<i32>,
#[serde(skip)]
pub(crate) api_key_rate_limit: Option<i32>,
#[serde(skip)]
pub(crate) api_key_is_standalone: bool,
#[serde(skip)]
pub(crate) local_rejection: Option<GatewayLocalAuthRejection>,
#[serde(skip)]
pub(crate) allowed_models: Option<Vec<String>>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct GatewayAdminPrincipalContext {
pub(crate) user_id: String,
pub(crate) user_role: String,
pub(crate) session_id: Option<String>,
pub(crate) management_token_id: Option<String>,
}
pub(in super::super) enum ControlDecisionAuthResolution {
Resolved(GatewayControlDecision),
}
pub(in super::super) async fn resolve_control_decision_auth(
state: &AppState,
headers: &http::HeaderMap,
uri: &Uri,
mut decision: GatewayControlDecision,
) -> Result<ControlDecisionAuthResolution, GatewayError> {
if let Some(admin_principal) =
resolve_trusted_admin_principal(headers, decision.auth_endpoint_signature.as_deref())
{
decision.admin_principal = Some(admin_principal);
} else if let Some(admin_principal) = resolve_local_admin_principal(
state,
headers,
uri,
decision.auth_endpoint_signature.as_deref(),
)
.await?
{
decision.admin_principal = Some(admin_principal);
}
if let Some(auth_context) = resolve_data_backed_auth_context(
state,
headers,
uri,
decision.auth_endpoint_signature.as_deref(),
)
.await?
{
decision.local_auth_rejection = auth_context.local_rejection.clone();
if !auth_context.user_id.is_empty() && !auth_context.api_key_id.is_empty() {
if let Some(cache_key) = decision
.auth_endpoint_signature
.as_deref()
.and_then(|signature| build_auth_context_cache_key(headers, uri, signature))
{
put_cached_auth_context(state, cache_key, auth_context.clone());
}
decision.auth_context = Some(auth_context);
}
}
if decision.local_auth_rejection.is_some() {
return Ok(ControlDecisionAuthResolution::Resolved(decision));
}
if decision.is_execution_runtime_candidate() {
return Ok(ControlDecisionAuthResolution::Resolved(decision));
}
if decision.auth_context.is_some() {
return Ok(ControlDecisionAuthResolution::Resolved(decision));
}
if skips_legacy_python_auth_context(&decision) {
return Ok(ControlDecisionAuthResolution::Resolved(decision));
}
Ok(ControlDecisionAuthResolution::Resolved(decision))
}
fn skips_legacy_python_auth_context(decision: &GatewayControlDecision) -> bool {
matches!(
decision.route_kind.as_deref(),
Some("chat" | "cli" | "compact")
)
}
fn resolve_trusted_admin_principal(
headers: &http::HeaderMap,
auth_endpoint_signature: Option<&str>,
) -> Option<GatewayAdminPrincipalContext> {
if !auth_endpoint_signature
.map(str::trim)
.unwrap_or_default()
.starts_with("admin:")
{
return None;
}
let trusted_headers = extract_trusted_admin_headers(headers)?;
Some(GatewayAdminPrincipalContext {
user_id: trusted_headers.user_id,
user_role: trusted_headers.user_role,
session_id: trusted_headers.session_id,
management_token_id: trusted_headers.management_token_id,
})
}
async fn resolve_local_admin_principal(
state: &AppState,
headers: &http::HeaderMap,
uri: &Uri,
auth_endpoint_signature: Option<&str>,
) -> Result<Option<GatewayAdminPrincipalContext>, GatewayError> {
let Some(signature) = auth_endpoint_signature
.map(str::trim)
.filter(|value| value.starts_with("admin:"))
else {
return Ok(None);
};
let extracted = extract_request_credentials(headers, uri, signature);
let Some(access_token) = extracted.bundle.authorization_bearer.as_deref() else {
return Ok(None);
};
let claims = match decode_local_auth_token(access_token, "access") {
Ok(claims) => claims,
Err(_) => return Ok(None),
};
if claims
.get("role")
.and_then(Value::as_str)
.is_some_and(|role| !role.eq_ignore_ascii_case("admin"))
{
return Ok(None);
}
resolve_local_admin_principal_from_claims(state, headers, uri, &claims).await
}
async fn resolve_local_admin_principal_from_claims(
state: &AppState,
headers: &http::HeaderMap,
uri: &Uri,
claims: &serde_json::Map<String, Value>,
) -> Result<Option<GatewayAdminPrincipalContext>, GatewayError> {
let Some(user_id) = claims.get("user_id").and_then(Value::as_str) else {
return Ok(None);
};
let Some(session_id) = claims.get("session_id").and_then(Value::as_str) else {
return Ok(None);
};
let Some(client_device_id) = extract_local_admin_client_device_id(headers, uri) else {
return Ok(None);
};
let Some(user) = state.find_user_auth_by_id(user_id).await? else {
return Ok(None);
};
if !user.is_active || user.is_deleted || !user.role.eq_ignore_ascii_case("admin") {
return Ok(None);
}
let now = chrono::Utc::now();
let Some(session) = state.find_user_session(user_id, session_id).await? else {
return Ok(None);
};
if session.is_revoked()
|| session.is_expired(now)
|| session.client_device_id != client_device_id
{
return Ok(None);
}
if session.should_touch(now) {
let _ = state
.touch_user_session(
user_id,
session_id,
now,
None,
local_admin_user_agent(headers).as_deref(),
)
.await;
}
Ok(Some(GatewayAdminPrincipalContext {
user_id: user.id,
user_role: "admin".to_string(),
session_id: Some(session.id),
management_token_id: None,
}))
}
fn extract_local_admin_client_device_id(headers: &http::HeaderMap, uri: &Uri) -> Option<String> {
let header_value = header_value_str(headers, "x-client-device-id");
let query_value = uri.query().and_then(|query| {
url::form_urlencoded::parse(query.as_bytes())
.find(|(key, _)| key == "client_device_id")
.map(|(_, value)| value.into_owned())
});
let candidate = header_value.or(query_value)?;
let candidate = candidate.trim();
if candidate.is_empty()
|| candidate.len() > 128
|| !candidate
.chars()
.all(|ch| ch.is_ascii_alphanumeric() || ch == '-' || ch == '_')
{
return None;
}
Some(candidate.to_string())
}
fn local_admin_user_agent(headers: &http::HeaderMap) -> Option<String> {
header_value_str(headers, http::header::USER_AGENT.as_str())
.map(|value| value.chars().take(1000).collect())
}
fn local_auth_secret() -> String {
std::env::var("JWT_SECRET_KEY")
.ok()
.map(|value| value.trim().to_string())
.filter(|value| !value.is_empty())
.unwrap_or_else(|| "aether-rust-dev-jwt-secret".to_string())
}
fn decode_local_auth_token(
token: &str,
expected_type: &str,
) -> Result<serde_json::Map<String, Value>, String> {
let mut parts = token.split('.');
let Some(header_segment) = parts.next() else {
return Err("invalid token".to_string());
};
let Some(payload_segment) = parts.next() else {
return Err("invalid token".to_string());
};
let Some(signature_segment) = parts.next() else {
return Err("invalid token".to_string());
};
if parts.next().is_some() {
return Err("invalid token".to_string());
}
let signing_input = format!("{header_segment}.{payload_segment}");
let signature = base64::engine::general_purpose::URL_SAFE_NO_PAD
.decode(signature_segment)
.map_err(|_| "invalid token".to_string())?;
let mut mac = hmac::Hmac::<sha2::Sha256>::new_from_slice(local_auth_secret().as_bytes())
.map_err(|_| "invalid token".to_string())?;
mac.update(signing_input.as_bytes());
mac.verify_slice(&signature)
.map_err(|_| "invalid token".to_string())?;
let payload_bytes = base64::engine::general_purpose::URL_SAFE_NO_PAD
.decode(payload_segment)
.map_err(|_| "invalid token".to_string())?;
let payload =
serde_json::from_slice::<Value>(&payload_bytes).map_err(|_| "invalid token".to_string())?;
let payload = payload
.as_object()
.cloned()
.ok_or_else(|| "invalid token".to_string())?;
let actual_type = payload
.get("type")
.and_then(Value::as_str)
.unwrap_or_default();
if actual_type != expected_type {
return Err("invalid token".to_string());
}
let exp = payload
.get("exp")
.and_then(Value::as_i64)
.ok_or_else(|| "invalid token".to_string())?;
if exp <= chrono::Utc::now().timestamp() {
return Err("expired token".to_string());
}
Ok(payload)
}
pub(crate) async fn resolve_execution_runtime_auth_context(
state: &AppState,
decision: &GatewayControlDecision,
headers: &http::HeaderMap,
uri: &Uri,
trace_id: &str,
) -> Result<Option<GatewayControlAuthContext>, GatewayError> {
let _ = trace_id;
if let Some(auth_context) = decision.auth_context.clone() {
return Ok(Some(auth_context));
}
let Some(auth_endpoint_signature) = decision.auth_endpoint_signature.as_deref() else {
return Ok(None);
};
let Some(cache_key) = build_auth_context_cache_key(headers, uri, auth_endpoint_signature)
else {
return Ok(None);
};
if let Some(auth_context) = get_cached_auth_context(state, &cache_key) {
return Ok(Some(auth_context));
}
if let Some(auth_context) =
resolve_data_backed_auth_context(state, headers, uri, Some(auth_endpoint_signature)).await?
{
if auth_context.user_id.is_empty() || auth_context.api_key_id.is_empty() {
return Ok(None);
}
put_cached_auth_context(state, cache_key, auth_context.clone());
return Ok(Some(auth_context));
}
Ok(None)
}
fn put_cached_auth_context(
state: &AppState,
cache_key: String,
auth_context: GatewayControlAuthContext,
) {
state.auth_context_cache.insert(
cache_key,
auth_context,
AUTH_CONTEXT_CACHE_TTL,
AUTH_CONTEXT_CACHE_MAX_ENTRIES,
);
}
pub(super) async fn resolve_data_backed_auth_context(
state: &AppState,
headers: &http::HeaderMap,
uri: &Uri,
auth_endpoint_signature: Option<&str>,
) -> Result<Option<GatewayControlAuthContext>, GatewayError> {
let Some(signature) = auth_endpoint_signature
.map(str::trim)
.filter(|value| !value.is_empty())
else {
return Ok(None);
};
if !state.has_auth_api_key_reader() {
return Ok(None);
}
let extracted = extract_request_credentials(headers, uri, signature);
let principal = derive_principal_candidate(&extracted);
let now_unix_secs = current_unix_secs();
match principal {
Some(GatewayPrincipalCandidate::TrustedHeaders(trusted_headers)) => {
resolve_trusted_auth_context(state, signature, trusted_headers, now_unix_secs).await
}
Some(GatewayPrincipalCandidate::ApiKeyHash { key_hash, .. }) => {
let snapshot = state
.read_auth_api_key_snapshot_by_key_hash(&key_hash, now_unix_secs)
.await?;
let Some(snapshot) = snapshot else {
return Ok(Some(GatewayControlAuthContext {
user_id: String::new(),
api_key_id: String::new(),
balance_remaining: None,
access_allowed: false,
user_rate_limit: None,
api_key_rate_limit: None,
api_key_is_standalone: false,
local_rejection: Some(GatewayLocalAuthRejection::InvalidApiKey),
allowed_models: None,
}));
};
state
.touch_auth_api_key_last_used_best_effort(&snapshot.api_key_id)
.await;
let wallet_access = resolve_wallet_auth_gate(state, &snapshot).await?;
Ok(Some(build_data_backed_auth_context(
snapshot,
signature,
None,
None,
wallet_access,
)))
}
Some(
GatewayPrincipalCandidate::DeferredBearerToken { .. }
| GatewayPrincipalCandidate::DeferredCookieHeader { .. },
) => Ok(None),
None => Ok(None),
}
}
async fn resolve_trusted_auth_context(
state: &AppState,
auth_endpoint_signature: &str,
trusted_headers: GatewayTrustedAuthHeaders,
now_unix_secs: u64,
) -> Result<Option<GatewayControlAuthContext>, GatewayError> {
let snapshot = state
.read_auth_api_key_snapshot(
&trusted_headers.user_id,
&trusted_headers.api_key_id,
now_unix_secs,
)
.await?;
let Some(snapshot) = snapshot else {
return Ok(Some(GatewayControlAuthContext {
user_id: trusted_headers.user_id,
api_key_id: trusted_headers.api_key_id,
balance_remaining: trusted_headers.balance_remaining,
access_allowed: false,
user_rate_limit: None,
api_key_rate_limit: None,
api_key_is_standalone: false,
local_rejection: Some(GatewayLocalAuthRejection::InvalidApiKey),
allowed_models: None,
}));
};
let wallet_access = resolve_wallet_auth_gate(state, &snapshot).await?;
Ok(Some(build_data_backed_auth_context(
snapshot,
auth_endpoint_signature,
trusted_headers.access_allowed,
trusted_headers.balance_remaining,
wallet_access,
)))
}
fn build_data_backed_auth_context(
snapshot: crate::gateway::gateway_data::StoredGatewayAuthApiKeySnapshot,
auth_endpoint_signature: &str,
header_access_allowed: Option<bool>,
balance_remaining: Option<f64>,
wallet_access: Option<aether_wallet::WalletAccessDecision>,
) -> GatewayControlAuthContext {
let allowed_models = snapshot
.effective_allowed_models()
.map(|items| items.to_vec());
let invalid_api_key = !snapshot.user_is_active
|| snapshot.user_is_deleted
|| !snapshot.api_key_is_active
|| snapshot
.api_key_expires_at_unix_secs
.is_some_and(|expires_at| expires_at < current_unix_secs());
let locked_api_key = snapshot.api_key_is_locked && !snapshot.api_key_is_standalone;
let access_allowed = header_access_allowed
.map(|value| value && snapshot.currently_usable)
.unwrap_or(snapshot.currently_usable);
let wallet_remaining = wallet_access
.as_ref()
.and_then(|decision| decision.remaining);
let requested_provider = auth_endpoint_signature
.split_once(':')
.map(|(provider, _)| provider)
.unwrap_or(auth_endpoint_signature)
.trim();
let local_rejection = if invalid_api_key {
Some(GatewayLocalAuthRejection::InvalidApiKey)
} else if locked_api_key {
Some(GatewayLocalAuthRejection::LockedApiKey)
} else if let Some(rejection) = wallet_access
.as_ref()
.and_then(local_rejection_from_wallet_access)
{
Some(rejection)
} else if header_access_allowed.is_some_and(|value| !value) && snapshot.currently_usable {
Some(GatewayLocalAuthRejection::BalanceDenied {
remaining: balance_remaining.or(wallet_remaining),
})
} else if !requested_provider.is_empty()
&& snapshot
.effective_allowed_providers()
.is_some_and(|allowed| !contains_string(allowed, requested_provider))
{
Some(GatewayLocalAuthRejection::ProviderNotAllowed {
provider: requested_provider.to_string(),
})
} else if snapshot
.effective_allowed_api_formats()
.is_some_and(|allowed| !contains_string(allowed, auth_endpoint_signature))
{
Some(GatewayLocalAuthRejection::ApiFormatNotAllowed {
api_format: auth_endpoint_signature.to_string(),
})
} else {
None
};
GatewayControlAuthContext {
user_id: snapshot.user_id,
api_key_id: snapshot.api_key_id,
balance_remaining: wallet_remaining.or(balance_remaining),
access_allowed,
user_rate_limit: snapshot.user_rate_limit,
api_key_rate_limit: snapshot.api_key_rate_limit,
api_key_is_standalone: snapshot.api_key_is_standalone,
local_rejection,
allowed_models,
}
}
fn get_cached_auth_context(state: &AppState, cache_key: &str) -> Option<GatewayControlAuthContext> {
state
.auth_context_cache
.get_fresh(cache_key, AUTH_CONTEXT_CACHE_TTL)
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use aether_data::repository::auth::{
InMemoryAuthApiKeySnapshotRepository, StoredAuthApiKeySnapshot,
};
use axum::http::{HeaderMap, Uri};
use super::resolve_data_backed_auth_context;
use crate::gateway::control::auth::credentials::hash_api_key;
use crate::gateway::{AppState, GatewayDataState};
fn sample_snapshot(api_key_id: &str, user_id: &str) -> StoredAuthApiKeySnapshot {
StoredAuthApiKeySnapshot::new(
user_id.to_string(),
"alice".to_string(),
Some("alice@example.com".to_string()),
"user".to_string(),
"local".to_string(),
true,
false,
Some(serde_json::json!(["openai"])),
Some(serde_json::json!(["openai:chat"])),
Some(serde_json::json!(["gpt-4.1"])),
api_key_id.to_string(),
Some("default".to_string()),
true,
false,
false,
Some(60),
Some(5),
Some(4_102_444_800),
Some(serde_json::json!(["openai"])),
Some(serde_json::json!(["openai:chat"])),
Some(serde_json::json!(["gpt-4.1"])),
)
.expect("snapshot should build")
}
fn uri(path: &str) -> Uri {
path.parse().expect("uri should parse")
}
#[tokio::test]
async fn data_backed_api_key_auth_touches_last_used_once_per_throttle_window() {
let api_key = "sk-test-touch";
let repository = Arc::new(InMemoryAuthApiKeySnapshotRepository::seed(vec![(
Some(hash_api_key(api_key)),
sample_snapshot("key-1", "user-1"),
)]));
let data = GatewayDataState::with_auth_api_key_repository_for_tests(repository.clone());
let state = AppState::new("http://127.0.0.1:9")
.expect("state should build")
.with_data_state_for_tests(data);
let mut headers = HeaderMap::new();
headers.insert(
http::header::AUTHORIZATION,
format!("Bearer {api_key}").parse().unwrap(),
);
let first = resolve_data_backed_auth_context(
&state,
&headers,
&uri("/v1/chat/completions"),
Some("openai:chat"),
)
.await
.expect("resolution should succeed")
.expect("auth context should exist");
assert_eq!(first.user_id, "user-1");
assert_eq!(first.api_key_id, "key-1");
assert_eq!(repository.touch_count("key-1"), 1);
let second = resolve_data_backed_auth_context(
&state,
&headers,
&uri("/v1/chat/completions"),
Some("openai:chat"),
)
.await
.expect("resolution should succeed")
.expect("auth context should exist");
assert_eq!(second.api_key_id, "key-1");
assert_eq!(repository.touch_count("key-1"), 1);
}
}

View File

@@ -0,0 +1,76 @@
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum GatewayCredentialCarrier {
AuthorizationBearer,
XApiKey,
ApiKey,
XGoogApiKey,
QueryKey,
CookieHeader,
}
#[derive(Debug, Clone, PartialEq)]
pub(super) struct GatewayTrustedAuthHeaders {
pub(super) user_id: String,
pub(super) api_key_id: String,
pub(super) balance_remaining: Option<f64>,
pub(super) access_allowed: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) struct GatewayTrustedAdminHeaders {
pub(super) user_id: String,
pub(super) user_role: String,
pub(super) session_id: Option<String>,
pub(super) management_token_id: Option<String>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(super) struct GatewayCredentialBundle {
pub(super) authorization_bearer: Option<String>,
pub(super) x_api_key: Option<String>,
pub(super) api_key: Option<String>,
pub(super) x_goog_api_key: Option<String>,
pub(super) query_key: Option<String>,
pub(super) cookie_header: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) enum GatewayPrimaryCredential {
ProviderApiKey {
raw: String,
carrier: GatewayCredentialCarrier,
},
BearerToken {
raw: String,
carrier: GatewayCredentialCarrier,
},
CookieHeader {
raw: String,
carrier: GatewayCredentialCarrier,
},
}
#[derive(Debug, Clone, PartialEq)]
pub(super) struct GatewayExtractedCredentials {
pub(super) trusted_headers: Option<GatewayTrustedAuthHeaders>,
pub(super) trusted_admin_headers: Option<GatewayTrustedAdminHeaders>,
pub(super) bundle: GatewayCredentialBundle,
pub(super) primary: Option<GatewayPrimaryCredential>,
}
#[derive(Debug, Clone, PartialEq)]
pub(super) enum GatewayPrincipalCandidate {
TrustedHeaders(GatewayTrustedAuthHeaders),
ApiKeyHash {
key_hash: String,
carrier: GatewayCredentialCarrier,
},
DeferredBearerToken {
raw: String,
carrier: GatewayCredentialCarrier,
},
DeferredCookieHeader {
raw: String,
carrier: GatewayCredentialCarrier,
},
}

View File

@@ -0,0 +1,68 @@
use axum::body::{Body, Bytes};
use axum::http::{HeaderName, HeaderValue, Response};
use crate::gateway::constants::CONTROL_EXECUTED_HEADER;
use crate::gateway::{AppState, GatewayControlDecision, GatewayError};
use super::resolve_execution_runtime_auth_context;
pub(crate) fn allows_control_execute_emergency(decision: &GatewayControlDecision) -> bool {
decision.is_execution_runtime_candidate()
}
pub(crate) async fn maybe_execute_via_control(
state: &AppState,
parts: &http::request::Parts,
body_bytes: Bytes,
trace_id: &str,
decision: Option<&GatewayControlDecision>,
require_stream: bool,
) -> Result<Option<Response<Body>>, GatewayError> {
let Some(decision) = decision else {
return Ok(None);
};
let mut local_decision = decision.clone();
if let Some(auth_context) = resolve_execution_runtime_auth_context(
state,
&local_decision,
&parts.headers,
&parts.uri,
trace_id,
)
.await?
{
local_decision.auth_context = Some(auth_context);
local_decision.local_auth_rejection = None;
}
let response = if require_stream {
crate::gateway::maybe_execute_via_execution_runtime_stream(
state,
parts,
&body_bytes,
trace_id,
Some(&local_decision),
)
.await?
} else {
crate::gateway::maybe_execute_via_execution_runtime_sync(
state,
parts,
&body_bytes,
trace_id,
Some(&local_decision),
)
.await?
};
Ok(response.map(mark_control_executed))
}
fn mark_control_executed(mut response: Response<Body>) -> Response<Body> {
response.headers_mut().insert(
HeaderName::from_static(CONTROL_EXECUTED_HEADER),
HeaderValue::from_static("true"),
);
response
}

View File

@@ -0,0 +1,25 @@
#[cfg(test)]
use axum::http::Uri;
#[path = "auth/mod.rs"]
mod auth;
#[path = "execute.rs"]
mod execute;
#[path = "public.rs"]
mod public;
#[path = "route.rs"]
mod route;
pub(crate) use auth::{
extract_requested_model, request_model_local_rejection, resolve_execution_runtime_auth_context,
should_buffer_request_for_local_auth, trusted_auth_local_rejection,
GatewayAdminPrincipalContext, GatewayControlAuthContext, GatewayLocalAuthRejection,
};
pub(crate) use execute::{allows_control_execute_emergency, maybe_execute_via_control};
pub(crate) use public::{resolve_public_request_context, GatewayPublicRequestContext};
#[cfg(test)]
pub(crate) use route::classify_control_route;
pub(crate) use route::{resolve_control_route, GatewayControlDecision};
#[cfg(test)]
mod tests;

View File

@@ -0,0 +1,73 @@
use axum::http::Uri;
use crate::gateway::headers::header_value_str;
use crate::gateway::{AppState, GatewayError};
use super::{resolve_control_route, GatewayControlDecision};
#[derive(Debug, Clone)]
pub(crate) struct GatewayPublicRequestContext {
pub(crate) trace_id: String,
pub(crate) request_method: http::Method,
pub(crate) request_path: String,
pub(crate) request_query_string: Option<String>,
pub(crate) request_content_type: Option<String>,
pub(crate) host_header: Option<String>,
pub(crate) control_decision: Option<GatewayControlDecision>,
}
impl GatewayPublicRequestContext {
pub(crate) fn from_request_parts(
trace_id: impl Into<String>,
method: &http::Method,
uri: &Uri,
headers: &http::HeaderMap,
control_decision: Option<GatewayControlDecision>,
) -> Self {
let request_path = if uri.path().starts_with('/') {
uri.path().to_string()
} else {
format!("/{}", uri.path())
};
let request_query_string = uri.query().map(ToOwned::to_owned);
Self {
trace_id: trace_id.into(),
request_method: method.clone(),
request_path,
request_query_string,
request_content_type: header_value_str(headers, http::header::CONTENT_TYPE.as_str()),
host_header: header_value_str(headers, http::header::HOST.as_str()),
control_decision,
}
}
pub(crate) fn request_path_and_query(&self) -> String {
if let Some(query) = self
.request_query_string
.as_deref()
.filter(|value| !value.is_empty())
{
format!("{}?{query}", self.request_path)
} else {
self.request_path.clone()
}
}
}
pub(crate) async fn resolve_public_request_context(
state: &AppState,
method: &http::Method,
uri: &Uri,
headers: &http::HeaderMap,
trace_id: &str,
) -> Result<GatewayPublicRequestContext, GatewayError> {
let control_decision = resolve_control_route(state, method, uri, headers, trace_id).await?;
Ok(GatewayPublicRequestContext::from_request_parts(
trace_id,
method,
uri,
headers,
control_decision,
))
}

View File

@@ -0,0 +1,224 @@
use axum::http::Uri;
use crate::gateway::headers::header_value_str;
use crate::gateway::{AppState, GatewayError};
#[path = "route/admin.rs"]
mod admin;
#[path = "route/ai.rs"]
mod ai;
#[path = "route/internal.rs"]
mod internal;
#[path = "route/oauth.rs"]
mod oauth;
#[path = "route/public_support.rs"]
mod public_support;
use super::auth::{resolve_control_decision_auth, ControlDecisionAuthResolution};
use super::{GatewayAdminPrincipalContext, GatewayControlAuthContext, GatewayLocalAuthRejection};
#[derive(Debug, Clone)]
pub(crate) struct GatewayControlDecision {
pub(crate) public_path: String,
pub(crate) public_query_string: Option<String>,
pub(crate) route_class: Option<String>,
pub(crate) route_family: Option<String>,
pub(crate) route_kind: Option<String>,
pub(crate) auth_endpoint_signature: Option<String>,
pub(crate) execution_runtime_candidate: bool,
pub(crate) auth_context: Option<GatewayControlAuthContext>,
pub(crate) admin_principal: Option<GatewayAdminPrincipalContext>,
pub(crate) local_auth_rejection: Option<GatewayLocalAuthRejection>,
}
impl GatewayControlDecision {
pub(crate) fn synthetic(
public_path: impl Into<String>,
route_class: Option<String>,
route_family: Option<String>,
route_kind: Option<String>,
auth_endpoint_signature: Option<String>,
) -> Self {
Self {
public_path: public_path.into(),
public_query_string: None,
route_class,
route_family,
route_kind,
auth_endpoint_signature,
execution_runtime_candidate: false,
auth_context: None,
admin_principal: None,
local_auth_rejection: None,
}
}
pub(crate) fn proxy_path_and_query(&self) -> String {
if let Some(query) = self
.public_query_string
.as_deref()
.filter(|value| !value.is_empty())
{
format!("{}?{}", self.public_path, query)
} else {
self.public_path.clone()
}
}
pub(crate) fn is_execution_runtime_candidate(&self) -> bool {
self.execution_runtime_candidate
}
pub(crate) fn with_execution_runtime_candidate(mut self, value: bool) -> Self {
self.execution_runtime_candidate = value;
self
}
}
#[derive(Debug, Clone)]
pub(super) struct ClassifiedRoute {
route_class: &'static str,
route_family: &'static str,
route_kind: &'static str,
auth_endpoint_signature: String,
execution_runtime_candidate: bool,
}
pub(super) fn classified(
route_class: &'static str,
route_family: &'static str,
route_kind: &'static str,
auth_endpoint_signature: impl Into<String>,
execution_runtime_candidate: bool,
) -> ClassifiedRoute {
ClassifiedRoute {
route_class,
route_family,
route_kind,
auth_endpoint_signature: auth_endpoint_signature.into(),
execution_runtime_candidate,
}
}
impl ClassifiedRoute {
fn into_decision(self, public_path: String) -> GatewayControlDecision {
GatewayControlDecision {
public_path,
public_query_string: None,
route_class: Some(self.route_class.to_string()),
route_family: Some(self.route_family.to_string()),
route_kind: Some(self.route_kind.to_string()),
auth_endpoint_signature: Some(self.auth_endpoint_signature),
execution_runtime_candidate: self.execution_runtime_candidate,
auth_context: None,
admin_principal: None,
local_auth_rejection: None,
}
}
}
pub(crate) async fn resolve_control_route(
state: &AppState,
method: &http::Method,
uri: &Uri,
headers: &http::HeaderMap,
_trace_id: &str,
) -> Result<Option<GatewayControlDecision>, GatewayError> {
let Some(mut decision) = classify_control_route(method, uri, headers) else {
return Ok(None);
};
decision.public_query_string = uri.query().map(ToOwned::to_owned);
match resolve_control_decision_auth(state, headers, uri, decision).await? {
ControlDecisionAuthResolution::Resolved(decision) => Ok(Some(decision)),
}
}
pub(crate) fn classify_control_route(
method: &http::Method,
uri: &Uri,
headers: &http::HeaderMap,
) -> Option<GatewayControlDecision> {
let path = uri.path();
let normalized_path = if path.starts_with('/') {
path.to_string()
} else {
format!("/{path}")
};
let public_models_auth_signature = detect_public_models_auth_signature(uri, headers);
let classified = public_support::classify_public_support_route(
method,
&normalized_path,
&public_models_auth_signature,
)
.or_else(|| oauth::classify_oauth_route(method, &normalized_path))
.or_else(|| admin::classify_admin_route(method, &normalized_path))
.or_else(|| internal::classify_internal_route(method, &normalized_path))
.or_else(|| ai::classify_ai_public_route(method, &normalized_path, headers))?;
Some(classified.into_decision(normalized_path))
}
pub(super) fn detect_public_models_auth_signature(uri: &Uri, headers: &http::HeaderMap) -> String {
let has_claude_key = header_value_str(headers, "x-api-key")
.or_else(|| header_value_str(headers, "api-key"))
.is_some();
let has_anthropic_version = header_value_str(headers, "anthropic-version").is_some();
if has_claude_key && has_anthropic_version {
return "claude:chat".to_string();
}
let has_gemini_key = header_value_str(headers, "x-goog-api-key").is_some()
|| uri.query().is_some_and(|query| {
url::form_urlencoded::parse(query.as_bytes())
.any(|(key, value)| key == "key" && !value.trim().is_empty())
});
if has_gemini_key {
return "gemini:chat".to_string();
}
if uri.path().starts_with("/v1beta/models") {
return "gemini:chat".to_string();
}
"openai:chat".to_string()
}
pub(super) fn is_claude_cli_request(headers: &http::HeaderMap) -> bool {
let auth_header = header_value_str(headers, http::header::AUTHORIZATION.as_str())
.unwrap_or_default()
.to_ascii_lowercase();
let has_bearer = auth_header.starts_with("bearer ");
let has_api_key =
header_value_str(headers, "x-api-key").is_some_and(|value| !value.trim().is_empty());
has_bearer && !has_api_key
}
pub(super) fn is_gemini_cli_request(headers: &http::HeaderMap) -> bool {
let x_app = header_value_str(headers, "x-app")
.unwrap_or_default()
.to_ascii_lowercase();
if x_app.contains("cli") {
return true;
}
let user_agent = header_value_str(headers, http::header::USER_AGENT.as_str())
.unwrap_or_default()
.to_ascii_lowercase();
user_agent.contains("geminicli") || user_agent.contains("gemini-cli")
}
pub(super) fn is_gemini_models_route(path: &str) -> bool {
(path.starts_with("/v1/models/") || path.starts_with("/v1beta/models/"))
&& (path.contains(":generateContent")
|| path.contains(":streamGenerateContent")
|| path.contains(":predictLongRunning"))
}
pub(super) fn is_gemini_operation_route(path: &str) -> bool {
(path.starts_with("/v1beta/models/") && path.contains("/operations/"))
|| path == "/v1beta/operations"
|| path.starts_with("/v1beta/operations/")
}

View File

@@ -0,0 +1,78 @@
use super::*;
#[path = "admin/basic_families.rs"]
mod basic_families;
#[path = "admin/endpoints_families.rs"]
mod endpoints_families;
#[path = "admin/model_provider_families.rs"]
mod model_provider_families;
#[path = "admin/observability_families.rs"]
mod observability_families;
#[path = "admin/operations_families.rs"]
mod operations_families;
#[path = "admin/provider_ops_routes.rs"]
mod provider_ops_routes;
#[path = "admin/system_families.rs"]
mod system_families;
use basic_families::classify_admin_basic_family_route;
use endpoints_families::classify_admin_endpoints_family_route;
use model_provider_families::classify_admin_model_provider_family_route;
use observability_families::classify_admin_observability_family_route;
use operations_families::classify_admin_operations_family_route;
use provider_ops_routes::classify_admin_provider_ops_routes;
use system_families::classify_admin_system_family_route;
pub(super) fn classify_admin_route(
method: &http::Method,
normalized_path: &str,
) -> Option<ClassifiedRoute> {
let normalized_path_no_trailing = normalized_path.trim_end_matches('/');
let normalized_path_no_trailing = if normalized_path_no_trailing.is_empty() {
"/"
} else {
normalized_path_no_trailing
};
if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/providers" | "/api/admin/providers/"
)
{
Some(classified(
"admin_proxy",
"providers_manage",
"list_providers",
"admin:providers",
false,
))
} else if let Some(route) =
classify_admin_basic_family_route(method, normalized_path, normalized_path_no_trailing)
{
Some(route)
} else if let Some(route) = classify_admin_observability_family_route(
method,
normalized_path,
normalized_path_no_trailing,
) {
Some(route)
} else if let Some(route) =
classify_admin_operations_family_route(method, normalized_path, normalized_path_no_trailing)
{
Some(route)
} else if let Some(route) =
classify_admin_system_family_route(method, normalized_path, normalized_path_no_trailing)
{
Some(route)
} else if let Some(route) = classify_admin_provider_ops_routes(method, normalized_path) {
Some(route)
} else if let Some(route) = classify_admin_model_provider_family_route(method, normalized_path)
{
Some(route)
} else if let Some(route) = classify_admin_endpoints_family_route(method, normalized_path) {
Some(route)
} else {
None
}
}

View File

@@ -0,0 +1,514 @@
use super::*;
pub(super) fn classify_admin_basic_family_route(
method: &http::Method,
normalized_path: &str,
normalized_path_no_trailing: &str,
) -> Option<ClassifiedRoute> {
if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/management-tokens" | "/api/admin/management-tokens/"
)
{
Some(classified(
"admin_proxy",
"management_tokens_manage",
"list_tokens",
"admin:management_tokens",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/management-tokens/")
{
Some(classified(
"admin_proxy",
"management_tokens_manage",
"get_token",
"admin:management_tokens",
false,
))
} else if method == http::Method::DELETE
&& normalized_path.starts_with("/api/admin/management-tokens/")
{
Some(classified(
"admin_proxy",
"management_tokens_manage",
"delete_token",
"admin:management_tokens",
false,
))
} else if method == http::Method::PATCH
&& normalized_path.starts_with("/api/admin/management-tokens/")
&& normalized_path.ends_with("/status")
{
Some(classified(
"admin_proxy",
"management_tokens_manage",
"toggle_status",
"admin:management_tokens",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/ldap/config" | "/api/admin/ldap/config/"
)
{
Some(classified(
"admin_proxy",
"ldap_manage",
"get_config",
"admin:ldap",
false,
))
} else if method == http::Method::PUT
&& matches!(
normalized_path,
"/api/admin/ldap/config" | "/api/admin/ldap/config/"
)
{
Some(classified(
"admin_proxy",
"ldap_manage",
"set_config",
"admin:ldap",
false,
))
} else if method == http::Method::POST
&& matches!(
normalized_path,
"/api/admin/ldap/test" | "/api/admin/ldap/test/"
)
{
Some(classified(
"admin_proxy",
"ldap_manage",
"test_connection",
"admin:ldap",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/gemini-files/mappings" | "/api/admin/gemini-files/mappings/"
)
{
Some(classified(
"admin_proxy",
"gemini_files_manage",
"list_mappings",
"admin:gemini_files",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/gemini-files/stats" | "/api/admin/gemini-files/stats/"
)
{
Some(classified(
"admin_proxy",
"gemini_files_manage",
"stats",
"admin:gemini_files",
false,
))
} else if method == http::Method::DELETE
&& matches!(
normalized_path,
"/api/admin/gemini-files/mappings" | "/api/admin/gemini-files/mappings/"
)
{
Some(classified(
"admin_proxy",
"gemini_files_manage",
"cleanup_mappings",
"admin:gemini_files",
false,
))
} else if method == http::Method::DELETE
&& normalized_path.starts_with("/api/admin/gemini-files/mappings/")
{
Some(classified(
"admin_proxy",
"gemini_files_manage",
"delete_mapping",
"admin:gemini_files",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/gemini-files/capable-keys" | "/api/admin/gemini-files/capable-keys/"
)
{
Some(classified(
"admin_proxy",
"gemini_files_manage",
"capable_keys",
"admin:gemini_files",
false,
))
} else if method == http::Method::POST
&& matches!(
normalized_path,
"/api/admin/gemini-files/upload" | "/api/admin/gemini-files/upload/"
)
{
Some(classified(
"admin_proxy",
"gemini_files_manage",
"upload",
"admin:gemini_files",
false,
))
} else if method == http::Method::GET && normalized_path == "/api/admin/modules/status" {
Some(classified(
"admin_proxy",
"modules_manage",
"status_list",
"admin:modules",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/modules/status/")
{
Some(classified(
"admin_proxy",
"modules_manage",
"status_detail",
"admin:modules",
false,
))
} else if method == http::Method::PUT
&& normalized_path.starts_with("/api/admin/modules/status/")
&& normalized_path.ends_with("/enabled")
{
Some(classified(
"admin_proxy",
"modules_manage",
"set_enabled",
"admin:modules",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/adaptive/keys" | "/api/admin/adaptive/keys/"
)
{
Some(classified(
"admin_proxy",
"adaptive_manage",
"list_keys",
"admin:adaptive",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/adaptive/summary" | "/api/admin/adaptive/summary/"
)
{
Some(classified(
"admin_proxy",
"adaptive_manage",
"summary",
"admin:adaptive",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/adaptive/keys/")
&& normalized_path.ends_with("/stats")
&& normalized_path.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"adaptive_manage",
"get_stats",
"admin:adaptive",
false,
))
} else if method == http::Method::PATCH
&& normalized_path.starts_with("/api/admin/adaptive/keys/")
&& normalized_path.ends_with("/mode")
&& normalized_path.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"adaptive_manage",
"toggle_mode",
"admin:adaptive",
false,
))
} else if method == http::Method::PATCH
&& normalized_path.starts_with("/api/admin/adaptive/keys/")
&& normalized_path.ends_with("/limit")
&& normalized_path.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"adaptive_manage",
"set_limit",
"admin:adaptive",
false,
))
} else if method == http::Method::DELETE
&& normalized_path.starts_with("/api/admin/adaptive/keys/")
&& normalized_path.ends_with("/learning")
&& normalized_path.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"adaptive_manage",
"reset_learning",
"admin:adaptive",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/provider-strategy/strategies" | "/api/admin/provider-strategy/strategies/"
)
{
Some(classified(
"admin_proxy",
"provider_strategy_manage",
"list_strategies",
"admin:provider_strategy",
false,
))
} else if method == http::Method::PUT
&& normalized_path.starts_with("/api/admin/provider-strategy/providers/")
&& normalized_path.ends_with("/billing")
{
Some(classified(
"admin_proxy",
"provider_strategy_manage",
"update_provider_billing",
"admin:provider_strategy",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/provider-strategy/providers/")
&& normalized_path.ends_with("/stats")
{
Some(classified(
"admin_proxy",
"provider_strategy_manage",
"get_provider_stats",
"admin:provider_strategy",
false,
))
} else if method == http::Method::DELETE
&& normalized_path.starts_with("/api/admin/provider-strategy/providers/")
&& normalized_path.ends_with("/quota")
&& normalized_path.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"provider_strategy_manage",
"reset_provider_quota",
"admin:provider_strategy",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/billing/presets" | "/api/admin/billing/presets/"
)
{
Some(classified(
"admin_proxy",
"billing_manage",
"list_presets",
"admin:billing",
false,
))
} else if method == http::Method::POST
&& matches!(
normalized_path,
"/api/admin/billing/presets/apply" | "/api/admin/billing/presets/apply/"
)
{
Some(classified(
"admin_proxy",
"billing_manage",
"apply_preset",
"admin:billing",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/billing/rules" | "/api/admin/billing/rules/"
)
{
Some(classified(
"admin_proxy",
"billing_manage",
"list_rules",
"admin:billing",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/billing/rules/")
&& normalized_path.matches('/').count() == 5
{
Some(classified(
"admin_proxy",
"billing_manage",
"get_rule",
"admin:billing",
false,
))
} else if method == http::Method::POST
&& matches!(
normalized_path,
"/api/admin/billing/rules" | "/api/admin/billing/rules/"
)
{
Some(classified(
"admin_proxy",
"billing_manage",
"create_rule",
"admin:billing",
false,
))
} else if method == http::Method::PUT
&& normalized_path.starts_with("/api/admin/billing/rules/")
&& normalized_path.matches('/').count() == 5
{
Some(classified(
"admin_proxy",
"billing_manage",
"update_rule",
"admin:billing",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/billing/collectors" | "/api/admin/billing/collectors/"
)
{
Some(classified(
"admin_proxy",
"billing_manage",
"list_collectors",
"admin:billing",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/billing/collectors/")
&& normalized_path.matches('/').count() == 5
{
Some(classified(
"admin_proxy",
"billing_manage",
"get_collector",
"admin:billing",
false,
))
} else if method == http::Method::POST
&& matches!(
normalized_path,
"/api/admin/billing/collectors" | "/api/admin/billing/collectors/"
)
{
Some(classified(
"admin_proxy",
"billing_manage",
"create_collector",
"admin:billing",
false,
))
} else if method == http::Method::PUT
&& normalized_path.starts_with("/api/admin/billing/collectors/")
&& normalized_path.matches('/').count() == 5
{
Some(classified(
"admin_proxy",
"billing_manage",
"update_collector",
"admin:billing",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/payments/orders" | "/api/admin/payments/orders/"
)
{
Some(classified(
"admin_proxy",
"payments_manage",
"list_orders",
"admin:payments",
false,
))
} else if method == http::Method::GET
&& normalized_path_no_trailing.starts_with("/api/admin/payments/orders/")
&& normalized_path_no_trailing.matches('/').count() == 5
{
Some(classified(
"admin_proxy",
"payments_manage",
"get_order",
"admin:payments",
false,
))
} else if method == http::Method::POST
&& normalized_path_no_trailing.starts_with("/api/admin/payments/orders/")
&& normalized_path_no_trailing.ends_with("/expire")
&& normalized_path_no_trailing.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"payments_manage",
"expire_order",
"admin:payments",
false,
))
} else if method == http::Method::POST
&& normalized_path_no_trailing.starts_with("/api/admin/payments/orders/")
&& normalized_path_no_trailing.ends_with("/credit")
&& normalized_path_no_trailing.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"payments_manage",
"credit_order",
"admin:payments",
false,
))
} else if method == http::Method::POST
&& normalized_path_no_trailing.starts_with("/api/admin/payments/orders/")
&& normalized_path_no_trailing.ends_with("/fail")
&& normalized_path_no_trailing.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"payments_manage",
"fail_order",
"admin:payments",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/payments/callbacks" | "/api/admin/payments/callbacks/"
)
{
Some(classified(
"admin_proxy",
"payments_manage",
"list_callbacks",
"admin:payments",
false,
))
} else {
None
}
}

View File

@@ -0,0 +1,270 @@
use super::*;
pub(super) fn classify_admin_endpoints_family_route(
method: &http::Method,
normalized_path: &str,
) -> Option<ClassifiedRoute> {
if method == http::Method::GET && normalized_path == "/api/admin/endpoints/health/summary" {
Some(classified(
"admin_proxy",
"endpoints_health",
"health_summary",
"admin:endpoints_health",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/endpoints/health/key/")
{
Some(classified(
"admin_proxy",
"endpoints_health",
"key_health",
"admin:endpoints_health",
false,
))
} else if method == http::Method::PATCH
&& normalized_path.starts_with("/api/admin/endpoints/health/keys/")
{
Some(classified(
"admin_proxy",
"endpoints_health",
"recover_key_health",
"admin:endpoints_health",
false,
))
} else if method == http::Method::PATCH && normalized_path == "/api/admin/endpoints/health/keys"
{
Some(classified(
"admin_proxy",
"endpoints_health",
"recover_all_keys_health",
"admin:endpoints_health",
false,
))
} else if method == http::Method::GET && normalized_path == "/api/admin/endpoints/health/status"
{
Some(classified(
"admin_proxy",
"endpoints_health",
"health_status",
"admin:endpoints_health",
false,
))
} else if method == http::Method::GET
&& normalized_path == "/api/admin/endpoints/health/api-formats"
{
Some(classified(
"admin_proxy",
"endpoints_health",
"health_api_formats",
"admin:endpoints_health",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/endpoints/rpm/key/")
{
Some(classified(
"admin_proxy",
"endpoints_rpm",
"key_rpm",
"admin:endpoints_rpm",
false,
))
} else if method == http::Method::DELETE
&& normalized_path.starts_with("/api/admin/endpoints/rpm/key/")
{
Some(classified(
"admin_proxy",
"endpoints_rpm",
"reset_key_rpm",
"admin:endpoints_rpm",
false,
))
} else if method == http::Method::GET
&& normalized_path == "/api/admin/endpoints/keys/grouped-by-format"
{
Some(classified(
"admin_proxy",
"endpoints_manage",
"keys_grouped_by_format",
"admin:endpoints_manage",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/endpoints/keys/")
&& normalized_path.ends_with("/reveal")
{
Some(classified(
"admin_proxy",
"endpoints_manage",
"reveal_key",
"admin:endpoints_manage",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/endpoints/keys/")
&& normalized_path.ends_with("/export")
{
Some(classified(
"admin_proxy",
"endpoints_manage",
"export_key",
"admin:endpoints_manage",
false,
))
} else if method == http::Method::PUT
&& normalized_path.starts_with("/api/admin/endpoints/keys/")
{
Some(classified(
"admin_proxy",
"endpoints_manage",
"update_key",
"admin:endpoints_manage",
false,
))
} else if method == http::Method::DELETE
&& normalized_path.starts_with("/api/admin/endpoints/keys/")
{
Some(classified(
"admin_proxy",
"endpoints_manage",
"delete_key",
"admin:endpoints_manage",
false,
))
} else if method == http::Method::POST
&& normalized_path == "/api/admin/endpoints/keys/batch-delete"
{
Some(classified(
"admin_proxy",
"endpoints_manage",
"batch_delete_keys",
"admin:endpoints_manage",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/endpoints/keys/")
&& normalized_path.ends_with("/clear-oauth-invalid")
{
Some(classified(
"admin_proxy",
"endpoints_manage",
"clear_oauth_invalid",
"admin:endpoints_manage",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/endpoints/providers/")
&& normalized_path.ends_with("/refresh-quota")
{
Some(classified(
"admin_proxy",
"endpoints_manage",
"refresh_quota",
"admin:endpoints_manage",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/endpoints/providers/")
&& normalized_path.ends_with("/keys")
{
Some(classified(
"admin_proxy",
"endpoints_manage",
"create_provider_key",
"admin:endpoints_manage",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/endpoints/providers/")
&& normalized_path.ends_with("/keys")
{
Some(classified(
"admin_proxy",
"endpoints_manage",
"list_provider_keys",
"admin:endpoints_manage",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/endpoints/providers/")
&& normalized_path.ends_with("/endpoints")
{
Some(classified(
"admin_proxy",
"endpoints_manage",
"list_provider_endpoints",
"admin:endpoints_manage",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/endpoints/providers/")
&& normalized_path.ends_with("/endpoints")
{
Some(classified(
"admin_proxy",
"endpoints_manage",
"create_endpoint",
"admin:endpoints_manage",
false,
))
} else if method == http::Method::PUT
&& normalized_path.starts_with("/api/admin/endpoints/")
&& !normalized_path.starts_with("/api/admin/endpoints/health/")
&& !normalized_path.starts_with("/api/admin/endpoints/rpm/")
&& !normalized_path.starts_with("/api/admin/endpoints/providers/")
&& !normalized_path.starts_with("/api/admin/endpoints/defaults/")
&& !normalized_path.starts_with("/api/admin/endpoints/keys/")
{
Some(classified(
"admin_proxy",
"endpoints_manage",
"update_endpoint",
"admin:endpoints_manage",
false,
))
} else if method == http::Method::DELETE
&& normalized_path.starts_with("/api/admin/endpoints/")
&& !normalized_path.starts_with("/api/admin/endpoints/health/")
&& !normalized_path.starts_with("/api/admin/endpoints/rpm/")
&& !normalized_path.starts_with("/api/admin/endpoints/providers/")
&& !normalized_path.starts_with("/api/admin/endpoints/defaults/")
&& !normalized_path.starts_with("/api/admin/endpoints/keys/")
{
Some(classified(
"admin_proxy",
"endpoints_manage",
"delete_endpoint",
"admin:endpoints_manage",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/endpoints/defaults/")
&& normalized_path.ends_with("/body-rules")
{
Some(classified(
"admin_proxy",
"endpoints_manage",
"default_body_rules",
"admin:endpoints_manage",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/endpoints/")
&& !normalized_path.starts_with("/api/admin/endpoints/health/")
&& !normalized_path.starts_with("/api/admin/endpoints/rpm/")
&& !normalized_path.starts_with("/api/admin/endpoints/providers/")
&& !normalized_path.starts_with("/api/admin/endpoints/defaults/")
&& !normalized_path.starts_with("/api/admin/endpoints/keys/")
{
Some(classified(
"admin_proxy",
"endpoints_manage",
"get_endpoint",
"admin:endpoints_manage",
false,
))
} else {
None
}
}

View File

@@ -0,0 +1,352 @@
use super::*;
pub(super) fn classify_admin_model_provider_family_route(
method: &http::Method,
normalized_path: &str,
) -> Option<ClassifiedRoute> {
if method == http::Method::GET && normalized_path == "/api/admin/models/catalog" {
Some(classified(
"admin_proxy",
"model_catalog_manage",
"catalog",
"admin:models",
false,
))
} else if method == http::Method::GET && normalized_path == "/api/admin/models/external" {
Some(classified(
"admin_proxy",
"model_external_manage",
"external",
"admin:models",
false,
))
} else if method == http::Method::DELETE
&& normalized_path == "/api/admin/models/external/cache"
{
Some(classified(
"admin_proxy",
"model_external_manage",
"clear_external_cache",
"admin:models",
false,
))
} else if method == http::Method::POST
&& matches!(
normalized_path,
"/api/admin/providers" | "/api/admin/providers/"
)
{
Some(classified(
"admin_proxy",
"providers_manage",
"create_provider",
"admin:providers",
false,
))
} else if method == http::Method::PATCH
&& normalized_path.starts_with("/api/admin/providers/")
&& normalized_path.matches('/').count() == 4
{
Some(classified(
"admin_proxy",
"providers_manage",
"update_provider",
"admin:providers",
false,
))
} else if method == http::Method::DELETE
&& normalized_path.starts_with("/api/admin/providers/")
&& normalized_path.matches('/').count() == 4
{
Some(classified(
"admin_proxy",
"providers_manage",
"delete_provider",
"admin:providers",
false,
))
} else if method == http::Method::GET && normalized_path == "/api/admin/providers/summary" {
Some(classified(
"admin_proxy",
"providers_manage",
"summary_list",
"admin:providers",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/providers/")
&& normalized_path.ends_with("/summary")
{
Some(classified(
"admin_proxy",
"providers_manage",
"provider_summary",
"admin:providers",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/providers/")
&& normalized_path.ends_with("/health-monitor")
{
Some(classified(
"admin_proxy",
"providers_manage",
"health_monitor",
"admin:providers",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/providers/")
&& normalized_path.ends_with("/mapping-preview")
{
Some(classified(
"admin_proxy",
"providers_manage",
"mapping_preview",
"admin:providers",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/providers/")
&& normalized_path.contains("/delete-task/")
{
Some(classified(
"admin_proxy",
"providers_manage",
"delete_provider_task",
"admin:providers",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/providers/")
&& normalized_path.ends_with("/pool-status")
{
Some(classified(
"admin_proxy",
"providers_manage",
"pool_status",
"admin:providers",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/providers/")
&& normalized_path.contains("/pool/clear-cooldown/")
{
Some(classified(
"admin_proxy",
"providers_manage",
"clear_pool_cooldown",
"admin:providers",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/providers/")
&& normalized_path.contains("/pool/reset-cost/")
{
Some(classified(
"admin_proxy",
"providers_manage",
"reset_pool_cost",
"admin:providers",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/providers/")
&& normalized_path.ends_with("/models")
&& normalized_path.matches('/').count() == 5
{
Some(classified(
"admin_proxy",
"provider_models_manage",
"list_provider_models",
"admin:providers",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/providers/")
&& normalized_path.ends_with("/models")
&& normalized_path.matches('/').count() == 5
{
Some(classified(
"admin_proxy",
"provider_models_manage",
"create_provider_model",
"admin:providers",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/providers/")
&& normalized_path.contains("/models/")
&& normalized_path.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"provider_models_manage",
"get_provider_model",
"admin:providers",
false,
))
} else if method == http::Method::PATCH
&& normalized_path.starts_with("/api/admin/providers/")
&& normalized_path.contains("/models/")
&& normalized_path.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"provider_models_manage",
"update_provider_model",
"admin:providers",
false,
))
} else if method == http::Method::DELETE
&& normalized_path.starts_with("/api/admin/providers/")
&& normalized_path.contains("/models/")
&& normalized_path.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"provider_models_manage",
"delete_provider_model",
"admin:providers",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/providers/")
&& normalized_path.ends_with("/models/batch")
{
Some(classified(
"admin_proxy",
"provider_models_manage",
"batch_create_provider_models",
"admin:providers",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/providers/")
&& normalized_path.ends_with("/available-source-models")
{
Some(classified(
"admin_proxy",
"provider_models_manage",
"available_source_models",
"admin:providers",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/providers/")
&& normalized_path.ends_with("/assign-global-models")
{
Some(classified(
"admin_proxy",
"provider_models_manage",
"assign_global_models",
"admin:providers",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/providers/")
&& normalized_path.ends_with("/import-from-upstream")
{
Some(classified(
"admin_proxy",
"provider_models_manage",
"import_from_upstream",
"admin:providers",
false,
))
} else if method == http::Method::POST
&& normalized_path == "/api/admin/models/global/batch-delete"
{
Some(classified(
"admin_proxy",
"global_models_manage",
"batch_delete_global_models",
"admin:models",
false,
))
} else if method == http::Method::GET && normalized_path == "/api/admin/models/global" {
Some(classified(
"admin_proxy",
"global_models_manage",
"list_global_models",
"admin:models",
false,
))
} else if method == http::Method::POST && normalized_path == "/api/admin/models/global" {
Some(classified(
"admin_proxy",
"global_models_manage",
"create_global_model",
"admin:models",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/models/global/")
&& normalized_path.ends_with("/assign-to-providers")
{
Some(classified(
"admin_proxy",
"global_models_manage",
"assign_to_providers",
"admin:models",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/models/global/")
&& normalized_path.ends_with("/providers")
{
Some(classified(
"admin_proxy",
"global_models_manage",
"global_model_providers",
"admin:models",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/models/global/")
&& normalized_path.ends_with("/routing")
{
Some(classified(
"admin_proxy",
"global_models_manage",
"routing_preview",
"admin:models",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/models/global/")
&& normalized_path.matches('/').count() == 5
{
Some(classified(
"admin_proxy",
"global_models_manage",
"get_global_model",
"admin:models",
false,
))
} else if method == http::Method::PATCH
&& normalized_path.starts_with("/api/admin/models/global/")
&& normalized_path.matches('/').count() == 5
{
Some(classified(
"admin_proxy",
"global_models_manage",
"update_global_model",
"admin:models",
false,
))
} else if method == http::Method::DELETE
&& normalized_path.starts_with("/api/admin/models/global/")
&& normalized_path.matches('/').count() == 5
{
Some(classified(
"admin_proxy",
"global_models_manage",
"delete_global_model",
"admin:models",
false,
))
} else {
None
}
}

View File

@@ -0,0 +1,664 @@
use super::*;
pub(super) fn classify_admin_observability_family_route(
method: &http::Method,
normalized_path: &str,
normalized_path_no_trailing: &str,
) -> Option<ClassifiedRoute> {
if method == http::Method::POST
&& matches!(
normalized_path,
"/api/admin/provider-query/models" | "/api/admin/provider-query/models/"
)
{
Some(classified(
"admin_proxy",
"provider_query_manage",
"query_models",
"admin:provider_query",
false,
))
} else if method == http::Method::POST
&& matches!(
normalized_path,
"/api/admin/provider-query/test-model" | "/api/admin/provider-query/test-model/"
)
{
Some(classified(
"admin_proxy",
"provider_query_manage",
"test_model",
"admin:provider_query",
false,
))
} else if method == http::Method::POST
&& matches!(
normalized_path,
"/api/admin/provider-query/test-model-failover"
| "/api/admin/provider-query/test-model-failover/"
)
{
Some(classified(
"admin_proxy",
"provider_query_manage",
"test_model_failover",
"admin:provider_query",
false,
))
} else if method == http::Method::POST
&& matches!(
normalized_path,
"/api/admin/security/ip/blacklist" | "/api/admin/security/ip/blacklist/"
)
{
Some(classified(
"admin_proxy",
"security_manage",
"blacklist_add",
"admin:security",
false,
))
} else if method == http::Method::DELETE
&& normalized_path.starts_with("/api/admin/security/ip/blacklist/")
{
Some(classified(
"admin_proxy",
"security_manage",
"blacklist_remove",
"admin:security",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/security/ip/blacklist/stats" | "/api/admin/security/ip/blacklist/stats/"
)
{
Some(classified(
"admin_proxy",
"security_manage",
"blacklist_stats",
"admin:security",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/security/ip/blacklist" | "/api/admin/security/ip/blacklist/"
)
{
Some(classified(
"admin_proxy",
"security_manage",
"blacklist_list",
"admin:security",
false,
))
} else if method == http::Method::POST
&& matches!(
normalized_path,
"/api/admin/security/ip/whitelist" | "/api/admin/security/ip/whitelist/"
)
{
Some(classified(
"admin_proxy",
"security_manage",
"whitelist_add",
"admin:security",
false,
))
} else if method == http::Method::DELETE
&& normalized_path.starts_with("/api/admin/security/ip/whitelist/")
{
Some(classified(
"admin_proxy",
"security_manage",
"whitelist_remove",
"admin:security",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/security/ip/whitelist" | "/api/admin/security/ip/whitelist/"
)
{
Some(classified(
"admin_proxy",
"security_manage",
"whitelist_list",
"admin:security",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/api-keys" | "/api/admin/api-keys/"
)
{
Some(classified(
"admin_proxy",
"api_keys_manage",
"list_api_keys",
"admin:api_keys",
false,
))
} else if method == http::Method::POST
&& matches!(
normalized_path,
"/api/admin/api-keys" | "/api/admin/api-keys/"
)
{
Some(classified(
"admin_proxy",
"api_keys_manage",
"create_api_key",
"admin:api_keys",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/api-keys/")
&& normalized_path.matches('/').count() == 4
{
Some(classified(
"admin_proxy",
"api_keys_manage",
"api_key_detail",
"admin:api_keys",
false,
))
} else if method == http::Method::PUT
&& normalized_path.starts_with("/api/admin/api-keys/")
&& normalized_path.matches('/').count() == 4
{
Some(classified(
"admin_proxy",
"api_keys_manage",
"update_api_key",
"admin:api_keys",
false,
))
} else if method == http::Method::PATCH
&& normalized_path.starts_with("/api/admin/api-keys/")
&& normalized_path.matches('/').count() == 4
{
Some(classified(
"admin_proxy",
"api_keys_manage",
"toggle_api_key",
"admin:api_keys",
false,
))
} else if method == http::Method::DELETE
&& normalized_path.starts_with("/api/admin/api-keys/")
&& normalized_path.matches('/').count() == 4
{
Some(classified(
"admin_proxy",
"api_keys_manage",
"delete_api_key",
"admin:api_keys",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/pool/overview" | "/api/admin/pool/overview/"
)
{
Some(classified(
"admin_proxy",
"pool_manage",
"overview",
"admin:pool",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/pool/scheduling-presets" | "/api/admin/pool/scheduling-presets/"
)
{
Some(classified(
"admin_proxy",
"pool_manage",
"scheduling_presets",
"admin:pool",
false,
))
} else if method == http::Method::GET
&& normalized_path_no_trailing.starts_with("/api/admin/pool/")
&& normalized_path_no_trailing.ends_with("/keys")
&& normalized_path_no_trailing.matches('/').count() == 5
{
Some(classified(
"admin_proxy",
"pool_manage",
"list_keys",
"admin:pool",
false,
))
} else if method == http::Method::POST
&& normalized_path_no_trailing.starts_with("/api/admin/pool/")
&& normalized_path_no_trailing.ends_with("/keys/batch-import")
&& normalized_path_no_trailing.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"pool_manage",
"batch_import_keys",
"admin:pool",
false,
))
} else if method == http::Method::POST
&& normalized_path_no_trailing.starts_with("/api/admin/pool/")
&& normalized_path_no_trailing.ends_with("/keys/batch-action")
&& normalized_path_no_trailing.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"pool_manage",
"batch_action_keys",
"admin:pool",
false,
))
} else if method == http::Method::POST
&& normalized_path_no_trailing.starts_with("/api/admin/pool/")
&& normalized_path_no_trailing.ends_with("/keys/resolve-selection")
&& normalized_path_no_trailing.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"pool_manage",
"resolve_selection",
"admin:pool",
false,
))
} else if method == http::Method::GET
&& normalized_path_no_trailing.starts_with("/api/admin/pool/")
&& normalized_path_no_trailing.contains("/keys/batch-delete-task/")
&& normalized_path_no_trailing.matches('/').count() == 7
{
Some(classified(
"admin_proxy",
"pool_manage",
"batch_delete_task_status",
"admin:pool",
false,
))
} else if method == http::Method::POST
&& normalized_path_no_trailing.starts_with("/api/admin/pool/")
&& normalized_path_no_trailing.ends_with("/keys/cleanup-banned")
&& normalized_path_no_trailing.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"pool_manage",
"cleanup_banned_keys",
"admin:pool",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/usage/aggregation/stats" | "/api/admin/usage/aggregation/stats/"
)
{
Some(classified(
"admin_proxy",
"usage_manage",
"aggregation_stats",
"admin:usage",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/usage/stats" | "/api/admin/usage/stats/"
)
{
Some(classified(
"admin_proxy",
"usage_manage",
"stats",
"admin:usage",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/usage/heatmap" | "/api/admin/usage/heatmap/"
)
{
Some(classified(
"admin_proxy",
"usage_manage",
"heatmap",
"admin:usage",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/usage/records" | "/api/admin/usage/records/"
)
{
Some(classified(
"admin_proxy",
"usage_manage",
"records",
"admin:usage",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/usage/active" | "/api/admin/usage/active/"
)
{
Some(classified(
"admin_proxy",
"usage_manage",
"active",
"admin:usage",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/usage/cache-affinity/hit-analysis"
| "/api/admin/usage/cache-affinity/hit-analysis/"
)
{
Some(classified(
"admin_proxy",
"usage_manage",
"cache_affinity_hit_analysis",
"admin:usage",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/usage/cache-affinity/interval-timeline"
| "/api/admin/usage/cache-affinity/interval-timeline/"
)
{
Some(classified(
"admin_proxy",
"usage_manage",
"cache_affinity_interval_timeline",
"admin:usage",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/usage/cache-affinity/ttl-analysis"
| "/api/admin/usage/cache-affinity/ttl-analysis/"
)
{
Some(classified(
"admin_proxy",
"usage_manage",
"cache_affinity_ttl_analysis",
"admin:usage",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/usage/")
&& normalized_path.ends_with("/curl")
&& normalized_path.matches('/').count() == 5
{
Some(classified(
"admin_proxy",
"usage_manage",
"curl",
"admin:usage",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/usage/")
&& normalized_path.ends_with("/replay")
&& normalized_path.matches('/').count() == 5
{
Some(classified(
"admin_proxy",
"usage_manage",
"replay",
"admin:usage",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/usage/")
&& normalized_path.matches('/').count() == 4
{
Some(classified(
"admin_proxy",
"usage_manage",
"detail",
"admin:usage",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/stats/providers/quota-usage" | "/api/admin/stats/providers/quota-usage/"
)
{
Some(classified(
"admin_proxy",
"stats_manage",
"provider_quota_usage",
"admin:stats",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/stats/comparison" | "/api/admin/stats/comparison/"
)
{
Some(classified(
"admin_proxy",
"stats_manage",
"comparison",
"admin:stats",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/stats/errors/distribution" | "/api/admin/stats/errors/distribution/"
)
{
Some(classified(
"admin_proxy",
"stats_manage",
"error_distribution",
"admin:stats",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/stats/performance/percentiles"
| "/api/admin/stats/performance/percentiles/"
)
{
Some(classified(
"admin_proxy",
"stats_manage",
"performance_percentiles",
"admin:stats",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/stats/cost/forecast" | "/api/admin/stats/cost/forecast/"
)
{
Some(classified(
"admin_proxy",
"stats_manage",
"cost_forecast",
"admin:stats",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/stats/cost/savings" | "/api/admin/stats/cost/savings/"
)
{
Some(classified(
"admin_proxy",
"stats_manage",
"cost_savings",
"admin:stats",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/stats/leaderboard/api-keys" | "/api/admin/stats/leaderboard/api-keys/"
)
{
Some(classified(
"admin_proxy",
"stats_manage",
"leaderboard_api_keys",
"admin:stats",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/stats/leaderboard/models" | "/api/admin/stats/leaderboard/models/"
)
{
Some(classified(
"admin_proxy",
"stats_manage",
"leaderboard_models",
"admin:stats",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/stats/leaderboard/users" | "/api/admin/stats/leaderboard/users/"
)
{
Some(classified(
"admin_proxy",
"stats_manage",
"leaderboard_users",
"admin:stats",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/stats/time-series" | "/api/admin/stats/time-series/"
)
{
Some(classified(
"admin_proxy",
"stats_manage",
"time_series",
"admin:stats",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/monitoring/audit-logs" | "/api/admin/monitoring/audit-logs/"
)
{
Some(classified(
"admin_proxy",
"monitoring",
"audit_logs",
"admin:monitoring",
false,
))
} else if method == http::Method::GET
&& (matches!(
normalized_path,
"/api/admin/monitoring/system-status"
| "/api/admin/monitoring/system-status/"
| "/api/admin/monitoring/suspicious-activities"
| "/api/admin/monitoring/suspicious-activities/"
| "/api/admin/monitoring/user-behavior"
) || normalized_path.starts_with("/api/admin/monitoring/user-behavior/"))
{
Some(classified(
"admin_proxy",
"monitoring",
"user_behavior",
"admin:monitoring",
false,
))
} else if method == http::Method::GET
&& (matches!(
normalized_path,
"/api/admin/monitoring/resilience-status"
| "/api/admin/monitoring/resilience-status/"
| "/api/admin/monitoring/resilience/circuit-history"
| "/api/admin/monitoring/resilience/circuit-history/"
) || (normalized_path == "/api/admin/monitoring/resilience/error-stats"))
{
Some(classified(
"admin_proxy",
"monitoring",
"monitoring_resilience",
"admin:monitoring",
false,
))
} else if method == http::Method::DELETE
&& normalized_path == "/api/admin/monitoring/resilience/error-stats"
{
Some(classified(
"admin_proxy",
"monitoring",
"monitoring_resilience",
"admin:monitoring",
false,
))
} else if (method == http::Method::GET || method == http::Method::DELETE)
&& normalized_path.starts_with("/api/admin/monitoring/cache")
{
Some(classified(
"admin_proxy",
"monitoring",
"monitoring_cache",
"admin:monitoring",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/monitoring/trace/stats/provider/")
{
Some(classified(
"admin_proxy",
"monitoring",
"trace_provider_stats",
"admin:monitoring",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/monitoring/trace/")
&& !normalized_path.starts_with("/api/admin/monitoring/trace/stats/")
{
Some(classified(
"admin_proxy",
"monitoring",
"trace_request",
"admin:monitoring",
false,
))
} else {
None
}
}

View File

@@ -0,0 +1,570 @@
use super::*;
pub(super) fn classify_admin_operations_family_route(
method: &http::Method,
normalized_path: &str,
_normalized_path_no_trailing: &str,
) -> Option<ClassifiedRoute> {
if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/provider-ops/architectures" | "/api/admin/provider-ops/architectures/"
)
{
Some(classified(
"admin_proxy",
"provider_ops_manage",
"list_architectures",
"admin:provider_ops",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/video-tasks" | "/api/admin/video-tasks/"
)
{
Some(classified(
"admin_proxy",
"video_tasks_manage",
"list_tasks",
"admin:video_tasks",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/video-tasks/stats" | "/api/admin/video-tasks/stats/"
)
{
Some(classified(
"admin_proxy",
"video_tasks_manage",
"stats",
"admin:video_tasks",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/video-tasks/")
&& normalized_path.ends_with("/video")
&& normalized_path.matches('/').count() == 5
{
Some(classified(
"admin_proxy",
"video_tasks_manage",
"video",
"admin:video_tasks",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/video-tasks/")
&& normalized_path.ends_with("/cancel")
&& normalized_path.matches('/').count() == 5
{
Some(classified(
"admin_proxy",
"video_tasks_manage",
"cancel",
"admin:video_tasks",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/video-tasks/")
&& normalized_path["/api/admin/video-tasks/".len()..]
.split('/')
.count()
== 1
&& !matches!(
normalized_path,
"/api/admin/video-tasks/stats" | "/api/admin/video-tasks/stats/"
)
{
Some(classified(
"admin_proxy",
"video_tasks_manage",
"detail",
"admin:video_tasks",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/provider-ops/architectures/")
&& !normalized_path.ends_with('/')
&& normalized_path.matches('/').count() == 5
{
Some(classified(
"admin_proxy",
"provider_ops_manage",
"get_architecture",
"admin:provider_ops",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/proxy-nodes" | "/api/admin/proxy-nodes/"
)
{
Some(classified(
"admin_proxy",
"proxy_nodes_manage",
"list_nodes",
"admin:proxy_nodes",
false,
))
} else if method == http::Method::POST
&& matches!(
normalized_path,
"/api/admin/proxy-nodes/register" | "/api/admin/proxy-nodes/register/"
)
{
Some(classified(
"admin_proxy",
"proxy_nodes_manage",
"register_node",
"admin:proxy_nodes",
false,
))
} else if method == http::Method::POST
&& matches!(
normalized_path,
"/api/admin/proxy-nodes/heartbeat" | "/api/admin/proxy-nodes/heartbeat/"
)
{
Some(classified(
"admin_proxy",
"proxy_nodes_manage",
"heartbeat_node",
"admin:proxy_nodes",
false,
))
} else if method == http::Method::POST
&& matches!(
normalized_path,
"/api/admin/proxy-nodes/unregister" | "/api/admin/proxy-nodes/unregister/"
)
{
Some(classified(
"admin_proxy",
"proxy_nodes_manage",
"unregister_node",
"admin:proxy_nodes",
false,
))
} else if method == http::Method::POST
&& matches!(
normalized_path,
"/api/admin/proxy-nodes/manual" | "/api/admin/proxy-nodes/manual/"
)
{
Some(classified(
"admin_proxy",
"proxy_nodes_manage",
"create_manual_node",
"admin:proxy_nodes",
false,
))
} else if method == http::Method::POST
&& matches!(
normalized_path,
"/api/admin/proxy-nodes/upgrade" | "/api/admin/proxy-nodes/upgrade/"
)
{
Some(classified(
"admin_proxy",
"proxy_nodes_manage",
"batch_upgrade_nodes",
"admin:proxy_nodes",
false,
))
} else if method == http::Method::POST
&& matches!(
normalized_path,
"/api/admin/proxy-nodes/test-url" | "/api/admin/proxy-nodes/test-url/"
)
{
Some(classified(
"admin_proxy",
"proxy_nodes_manage",
"test_proxy_url",
"admin:proxy_nodes",
false,
))
} else if method == http::Method::PATCH
&& normalized_path.starts_with("/api/admin/proxy-nodes/")
&& !normalized_path.ends_with("/test")
&& !normalized_path.ends_with("/config")
&& !normalized_path.ends_with("/events")
{
Some(classified(
"admin_proxy",
"proxy_nodes_manage",
"update_manual_node",
"admin:proxy_nodes",
false,
))
} else if method == http::Method::DELETE
&& normalized_path.starts_with("/api/admin/proxy-nodes/")
&& !normalized_path.ends_with("/test")
&& !normalized_path.ends_with("/config")
&& !normalized_path.ends_with("/events")
{
Some(classified(
"admin_proxy",
"proxy_nodes_manage",
"delete_node",
"admin:proxy_nodes",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/proxy-nodes/")
&& normalized_path.ends_with("/test")
{
Some(classified(
"admin_proxy",
"proxy_nodes_manage",
"test_node",
"admin:proxy_nodes",
false,
))
} else if method == http::Method::PUT
&& normalized_path.starts_with("/api/admin/proxy-nodes/")
&& normalized_path.ends_with("/config")
{
Some(classified(
"admin_proxy",
"proxy_nodes_manage",
"update_node_config",
"admin:proxy_nodes",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/proxy-nodes/")
&& normalized_path.ends_with("/events")
{
Some(classified(
"admin_proxy",
"proxy_nodes_manage",
"list_node_events",
"admin:proxy_nodes",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/wallets" | "/api/admin/wallets/"
)
{
Some(classified(
"admin_proxy",
"wallets_manage",
"list_wallets",
"admin:wallets",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/wallets/ledger" | "/api/admin/wallets/ledger/"
)
{
Some(classified(
"admin_proxy",
"wallets_manage",
"ledger",
"admin:wallets",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/wallets/refund-requests" | "/api/admin/wallets/refund-requests/"
)
{
Some(classified(
"admin_proxy",
"wallets_manage",
"list_refund_requests",
"admin:wallets",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/wallets/")
&& normalized_path.ends_with("/transactions")
&& normalized_path.matches('/').count() == 5
{
Some(classified(
"admin_proxy",
"wallets_manage",
"list_wallet_transactions",
"admin:wallets",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/wallets/")
&& normalized_path.ends_with("/refunds")
&& normalized_path.matches('/').count() == 5
{
Some(classified(
"admin_proxy",
"wallets_manage",
"list_wallet_refunds",
"admin:wallets",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/wallets/")
&& !normalized_path.ends_with("/transactions")
&& !normalized_path.ends_with("/refunds")
&& normalized_path.matches('/').count() == 4
{
Some(classified(
"admin_proxy",
"wallets_manage",
"wallet_detail",
"admin:wallets",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/wallets/")
&& normalized_path.ends_with("/adjust")
&& normalized_path.matches('/').count() == 5
{
Some(classified(
"admin_proxy",
"wallets_manage",
"adjust_balance",
"admin:wallets",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/wallets/")
&& normalized_path.ends_with("/recharge")
&& normalized_path.matches('/').count() == 5
{
Some(classified(
"admin_proxy",
"wallets_manage",
"recharge_balance",
"admin:wallets",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/wallets/")
&& normalized_path.contains("/refunds/")
&& normalized_path.ends_with("/process")
&& normalized_path.matches('/').count() == 7
{
Some(classified(
"admin_proxy",
"wallets_manage",
"process_refund",
"admin:wallets",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/wallets/")
&& normalized_path.contains("/refunds/")
&& normalized_path.ends_with("/complete")
&& normalized_path.matches('/').count() == 7
{
Some(classified(
"admin_proxy",
"wallets_manage",
"complete_refund",
"admin:wallets",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/wallets/")
&& normalized_path.contains("/refunds/")
&& normalized_path.ends_with("/fail")
&& normalized_path.matches('/').count() == 7
{
Some(classified(
"admin_proxy",
"wallets_manage",
"fail_refund",
"admin:wallets",
false,
))
} else if method == http::Method::GET
&& matches!(normalized_path, "/api/admin/users" | "/api/admin/users/")
{
Some(classified(
"admin_proxy",
"users_manage",
"list_users",
"admin:users",
false,
))
} else if method == http::Method::POST
&& matches!(normalized_path, "/api/admin/users" | "/api/admin/users/")
{
Some(classified(
"admin_proxy",
"users_manage",
"create_user",
"admin:users",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/users/")
&& normalized_path.ends_with("/sessions")
&& normalized_path.matches('/').count() == 5
{
Some(classified(
"admin_proxy",
"users_manage",
"list_user_sessions",
"admin:users",
false,
))
} else if method == http::Method::DELETE
&& normalized_path.starts_with("/api/admin/users/")
&& normalized_path.ends_with("/sessions")
&& normalized_path.matches('/').count() == 5
{
Some(classified(
"admin_proxy",
"users_manage",
"delete_user_sessions",
"admin:users",
false,
))
} else if method == http::Method::DELETE
&& normalized_path.starts_with("/api/admin/users/")
&& normalized_path.contains("/sessions/")
&& normalized_path.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"users_manage",
"delete_user_session",
"admin:users",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/users/")
&& normalized_path.ends_with("/api-keys")
&& normalized_path.matches('/').count() == 5
{
Some(classified(
"admin_proxy",
"users_manage",
"list_user_api_keys",
"admin:users",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/users/")
&& normalized_path.ends_with("/api-keys")
&& normalized_path.matches('/').count() == 5
{
Some(classified(
"admin_proxy",
"users_manage",
"create_user_api_key",
"admin:users",
false,
))
} else if method == http::Method::DELETE
&& normalized_path.starts_with("/api/admin/users/")
&& normalized_path.contains("/api-keys/")
&& !normalized_path.ends_with("/lock")
&& !normalized_path.ends_with("/full-key")
&& normalized_path.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"users_manage",
"delete_user_api_key",
"admin:users",
false,
))
} else if method == http::Method::PUT
&& normalized_path.starts_with("/api/admin/users/")
&& normalized_path.contains("/api-keys/")
&& !normalized_path.ends_with("/lock")
&& !normalized_path.ends_with("/full-key")
&& normalized_path.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"users_manage",
"update_user_api_key",
"admin:users",
false,
))
} else if method == http::Method::PATCH
&& normalized_path.starts_with("/api/admin/users/")
&& normalized_path.ends_with("/lock")
&& normalized_path.matches('/').count() == 7
{
Some(classified(
"admin_proxy",
"users_manage",
"lock_user_api_key",
"admin:users",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/users/")
&& normalized_path.ends_with("/full-key")
&& normalized_path.matches('/').count() == 7
{
Some(classified(
"admin_proxy",
"users_manage",
"reveal_user_api_key",
"admin:users",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/users/")
&& !normalized_path.ends_with("/sessions")
&& !normalized_path.contains("/sessions/")
&& !normalized_path.ends_with("/api-keys")
&& !normalized_path.contains("/api-keys/")
&& normalized_path.matches('/').count() == 4
{
Some(classified(
"admin_proxy",
"users_manage",
"get_user",
"admin:users",
false,
))
} else if method == http::Method::PUT
&& normalized_path.starts_with("/api/admin/users/")
&& !normalized_path.ends_with("/sessions")
&& !normalized_path.contains("/sessions/")
&& !normalized_path.ends_with("/api-keys")
&& !normalized_path.contains("/api-keys/")
&& normalized_path.matches('/').count() == 4
{
Some(classified(
"admin_proxy",
"users_manage",
"update_user",
"admin:users",
false,
))
} else if method == http::Method::DELETE
&& normalized_path.starts_with("/api/admin/users/")
&& !normalized_path.ends_with("/sessions")
&& !normalized_path.contains("/sessions/")
&& !normalized_path.ends_with("/api-keys")
&& !normalized_path.contains("/api-keys/")
&& normalized_path.matches('/').count() == 4
{
Some(classified(
"admin_proxy",
"users_manage",
"delete_user",
"admin:users",
false,
))
} else {
None
}
}

View File

@@ -0,0 +1,155 @@
use super::*;
pub(super) fn classify_admin_provider_ops_routes(
method: &http::Method,
normalized_path: &str,
) -> Option<ClassifiedRoute> {
if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/provider-ops/providers/")
&& normalized_path.ends_with("/status")
&& normalized_path.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"provider_ops_manage",
"get_provider_status",
"admin:provider_ops",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/provider-ops/providers/")
&& normalized_path.ends_with("/config")
&& normalized_path.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"provider_ops_manage",
"get_provider_config",
"admin:provider_ops",
false,
))
} else if method == http::Method::PUT
&& normalized_path.starts_with("/api/admin/provider-ops/providers/")
&& normalized_path.ends_with("/config")
&& normalized_path.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"provider_ops_manage",
"save_provider_config",
"admin:provider_ops",
false,
))
} else if method == http::Method::DELETE
&& normalized_path.starts_with("/api/admin/provider-ops/providers/")
&& normalized_path.ends_with("/config")
&& normalized_path.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"provider_ops_manage",
"delete_provider_config",
"admin:provider_ops",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/provider-ops/providers/")
&& normalized_path.ends_with("/connect")
&& normalized_path.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"provider_ops_manage",
"connect_provider",
"admin:provider_ops",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/provider-ops/providers/")
&& normalized_path.ends_with("/verify")
&& normalized_path.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"provider_ops_manage",
"verify_provider",
"admin:provider_ops",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/provider-ops/providers/")
&& normalized_path.ends_with("/disconnect")
&& normalized_path.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"provider_ops_manage",
"disconnect_provider",
"admin:provider_ops",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/provider-ops/providers/")
&& normalized_path.ends_with("/balance")
&& normalized_path.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"provider_ops_manage",
"get_provider_balance",
"admin:provider_ops",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/provider-ops/providers/")
&& normalized_path.ends_with("/balance")
&& normalized_path.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"provider_ops_manage",
"refresh_provider_balance",
"admin:provider_ops",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/provider-ops/providers/")
&& normalized_path.ends_with("/checkin")
&& normalized_path.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"provider_ops_manage",
"provider_checkin",
"admin:provider_ops",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/provider-ops/providers/")
&& normalized_path.contains("/actions/")
&& normalized_path.matches('/').count() == 7
{
Some(classified(
"admin_proxy",
"provider_ops_manage",
"execute_provider_action",
"admin:provider_ops",
false,
))
} else if method == http::Method::POST
&& matches!(
normalized_path,
"/api/admin/provider-ops/batch/balance" | "/api/admin/provider-ops/batch/balance/"
)
{
Some(classified(
"admin_proxy",
"provider_ops_manage",
"batch_balance",
"admin:provider_ops",
false,
))
} else {
None
}
}

View File

@@ -0,0 +1,270 @@
use super::*;
pub(super) fn classify_admin_system_family_route(
method: &http::Method,
normalized_path: &str,
_normalized_path_no_trailing: &str,
) -> Option<ClassifiedRoute> {
if method == http::Method::GET && normalized_path == "/api/admin/system/version" {
Some(classified(
"admin_proxy",
"system_manage",
"version",
"admin:system",
false,
))
} else if method == http::Method::GET && normalized_path == "/api/admin/system/check-update" {
Some(classified(
"admin_proxy",
"system_manage",
"check_update",
"admin:system",
false,
))
} else if method == http::Method::GET && normalized_path == "/api/admin/system/aws-regions" {
Some(classified(
"admin_proxy",
"system_manage",
"aws_regions",
"admin:system",
false,
))
} else if method == http::Method::GET && normalized_path == "/api/admin/system/stats" {
Some(classified(
"admin_proxy",
"system_manage",
"stats",
"admin:system",
false,
))
} else if method == http::Method::GET && normalized_path == "/api/admin/system/settings" {
Some(classified(
"admin_proxy",
"system_manage",
"settings_get",
"admin:system",
false,
))
} else if method == http::Method::GET && normalized_path == "/api/admin/system/config/export" {
Some(classified(
"admin_proxy",
"system_manage",
"config_export",
"admin:system",
false,
))
} else if method == http::Method::GET && normalized_path == "/api/admin/system/users/export" {
Some(classified(
"admin_proxy",
"system_manage",
"users_export",
"admin:system",
false,
))
} else if method == http::Method::POST && normalized_path == "/api/admin/system/config/import" {
Some(classified(
"admin_proxy",
"system_manage",
"config_import",
"admin:system",
false,
))
} else if method == http::Method::POST && normalized_path == "/api/admin/system/users/import" {
Some(classified(
"admin_proxy",
"system_manage",
"users_import",
"admin:system",
false,
))
} else if method == http::Method::POST && normalized_path == "/api/admin/system/smtp/test" {
Some(classified(
"admin_proxy",
"system_manage",
"smtp_test",
"admin:system",
false,
))
} else if method == http::Method::POST && normalized_path == "/api/admin/system/cleanup" {
Some(classified(
"admin_proxy",
"system_manage",
"cleanup",
"admin:system",
false,
))
} else if method == http::Method::POST && normalized_path == "/api/admin/system/purge/config" {
Some(classified(
"admin_proxy",
"system_manage",
"purge_config",
"admin:system",
false,
))
} else if method == http::Method::POST && normalized_path == "/api/admin/system/purge/users" {
Some(classified(
"admin_proxy",
"system_manage",
"purge_users",
"admin:system",
false,
))
} else if method == http::Method::POST && normalized_path == "/api/admin/system/purge/usage" {
Some(classified(
"admin_proxy",
"system_manage",
"purge_usage",
"admin:system",
false,
))
} else if method == http::Method::POST
&& normalized_path == "/api/admin/system/purge/audit-logs"
{
Some(classified(
"admin_proxy",
"system_manage",
"purge_audit_logs",
"admin:system",
false,
))
} else if method == http::Method::POST
&& normalized_path == "/api/admin/system/purge/request-bodies"
{
Some(classified(
"admin_proxy",
"system_manage",
"purge_request_bodies",
"admin:system",
false,
))
} else if method == http::Method::POST && normalized_path == "/api/admin/system/purge/stats" {
Some(classified(
"admin_proxy",
"system_manage",
"purge_stats",
"admin:system",
false,
))
} else if method == http::Method::PUT && normalized_path == "/api/admin/system/settings" {
Some(classified(
"admin_proxy",
"system_manage",
"settings_set",
"admin:system",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/system/configs" | "/api/admin/system/configs/"
)
{
Some(classified(
"admin_proxy",
"system_manage",
"configs_list",
"admin:system",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/system/configs/")
&& normalized_path.matches('/').count() == 5
{
Some(classified(
"admin_proxy",
"system_manage",
"config_get",
"admin:system",
false,
))
} else if method == http::Method::PUT
&& normalized_path.starts_with("/api/admin/system/configs/")
&& normalized_path.matches('/').count() == 5
{
Some(classified(
"admin_proxy",
"system_manage",
"config_set",
"admin:system",
false,
))
} else if method == http::Method::DELETE
&& normalized_path.starts_with("/api/admin/system/configs/")
&& normalized_path.matches('/').count() == 5
{
Some(classified(
"admin_proxy",
"system_manage",
"config_delete",
"admin:system",
false,
))
} else if method == http::Method::GET && normalized_path == "/api/admin/system/api-formats" {
Some(classified(
"admin_proxy",
"system_manage",
"api_formats",
"admin:system",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/system/email/templates" | "/api/admin/system/email/templates/"
)
{
Some(classified(
"admin_proxy",
"system_manage",
"email_templates_list",
"admin:system",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/system/email/templates/")
&& normalized_path.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"system_manage",
"email_template_get",
"admin:system",
false,
))
} else if method == http::Method::PUT
&& normalized_path.starts_with("/api/admin/system/email/templates/")
&& normalized_path.matches('/').count() == 6
{
Some(classified(
"admin_proxy",
"system_manage",
"email_template_set",
"admin:system",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/system/email/templates/")
&& normalized_path.ends_with("/preview")
{
Some(classified(
"admin_proxy",
"system_manage",
"email_template_preview",
"admin:system",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/system/email/templates/")
&& normalized_path.ends_with("/reset")
{
Some(classified(
"admin_proxy",
"system_manage",
"email_template_reset",
"admin:system",
false,
))
} else {
None
}
}

View File

@@ -0,0 +1,102 @@
use super::{
classified, is_claude_cli_request, is_gemini_cli_request, is_gemini_models_route,
is_gemini_operation_route, ClassifiedRoute,
};
pub(super) fn classify_ai_public_route(
method: &http::Method,
normalized_path: &str,
headers: &http::HeaderMap,
) -> Option<ClassifiedRoute> {
if method == http::Method::POST && normalized_path == "/v1/chat/completions" {
Some(classified(
"ai_public",
"openai",
"chat",
"openai:chat",
true,
))
} else if method == http::Method::POST
&& matches!(normalized_path, "/v1/responses" | "/v1/responses/compact")
{
if normalized_path.ends_with("/compact") {
Some(classified(
"ai_public",
"openai",
"compact",
"openai:compact",
true,
))
} else {
Some(classified("ai_public", "openai", "cli", "openai:cli", true))
}
} else if method == http::Method::POST && normalized_path == "/v1/messages/count_tokens" {
Some(classified(
"ai_public",
"claude",
"count_tokens",
"claude:chat",
false,
))
} else if method == http::Method::POST && normalized_path == "/v1/messages" {
if is_claude_cli_request(headers) {
Some(classified("ai_public", "claude", "cli", "claude:cli", true))
} else {
Some(classified(
"ai_public",
"claude",
"chat",
"claude:chat",
true,
))
}
} else if normalized_path.starts_with("/v1/videos") {
Some(classified(
"ai_public",
"openai",
"video",
"openai:video",
true,
))
} else if is_gemini_models_route(normalized_path) {
if normalized_path.ends_with(":predictLongRunning") {
Some(classified(
"ai_public",
"gemini",
"video",
"gemini:video",
true,
))
} else if is_gemini_cli_request(headers) {
Some(classified("ai_public", "gemini", "cli", "gemini:cli", true))
} else {
Some(classified(
"ai_public",
"gemini",
"chat",
"gemini:chat",
true,
))
}
} else if is_gemini_operation_route(normalized_path) {
Some(classified(
"ai_public",
"gemini",
"video",
"gemini:video",
true,
))
} else if (method == http::Method::POST && normalized_path == "/upload/v1beta/files")
|| normalized_path.starts_with("/v1beta/files")
{
Some(classified(
"ai_public",
"gemini",
"files",
"gemini:chat",
true,
))
} else {
None
}
}

View File

@@ -0,0 +1,49 @@
use super::{classified, ClassifiedRoute};
use crate::gateway::{is_tunnel_heartbeat_path, is_tunnel_node_status_path, TUNNEL_ROUTE_FAMILY};
pub(super) fn classify_internal_route(
method: &http::Method,
normalized_path: &str,
) -> Option<ClassifiedRoute> {
if method == http::Method::POST && normalized_path.starts_with("/api/internal/gateway/") {
let route_kind = match normalized_path {
"/api/internal/gateway/resolve" => "resolve",
"/api/internal/gateway/auth-context" => "auth_context",
"/api/internal/gateway/decision-sync" => "decision_sync",
"/api/internal/gateway/decision-stream" => "decision_stream",
"/api/internal/gateway/plan-sync" => "plan_sync",
"/api/internal/gateway/plan-stream" => "plan_stream",
"/api/internal/gateway/report-sync" => "report_sync",
"/api/internal/gateway/report-stream" => "report_stream",
"/api/internal/gateway/finalize-sync" => "finalize_sync",
"/api/internal/gateway/execute-sync" => "execute_sync",
"/api/internal/gateway/execute-stream" => "execute_stream",
_ => "legacy_gateway",
};
Some(classified(
"internal_proxy",
"gateway_legacy",
route_kind,
"",
false,
))
} else if method == http::Method::POST && is_tunnel_heartbeat_path(normalized_path) {
Some(classified(
"internal_proxy",
TUNNEL_ROUTE_FAMILY,
"heartbeat",
"",
false,
))
} else if method == http::Method::POST && is_tunnel_node_status_path(normalized_path) {
Some(classified(
"internal_proxy",
TUNNEL_ROUTE_FAMILY,
"node_status",
"",
false,
))
} else {
None
}
}

View File

@@ -0,0 +1,278 @@
use super::{classified, ClassifiedRoute};
pub(super) fn classify_oauth_route(
method: &http::Method,
normalized_path: &str,
) -> Option<ClassifiedRoute> {
if method == http::Method::GET && normalized_path == "/api/oauth/providers" {
Some(classified(
"public_support",
"oauth_public_legacy",
"providers",
"",
false,
))
} else if method == http::Method::GET && normalized_path.starts_with("/api/oauth/") {
if normalized_path.ends_with("/authorize") {
Some(classified(
"public_support",
"oauth_public_legacy",
"authorize",
"",
false,
))
} else if normalized_path.ends_with("/callback") {
Some(classified(
"public_support",
"oauth_public_legacy",
"callback",
"",
false,
))
} else {
None
}
} else if method == http::Method::GET && normalized_path == "/api/user/oauth/bindable-providers"
{
Some(classified(
"public_support",
"oauth_user_legacy",
"bindable_providers",
"",
false,
))
} else if method == http::Method::GET && normalized_path == "/api/user/oauth/links" {
Some(classified(
"public_support",
"oauth_user_legacy",
"links",
"",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/user/oauth/")
&& normalized_path.ends_with("/bind-token")
{
Some(classified(
"public_support",
"oauth_user_legacy",
"bind_token",
"",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/user/oauth/")
&& normalized_path.ends_with("/bind")
{
Some(classified(
"public_support",
"oauth_user_legacy",
"bind",
"",
false,
))
} else if method == http::Method::DELETE && normalized_path.starts_with("/api/user/oauth/") {
Some(classified(
"public_support",
"oauth_user_legacy",
"unbind",
"",
false,
))
} else if method == http::Method::GET && normalized_path == "/api/admin/oauth/supported-types" {
Some(classified(
"admin_proxy",
"oauth_manage",
"supported_types",
"admin:oauth",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/admin/oauth/providers" | "/api/admin/oauth/providers/"
)
{
Some(classified(
"admin_proxy",
"oauth_manage",
"list_providers",
"admin:oauth",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/oauth/providers/")
&& normalized_path.ends_with("/test")
{
Some(classified(
"admin_proxy",
"oauth_manage",
"test_provider",
"admin:oauth",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/oauth/providers/")
{
Some(classified(
"admin_proxy",
"oauth_manage",
"get_provider",
"admin:oauth",
false,
))
} else if method == http::Method::PUT
&& normalized_path.starts_with("/api/admin/oauth/providers/")
{
Some(classified(
"admin_proxy",
"oauth_manage",
"upsert_provider",
"admin:oauth",
false,
))
} else if method == http::Method::DELETE
&& normalized_path.starts_with("/api/admin/oauth/providers/")
{
Some(classified(
"admin_proxy",
"oauth_manage",
"delete_provider",
"admin:oauth",
false,
))
} else if method == http::Method::GET
&& normalized_path == "/api/admin/provider-oauth/supported-types"
{
Some(classified(
"admin_proxy",
"provider_oauth_manage",
"supported_types",
"admin:provider_oauth",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/provider-oauth/keys/")
&& normalized_path.ends_with("/start")
{
Some(classified(
"admin_proxy",
"provider_oauth_manage",
"start_key_oauth",
"admin:provider_oauth",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/provider-oauth/providers/")
&& normalized_path.ends_with("/start")
{
Some(classified(
"admin_proxy",
"provider_oauth_manage",
"start_provider_oauth",
"admin:provider_oauth",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/provider-oauth/keys/")
&& normalized_path.ends_with("/complete")
{
Some(classified(
"admin_proxy",
"provider_oauth_manage",
"complete_key_oauth",
"admin:provider_oauth",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/provider-oauth/keys/")
&& normalized_path.ends_with("/refresh")
{
Some(classified(
"admin_proxy",
"provider_oauth_manage",
"refresh_key_oauth",
"admin:provider_oauth",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/provider-oauth/providers/")
&& normalized_path.ends_with("/complete")
{
Some(classified(
"admin_proxy",
"provider_oauth_manage",
"complete_provider_oauth",
"admin:provider_oauth",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/provider-oauth/providers/")
&& normalized_path.ends_with("/import-refresh-token")
{
Some(classified(
"admin_proxy",
"provider_oauth_manage",
"import_refresh_token",
"admin:provider_oauth",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/provider-oauth/providers/")
&& normalized_path.ends_with("/batch-import")
{
Some(classified(
"admin_proxy",
"provider_oauth_manage",
"batch_import_oauth",
"admin:provider_oauth",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/provider-oauth/providers/")
&& normalized_path.ends_with("/batch-import/tasks")
{
Some(classified(
"admin_proxy",
"provider_oauth_manage",
"start_batch_import_oauth_task",
"admin:provider_oauth",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/admin/provider-oauth/providers/")
&& normalized_path.contains("/batch-import/tasks/")
{
Some(classified(
"admin_proxy",
"provider_oauth_manage",
"get_batch_import_task_status",
"admin:provider_oauth",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/provider-oauth/providers/")
&& normalized_path.ends_with("/device-authorize")
{
Some(classified(
"admin_proxy",
"provider_oauth_manage",
"device_authorize",
"admin:provider_oauth",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/admin/provider-oauth/providers/")
&& normalized_path.ends_with("/device-poll")
{
Some(classified(
"admin_proxy",
"provider_oauth_manage",
"device_poll",
"admin:provider_oauth",
false,
))
} else {
None
}
}

View File

@@ -0,0 +1,635 @@
use super::{classified, is_gemini_models_route, is_gemini_operation_route, ClassifiedRoute};
pub(super) fn classify_public_support_route(
method: &http::Method,
normalized_path: &str,
public_models_auth_signature: &str,
) -> Option<ClassifiedRoute> {
if method == http::Method::GET && normalized_path == "/v1/models" {
Some(classified(
"public_support",
"models",
"list",
public_models_auth_signature,
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/v1/models/")
&& !is_gemini_models_route(normalized_path)
&& !is_gemini_operation_route(normalized_path)
{
Some(classified(
"public_support",
"models",
"detail",
public_models_auth_signature,
false,
))
} else if method == http::Method::GET && normalized_path == "/v1beta/models" {
Some(classified(
"public_support",
"models",
"list",
public_models_auth_signature,
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/v1beta/models/")
&& !is_gemini_models_route(normalized_path)
&& !is_gemini_operation_route(normalized_path)
{
Some(classified(
"public_support",
"models",
"detail",
public_models_auth_signature,
false,
))
} else if method == http::Method::POST
&& matches!(
normalized_path,
"/api/announcements" | "/api/announcements/"
)
{
Some(classified(
"admin_proxy",
"announcements_manage",
"create_announcement",
"admin:announcements",
false,
))
} else if method == http::Method::PUT
&& normalized_path.starts_with("/api/announcements/")
&& normalized_path != "/api/announcements/active"
&& normalized_path != "/api/announcements/users/me/unread-count"
&& normalized_path != "/api/announcements/users/me/unread-count/"
&& !normalized_path.ends_with("/read-status")
{
Some(classified(
"admin_proxy",
"announcements_manage",
"update_announcement",
"admin:announcements",
false,
))
} else if method == http::Method::DELETE
&& normalized_path.starts_with("/api/announcements/")
&& normalized_path != "/api/announcements/active"
&& normalized_path != "/api/announcements/users/me/unread-count"
&& normalized_path != "/api/announcements/users/me/unread-count/"
&& !normalized_path.ends_with("/read-status")
{
Some(classified(
"admin_proxy",
"announcements_manage",
"delete_announcement",
"admin:announcements",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/announcements" | "/api/announcements/"
)
{
Some(classified(
"public_support",
"announcements",
"list",
"public:announcements",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/announcements/active" | "/api/announcements/active/"
)
{
Some(classified(
"public_support",
"announcements",
"active",
"public:announcements",
false,
))
} else if method == http::Method::GET
&& normalized_path.starts_with("/api/announcements/")
&& normalized_path != "/api/announcements/active"
&& normalized_path != "/api/announcements/users/me/unread-count"
&& normalized_path != "/api/announcements/users/me/unread-count/"
&& !normalized_path.ends_with("/read-status")
{
Some(classified(
"public_support",
"announcements",
"detail",
"public:announcements",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/public/site-info"
| "/api/public/providers"
| "/api/public/models"
| "/api/public/search/models"
| "/api/public/stats"
| "/api/public/global-models"
| "/api/public/health/api-formats"
)
{
let route_kind = match normalized_path {
"/api/public/site-info" => "site_info",
"/api/public/providers" => "providers",
"/api/public/models" => "models",
"/api/public/search/models" => "search_models",
"/api/public/stats" => "stats",
"/api/public/global-models" => "global_models",
"/api/public/health/api-formats" => "health_api_formats",
_ => "site_info",
};
Some(classified(
"public_support",
"public_catalog",
route_kind,
"public:catalog",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/auth/registration-settings" | "/api/auth/settings"
)
{
let route_kind = match normalized_path {
"/api/auth/registration-settings" => "registration_settings",
"/api/auth/settings" => "settings",
_ => "settings",
};
Some(classified(
"public_support",
"auth_public",
route_kind,
"public:auth",
false,
))
} else if matches!(method, &http::Method::GET | &http::Method::POST)
&& matches!(
normalized_path,
"/api/auth/login"
| "/api/auth/refresh"
| "/api/auth/register"
| "/api/auth/me"
| "/api/auth/logout"
| "/api/auth/send-verification-code"
| "/api/auth/verify-email"
| "/api/auth/verification-status"
)
{
let route_kind = match normalized_path {
"/api/auth/login" => "login",
"/api/auth/refresh" => "refresh",
"/api/auth/register" => "register",
"/api/auth/me" => "me",
"/api/auth/logout" => "logout",
"/api/auth/send-verification-code" => "send_verification_code",
"/api/auth/verify-email" => "verify_email",
"/api/auth/verification-status" => "verification_status",
_ => "login",
};
Some(classified(
"public_support",
"auth_legacy",
route_kind,
"user:auth",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/dashboard/stats"
| "/api/dashboard/recent-requests"
| "/api/dashboard/provider-status"
| "/api/dashboard/daily-stats"
)
{
let route_kind = match normalized_path {
"/api/dashboard/stats" => "stats",
"/api/dashboard/recent-requests" => "recent_requests",
"/api/dashboard/provider-status" => "provider_status",
"/api/dashboard/daily-stats" => "daily_stats",
_ => "stats",
};
Some(classified(
"public_support",
"dashboard_legacy",
route_kind,
"user:dashboard",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/monitoring/my-audit-logs" | "/api/monitoring/rate-limit-status"
)
{
let route_kind = match normalized_path {
"/api/monitoring/my-audit-logs" => "audit_logs",
"/api/monitoring/rate-limit-status" => "rate_limit_status",
_ => "audit_logs",
};
Some(classified(
"public_support",
"monitoring_user_legacy",
route_kind,
"user:monitoring",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/announcements/users/me/unread-count"
| "/api/announcements/users/me/unread-count/"
)
{
Some(classified(
"public_support",
"announcement_user_legacy",
"unread_count",
"user:announcements",
false,
))
} else if method == http::Method::POST
&& matches!(
normalized_path,
"/api/announcements/read-all" | "/api/announcements/read-all/"
)
{
Some(classified(
"public_support",
"announcement_user_legacy",
"read_all",
"user:announcements",
false,
))
} else if method == http::Method::PATCH
&& normalized_path.starts_with("/api/announcements/")
&& (normalized_path.ends_with("/read-status") || normalized_path.ends_with("/read-status/"))
{
Some(classified(
"public_support",
"announcement_user_legacy",
"read_status",
"user:announcements",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/wallet/balance"
| "/api/wallet/transactions"
| "/api/wallet/flow"
| "/api/wallet/today-cost"
| "/api/wallet/recharge"
| "/api/wallet/refunds"
)
{
let route_kind = match normalized_path {
"/api/wallet/balance" => "balance",
"/api/wallet/transactions" => "transactions",
"/api/wallet/flow" => "flow",
"/api/wallet/today-cost" => "today_cost",
"/api/wallet/recharge" => "list_recharge_orders",
"/api/wallet/refunds" => "list_refunds",
_ => "balance",
};
Some(classified(
"public_support",
"wallet_legacy",
route_kind,
"user:wallet",
false,
))
} else if method == http::Method::GET && normalized_path.starts_with("/api/wallet/recharge/") {
Some(classified(
"public_support",
"wallet_legacy",
"recharge_detail",
"user:wallet",
false,
))
} else if method == http::Method::GET && normalized_path.starts_with("/api/wallet/refunds/") {
Some(classified(
"public_support",
"wallet_legacy",
"refund_detail",
"user:wallet",
false,
))
} else if method == http::Method::POST
&& matches!(
normalized_path,
"/api/wallet/recharge" | "/api/wallet/refunds"
)
{
let route_kind = match normalized_path {
"/api/wallet/recharge" => "create_recharge_order",
"/api/wallet/refunds" => "create_refund",
_ => "create_recharge_order",
};
Some(classified(
"public_support",
"wallet_legacy",
route_kind,
"user:wallet",
false,
))
} else if method == http::Method::POST && normalized_path.starts_with("/api/payment/callback/")
{
Some(classified(
"public_support",
"payment_callback_legacy",
"callback",
"public:payment",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/users/me"
| "/api/users/me/sessions"
| "/api/users/me/api-keys"
| "/api/users/me/usage"
| "/api/users/me/usage/active"
| "/api/users/me/usage/interval-timeline"
| "/api/users/me/usage/heatmap"
| "/api/users/me/providers"
| "/api/users/me/available-models"
| "/api/users/me/endpoint-status"
| "/api/users/me/preferences"
| "/api/users/me/model-capabilities"
)
{
let route_kind = match normalized_path {
"/api/users/me" => "detail",
"/api/users/me/sessions" => "sessions",
"/api/users/me/api-keys" => "api_keys_list",
"/api/users/me/usage" => "usage",
"/api/users/me/usage/active" => "usage_active",
"/api/users/me/usage/interval-timeline" => "usage_interval_timeline",
"/api/users/me/usage/heatmap" => "usage_heatmap",
"/api/users/me/providers" => "providers",
"/api/users/me/available-models" => "available_models",
"/api/users/me/endpoint-status" => "endpoint_status",
"/api/users/me/preferences" => "preferences",
"/api/users/me/model-capabilities" => "model_capabilities",
_ => "detail",
};
Some(classified(
"public_support",
"users_me_legacy",
route_kind,
"user:self",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/me/management-tokens" | "/api/me/management-tokens/"
)
{
Some(classified(
"public_support",
"users_me_legacy",
"management_tokens_list",
"user:self",
false,
))
} else if method == http::Method::PUT
&& matches!(
normalized_path,
"/api/users/me" | "/api/users/me/preferences" | "/api/users/me/model-capabilities"
)
{
let route_kind = match normalized_path {
"/api/users/me" => "update_detail",
"/api/users/me/preferences" => "preferences_update",
"/api/users/me/model-capabilities" => "model_capabilities_update",
_ => "update_detail",
};
Some(classified(
"public_support",
"users_me_legacy",
route_kind,
"user:self",
false,
))
} else if method == http::Method::POST
&& matches!(
normalized_path,
"/api/me/management-tokens" | "/api/me/management-tokens/"
)
{
Some(classified(
"public_support",
"users_me_legacy",
"management_tokens_create",
"user:self",
false,
))
} else if method == http::Method::POST
&& normalized_path.starts_with("/api/me/management-tokens/")
&& normalized_path.ends_with("/regenerate")
{
Some(classified(
"public_support",
"users_me_legacy",
"management_token_regenerate",
"user:self",
false,
))
} else if method == http::Method::PATCH && normalized_path == "/api/users/me/password" {
Some(classified(
"public_support",
"users_me_legacy",
"password",
"user:self",
false,
))
} else if method == http::Method::PATCH
&& normalized_path.starts_with("/api/me/management-tokens/")
&& normalized_path.ends_with("/status")
{
Some(classified(
"public_support",
"users_me_legacy",
"management_token_toggle",
"user:self",
false,
))
} else if method == http::Method::DELETE && normalized_path == "/api/users/me/sessions/others" {
Some(classified(
"public_support",
"users_me_legacy",
"sessions_others_delete",
"user:self",
false,
))
} else if matches!(method, &http::Method::PATCH | &http::Method::DELETE)
&& normalized_path.starts_with("/api/users/me/sessions/")
{
let route_kind = if method == http::Method::PATCH {
"session_update"
} else {
"session_delete"
};
Some(classified(
"public_support",
"users_me_legacy",
route_kind,
"user:self",
false,
))
} else if matches!(method, &http::Method::GET | &http::Method::POST)
&& normalized_path == "/api/users/me/api-keys"
{
let route_kind = if method == http::Method::GET {
"api_keys_list"
} else {
"api_keys_create"
};
Some(classified(
"public_support",
"users_me_legacy",
route_kind,
"user:self",
false,
))
} else if method == http::Method::PUT
&& (normalized_path.ends_with("/providers") || normalized_path.ends_with("/capabilities"))
&& normalized_path.starts_with("/api/users/me/api-keys/")
{
let route_kind = if normalized_path.ends_with("/providers") {
"api_key_providers_update"
} else {
"api_key_capabilities_update"
};
Some(classified(
"public_support",
"users_me_legacy",
route_kind,
"user:self",
false,
))
} else if matches!(
method,
&http::Method::GET | &http::Method::PUT | &http::Method::PATCH | &http::Method::DELETE
) && normalized_path.starts_with("/api/users/me/api-keys/")
{
let route_kind = match *method {
http::Method::GET => "api_key_detail",
http::Method::PUT => "api_key_update",
http::Method::PATCH => "api_key_patch",
http::Method::DELETE => "api_key_delete",
_ => "api_key_detail",
};
Some(classified(
"public_support",
"users_me_legacy",
route_kind,
"user:self",
false,
))
} else if matches!(
method,
&http::Method::GET | &http::Method::PUT | &http::Method::DELETE
) && normalized_path.starts_with("/api/me/management-tokens/")
{
let route_kind = match *method {
http::Method::GET => "management_token_detail",
http::Method::PUT => "management_token_update",
http::Method::DELETE => "management_token_delete",
_ => "management_token_detail",
};
Some(classified(
"public_support",
"users_me_legacy",
route_kind,
"user:self",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/api/capabilities" | "/api/capabilities/user-configurable"
)
{
let route_kind = match normalized_path {
"/api/capabilities" => "list",
"/api/capabilities/user-configurable" => "user_configurable",
_ => "list",
};
Some(classified(
"public_support",
"capabilities",
route_kind,
"public:capabilities",
false,
))
} else if method == http::Method::GET && normalized_path.starts_with("/api/capabilities/model/")
{
Some(classified(
"public_support",
"capabilities",
"model",
"public:capabilities",
false,
))
} else if method == http::Method::GET && normalized_path == "/api/modules/auth-status" {
Some(classified(
"public_support",
"modules",
"auth_status",
"public:modules",
false,
))
} else if method == http::Method::GET
&& matches!(
normalized_path,
"/" | "/health" | "/v1/health" | "/v1/providers" | "/v1/test-connection"
)
{
let route_kind = match normalized_path {
"/" => "root",
"/health" | "/v1/health" => "health",
"/v1/providers" => "providers",
"/v1/test-connection" => "test_connection",
_ => "root",
};
Some(classified(
"public_support",
"system_catalog",
route_kind,
"public:system_catalog",
false,
))
} else if method == http::Method::GET && normalized_path.starts_with("/v1/providers/") {
Some(classified(
"public_support",
"system_catalog",
"provider_detail",
"public:system_catalog",
false,
))
} else if method == http::Method::GET && normalized_path == "/test-connection" {
Some(classified(
"public_support",
"system_catalog",
"test_connection",
"public:system_catalog",
false,
))
} else {
None
}
}

View File

@@ -0,0 +1,115 @@
use super::*;
#[test]
fn classifies_admin_adaptive_keys_list_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/adaptive/keys?provider_id=provider-openai"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("adaptive_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("list_keys"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:adaptive")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_adaptive_summary_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/adaptive/summary"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("adaptive_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("summary"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:adaptive")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_adaptive_stats_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/adaptive/keys/key-openai/stats"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("adaptive_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("get_stats"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:adaptive")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_adaptive_toggle_mode_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/adaptive/keys/key-openai/mode"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::PATCH, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("adaptive_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("toggle_mode"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:adaptive")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_adaptive_set_limit_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/adaptive/keys/key-openai/limit?limit=9"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::PATCH, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("adaptive_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("set_limit"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:adaptive")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_adaptive_reset_learning_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/adaptive/keys/key-openai/learning"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::DELETE, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("adaptive_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("reset_learning"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:adaptive")
);
assert!(!decision.is_execution_runtime_candidate());
}

View File

@@ -0,0 +1,113 @@
use super::*;
#[test]
fn classifies_admin_api_keys_list_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/api-keys?limit=100"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("api_keys_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("list_api_keys"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:api_keys")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_api_keys_create_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/api-keys".parse().expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("api_keys_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("create_api_key"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:api_keys")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_api_keys_detail_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/api-keys/key-123"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("api_keys_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("api_key_detail"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:api_keys")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_api_keys_update_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/api-keys/key-123"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::PUT, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("api_keys_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("update_api_key"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:api_keys")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_api_keys_toggle_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/api-keys/key-123"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::PATCH, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("api_keys_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("toggle_api_key"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:api_keys")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_api_keys_delete_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/api-keys/key-123"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::DELETE, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("api_keys_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("delete_api_key"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:api_keys")
);
assert!(!decision.is_execution_runtime_candidate());
}

View File

@@ -0,0 +1,121 @@
use super::*;
#[test]
fn classifies_admin_billing_presets_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/billing/presets"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("billing_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("list_presets"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:billing")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_billing_apply_preset_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/billing/presets/apply"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("billing_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("apply_preset"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:billing")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_billing_rule_routes_as_admin_proxy_route() {
let headers = headers(&[]);
let list_uri: Uri = "/api/admin/billing/rules?page=1"
.parse()
.expect("uri should parse");
let list = classify_control_route(&http::Method::GET, &list_uri, &headers)
.expect("route should classify");
assert_eq!(list.route_family.as_deref(), Some("billing_manage"));
assert_eq!(list.route_kind.as_deref(), Some("list_rules"));
let detail_uri: Uri = "/api/admin/billing/rules/rule-1"
.parse()
.expect("uri should parse");
let detail = classify_control_route(&http::Method::GET, &detail_uri, &headers)
.expect("route should classify");
assert_eq!(detail.route_family.as_deref(), Some("billing_manage"));
assert_eq!(detail.route_kind.as_deref(), Some("get_rule"));
let create_uri: Uri = "/api/admin/billing/rules"
.parse()
.expect("uri should parse");
let create = classify_control_route(&http::Method::POST, &create_uri, &headers)
.expect("route should classify");
assert_eq!(create.route_family.as_deref(), Some("billing_manage"));
assert_eq!(create.route_kind.as_deref(), Some("create_rule"));
let update_uri: Uri = "/api/admin/billing/rules/rule-1"
.parse()
.expect("uri should parse");
let update = classify_control_route(&http::Method::PUT, &update_uri, &headers)
.expect("route should classify");
assert_eq!(update.route_family.as_deref(), Some("billing_manage"));
assert_eq!(update.route_kind.as_deref(), Some("update_rule"));
assert_eq!(
update.auth_endpoint_signature.as_deref(),
Some("admin:billing")
);
}
#[test]
fn classifies_admin_billing_collector_routes_as_admin_proxy_route() {
let headers = headers(&[]);
let list_uri: Uri = "/api/admin/billing/collectors?page=1"
.parse()
.expect("uri should parse");
let list = classify_control_route(&http::Method::GET, &list_uri, &headers)
.expect("route should classify");
assert_eq!(list.route_family.as_deref(), Some("billing_manage"));
assert_eq!(list.route_kind.as_deref(), Some("list_collectors"));
let detail_uri: Uri = "/api/admin/billing/collectors/collector-1"
.parse()
.expect("uri should parse");
let detail = classify_control_route(&http::Method::GET, &detail_uri, &headers)
.expect("route should classify");
assert_eq!(detail.route_family.as_deref(), Some("billing_manage"));
assert_eq!(detail.route_kind.as_deref(), Some("get_collector"));
let create_uri: Uri = "/api/admin/billing/collectors"
.parse()
.expect("uri should parse");
let create = classify_control_route(&http::Method::POST, &create_uri, &headers)
.expect("route should classify");
assert_eq!(create.route_family.as_deref(), Some("billing_manage"));
assert_eq!(create.route_kind.as_deref(), Some("create_collector"));
let update_uri: Uri = "/api/admin/billing/collectors/collector-1"
.parse()
.expect("uri should parse");
let update = classify_control_route(&http::Method::PUT, &update_uri, &headers)
.expect("route should classify");
assert_eq!(update.route_family.as_deref(), Some("billing_manage"));
assert_eq!(update.route_kind.as_deref(), Some("update_collector"));
assert_eq!(
update.auth_endpoint_signature.as_deref(),
Some("admin:billing")
);
}

View File

@@ -0,0 +1,753 @@
use super::*;
#[test]
fn classifies_admin_endpoint_health_api_formats_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/endpoints/health/api-formats?lookback_hours=12"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("endpoints_health"));
assert_eq!(decision.route_kind.as_deref(), Some("health_api_formats"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:endpoints_health")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_modules_status_list_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/modules/status"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("modules_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("status_list"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:modules")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_modules_status_detail_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/modules/status/oauth"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("modules_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("status_detail"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:modules")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_modules_set_enabled_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/modules/status/management_tokens/enabled"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::PUT, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("modules_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("set_enabled"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:modules")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_system_version_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/system/version"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("system_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("version"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:system")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_system_settings_get_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/system/settings"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("system_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("settings_get"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:system")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_system_config_export_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/system/config/export"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("system_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("config_export"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:system")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_system_users_export_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/system/users/export"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("system_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("users_export"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:system")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_system_maintenance_write_routes_as_admin_proxy_route() {
let headers = headers(&[]);
let cases = [
("/api/admin/system/config/import", "config_import"),
("/api/admin/system/users/import", "users_import"),
("/api/admin/system/smtp/test", "smtp_test"),
("/api/admin/system/cleanup", "cleanup"),
("/api/admin/system/purge/config", "purge_config"),
("/api/admin/system/purge/users", "purge_users"),
("/api/admin/system/purge/usage", "purge_usage"),
("/api/admin/system/purge/audit-logs", "purge_audit_logs"),
(
"/api/admin/system/purge/request-bodies",
"purge_request_bodies",
),
("/api/admin/system/purge/stats", "purge_stats"),
];
for (path, expected_kind) in cases {
let uri: Uri = path.parse().expect("uri should parse");
let decision = classify_control_route(&http::Method::POST, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("system_manage"));
assert_eq!(decision.route_kind.as_deref(), Some(expected_kind));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:system")
);
assert!(!decision.is_execution_runtime_candidate());
}
}
#[test]
fn classifies_admin_system_check_update_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/system/check-update"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("system_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("check_update"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:system")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_system_aws_regions_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/system/aws-regions"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("system_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("aws_regions"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:system")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_system_stats_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/system/stats".parse().expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("system_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("stats"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:system")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_system_settings_set_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/system/settings"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::PUT, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("system_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("settings_set"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:system")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_system_email_templates_list_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/system/email/templates"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("system_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("email_templates_list"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:system")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_system_email_template_get_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/system/email/templates/verification"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("system_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("email_template_get"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:system")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_system_email_template_set_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/system/email/templates/verification"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::PUT, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("system_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("email_template_set"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:system")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_system_email_template_preview_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/system/email/templates/verification/preview"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("system_manage"));
assert_eq!(
decision.route_kind.as_deref(),
Some("email_template_preview")
);
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:system")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_system_email_template_reset_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/system/email/templates/verification/reset"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("system_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("email_template_reset"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:system")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_system_configs_list_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/system/configs?limit=20"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("system_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("configs_list"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:system")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_system_config_get_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/system/configs/smtp_password"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("system_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("config_get"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:system")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_system_config_set_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/system/configs/smtp_password"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::PUT, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("system_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("config_set"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:system")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_system_config_delete_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/system/configs/request_log_level"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::DELETE, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("system_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("config_delete"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:system")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_system_api_formats_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/system/api-formats"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("system_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("api_formats"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:system")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_list_providers_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/providers/?skip=0&limit=50"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("providers_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("list_providers"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:providers")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_list_management_tokens_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/management-tokens?limit=20"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("management_tokens_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("list_tokens"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:management_tokens")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_ldap_config_get_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/ldap/config".parse().expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("ldap_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("get_config"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:ldap")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_ldap_config_set_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/ldap/config".parse().expect("uri should parse");
let decision =
classify_control_route(&http::Method::PUT, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("ldap_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("set_config"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:ldap")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_ldap_test_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/ldap/test".parse().expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("ldap_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("test_connection"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:ldap")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_list_gemini_file_mappings_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/gemini-files/mappings?page=1"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("gemini_files_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("list_mappings"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:gemini_files")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_gemini_file_stats_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/gemini-files/stats"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("gemini_files_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("stats"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:gemini_files")
);
}
#[test]
fn classifies_admin_delete_gemini_file_mapping_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/gemini-files/mappings/mapping-123"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::DELETE, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("gemini_files_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("delete_mapping"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:gemini_files")
);
}
#[test]
fn classifies_admin_cleanup_gemini_file_mappings_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/gemini-files/mappings"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::DELETE, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("gemini_files_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("cleanup_mappings"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:gemini_files")
);
}
#[test]
fn classifies_admin_list_gemini_file_capable_keys_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/gemini-files/capable-keys"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("gemini_files_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("capable_keys"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:gemini_files")
);
}
#[test]
fn classifies_admin_gemini_file_upload_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/gemini-files/upload"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("gemini_files_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("upload"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:gemini_files")
);
}
#[test]
fn classifies_admin_oauth_supported_types_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/oauth/supported-types"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("oauth_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("supported_types"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:oauth")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_oauth_supported_types_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/provider-oauth/supported-types"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_oauth_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("supported_types"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:provider_oauth")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_monitoring_trace_request_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/monitoring/trace/request-id-123"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("monitoring"));
assert_eq!(decision.route_kind.as_deref(), Some("trace_request"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:monitoring")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_monitoring_trace_provider_stats_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/monitoring/trace/stats/provider/provider-id"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("monitoring"));
assert_eq!(decision.route_kind.as_deref(), Some("trace_provider_stats"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:monitoring")
);
assert!(!decision.is_execution_runtime_candidate());
}

View File

@@ -0,0 +1,382 @@
use super::*;
#[test]
fn classifies_admin_endpoint_health_summary_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/endpoints/health/summary"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("endpoints_health"));
assert_eq!(decision.route_kind.as_deref(), Some("health_summary"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:endpoints_health")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_endpoint_key_health_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/endpoints/health/key/key-openai?api_format=openai:chat"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("endpoints_health"));
assert_eq!(decision.route_kind.as_deref(), Some("key_health"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:endpoints_health")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_endpoint_recover_key_health_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/endpoints/health/keys/key-openai?api_format=openai:chat"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::PATCH, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("endpoints_health"));
assert_eq!(decision.route_kind.as_deref(), Some("recover_key_health"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:endpoints_health")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_endpoint_recover_all_keys_health_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/endpoints/health/keys"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::PATCH, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("endpoints_health"));
assert_eq!(
decision.route_kind.as_deref(),
Some("recover_all_keys_health")
);
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:endpoints_health")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_endpoint_health_status_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/endpoints/health/status?lookback_hours=12"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("endpoints_health"));
assert_eq!(decision.route_kind.as_deref(), Some("health_status"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:endpoints_health")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_endpoint_key_rpm_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/endpoints/rpm/key/key-1"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("endpoints_rpm"));
assert_eq!(decision.route_kind.as_deref(), Some("key_rpm"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:endpoints_rpm")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_endpoint_reset_key_rpm_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/endpoints/rpm/key/key-1"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::DELETE, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("endpoints_rpm"));
assert_eq!(decision.route_kind.as_deref(), Some("reset_key_rpm"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:endpoints_rpm")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_list_provider_endpoints_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/endpoints/providers/provider-1/endpoints?skip=0&limit=50"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("endpoints_manage"));
assert_eq!(
decision.route_kind.as_deref(),
Some("list_provider_endpoints")
);
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:endpoints_manage")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_list_provider_keys_as_admin_proxy_route() {
let headers = http::HeaderMap::new();
let uri: Uri = "/api/admin/endpoints/providers/provider-openai/keys?skip=0&limit=20"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::GET, &uri, &headers)
.expect("decision should resolve");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("endpoints_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("list_provider_keys"));
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_keys_grouped_by_format_as_admin_proxy_route() {
let headers = http::HeaderMap::new();
let uri: Uri = "/api/admin/endpoints/keys/grouped-by-format"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::GET, &uri, &headers)
.expect("decision should resolve");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("endpoints_manage"));
assert_eq!(
decision.route_kind.as_deref(),
Some("keys_grouped_by_format")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_reveal_key_as_admin_proxy_route() {
let headers = http::HeaderMap::new();
let uri: Uri = "/api/admin/endpoints/keys/key-openai/reveal"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::GET, &uri, &headers)
.expect("decision should resolve");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("endpoints_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("reveal_key"));
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_export_key_as_admin_proxy_route() {
let headers = http::HeaderMap::new();
let uri: Uri = "/api/admin/endpoints/keys/key-openai/export"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::GET, &uri, &headers)
.expect("decision should resolve");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("endpoints_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("export_key"));
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_update_key_as_admin_proxy_route() {
let headers = http::HeaderMap::new();
let uri: Uri = "/api/admin/endpoints/keys/key-openai"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::PUT, &uri, &headers)
.expect("decision should resolve");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("endpoints_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("update_key"));
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_delete_key_as_admin_proxy_route() {
let headers = http::HeaderMap::new();
let uri: Uri = "/api/admin/endpoints/keys/key-openai"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::DELETE, &uri, &headers)
.expect("decision should resolve");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("endpoints_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("delete_key"));
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_batch_delete_keys_as_admin_proxy_route() {
let headers = http::HeaderMap::new();
let uri: Uri = "/api/admin/endpoints/keys/batch-delete"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::POST, &uri, &headers)
.expect("decision should resolve");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("endpoints_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("batch_delete_keys"));
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_clear_oauth_invalid_as_admin_proxy_route() {
let headers = http::HeaderMap::new();
let uri: Uri = "/api/admin/endpoints/keys/key-openai/clear-oauth-invalid"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::POST, &uri, &headers)
.expect("decision should resolve");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("endpoints_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("clear_oauth_invalid"));
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_create_provider_key_as_admin_proxy_route() {
let headers = http::HeaderMap::new();
let uri: Uri = "/api/admin/endpoints/providers/provider-openai/keys"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::POST, &uri, &headers)
.expect("decision should resolve");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("endpoints_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("create_provider_key"));
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_get_endpoint_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/endpoints/endpoint-1"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("endpoints_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("get_endpoint"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:endpoints_manage")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_create_endpoint_as_admin_proxy_route() {
let headers = http::HeaderMap::new();
let uri: Uri = "/api/admin/endpoints/providers/provider-openai/endpoints"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::POST, &uri, &headers)
.expect("decision should resolve");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("endpoints_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("create_endpoint"));
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_update_endpoint_as_admin_proxy_route() {
let headers = http::HeaderMap::new();
let uri: Uri = "/api/admin/endpoints/endpoint-openai-chat"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::PUT, &uri, &headers)
.expect("decision should resolve");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("endpoints_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("update_endpoint"));
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_delete_endpoint_as_admin_proxy_route() {
let headers = http::HeaderMap::new();
let uri: Uri = "/api/admin/endpoints/endpoint-openai-chat"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::DELETE, &uri, &headers)
.expect("decision should resolve");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("endpoints_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("delete_endpoint"));
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_refresh_provider_quota_as_admin_proxy_route() {
let headers = http::HeaderMap::new();
let uri: Uri = "/api/admin/endpoints/providers/provider-codex/refresh-quota"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::POST, &uri, &headers)
.expect("decision should resolve");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("endpoints_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("refresh_quota"));
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_default_body_rules_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/endpoints/defaults/openai:cli/body-rules?provider_type=codex"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("endpoints_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("default_body_rules"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:endpoints_manage")
);
assert!(!decision.is_execution_runtime_candidate());
}

View File

@@ -0,0 +1,387 @@
use super::*;
#[test]
fn classifies_admin_monitoring_audit_logs_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/monitoring/audit-logs"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("monitoring"));
assert_eq!(decision.route_kind.as_deref(), Some("audit_logs"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:monitoring")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_monitoring_trace_request_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/monitoring/trace/request-1"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("monitoring"));
assert_eq!(decision.route_kind.as_deref(), Some("trace_request"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:monitoring")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_monitoring_cache_stats_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/monitoring/cache/stats"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("monitoring"));
assert_eq!(decision.route_kind.as_deref(), Some("monitoring_cache"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:monitoring")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_monitoring_cache_affinities_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/monitoring/cache/affinities"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("monitoring"));
assert_eq!(decision.route_kind.as_deref(), Some("monitoring_cache"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:monitoring")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_monitoring_cache_affinity_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/monitoring/cache/affinity/user-1"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("monitoring"));
assert_eq!(decision.route_kind.as_deref(), Some("monitoring_cache"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:monitoring")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_monitoring_cache_users_delete_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/monitoring/cache/users/user-1"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::DELETE, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("monitoring"));
assert_eq!(decision.route_kind.as_deref(), Some("monitoring_cache"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:monitoring")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_monitoring_cache_affinity_delete_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/monitoring/cache/affinity/user-key-1/endpoint-1/model-alpha/openai"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::DELETE, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("monitoring"));
assert_eq!(decision.route_kind.as_deref(), Some("monitoring_cache"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:monitoring")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_monitoring_cache_flush_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/monitoring/cache"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::DELETE, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("monitoring"));
assert_eq!(decision.route_kind.as_deref(), Some("monitoring_cache"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:monitoring")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_monitoring_cache_provider_delete_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/monitoring/cache/providers/provider-1"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::DELETE, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("monitoring"));
assert_eq!(decision.route_kind.as_deref(), Some("monitoring_cache"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:monitoring")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_monitoring_cache_metrics_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/monitoring/cache/metrics"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("monitoring"));
assert_eq!(decision.route_kind.as_deref(), Some("monitoring_cache"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:monitoring")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_monitoring_cache_config_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/monitoring/cache/config"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("monitoring"));
assert_eq!(decision.route_kind.as_deref(), Some("monitoring_cache"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:monitoring")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_monitoring_model_mapping_stats_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/monitoring/cache/model-mapping/stats"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("monitoring"));
assert_eq!(decision.route_kind.as_deref(), Some("monitoring_cache"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:monitoring")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_monitoring_model_mapping_delete_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/monitoring/cache/model-mapping"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::DELETE, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("monitoring"));
assert_eq!(decision.route_kind.as_deref(), Some("monitoring_cache"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:monitoring")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_monitoring_model_mapping_delete_model_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/monitoring/cache/model-mapping/model-alpha"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::DELETE, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("monitoring"));
assert_eq!(decision.route_kind.as_deref(), Some("monitoring_cache"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:monitoring")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_monitoring_model_mapping_delete_provider_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/monitoring/cache/model-mapping/provider/provider-1/model-alpha"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::DELETE, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("monitoring"));
assert_eq!(decision.route_kind.as_deref(), Some("monitoring_cache"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:monitoring")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_monitoring_redis_keys_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/monitoring/cache/redis-keys"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("monitoring"));
assert_eq!(decision.route_kind.as_deref(), Some("monitoring_cache"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:monitoring")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_monitoring_redis_keys_delete_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/monitoring/cache/redis-keys/dashboard"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::DELETE, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("monitoring"));
assert_eq!(decision.route_kind.as_deref(), Some("monitoring_cache"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:monitoring")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_monitoring_resilience_status_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/monitoring/resilience-status"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("monitoring"));
assert_eq!(
decision.route_kind.as_deref(),
Some("monitoring_resilience")
);
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:monitoring")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_monitoring_resilience_error_stats_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/monitoring/resilience/error-stats"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::DELETE, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("monitoring"));
assert_eq!(
decision.route_kind.as_deref(),
Some("monitoring_resilience")
);
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:monitoring")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_monitoring_user_behavior_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/monitoring/user-behavior/user-1"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("monitoring"));
assert_eq!(decision.route_kind.as_deref(), Some("user_behavior"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:monitoring")
);
assert!(!decision.is_execution_runtime_candidate());
}

View File

@@ -0,0 +1,284 @@
use super::*;
#[test]
fn classifies_admin_provider_oauth_start_key_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/provider-oauth/keys/key-123/start"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_oauth_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("start_key_oauth"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:provider_oauth")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_oauth_start_provider_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/provider-oauth/providers/provider-123/start"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_oauth_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("start_provider_oauth"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:provider_oauth")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_oauth_batch_import_task_status_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/provider-oauth/providers/provider-123/batch-import/tasks/task-456"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_oauth_manage")
);
assert_eq!(
decision.route_kind.as_deref(),
Some("get_batch_import_task_status")
);
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:provider_oauth")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_oauth_maintenance_routes_as_admin_proxy_route() {
let headers = headers(&[]);
for (method, path, route_kind) in [
(
http::Method::POST,
"/api/admin/provider-oauth/keys/key-123/complete",
"complete_key_oauth",
),
(
http::Method::POST,
"/api/admin/provider-oauth/keys/key-123/refresh",
"refresh_key_oauth",
),
(
http::Method::POST,
"/api/admin/provider-oauth/providers/provider-123/complete",
"complete_provider_oauth",
),
(
http::Method::POST,
"/api/admin/provider-oauth/providers/provider-123/import-refresh-token",
"import_refresh_token",
),
(
http::Method::POST,
"/api/admin/provider-oauth/providers/provider-123/batch-import",
"batch_import_oauth",
),
(
http::Method::POST,
"/api/admin/provider-oauth/providers/provider-123/batch-import/tasks",
"start_batch_import_oauth_task",
),
(
http::Method::GET,
"/api/admin/provider-oauth/providers/provider-123/batch-import/tasks/task-123",
"get_batch_import_task_status",
),
(
http::Method::POST,
"/api/admin/provider-oauth/providers/provider-123/device-authorize",
"device_authorize",
),
(
http::Method::POST,
"/api/admin/provider-oauth/providers/provider-123/device-poll",
"device_poll",
),
] {
let uri: Uri = path.parse().expect("uri should parse");
let decision =
classify_control_route(&method, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_oauth_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some(route_kind));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:provider_oauth")
);
assert!(!decision.is_execution_runtime_candidate());
}
}
#[test]
fn classifies_admin_oauth_list_providers_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/oauth/providers?limit=20"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("oauth_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("list_providers"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:oauth")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_oauth_get_provider_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/oauth/providers/linuxdo"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("oauth_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("get_provider"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:oauth")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_oauth_upsert_provider_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/oauth/providers/linuxdo"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::PUT, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("oauth_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("upsert_provider"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:oauth")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_oauth_delete_provider_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/oauth/providers/linuxdo"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::DELETE, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("oauth_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("delete_provider"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:oauth")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_oauth_test_provider_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/oauth/providers/linuxdo/test"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("oauth_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("test_provider"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:oauth")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_get_management_token_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/management-tokens/token-123"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("management_tokens_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("get_token"));
}
#[test]
fn classifies_admin_delete_management_token_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/management-tokens/token-123"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::DELETE, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("management_tokens_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("delete_token"));
}
#[test]
fn classifies_admin_toggle_management_token_status_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/management-tokens/token-123/status"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::PATCH, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("management_tokens_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("toggle_status"));
}

View File

@@ -0,0 +1,136 @@
use super::*;
#[test]
fn classifies_admin_payments_list_orders_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/payments/orders"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("payments_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("list_orders"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:payments")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_payments_get_order_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/payments/orders/order-1"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("payments_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("get_order"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:payments")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_payments_trailing_slash_routes_as_admin_proxy_route() {
let headers = headers(&[]);
let detail_uri: Uri = "/api/admin/payments/orders/order-1/"
.parse()
.expect("uri should parse");
let detail = classify_control_route(&http::Method::GET, &detail_uri, &headers)
.expect("detail route should classify");
assert_eq!(detail.route_family.as_deref(), Some("payments_manage"));
assert_eq!(detail.route_kind.as_deref(), Some("get_order"));
let credit_uri: Uri = "/api/admin/payments/orders/order-1/credit/"
.parse()
.expect("uri should parse");
let credit = classify_control_route(&http::Method::POST, &credit_uri, &headers)
.expect("credit route should classify");
assert_eq!(credit.route_family.as_deref(), Some("payments_manage"));
assert_eq!(credit.route_kind.as_deref(), Some("credit_order"));
}
#[test]
fn classifies_admin_payments_expire_order_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/payments/orders/order-1/expire"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("payments_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("expire_order"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:payments")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_payments_credit_order_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/payments/orders/order-1/credit"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("payments_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("credit_order"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:payments")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_payments_fail_order_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/payments/orders/order-1/fail"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("payments_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("fail_order"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:payments")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_payments_callbacks_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/payments/callbacks"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("payments_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("list_callbacks"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:payments")
);
assert!(!decision.is_execution_runtime_candidate());
}

View File

@@ -0,0 +1,174 @@
use super::*;
#[test]
fn classifies_admin_pool_overview_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/pool/overview"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("pool_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("overview"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:pool")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_pool_scheduling_presets_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/pool/scheduling-presets"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("pool_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("scheduling_presets"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:pool")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_pool_provider_key_routes_as_admin_proxy_route() {
let headers = headers(&[]);
let list_uri: Uri = "/api/admin/pool/provider-1/keys?page=1"
.parse()
.expect("uri should parse");
let list = classify_control_route(&http::Method::GET, &list_uri, &headers)
.expect("route should classify");
assert_eq!(list.route_family.as_deref(), Some("pool_manage"));
assert_eq!(list.route_kind.as_deref(), Some("list_keys"));
let batch_import_uri: Uri = "/api/admin/pool/provider-1/keys/batch-import"
.parse()
.expect("uri should parse");
let batch_import = classify_control_route(&http::Method::POST, &batch_import_uri, &headers)
.expect("route should classify");
assert_eq!(batch_import.route_family.as_deref(), Some("pool_manage"));
assert_eq!(
batch_import.route_kind.as_deref(),
Some("batch_import_keys")
);
let batch_action_uri: Uri = "/api/admin/pool/provider-1/keys/batch-action"
.parse()
.expect("uri should parse");
let batch_action = classify_control_route(&http::Method::POST, &batch_action_uri, &headers)
.expect("route should classify");
assert_eq!(batch_action.route_family.as_deref(), Some("pool_manage"));
assert_eq!(
batch_action.route_kind.as_deref(),
Some("batch_action_keys")
);
let resolve_selection_uri: Uri = "/api/admin/pool/provider-1/keys/resolve-selection"
.parse()
.expect("uri should parse");
let resolve_selection =
classify_control_route(&http::Method::POST, &resolve_selection_uri, &headers)
.expect("route should classify");
assert_eq!(
resolve_selection.route_family.as_deref(),
Some("pool_manage")
);
assert_eq!(
resolve_selection.route_kind.as_deref(),
Some("resolve_selection")
);
let batch_delete_task_uri: Uri = "/api/admin/pool/provider-1/keys/batch-delete-task/task-1"
.parse()
.expect("uri should parse");
let batch_delete_task =
classify_control_route(&http::Method::GET, &batch_delete_task_uri, &headers)
.expect("route should classify");
assert_eq!(
batch_delete_task.route_family.as_deref(),
Some("pool_manage")
);
assert_eq!(
batch_delete_task.route_kind.as_deref(),
Some("batch_delete_task_status")
);
let cleanup_banned_uri: Uri = "/api/admin/pool/provider-1/keys/cleanup-banned"
.parse()
.expect("uri should parse");
let cleanup_banned = classify_control_route(&http::Method::POST, &cleanup_banned_uri, &headers)
.expect("route should classify");
assert_eq!(cleanup_banned.route_family.as_deref(), Some("pool_manage"));
assert_eq!(
cleanup_banned.route_kind.as_deref(),
Some("cleanup_banned_keys")
);
assert_eq!(
cleanup_banned.auth_endpoint_signature.as_deref(),
Some("admin:pool")
);
}
#[test]
fn classifies_admin_pool_trailing_slash_routes_as_admin_proxy_route() {
let headers = headers(&[]);
let list_uri: Uri = "/api/admin/pool/provider-1/keys/"
.parse()
.expect("uri should parse");
let list = classify_control_route(&http::Method::GET, &list_uri, &headers)
.expect("route should classify");
assert_eq!(list.route_family.as_deref(), Some("pool_manage"));
assert_eq!(list.route_kind.as_deref(), Some("list_keys"));
let resolve_selection_uri: Uri = "/api/admin/pool/provider-1/keys/resolve-selection/"
.parse()
.expect("uri should parse");
let resolve_selection =
classify_control_route(&http::Method::POST, &resolve_selection_uri, &headers)
.expect("route should classify");
assert_eq!(
resolve_selection.route_family.as_deref(),
Some("pool_manage")
);
assert_eq!(
resolve_selection.route_kind.as_deref(),
Some("resolve_selection")
);
}
#[test]
fn classifies_admin_pool_malformed_provider_id_routes_as_admin_proxy_route() {
let headers = headers(&[]);
let list_uri: Uri = "/api/admin/pool//keys".parse().expect("uri should parse");
let list = classify_control_route(&http::Method::GET, &list_uri, &headers)
.expect("route should classify");
assert_eq!(list.route_family.as_deref(), Some("pool_manage"));
assert_eq!(list.route_kind.as_deref(), Some("list_keys"));
let import_uri: Uri = "/api/admin/pool//keys/batch-import"
.parse()
.expect("uri should parse");
let import = classify_control_route(&http::Method::POST, &import_uri, &headers)
.expect("route should classify");
assert_eq!(import.route_family.as_deref(), Some("pool_manage"));
assert_eq!(import.route_kind.as_deref(), Some("batch_import_keys"));
let cleanup_uri: Uri = "/api/admin/pool//keys/cleanup-banned"
.parse()
.expect("uri should parse");
let cleanup = classify_control_route(&http::Method::POST, &cleanup_uri, &headers)
.expect("route should classify");
assert_eq!(cleanup.route_family.as_deref(), Some("pool_manage"));
assert_eq!(cleanup.route_kind.as_deref(), Some("cleanup_banned_keys"));
}

View File

@@ -0,0 +1,318 @@
use super::*;
#[test]
fn classifies_admin_provider_ops_architectures_list_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/provider-ops/architectures?limit=20"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_ops_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("list_architectures"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:provider_ops")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_ops_architecture_detail_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/provider-ops/architectures/generic_api"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_ops_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("get_architecture"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:provider_ops")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_ops_provider_status_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/provider-ops/providers/provider-openai/status"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_ops_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("get_provider_status"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:provider_ops")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_ops_provider_config_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/provider-ops/providers/provider-openai/config"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_ops_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("get_provider_config"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:provider_ops")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_ops_save_provider_config_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/provider-ops/providers/provider-openai/config"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::PUT, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_ops_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("save_provider_config"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:provider_ops")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_ops_delete_provider_config_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/provider-ops/providers/provider-openai/config"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::DELETE, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_ops_manage")
);
assert_eq!(
decision.route_kind.as_deref(),
Some("delete_provider_config")
);
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:provider_ops")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_ops_disconnect_provider_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/provider-ops/providers/provider-openai/disconnect"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_ops_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("disconnect_provider"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:provider_ops")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_ops_connect_provider_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/provider-ops/providers/provider-openai/connect"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_ops_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("connect_provider"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:provider_ops")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_ops_verify_provider_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/provider-ops/providers/provider-openai/verify"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_ops_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("verify_provider"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:provider_ops")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_ops_get_balance_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/provider-ops/providers/provider-openai/balance"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_ops_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("get_provider_balance"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:provider_ops")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_ops_refresh_balance_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/provider-ops/providers/provider-openai/balance"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_ops_manage")
);
assert_eq!(
decision.route_kind.as_deref(),
Some("refresh_provider_balance")
);
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:provider_ops")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_ops_checkin_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/provider-ops/providers/provider-openai/checkin"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_ops_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("provider_checkin"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:provider_ops")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_ops_execute_action_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/provider-ops/providers/provider-openai/actions/query_balance"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_ops_manage")
);
assert_eq!(
decision.route_kind.as_deref(),
Some("execute_provider_action")
);
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:provider_ops")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_ops_batch_balance_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/provider-ops/batch/balance"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_ops_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("batch_balance"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:provider_ops")
);
assert!(!decision.is_execution_runtime_candidate());
}

View File

@@ -0,0 +1,67 @@
use super::*;
#[test]
fn classifies_admin_provider_query_models_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/provider-query/models"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_query_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("query_models"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:provider_query")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_query_test_model_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/provider-query/test-model"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_query_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("test_model"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:provider_query")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_query_test_model_failover_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/provider-query/test-model-failover"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_query_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("test_model_failover"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:provider_query")
);
assert!(!decision.is_execution_runtime_candidate());
}

View File

@@ -0,0 +1,92 @@
use super::*;
#[test]
fn classifies_admin_provider_strategy_list_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/provider-strategy/strategies"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_strategy_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("list_strategies"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:provider_strategy")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_strategy_reset_quota_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/provider-strategy/providers/provider-openai/quota"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::DELETE, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_strategy_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("reset_provider_quota"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:provider_strategy")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_strategy_update_billing_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/provider-strategy/providers/provider-openai/billing"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::PUT, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_strategy_manage")
);
assert_eq!(
decision.route_kind.as_deref(),
Some("update_provider_billing")
);
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:provider_strategy")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_strategy_stats_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/provider-strategy/providers/provider-openai/stats?hours=48"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_strategy_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("get_provider_stats"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:provider_strategy")
);
assert!(!decision.is_execution_runtime_candidate());
}

View File

@@ -0,0 +1,691 @@
use super::*;
#[test]
fn classifies_admin_create_provider_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/providers/".parse().expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("providers_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("create_provider"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:providers")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_providers_summary_list_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/providers/summary?page=1&page_size=20"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("providers_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("summary_list"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:providers")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_update_provider_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/providers/provider-openai"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::PATCH, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("providers_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("update_provider"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:providers")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_delete_provider_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/providers/provider-openai"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::DELETE, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("providers_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("delete_provider"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:providers")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_summary_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/providers/provider-openai/summary"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("providers_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("provider_summary"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:providers")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_health_monitor_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/providers/provider-openai/health-monitor?lookback_hours=6"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("providers_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("health_monitor"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:providers")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_mapping_preview_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/providers/provider-openai/mapping-preview"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("providers_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("mapping_preview"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:providers")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_delete_task_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/providers/provider-openai/delete-task/task-1234"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("providers_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("delete_provider_task"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:providers")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_pool_status_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/providers/provider-openai/pool-status"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("providers_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("pool_status"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:providers")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_clear_pool_cooldown_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/providers/provider-openai/pool/clear-cooldown/key-openai"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("providers_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("clear_pool_cooldown"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:providers")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_reset_pool_cost_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/providers/provider-openai/pool/reset-cost/key-openai"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("providers_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("reset_pool_cost"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:providers")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_list_provider_models_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/providers/provider-openai/models?skip=0&limit=20"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_models_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("list_provider_models"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:providers")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_get_provider_model_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/providers/provider-openai/models/model-gpt-5"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_models_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("get_provider_model"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:providers")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_create_provider_model_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/providers/provider-openai/models"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_models_manage")
);
assert_eq!(
decision.route_kind.as_deref(),
Some("create_provider_model")
);
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:providers")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_update_provider_model_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/providers/provider-openai/models/model-gpt-5"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::PATCH, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_models_manage")
);
assert_eq!(
decision.route_kind.as_deref(),
Some("update_provider_model")
);
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:providers")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_delete_provider_model_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/providers/provider-openai/models/model-gpt-5"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::DELETE, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_models_manage")
);
assert_eq!(
decision.route_kind.as_deref(),
Some("delete_provider_model")
);
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:providers")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_batch_create_provider_models_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/providers/provider-openai/models/batch"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_models_manage")
);
assert_eq!(
decision.route_kind.as_deref(),
Some("batch_create_provider_models")
);
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:providers")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_provider_available_source_models_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/providers/provider-openai/available-source-models"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_models_manage")
);
assert_eq!(
decision.route_kind.as_deref(),
Some("available_source_models")
);
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:providers")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_assign_global_models_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/providers/provider-openai/assign-global-models"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_models_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("assign_global_models"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:providers")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_import_provider_models_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/providers/provider-openai/import-from-upstream"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("provider_models_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("import_from_upstream"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:providers")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_list_global_models_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/models/global?skip=0&limit=20"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("global_models_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("list_global_models"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:models")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_model_catalog_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/models/catalog"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("model_catalog_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("catalog"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:models")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_external_models_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/models/external"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("model_external_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("external"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:models")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_clear_external_models_cache_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/models/external/cache"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::DELETE, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("model_external_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("clear_external_cache"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:models")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_global_model_routing_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/models/global/global-gpt-5/routing"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("global_models_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("routing_preview"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:models")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_get_global_model_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/models/global/global-gpt-5"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("global_models_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("get_global_model"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:models")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_create_global_model_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/models/global"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("global_models_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("create_global_model"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:models")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_assign_global_model_to_providers_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/models/global/global-gpt-5/assign-to-providers"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("global_models_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("assign_to_providers"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:models")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_global_model_providers_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/models/global/global-gpt-5/providers"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("global_models_manage")
);
assert_eq!(
decision.route_kind.as_deref(),
Some("global_model_providers")
);
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:models")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_update_global_model_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/models/global/global-gpt-5"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::PATCH, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("global_models_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("update_global_model"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:models")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_delete_global_model_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/models/global/global-gpt-5"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::DELETE, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("global_models_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("delete_global_model"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:models")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_batch_delete_global_models_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/models/global/batch-delete"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("global_models_manage")
);
assert_eq!(
decision.route_kind.as_deref(),
Some("batch_delete_global_models")
);
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:models")
);
assert!(!decision.is_execution_runtime_candidate());
}

View File

@@ -0,0 +1,58 @@
use super::*;
#[test]
fn classifies_admin_proxy_nodes_list_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/proxy-nodes?status=online&skip=10&limit=20"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("proxy_nodes_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("list_nodes"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:proxy_nodes")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_proxy_nodes_register_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/proxy-nodes/register"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("proxy_nodes_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("register_node"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:proxy_nodes")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_proxy_nodes_events_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/proxy-nodes/node-1/events?limit=50"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("proxy_nodes_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("list_node_events"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:proxy_nodes")
);
assert!(!decision.is_execution_runtime_candidate());
}

View File

@@ -0,0 +1,134 @@
use super::*;
#[test]
fn classifies_admin_security_blacklist_add_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/security/ip/blacklist"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("security_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("blacklist_add"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:security")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_security_blacklist_remove_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/security/ip/blacklist/1.2.3.4"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::DELETE, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("security_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("blacklist_remove"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:security")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_security_blacklist_stats_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/security/ip/blacklist/stats"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("security_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("blacklist_stats"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:security")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_security_blacklist_list_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/security/ip/blacklist"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("security_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("blacklist_list"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:security")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_security_whitelist_add_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/security/ip/whitelist"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("security_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("whitelist_add"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:security")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_security_whitelist_remove_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/security/ip/whitelist/1.2.3.4"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::DELETE, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("security_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("whitelist_remove"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:security")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_security_whitelist_list_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/security/ip/whitelist"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("security_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("whitelist_list"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:security")
);
assert!(!decision.is_execution_runtime_candidate());
}

View File

@@ -0,0 +1,194 @@
use super::*;
#[test]
fn classifies_admin_stats_provider_quota_usage_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/stats/providers/quota-usage"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("stats_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("provider_quota_usage"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:stats")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_stats_comparison_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/stats/comparison"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("stats_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("comparison"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:stats")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_stats_error_distribution_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/stats/errors/distribution"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("stats_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("error_distribution"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:stats")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_stats_performance_percentiles_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/stats/performance/percentiles"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("stats_manage"));
assert_eq!(
decision.route_kind.as_deref(),
Some("performance_percentiles")
);
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:stats")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_stats_cost_forecast_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/stats/cost/forecast"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("stats_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("cost_forecast"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:stats")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_stats_leaderboard_api_keys_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/stats/leaderboard/api-keys"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("stats_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("leaderboard_api_keys"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:stats")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_stats_leaderboard_models_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/stats/leaderboard/models"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("stats_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("leaderboard_models"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:stats")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_stats_leaderboard_users_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/stats/leaderboard/users"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("stats_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("leaderboard_users"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:stats")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_stats_cost_savings_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/stats/cost/savings"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("stats_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("cost_savings"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:stats")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_stats_time_series_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/stats/time-series"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("stats_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("time_series"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:stats")
);
assert!(!decision.is_execution_runtime_candidate());
}

View File

@@ -0,0 +1,249 @@
use super::*;
#[test]
fn classifies_admin_usage_stats_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/usage/stats".parse().expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("usage_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("stats"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:usage")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_usage_aggregation_stats_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/usage/aggregation/stats"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("usage_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("aggregation_stats"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:usage")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_usage_heatmap_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/usage/heatmap"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("usage_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("heatmap"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:usage")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_usage_records_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/usage/records"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("usage_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("records"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:usage")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_usage_active_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/usage/active".parse().expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("usage_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("active"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:usage")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_usage_cache_affinity_hit_analysis_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/usage/cache-affinity/hit-analysis"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("usage_manage"));
assert_eq!(
decision.route_kind.as_deref(),
Some("cache_affinity_hit_analysis")
);
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:usage")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_usage_cache_affinity_interval_timeline_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/usage/cache-affinity/interval-timeline"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("usage_manage"));
assert_eq!(
decision.route_kind.as_deref(),
Some("cache_affinity_interval_timeline")
);
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:usage")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_usage_cache_affinity_ttl_analysis_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/usage/cache-affinity/ttl-analysis"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("usage_manage"));
assert_eq!(
decision.route_kind.as_deref(),
Some("cache_affinity_ttl_analysis")
);
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:usage")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_usage_detail_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/usage/usage-1"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("usage_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("detail"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:usage")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_usage_detail_with_empty_usage_id_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/usage/".parse().expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("usage_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("detail"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:usage")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_usage_curl_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/usage/usage-1/curl"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("usage_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("curl"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:usage")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_usage_curl_with_empty_usage_id_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/usage//curl".parse().expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("usage_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("curl"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:usage")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_usage_replay_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/usage/usage-1/replay"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("usage_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("replay"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:usage")
);
assert!(!decision.is_execution_runtime_candidate());
}

View File

@@ -0,0 +1,182 @@
use super::*;
#[test]
fn classifies_admin_users_list_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/users".parse().expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("users_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("list_users"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:users")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_users_create_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/users".parse().expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("users_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("create_user"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:users")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_user_detail_routes_as_admin_proxy_route() {
let headers = headers(&[]);
let get_uri: Uri = "/api/admin/users/user-1".parse().expect("uri should parse");
let get = classify_control_route(&http::Method::GET, &get_uri, &headers)
.expect("route should classify");
assert_eq!(get.route_family.as_deref(), Some("users_manage"));
assert_eq!(get.route_kind.as_deref(), Some("get_user"));
let put_uri: Uri = "/api/admin/users/user-1".parse().expect("uri should parse");
let put = classify_control_route(&http::Method::PUT, &put_uri, &headers)
.expect("route should classify");
assert_eq!(put.route_family.as_deref(), Some("users_manage"));
assert_eq!(put.route_kind.as_deref(), Some("update_user"));
let delete_uri: Uri = "/api/admin/users/user-1".parse().expect("uri should parse");
let delete = classify_control_route(&http::Method::DELETE, &delete_uri, &headers)
.expect("route should classify");
assert_eq!(delete.route_family.as_deref(), Some("users_manage"));
assert_eq!(delete.route_kind.as_deref(), Some("delete_user"));
assert_eq!(
delete.auth_endpoint_signature.as_deref(),
Some("admin:users")
);
}
#[test]
fn classifies_admin_user_session_routes_as_admin_proxy_route() {
let headers = headers(&[]);
let list_uri: Uri = "/api/admin/users/user-1/sessions"
.parse()
.expect("uri should parse");
let list = classify_control_route(&http::Method::GET, &list_uri, &headers)
.expect("route should classify");
assert_eq!(list.route_family.as_deref(), Some("users_manage"));
assert_eq!(list.route_kind.as_deref(), Some("list_user_sessions"));
let delete_all_uri: Uri = "/api/admin/users/user-1/sessions"
.parse()
.expect("uri should parse");
let delete_all = classify_control_route(&http::Method::DELETE, &delete_all_uri, &headers)
.expect("route should classify");
assert_eq!(delete_all.route_family.as_deref(), Some("users_manage"));
assert_eq!(
delete_all.route_kind.as_deref(),
Some("delete_user_sessions")
);
let delete_one_uri: Uri = "/api/admin/users/user-1/sessions/session-1"
.parse()
.expect("uri should parse");
let delete_one = classify_control_route(&http::Method::DELETE, &delete_one_uri, &headers)
.expect("route should classify");
assert_eq!(delete_one.route_family.as_deref(), Some("users_manage"));
assert_eq!(
delete_one.route_kind.as_deref(),
Some("delete_user_session")
);
assert_eq!(
delete_one.auth_endpoint_signature.as_deref(),
Some("admin:users")
);
}
#[test]
fn classifies_admin_user_api_key_routes_as_admin_proxy_route() {
let headers = headers(&[]);
let list_uri: Uri = "/api/admin/users/user-1/api-keys"
.parse()
.expect("uri should parse");
let list = classify_control_route(&http::Method::GET, &list_uri, &headers)
.expect("route should classify");
assert_eq!(list.route_family.as_deref(), Some("users_manage"));
assert_eq!(list.route_kind.as_deref(), Some("list_user_api_keys"));
let create_uri: Uri = "/api/admin/users/user-1/api-keys"
.parse()
.expect("uri should parse");
let create = classify_control_route(&http::Method::POST, &create_uri, &headers)
.expect("route should classify");
assert_eq!(create.route_family.as_deref(), Some("users_manage"));
assert_eq!(create.route_kind.as_deref(), Some("create_user_api_key"));
let delete_uri: Uri = "/api/admin/users/user-1/api-keys/key-1"
.parse()
.expect("uri should parse");
let delete = classify_control_route(&http::Method::DELETE, &delete_uri, &headers)
.expect("route should classify");
assert_eq!(delete.route_family.as_deref(), Some("users_manage"));
assert_eq!(delete.route_kind.as_deref(), Some("delete_user_api_key"));
let update_uri: Uri = "/api/admin/users/user-1/api-keys/key-1"
.parse()
.expect("uri should parse");
let update = classify_control_route(&http::Method::PUT, &update_uri, &headers)
.expect("route should classify");
assert_eq!(update.route_family.as_deref(), Some("users_manage"));
assert_eq!(update.route_kind.as_deref(), Some("update_user_api_key"));
let lock_uri: Uri = "/api/admin/users/user-1/api-keys/key-1/lock"
.parse()
.expect("uri should parse");
let lock = classify_control_route(&http::Method::PATCH, &lock_uri, &headers)
.expect("route should classify");
assert_eq!(lock.route_family.as_deref(), Some("users_manage"));
assert_eq!(lock.route_kind.as_deref(), Some("lock_user_api_key"));
let full_key_uri: Uri = "/api/admin/users/user-1/api-keys/key-1/full-key"
.parse()
.expect("uri should parse");
let full_key = classify_control_route(&http::Method::GET, &full_key_uri, &headers)
.expect("route should classify");
assert_eq!(full_key.route_family.as_deref(), Some("users_manage"));
assert_eq!(full_key.route_kind.as_deref(), Some("reveal_user_api_key"));
assert_eq!(
full_key.auth_endpoint_signature.as_deref(),
Some("admin:users")
);
}
#[test]
fn classifies_admin_user_api_key_mutation_routes_with_admin_users_signature() {
let headers = headers(&[]);
let create_uri: Uri = "/api/admin/users/user-1/api-keys"
.parse()
.expect("uri should parse");
let create = classify_control_route(&http::Method::POST, &create_uri, &headers)
.expect("route should classify");
assert_eq!(create.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
create.auth_endpoint_signature.as_deref(),
Some("admin:users")
);
let lock_uri: Uri = "/api/admin/users/user-1/api-keys/key-1/lock"
.parse()
.expect("uri should parse");
let lock = classify_control_route(&http::Method::PATCH, &lock_uri, &headers)
.expect("route should classify");
assert_eq!(lock.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(lock.auth_endpoint_signature.as_deref(), Some("admin:users"));
}

View File

@@ -0,0 +1,96 @@
use super::*;
#[test]
fn classifies_admin_video_tasks_list_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/video-tasks?status=completed&page=2"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("video_tasks_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("list_tasks"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:video_tasks")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_video_tasks_stats_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/video-tasks/stats"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("video_tasks_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("stats"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:video_tasks")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_video_tasks_detail_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/video-tasks/task-123"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("video_tasks_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("detail"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:video_tasks")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_video_tasks_cancel_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/video-tasks/task-123/cancel"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("video_tasks_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("cancel"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:video_tasks")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_video_tasks_video_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/video-tasks/task-123/video"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("video_tasks_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("video"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:video_tasks")
);
assert!(!decision.is_execution_runtime_candidate());
}

View File

@@ -0,0 +1,254 @@
use super::*;
#[test]
fn classifies_admin_wallets_list_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/wallets?status=active&limit=20"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("wallets_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("list_wallets"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:wallets")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_wallets_ledger_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/wallets/ledger?owner_type=user"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("wallets_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("ledger"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:wallets")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_wallets_refund_requests_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/wallets/refund-requests?status=pending_approval"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("wallets_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("list_refund_requests"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:wallets")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_wallets_detail_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/wallets/wallet-123"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("wallets_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("wallet_detail"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:wallets")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_wallets_transactions_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/wallets/wallet-123/transactions?limit=50"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("wallets_manage"));
assert_eq!(
decision.route_kind.as_deref(),
Some("list_wallet_transactions")
);
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:wallets")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_wallets_transactions_with_empty_wallet_id_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/wallets//transactions"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("wallets_manage"));
assert_eq!(
decision.route_kind.as_deref(),
Some("list_wallet_transactions")
);
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:wallets")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_wallets_refunds_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/wallets/wallet-123/refunds?limit=50"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("wallets_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("list_wallet_refunds"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:wallets")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_wallets_adjust_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/wallets/wallet-123/adjust"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("wallets_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("adjust_balance"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:wallets")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_wallets_recharge_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/wallets/wallet-123/recharge"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("wallets_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("recharge_balance"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:wallets")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_wallets_process_refund_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/wallets/wallet-123/refunds/refund-1/process"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("wallets_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("process_refund"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:wallets")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_wallets_process_refund_with_empty_refund_id_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/wallets/wallet-123/refunds//process"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("wallets_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("process_refund"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:wallets")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_wallets_complete_refund_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/wallets/wallet-123/refunds/refund-1/complete"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("wallets_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("complete_refund"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:wallets")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_wallets_fail_refund_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/admin/wallets/wallet-123/refunds/refund-1/fail"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("wallets_manage"));
assert_eq!(decision.route_kind.as_deref(), Some("fail_refund"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:wallets")
);
assert!(!decision.is_execution_runtime_candidate());
}

View File

@@ -0,0 +1,87 @@
use super::*;
#[test]
fn classifies_claude_count_tokens_as_non_execution_runtime_public_route() {
let headers = headers(&[("x-api-key", "sk-test")]);
let uri: Uri = "/v1/messages/count_tokens"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_family.as_deref(), Some("claude"));
assert_eq!(decision.route_kind.as_deref(), Some("count_tokens"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("claude:chat")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_models_list_as_claude_when_headers_match() {
let headers = headers(&[
("x-api-key", "sk-claude"),
("anthropic-version", "2023-06-01"),
]);
let uri: Uri = "/v1/models".parse().expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("claude:chat")
);
}
#[test]
fn classifies_claude_messages_cli_when_bearer_without_api_key() {
let headers = headers(&[("authorization", "Bearer token-123")]);
let uri: Uri = "/v1/messages".parse().expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_family.as_deref(), Some("claude"));
assert_eq!(decision.route_kind.as_deref(), Some("cli"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("claude:cli")
);
assert!(decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_gemini_cli_generate_content_when_x_app_contains_cli() {
let headers = headers(&[("x-app", "Gemini-CLI")]);
let uri: Uri = "/v1beta/models/gemini-2.5-pro:generateContent"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_family.as_deref(), Some("gemini"));
assert_eq!(decision.route_kind.as_deref(), Some("cli"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("gemini:cli")
);
assert!(decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_gemini_predict_long_running_as_video_route() {
let headers = headers(&[]);
let uri: Uri = "/v1beta/models/veo-3:predictLongRunning"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_family.as_deref(), Some("gemini"));
assert_eq!(decision.route_kind.as_deref(), Some("video"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("gemini:video")
);
assert!(decision.is_execution_runtime_candidate());
}

View File

@@ -0,0 +1,55 @@
use super::*;
#[test]
fn classifies_internal_tunnel_heartbeat_as_internal_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/internal/tunnel/heartbeat"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("internal_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some(crate::gateway::TUNNEL_ROUTE_FAMILY)
);
assert_eq!(decision.route_kind.as_deref(), Some("heartbeat"));
assert_eq!(decision.auth_endpoint_signature.as_deref(), Some(""));
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_internal_gateway_resolve_as_internal_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/internal/gateway/resolve"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("internal_proxy"));
assert_eq!(decision.route_family.as_deref(), Some("gateway_legacy"));
assert_eq!(decision.route_kind.as_deref(), Some("resolve"));
assert_eq!(decision.auth_endpoint_signature.as_deref(), Some(""));
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_internal_tunnel_node_status_as_internal_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/internal/tunnel/node-status"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("internal_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some(crate::gateway::TUNNEL_ROUTE_FAMILY)
);
assert_eq!(decision.route_kind.as_deref(), Some("node_status"));
assert_eq!(decision.auth_endpoint_signature.as_deref(), Some(""));
assert!(!decision.is_execution_runtime_candidate());
}

View File

@@ -0,0 +1,88 @@
use super::*;
fn headers(items: &[(&str, &str)]) -> http::HeaderMap {
let mut headers = http::HeaderMap::new();
for (name, value) in items {
headers.insert(
http::header::HeaderName::from_bytes(name.as_bytes()).expect("valid header name"),
http::HeaderValue::from_str(value).expect("valid header value"),
);
}
headers
}
#[test]
fn builds_public_request_context_from_request_parts() {
let mut headers = headers(&[
(http::header::HOST.as_str(), "api.example.test"),
(http::header::CONTENT_TYPE.as_str(), "application/json"),
]);
headers.insert("x-app", http::HeaderValue::from_static("gemini-cli"));
let uri: Uri = "/v1beta/models/gemini-2.5-pro:generateContent?alt=sse"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::POST, &uri, &headers);
let context = GatewayPublicRequestContext::from_request_parts(
"trace-123",
&http::Method::POST,
&uri,
&headers,
decision,
);
assert_eq!(context.trace_id, "trace-123");
assert_eq!(context.request_method, http::Method::POST);
assert_eq!(
context.request_path,
"/v1beta/models/gemini-2.5-pro:generateContent"
);
assert_eq!(context.request_query_string.as_deref(), Some("alt=sse"));
assert_eq!(
context.request_path_and_query(),
"/v1beta/models/gemini-2.5-pro:generateContent?alt=sse"
);
assert_eq!(
context.request_content_type.as_deref(),
Some("application/json")
);
assert_eq!(context.host_header.as_deref(), Some("api.example.test"));
assert_eq!(
context
.control_decision
.as_ref()
.and_then(|value| value.route_family.as_deref()),
Some("gemini")
);
assert_eq!(
context
.control_decision
.as_ref()
.and_then(|value| value.route_kind.as_deref()),
Some("cli")
);
}
mod admin_adaptive;
mod admin_api_keys;
mod admin_billing;
mod admin_core;
mod admin_endpoints;
mod admin_monitoring;
mod admin_oauth;
mod admin_payments;
mod admin_pool;
mod admin_provider_ops;
mod admin_provider_query;
mod admin_provider_strategy;
mod admin_providers_models;
mod admin_proxy_nodes;
mod admin_security;
mod admin_stats;
mod admin_usage;
mod admin_users;
mod admin_video_tasks;
mod admin_wallets;
mod ai;
mod internal;
mod public_support;

View File

@@ -0,0 +1,817 @@
use super::*;
#[test]
fn classifies_models_list_as_public_support_route() {
let headers = headers(&[("authorization", "Bearer sk-test")]);
let uri: Uri = "/v1/models".parse().expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(decision.route_family.as_deref(), Some("models"));
assert_eq!(decision.route_kind.as_deref(), Some("list"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("openai:chat")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_v1beta_models_as_gemini_public_support_route() {
let headers = headers(&[]);
let uri: Uri = "/v1beta/models?pageSize=10"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(decision.route_family.as_deref(), Some("models"));
assert_eq!(decision.route_kind.as_deref(), Some("list"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("gemini:chat")
);
}
#[test]
fn classifies_public_catalog_site_info_as_public_support_route() {
let headers = headers(&[]);
let uri: Uri = "/api/public/site-info".parse().expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(decision.route_family.as_deref(), Some("public_catalog"));
assert_eq!(decision.route_kind.as_deref(), Some("site_info"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("public:catalog")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_public_announcement_list_as_public_support_route() {
let headers = headers(&[]);
let uri: Uri = "/api/announcements?limit=20"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(decision.route_family.as_deref(), Some("announcements"));
assert_eq!(decision.route_kind.as_deref(), Some("list"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("public:announcements")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_announcement_create_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/announcements".parse().expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("announcements_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("create_announcement"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:announcements")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_announcement_update_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/announcements/announcement-1"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::PUT, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("announcements_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("update_announcement"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:announcements")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_admin_announcement_delete_as_admin_proxy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/announcements/announcement-1"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::DELETE, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("admin_proxy"));
assert_eq!(
decision.route_family.as_deref(),
Some("announcements_manage")
);
assert_eq!(decision.route_kind.as_deref(), Some("delete_announcement"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("admin:announcements")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_public_active_announcements_as_public_support_route() {
let headers = headers(&[]);
let uri: Uri = "/api/announcements/active"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(decision.route_family.as_deref(), Some("announcements"));
assert_eq!(decision.route_kind.as_deref(), Some("active"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("public:announcements")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_public_announcement_detail_as_public_support_route() {
let headers = headers(&[]);
let uri: Uri = "/api/announcements/announcement-1"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(decision.route_family.as_deref(), Some("announcements"));
assert_eq!(decision.route_kind.as_deref(), Some("detail"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("public:announcements")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_dashboard_stats_as_public_support_legacy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/dashboard/stats".parse().expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(decision.route_family.as_deref(), Some("dashboard_legacy"));
assert_eq!(decision.route_kind.as_deref(), Some("stats"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("user:dashboard")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_user_monitoring_audit_logs_as_public_support_legacy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/monitoring/my-audit-logs"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(
decision.route_family.as_deref(),
Some("monitoring_user_legacy")
);
assert_eq!(decision.route_kind.as_deref(), Some("audit_logs"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("user:monitoring")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_announcement_unread_count_as_public_support_legacy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/announcements/users/me/unread-count"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(
decision.route_family.as_deref(),
Some("announcement_user_legacy")
);
assert_eq!(decision.route_kind.as_deref(), Some("unread_count"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("user:announcements")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_announcement_read_status_as_public_support_legacy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/announcements/announcement-1/read-status"
.parse()
.expect("uri should parse");
let decision = classify_control_route(&http::Method::PATCH, &uri, &headers)
.expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(
decision.route_family.as_deref(),
Some("announcement_user_legacy")
);
assert_eq!(decision.route_kind.as_deref(), Some("read_status"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("user:announcements")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_announcement_read_all_as_public_support_legacy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/announcements/read-all"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(
decision.route_family.as_deref(),
Some("announcement_user_legacy")
);
assert_eq!(decision.route_kind.as_deref(), Some("read_all"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("user:announcements")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_wallet_legacy_routes_as_public_support_legacy_route() {
let headers = headers(&[]);
for (method, uri, route_kind) in [
(http::Method::GET, "/api/wallet/balance", "balance"),
(
http::Method::GET,
"/api/wallet/transactions?limit=20",
"transactions",
),
(http::Method::GET, "/api/wallet/flow?limit=20", "flow"),
(http::Method::GET, "/api/wallet/today-cost", "today_cost"),
(
http::Method::GET,
"/api/wallet/recharge?limit=20",
"list_recharge_orders",
),
(
http::Method::POST,
"/api/wallet/recharge",
"create_recharge_order",
),
(
http::Method::GET,
"/api/wallet/recharge/order-1",
"recharge_detail",
),
(
http::Method::GET,
"/api/wallet/refunds?limit=20",
"list_refunds",
),
(http::Method::POST, "/api/wallet/refunds", "create_refund"),
(
http::Method::GET,
"/api/wallet/refunds/refund-1",
"refund_detail",
),
] {
let uri: Uri = uri.parse().expect("uri should parse");
let decision =
classify_control_route(&method, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(decision.route_family.as_deref(), Some("wallet_legacy"));
assert_eq!(decision.route_kind.as_deref(), Some(route_kind));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("user:wallet")
);
assert!(!decision.is_execution_runtime_candidate());
}
}
#[test]
fn classifies_users_me_legacy_routes_as_public_support_legacy_route() {
let headers = headers(&[]);
for (method, uri, route_kind) in [
(http::Method::GET, "/api/users/me", "detail"),
(http::Method::PUT, "/api/users/me", "update_detail"),
(http::Method::PATCH, "/api/users/me/password", "password"),
(http::Method::GET, "/api/users/me/sessions", "sessions"),
(
http::Method::DELETE,
"/api/users/me/sessions/others",
"sessions_others_delete",
),
(
http::Method::PATCH,
"/api/users/me/sessions/session-1",
"session_update",
),
(
http::Method::GET,
"/api/users/me/api-keys/key-1",
"api_key_detail",
),
(
http::Method::POST,
"/api/users/me/api-keys",
"api_keys_create",
),
(
http::Method::PUT,
"/api/users/me/api-keys/key-1",
"api_key_update",
),
(
http::Method::PATCH,
"/api/users/me/api-keys/key-1",
"api_key_patch",
),
(
http::Method::DELETE,
"/api/users/me/api-keys/key-1",
"api_key_delete",
),
(
http::Method::PUT,
"/api/users/me/api-keys/key-1/providers",
"api_key_providers_update",
),
(
http::Method::PUT,
"/api/users/me/api-keys/key-1/capabilities",
"api_key_capabilities_update",
),
(
http::Method::GET,
"/api/users/me/available-models",
"available_models",
),
(
http::Method::PUT,
"/api/users/me/model-capabilities",
"model_capabilities_update",
),
(
http::Method::GET,
"/api/me/management-tokens",
"management_tokens_list",
),
(
http::Method::POST,
"/api/me/management-tokens",
"management_tokens_create",
),
(
http::Method::GET,
"/api/me/management-tokens/token-1",
"management_token_detail",
),
(
http::Method::PUT,
"/api/me/management-tokens/token-1",
"management_token_update",
),
(
http::Method::DELETE,
"/api/me/management-tokens/token-1",
"management_token_delete",
),
(
http::Method::PATCH,
"/api/me/management-tokens/token-1/status",
"management_token_toggle",
),
(
http::Method::POST,
"/api/me/management-tokens/token-1/regenerate",
"management_token_regenerate",
),
] {
let uri: Uri = uri.parse().expect("uri should parse");
let decision =
classify_control_route(&method, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(decision.route_family.as_deref(), Some("users_me_legacy"));
assert_eq!(decision.route_kind.as_deref(), Some(route_kind));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("user:self")
);
assert!(!decision.is_execution_runtime_candidate());
}
}
#[test]
fn classifies_payment_callback_as_public_support_legacy_route() {
let headers = headers(&[]);
let uri: Uri = "/api/payment/callback/alipay"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(
decision.route_family.as_deref(),
Some("payment_callback_legacy")
);
assert_eq!(decision.route_kind.as_deref(), Some("callback"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("public:payment")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_public_catalog_providers_as_public_support_route() {
let headers = headers(&[]);
let uri: Uri = "/api/public/providers?limit=20"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(decision.route_family.as_deref(), Some("public_catalog"));
assert_eq!(decision.route_kind.as_deref(), Some("providers"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("public:catalog")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_public_catalog_models_as_public_support_route() {
let headers = headers(&[]);
let uri: Uri = "/api/public/models?provider_id=provider-openai"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(decision.route_family.as_deref(), Some("public_catalog"));
assert_eq!(decision.route_kind.as_deref(), Some("models"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("public:catalog")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_public_catalog_search_models_as_public_support_route() {
let headers = headers(&[]);
let uri: Uri = "/api/public/search/models?q=gpt"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(decision.route_family.as_deref(), Some("public_catalog"));
assert_eq!(decision.route_kind.as_deref(), Some("search_models"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("public:catalog")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_public_catalog_stats_as_public_support_route() {
let headers = headers(&[]);
let uri: Uri = "/api/public/stats".parse().expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(decision.route_family.as_deref(), Some("public_catalog"));
assert_eq!(decision.route_kind.as_deref(), Some("stats"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("public:catalog")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_public_catalog_global_models_as_public_support_route() {
let headers = headers(&[]);
let uri: Uri = "/api/public/global-models?limit=10"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(decision.route_family.as_deref(), Some("public_catalog"));
assert_eq!(decision.route_kind.as_deref(), Some("global_models"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("public:catalog")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_public_catalog_health_api_formats_as_public_support_route() {
let headers = headers(&[]);
let uri: Uri = "/api/public/health/api-formats?lookback_hours=12"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(decision.route_family.as_deref(), Some("public_catalog"));
assert_eq!(decision.route_kind.as_deref(), Some("health_api_formats"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("public:catalog")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_auth_registration_settings_as_public_support_route() {
let headers = headers(&[]);
let uri: Uri = "/api/auth/registration-settings"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(decision.route_family.as_deref(), Some("auth_public"));
assert_eq!(
decision.route_kind.as_deref(),
Some("registration_settings")
);
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("public:auth")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_auth_settings_as_public_support_route() {
let headers = headers(&[]);
let uri: Uri = "/api/auth/settings".parse().expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(decision.route_family.as_deref(), Some("auth_public"));
assert_eq!(decision.route_kind.as_deref(), Some("settings"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("public:auth")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_auth_legacy_routes_as_public_support_route() {
for (method, path, route_kind) in [
(http::Method::POST, "/api/auth/login", "login"),
(http::Method::POST, "/api/auth/refresh", "refresh"),
(http::Method::POST, "/api/auth/register", "register"),
(http::Method::GET, "/api/auth/me", "me"),
(http::Method::POST, "/api/auth/logout", "logout"),
(
http::Method::POST,
"/api/auth/send-verification-code",
"send_verification_code",
),
(http::Method::POST, "/api/auth/verify-email", "verify_email"),
(
http::Method::POST,
"/api/auth/verification-status",
"verification_status",
),
] {
let headers = headers(&[]);
let uri: Uri = path.parse().expect("uri should parse");
let decision =
classify_control_route(&method, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(decision.route_family.as_deref(), Some("auth_legacy"));
assert_eq!(decision.route_kind.as_deref(), Some(route_kind));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("user:auth")
);
assert!(!decision.is_execution_runtime_candidate());
}
}
#[test]
fn classifies_oauth_public_providers_as_public_support_route() {
let headers = headers(&[]);
let uri: Uri = "/api/oauth/providers".parse().expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(
decision.route_family.as_deref(),
Some("oauth_public_legacy")
);
assert_eq!(decision.route_kind.as_deref(), Some("providers"));
assert_eq!(decision.auth_endpoint_signature.as_deref(), Some(""));
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_oauth_public_authorize_as_public_support_route() {
let headers = headers(&[]);
let uri: Uri = "/api/oauth/linuxdo/authorize?client_device_id=device-1"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(
decision.route_family.as_deref(),
Some("oauth_public_legacy")
);
assert_eq!(decision.route_kind.as_deref(), Some("authorize"));
assert_eq!(decision.auth_endpoint_signature.as_deref(), Some(""));
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_oauth_user_bindable_providers_as_public_support_route() {
let headers = headers(&[]);
let uri: Uri = "/api/user/oauth/bindable-providers"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(decision.route_family.as_deref(), Some("oauth_user_legacy"));
assert_eq!(decision.route_kind.as_deref(), Some("bindable_providers"));
assert_eq!(decision.auth_endpoint_signature.as_deref(), Some(""));
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_oauth_user_bind_token_as_public_support_route() {
let headers = headers(&[]);
let uri: Uri = "/api/user/oauth/linuxdo/bind-token"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::POST, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(decision.route_family.as_deref(), Some("oauth_user_legacy"));
assert_eq!(decision.route_kind.as_deref(), Some("bind_token"));
assert_eq!(decision.auth_endpoint_signature.as_deref(), Some(""));
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_capabilities_list_as_public_support_route() {
let headers = headers(&[]);
let uri: Uri = "/api/capabilities".parse().expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(decision.route_family.as_deref(), Some("capabilities"));
assert_eq!(decision.route_kind.as_deref(), Some("list"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("public:capabilities")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_capabilities_user_configurable_as_public_support_route() {
let headers = headers(&[]);
let uri: Uri = "/api/capabilities/user-configurable"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(decision.route_family.as_deref(), Some("capabilities"));
assert_eq!(decision.route_kind.as_deref(), Some("user_configurable"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("public:capabilities")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_capabilities_model_as_public_support_route() {
let headers = headers(&[]);
let uri: Uri = "/api/capabilities/model/gpt-5"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(decision.route_family.as_deref(), Some("capabilities"));
assert_eq!(decision.route_kind.as_deref(), Some("model"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("public:capabilities")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_modules_auth_status_as_public_support_route() {
let headers = headers(&[]);
let uri: Uri = "/api/modules/auth-status"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(decision.route_family.as_deref(), Some("modules"));
assert_eq!(decision.route_kind.as_deref(), Some("auth_status"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("public:modules")
);
assert!(!decision.is_execution_runtime_candidate());
}
#[test]
fn classifies_system_catalog_provider_detail_as_public_support_route() {
let headers = headers(&[]);
let uri: Uri = "/v1/providers/provider-openai"
.parse()
.expect("uri should parse");
let decision =
classify_control_route(&http::Method::GET, &uri, &headers).expect("route should classify");
assert_eq!(decision.route_class.as_deref(), Some("public_support"));
assert_eq!(decision.route_family.as_deref(), Some("system_catalog"));
assert_eq!(decision.route_kind.as_deref(), Some("provider_detail"));
assert_eq!(
decision.auth_endpoint_signature.as_deref(),
Some("public:system_catalog")
);
assert!(!decision.is_execution_runtime_candidate());
}