mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-04 08:27:46 +08:00
Merge pull request #850 from AAEE86/ci/gateway-test-slim-batch1
ci(gateway): reduce Test (Gateway) runtime without duplicate execution
This commit is contained in:
@@ -799,6 +799,7 @@ mod tests {
|
||||
use aes_gcm::aead::{Aead, AeadCore, KeyInit, OsRng, Payload};
|
||||
use aes_gcm::Aes256Gcm;
|
||||
use aether_crypto::DEVELOPMENT_ENCRYPTION_KEY;
|
||||
use base64::Engine as _;
|
||||
use bytes::Bytes;
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde_json::json;
|
||||
@@ -1243,8 +1244,18 @@ mod tests {
|
||||
assert_eq!(restored.key_id, None);
|
||||
assert_eq!(restored.export_version.as_deref(), Some("2.3"));
|
||||
|
||||
// 17 个互不相同的合法 base64-32 字节直接密钥:本段只验证“legacy 候选 >16 → TooManyLegacyKeys”,
|
||||
// 不测口令强度、不解密。直接密钥走 decode_direct_fernet_key(生产已支持路径),跳过 PBKDF2,
|
||||
// 避免本用例为计数语义再付 17×10 万次迭代;上半段 DEVELOPMENT_ENCRYPTION_KEY 真实 v1 兼容
|
||||
// 与 wrong-legacy-secret 派生路径保持不变。
|
||||
let too_many: Vec<_> = (0..17)
|
||||
.map(|index| BackupDecryptionKey::historical(format!("legacy-{index}")).unwrap())
|
||||
.map(|index| {
|
||||
let mut material = [0u8; 32];
|
||||
material[0] = index as u8 + 1;
|
||||
material[31] = index as u8 + 1;
|
||||
let secret = base64::engine::general_purpose::STANDARD.encode(material);
|
||||
BackupDecryptionKey::historical(secret).unwrap()
|
||||
})
|
||||
.collect();
|
||||
assert!(matches!(
|
||||
restore_backup_json(
|
||||
|
||||
@@ -5165,15 +5165,6 @@ mod tests {
|
||||
))
|
||||
}
|
||||
|
||||
fn provider_catalog_credential_state() -> AppState {
|
||||
AppState::new()
|
||||
.expect("credential state should build")
|
||||
.with_data_state_for_tests(
|
||||
GatewayDataState::disabled()
|
||||
.with_encryption_key_for_tests(aether_crypto::DEVELOPMENT_ENCRYPTION_KEY),
|
||||
)
|
||||
}
|
||||
|
||||
fn large_pool_fixture(
|
||||
key_count: usize,
|
||||
provider_config: Option<serde_json::Value>,
|
||||
@@ -5224,18 +5215,12 @@ mod tests {
|
||||
)
|
||||
.expect("endpoint transport should build");
|
||||
|
||||
let credential_state = provider_catalog_credential_state();
|
||||
// 这些用例只验证池扫描、跳过计数和游标预算,不会发起请求或读取凭据。
|
||||
// 留空凭据可跳过无关的 Fernet 加解密,同时避免复用绑定密文破坏 key_id AAD。
|
||||
let mut keys = Vec::with_capacity(key_count);
|
||||
let mut rows = Vec::with_capacity(key_count);
|
||||
for index in 0..key_count {
|
||||
let key_id = format!("key-{index:05}");
|
||||
let encrypted_api_key = credential_state
|
||||
.seal_provider_catalog_key_api_key(
|
||||
"provider-pool",
|
||||
&key_id,
|
||||
&format!("secret-{index}"),
|
||||
)
|
||||
.expect("api key should encrypt");
|
||||
let mut key = StoredProviderCatalogKey::new(
|
||||
key_id.clone(),
|
||||
"provider-pool".to_string(),
|
||||
@@ -5247,7 +5232,7 @@ mod tests {
|
||||
.expect("key should build")
|
||||
.with_transport_fields(
|
||||
Some(json!(["openai:chat"])),
|
||||
encrypted_api_key,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
@@ -5382,10 +5367,8 @@ mod tests {
|
||||
.expect("endpoint transport should build")
|
||||
}
|
||||
|
||||
/// 这些测试只检查池调度状态,不涉及凭据解密,因此不构造无关的密文。
|
||||
fn sample_codex_pool_key(provider_id: &str, key_id: &str) -> StoredProviderCatalogKey {
|
||||
let encrypted_api_key = provider_catalog_credential_state()
|
||||
.seal_provider_catalog_key_api_key(provider_id, key_id, &format!("secret-{key_id}"))
|
||||
.expect("api key should encrypt");
|
||||
let mut key = StoredProviderCatalogKey::new(
|
||||
key_id.to_string(),
|
||||
provider_id.to_string(),
|
||||
@@ -5397,7 +5380,7 @@ mod tests {
|
||||
.expect("key should build")
|
||||
.with_transport_fields(
|
||||
Some(json!(["openai:responses"])),
|
||||
encrypted_api_key,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(json!({"openai:responses": 1})),
|
||||
|
||||
@@ -44,21 +44,11 @@ where
|
||||
F: FnOnce() -> Fut + Send + 'static,
|
||||
Fut: std::future::Future<Output = ()> + 'static,
|
||||
{
|
||||
let handle = std::thread::Builder::new()
|
||||
.name(test_name.to_string())
|
||||
.stack_size(PROVIDER_KEYS_TEST_STACK_BYTES)
|
||||
.spawn(move || {
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.expect("test runtime should build");
|
||||
runtime.block_on(make_future());
|
||||
})
|
||||
.expect("provider keys test thread should spawn");
|
||||
|
||||
if let Err(payload) = handle.join() {
|
||||
std::panic::resume_unwind(payload);
|
||||
}
|
||||
crate::tests::run_async_test_on_large_stack(
|
||||
test_name,
|
||||
PROVIDER_KEYS_TEST_STACK_BYTES,
|
||||
make_future,
|
||||
);
|
||||
}
|
||||
|
||||
struct SummaryNullingProviderCatalogReadRepository {
|
||||
|
||||
@@ -59,21 +59,11 @@ where
|
||||
F: FnOnce() -> Fut + Send + 'static,
|
||||
Fut: std::future::Future<Output = ()> + 'static,
|
||||
{
|
||||
let handle = std::thread::Builder::new()
|
||||
.name(test_name.to_string())
|
||||
.stack_size(PROVIDER_QUOTA_TEST_STACK_BYTES)
|
||||
.spawn(move || {
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.expect("test runtime should build");
|
||||
runtime.block_on(make_future());
|
||||
})
|
||||
.expect("provider quota test thread should spawn");
|
||||
|
||||
if let Err(payload) = handle.join() {
|
||||
std::panic::resume_unwind(payload);
|
||||
}
|
||||
crate::tests::run_async_test_on_large_stack(
|
||||
test_name,
|
||||
PROVIDER_QUOTA_TEST_STACK_BYTES,
|
||||
make_future,
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -56,21 +56,11 @@ where
|
||||
F: FnOnce() -> Fut + Send + 'static,
|
||||
Fut: std::future::Future<Output = ()> + 'static,
|
||||
{
|
||||
let handle = std::thread::Builder::new()
|
||||
.name(test_name.to_string())
|
||||
.stack_size(ADMIN_OAUTH_TEST_STACK_BYTES)
|
||||
.spawn(move || {
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.expect("test runtime should build");
|
||||
runtime.block_on(make_future());
|
||||
})
|
||||
.expect("admin oauth test thread should spawn");
|
||||
|
||||
if let Err(payload) = handle.join() {
|
||||
std::panic::resume_unwind(payload);
|
||||
}
|
||||
crate::tests::run_async_test_on_large_stack(
|
||||
test_name,
|
||||
ADMIN_OAUTH_TEST_STACK_BYTES,
|
||||
make_future,
|
||||
);
|
||||
}
|
||||
|
||||
fn decrypt_persisted_provider_api_key(key: &StoredProviderCatalogKey) -> String {
|
||||
|
||||
@@ -68,21 +68,11 @@ where
|
||||
F: FnOnce() -> Fut + Send + 'static,
|
||||
Fut: std::future::Future<Output = ()> + 'static,
|
||||
{
|
||||
let handle = std::thread::Builder::new()
|
||||
.name(test_name.to_string())
|
||||
.stack_size(PROVIDER_OPS_TEST_STACK_BYTES)
|
||||
.spawn(move || {
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.expect("test runtime should build");
|
||||
runtime.block_on(make_future());
|
||||
})
|
||||
.expect("provider ops test thread should spawn");
|
||||
|
||||
if let Err(payload) = handle.join() {
|
||||
std::panic::resume_unwind(payload);
|
||||
}
|
||||
crate::tests::run_async_test_on_large_stack(
|
||||
test_name,
|
||||
PROVIDER_OPS_TEST_STACK_BYTES,
|
||||
make_future,
|
||||
);
|
||||
}
|
||||
|
||||
async fn start_managed_redis_or_skip() -> Option<ManagedRedisServer> {
|
||||
|
||||
@@ -36,21 +36,11 @@ where
|
||||
F: FnOnce() -> Fut + Send + 'static,
|
||||
Fut: std::future::Future<Output = ()> + 'static,
|
||||
{
|
||||
let handle = std::thread::Builder::new()
|
||||
.name(test_name.to_string())
|
||||
.stack_size(PROVIDER_QUERY_TEST_STACK_BYTES)
|
||||
.spawn(move || {
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.expect("test runtime should build");
|
||||
runtime.block_on(make_future());
|
||||
})
|
||||
.expect("provider query test thread should spawn");
|
||||
|
||||
if let Err(payload) = handle.join() {
|
||||
std::panic::resume_unwind(payload);
|
||||
}
|
||||
crate::tests::run_async_test_on_large_stack(
|
||||
test_name,
|
||||
PROVIDER_QUERY_TEST_STACK_BYTES,
|
||||
make_future,
|
||||
);
|
||||
}
|
||||
|
||||
fn crc32(data: &[u8]) -> u32 {
|
||||
|
||||
@@ -239,11 +239,25 @@ async fn send_admin_security_request(
|
||||
method: reqwest::Method,
|
||||
path: &str,
|
||||
body: Option<serde_json::Value>,
|
||||
) -> (StatusCode, serde_json::Value, usize) {
|
||||
let path = path.to_string();
|
||||
crate::tests::run_async_test_on_large_stack_with_result(
|
||||
"admin-security-router-request",
|
||||
16 * 1024 * 1024,
|
||||
move || send_admin_security_request_on_large_stack(gateway, method, path, body),
|
||||
)
|
||||
}
|
||||
|
||||
async fn send_admin_security_request_on_large_stack(
|
||||
gateway: Router,
|
||||
method: reqwest::Method,
|
||||
path: String,
|
||||
body: Option<serde_json::Value>,
|
||||
) -> (StatusCode, serde_json::Value, usize) {
|
||||
let upstream_hits = Arc::new(Mutex::new(0usize));
|
||||
let upstream_hits_clone = Arc::clone(&upstream_hits);
|
||||
let upstream = Router::new().route(
|
||||
path,
|
||||
&path,
|
||||
any(move |_request: Request| {
|
||||
let upstream_hits_inner = Arc::clone(&upstream_hits_clone);
|
||||
async move {
|
||||
@@ -253,26 +267,52 @@ async fn send_admin_security_request(
|
||||
}),
|
||||
);
|
||||
|
||||
let (upstream_url, upstream_handle) = start_server(upstream).await;
|
||||
let (gateway_url, gateway_handle) = start_server(gateway).await;
|
||||
let (_upstream_url, upstream_handle) = start_server(upstream).await;
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
let mut request = client
|
||||
.request(method, format!("{gateway_url}{path}"))
|
||||
// 这些用例只验证本地安全路由和“不得转发”断言,不需要为 Gateway
|
||||
// 再启动一个 TCP listener;send_request 会补齐 ConnectInfo,仍经过完整 Router。
|
||||
let mut request_builder = Request::builder()
|
||||
.method(method.as_str())
|
||||
.uri(&path)
|
||||
.header(crate::constants::GATEWAY_HEADER, "rust-phase3b")
|
||||
.header(TRUSTED_ADMIN_USER_ID_HEADER, "admin-user-123")
|
||||
.header(TRUSTED_ADMIN_USER_ROLE_HEADER, "admin")
|
||||
.header(TRUSTED_ADMIN_SESSION_ID_HEADER, "session-123");
|
||||
if let Some(body) = body {
|
||||
request = request.json(&body);
|
||||
request_builder = request_builder.header(http::header::CONTENT_TYPE, "application/json");
|
||||
let request = request_builder
|
||||
.body(Body::from(body.to_string()))
|
||||
.expect("request should build");
|
||||
let response = send_request(gateway, request).await;
|
||||
let status = response.status();
|
||||
let payload = response
|
||||
.into_body()
|
||||
.collect()
|
||||
.await
|
||||
.expect("response body should collect")
|
||||
.to_bytes();
|
||||
let payload: serde_json::Value =
|
||||
serde_json::from_slice(&payload).expect("json body should parse");
|
||||
let upstream_count = *upstream_hits.lock().expect("mutex should lock");
|
||||
upstream_handle.abort();
|
||||
return (status, payload, upstream_count);
|
||||
}
|
||||
|
||||
let response = request.send().await.expect("request should succeed");
|
||||
let request = request_builder
|
||||
.body(Body::empty())
|
||||
.expect("request should build");
|
||||
let response = send_request(gateway, request).await;
|
||||
let status = response.status();
|
||||
let payload: serde_json::Value = response.json().await.expect("json body should parse");
|
||||
let payload = response
|
||||
.into_body()
|
||||
.collect()
|
||||
.await
|
||||
.expect("response body should collect")
|
||||
.to_bytes();
|
||||
let payload: serde_json::Value =
|
||||
serde_json::from_slice(&payload).expect("json body should parse");
|
||||
let upstream_count = *upstream_hits.lock().expect("mutex should lock");
|
||||
|
||||
gateway_handle.abort();
|
||||
upstream_handle.abort();
|
||||
|
||||
(status, payload, upstream_count)
|
||||
|
||||
@@ -341,21 +341,11 @@ where
|
||||
F: FnOnce() -> Fut + Send + 'static,
|
||||
Fut: std::future::Future<Output = ()> + 'static,
|
||||
{
|
||||
let handle = std::thread::Builder::new()
|
||||
.name(test_name.to_string())
|
||||
.stack_size(ADMIN_SYSTEM_IMPORT_TEST_STACK_BYTES)
|
||||
.spawn(move || {
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.expect("test runtime should build");
|
||||
runtime.block_on(make_future());
|
||||
})
|
||||
.expect("admin system import test thread should spawn");
|
||||
|
||||
if let Err(payload) = handle.join() {
|
||||
std::panic::resume_unwind(payload);
|
||||
}
|
||||
crate::tests::run_async_test_on_large_stack(
|
||||
test_name,
|
||||
ADMIN_SYSTEM_IMPORT_TEST_STACK_BYTES,
|
||||
make_future,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -37,21 +37,7 @@ where
|
||||
F: FnOnce() -> Fut + Send + 'static,
|
||||
Fut: std::future::Future<Output = ()> + 'static,
|
||||
{
|
||||
let handle = std::thread::Builder::new()
|
||||
.name(test_name.to_string())
|
||||
.stack_size(FILES_TEST_STACK_BYTES)
|
||||
.spawn(move || {
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.expect("test runtime should build");
|
||||
runtime.block_on(make_future());
|
||||
})
|
||||
.expect("files test thread should spawn");
|
||||
|
||||
if let Err(payload) = handle.join() {
|
||||
std::panic::resume_unwind(payload);
|
||||
}
|
||||
crate::tests::run_async_test_on_large_stack(test_name, FILES_TEST_STACK_BYTES, make_future);
|
||||
}
|
||||
|
||||
fn hash_api_key(value: &str) -> String {
|
||||
|
||||
@@ -39,21 +39,7 @@ fn run_frontdoor_async_test<F>(name: &'static str, future: F)
|
||||
where
|
||||
F: std::future::Future<Output = ()> + Send + 'static,
|
||||
{
|
||||
let handle = std::thread::Builder::new()
|
||||
.name(name.to_string())
|
||||
.stack_size(16 * 1024 * 1024)
|
||||
.spawn(move || {
|
||||
tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.expect("frontdoor test runtime should build")
|
||||
.block_on(future);
|
||||
})
|
||||
.expect("large-stack frontdoor test thread should spawn");
|
||||
|
||||
if let Err(payload) = handle.join() {
|
||||
std::panic::resume_unwind(payload);
|
||||
}
|
||||
crate::tests::run_async_test_on_large_stack(name, 16 * 1024 * 1024, || future);
|
||||
}
|
||||
|
||||
fn hash_api_key(value: &str) -> String {
|
||||
|
||||
@@ -10,7 +10,6 @@ pub(super) use http::StatusCode;
|
||||
pub(super) use serde_json::json;
|
||||
|
||||
mod ai_execute;
|
||||
mod architecture;
|
||||
mod async_task;
|
||||
mod audit;
|
||||
mod concurrency;
|
||||
@@ -46,6 +45,50 @@ pub(super) async fn start_server(app: Router) -> (String, tokio::task::JoinHandl
|
||||
(format!("http://{addr}"), handle)
|
||||
}
|
||||
|
||||
/// 在独立的大栈线程中运行需要深调用栈的异步测试。
|
||||
///
|
||||
/// 这些测试仍保留 16 MiB 栈空间;这里只统一线程和 runtime 的启动逻辑,
|
||||
/// 避免每个测试分区各自复制一份 helper,降低维护时误改测试执行语义的风险。
|
||||
pub(crate) fn run_async_test_on_large_stack<F, Fut>(
|
||||
test_name: &'static str,
|
||||
stack_size: usize,
|
||||
make_future: F,
|
||||
) where
|
||||
F: FnOnce() -> Fut + Send + 'static,
|
||||
Fut: std::future::Future<Output = ()> + 'static,
|
||||
{
|
||||
run_async_test_on_large_stack_with_result(test_name, stack_size, make_future);
|
||||
}
|
||||
|
||||
/// 与上面的 helper 相同,但允许深栈测试返回结果,供公共请求 helper 使用。
|
||||
pub(crate) fn run_async_test_on_large_stack_with_result<F, Fut, R>(
|
||||
test_name: &'static str,
|
||||
stack_size: usize,
|
||||
make_future: F,
|
||||
) -> R
|
||||
where
|
||||
F: FnOnce() -> Fut + Send + 'static,
|
||||
Fut: std::future::Future<Output = R> + 'static,
|
||||
R: Send + 'static,
|
||||
{
|
||||
let handle = std::thread::Builder::new()
|
||||
.name(test_name.to_string())
|
||||
.stack_size(stack_size)
|
||||
.spawn(move || {
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.expect("test runtime should build");
|
||||
runtime.block_on(make_future())
|
||||
})
|
||||
.expect("large-stack test thread should spawn");
|
||||
|
||||
match handle.join() {
|
||||
Ok(result) => result,
|
||||
Err(payload) => std::panic::resume_unwind(payload),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) const OPERATIONAL_ADMIN_DEVICE_ID: &str = "device-operational-admin";
|
||||
|
||||
pub(super) async fn start_authenticated_operational_server(
|
||||
|
||||
+4
-2
@@ -1194,7 +1194,7 @@ fn ai_serving_planner_separates_local_candidate_resolution_from_ranking() {
|
||||
|
||||
let candidate_resolution =
|
||||
read_workspace_file("apps/aether-gateway/src/ai_serving/planner/candidate_resolution.rs");
|
||||
let ranking_call = candidate_resolution
|
||||
candidate_resolution
|
||||
.find("rank_eligible_local_execution_candidates(")
|
||||
.expect("candidate_resolution.rs should call core-backed local candidate ranking");
|
||||
assert!(
|
||||
@@ -5045,7 +5045,9 @@ fn retired_api_format_occurrences_are_whitelisted() {
|
||||
.expect("file should be under workspace root")
|
||||
.to_string_lossy()
|
||||
.replace('\\', "/");
|
||||
if relative == "apps/aether-gateway/src/tests/architecture/ai_serving.rs" {
|
||||
if relative == "apps/aether-gateway/tests/architecture/ai_serving.rs"
|
||||
|| relative == "apps/aether-gateway/src/tests/architecture/ai_serving.rs"
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
+11
-9
@@ -1,7 +1,9 @@
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
pub(super) fn collect_rust_files(root: &Path, files: &mut Vec<PathBuf>) {
|
||||
// 架构守卫在独立 integration test 中是顶层模块;helper 统一 pub(crate),
|
||||
// 子模块经 `use super::*` / `use super::{...}` 访问(与原 lib 内布局一致)。
|
||||
pub(crate) 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();
|
||||
@@ -15,7 +17,7 @@ pub(super) fn collect_rust_files(root: &Path, files: &mut Vec<PathBuf>) {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn assert_no_sqlx_queries(root_relative_path: &str) {
|
||||
pub(crate) 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);
|
||||
@@ -80,7 +82,7 @@ fn sql_pool_scan_distinguishes_pool_types_from_repository_names() {
|
||||
));
|
||||
}
|
||||
|
||||
pub(super) fn assert_no_sensitive_log_patterns(root_relative_path: &str, patterns: &[&str]) {
|
||||
pub(crate) 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);
|
||||
@@ -109,7 +111,7 @@ pub(super) fn assert_no_sensitive_log_patterns(root_relative_path: &str, pattern
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn assert_no_module_dependency_patterns(root_relative_path: &str, patterns: &[&str]) {
|
||||
pub(crate) 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);
|
||||
@@ -138,14 +140,14 @@ pub(super) fn assert_no_module_dependency_patterns(root_relative_path: &str, pat
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn workspace_file_exists(root_relative_path: &str) -> bool {
|
||||
pub(crate) fn workspace_file_exists(root_relative_path: &str) -> bool {
|
||||
Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("../..")
|
||||
.join(root_relative_path)
|
||||
.exists()
|
||||
}
|
||||
|
||||
pub(super) fn workspace_files_with_extension(
|
||||
pub(crate) fn workspace_files_with_extension(
|
||||
root_relative_path: &str,
|
||||
extension: &str,
|
||||
) -> Vec<PathBuf> {
|
||||
@@ -162,7 +164,7 @@ pub(super) fn workspace_files_with_extension(
|
||||
files
|
||||
}
|
||||
|
||||
pub(super) fn collect_workspace_rust_files(root_relative_path: &str) -> Vec<PathBuf> {
|
||||
pub(crate) fn collect_workspace_rust_files(root_relative_path: &str) -> Vec<PathBuf> {
|
||||
let root = Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("../..")
|
||||
.join(root_relative_path);
|
||||
@@ -172,7 +174,7 @@ pub(super) fn collect_workspace_rust_files(root_relative_path: &str) -> Vec<Path
|
||||
files
|
||||
}
|
||||
|
||||
pub(super) fn read_workspace_file(path: &str) -> String {
|
||||
pub(crate) fn read_workspace_file(path: &str) -> String {
|
||||
let workspace_root = Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("../..")
|
||||
.canonicalize()
|
||||
@@ -180,7 +182,7 @@ pub(super) fn read_workspace_file(path: &str) -> String {
|
||||
fs::read_to_string(workspace_root.join(path)).expect("source file should be readable")
|
||||
}
|
||||
|
||||
pub(super) fn read_workspace_module_tree(path: &str) -> String {
|
||||
pub(crate) fn read_workspace_module_tree(path: &str) -> String {
|
||||
let workspace_root = Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("../..")
|
||||
.canonicalize()
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::Path;
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
//! 架构守卫独立测试目标。
|
||||
//!
|
||||
//! 从 lib 的 `cfg(test)` 巨型编译单元迁出:只做源码/manifest 字符串断言,
|
||||
//! 不启动 AppState、不依赖 gateway 私有类型,用于压低 lib test 编译面与 rustc 峰值。
|
||||
mod architecture;
|
||||
Reference in New Issue
Block a user