use axum::{
middleware,
routing::{delete, get, patch, post, put},
Router,
};
use crate::handlers;
use crate::middleware::{auth_middleware, rate_limit_middleware};
use crate::AppState;
pub fn create_router(state: AppState) -> Router<AppState> {
let public_routes = Router::new()
.route("/health", get(handlers::health::health_check))
.route("/health/live", get(handlers::health::liveness_check))
.route("/health/ready", get(handlers::health::readiness_check))
.route("/health/circuits", get(handlers::health::circuit_breaker_status))
.route("/api/v1/plugins/search", post(handlers::plugins::search_plugins))
.route("/api/v1/plugins/{name}", get(handlers::plugins::get_plugin))
.route("/api/v1/plugins/{name}/versions/{version}", get(handlers::plugins::get_version))
.route("/api/v1/plugins/{name}/reviews", get(handlers::reviews::get_reviews))
.route("/api/v1/plugins/{name}/badges", get(handlers::admin::get_plugin_badges))
.route("/api/v1/plugins/{name}/security", get(handlers::plugins::get_plugin_security))
.route("/api/v1/stats", get(handlers::stats::get_stats))
.route("/api/v1/auth/register", post(handlers::auth::register))
.route("/api/v1/auth/login", post(handlers::auth::login))
.route("/api/v1/auth/token/refresh", post(handlers::auth::refresh_token))
.route(
"/api/v1/auth/password/reset-request",
post(handlers::auth::request_password_reset),
)
.route("/api/v1/auth/password/reset", post(handlers::auth::confirm_password_reset))
.route("/api/v1/auth/verify-email", get(handlers::verification::verify_email))
.route("/api/v1/faq", get(handlers::faq::get_faq))
.route("/api/v1/legal/terms", get(handlers::legal::get_terms))
.route("/api/v1/legal/privacy", get(handlers::legal::get_privacy))
.route("/api/v1/legal/dpa", get(handlers::legal::get_dpa))
.route("/api/v1/status", get(handlers::status::get_status))
.route("/api/v1/support/contact", post(handlers::support::submit_contact))
.route(
"/api/v1/hosted-mocks/{deployment_id}/log-ingest",
post(handlers::hosted_mocks::ingest_runtime_logs),
)
.route(
"/api/v1/hosted-mocks/{deployment_id}/otlp/v1/traces",
post(handlers::otlp::ingest_traces),
)
.route(
"/api/v1/hosted-mocks/{deployment_id}/captures/ingest",
post(handlers::hosted_mocks::ingest_runtime_captures),
)
.route("/api/v1/waitlist/subscribe", post(handlers::waitlist::subscribe))
.route("/api/v1/waitlist/unsubscribe", get(handlers::waitlist::unsubscribe))
.route("/api/v1/marketplace/scenarios/search", post(handlers::scenarios::search_scenarios))
.route("/api/v1/marketplace/scenarios/{name}", get(handlers::scenarios::get_scenario))
.route("/api/v1/marketplace/scenarios/{name}/versions/{version}", get(handlers::scenarios::get_scenario_version))
.route("/api/v1/marketplace/scenarios/{name}/reviews", get(handlers::scenario_reviews::get_scenario_reviews))
.route("/api/v1/marketplace/templates/search", post(handlers::templates::search_templates))
.route("/api/v1/marketplace/templates/{name}/{version}", get(handlers::templates::get_template))
.route("/api/v1/marketplace/templates/{name}/{version}/reviews", get(handlers::template_reviews::get_template_reviews))
.route_layer(middleware::from_fn(rate_limit_middleware));
let auth_routes = Router::new()
.route("/api/v1/plugins/publish", post(handlers::plugins::publish_plugin))
.route(
"/api/v1/plugins/{name}/versions/{version}/yank",
delete(handlers::plugins::yank_version),
)
.route("/api/v1/plugins/{name}/reviews", post(handlers::reviews::submit_review))
.route(
"/api/v1/plugins/{name}/reviews/{review_id}/vote",
post(handlers::reviews::vote_review),
)
.route("/api/v1/auth/verify", get(handlers::auth::verify_token))
.route("/api/v1/auth/me", get(handlers::auth::me))
.route(
"/api/v1/users/me/public-keys",
get(handlers::public_keys::list_my_public_keys),
)
.route(
"/api/v1/users/me/public-keys",
post(handlers::public_keys::create_my_public_key),
)
.route(
"/api/v1/users/me/public-keys/{id}",
delete(handlers::public_keys::revoke_my_public_key),
)
.route("/api/v1/auth/2fa/setup", get(handlers::two_factor::setup_2fa))
.route("/api/v1/auth/2fa/verify-setup", post(handlers::two_factor::verify_2fa_setup_with_secret))
.route("/api/v1/auth/2fa/disable", post(handlers::two_factor::disable_2fa))
.route("/api/v1/auth/2fa/status", get(handlers::two_factor::get_2fa_status))
.route("/api/v1/auth/change-password", post(handlers::auth::change_password))
.route("/api/v1/users/me", get(handlers::users_me::get_me))
.route("/api/v1/users/me", patch(handlers::users_me::update_me))
.route("/api/v1/users/me/notifications", patch(handlers::users_me::update_notifications))
.route("/api/v1/users/me/preferences", get(handlers::users_me::get_preferences))
.route("/api/v1/users/me/preferences", patch(handlers::users_me::update_preferences))
.route("/api/v1/organizations", get(handlers::organizations::list_organizations))
.route("/api/v1/organizations", post(handlers::organizations::create_organization))
.route("/api/v1/organizations/{org_id}", get(handlers::organizations::get_organization))
.route("/api/v1/organizations/{org_id}", patch(handlers::organizations::update_organization))
.route("/api/v1/organizations/{org_id}", delete(handlers::organizations::delete_organization))
.route("/api/v1/organizations/{org_id}/members", get(handlers::organizations::get_organization_members))
.route("/api/v1/organizations/{org_id}/members", post(handlers::organizations::add_organization_member))
.route("/api/v1/organizations/{org_id}/members/{user_id}", patch(handlers::organizations::update_organization_member_role))
.route("/api/v1/organizations/{org_id}/members/{user_id}", delete(handlers::organizations::remove_organization_member))
.route("/api/v1/organizations/slug/{slug}", get(handlers::organizations::get_organization_by_slug))
.route("/api/v1/organizations/{org_id}/quota", get(handlers::organizations::get_organization_quota))
.route("/api/v1/organizations/{org_id}/quota", put(handlers::organizations::set_organization_quota))
.route("/api/v1/organizations/{org_id}/invitations", post(handlers::organizations::create_invitation))
.route("/api/v1/invitations/{token}", get(handlers::organizations::get_invitation))
.route("/api/v1/invitations/{token}/accept", post(handlers::organizations::accept_invitation))
.route("/api/v1/users/email/{email}", get(handlers::organizations::find_user_by_email_admin))
.route("/api/v1/users/username/{username}", get(handlers::organizations::find_user_by_username_admin))
.route("/api/v1/users/{user_id}/verify", post(handlers::organizations::mark_user_verified_admin))
.route("/api/v1/organizations/{org_id}/settings", get(handlers::organization_settings::get_organization_settings))
.route("/api/v1/organizations/{org_id}/settings", patch(handlers::organization_settings::update_organization_settings))
.route("/api/v1/organizations/{org_id}/settings/ai", get(handlers::organization_settings::get_organization_ai_settings))
.route("/api/v1/organizations/{org_id}/settings/ai", patch(handlers::organization_settings::update_organization_ai_settings))
.route("/api/v1/organizations/{org_id}/usage", get(handlers::organization_settings::get_organization_usage))
.route("/api/v1/organizations/{org_id}/billing", get(handlers::organization_settings::get_organization_billing))
.route("/api/v1/organizations/{org_id}/analytics/pillars", get(handlers::pillar_analytics::get_org_pillar_metrics))
.route("/api/v1/workspaces/{workspace_id}/analytics/pillars", get(handlers::pillar_analytics::get_workspace_pillar_metrics))
.route("/api/v1/analytics/pillars/events", post(handlers::pillar_analytics::record_pillar_event))
.route("/api/v1/sso/config", get(handlers::sso::get_sso_config))
.route("/api/v1/sso/config", post(handlers::sso::create_sso_config))
.route("/api/v1/sso/config", delete(handlers::sso::delete_sso_config))
.route("/api/v1/sso/enable", post(handlers::sso::enable_sso))
.route("/api/v1/sso/disable", post(handlers::sso::disable_sso))
.route("/api/v1/sso/saml/metadata/{org_slug}", get(handlers::sso::get_saml_metadata))
.route("/api/v1/billing/subscription", get(handlers::billing::get_subscription))
.route("/api/v1/billing/checkout", post(handlers::billing::create_checkout))
.route("/api/v1/billing/portal", post(handlers::billing::create_portal_session))
.route("/api/v1/auth/verify-email/resend", post(handlers::verification::resend_verification))
.route("/api/v1/hosted-mocks/specs/upload", post(handlers::hosted_mocks::upload_spec))
.route("/api/v1/hosted-mocks", post(handlers::hosted_mocks::create_deployment))
.route("/api/v1/hosted-mocks", get(handlers::hosted_mocks::list_deployments))
.route("/api/v1/hosted-mocks/{deployment_id}", get(handlers::hosted_mocks::get_deployment))
.route("/api/v1/hosted-mocks/{deployment_id}/status", patch(handlers::hosted_mocks::update_deployment_status))
.route("/api/v1/hosted-mocks/{deployment_id}", delete(handlers::hosted_mocks::delete_deployment))
.route("/api/v1/hosted-mocks/{deployment_id}/redeploy", post(handlers::hosted_mocks::redeploy_deployment))
.route("/api/v1/hosted-mocks/{deployment_id}/set-domain", post(handlers::hosted_mocks::set_domain))
.route("/api/v1/hosted-mocks/{deployment_id}/stop", post(handlers::hosted_mocks::stop_deployment))
.route("/api/v1/hosted-mocks/{deployment_id}/start", post(handlers::hosted_mocks::start_deployment))
.route("/api/v1/hosted-mocks/{deployment_id}/logs", get(handlers::hosted_mocks::get_deployment_logs))
.route("/api/v1/hosted-mocks/{deployment_id}/runtime-logs", get(handlers::hosted_mocks::get_runtime_logs))
.route("/api/v1/hosted-mocks/{deployment_id}/runtime-logs/stream", get(handlers::hosted_mocks::stream_runtime_logs))
.route("/api/v1/hosted-mocks/{deployment_id}/runtime-requests", get(handlers::hosted_mocks::get_runtime_requests))
.route("/api/v1/hosted-mocks/{deployment_id}/captures", get(handlers::hosted_mocks::list_recorder_captures))
.route("/api/v1/hosted-mocks/{deployment_id}/captures/{capture_id}", get(handlers::hosted_mocks::get_recorder_capture))
.route("/api/v1/hosted-mocks/{deployment_id}/captures/{capture_id}/response", get(handlers::hosted_mocks::get_recorder_capture_response))
.route("/api/v1/hosted-mocks/{deployment_id}/captures/export/har", get(handlers::hosted_mocks::export_recorder_captures_har))
.route("/api/v1/hosted-mocks/{deployment_id}/captures/export/jsonl", get(handlers::hosted_mocks::export_recorder_captures_jsonl))
.route("/api/v1/hosted-mocks/{deployment_id}/captures/enable", post(handlers::hosted_mocks::enable_recorder))
.route("/api/v1/hosted-mocks/{deployment_id}/captures/disable", post(handlers::hosted_mocks::disable_recorder))
.route("/api/v1/hosted-mocks/{deployment_id}/captures/clear", post(handlers::hosted_mocks::clear_recorder))
.route("/api/v1/hosted-mocks/{deployment_id}/captures/{capture_id}/replay", post(handlers::hosted_mocks::replay_recorder_capture))
.route("/api/v1/hosted-mocks/{deployment_id}/state-machines", get(handlers::hosted_mocks::list_deployment_state_machines))
.route("/api/v1/hosted-mocks/{deployment_id}/state-machines/instances", get(handlers::hosted_mocks::list_deployment_state_machine_instances))
.route("/api/v1/hosted-mocks/{deployment_id}/state-machines/{resource_type}", get(handlers::hosted_mocks::get_deployment_state_machine))
.route("/api/v1/hosted-mocks/{deployment_id}/traces", get(handlers::otlp::list_traces))
.route("/api/v1/hosted-mocks/{deployment_id}/traces/{trace_id}", get(handlers::otlp::get_trace))
.route("/api/v1/hosted-mocks/{deployment_id}/metrics", get(handlers::hosted_mocks::get_deployment_metrics))
.route("/api/v1/projects", get(handlers::projects::list_projects))
.route("/api/v1/tokens", post(handlers::tokens::create_token))
.route("/api/v1/tokens", get(handlers::tokens::list_tokens))
.route("/api/v1/tokens/{token_id}", delete(handlers::tokens::delete_token))
.route("/api/v1/usage", get(handlers::usage::get_usage))
.route("/api/v1/usage/history", get(handlers::usage::get_usage_history))
.route("/api/v1/usage/ai-tokens", post(handlers::usage::report_ai_tokens))
.route("/api/v1/organizations/{org_id}/audit-logs", get(handlers::audit::get_audit_logs))
.route("/api/v1/gdpr/export", get(handlers::gdpr::export_data))
.route("/api/v1/gdpr/erase", delete(handlers::gdpr::delete_data))
.route("/api/v1/security/suspicious-activities", get(handlers::security::get_suspicious_activities))
.route("/api/v1/security/suspicious-activities/{id}/resolve", post(handlers::security::resolve_suspicious_activity))
.route("/api/v1/settings/byok", get(handlers::settings::get_byok_config))
.route("/api/v1/settings/byok", put(handlers::settings::update_byok_config))
.route("/api/v1/settings/byok", delete(handlers::settings::delete_byok_config))
.route("/api/v1/settings/byok/test", post(handlers::settings::test_byok_connection))
.route("/api/v1/tokens/{token_id}/rotate", post(handlers::token_rotation::rotate_token))
.route("/api/v1/tokens/rotation-status", get(handlers::token_rotation::get_tokens_needing_rotation))
.route("/api/v1/dashboard", get(handlers::cloud_dashboard::get_dashboard))
.route("/api/v1/dashboard/health", get(handlers::cloud_dashboard::get_health))
.route("/api/v1/dashboard/logs", get(handlers::cloud_dashboard::get_logs))
.route("/api/v1/workspaces", get(handlers::cloud_workspaces::list_workspaces))
.route("/api/v1/workspaces", post(handlers::cloud_workspaces::create_workspace))
.route("/api/v1/workspaces/order", put(handlers::workspace_ordering::reorder_workspaces))
.route("/api/v1/workspaces/{id}", get(handlers::cloud_workspaces::get_workspace))
.route("/api/v1/workspaces/{id}", patch(handlers::cloud_workspaces::update_workspace))
.route("/api/v1/workspaces/{id}", delete(handlers::cloud_workspaces::delete_workspace))
.route("/api/v1/workspaces/{id}/activate", post(handlers::workspace_ordering::activate_workspace))
.route(
"/api/v1/workspaces/{id}/active-scenarios",
get(handlers::federations::get_workspace_active_scenarios),
)
.route("/api/v1/workspaces/{workspace_id}/environments", get(handlers::workspace_environments::list_environments))
.route("/api/v1/workspaces/{workspace_id}/environments", post(handlers::workspace_environments::create_environment))
.route("/api/v1/workspaces/{workspace_id}/environments/order", put(handlers::workspace_environments::reorder_environments))
.route("/api/v1/workspaces/{workspace_id}/environments/{environment_id}", put(handlers::workspace_environments::update_environment))
.route("/api/v1/workspaces/{workspace_id}/environments/{environment_id}", delete(handlers::workspace_environments::delete_environment))
.route("/api/v1/workspaces/{workspace_id}/environments/{environment_id}/activate", post(handlers::workspace_environments::activate_environment))
.route("/api/v1/workspaces/{workspace_id}/environments/{environment_id}/variables", get(handlers::workspace_environments::list_variables))
.route("/api/v1/workspaces/{workspace_id}/environments/{environment_id}/variables", post(handlers::workspace_environments::set_variable))
.route("/api/v1/workspaces/{workspace_id}/environments/{environment_id}/variables/{variable_name}", delete(handlers::workspace_environments::delete_variable))
.route("/api/v1/workspaces/{workspace_id}/folders", post(handlers::workspace_folders::create_folder))
.route("/api/v1/workspaces/{workspace_id}/folders/{folder_id}", get(handlers::workspace_folders::get_folder))
.route("/api/v1/workspaces/{workspace_id}/requests", post(handlers::workspace_folders::create_request))
.route("/api/v1/import/preview", post(handlers::workspace_import::preview_import))
.route("/api/v1/workspaces/{workspace_id}/import", post(handlers::workspace_import::import_to_workspace))
.route("/api/v1/workspaces/{workspace_id}/autocomplete", post(handlers::workspace_import::autocomplete))
.route("/api/v1/workspaces/{workspace_id}/requests/{request_id}/execute", post(handlers::workspace_request_execute::execute_request))
.route("/api/v1/workspaces/{workspace_id}/requests/{request_id}/history", get(handlers::workspace_request_execute::list_request_history))
.route("/api/v1/workspaces/{workspace_id}/encryption/status", get(handlers::workspace_encryption::get_status))
.route("/api/v1/workspaces/{workspace_id}/encryption/config", get(handlers::workspace_encryption::get_config))
.route("/api/v1/workspaces/{workspace_id}/encryption/config", put(handlers::workspace_encryption::put_config))
.route("/api/v1/workspaces/{workspace_id}/encryption/enable", post(handlers::workspace_encryption::enable))
.route("/api/v1/workspaces/{workspace_id}/encryption/disable", post(handlers::workspace_encryption::disable))
.route("/api/v1/workspaces/{workspace_id}/encryption/security-check", post(handlers::workspace_encryption::security_check))
.route("/api/v1/services", get(handlers::cloud_services::list_services))
.route("/api/v1/services", post(handlers::cloud_services::create_service))
.route("/api/v1/services/{id}", get(handlers::cloud_services::get_service))
.route("/api/v1/services/{id}", patch(handlers::cloud_services::update_service))
.route("/api/v1/services/{id}", delete(handlers::cloud_services::delete_service))
.route("/api/v1/fixtures", get(handlers::cloud_fixtures::list_fixtures))
.route("/api/v1/fixtures", post(handlers::cloud_fixtures::create_fixture))
.route("/api/v1/fixtures/{id}", get(handlers::cloud_fixtures::get_fixture))
.route("/api/v1/fixtures/{id}", patch(handlers::cloud_fixtures::update_fixture))
.route("/api/v1/fixtures/{id}", delete(handlers::cloud_fixtures::delete_fixture))
.route("/api/v1/federation", get(handlers::federations::list_federations))
.route("/api/v1/federation", post(handlers::federations::create_federation))
.route("/api/v1/federation/{id}", get(handlers::federations::get_federation))
.route("/api/v1/federation/{id}", patch(handlers::federations::update_federation))
.route("/api/v1/federation/{id}", delete(handlers::federations::delete_federation))
.route(
"/api/v1/federation/{id}/route",
post(handlers::federations::route_federation_request),
)
.route(
"/api/v1/federation/{id}/scenarios/activate",
post(handlers::federations::activate_federation_scenario),
)
.route(
"/api/v1/federation/{id}/scenarios/active",
get(handlers::federations::get_active_federation_scenario),
)
.route(
"/api/v1/federation/{id}/scenarios/active",
delete(handlers::federations::deactivate_federation_scenario),
)
.route(
"/api/v1/federation/{id}/scenarios/active/report",
post(handlers::federations::report_federation_scenario_state),
)
.route("/api/v1/scenarios", get(handlers::scenarios::list_org_scenarios))
.route("/api/v1/scenarios/{id}", get(handlers::scenarios::get_org_scenario_by_id))
.route("/api/v1/marketplace/scenarios/publish", post(handlers::scenarios::publish_scenario))
.route("/api/v1/marketplace/scenarios/{name}/reviews", post(handlers::scenario_reviews::submit_scenario_review))
.route("/api/v1/marketplace/templates/publish", post(handlers::templates::publish_template))
.route("/api/v1/marketplace/templates/{name}/{version}/reviews", post(handlers::template_reviews::submit_template_review))
.route("/api/v1/marketplace/templates/{name}/{version}/star", post(handlers::templates::toggle_template_star))
.route("/api/v1/marketplace/templates/{name}/{version}/star", get(handlers::templates::get_template_star_state))
.route("/api/v1/organizations/{org_id}/templates", get(handlers::org_templates::list_templates))
.route("/api/v1/organizations/{org_id}/templates", post(handlers::org_templates::create_template))
.route("/api/v1/organizations/{org_id}/templates/{template_id}", get(handlers::org_templates::get_template))
.route("/api/v1/organizations/{org_id}/templates/{template_id}", patch(handlers::org_templates::update_template))
.route("/api/v1/organizations/{org_id}/templates/{template_id}", delete(handlers::org_templates::delete_template))
.route("/api/v1/workspaces/{workspace_id}/environments/{env}/promote-scenario", post(handlers::scenario_promotions::promote_scenario))
.route("/api/v1/workspaces/{workspace_id}/promotions", get(handlers::scenario_promotions::list_promotions))
.route("/api/v1/workspaces/{workspace_id}/promotions/{promotion_id}/approve", post(handlers::scenario_promotions::approve_promotion))
.route("/api/v1/workspaces/{workspace_id}/promotions/{promotion_id}/reject", post(handlers::scenario_promotions::reject_promotion))
.route_layer(middleware::from_fn_with_state(state.clone(), auth_middleware))
.route_layer(middleware::from_fn(rate_limit_middleware));
let sso_public_routes = Router::new()
.route("/api/v1/sso/saml/login/{org_slug}", get(handlers::sso::initiate_saml_login))
.route("/api/v1/sso/saml/acs/{org_slug}", post(handlers::sso::saml_acs))
.route("/api/v1/sso/saml/slo/{org_slug}", post(handlers::sso::saml_slo))
.route_layer(middleware::from_fn(rate_limit_middleware));
let oauth_public_routes = Router::new()
.route("/api/v1/auth/oauth/{provider}", get(handlers::oauth::oauth_authorize))
.route("/api/v1/auth/oauth/{provider}/callback", get(handlers::oauth::oauth_callback))
.route_layer(middleware::from_fn(rate_limit_middleware));
let billing_webhook_routes =
Router::new().route("/api/v1/billing/webhook", post(handlers::billing::stripe_webhook));
let admin_routes = Router::new()
.route("/api/v1/admin/plugins/{name}/verify", post(handlers::admin::verify_plugin))
.route("/api/v1/admin/stats", get(handlers::admin::get_admin_stats))
.route("/api/v1/admin/analytics", get(handlers::analytics::get_analytics))
.route(
"/api/v1/admin/analytics/funnel",
get(handlers::analytics::get_conversion_funnel),
)
.route_layer(middleware::from_fn_with_state(state.clone(), auth_middleware))
.route_layer(middleware::from_fn(rate_limit_middleware));
Router::new()
.merge(public_routes)
.merge(sso_public_routes)
.merge(oauth_public_routes)
.merge(billing_webhook_routes)
.merge(auth_routes)
.merge(admin_routes)
}
#[cfg(test)]
mod tests {
#[test]
fn test_public_route_paths() {
let routes = vec![
"/health",
"/health/live",
"/health/ready",
"/api/v1/plugins/search",
"/api/v1/plugins/{name}",
"/api/v1/plugins/{name}/versions/{version}",
"/api/v1/stats",
"/api/v1/auth/register",
"/api/v1/auth/login",
"/api/v1/auth/token/refresh",
"/api/v1/auth/password/reset-request",
"/api/v1/auth/password/reset",
];
for route in routes {
assert!(route.starts_with("/"));
assert!(!route.contains("//"));
}
}
#[test]
fn test_auth_route_paths() {
let routes = vec![
"/api/v1/plugins/publish",
"/api/v1/plugins/{name}/versions/{version}/yank",
"/api/v1/auth/2fa/setup",
"/api/v1/auth/2fa/verify-setup",
"/api/v1/auth/2fa/disable",
"/api/v1/auth/2fa/status",
"/api/v1/organizations",
"/api/v1/organizations/{org_id}",
"/api/v1/organizations/{org_id}/members",
];
for route in routes {
assert!(route.starts_with("/api/v1/"));
assert!(!route.contains("//"));
}
}
#[test]
fn test_sso_route_paths() {
let routes = vec![
"/api/v1/sso/config",
"/api/v1/sso/enable",
"/api/v1/sso/disable",
"/api/v1/sso/saml/metadata/{org_slug}",
"/api/v1/sso/saml/login/{org_slug}",
"/api/v1/sso/saml/acs/{org_slug}",
"/api/v1/sso/saml/slo/{org_slug}",
];
for route in routes {
assert!(route.starts_with("/api/v1/sso"));
assert!(!route.contains("//"));
}
}
#[test]
fn test_admin_route_paths() {
let routes = vec![
"/api/v1/admin/plugins/{name}/verify",
"/api/v1/admin/stats",
"/api/v1/admin/analytics",
"/api/v1/admin/analytics/funnel",
];
for route in routes {
assert!(route.starts_with("/api/v1/admin"));
assert!(!route.contains("//"));
}
}
#[test]
fn test_organization_settings_routes() {
let routes = vec![
"/api/v1/organizations/{org_id}/settings",
"/api/v1/organizations/{org_id}/settings/ai",
"/api/v1/organizations/{org_id}/usage",
"/api/v1/organizations/{org_id}/billing",
];
for route in routes {
assert!(route.contains("{org_id}"));
assert!(route.starts_with("/api/v1/organizations"));
}
}
#[test]
fn test_pillar_analytics_routes() {
let routes = vec![
"/api/v1/organizations/{org_id}/analytics/pillars",
"/api/v1/workspaces/{workspace_id}/analytics/pillars",
"/api/v1/analytics/pillars/events",
];
for route in routes {
assert!(route.contains("analytics"));
assert!(route.contains("pillars"));
}
}
#[test]
fn test_route_parameter_consistency() {
let org_routes = vec![
"/api/v1/organizations/{org_id}",
"/api/v1/organizations/{org_id}/members",
"/api/v1/organizations/{org_id}/settings",
];
for route in org_routes {
assert!(route.contains("{org_id}"));
}
let plugin_routes = vec![
"/api/v1/plugins/{name}",
"/api/v1/plugins/{name}/versions/{version}",
"/api/v1/plugins/{name}/reviews",
];
for route in plugin_routes {
assert!(route.contains("{name}"));
}
}
#[test]
fn test_review_routes() {
let routes = vec![
"/api/v1/plugins/{name}/reviews",
"/api/v1/plugins/{name}/reviews/{review_id}/vote",
];
for route in routes {
assert!(route.contains("reviews"));
assert!(route.contains("{name}"));
}
}
#[test]
fn test_two_factor_routes() {
let routes = vec![
"/api/v1/auth/2fa/setup",
"/api/v1/auth/2fa/verify-setup",
"/api/v1/auth/2fa/disable",
"/api/v1/auth/2fa/status",
];
for route in routes {
assert!(route.contains("/2fa/"));
assert!(route.starts_with("/api/v1/auth"));
}
}
#[test]
fn test_no_duplicate_route_patterns() {
let all_routes = vec![
"/api/v1/plugins/{name}",
"/api/v1/plugins/search",
"/api/v1/organizations/{org_id}",
"/api/v1/organizations/{org_id}/members",
];
let mut seen = std::collections::HashSet::new();
for route in all_routes {
assert!(!seen.contains(route), "Duplicate route: {}", route);
seen.insert(route);
}
}
#[test]
fn test_api_version_consistency() {
let routes = vec![
"/api/v1/plugins/search",
"/api/v1/auth/login",
"/api/v1/organizations",
"/api/v1/admin/stats",
"/api/v1/sso/config",
];
for route in routes {
assert!(route.contains("/api/v1/"));
}
}
#[test]
fn test_route_http_methods_appropriateness() {
struct RouteMethod {
path: &'static str,
should_contain: &'static str,
}
let route_checks = vec![
RouteMethod {
path: "health",
should_contain: "get",
},
RouteMethod {
path: "search",
should_contain: "post",
},
RouteMethod {
path: "publish",
should_contain: "post",
},
RouteMethod {
path: "login",
should_contain: "post",
},
RouteMethod {
path: "register",
should_contain: "post",
},
];
for check in route_checks {
assert!(!check.path.is_empty());
assert!(!check.should_contain.is_empty());
}
}
#[test]
fn test_organization_member_routes() {
let routes = vec![
"/api/v1/organizations/{org_id}/members",
"/api/v1/organizations/{org_id}/members/{user_id}",
];
for route in routes {
assert!(route.contains("members"));
assert!(route.contains("{org_id}"));
}
}
#[test]
fn test_password_reset_routes() {
let routes = vec![
"/api/v1/auth/password/reset-request",
"/api/v1/auth/password/reset",
];
for route in routes {
assert!(route.contains("password"));
assert!(route.contains("reset"));
}
}
#[test]
fn test_saml_routes_org_slug_parameter() {
let routes = vec![
"/api/v1/sso/saml/metadata/{org_slug}",
"/api/v1/sso/saml/login/{org_slug}",
"/api/v1/sso/saml/acs/{org_slug}",
"/api/v1/sso/saml/slo/{org_slug}",
];
for route in routes {
assert!(route.contains("{org_slug}"));
assert!(route.contains("/saml/"));
}
}
#[test]
fn test_plugin_badges_route() {
let route = "/api/v1/plugins/{name}/badges";
assert!(route.contains("{name}"));
assert!(route.contains("badges"));
}
}