mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
refactor: 拆分 admin handler 大文件为模块目录,消除 shared.rs 反模式
- endpoint_keys/pool/architecture 等大文件拆分为独立模块目录 - 删除 crud/query/strategy/system 中的 shared.rs,内容归位到各自模块 - endpoints_admin/models/oauth/write 等模块拆出 payloads/responses/support 子文件 - system/core 下多个 *_routes.rs 合并到 system_routes.rs - 更新 refactor-execution-plan 文档进度
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,404 @@
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn non_admin_handlers_do_not_depend_on_admin_stats_module() {
|
||||
let handlers_mod = read_workspace_file("apps/aether-gateway/src/handlers/mod.rs");
|
||||
assert!(
|
||||
!handlers_mod.contains("pub(crate) use admin::{"),
|
||||
"handlers/mod.rs should stay as pure module wiring after shared usage stats facade extraction"
|
||||
);
|
||||
|
||||
for path in [
|
||||
"apps/aether-gateway/src/handlers/public/support/user_me.rs",
|
||||
"apps/aether-gateway/src/handlers/public/support/wallet/reads.rs",
|
||||
"apps/aether-gateway/src/handlers/admin/observability/monitoring/cache_store.rs",
|
||||
] {
|
||||
let file = read_workspace_file(path);
|
||||
assert!(
|
||||
!file.contains("handlers::admin::stats::"),
|
||||
"{path} should not depend directly on admin::stats"
|
||||
);
|
||||
}
|
||||
|
||||
let admin_mod = read_workspace_file("apps/aether-gateway/src/handlers/admin/mod.rs");
|
||||
assert!(
|
||||
!admin_mod.contains("pub(crate) mod facade;"),
|
||||
"handlers/admin/mod.rs should not keep admin facade after direct subdomain exposure"
|
||||
);
|
||||
|
||||
let shared_mod = read_workspace_file("apps/aether-gateway/src/handlers/shared/mod.rs");
|
||||
for pattern in [
|
||||
"admin_stats_bad_request_response",
|
||||
"list_usage_for_optional_range",
|
||||
"parse_bounded_u32",
|
||||
"round_to",
|
||||
"AdminStatsTimeRange",
|
||||
"AdminStatsUsageFilter",
|
||||
] {
|
||||
assert!(
|
||||
shared_mod.contains(pattern),
|
||||
"handlers/shared/mod.rs should expose shared usage stats helper {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let admin_observability_mod =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/observability/mod.rs");
|
||||
for pattern in [
|
||||
"admin_stats_bad_request_response",
|
||||
"aggregate_usage_stats",
|
||||
"list_usage_for_optional_range",
|
||||
"parse_bounded_u32",
|
||||
"round_to",
|
||||
"AdminStatsTimeRange",
|
||||
"AdminStatsUsageFilter",
|
||||
"match_admin_monitoring_route",
|
||||
"AdminMonitoringRoute",
|
||||
"ADMIN_MONITORING_REDIS_REQUIRED_DETAIL",
|
||||
"test_support",
|
||||
] {
|
||||
assert!(
|
||||
!admin_observability_mod.contains(pattern),
|
||||
"handlers/admin/observability/mod.rs should not re-export {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let shared_usage_stats =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/shared/usage_stats.rs");
|
||||
assert!(
|
||||
shared_usage_stats.contains("crate::handlers::admin::observability::stats::{"),
|
||||
"handlers/shared/usage_stats.rs should depend on observability::stats directly"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_monitoring_root_stays_thin() {
|
||||
let monitoring_mod = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/observability/monitoring/mod.rs",
|
||||
);
|
||||
for pattern in [
|
||||
"mod common;",
|
||||
"use self::activity::{",
|
||||
"use self::cache::{",
|
||||
"use self::resilience::{",
|
||||
"use self::trace::{",
|
||||
"pub(crate) use self::routes::{",
|
||||
"const ADMIN_MONITORING_",
|
||||
] {
|
||||
assert!(
|
||||
!monitoring_mod.contains(pattern),
|
||||
"handlers/admin/observability/monitoring/mod.rs should not act as a glue re-export layer for {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
assert!(
|
||||
monitoring_mod.contains("routes::maybe_build_local_admin_monitoring_response"),
|
||||
"handlers/admin/observability/monitoring/mod.rs should delegate through routes module"
|
||||
);
|
||||
assert!(
|
||||
monitoring_mod.contains("mod cache_config;"),
|
||||
"handlers/admin/observability/monitoring/mod.rs should register cache_config as a dedicated cache boundary"
|
||||
);
|
||||
assert!(
|
||||
monitoring_mod.contains("mod cache_mutations;"),
|
||||
"handlers/admin/observability/monitoring/mod.rs should register cache_mutations as a dedicated mutation boundary"
|
||||
);
|
||||
assert!(
|
||||
monitoring_mod.contains("mod responses;"),
|
||||
"handlers/admin/observability/monitoring/mod.rs should register responses as a dedicated response boundary"
|
||||
);
|
||||
assert!(
|
||||
!workspace_file_exists(
|
||||
"apps/aether-gateway/src/handlers/admin/observability/monitoring/common.rs",
|
||||
),
|
||||
"handlers/admin/observability/monitoring/common.rs should stay removed after boundary split"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_stats_root_stays_thin() {
|
||||
let stats_mod =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/observability/stats/mod.rs");
|
||||
for pattern in [
|
||||
"use self::leaderboard::{",
|
||||
"use self::responses::{",
|
||||
"use self::timeseries::{",
|
||||
"enum AdminStatsComparisonType",
|
||||
"enum AdminStatsGranularity",
|
||||
"struct AdminStatsForecastPoint",
|
||||
"struct AdminStatsLeaderboardItem",
|
||||
"struct AdminStatsUserMetadata",
|
||||
"struct AdminStatsTimeSeriesBucket",
|
||||
"impl AdminStatsTimeRange {",
|
||||
"pub(crate) fn round_to(",
|
||||
] {
|
||||
assert!(
|
||||
!stats_mod.contains(pattern),
|
||||
"handlers/admin/observability/stats/mod.rs should not own stats helper implementation {pattern}"
|
||||
);
|
||||
}
|
||||
for pattern in [
|
||||
"mod helpers;",
|
||||
"pub(crate) use self::helpers::{round_to, AdminStatsTimeRange, AdminStatsUsageFilter};",
|
||||
"pub(crate) use self::range::{list_usage_for_optional_range, parse_bounded_u32};",
|
||||
"pub(crate) use self::responses::admin_stats_bad_request_response;",
|
||||
"pub(crate) use self::timeseries::aggregate_usage_stats;",
|
||||
] {
|
||||
assert!(
|
||||
stats_mod.contains(pattern),
|
||||
"handlers/admin/observability/stats/mod.rs should stay as a thin seam for {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let analytics_routes = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/observability/stats/analytics_routes.rs",
|
||||
);
|
||||
for pattern in [
|
||||
"use super::helpers::{",
|
||||
"use super::range::{",
|
||||
"use super::responses::{",
|
||||
"use super::timeseries::{",
|
||||
] {
|
||||
assert!(
|
||||
analytics_routes.contains(pattern),
|
||||
"stats/analytics_routes.rs should depend on split stats boundaries via {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let cost_routes = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/observability/stats/cost_routes.rs",
|
||||
);
|
||||
for pattern in [
|
||||
"use super::helpers::{",
|
||||
"use super::range::{",
|
||||
"use super::responses::{",
|
||||
"use super::timeseries::{",
|
||||
] {
|
||||
assert!(
|
||||
cost_routes.contains(pattern),
|
||||
"stats/cost_routes.rs should depend on split stats boundaries via {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let leaderboard_routes = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/observability/stats/leaderboard_routes.rs",
|
||||
);
|
||||
for pattern in [
|
||||
"use super::helpers::{",
|
||||
"use super::leaderboard::{",
|
||||
"use super::range::{",
|
||||
"use super::responses::{",
|
||||
] {
|
||||
assert!(
|
||||
leaderboard_routes.contains(pattern),
|
||||
"stats/leaderboard_routes.rs should depend on split stats boundaries via {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let provider_quota_routes = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/observability/stats/provider_quota_routes.rs",
|
||||
);
|
||||
assert!(
|
||||
provider_quota_routes
|
||||
.contains("use super::responses::admin_stats_provider_quota_usage_empty_response;"),
|
||||
"stats/provider_quota_routes.rs should depend on responses boundary directly"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_monitoring_cache_mutations_are_split_from_reads() {
|
||||
let monitoring_cache = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/observability/monitoring/cache.rs",
|
||||
);
|
||||
for pattern in [
|
||||
"pub(super) async fn build_admin_monitoring_cache_users_delete_response(",
|
||||
"pub(super) async fn build_admin_monitoring_cache_affinity_delete_response(",
|
||||
"pub(super) async fn build_admin_monitoring_cache_flush_response(",
|
||||
"pub(super) async fn build_admin_monitoring_cache_provider_delete_response(",
|
||||
"pub(super) async fn build_admin_monitoring_model_mapping_delete_response(",
|
||||
"pub(super) async fn build_admin_monitoring_model_mapping_delete_model_response(",
|
||||
"pub(super) async fn build_admin_monitoring_model_mapping_delete_provider_response(",
|
||||
"pub(super) async fn build_admin_monitoring_redis_keys_delete_response(",
|
||||
] {
|
||||
assert!(
|
||||
!monitoring_cache.contains(pattern),
|
||||
"monitoring/cache.rs should stay focused on read/report handlers, not {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let monitoring_cache_mutations = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/observability/monitoring/cache_mutations.rs",
|
||||
);
|
||||
for pattern in [
|
||||
"pub(super) async fn build_admin_monitoring_cache_users_delete_response(",
|
||||
"pub(super) async fn build_admin_monitoring_cache_affinity_delete_response(",
|
||||
"pub(super) async fn build_admin_monitoring_cache_flush_response(",
|
||||
"pub(super) async fn build_admin_monitoring_cache_provider_delete_response(",
|
||||
"pub(super) async fn build_admin_monitoring_model_mapping_delete_response(",
|
||||
"pub(super) async fn build_admin_monitoring_model_mapping_delete_model_response(",
|
||||
"pub(super) async fn build_admin_monitoring_model_mapping_delete_provider_response(",
|
||||
"pub(super) async fn build_admin_monitoring_redis_keys_delete_response(",
|
||||
] {
|
||||
assert!(
|
||||
monitoring_cache_mutations.contains(pattern),
|
||||
"monitoring/cache_mutations.rs should own {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let monitoring_routes = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/observability/monitoring/routes.rs",
|
||||
);
|
||||
assert!(
|
||||
monitoring_routes.contains("use super::cache_mutations::{"),
|
||||
"monitoring/routes.rs should depend on cache_mutations directly for delete handlers"
|
||||
);
|
||||
assert!(
|
||||
monitoring_routes.contains("use super::cache_affinity_reads::{"),
|
||||
"monitoring/routes.rs should depend on cache_affinity_reads directly for affinity read handlers"
|
||||
);
|
||||
assert!(
|
||||
monitoring_routes.contains("use super::cache_model_mapping::{"),
|
||||
"monitoring/routes.rs should depend on cache_model_mapping directly for model-mapping read handlers"
|
||||
);
|
||||
|
||||
let monitoring_mod = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/observability/monitoring/mod.rs",
|
||||
);
|
||||
for pattern in ["mod cache_affinity_reads;", "mod cache_model_mapping;"] {
|
||||
assert!(
|
||||
monitoring_mod.contains(pattern),
|
||||
"monitoring/mod.rs should register split read module {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let monitoring_affinity_reads = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/observability/monitoring/cache_affinity_reads.rs",
|
||||
);
|
||||
for pattern in [
|
||||
"pub(super) async fn build_admin_monitoring_cache_affinities_response(",
|
||||
"pub(super) async fn build_admin_monitoring_cache_affinity_response(",
|
||||
] {
|
||||
assert!(
|
||||
monitoring_affinity_reads.contains(pattern),
|
||||
"monitoring/cache_affinity_reads.rs should own {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let monitoring_model_mapping = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/observability/monitoring/cache_model_mapping.rs",
|
||||
);
|
||||
for pattern in [
|
||||
"pub(super) async fn build_admin_monitoring_model_mapping_stats_response(",
|
||||
"pub(super) async fn build_admin_monitoring_redis_cache_categories_response(",
|
||||
] {
|
||||
assert!(
|
||||
monitoring_model_mapping.contains(pattern),
|
||||
"monitoring/cache_model_mapping.rs should own {pattern}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_usage_root_stays_thin() {
|
||||
let usage_mod =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/observability/usage/mod.rs");
|
||||
for pattern in ["pub(crate) use analytics::{", "pub(crate) use helpers::{"] {
|
||||
assert!(
|
||||
!usage_mod.contains(pattern),
|
||||
"handlers/admin/observability/usage/mod.rs should not re-export helper seam {pattern}"
|
||||
);
|
||||
}
|
||||
for pattern in [
|
||||
"mod analytics;",
|
||||
"mod analytics_routes;",
|
||||
"mod detail_routes;",
|
||||
"mod helpers;",
|
||||
"mod replay;",
|
||||
"mod summary_routes;",
|
||||
"detail_routes::maybe_build_local_admin_usage_detail_response",
|
||||
"summary_routes::maybe_build_local_admin_usage_summary_response",
|
||||
"analytics_routes::maybe_build_local_admin_usage_analytics_response",
|
||||
] {
|
||||
assert!(
|
||||
usage_mod.contains(pattern),
|
||||
"handlers/admin/observability/usage/mod.rs should stay as a thin router for {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let analytics_routes = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/observability/usage/analytics_routes.rs",
|
||||
);
|
||||
assert!(
|
||||
analytics_routes.contains("use super::analytics::{"),
|
||||
"usage/analytics_routes.rs should depend on analytics boundary directly"
|
||||
);
|
||||
assert!(
|
||||
analytics_routes.contains("use super::helpers::{"),
|
||||
"usage/analytics_routes.rs should depend on helpers boundary directly"
|
||||
);
|
||||
|
||||
let summary_routes = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/observability/usage/summary_routes.rs",
|
||||
);
|
||||
assert!(
|
||||
summary_routes.contains("use super::analytics::{"),
|
||||
"usage/summary_routes.rs should depend on analytics boundary directly"
|
||||
);
|
||||
assert!(
|
||||
summary_routes.contains("use super::helpers::{"),
|
||||
"usage/summary_routes.rs should depend on helpers boundary directly"
|
||||
);
|
||||
|
||||
let detail_routes = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/observability/usage/detail_routes.rs",
|
||||
);
|
||||
assert!(
|
||||
detail_routes.contains("use super::analytics::{"),
|
||||
"usage/detail_routes.rs should depend on analytics boundary directly"
|
||||
);
|
||||
assert!(
|
||||
detail_routes.contains("use super::helpers::{"),
|
||||
"usage/detail_routes.rs should depend on helpers boundary directly"
|
||||
);
|
||||
|
||||
let replay =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/observability/usage/replay.rs");
|
||||
assert!(
|
||||
replay.contains("use super::helpers::{"),
|
||||
"usage/replay.rs should depend on helpers boundary directly"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_monitoring_snapshots_stay_app_local() {
|
||||
let monitoring_cache_types = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/observability/monitoring/cache_types.rs",
|
||||
);
|
||||
for pattern in [
|
||||
"pub(super) struct AdminMonitoringCacheSnapshot",
|
||||
"pub(super) struct AdminMonitoringCacheAffinityRecord",
|
||||
] {
|
||||
assert!(
|
||||
monitoring_cache_types.contains(pattern),
|
||||
"monitoring/cache_types.rs should own {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let monitoring_resilience = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/observability/monitoring/resilience.rs",
|
||||
);
|
||||
assert!(
|
||||
monitoring_resilience.contains("AdminMonitoringResilienceSnapshot"),
|
||||
"monitoring/resilience.rs should keep resilience snapshot ownership locally"
|
||||
);
|
||||
assert!(
|
||||
monitoring_resilience.contains("struct AdminMonitoringResilienceSnapshot"),
|
||||
"monitoring/resilience.rs should define AdminMonitoringResilienceSnapshot locally"
|
||||
);
|
||||
|
||||
let data_system = read_workspace_file("crates/aether-data/src/repository/system.rs");
|
||||
assert!(
|
||||
!data_system.contains("AdminMonitoringCacheSnapshot")
|
||||
&& !data_system.contains("AdminMonitoringResilienceSnapshot"),
|
||||
"monitoring snapshots are admin view DTOs and should not move into aether-data"
|
||||
);
|
||||
}
|
||||
846
apps/aether-gateway/src/tests/architecture/admin_provider.rs
Normal file
846
apps/aether-gateway/src/tests/architecture/admin_provider.rs
Normal file
@@ -0,0 +1,846 @@
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn admin_provider_root_stays_thin() {
|
||||
let provider_mod =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/mod.rs");
|
||||
for pattern in [
|
||||
"pub(crate) mod endpoint_keys;",
|
||||
"pub(crate) mod endpoints_admin;",
|
||||
"pub(crate) mod oauth;",
|
||||
"pub(crate) mod ops;",
|
||||
"pub(crate) mod pool;",
|
||||
"pub(crate) mod pool_admin;",
|
||||
"pub(crate) mod shared;",
|
||||
"pub(crate) mod write;",
|
||||
] {
|
||||
assert!(
|
||||
provider_mod.contains(pattern),
|
||||
"handlers/admin/provider/mod.rs should expose provider subdomain module {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
for pattern in [
|
||||
"pub(crate) use self::oauth::{",
|
||||
"pub(crate) use self::ops::{",
|
||||
"pub(crate) use self::pool::{",
|
||||
"pub(crate) use self::endpoints_admin::{",
|
||||
"pub(crate) use self::pool_admin::{",
|
||||
"pub(crate) use self::write::{",
|
||||
"build_proxy_error_response",
|
||||
"build_internal_control_error_response",
|
||||
"admin_provider_ops_local_action_response",
|
||||
"admin_provider_pool_config",
|
||||
"build_admin_create_provider_key_record",
|
||||
"build_admin_export_key_payload",
|
||||
"normalize_provider_billing_type",
|
||||
"parse_optional_rfc3339_unix_secs",
|
||||
] {
|
||||
assert!(
|
||||
!provider_mod.contains(pattern),
|
||||
"handlers/admin/provider/mod.rs should not act as internal helper export hub for {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
for pattern in [
|
||||
"pub(crate) use self::crud::maybe_build_local_admin_providers_response;",
|
||||
"pub(crate) use self::models::maybe_build_local_admin_provider_models_response;",
|
||||
"pub(crate) use self::oauth::maybe_build_local_admin_provider_oauth_response;",
|
||||
"pub(crate) use self::ops::maybe_build_local_admin_provider_ops_response;",
|
||||
"pub(crate) use self::query::maybe_build_local_admin_provider_query_response;",
|
||||
"pub(crate) use self::strategy::maybe_build_local_admin_provider_strategy_response;",
|
||||
] {
|
||||
assert!(
|
||||
provider_mod.contains(pattern),
|
||||
"handlers/admin/provider/mod.rs should keep route entry seam {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let ops_mod = read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/ops/mod.rs");
|
||||
assert!(
|
||||
!ops_mod.contains("pub(crate) use self::providers::admin_provider_ops_local_action_response;"),
|
||||
"handlers/admin/provider/ops/mod.rs should not re-export admin_provider_ops_local_action_response"
|
||||
);
|
||||
|
||||
let oauth_mod =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/oauth/mod.rs");
|
||||
for pattern in [
|
||||
"pub(crate) use self::quota as provider_oauth_quota;",
|
||||
"pub(crate) use self::refresh as provider_oauth_refresh;",
|
||||
"pub(crate) use self::state as provider_oauth_state;",
|
||||
] {
|
||||
assert!(
|
||||
!oauth_mod.contains(pattern),
|
||||
"handlers/admin/provider/oauth/mod.rs should not alias re-export {pattern}"
|
||||
);
|
||||
}
|
||||
assert!(
|
||||
oauth_mod.contains(
|
||||
"pub(crate) use self::dispatch::maybe_build_local_admin_provider_oauth_response;"
|
||||
),
|
||||
"handlers/admin/provider/oauth/mod.rs should continue exposing the dispatch entry seam"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_provider_endpoints_admin_mod_uses_specific_route_owners() {
|
||||
let endpoints_admin_mod = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/provider/endpoints_admin/mod.rs",
|
||||
);
|
||||
for pattern in [
|
||||
"mod create;",
|
||||
"mod defaults;",
|
||||
"mod delete;",
|
||||
"mod detail;",
|
||||
"mod list;",
|
||||
"mod update;",
|
||||
"create::maybe_handle(state, request_context, request_body)",
|
||||
"update::maybe_handle(state, request_context, request_body)",
|
||||
"delete::maybe_handle(state, request_context, request_body)",
|
||||
"list::maybe_handle(state, request_context, request_body)",
|
||||
"detail::maybe_handle(state, request_context, request_body)",
|
||||
"defaults::maybe_handle(state, request_context, request_body)",
|
||||
] {
|
||||
assert!(
|
||||
endpoints_admin_mod.contains(pattern),
|
||||
"handlers/admin/provider/endpoints_admin/mod.rs should dispatch through explicit route owner {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
for forbidden in [
|
||||
"mod read_routes;",
|
||||
"mod write_routes;",
|
||||
"read_routes::maybe_build_local_admin_endpoints_read_response",
|
||||
"write_routes::maybe_build_local_admin_endpoints_write_response",
|
||||
] {
|
||||
assert!(
|
||||
!endpoints_admin_mod.contains(forbidden),
|
||||
"handlers/admin/provider/endpoints_admin/mod.rs should not keep route bus seam {forbidden}"
|
||||
);
|
||||
}
|
||||
|
||||
for path in [
|
||||
"apps/aether-gateway/src/handlers/admin/provider/endpoints_admin/create.rs",
|
||||
"apps/aether-gateway/src/handlers/admin/provider/endpoints_admin/update.rs",
|
||||
"apps/aether-gateway/src/handlers/admin/provider/endpoints_admin/delete.rs",
|
||||
"apps/aether-gateway/src/handlers/admin/provider/endpoints_admin/list.rs",
|
||||
"apps/aether-gateway/src/handlers/admin/provider/endpoints_admin/detail.rs",
|
||||
"apps/aether-gateway/src/handlers/admin/provider/endpoints_admin/defaults.rs",
|
||||
] {
|
||||
assert!(
|
||||
workspace_file_exists(path),
|
||||
"{path} should exist once endpoints_admin dispatches through specific route owners"
|
||||
);
|
||||
}
|
||||
|
||||
for path in [
|
||||
"apps/aether-gateway/src/handlers/admin/provider/endpoints_admin/read_routes.rs",
|
||||
"apps/aether-gateway/src/handlers/admin/provider/endpoints_admin/write_routes.rs",
|
||||
] {
|
||||
assert!(
|
||||
!workspace_file_exists(path),
|
||||
"{path} should be deleted once endpoints_admin stops routing through read/write buses"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_provider_strategy_uses_shared_billing_normalizers() {
|
||||
let strategy_builders =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/strategy/builders.rs");
|
||||
assert!(
|
||||
!strategy_builders.contains("use super::super::write::{"),
|
||||
"handlers/admin/provider/strategy/builders.rs should not borrow billing/time normalizers from provider::write"
|
||||
);
|
||||
assert!(
|
||||
strategy_builders.contains("crate::handlers::admin::provider::shared::support::{"),
|
||||
"handlers/admin/provider/strategy/builders.rs should import shared provider normalizers from provider::shared::support"
|
||||
);
|
||||
|
||||
let provider_shared_support =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/shared/support.rs");
|
||||
for pattern in [
|
||||
"pub(crate) fn normalize_provider_billing_type(",
|
||||
"pub(crate) fn parse_optional_rfc3339_unix_secs(",
|
||||
] {
|
||||
assert!(
|
||||
provider_shared_support.contains(pattern),
|
||||
"handlers/admin/provider/shared/support.rs should own provider-wide billing/time normalizer {pattern}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_provider_query_and_strategy_use_specific_local_owners() {
|
||||
let query_mod =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/query/mod.rs");
|
||||
for pattern in ["mod payload;", "mod response;", "mod routes;"] {
|
||||
assert!(
|
||||
query_mod.contains(pattern),
|
||||
"handlers/admin/provider/query/mod.rs should register specific local owner {pattern}"
|
||||
);
|
||||
}
|
||||
assert!(
|
||||
!query_mod.contains("mod shared;"),
|
||||
"handlers/admin/provider/query/mod.rs should not retain a generic shared module"
|
||||
);
|
||||
|
||||
for path in [
|
||||
"apps/aether-gateway/src/handlers/admin/provider/query/models.rs",
|
||||
"apps/aether-gateway/src/handlers/admin/provider/query/routes.rs",
|
||||
] {
|
||||
let contents = read_workspace_file(path);
|
||||
assert!(
|
||||
!contents.contains("super::shared::{"),
|
||||
"{path} should not depend on a generic query::shared hub"
|
||||
);
|
||||
}
|
||||
|
||||
assert!(
|
||||
workspace_file_exists("apps/aether-gateway/src/handlers/admin/provider/query/payload.rs"),
|
||||
"handlers/admin/provider/query/payload.rs should own provider query parsing and extractors"
|
||||
);
|
||||
assert!(
|
||||
workspace_file_exists("apps/aether-gateway/src/handlers/admin/provider/query/response.rs"),
|
||||
"handlers/admin/provider/query/response.rs should own provider query response helpers"
|
||||
);
|
||||
|
||||
let strategy_mod =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/strategy/mod.rs");
|
||||
for pattern in ["mod builders;", "mod responses;", "mod routes;"] {
|
||||
assert!(
|
||||
strategy_mod.contains(pattern),
|
||||
"handlers/admin/provider/strategy/mod.rs should register specific local owner {pattern}"
|
||||
);
|
||||
}
|
||||
assert!(
|
||||
!strategy_mod.contains("mod shared;"),
|
||||
"handlers/admin/provider/strategy/mod.rs should not retain a generic shared module"
|
||||
);
|
||||
|
||||
let strategy_routes =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/strategy/routes.rs");
|
||||
assert!(
|
||||
strategy_routes.contains("use super::responses::{"),
|
||||
"handlers/admin/provider/strategy/routes.rs should import route response helpers from strategy::responses"
|
||||
);
|
||||
assert!(
|
||||
!strategy_routes.contains("use super::shared::{"),
|
||||
"handlers/admin/provider/strategy/routes.rs should not depend on strategy::shared"
|
||||
);
|
||||
|
||||
let strategy_builders =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/strategy/builders.rs");
|
||||
assert!(
|
||||
!strategy_builders.contains("use super::shared::"),
|
||||
"handlers/admin/provider/strategy/builders.rs should keep provider-not-found response local"
|
||||
);
|
||||
|
||||
assert!(
|
||||
workspace_file_exists("apps/aether-gateway/src/handlers/admin/provider/strategy/responses.rs"),
|
||||
"handlers/admin/provider/strategy/responses.rs should own strategy route-level shared responses"
|
||||
);
|
||||
assert!(
|
||||
!workspace_file_exists("apps/aether-gateway/src/handlers/admin/provider/strategy/shared.rs"),
|
||||
"handlers/admin/provider/strategy/shared.rs should be removed once the local shared hub is narrowed"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_provider_crud_uses_specific_local_response_owner() {
|
||||
let crud_mod =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/crud/mod.rs");
|
||||
for pattern in ["mod responses;", "mod routes;"] {
|
||||
assert!(
|
||||
crud_mod.contains(pattern),
|
||||
"handlers/admin/provider/crud/mod.rs should register local owner {pattern}"
|
||||
);
|
||||
}
|
||||
for forbidden in ["mod shared;", "use shared::*;"] {
|
||||
assert!(
|
||||
!crud_mod.contains(forbidden),
|
||||
"handlers/admin/provider/crud/mod.rs should not retain generic shared glue {forbidden}"
|
||||
);
|
||||
}
|
||||
|
||||
let crud_routes =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/crud/routes.rs");
|
||||
assert!(
|
||||
crud_routes.contains("use super::responses::build_admin_providers_data_unavailable_response;"),
|
||||
"handlers/admin/provider/crud/routes.rs should import data-unavailable response from crud::responses"
|
||||
);
|
||||
assert!(
|
||||
!crud_routes.contains("use super::shared::"),
|
||||
"handlers/admin/provider/crud/routes.rs should not depend on crud::shared"
|
||||
);
|
||||
|
||||
assert!(
|
||||
workspace_file_exists("apps/aether-gateway/src/handlers/admin/provider/crud/responses.rs"),
|
||||
"handlers/admin/provider/crud/responses.rs should own provider CRUD response helpers"
|
||||
);
|
||||
assert!(
|
||||
!workspace_file_exists("apps/aether-gateway/src/handlers/admin/provider/crud/shared.rs"),
|
||||
"handlers/admin/provider/crud/shared.rs should be removed once the local shared hub is narrowed"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_provider_pool_uses_config_and_runtime_owners() {
|
||||
let pool_mod = read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/pool.rs");
|
||||
for pattern in ["pub(crate) mod config;", "pub(crate) mod runtime;"] {
|
||||
assert!(
|
||||
pool_mod.contains(pattern),
|
||||
"handlers/admin/provider/pool.rs should expose explicit pool owner {pattern}"
|
||||
);
|
||||
}
|
||||
for forbidden in [
|
||||
"pub(crate) use config::admin_provider_pool_config;",
|
||||
"pub(crate) use runtime::{",
|
||||
"fn admin_provider_pool_lru_enabled(",
|
||||
"fn pool_sticky_pattern(",
|
||||
] {
|
||||
assert!(
|
||||
!pool_mod.contains(forbidden),
|
||||
"handlers/admin/provider/pool.rs should not remain a pool helper implementation hub for {forbidden}"
|
||||
);
|
||||
}
|
||||
|
||||
let pool_config =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/pool/config.rs");
|
||||
assert!(
|
||||
pool_config.contains("pub(crate) fn admin_provider_pool_config("),
|
||||
"handlers/admin/provider/pool/config.rs should own provider pool config parsing"
|
||||
);
|
||||
|
||||
let pool_runtime =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/pool/runtime.rs");
|
||||
for pattern in [
|
||||
"pub(crate) async fn read_admin_provider_pool_cooldown_counts(",
|
||||
"pub(crate) async fn read_admin_provider_pool_runtime_state(",
|
||||
"pub(crate) async fn build_admin_provider_pool_status_payload(",
|
||||
"pub(crate) async fn clear_admin_provider_pool_cooldown(",
|
||||
"pub(crate) async fn reset_admin_provider_pool_cost(",
|
||||
] {
|
||||
assert!(
|
||||
pool_runtime.contains(pattern),
|
||||
"handlers/admin/provider/pool/runtime.rs should own pool runtime helper {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let pool_admin_read_routes = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/provider/pool_admin/read_routes.rs",
|
||||
);
|
||||
for pattern in [
|
||||
"crate::handlers::admin::provider::pool::config::admin_provider_pool_config",
|
||||
"crate::handlers::admin::provider::pool::runtime::{",
|
||||
] {
|
||||
assert!(
|
||||
pool_admin_read_routes.contains(pattern),
|
||||
"handlers/admin/provider/pool_admin/read_routes.rs should import explicit pool owner {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let crud_routes =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/crud/routes.rs");
|
||||
assert!(
|
||||
crud_routes.contains("crate::handlers::admin::provider::pool::runtime::{"),
|
||||
"handlers/admin/provider/crud/routes.rs should import pool status and mutations from pool::runtime"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_provider_pool_admin_mod_stays_thin() {
|
||||
let pool_admin_mod =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/pool_admin/mod.rs");
|
||||
for pattern in [
|
||||
"mod support;",
|
||||
"mod batch_routes;",
|
||||
"mod payloads;",
|
||||
"mod read_routes;",
|
||||
"mod selection;",
|
||||
"use self::support::{build_admin_pool_error_response, is_admin_pool_route};",
|
||||
] {
|
||||
assert!(
|
||||
pool_admin_mod.contains(pattern),
|
||||
"handlers/admin/provider/pool_admin/mod.rs should keep explicit boundary {pattern}"
|
||||
);
|
||||
}
|
||||
for forbidden in [
|
||||
"const ADMIN_POOL_PROVIDER_CATALOG_READER_UNAVAILABLE_DETAIL:",
|
||||
"const ADMIN_POOL_PROVIDER_CATALOG_WRITER_UNAVAILABLE_DETAIL:",
|
||||
"const ADMIN_POOL_BANNED_KEY_CLEANUP_EMPTY_MESSAGE:",
|
||||
"struct AdminPoolResolveSelectionRequest",
|
||||
"fn parse_admin_pool_page(",
|
||||
"fn parse_admin_pool_page_size(",
|
||||
"fn parse_admin_pool_search(",
|
||||
"fn parse_admin_pool_status_filter(",
|
||||
"fn admin_pool_provider_id_from_path(",
|
||||
"fn is_admin_pool_route(",
|
||||
"fn build_admin_pool_error_response(",
|
||||
] {
|
||||
assert!(
|
||||
!pool_admin_mod.contains(forbidden),
|
||||
"handlers/admin/provider/pool_admin/mod.rs should not remain a local helper hub for {forbidden}"
|
||||
);
|
||||
}
|
||||
|
||||
let pool_admin_support = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/provider/pool_admin/support.rs",
|
||||
);
|
||||
for pattern in [
|
||||
"pub(super) const ADMIN_POOL_PROVIDER_CATALOG_READER_UNAVAILABLE_DETAIL:",
|
||||
"pub(super) const ADMIN_POOL_PROVIDER_CATALOG_WRITER_UNAVAILABLE_DETAIL:",
|
||||
"pub(super) const ADMIN_POOL_BANNED_KEY_CLEANUP_EMPTY_MESSAGE:",
|
||||
"pub(super) struct AdminPoolResolveSelectionRequest",
|
||||
"pub(super) fn parse_admin_pool_page(",
|
||||
"pub(super) fn parse_admin_pool_page_size(",
|
||||
"pub(super) fn parse_admin_pool_search(",
|
||||
"pub(super) fn parse_admin_pool_status_filter(",
|
||||
"pub(super) fn admin_pool_provider_id_from_path(",
|
||||
"pub(super) fn is_admin_pool_route(",
|
||||
"pub(super) fn build_admin_pool_error_response(",
|
||||
] {
|
||||
assert!(
|
||||
pool_admin_support.contains(pattern),
|
||||
"handlers/admin/provider/pool_admin/support.rs should own {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let pool_admin_read_routes = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/provider/pool_admin/read_routes.rs",
|
||||
);
|
||||
assert!(
|
||||
pool_admin_read_routes.contains("use super::support::{"),
|
||||
"handlers/admin/provider/pool_admin/read_routes.rs should import parsing helpers from support.rs"
|
||||
);
|
||||
|
||||
let pool_admin_batch_routes = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/provider/pool_admin/batch_routes.rs",
|
||||
);
|
||||
assert!(
|
||||
pool_admin_batch_routes.contains("use super::support::{"),
|
||||
"handlers/admin/provider/pool_admin/batch_routes.rs should import shared route helpers from support.rs"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_provider_write_uses_specific_local_owners() {
|
||||
let write_mod =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/write/mod.rs");
|
||||
for pattern in [
|
||||
"pub(crate) mod keys;",
|
||||
"pub(crate) mod normalize;",
|
||||
"pub(crate) mod provider;",
|
||||
"pub(crate) mod reveal;",
|
||||
] {
|
||||
assert!(
|
||||
write_mod.contains(pattern),
|
||||
"handlers/admin/provider/write/mod.rs should expose explicit write owner {pattern}"
|
||||
);
|
||||
}
|
||||
for forbidden in [
|
||||
"pub(crate) use self::keys::{",
|
||||
"pub(crate) use self::provider::{",
|
||||
"pub(crate) use self::reveal::{",
|
||||
"pub(crate) fn normalize_provider_type_input(",
|
||||
"pub(crate) fn normalize_auth_type(",
|
||||
"pub(crate) fn validate_vertex_api_formats(",
|
||||
] {
|
||||
assert!(
|
||||
!write_mod.contains(forbidden),
|
||||
"handlers/admin/provider/write/mod.rs should not remain a write helper export hub for {forbidden}"
|
||||
);
|
||||
}
|
||||
|
||||
let write_normalize =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/write/normalize.rs");
|
||||
for pattern in [
|
||||
"pub(crate) fn normalize_provider_type_input(",
|
||||
"pub(crate) fn normalize_auth_type(",
|
||||
"pub(crate) fn validate_vertex_api_formats(",
|
||||
] {
|
||||
assert!(
|
||||
write_normalize.contains(pattern),
|
||||
"handlers/admin/provider/write/normalize.rs should own write normalization helper {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let write_keys =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/write/keys.rs");
|
||||
assert!(
|
||||
write_keys.contains("use super::normalize::{normalize_auth_type, validate_vertex_api_formats};"),
|
||||
"handlers/admin/provider/write/keys.rs should import write-local normalize helpers from write::normalize"
|
||||
);
|
||||
assert!(
|
||||
write_keys.contains("normalize_json_object,")
|
||||
&& write_keys.contains("normalize_string_list,"),
|
||||
"handlers/admin/provider/write/keys.rs should import JSON normalization helpers from admin::shared directly"
|
||||
);
|
||||
|
||||
let write_provider =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/write/provider.rs");
|
||||
assert!(
|
||||
write_provider.contains("use super::normalize::normalize_provider_type_input;"),
|
||||
"handlers/admin/provider/write/provider.rs should import provider-type normalization from write::normalize"
|
||||
);
|
||||
assert!(
|
||||
write_provider.contains("crate::handlers::admin::shared::normalize_json_object;"),
|
||||
"handlers/admin/provider/write/provider.rs should import JSON normalization from admin::shared directly"
|
||||
);
|
||||
|
||||
let endpoint_keys_reads = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/provider/endpoint_keys/reads.rs",
|
||||
);
|
||||
assert!(
|
||||
endpoint_keys_reads.contains("use super::super::write::reveal::{"),
|
||||
"handlers/admin/provider/endpoint_keys/reads.rs should import explicit reveal owner from write::reveal"
|
||||
);
|
||||
let endpoint_keys_mutations = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/provider/endpoint_keys/mutations.rs",
|
||||
);
|
||||
for pattern in ["use super::super::write::keys::{"] {
|
||||
assert!(
|
||||
endpoint_keys_mutations.contains(pattern),
|
||||
"handlers/admin/provider/endpoint_keys/mutations.rs should import explicit write owner {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let crud_routes =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/crud/routes.rs");
|
||||
assert!(
|
||||
crud_routes.contains("crate::handlers::admin::provider::write::provider::{"),
|
||||
"handlers/admin/provider/crud/routes.rs should import provider record builders from write::provider"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_provider_ops_providers_mod_stays_thin() {
|
||||
let providers_mod =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/ops/providers/mod.rs");
|
||||
for pattern in [
|
||||
"pub(crate) mod actions;",
|
||||
"mod config;",
|
||||
"mod routes;",
|
||||
"mod support;",
|
||||
"mod verify;",
|
||||
"pub(super) use self::routes::maybe_build_local_admin_provider_ops_providers_response;",
|
||||
] {
|
||||
assert!(
|
||||
providers_mod.contains(pattern),
|
||||
"handlers/admin/provider/ops/providers/mod.rs should keep explicit boundary {pattern}"
|
||||
);
|
||||
}
|
||||
for pattern in [
|
||||
"pub(crate) use self::actions::admin_provider_ops_local_action_response;",
|
||||
"const ADMIN_PROVIDER_OPS_SENSITIVE_FIELDS:",
|
||||
"const ADMIN_PROVIDER_OPS_CONNECT_RUST_ONLY_MESSAGE:",
|
||||
"const ADMIN_PROVIDER_OPS_ACTION_RUST_ONLY_MESSAGE:",
|
||||
"const ADMIN_PROVIDER_OPS_VERIFY_RUST_ONLY_MESSAGE:",
|
||||
"struct AdminProviderOpsSaveConfigRequest",
|
||||
"struct AdminProviderOpsConnectRequest",
|
||||
"struct AdminProviderOpsExecuteActionRequest",
|
||||
"struct AdminProviderOpsCheckinOutcome",
|
||||
] {
|
||||
assert!(
|
||||
!providers_mod.contains(pattern),
|
||||
"handlers/admin/provider/ops/providers/mod.rs should not keep helper/data owner {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let providers_support = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/provider/ops/providers/support.rs",
|
||||
);
|
||||
for pattern in [
|
||||
"pub(super) const ADMIN_PROVIDER_OPS_SENSITIVE_FIELDS:",
|
||||
"pub(super) const ADMIN_PROVIDER_OPS_CONNECT_RUST_ONLY_MESSAGE:",
|
||||
"pub(super) const ADMIN_PROVIDER_OPS_ACTION_RUST_ONLY_MESSAGE:",
|
||||
"pub(super) const ADMIN_PROVIDER_OPS_VERIFY_RUST_ONLY_MESSAGE:",
|
||||
"pub(super) struct AdminProviderOpsSaveConfigRequest",
|
||||
"pub(super) struct AdminProviderOpsConnectRequest",
|
||||
"pub(super) struct AdminProviderOpsExecuteActionRequest",
|
||||
"pub(super) struct AdminProviderOpsCheckinOutcome",
|
||||
] {
|
||||
assert!(
|
||||
providers_support.contains(pattern),
|
||||
"handlers/admin/provider/ops/providers/support.rs should own {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let providers_routes = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/provider/ops/providers/routes.rs",
|
||||
);
|
||||
assert!(
|
||||
providers_routes.contains("use super::actions::{"),
|
||||
"handlers/admin/provider/ops/providers/routes.rs should import action helpers from actions.rs"
|
||||
);
|
||||
assert!(
|
||||
providers_routes.contains("use super::config::{"),
|
||||
"handlers/admin/provider/ops/providers/routes.rs should import config helpers from config.rs"
|
||||
);
|
||||
assert!(
|
||||
providers_routes.contains("use super::support::{"),
|
||||
"handlers/admin/provider/ops/providers/routes.rs should import DTO/constants from support.rs"
|
||||
);
|
||||
assert!(
|
||||
providers_routes.contains("use super::verify::{"),
|
||||
"handlers/admin/provider/ops/providers/routes.rs should import verify helpers from verify.rs"
|
||||
);
|
||||
assert!(
|
||||
!providers_routes.contains("use super::{"),
|
||||
"handlers/admin/provider/ops/providers/routes.rs should not depend on a catch-all providers::mod hub"
|
||||
);
|
||||
|
||||
for path in [
|
||||
"apps/aether-gateway/src/maintenance/runtime.rs",
|
||||
"apps/aether-gateway/src/maintenance/runtime/provider_checkin.rs",
|
||||
] {
|
||||
let contents = read_workspace_file(path);
|
||||
assert!(
|
||||
contents.contains(
|
||||
"provider::ops::providers::actions::admin_provider_ops_local_action_response"
|
||||
),
|
||||
"{path} should call provider ops action helper through providers::actions"
|
||||
);
|
||||
assert!(
|
||||
!contents
|
||||
.contains("provider::ops::providers::admin_provider_ops_local_action_response"),
|
||||
"{path} should not depend on providers::mod action helper re-export"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_provider_oauth_dispatch_uses_helper_owner() {
|
||||
let dispatch_mod = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/provider/oauth/dispatch/mod.rs",
|
||||
);
|
||||
assert!(
|
||||
dispatch_mod.contains("mod helpers;"),
|
||||
"handlers/admin/provider/oauth/dispatch/mod.rs should register dispatch::helpers"
|
||||
);
|
||||
assert!(
|
||||
!dispatch_mod.contains("fn attach_admin_provider_oauth_audit_response("),
|
||||
"handlers/admin/provider/oauth/dispatch/mod.rs should not own dispatch audit helper implementation"
|
||||
);
|
||||
assert!(
|
||||
dispatch_mod.contains("helpers::attach_admin_provider_oauth_audit_response("),
|
||||
"handlers/admin/provider/oauth/dispatch/mod.rs should delegate audit attachment to dispatch::helpers"
|
||||
);
|
||||
|
||||
let dispatch_helpers = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/provider/oauth/dispatch/helpers.rs",
|
||||
);
|
||||
assert!(
|
||||
dispatch_helpers.contains("pub(super) fn attach_admin_provider_oauth_audit_response("),
|
||||
"handlers/admin/provider/oauth/dispatch/helpers.rs should own dispatch audit helper"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_provider_oauth_quota_mod_stays_thin() {
|
||||
let quota_mod =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/oauth/quota/mod.rs");
|
||||
for pattern in [
|
||||
"pub(crate) mod antigravity;",
|
||||
"pub(crate) mod codex;",
|
||||
"pub(crate) mod kiro;",
|
||||
"pub(crate) mod shared;",
|
||||
] {
|
||||
assert!(
|
||||
quota_mod.contains(pattern),
|
||||
"handlers/admin/provider/oauth/quota/mod.rs should expose explicit quota owner {pattern}"
|
||||
);
|
||||
}
|
||||
for forbidden in [
|
||||
"pub(crate) use self::antigravity::refresh_antigravity_provider_quota_locally;",
|
||||
"pub(crate) use self::codex::refresh_codex_provider_quota_locally;",
|
||||
"pub(crate) use self::kiro::refresh_kiro_provider_quota_locally;",
|
||||
"pub(crate) use self::shared::{normalize_string_id_list, persist_provider_quota_refresh_state};",
|
||||
"use self::shared::{",
|
||||
] {
|
||||
assert!(
|
||||
!quota_mod.contains(forbidden),
|
||||
"handlers/admin/provider/oauth/quota/mod.rs should not remain a quota helper export hub for {forbidden}"
|
||||
);
|
||||
}
|
||||
|
||||
let endpoint_keys_quota = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/provider/endpoint_keys/quota.rs",
|
||||
);
|
||||
for pattern in [
|
||||
"use super::super::oauth::quota::antigravity::refresh_antigravity_provider_quota_locally;",
|
||||
"use super::super::oauth::quota::codex::refresh_codex_provider_quota_locally;",
|
||||
"use super::super::oauth::quota::kiro::refresh_kiro_provider_quota_locally;",
|
||||
"use super::super::oauth::quota::shared::normalize_string_id_list;",
|
||||
] {
|
||||
assert!(
|
||||
endpoint_keys_quota.contains(pattern),
|
||||
"handlers/admin/provider/endpoint_keys/quota.rs should import quota helper via explicit owner {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let oauth_refresh =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/oauth/refresh.rs");
|
||||
for pattern in [
|
||||
"use super::quota::antigravity::refresh_antigravity_provider_quota_locally;",
|
||||
"use super::quota::codex::refresh_codex_provider_quota_locally;",
|
||||
"use super::quota::kiro::refresh_kiro_provider_quota_locally;",
|
||||
"use super::quota::shared::persist_provider_quota_refresh_state;",
|
||||
] {
|
||||
assert!(
|
||||
oauth_refresh.contains(pattern),
|
||||
"handlers/admin/provider/oauth/refresh.rs should import quota helper via explicit owner {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let quota_codex =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/oauth/quota/codex.rs");
|
||||
assert!(
|
||||
quota_codex.contains("use super::shared::{"),
|
||||
"handlers/admin/provider/oauth/quota/codex.rs should import common quota helpers from shared.rs"
|
||||
);
|
||||
let quota_kiro =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/oauth/quota/kiro.rs");
|
||||
assert!(
|
||||
quota_kiro.contains("use super::shared::{"),
|
||||
"handlers/admin/provider/oauth/quota/kiro.rs should import common quota helpers from shared.rs"
|
||||
);
|
||||
let quota_antigravity = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/provider/oauth/quota/antigravity.rs",
|
||||
);
|
||||
assert!(
|
||||
quota_antigravity.contains("use super::shared::{"),
|
||||
"handlers/admin/provider/oauth/quota/antigravity.rs should import common quota helpers from shared.rs"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_provider_endpoint_keys_mod_stays_thin() {
|
||||
let endpoint_keys =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/endpoint_keys.rs");
|
||||
for pattern in [
|
||||
"mod mutations;",
|
||||
"mod quota;",
|
||||
"mod reads;",
|
||||
"reads::maybe_handle(",
|
||||
"mutations::maybe_handle(",
|
||||
"quota::maybe_handle(",
|
||||
] {
|
||||
assert!(
|
||||
endpoint_keys.contains(pattern),
|
||||
"handlers/admin/provider/endpoint_keys.rs should keep explicit endpoint-key boundary {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
for forbidden in [
|
||||
"keys_grouped_by_format",
|
||||
"reveal_key",
|
||||
"export_key",
|
||||
"update_key",
|
||||
"delete_key",
|
||||
"batch_delete_keys",
|
||||
"clear_oauth_invalid",
|
||||
"refresh_quota",
|
||||
"create_provider_key",
|
||||
"list_provider_keys",
|
||||
"build_admin_keys_grouped_by_format_payload",
|
||||
"build_admin_create_provider_key_record",
|
||||
"refresh_codex_provider_quota_locally",
|
||||
] {
|
||||
assert!(
|
||||
!endpoint_keys.contains(forbidden),
|
||||
"handlers/admin/provider/endpoint_keys.rs should not remain a route/helper hub for {forbidden}"
|
||||
);
|
||||
}
|
||||
|
||||
let endpoint_keys_reads = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/provider/endpoint_keys/reads.rs",
|
||||
);
|
||||
for pattern in [
|
||||
"keys_grouped_by_format",
|
||||
"reveal_key",
|
||||
"export_key",
|
||||
"list_provider_keys",
|
||||
] {
|
||||
assert!(
|
||||
endpoint_keys_reads.contains(pattern),
|
||||
"handlers/admin/provider/endpoint_keys/reads.rs should own read route {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let endpoint_keys_mutations = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/provider/endpoint_keys/mutations.rs",
|
||||
);
|
||||
for pattern in [
|
||||
"update_key",
|
||||
"delete_key",
|
||||
"batch_delete_keys",
|
||||
"clear_oauth_invalid",
|
||||
"create_provider_key",
|
||||
] {
|
||||
assert!(
|
||||
endpoint_keys_mutations.contains(pattern),
|
||||
"handlers/admin/provider/endpoint_keys/mutations.rs should own mutation route {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let endpoint_keys_quota = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/provider/endpoint_keys/quota.rs",
|
||||
);
|
||||
assert!(
|
||||
endpoint_keys_quota.contains("refresh_quota"),
|
||||
"handlers/admin/provider/endpoint_keys/quota.rs should own quota refresh route"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_provider_models_own_provider_model_builders() {
|
||||
let provider_models_mod =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/models/mod.rs");
|
||||
for pattern in ["mod payloads;", "mod write;"] {
|
||||
assert!(
|
||||
provider_models_mod.contains(pattern),
|
||||
"handlers/admin/provider/models/mod.rs should register local provider-model owner module {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
for path in [
|
||||
"apps/aether-gateway/src/handlers/admin/provider/models/list.rs",
|
||||
"apps/aether-gateway/src/handlers/admin/provider/models/detail.rs",
|
||||
"apps/aether-gateway/src/handlers/admin/provider/models/create.rs",
|
||||
"apps/aether-gateway/src/handlers/admin/provider/models/update.rs",
|
||||
"apps/aether-gateway/src/handlers/admin/provider/models/batch.rs",
|
||||
"apps/aether-gateway/src/handlers/admin/provider/models/import.rs",
|
||||
"apps/aether-gateway/src/handlers/admin/provider/models/available_source.rs",
|
||||
"apps/aether-gateway/src/handlers/admin/provider/models/assign_global.rs",
|
||||
] {
|
||||
let contents = read_workspace_file(path);
|
||||
for forbidden in [
|
||||
"super::super::super::model::",
|
||||
"crate::handlers::admin::model::",
|
||||
] {
|
||||
assert!(
|
||||
!contents.contains(forbidden),
|
||||
"{path} should not borrow provider-model builders from admin/model via {forbidden}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let model_mod = read_workspace_file("apps/aether-gateway/src/handlers/admin/model/mod.rs");
|
||||
for forbidden in [
|
||||
"admin_provider_model_name_exists",
|
||||
"build_admin_provider_model_payload",
|
||||
"build_admin_provider_model_response",
|
||||
"build_admin_provider_models_payload",
|
||||
"build_admin_provider_model_create_record",
|
||||
"build_admin_provider_model_update_record",
|
||||
"build_admin_provider_available_source_models_payload",
|
||||
"build_admin_batch_assign_global_models_payload",
|
||||
"build_admin_import_provider_models_payload",
|
||||
] {
|
||||
assert!(
|
||||
!model_mod.contains(forbidden),
|
||||
"handlers/admin/model/mod.rs should not export provider-model owner {forbidden}"
|
||||
);
|
||||
}
|
||||
}
|
||||
290
apps/aether-gateway/src/tests/architecture/admin_shared.rs
Normal file
290
apps/aether-gateway/src/tests/architecture/admin_shared.rs
Normal file
@@ -0,0 +1,290 @@
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn admin_shared_does_not_own_provider_support() {
|
||||
let admin_shared = read_workspace_file("apps/aether-gateway/src/handlers/admin/shared/mod.rs");
|
||||
assert!(
|
||||
!admin_shared.contains("mod support;"),
|
||||
"handlers/admin/shared/mod.rs should not keep provider support module"
|
||||
);
|
||||
assert!(
|
||||
!workspace_file_exists("apps/aether-gateway/src/handlers/admin/shared/support.rs"),
|
||||
"handlers/admin/shared/support.rs should be removed after provider support extraction"
|
||||
);
|
||||
|
||||
let provider_support =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/shared/support.rs");
|
||||
for pattern in [
|
||||
"pub(crate) struct AdminProviderPoolConfig",
|
||||
"pub(crate) struct AdminProviderPoolRuntimeState",
|
||||
"pub(crate) const ADMIN_PROVIDER_POOL_SCAN_BATCH",
|
||||
"pub(crate) const ADMIN_PROVIDER_OAUTH_DATA_UNAVAILABLE_DETAIL",
|
||||
] {
|
||||
assert!(
|
||||
provider_support.contains(pattern),
|
||||
"provider/shared/support.rs should own {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let admin_shared_paths =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/shared/paths.rs");
|
||||
for pattern in [
|
||||
"pub(crate) fn admin_provider_id_for_manage_path",
|
||||
"pub(crate) fn admin_provider_oauth_start_key_id",
|
||||
"pub(crate) fn admin_provider_ops_architecture_id_from_path",
|
||||
] {
|
||||
assert!(
|
||||
!admin_shared_paths.contains(pattern),
|
||||
"handlers/admin/shared/paths.rs should not own {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let provider_paths =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/shared/paths.rs");
|
||||
for pattern in [
|
||||
"pub(crate) fn admin_provider_id_for_manage_path",
|
||||
"pub(crate) fn admin_provider_oauth_start_key_id",
|
||||
"pub(crate) fn admin_provider_ops_architecture_id_from_path",
|
||||
] {
|
||||
assert!(
|
||||
provider_paths.contains(pattern),
|
||||
"provider/shared/paths.rs should own {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let provider_shared_mod =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/shared/mod.rs");
|
||||
for pattern in [
|
||||
"pub(crate) mod paths;",
|
||||
"pub(crate) mod payloads;",
|
||||
"pub(crate) mod support;",
|
||||
] {
|
||||
assert!(
|
||||
provider_shared_mod.contains(pattern),
|
||||
"handlers/admin/provider/shared/mod.rs should expose explicit provider shared submodule {pattern}"
|
||||
);
|
||||
}
|
||||
for forbidden in [
|
||||
"pub(crate) use self::paths::*;",
|
||||
"pub(crate) use self::payloads::*;",
|
||||
"pub(crate) use self::support::*;",
|
||||
] {
|
||||
assert!(
|
||||
!provider_shared_mod.contains(forbidden),
|
||||
"handlers/admin/provider/shared/mod.rs should not remain a wildcard re-export hub for {forbidden}"
|
||||
);
|
||||
}
|
||||
|
||||
let admin_shared_payloads =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/shared/payloads.rs");
|
||||
for pattern in [
|
||||
"pub(crate) struct AdminProviderCreateRequest",
|
||||
"pub(crate) struct AdminProviderEndpointCreateRequest",
|
||||
"pub(crate) struct AdminProviderModelCreateRequest",
|
||||
] {
|
||||
assert!(
|
||||
!admin_shared_payloads.contains(pattern),
|
||||
"handlers/admin/shared/payloads.rs should not own {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let provider_payloads =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/shared/payloads.rs");
|
||||
for pattern in [
|
||||
"pub(crate) struct AdminProviderCreateRequest",
|
||||
"pub(crate) struct AdminProviderModelCreateRequest",
|
||||
] {
|
||||
assert!(
|
||||
provider_payloads.contains(pattern),
|
||||
"provider/shared/payloads.rs should own {pattern}"
|
||||
);
|
||||
}
|
||||
for pattern in [
|
||||
"AdminProviderEndpointCreateRequest",
|
||||
"AdminProviderEndpointUpdateRequest",
|
||||
] {
|
||||
assert!(
|
||||
!provider_payloads.contains(pattern),
|
||||
"provider/shared/payloads.rs should not retain endpoint CRUD payload owner {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let provider_endpoints_payloads = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/provider/endpoints_admin/payloads.rs",
|
||||
);
|
||||
for pattern in [
|
||||
"struct AdminProviderEndpointCreateRequest",
|
||||
"struct AdminProviderEndpointUpdateRequest",
|
||||
] {
|
||||
assert!(
|
||||
provider_endpoints_payloads.contains(pattern),
|
||||
"provider/endpoints_admin/payloads.rs should own {pattern}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_shared_does_not_own_model_global_routes_or_payloads() {
|
||||
let admin_shared_paths =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/shared/paths.rs");
|
||||
for pattern in [
|
||||
"pub(crate) fn is_admin_global_models_root",
|
||||
"pub(crate) fn admin_global_model_id_from_path",
|
||||
"pub(crate) fn admin_global_model_routing_id",
|
||||
] {
|
||||
assert!(
|
||||
!admin_shared_paths.contains(pattern),
|
||||
"handlers/admin/shared/paths.rs should not own {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let model_shared_paths =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/model/shared/paths.rs");
|
||||
for pattern in [
|
||||
"pub(crate) fn is_admin_global_models_root",
|
||||
"pub(crate) fn admin_global_model_id_from_path",
|
||||
"pub(crate) fn admin_global_model_routing_id",
|
||||
] {
|
||||
assert!(
|
||||
model_shared_paths.contains(pattern),
|
||||
"model/shared/paths.rs should own {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let admin_shared_payloads =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/shared/payloads.rs");
|
||||
for pattern in [
|
||||
"pub(crate) struct AdminGlobalModelCreateRequest",
|
||||
"pub(crate) struct AdminGlobalModelUpdateRequest",
|
||||
"pub(crate) struct AdminBatchAssignToProvidersRequest",
|
||||
] {
|
||||
assert!(
|
||||
!admin_shared_payloads.contains(pattern),
|
||||
"handlers/admin/shared/payloads.rs should not own {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let model_shared_payloads =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/model/shared/payloads.rs");
|
||||
for pattern in [
|
||||
"pub(crate) struct AdminGlobalModelCreateRequest",
|
||||
"pub(crate) struct AdminGlobalModelUpdateRequest",
|
||||
"pub(crate) struct AdminBatchAssignToProvidersRequest",
|
||||
] {
|
||||
assert!(
|
||||
model_shared_payloads.contains(pattern),
|
||||
"model/shared/payloads.rs should own {pattern}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_shared_does_not_own_system_core_routes_or_payloads() {
|
||||
let admin_shared_paths =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/shared/paths.rs");
|
||||
for pattern in [
|
||||
"pub(crate) fn is_admin_management_tokens_root",
|
||||
"pub(crate) fn is_admin_system_configs_root",
|
||||
"pub(crate) fn admin_oauth_provider_type_from_path",
|
||||
] {
|
||||
assert!(
|
||||
!admin_shared_paths.contains(pattern),
|
||||
"handlers/admin/shared/paths.rs should not own {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let system_shared_paths =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/system/shared/paths.rs");
|
||||
for pattern in [
|
||||
"pub(crate) fn is_admin_management_tokens_root",
|
||||
"pub(crate) fn is_admin_system_configs_root",
|
||||
] {
|
||||
assert!(
|
||||
system_shared_paths.contains(pattern),
|
||||
"system/shared/paths.rs should own {pattern}"
|
||||
);
|
||||
}
|
||||
for pattern in [
|
||||
"admin_oauth_provider_type_from_path",
|
||||
"admin_oauth_test_provider_type_from_path",
|
||||
] {
|
||||
assert!(
|
||||
!system_shared_paths.contains(pattern),
|
||||
"system/shared/paths.rs should not own auth oauth path helper {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let admin_shared_payloads =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/shared/payloads.rs");
|
||||
assert!(
|
||||
!admin_shared_payloads.contains("pub(crate) struct AdminOAuthProviderUpsertRequest"),
|
||||
"handlers/admin/shared/payloads.rs should not own AdminOAuthProviderUpsertRequest"
|
||||
);
|
||||
|
||||
let auth_oauth_config =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/auth/oauth_config.rs");
|
||||
assert!(
|
||||
auth_oauth_config.contains("pub(crate) struct AdminOAuthProviderUpsertRequest"),
|
||||
"auth/oauth_config.rs should own AdminOAuthProviderUpsertRequest"
|
||||
);
|
||||
for pattern in [
|
||||
"pub(crate) fn admin_oauth_provider_type_from_path",
|
||||
"pub(crate) fn admin_oauth_test_provider_type_from_path",
|
||||
] {
|
||||
assert!(
|
||||
auth_oauth_config.contains(pattern),
|
||||
"auth/oauth_config.rs should own {pattern}"
|
||||
);
|
||||
}
|
||||
assert!(
|
||||
!auth_oauth_config.contains("pub(crate) fn build_proxy_error_response"),
|
||||
"auth/oauth_config.rs should not own build_proxy_error_response"
|
||||
);
|
||||
assert!(
|
||||
!workspace_file_exists("apps/aether-gateway/src/handlers/admin/system/shared/payloads.rs"),
|
||||
"system/shared/payloads.rs should be removed after oauth payload ownership moves to auth"
|
||||
);
|
||||
|
||||
let admin_shared_mod =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/shared/mod.rs");
|
||||
assert!(
|
||||
admin_shared_mod.contains("mod proxy_errors;")
|
||||
&& admin_shared_mod
|
||||
.contains("pub(crate) use self::proxy_errors::build_proxy_error_response;"),
|
||||
"handlers/admin/shared/mod.rs should expose shared admin proxy error builder"
|
||||
);
|
||||
let admin_shared_proxy_errors =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/shared/proxy_errors.rs");
|
||||
assert!(
|
||||
admin_shared_proxy_errors.contains("pub(crate) fn build_proxy_error_response"),
|
||||
"handlers/admin/shared/proxy_errors.rs should own build_proxy_error_response"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_handlers_expose_real_subdomains_without_facade() {
|
||||
let admin_mod = read_workspace_file("apps/aether-gateway/src/handlers/admin/mod.rs");
|
||||
assert!(
|
||||
!admin_mod.contains("pub(crate) use self::"),
|
||||
"handlers/admin/mod.rs should stay as pure module wiring after facade removal"
|
||||
);
|
||||
|
||||
for pattern in [
|
||||
"pub(crate) mod auth;",
|
||||
"pub(crate) mod endpoint;",
|
||||
"pub(crate) mod features;",
|
||||
"pub(crate) mod observability;",
|
||||
"pub(crate) mod provider;",
|
||||
"pub(crate) mod system;",
|
||||
] {
|
||||
assert!(
|
||||
admin_mod.contains(pattern),
|
||||
"handlers/admin/mod.rs should expose admin subdomain module {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
assert!(
|
||||
!workspace_file_exists("apps/aether-gateway/src/handlers/admin/facade.rs"),
|
||||
"handlers/admin/facade.rs should be removed after direct subdomain exposure"
|
||||
);
|
||||
}
|
||||
431
apps/aether-gateway/src/tests/architecture/admin_system.rs
Normal file
431
apps/aether-gateway/src/tests/architecture/admin_system.rs
Normal file
@@ -0,0 +1,431 @@
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn admin_system_and_endpoint_roots_stay_thin() {
|
||||
let system_mod = read_workspace_file("apps/aether-gateway/src/handlers/admin/system/mod.rs");
|
||||
for pattern in [
|
||||
"pub(super) use super::auth::{",
|
||||
"pub(super) use super::model::{",
|
||||
"pub(super) use super::provider::{",
|
||||
] {
|
||||
assert!(
|
||||
!system_mod.contains(pattern),
|
||||
"handlers/admin/system/mod.rs should not act as a cross-domain re-export layer for {pattern}"
|
||||
);
|
||||
}
|
||||
for pattern in [
|
||||
"pub(crate) use self::adaptive::maybe_build_local_admin_adaptive_response;",
|
||||
"pub(crate) use self::core::maybe_build_local_admin_core_response;",
|
||||
"pub(crate) use self::management_tokens::maybe_build_local_admin_management_tokens_response;",
|
||||
"pub(crate) use self::modules::maybe_build_local_admin_modules_response;",
|
||||
"pub(crate) use self::proxy_nodes::maybe_build_local_admin_proxy_nodes_response;",
|
||||
] {
|
||||
assert!(
|
||||
system_mod.contains(pattern),
|
||||
"handlers/admin/system/mod.rs should stay as a thin system subdomain router for {pattern}"
|
||||
);
|
||||
}
|
||||
assert!(
|
||||
system_mod.contains(
|
||||
"pub(crate) use crate::handlers::admin::provider::pool_admin::maybe_build_local_admin_pool_response;"
|
||||
),
|
||||
"handlers/admin/system/mod.rs should delegate pool admin seam directly to provider::pool_admin"
|
||||
);
|
||||
|
||||
assert!(
|
||||
!workspace_file_exists("apps/aether-gateway/src/handlers/admin/system/pool/mod.rs"),
|
||||
"handlers/admin/system/pool/mod.rs should be deleted once system root delegates pool admin directly to provider::pool_admin"
|
||||
);
|
||||
|
||||
let endpoint_mod =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/endpoint/mod.rs");
|
||||
for pattern in [
|
||||
"use self::extractors::{",
|
||||
"use self::health_builders::{",
|
||||
"use self::payloads::{",
|
||||
] {
|
||||
assert!(
|
||||
!endpoint_mod.contains(pattern),
|
||||
"handlers/admin/endpoint/mod.rs should not re-export local helper seam {pattern}"
|
||||
);
|
||||
}
|
||||
assert!(
|
||||
endpoint_mod.contains(
|
||||
"pub(crate) use self::health_builders::build_admin_endpoint_health_status_payload;"
|
||||
),
|
||||
"handlers/admin/endpoint/mod.rs should keep only the public health status payload seam"
|
||||
);
|
||||
|
||||
let system_core_mod =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/system/core/mod.rs");
|
||||
for pattern in [
|
||||
"maybe_build_local_admin_management_tokens_response",
|
||||
"maybe_build_local_admin_oauth_response",
|
||||
"maybe_build_local_admin_modules_response",
|
||||
"maybe_build_local_admin_model_catalog_response",
|
||||
] {
|
||||
assert!(
|
||||
system_core_mod.contains(pattern),
|
||||
"handlers/admin/system/core/mod.rs should call the real owner {pattern}"
|
||||
);
|
||||
}
|
||||
for path in [
|
||||
"apps/aether-gateway/src/handlers/admin/system/core/management_tokens_routes.rs",
|
||||
"apps/aether-gateway/src/handlers/admin/system/core/model_routes.rs",
|
||||
"apps/aether-gateway/src/handlers/admin/system/core/modules_routes.rs",
|
||||
"apps/aether-gateway/src/handlers/admin/system/core/oauth_routes.rs",
|
||||
] {
|
||||
assert!(
|
||||
!workspace_file_exists(path),
|
||||
"{path} should be deleted once system/core/mod.rs dispatches directly to real owners"
|
||||
);
|
||||
}
|
||||
|
||||
let system_routes =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/system/core/system_routes.rs");
|
||||
assert!(
|
||||
!system_routes.contains("use crate::handlers::public::{"),
|
||||
"handlers/admin/system/core/system_routes.rs should not borrow system-owned route helpers from handlers/public"
|
||||
);
|
||||
assert!(
|
||||
!system_routes.contains("crate::handlers::admin::auth::build_proxy_error_response")
|
||||
&& !system_routes.contains("use crate::handlers::admin::auth::build_proxy_error_response;"),
|
||||
"handlers/admin/system/core/system_routes.rs should not borrow proxy error builder from auth"
|
||||
);
|
||||
for pattern in [
|
||||
"build_admin_email_template_payload",
|
||||
"build_admin_email_templates_payload",
|
||||
"preview_admin_email_template",
|
||||
"reset_admin_email_template",
|
||||
] {
|
||||
assert!(
|
||||
system_routes.contains(pattern),
|
||||
"handlers/admin/system/core/system_routes.rs should keep delegating through admin system shared helper {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
assert!(
|
||||
endpoint_mod.contains(
|
||||
"endpoint_keys::maybe_build_local_admin_endpoints_keys_response"
|
||||
),
|
||||
"handlers/admin/endpoint/mod.rs should dispatch provider key management directly to provider::endpoint_keys"
|
||||
);
|
||||
|
||||
assert!(
|
||||
endpoint_mod.contains(
|
||||
"endpoints_admin::maybe_build_local_admin_endpoints_routes_response"
|
||||
),
|
||||
"handlers/admin/endpoint/mod.rs should dispatch provider endpoint CRUD directly to provider::endpoints_admin"
|
||||
);
|
||||
for path in [
|
||||
"apps/aether-gateway/src/handlers/admin/endpoint/keys.rs",
|
||||
"apps/aether-gateway/src/handlers/admin/endpoint/routes.rs",
|
||||
] {
|
||||
assert!(
|
||||
!workspace_file_exists(path),
|
||||
"{path} should be deleted once endpoint root dispatches directly to provider-owned handlers"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_model_root_owns_model_catalog_routes() {
|
||||
let model_mod = read_workspace_file("apps/aether-gateway/src/handlers/admin/model/mod.rs");
|
||||
assert!(
|
||||
model_mod.contains("mod catalog_routes;"),
|
||||
"handlers/admin/model/mod.rs should register catalog_routes owner"
|
||||
);
|
||||
assert!(
|
||||
model_mod.contains(
|
||||
"pub(crate) use self::catalog_routes::maybe_build_local_admin_model_catalog_response;"
|
||||
),
|
||||
"handlers/admin/model/mod.rs should expose model catalog route seam"
|
||||
);
|
||||
|
||||
let model_catalog_routes =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/model/catalog_routes.rs");
|
||||
for pattern in [
|
||||
"build_admin_model_catalog_payload",
|
||||
"read_admin_external_models_cache",
|
||||
"clear_admin_external_models_cache",
|
||||
"ADMIN_MODEL_CATALOG_DATA_UNAVAILABLE_DETAIL",
|
||||
] {
|
||||
assert!(
|
||||
model_catalog_routes.contains(pattern),
|
||||
"handlers/admin/model/catalog_routes.rs should own {pattern}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_system_owns_admin_module_helpers() {
|
||||
let public_modules_helpers =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/public/system_modules_helpers.rs");
|
||||
for pattern in [
|
||||
"admin_module_by_name",
|
||||
"admin_module_name_from_enabled_path",
|
||||
"admin_module_name_from_status_path",
|
||||
"build_admin_module_runtime_state",
|
||||
"build_admin_module_status_payload",
|
||||
"build_admin_module_validation_result",
|
||||
"build_admin_modules_status_payload",
|
||||
"AdminSetModuleEnabledRequest",
|
||||
] {
|
||||
assert!(
|
||||
!public_modules_helpers.contains(pattern),
|
||||
"handlers/public/system_modules_helpers.rs should not re-export admin module helper {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let public_modules = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/public/system_modules_helpers/modules.rs",
|
||||
);
|
||||
for pattern in [
|
||||
"pub(crate) struct AdminModuleDefinition",
|
||||
"pub(crate) struct AdminSetModuleEnabledRequest",
|
||||
"pub(crate) struct AdminModuleRuntimeState",
|
||||
"pub(crate) fn admin_module_by_name",
|
||||
"pub(crate) async fn build_admin_module_runtime_state",
|
||||
"pub(crate) fn build_admin_module_validation_result",
|
||||
"pub(crate) async fn build_admin_module_status_payload",
|
||||
"pub(crate) async fn build_admin_modules_status_payload",
|
||||
"pub(crate) fn admin_module_name_from_status_path",
|
||||
"pub(crate) fn admin_module_name_from_enabled_path",
|
||||
] {
|
||||
assert!(
|
||||
!public_modules.contains(pattern),
|
||||
"handlers/public/system_modules_helpers/modules.rs should not own admin module helper {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let system_shared_modules =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/system/shared/modules.rs");
|
||||
for pattern in [
|
||||
"pub(crate) struct AdminModuleDefinition",
|
||||
"pub(crate) struct AdminSetModuleEnabledRequest",
|
||||
"pub(crate) struct AdminModuleRuntimeState",
|
||||
"pub(crate) fn admin_module_by_name",
|
||||
"pub(crate) async fn build_admin_module_runtime_state",
|
||||
"pub(crate) fn build_admin_module_validation_result",
|
||||
"pub(crate) async fn build_admin_module_status_payload",
|
||||
"pub(crate) async fn build_admin_modules_status_payload",
|
||||
"pub(crate) fn admin_module_name_from_status_path",
|
||||
"pub(crate) fn admin_module_name_from_enabled_path",
|
||||
] {
|
||||
assert!(
|
||||
system_shared_modules.contains(pattern),
|
||||
"handlers/admin/system/shared/modules.rs should own {pattern}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn admin_system_owns_system_route_helpers() {
|
||||
let public_system_helpers =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/public/system_modules_helpers.rs");
|
||||
for pattern in [
|
||||
"current_aether_version",
|
||||
"build_admin_system_check_update_payload",
|
||||
"build_admin_system_stats_payload",
|
||||
"build_admin_system_settings_payload",
|
||||
"apply_admin_system_settings_update",
|
||||
"build_admin_api_formats_payload",
|
||||
"build_admin_system_config_export_payload",
|
||||
"build_admin_system_users_export_payload",
|
||||
"build_admin_system_configs_payload",
|
||||
"build_admin_system_config_detail_payload",
|
||||
"apply_admin_system_config_update",
|
||||
"delete_admin_system_config",
|
||||
"serialize_admin_system_users_export_wallet",
|
||||
"module_available_from_env",
|
||||
"system_config_bool",
|
||||
"system_config_string",
|
||||
"read_admin_email_template_payload",
|
||||
"escape_admin_email_template_html",
|
||||
"render_admin_email_template_html",
|
||||
] {
|
||||
assert!(
|
||||
!public_system_helpers.contains(pattern),
|
||||
"handlers/public/system_modules_helpers.rs should not re-export admin system helper {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let public_mod = read_workspace_file("apps/aether-gateway/src/handlers/public/mod.rs");
|
||||
for pattern in [
|
||||
"current_aether_version",
|
||||
"build_admin_system_check_update_payload",
|
||||
"build_admin_system_stats_payload",
|
||||
"build_admin_system_settings_payload",
|
||||
"apply_admin_system_settings_update",
|
||||
"build_admin_api_formats_payload",
|
||||
"build_admin_system_config_export_payload",
|
||||
"build_admin_system_users_export_payload",
|
||||
"build_admin_system_configs_payload",
|
||||
"build_admin_system_config_detail_payload",
|
||||
"apply_admin_system_config_update",
|
||||
"delete_admin_system_config",
|
||||
"serialize_admin_system_users_export_wallet",
|
||||
"module_available_from_env",
|
||||
"system_config_bool",
|
||||
"system_config_string",
|
||||
"read_admin_email_template_payload",
|
||||
"escape_admin_email_template_html",
|
||||
"render_admin_email_template_html",
|
||||
] {
|
||||
assert!(
|
||||
!public_mod.contains(pattern),
|
||||
"handlers/public/mod.rs should not re-export admin system helper {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let public_system_file = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/public/system_modules_helpers/system.rs",
|
||||
);
|
||||
for pattern in [
|
||||
"pub(crate) fn current_aether_version",
|
||||
"pub(crate) fn build_admin_system_check_update_payload",
|
||||
"pub(crate) async fn build_admin_system_stats_payload",
|
||||
"pub(crate) async fn build_admin_system_settings_payload",
|
||||
"pub(crate) async fn build_admin_system_config_export_payload",
|
||||
"pub(crate) async fn build_admin_system_users_export_payload",
|
||||
"pub(crate) fn build_admin_system_configs_payload",
|
||||
"pub(crate) async fn build_admin_system_config_detail_payload",
|
||||
"pub(crate) async fn apply_admin_system_config_update",
|
||||
"pub(crate) async fn delete_admin_system_config",
|
||||
"pub(crate) fn serialize_admin_system_users_export_wallet",
|
||||
"pub(crate) fn module_available_from_env",
|
||||
"pub(crate) fn system_config_bool",
|
||||
"pub(crate) fn system_config_string",
|
||||
"pub(crate) async fn read_admin_email_template_payload",
|
||||
"pub(crate) fn escape_admin_email_template_html",
|
||||
"pub(crate) fn render_admin_email_template_html",
|
||||
] {
|
||||
assert!(
|
||||
!public_system_file.contains(pattern),
|
||||
"handlers/public/system_modules_helpers/system.rs should not own shared/admin system helper {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let shared_mod = read_workspace_file("apps/aether-gateway/src/handlers/shared/mod.rs");
|
||||
for pattern in [
|
||||
"mod email_templates;",
|
||||
"mod system_config_values;",
|
||||
"pub(crate) use self::email_templates::{",
|
||||
"pub(crate) use self::system_config_values::{",
|
||||
] {
|
||||
assert!(
|
||||
shared_mod.contains(pattern),
|
||||
"handlers/shared/mod.rs should wire shared system helper owner {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let shared_system_config_values =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/shared/system_config_values.rs");
|
||||
for pattern in [
|
||||
"pub(crate) fn module_available_from_env",
|
||||
"pub(crate) fn system_config_bool",
|
||||
"pub(crate) fn system_config_string",
|
||||
] {
|
||||
assert!(
|
||||
shared_system_config_values.contains(pattern),
|
||||
"handlers/shared/system_config_values.rs should own {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let shared_email_templates =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/shared/email_templates.rs");
|
||||
for pattern in [
|
||||
"pub(crate) fn admin_email_template_definition",
|
||||
"pub(crate) fn admin_email_template_subject_key",
|
||||
"pub(crate) fn admin_email_template_html_key",
|
||||
"pub(crate) async fn read_admin_email_template_payload",
|
||||
"pub(crate) fn escape_admin_email_template_html",
|
||||
"pub(crate) fn render_admin_email_template_html",
|
||||
] {
|
||||
assert!(
|
||||
shared_email_templates.contains(pattern),
|
||||
"handlers/shared/email_templates.rs should own {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let system_shared_mod =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/system/shared/mod.rs");
|
||||
for pattern in [
|
||||
"pub(crate) mod configs;",
|
||||
"pub(crate) mod email_templates;",
|
||||
"pub(crate) mod modules;",
|
||||
"pub(crate) mod paths;",
|
||||
"pub(crate) mod settings;",
|
||||
] {
|
||||
assert!(
|
||||
system_shared_mod.contains(pattern),
|
||||
"handlers/admin/system/shared/mod.rs should wire system helper owner {pattern}"
|
||||
);
|
||||
}
|
||||
for forbidden in [
|
||||
"pub(crate) use self::configs::*;",
|
||||
"pub(crate) use self::email_templates::{",
|
||||
"pub(crate) use self::modules::*;",
|
||||
"pub(crate) use self::paths::*;",
|
||||
"pub(crate) use self::settings::*;",
|
||||
] {
|
||||
assert!(
|
||||
!system_shared_mod.contains(forbidden),
|
||||
"handlers/admin/system/shared/mod.rs should not remain a re-export hub for {forbidden}"
|
||||
);
|
||||
}
|
||||
|
||||
assert!(
|
||||
!workspace_file_exists("apps/aether-gateway/src/handlers/admin/system/shared/system.rs"),
|
||||
"handlers/admin/system/shared/system.rs should be replaced by email_templates.rs"
|
||||
);
|
||||
|
||||
let system_shared_settings =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/system/shared/settings.rs");
|
||||
for pattern in [
|
||||
"pub(crate) fn current_aether_version",
|
||||
"pub(crate) fn build_admin_system_check_update_payload",
|
||||
"pub(crate) async fn build_admin_system_stats_payload",
|
||||
"pub(crate) async fn build_admin_system_settings_payload",
|
||||
"pub(crate) async fn apply_admin_system_settings_update",
|
||||
"pub(crate) fn build_admin_api_formats_payload",
|
||||
] {
|
||||
assert!(
|
||||
system_shared_settings.contains(pattern),
|
||||
"handlers/admin/system/shared/settings.rs should own {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let system_shared_configs =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/system/shared/configs.rs");
|
||||
for pattern in [
|
||||
"pub(crate) async fn build_admin_system_config_export_payload",
|
||||
"pub(crate) fn serialize_admin_system_users_export_wallet",
|
||||
"pub(crate) async fn build_admin_system_users_export_payload",
|
||||
"pub(crate) fn build_admin_system_configs_payload",
|
||||
"pub(crate) async fn build_admin_system_config_detail_payload",
|
||||
"pub(crate) async fn apply_admin_system_config_update",
|
||||
"pub(crate) async fn delete_admin_system_config",
|
||||
] {
|
||||
assert!(
|
||||
system_shared_configs.contains(pattern),
|
||||
"handlers/admin/system/shared/configs.rs should own {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let system_shared_email_templates = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/system/shared/email_templates.rs",
|
||||
);
|
||||
for pattern in [
|
||||
"pub(crate) async fn build_admin_email_templates_payload",
|
||||
"pub(crate) async fn build_admin_email_template_payload",
|
||||
"pub(crate) async fn apply_admin_email_template_update",
|
||||
"pub(crate) async fn preview_admin_email_template",
|
||||
"pub(crate) async fn reset_admin_email_template",
|
||||
] {
|
||||
assert!(
|
||||
system_shared_email_templates.contains(pattern),
|
||||
"handlers/admin/system/shared/email_templates.rs should own {pattern}"
|
||||
);
|
||||
}
|
||||
}
|
||||
1073
apps/aether-gateway/src/tests/architecture/ai_pipeline.rs
Normal file
1073
apps/aether-gateway/src/tests/architecture/ai_pipeline.rs
Normal file
File diff suppressed because it is too large
Load Diff
133
apps/aether-gateway/src/tests/architecture/mod.rs
Normal file
133
apps/aether-gateway/src/tests/architecture/mod.rs
Normal file
@@ -0,0 +1,133 @@
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub(super) fn collect_rust_files(root: &Path, files: &mut Vec<PathBuf>) {
|
||||
for entry in fs::read_dir(root).expect("directory should be readable") {
|
||||
let entry = entry.expect("directory entry should be readable");
|
||||
let path = entry.path();
|
||||
if path.is_dir() {
|
||||
collect_rust_files(&path, files);
|
||||
continue;
|
||||
}
|
||||
if path.extension().and_then(|value| value.to_str()) == Some("rs") {
|
||||
files.push(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn assert_no_sqlx_queries(root_relative_path: &str) {
|
||||
let root = Path::new(env!("CARGO_MANIFEST_DIR")).join(root_relative_path);
|
||||
let mut files = Vec::new();
|
||||
collect_rust_files(&root, &mut files);
|
||||
|
||||
let patterns = [
|
||||
"sqlx::query(",
|
||||
"sqlx::query_scalar",
|
||||
"query_scalar::<",
|
||||
"QueryBuilder<",
|
||||
];
|
||||
let violations = files
|
||||
.into_iter()
|
||||
.filter_map(|path| {
|
||||
let source = fs::read_to_string(&path).expect("source file should be readable");
|
||||
let hits = patterns
|
||||
.iter()
|
||||
.filter(|pattern| source.contains(**pattern))
|
||||
.copied()
|
||||
.collect::<Vec<_>>();
|
||||
if hits.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(format!("{} -> {}", path.display(), hits.join(", ")))
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert!(
|
||||
violations.is_empty(),
|
||||
"disallowed SQL ownership violations:\n{}",
|
||||
violations.join("\n")
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn assert_no_sensitive_log_patterns(root_relative_path: &str, patterns: &[&str]) {
|
||||
let root = Path::new(env!("CARGO_MANIFEST_DIR")).join(root_relative_path);
|
||||
let mut files = Vec::new();
|
||||
collect_rust_files(&root, &mut files);
|
||||
|
||||
let violations = files
|
||||
.into_iter()
|
||||
.filter_map(|path| {
|
||||
let source = fs::read_to_string(&path).expect("source file should be readable");
|
||||
let hits = patterns
|
||||
.iter()
|
||||
.filter(|pattern| source.contains(**pattern))
|
||||
.copied()
|
||||
.collect::<Vec<_>>();
|
||||
if hits.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(format!("{} -> {}", path.display(), hits.join(", ")))
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert!(
|
||||
violations.is_empty(),
|
||||
"disallowed sensitive logging patterns:\n{}",
|
||||
violations.join("\n")
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn assert_no_module_dependency_patterns(root_relative_path: &str, patterns: &[&str]) {
|
||||
let root = Path::new(env!("CARGO_MANIFEST_DIR")).join(root_relative_path);
|
||||
let mut files = Vec::new();
|
||||
collect_rust_files(&root, &mut files);
|
||||
|
||||
let violations = files
|
||||
.into_iter()
|
||||
.filter_map(|path| {
|
||||
let source = fs::read_to_string(&path).expect("source file should be readable");
|
||||
let hits = patterns
|
||||
.iter()
|
||||
.filter(|pattern| source.contains(**pattern))
|
||||
.copied()
|
||||
.collect::<Vec<_>>();
|
||||
if hits.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(format!("{} -> {}", path.display(), hits.join(", ")))
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert!(
|
||||
violations.is_empty(),
|
||||
"disallowed module dependency patterns:\n{}",
|
||||
violations.join("\n")
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn workspace_file_exists(root_relative_path: &str) -> bool {
|
||||
Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("../..")
|
||||
.join(root_relative_path)
|
||||
.exists()
|
||||
}
|
||||
|
||||
pub(super) fn read_workspace_file(path: &str) -> String {
|
||||
let workspace_root = Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("../..")
|
||||
.canonicalize()
|
||||
.expect("workspace root should resolve");
|
||||
fs::read_to_string(workspace_root.join(path)).expect("source file should be readable")
|
||||
}
|
||||
|
||||
mod admin_observability;
|
||||
mod admin_provider;
|
||||
mod admin_shared;
|
||||
mod admin_system;
|
||||
mod ai_pipeline;
|
||||
mod runtime_and_security;
|
||||
mod sql_and_data;
|
||||
mod usage;
|
||||
1243
apps/aether-gateway/src/tests/architecture/runtime_and_security.rs
Normal file
1243
apps/aether-gateway/src/tests/architecture/runtime_and_security.rs
Normal file
File diff suppressed because it is too large
Load Diff
251
apps/aether-gateway/src/tests/architecture/sql_and_data.rs
Normal file
251
apps/aether-gateway/src/tests/architecture/sql_and_data.rs
Normal file
@@ -0,0 +1,251 @@
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn handlers_do_not_inline_sql_queries() {
|
||||
assert_no_sqlx_queries("src/handlers");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gateway_runtime_does_not_inline_sql_queries() {
|
||||
assert_no_sqlx_queries("src/state/runtime");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wallet_repository_does_not_reexport_settlement_types() {
|
||||
let wallet_mod = read_workspace_file("crates/aether-data/src/repository/wallet/mod.rs");
|
||||
let wallet_types = read_workspace_file("crates/aether-data/src/repository/wallet/types.rs");
|
||||
let wallet_sql = read_workspace_file("crates/aether-data/src/repository/wallet/sql.rs");
|
||||
let wallet_memory = read_workspace_file("crates/aether-data/src/repository/wallet/memory.rs");
|
||||
|
||||
assert!(
|
||||
!wallet_mod.contains("StoredUsageSettlement"),
|
||||
"wallet/mod.rs should not export StoredUsageSettlement"
|
||||
);
|
||||
assert!(
|
||||
!wallet_mod.contains("UsageSettlementInput"),
|
||||
"wallet/mod.rs should not export UsageSettlementInput"
|
||||
);
|
||||
assert!(
|
||||
!wallet_types.contains("pub use crate::repository::settlement"),
|
||||
"wallet/types.rs should not re-export settlement types"
|
||||
);
|
||||
assert!(
|
||||
!wallet_types.contains("async fn settle_usage("),
|
||||
"wallet/types.rs should not own settlement entrypoints"
|
||||
);
|
||||
assert!(
|
||||
!wallet_sql.contains("impl SettlementWriteRepository"),
|
||||
"wallet/sql.rs should not implement SettlementWriteRepository"
|
||||
);
|
||||
assert!(
|
||||
!wallet_memory.contains("impl SettlementWriteRepository"),
|
||||
"wallet/memory.rs should not implement SettlementWriteRepository"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gateway_system_config_types_are_owned_by_aether_data() {
|
||||
let state_mod = read_workspace_file("apps/aether-gateway/src/data/state/mod.rs");
|
||||
assert!(
|
||||
state_mod.contains("aether_data::repository::system"),
|
||||
"data/state/mod.rs should depend on aether-data system types"
|
||||
);
|
||||
assert!(
|
||||
!state_mod.contains("pub(crate) struct StoredSystemConfigEntry"),
|
||||
"data/state/mod.rs should not define StoredSystemConfigEntry locally"
|
||||
);
|
||||
|
||||
let state_core = read_workspace_file("apps/aether-gateway/src/data/state/core.rs");
|
||||
for pattern in [
|
||||
"backend.list_system_config_entries().await",
|
||||
"upsert_system_config_entry(key, value, description)",
|
||||
"AdminSystemStats::default()",
|
||||
] {
|
||||
assert!(
|
||||
state_core.contains(pattern),
|
||||
"data/state/core.rs should use shared system DTO path {pattern}"
|
||||
);
|
||||
}
|
||||
for pattern in [
|
||||
"|(key, value, description, updated_at_unix_secs)|",
|
||||
"Ok((0, 0, 0, 0))",
|
||||
] {
|
||||
assert!(
|
||||
!state_core.contains(pattern),
|
||||
"data/state/core.rs should not own local system DTO projection {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let system_types = read_workspace_file("crates/aether-data/src/repository/system.rs");
|
||||
for pattern in [
|
||||
"pub struct StoredSystemConfigEntry",
|
||||
"pub struct AdminSystemStats",
|
||||
"pub struct AdminSecurityBlacklistEntry",
|
||||
] {
|
||||
assert!(
|
||||
system_types.contains(pattern),
|
||||
"aether-data system module should own {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let admin_types = read_workspace_file("apps/aether-gateway/src/state/admin_types.rs");
|
||||
assert!(
|
||||
admin_types.contains("aether_data::repository::system::AdminSecurityBlacklistEntry"),
|
||||
"state/admin_types.rs should re-export AdminSecurityBlacklistEntry from aether-data"
|
||||
);
|
||||
assert!(
|
||||
!admin_types.contains("struct AdminSecurityBlacklistEntry"),
|
||||
"state/admin_types.rs should not define AdminSecurityBlacklistEntry locally"
|
||||
);
|
||||
|
||||
let runtime_mod = read_workspace_file("apps/aether-gateway/src/state/runtime/mod.rs");
|
||||
assert!(
|
||||
!runtime_mod.contains("AdminSecurityBlacklistEntryPayload"),
|
||||
"state/runtime/mod.rs should not keep the unused blacklist payload wrapper"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gateway_auth_snapshot_type_is_owned_by_aether_data() {
|
||||
let gateway_auth = read_workspace_file("apps/aether-gateway/src/data/auth.rs");
|
||||
let runtime_mod = read_workspace_file("apps/aether-gateway/src/state/runtime/mod.rs");
|
||||
let auth_api_keys =
|
||||
read_workspace_file("apps/aether-gateway/src/state/runtime/auth/api_keys.rs");
|
||||
assert!(
|
||||
gateway_auth.contains("aether_data::repository::auth"),
|
||||
"data/auth.rs should depend on aether-data auth snapshot types"
|
||||
);
|
||||
assert!(
|
||||
gateway_auth.contains("ResolvedAuthApiKeySnapshot as GatewayAuthApiKeySnapshot"),
|
||||
"data/auth.rs should expose the shared resolved auth snapshot type under the gateway-facing name"
|
||||
);
|
||||
for pattern in [
|
||||
"pub(crate) struct GatewayAuthApiKeySnapshot",
|
||||
"pub(crate) async fn read_auth_api_key_snapshot(",
|
||||
"pub(crate) async fn read_auth_api_key_snapshot_by_key_hash(",
|
||||
"fn effective_allowed_providers(",
|
||||
"fn effective_allowed_api_formats(",
|
||||
"fn effective_allowed_models(",
|
||||
] {
|
||||
assert!(
|
||||
!gateway_auth.contains(pattern),
|
||||
"data/auth.rs should not own local auth snapshot logic {pattern}"
|
||||
);
|
||||
}
|
||||
for pattern in [
|
||||
"pub(crate) async fn read_auth_api_key_snapshot(",
|
||||
"pub(crate) async fn read_auth_api_key_snapshots_by_ids(",
|
||||
] {
|
||||
assert!(
|
||||
!auth_api_keys.contains(pattern),
|
||||
"state/runtime/auth/api_keys.rs should not keep auth snapshot read wrapper {pattern}"
|
||||
);
|
||||
}
|
||||
assert!(
|
||||
!runtime_mod.contains("mod audit;"),
|
||||
"state/runtime/mod.rs should not keep the obsolete audit runtime module"
|
||||
);
|
||||
assert!(
|
||||
auth_api_keys.contains("touch_auth_api_key_last_used_best_effort"),
|
||||
"state/runtime/auth/api_keys.rs should own auth api key last_used touch helper"
|
||||
);
|
||||
assert!(
|
||||
!auth_api_keys.contains("fn has_auth_api_key_writer("),
|
||||
"state/runtime/auth/api_keys.rs should not keep auth api key writer passthrough"
|
||||
);
|
||||
|
||||
let auth_types = read_workspace_file("crates/aether-data/src/repository/auth/types.rs");
|
||||
for pattern in [
|
||||
"pub struct ResolvedAuthApiKeySnapshot",
|
||||
"pub trait ResolvedAuthApiKeySnapshotReader",
|
||||
"pub async fn read_resolved_auth_api_key_snapshot(",
|
||||
"pub async fn read_resolved_auth_api_key_snapshot_by_key_hash(",
|
||||
"pub async fn read_resolved_auth_api_key_snapshot_by_user_api_key_ids(",
|
||||
"pub fn effective_allowed_providers(&self)",
|
||||
"pub fn effective_allowed_api_formats(&self)",
|
||||
"pub fn effective_allowed_models(&self)",
|
||||
] {
|
||||
assert!(
|
||||
auth_types.contains(pattern),
|
||||
"aether-data auth types should own {pattern}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gateway_auth_data_layer_does_not_keep_ldap_row_wrapper() {
|
||||
let gateway_auth_state = read_workspace_file("apps/aether-gateway/src/data/state/auth.rs");
|
||||
for pattern in [
|
||||
"struct StoredLdapAuthUserRow",
|
||||
"fn map_ldap_user_auth_row(",
|
||||
"Result<Option<StoredLdapAuthUserRow>, DataLayerError>",
|
||||
"existing.user.",
|
||||
] {
|
||||
assert!(
|
||||
!gateway_auth_state.contains(pattern),
|
||||
"data/state/auth.rs should not keep ldap row wrapper {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
for pattern in [
|
||||
"Result<Option<StoredUserAuthRecord>, DataLayerError>",
|
||||
"return map_user_auth_row(row).map(Some);",
|
||||
] {
|
||||
assert!(
|
||||
gateway_auth_state.contains(pattern),
|
||||
"data/state/auth.rs should use shared user auth record directly via {pattern}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gateway_provider_oauth_storage_types_are_owned_by_aether_data() {
|
||||
let provider_oauth_state =
|
||||
read_workspace_file("apps/aether-gateway/src/handlers/admin/provider/oauth/state.rs");
|
||||
assert!(
|
||||
provider_oauth_state.contains("aether_data::repository::provider_oauth"),
|
||||
"provider_oauth/state.rs should depend on aether-data provider oauth storage types"
|
||||
);
|
||||
for pattern in [
|
||||
"pub(crate) struct StoredAdminProviderOAuthDeviceSession",
|
||||
"pub(crate) struct StoredAdminProviderOAuthState",
|
||||
"const KIRO_DEVICE_AUTH_SESSION_PREFIX",
|
||||
"fn provider_oauth_device_session_key(",
|
||||
"fn build_provider_oauth_batch_task_status_payload(",
|
||||
"fn provider_oauth_batch_task_key(",
|
||||
"const PROVIDER_OAUTH_BATCH_TASK_TTL_SECS",
|
||||
"format!(\"provider_oauth_state:{nonce}\")",
|
||||
] {
|
||||
assert!(
|
||||
!provider_oauth_state.contains(pattern),
|
||||
"provider_oauth/state.rs should not own local storage helper {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let dispatch_device = read_workspace_file(
|
||||
"apps/aether-gateway/src/handlers/admin/provider/oauth/dispatch/device.rs",
|
||||
);
|
||||
assert!(
|
||||
dispatch_device.contains("aether_data::repository::provider_oauth"),
|
||||
"provider_oauth/dispatch/device.rs should use shared provider oauth storage DTOs"
|
||||
);
|
||||
|
||||
let shared_provider_oauth =
|
||||
read_workspace_file("crates/aether-data/src/repository/provider_oauth.rs");
|
||||
for pattern in [
|
||||
"pub struct StoredAdminProviderOAuthDeviceSession",
|
||||
"pub struct StoredAdminProviderOAuthState",
|
||||
"pub fn provider_oauth_device_session_storage_key(",
|
||||
"pub fn provider_oauth_state_storage_key(",
|
||||
"pub fn provider_oauth_batch_task_storage_key(",
|
||||
"pub fn build_provider_oauth_batch_task_status_payload(",
|
||||
"pub const KIRO_DEVICE_AUTH_SESSION_TTL_BUFFER_SECS: u64 = 60;",
|
||||
"pub const PROVIDER_OAUTH_BATCH_TASK_TTL_SECS: u64 = 24 * 60 * 60;",
|
||||
"pub const PROVIDER_OAUTH_STATE_TTL_SECS: u64 = 600;",
|
||||
] {
|
||||
assert!(
|
||||
shared_provider_oauth.contains(pattern),
|
||||
"aether-data provider oauth storage module should own {pattern}"
|
||||
);
|
||||
}
|
||||
}
|
||||
195
apps/aether-gateway/src/tests/architecture/usage.rs
Normal file
195
apps/aether-gateway/src/tests/architecture/usage.rs
Normal file
@@ -0,0 +1,195 @@
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn usage_runtime_paths_depend_on_shared_crates_not_app_runtime_shims() {
|
||||
for path in [
|
||||
"apps/aether-gateway/src/usage/runtime.rs",
|
||||
"apps/aether-gateway/src/usage/worker.rs",
|
||||
"apps/aether-gateway/src/async_task/runtime.rs",
|
||||
] {
|
||||
let source = read_workspace_file(path);
|
||||
assert!(
|
||||
source.contains("aether_usage_runtime"),
|
||||
"{path} should depend on aether_usage_runtime"
|
||||
);
|
||||
assert!(
|
||||
!source.contains("wallet_runtime"),
|
||||
"{path} should not depend on wallet_runtime"
|
||||
);
|
||||
}
|
||||
|
||||
for path in ["apps/aether-gateway/src/async_task/runtime.rs"] {
|
||||
let source = read_workspace_file(path);
|
||||
assert!(
|
||||
source.contains("aether_billing"),
|
||||
"{path} should depend on aether_billing"
|
||||
);
|
||||
assert!(
|
||||
!source.contains("billing_runtime::enrich_usage_event_with_billing"),
|
||||
"{path} should not depend on billing_runtime compat re-export"
|
||||
);
|
||||
assert!(
|
||||
!source.contains("settlement_runtime::settle_usage_if_needed"),
|
||||
"{path} should not depend on settlement_runtime compat re-export"
|
||||
);
|
||||
}
|
||||
|
||||
let usage_runtime = read_workspace_file("apps/aether-gateway/src/usage/runtime.rs");
|
||||
assert!(
|
||||
!usage_runtime.contains("GatewayDataState"),
|
||||
"usage/runtime.rs should not own GatewayDataState integration impls anymore"
|
||||
);
|
||||
assert!(
|
||||
!usage_runtime.contains("UsageBillingEventEnricher"),
|
||||
"usage/runtime.rs should not own UsageBillingEventEnricher impl anymore"
|
||||
);
|
||||
assert!(
|
||||
!usage_runtime.contains("UsageRuntimeAccess"),
|
||||
"usage/runtime.rs should not own UsageRuntimeAccess impl anymore"
|
||||
);
|
||||
|
||||
let usage_worker = read_workspace_file("apps/aether-gateway/src/usage/worker.rs");
|
||||
let runtime_usage_worker = usage_worker
|
||||
.split("#[cfg(test)]")
|
||||
.next()
|
||||
.unwrap_or(usage_worker.as_str());
|
||||
assert!(
|
||||
!runtime_usage_worker.contains("GatewayDataState"),
|
||||
"usage/worker.rs runtime path should not own GatewayDataState integration impls anymore"
|
||||
);
|
||||
assert!(
|
||||
!runtime_usage_worker.contains("UsageRecordWriter"),
|
||||
"usage/worker.rs runtime path should not own UsageRecordWriter impl anymore"
|
||||
);
|
||||
|
||||
let integrations = read_workspace_file("apps/aether-gateway/src/data/state/integrations.rs");
|
||||
for pattern in [
|
||||
"UsageBillingEventEnricher for GatewayDataState",
|
||||
"UsageRuntimeAccess for GatewayDataState",
|
||||
"UsageRecordWriter for GatewayDataState",
|
||||
"UsageSettlementWriter for GatewayDataState",
|
||||
] {
|
||||
assert!(
|
||||
integrations.contains(pattern),
|
||||
"data/state/integrations.rs should centralize {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let usage_reporting_context =
|
||||
read_workspace_file("apps/aether-gateway/src/usage/reporting/context.rs");
|
||||
assert!(
|
||||
usage_reporting_context.contains("aether_usage_runtime"),
|
||||
"usage/reporting/context.rs should depend on aether_usage_runtime"
|
||||
);
|
||||
assert!(
|
||||
usage_reporting_context.contains("resolve_video_task_report_lookup"),
|
||||
"usage/reporting/context.rs should depend on shared video task report lookup helper"
|
||||
);
|
||||
for pattern in [
|
||||
"build_locally_actionable_report_context_from_video_task",
|
||||
"report_context_is_locally_actionable",
|
||||
] {
|
||||
assert!(
|
||||
usage_reporting_context.contains(pattern),
|
||||
"usage/reporting/context.rs should depend on shared usage helper {pattern}"
|
||||
);
|
||||
}
|
||||
assert!(
|
||||
usage_reporting_context.contains("VideoTaskReportLookup::TaskIdOrExternal"),
|
||||
"usage/reporting/context.rs should keep app-local external task fallback orchestration"
|
||||
);
|
||||
for pattern in [
|
||||
"build_locally_actionable_report_context_from_request_candidate",
|
||||
"read_request_candidates_by_request_id(",
|
||||
"resolve_locally_actionable_report_context_from_request_candidates(",
|
||||
] {
|
||||
assert!(
|
||||
!usage_reporting_context.contains(pattern),
|
||||
"usage/reporting/context.rs should not own request-candidate resolver details {pattern}"
|
||||
);
|
||||
}
|
||||
for pattern in [
|
||||
"context\n .get(\"local_task_id\")",
|
||||
"context\n .get(\"local_short_id\")",
|
||||
"context\n .get(\"task_id\")",
|
||||
"VideoTaskLookupKey::ShortId(short_id)",
|
||||
"fn insert_missing_string_value(",
|
||||
"fn insert_missing_optional_string_value(",
|
||||
"fn has_non_empty_str(",
|
||||
"fn has_u64(",
|
||||
] {
|
||||
assert!(
|
||||
!usage_reporting_context.contains(pattern),
|
||||
"usage/reporting/context.rs should not own video task report lookup parsing {pattern}"
|
||||
);
|
||||
}
|
||||
|
||||
let usage_reporting_mod = read_workspace_file("apps/aether-gateway/src/usage/reporting/mod.rs");
|
||||
assert!(
|
||||
usage_reporting_mod.contains("aether_usage_runtime"),
|
||||
"usage/reporting/mod.rs should depend on aether_usage_runtime"
|
||||
);
|
||||
for pattern in [
|
||||
"is_local_ai_sync_report_kind",
|
||||
"is_local_ai_stream_report_kind",
|
||||
"sync_report_represents_failure",
|
||||
"extract_gemini_file_mapping_entries",
|
||||
"gemini_file_mapping_cache_key",
|
||||
"normalize_gemini_file_name",
|
||||
"report_request_id",
|
||||
"should_handle_local_sync_report",
|
||||
"should_handle_local_stream_report",
|
||||
"GEMINI_FILE_MAPPING_TTL_SECONDS",
|
||||
] {
|
||||
assert!(
|
||||
usage_reporting_mod.contains(pattern),
|
||||
"usage/reporting/mod.rs should depend on shared usage helper {pattern}"
|
||||
);
|
||||
}
|
||||
for pattern in [
|
||||
"fn is_local_ai_sync_report_kind(",
|
||||
"fn is_local_ai_stream_report_kind(",
|
||||
"fn sync_report_represents_failure(",
|
||||
"fn extract_gemini_file_mapping_entries(",
|
||||
"fn maybe_push_local_gemini_file_mapping_entry(",
|
||||
"fn extract_sync_report_body_json(",
|
||||
"fn content_type_starts_with(",
|
||||
"fn normalize_file_name(",
|
||||
"const GEMINI_FILE_MAPPING_TTL_SECONDS",
|
||||
"const GEMINI_FILE_MAPPING_CACHE_PREFIX",
|
||||
"fn gemini_file_mapping_cache_key(",
|
||||
"fn report_request_id(",
|
||||
"fn should_handle_local_sync_report(",
|
||||
"fn should_handle_local_stream_report(",
|
||||
"\"openai_video_delete_sync_success\" && payload.status_code == 404",
|
||||
] {
|
||||
assert!(
|
||||
!usage_reporting_mod.contains(pattern),
|
||||
"usage/reporting/mod.rs should not own local report classification logic {pattern}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn handlers_do_not_depend_on_raw_state_records() {
|
||||
assert_no_sensitive_log_patterns(
|
||||
"src/handlers",
|
||||
&[
|
||||
"StoredUserSessionRecord",
|
||||
"StoredUserPreferenceRecord",
|
||||
"AdminPaymentCallbackRecord",
|
||||
],
|
||||
);
|
||||
|
||||
let state_types = read_workspace_file("apps/aether-gateway/src/state/types.rs");
|
||||
for pattern in [
|
||||
"pub(crate) struct GatewayUserSessionView",
|
||||
"pub(crate) struct GatewayUserPreferenceView",
|
||||
"pub(crate) struct GatewayAdminPaymentCallbackView",
|
||||
] {
|
||||
assert!(
|
||||
state_types.contains(pattern),
|
||||
"state/types.rs should keep handler-facing state view {pattern}"
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user