mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-04 08:27:46 +08:00
test: slim gateway startup helpers and configure nextest
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
# GitHub-hosted ubuntu runner 当前按 4 vCPU 配置;固定线程数可避免 runner
|
||||||
|
# 规格变化时测试并发和内存峰值随之漂移。
|
||||||
|
[profile.default]
|
||||||
|
test-threads = 4
|
||||||
|
|
||||||
|
# 60 秒后标记慢测试,连续两轮仍未结束则终止,避免单个卡死用例拖满整个 job。
|
||||||
|
# 真实连接/数据库测试仍有足够时间完成;超时结果保持失败,不隐藏回归。
|
||||||
|
slow-timeout = { period = "60s", terminate-after = 2, grace-period = "10s" }
|
||||||
@@ -44,21 +44,11 @@ where
|
|||||||
F: FnOnce() -> Fut + Send + 'static,
|
F: FnOnce() -> Fut + Send + 'static,
|
||||||
Fut: std::future::Future<Output = ()> + 'static,
|
Fut: std::future::Future<Output = ()> + 'static,
|
||||||
{
|
{
|
||||||
let handle = std::thread::Builder::new()
|
crate::tests::run_async_test_on_large_stack(
|
||||||
.name(test_name.to_string())
|
test_name,
|
||||||
.stack_size(PROVIDER_KEYS_TEST_STACK_BYTES)
|
PROVIDER_KEYS_TEST_STACK_BYTES,
|
||||||
.spawn(move || {
|
make_future,
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SummaryNullingProviderCatalogReadRepository {
|
struct SummaryNullingProviderCatalogReadRepository {
|
||||||
|
|||||||
@@ -59,21 +59,11 @@ where
|
|||||||
F: FnOnce() -> Fut + Send + 'static,
|
F: FnOnce() -> Fut + Send + 'static,
|
||||||
Fut: std::future::Future<Output = ()> + 'static,
|
Fut: std::future::Future<Output = ()> + 'static,
|
||||||
{
|
{
|
||||||
let handle = std::thread::Builder::new()
|
crate::tests::run_async_test_on_large_stack(
|
||||||
.name(test_name.to_string())
|
test_name,
|
||||||
.stack_size(PROVIDER_QUOTA_TEST_STACK_BYTES)
|
PROVIDER_QUOTA_TEST_STACK_BYTES,
|
||||||
.spawn(move || {
|
make_future,
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|||||||
@@ -56,21 +56,11 @@ where
|
|||||||
F: FnOnce() -> Fut + Send + 'static,
|
F: FnOnce() -> Fut + Send + 'static,
|
||||||
Fut: std::future::Future<Output = ()> + 'static,
|
Fut: std::future::Future<Output = ()> + 'static,
|
||||||
{
|
{
|
||||||
let handle = std::thread::Builder::new()
|
crate::tests::run_async_test_on_large_stack(
|
||||||
.name(test_name.to_string())
|
test_name,
|
||||||
.stack_size(ADMIN_OAUTH_TEST_STACK_BYTES)
|
ADMIN_OAUTH_TEST_STACK_BYTES,
|
||||||
.spawn(move || {
|
make_future,
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decrypt_persisted_provider_api_key(key: &StoredProviderCatalogKey) -> String {
|
fn decrypt_persisted_provider_api_key(key: &StoredProviderCatalogKey) -> String {
|
||||||
|
|||||||
@@ -68,21 +68,11 @@ where
|
|||||||
F: FnOnce() -> Fut + Send + 'static,
|
F: FnOnce() -> Fut + Send + 'static,
|
||||||
Fut: std::future::Future<Output = ()> + 'static,
|
Fut: std::future::Future<Output = ()> + 'static,
|
||||||
{
|
{
|
||||||
let handle = std::thread::Builder::new()
|
crate::tests::run_async_test_on_large_stack(
|
||||||
.name(test_name.to_string())
|
test_name,
|
||||||
.stack_size(PROVIDER_OPS_TEST_STACK_BYTES)
|
PROVIDER_OPS_TEST_STACK_BYTES,
|
||||||
.spawn(move || {
|
make_future,
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn start_managed_redis_or_skip() -> Option<ManagedRedisServer> {
|
async fn start_managed_redis_or_skip() -> Option<ManagedRedisServer> {
|
||||||
|
|||||||
@@ -36,21 +36,11 @@ where
|
|||||||
F: FnOnce() -> Fut + Send + 'static,
|
F: FnOnce() -> Fut + Send + 'static,
|
||||||
Fut: std::future::Future<Output = ()> + 'static,
|
Fut: std::future::Future<Output = ()> + 'static,
|
||||||
{
|
{
|
||||||
let handle = std::thread::Builder::new()
|
crate::tests::run_async_test_on_large_stack(
|
||||||
.name(test_name.to_string())
|
test_name,
|
||||||
.stack_size(PROVIDER_QUERY_TEST_STACK_BYTES)
|
PROVIDER_QUERY_TEST_STACK_BYTES,
|
||||||
.spawn(move || {
|
make_future,
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn crc32(data: &[u8]) -> u32 {
|
fn crc32(data: &[u8]) -> u32 {
|
||||||
|
|||||||
@@ -239,11 +239,25 @@ async fn send_admin_security_request(
|
|||||||
method: reqwest::Method,
|
method: reqwest::Method,
|
||||||
path: &str,
|
path: &str,
|
||||||
body: Option<serde_json::Value>,
|
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) {
|
) -> (StatusCode, serde_json::Value, usize) {
|
||||||
let upstream_hits = Arc::new(Mutex::new(0usize));
|
let upstream_hits = Arc::new(Mutex::new(0usize));
|
||||||
let upstream_hits_clone = Arc::clone(&upstream_hits);
|
let upstream_hits_clone = Arc::clone(&upstream_hits);
|
||||||
let upstream = Router::new().route(
|
let upstream = Router::new().route(
|
||||||
path,
|
&path,
|
||||||
any(move |_request: Request| {
|
any(move |_request: Request| {
|
||||||
let upstream_hits_inner = Arc::clone(&upstream_hits_clone);
|
let upstream_hits_inner = Arc::clone(&upstream_hits_clone);
|
||||||
async move {
|
async move {
|
||||||
@@ -253,26 +267,52 @@ async fn send_admin_security_request(
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (upstream_url, upstream_handle) = start_server(upstream).await;
|
let (_upstream_url, upstream_handle) = start_server(upstream).await;
|
||||||
let (gateway_url, gateway_handle) = start_server(gateway).await;
|
|
||||||
|
|
||||||
let client = reqwest::Client::new();
|
// 这些用例只验证本地安全路由和“不得转发”断言,不需要为 Gateway
|
||||||
let mut request = client
|
// 再启动一个 TCP listener;send_request 会补齐 ConnectInfo,仍经过完整 Router。
|
||||||
.request(method, format!("{gateway_url}{path}"))
|
let mut request_builder = Request::builder()
|
||||||
|
.method(method.as_str())
|
||||||
|
.uri(&path)
|
||||||
.header(crate::constants::GATEWAY_HEADER, "rust-phase3b")
|
.header(crate::constants::GATEWAY_HEADER, "rust-phase3b")
|
||||||
.header(TRUSTED_ADMIN_USER_ID_HEADER, "admin-user-123")
|
.header(TRUSTED_ADMIN_USER_ID_HEADER, "admin-user-123")
|
||||||
.header(TRUSTED_ADMIN_USER_ROLE_HEADER, "admin")
|
.header(TRUSTED_ADMIN_USER_ROLE_HEADER, "admin")
|
||||||
.header(TRUSTED_ADMIN_SESSION_ID_HEADER, "session-123");
|
.header(TRUSTED_ADMIN_SESSION_ID_HEADER, "session-123");
|
||||||
if let Some(body) = body {
|
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 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");
|
let upstream_count = *upstream_hits.lock().expect("mutex should lock");
|
||||||
|
|
||||||
gateway_handle.abort();
|
|
||||||
upstream_handle.abort();
|
upstream_handle.abort();
|
||||||
|
|
||||||
(status, payload, upstream_count)
|
(status, payload, upstream_count)
|
||||||
|
|||||||
@@ -341,21 +341,11 @@ where
|
|||||||
F: FnOnce() -> Fut + Send + 'static,
|
F: FnOnce() -> Fut + Send + 'static,
|
||||||
Fut: std::future::Future<Output = ()> + 'static,
|
Fut: std::future::Future<Output = ()> + 'static,
|
||||||
{
|
{
|
||||||
let handle = std::thread::Builder::new()
|
crate::tests::run_async_test_on_large_stack(
|
||||||
.name(test_name.to_string())
|
test_name,
|
||||||
.stack_size(ADMIN_SYSTEM_IMPORT_TEST_STACK_BYTES)
|
ADMIN_SYSTEM_IMPORT_TEST_STACK_BYTES,
|
||||||
.spawn(move || {
|
make_future,
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -37,21 +37,7 @@ where
|
|||||||
F: FnOnce() -> Fut + Send + 'static,
|
F: FnOnce() -> Fut + Send + 'static,
|
||||||
Fut: std::future::Future<Output = ()> + 'static,
|
Fut: std::future::Future<Output = ()> + 'static,
|
||||||
{
|
{
|
||||||
let handle = std::thread::Builder::new()
|
crate::tests::run_async_test_on_large_stack(test_name, FILES_TEST_STACK_BYTES, make_future);
|
||||||
.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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hash_api_key(value: &str) -> String {
|
fn hash_api_key(value: &str) -> String {
|
||||||
|
|||||||
@@ -39,21 +39,7 @@ fn run_frontdoor_async_test<F>(name: &'static str, future: F)
|
|||||||
where
|
where
|
||||||
F: std::future::Future<Output = ()> + Send + 'static,
|
F: std::future::Future<Output = ()> + Send + 'static,
|
||||||
{
|
{
|
||||||
let handle = std::thread::Builder::new()
|
crate::tests::run_async_test_on_large_stack(name, 16 * 1024 * 1024, || future);
|
||||||
.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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hash_api_key(value: &str) -> String {
|
fn hash_api_key(value: &str) -> String {
|
||||||
|
|||||||
@@ -45,6 +45,50 @@ pub(super) async fn start_server(app: Router) -> (String, tokio::task::JoinHandl
|
|||||||
(format!("http://{addr}"), handle)
|
(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) const OPERATIONAL_ADMIN_DEVICE_ID: &str = "device-operational-admin";
|
||||||
|
|
||||||
pub(super) async fn start_authenticated_operational_server(
|
pub(super) async fn start_authenticated_operational_server(
|
||||||
|
|||||||
Reference in New Issue
Block a user