fix(ci): use Windows-compatible Torrent RPC integration test

This commit is contained in:
NimBold
2026-08-01 20:09:57 +03:30
parent 92092e9844
commit bb64c4cd52
5 changed files with 106 additions and 12 deletions
-4
View File
@@ -71,10 +71,6 @@ jobs:
run: | run: |
cargo test --tests --target ${{ matrix.target }} cargo test --tests --target ${{ matrix.target }}
cargo test --lib --no-run --target ${{ matrix.target }} cargo test --lib --no-run --target ${{ matrix.target }}
- name: Test Torrent RPC harness on Windows
if: runner.os == 'Windows'
working-directory: src-tauri
run: cargo test torrent_probe::tests::http_rpc_harness --target ${{ matrix.target }} -- --nocapture
- name: Provision locked engines - name: Provision locked engines
if: runner.os != 'macOS' if: runner.os != 'macOS'
run: node scripts/provision-engines.js --target ${{ matrix.target }} run: node scripts/provision-engines.js --target ${{ matrix.target }}
+1 -1
View File
@@ -35,7 +35,7 @@
"check:updates": "node scripts/check-updates.js", "check:updates": "node scripts/check-updates.js",
"smoke:torrent": "node scripts/smoke-torrent.js", "smoke:torrent": "node scripts/smoke-torrent.js",
"smoke:torrent:failure-paths": "node scripts/smoke-torrent.js --failure-paths", "smoke:torrent:failure-paths": "node scripts/smoke-torrent.js --failure-paths",
"test:torrent:rpc": "cd src-tauri && cargo test torrent_probe::tests::http_rpc_harness -- --nocapture", "test:torrent:rpc": "cd src-tauri && cargo test --test torrent_rpc -- --nocapture",
"verify:macos-signing": "node scripts/verify-macos-signing.js", "verify:macos-signing": "node scripts/verify-macos-signing.js",
"preview": "vite preview", "preview": "vite preview",
"tauri": "tauri", "tauri": "tauri",
+2 -1
View File
@@ -3235,7 +3235,8 @@ fn dispatch_deep_links(app_handle: tauri::AppHandle, deep_links: Vec<url::Url>)
}); });
} }
pub(crate) async fn rpc_call( #[doc(hidden)]
pub async fn rpc_call(
port: u16, port: u16,
secret: &str, secret: &str,
method: &str, method: &str,
+7 -6
View File
@@ -36,9 +36,9 @@ npm run smoke:torrent
Use `node scripts/smoke-torrent.js --binary /path/to/aria2c` when validating a Use `node scripts/smoke-torrent.js --binary /path/to/aria2c` when validating a
packaged or target-specific Aria2 binary. packaged or target-specific Aria2 binary.
Run the HTTP-boundary Torrent probe harness with a controllable local JSON-RPC Run the HTTP-boundary Torrent RPC integration test. It drives the production
server. It drives the production Aria2 RPC client through scripted status, Aria2 RPC client through a local JSON-RPC server and verifies successful
outage, race, cancellation, and daemon-termination cases: requests plus HTTP gateway errors:
```sh ```sh
npm run test:torrent:rpc npm run test:torrent:rpc
@@ -51,6 +51,7 @@ npm run smoke:torrent:failure-paths
``` ```
Native CI runs this failure-path smoke after staging the target-specific Native CI runs this failure-path smoke after staging the target-specific
bundled engines on macOS, Windows, and Linux. Windows also runs the filtered bundled engines on macOS, Windows, and Linux. Windows runs this RPC
HTTP-boundary RPC harness explicitly because its general Rust job compiles the integration test through its target-qualified `cargo test --tests` step;
library tests without executing them. the general Rust job compiles the library tests without executing the
known-broken Tauri library harness.
+96
View File
@@ -0,0 +1,96 @@
use axum::{extract::Json, http::StatusCode, response::IntoResponse, routing::post, Router};
use firelink_lib::rpc_call;
use serde_json::{json, Value};
use std::net::SocketAddr;
use tokio::sync::oneshot;
async fn start_server(
app: Router,
) -> (SocketAddr, oneshot::Sender<()>, tokio::task::JoinHandle<()>) {
let listener = tokio::net::TcpListener::bind(("127.0.0.1", 0))
.await
.expect("RPC test server should bind");
let address = listener
.local_addr()
.expect("RPC test server should have an address");
let (shutdown, shutdown_signal) = oneshot::channel();
let task = tokio::spawn(async move {
axum::serve(listener, app)
.with_graceful_shutdown(async {
let _ = shutdown_signal.await;
})
.await
.expect("RPC test server should stop cleanly");
});
(address, shutdown, task)
}
struct TestResponse(StatusCode, Value);
impl IntoResponse for TestResponse {
fn into_response(self) -> axum::response::Response {
(self.0, Json(self.1)).into_response()
}
}
async fn successful_rpc(Json(request): Json<Value>) -> TestResponse {
assert_eq!(request.get("jsonrpc"), Some(&json!("2.0")));
assert_eq!(request.get("id"), Some(&json!("1")));
assert_eq!(request.get("method"), Some(&json!("aria2.getVersion")));
assert_eq!(
request.get("params"),
Some(&json!(["token:test-secret", {"include": "version"}]))
);
TestResponse(
StatusCode::OK,
json!({"jsonrpc": "2.0", "id": "1", "result": {"version": "test"}}),
)
}
async fn gateway_error(Json(_request): Json<Value>) -> TestResponse {
TestResponse(
StatusCode::BAD_GATEWAY,
json!({"jsonrpc": "2.0", "id": "1", "error": {"code": 1, "message": "backend unavailable"}}),
)
}
async fn stop_server(shutdown: oneshot::Sender<()>, task: tokio::task::JoinHandle<()>) {
let _ = shutdown.send(());
task.await.expect("RPC test server task should join");
}
#[tokio::test]
async fn production_rpc_client_sends_authenticated_json_rpc() {
let app = Router::new().route("/jsonrpc", post(successful_rpc));
let (address, shutdown, task) = start_server(app).await;
let result = rpc_call(
address.port(),
"test-secret",
"aria2.getVersion",
json!([{"include": "version"}]),
)
.await
.expect("successful RPC response should decode");
assert_eq!(result, json!({"version": "test"}));
stop_server(shutdown, task).await;
}
#[tokio::test]
async fn production_rpc_client_preserves_http_gateway_context() {
let app = Router::new().route("/jsonrpc", post(gateway_error));
let (address, shutdown, task) = start_server(app).await;
let error = rpc_call(address.port(), "test-secret", "aria2.getVersion", json!([]))
.await
.expect_err("gateway response should fail");
assert!(
error.contains("HTTP 502 Bad Gateway"),
"unexpected error: {error}"
);
assert!(
error.contains("backend unavailable"),
"unexpected error: {error}"
);
stop_server(shutdown, task).await;
}