chore: initial sanitized public snapshot

This commit is contained in:
Aria2 Rust Pro Contributors
2026-07-18 14:51:59 +08:00
commit 14dcf8c9bf
321 changed files with 76893 additions and 0 deletions
@@ -0,0 +1,397 @@
use std::{
collections::BTreeMap,
sync::{Arc, Mutex},
};
use aria2_rust_pro_core::{
DownloadEngine, DownloadId, EventListener, RuntimeEvent, RuntimeEventKind,
};
use super::notification::{
MAX_PENDING_WEBSOCKET_BROADCAST_FRAMES, MAX_PENDING_WEBSOCKET_FRAMES_PER_SESSION,
};
use super::*;
use crate::{InProcessRpcDispatcher, JsonRpcRequest, RpcValue};
#[test]
/// Verifies that notification kinds keep the upstream aria2 WebSocket method names.
fn notification_kind_uses_upstream_websocket_method_names() {
assert_eq!(
RpcNotificationKind::DownloadStarted.method_name(),
"aria2.onDownloadStart"
);
assert_eq!(
RpcNotificationKind::DownloadBtDownloadComplete.method_name(),
"aria2.onBtDownloadComplete"
);
}
#[test]
/// Verifies that notification events render the upstream `JSON-RPC` notification shape.
fn websocket_event_renders_upstream_jsonrpc_notification_shape() {
let event = RpcNotificationEvent {
kind: RpcNotificationKind::DownloadComplete,
method: String::new(),
gid: Some("a1b2c3".to_owned()),
payload: None,
meta: crate::model::RpcMeta::default(),
};
assert_eq!(
event.to_websocket_json(),
"{\"jsonrpc\":\"2.0\",\"method\":\"aria2.onDownloadComplete\",\"params\":[{\"gid\":\"a1b2c3\"}]}"
);
assert_eq!(
event.to_websocket_frame(),
RpcWebSocketFrame::Text(
"{\"jsonrpc\":\"2.0\",\"method\":\"aria2.onDownloadComplete\",\"params\":[{\"gid\":\"a1b2c3\"}]}".to_owned()
)
);
}
#[test]
/// Verifies that object payloads merge with the gid field in notification output.
fn websocket_event_merges_gid_with_object_payload() {
let event = RpcNotificationEvent {
kind: RpcNotificationKind::DownloadError,
method: String::new(),
gid: Some("deadbeef".to_owned()),
payload: Some(RpcValue::Object(BTreeMap::from([(
"status".to_owned(),
RpcValue::String("error".to_owned()),
)]))),
meta: crate::model::RpcMeta::default(),
};
assert_eq!(
event.to_websocket_json(),
"{\"jsonrpc\":\"2.0\",\"method\":\"aria2.onDownloadError\",\"params\":[{\"gid\":\"deadbeef\",\"status\":\"error\"}]}"
);
}
#[test]
/// Verifies that one notification frame is emitted for each matching subscription.
fn registry_emits_one_frame_per_matching_subscription() {
let mut registry = WebSocketNotificationRegistry::default();
registry.subscribe(WebSocketSubscription {
id: "sub-a".to_owned(),
kind: RpcNotificationKind::DownloadStarted,
topic: "aria2.onDownloadStart".to_owned(),
});
registry.subscribe(WebSocketSubscription {
id: "sub-b".to_owned(),
kind: RpcNotificationKind::DownloadStarted,
topic: "aria2.onDownloadStart".to_owned(),
});
registry.subscribe(WebSocketSubscription {
id: "sub-c".to_owned(),
kind: RpcNotificationKind::DownloadComplete,
topic: "aria2.onDownloadComplete".to_owned(),
});
let event = RpcNotificationEvent {
kind: RpcNotificationKind::DownloadStarted,
method: String::new(),
gid: Some("feedface".to_owned()),
payload: None,
meta: crate::model::RpcMeta::default(),
};
let frames = registry.frames_for_event(&event);
assert_eq!(frames.len(), 2);
assert!(frames.iter().any(|(id, _)| id == "sub-a"));
assert!(frames.iter().any(|(id, _)| id == "sub-b"));
assert!(
frames
.iter()
.all(|(_, frame)| matches!(frame, RpcWebSocketFrame::Text(_)))
);
}
#[test]
/// Verifies that broadcast queueing fans notification frames out to every connected session.
fn session_registry_broadcasts_notification_frames_to_all_sessions() {
let mut registry = WebSocketSessionRegistry::default();
registry.connect("sess-a");
registry.connect("sess-b");
let event = RpcNotificationEvent {
kind: RpcNotificationKind::DownloadComplete,
method: String::new(),
gid: Some("abc123".to_owned()),
payload: None,
meta: crate::model::RpcMeta::default(),
};
registry.queue_event_for_all(&event);
assert_eq!(registry.pending_count("sess-a"), Some(1));
assert_eq!(registry.pending_count("sess-b"), Some(1));
assert_eq!(
registry.pop_frame("sess-a"),
Some(RpcWebSocketFrame::Text(
"{\"jsonrpc\":\"2.0\",\"method\":\"aria2.onDownloadComplete\",\"params\":[{\"gid\":\"abc123\"}]}".to_owned()
))
);
assert_eq!(
registry.pop_frame("sess-b"),
Some(RpcWebSocketFrame::Text(
"{\"jsonrpc\":\"2.0\",\"method\":\"aria2.onDownloadComplete\",\"params\":[{\"gid\":\"abc123\"}]}".to_owned()
))
);
}
#[test]
/// Verifies that bridged broadcast ingress stays bounded and drops the oldest frame first.
fn session_registry_enforces_bounded_broadcast_ingress_backpressure() {
let mut registry = WebSocketSessionRegistry::default();
registry.connect("sess-a");
for index in 0..(MAX_PENDING_WEBSOCKET_BROADCAST_FRAMES + 8) {
registry.queue_broadcast_frame(RpcWebSocketFrame::Ping(vec![
u8::try_from(index % 256).expect("byte should fit"),
]));
}
assert_eq!(
registry.pending_broadcast_count(),
MAX_PENDING_WEBSOCKET_BROADCAST_FRAMES
);
assert_eq!(
registry.pop_frame("sess-a"),
Some(RpcWebSocketFrame::Ping(vec![8])),
"oldest bridged broadcast frames should be dropped first once the ingress limit is reached"
);
}
#[test]
/// Verifies that bridged broadcast ingress fans out only when a session drain occurs.
fn session_registry_drains_broadcast_ingress_into_connected_sessions() {
let mut registry = WebSocketSessionRegistry::default();
registry.connect("sess-a");
registry.connect("sess-b");
registry.queue_broadcast_frame(RpcWebSocketFrame::Ping(vec![4, 2]));
assert_eq!(registry.pending_count("sess-a"), Some(0));
assert_eq!(registry.pending_count("sess-b"), Some(0));
assert_eq!(registry.pending_broadcast_count(), 1);
let sess_a_frames = registry.drain_session_frames("sess-a");
assert_eq!(registry.pending_broadcast_count(), 0);
assert_eq!(sess_a_frames, vec![RpcWebSocketFrame::Ping(vec![4, 2])]);
assert_eq!(
registry.pop_frame("sess-b"),
Some(RpcWebSocketFrame::Ping(vec![4, 2]))
);
}
#[test]
/// Verifies targeted queueing, missing-session rejection, and disconnect behavior.
fn session_registry_supports_targeted_queue_and_disconnect() {
let mut registry = WebSocketSessionRegistry::default();
registry.connect("sess-a");
registry.connect("sess-b");
assert!(registry.queue_frame_for_session("sess-a", RpcWebSocketFrame::Ping(vec![1, 2, 3]),));
assert!(!registry.queue_frame_for_session("sess-missing", RpcWebSocketFrame::Ping(vec![9]),));
assert_eq!(registry.pending_count("sess-a"), Some(1));
assert_eq!(registry.pending_count("sess-b"), Some(0));
assert_eq!(
registry.pop_frame("sess-a"),
Some(RpcWebSocketFrame::Ping(vec![1, 2, 3]))
);
registry.disconnect("sess-b");
assert!(!registry.contains("sess-b"));
assert_eq!(registry.len(), 1);
}
#[test]
/// Verifies that the per-session queue is bounded and drops the oldest frame under pressure.
fn session_registry_enforces_bounded_pending_frame_backpressure() {
let mut registry = WebSocketSessionRegistry::default();
registry.connect("sess-a");
for index in 0..(MAX_PENDING_WEBSOCKET_FRAMES_PER_SESSION + 8) {
assert!(registry.queue_frame_for_session(
"sess-a",
RpcWebSocketFrame::Ping(vec![u8::try_from(index % 256).expect("byte should fit")]),
));
}
assert_eq!(
registry.pending_count("sess-a"),
Some(MAX_PENDING_WEBSOCKET_FRAMES_PER_SESSION)
);
assert_eq!(
registry.pop_frame("sess-a"),
Some(RpcWebSocketFrame::Ping(vec![8])),
"oldest queued frames should be dropped first once the per-session limit is reached"
);
}
#[test]
/// Verifies that the runtime-event bridge queues real completion events into session frames.
fn runtime_event_bridge_queues_real_download_events_into_sessions() {
let sessions = Arc::new(Mutex::new(WebSocketSessionRegistry::default()));
sessions
.lock()
.expect("sessions lock should succeed")
.connect("sess-a");
let mut bridge = RuntimeEventWebSocketBridge::new(Arc::clone(&sessions));
bridge.on_event(
&RuntimeEvent::new(RuntimeEventKind::DownloadCompleted).with_gid(DownloadId::new(0x2a)),
);
assert_eq!(
sessions
.lock()
.expect("sessions lock should succeed")
.pending_broadcast_count(),
1
);
let frame = sessions
.lock()
.expect("sessions lock should succeed")
.pop_frame("sess-a")
.expect("download completion should queue a notification frame");
match frame {
RpcWebSocketFrame::Text(text) => {
assert!(text.contains("aria2.onDownloadComplete"));
assert!(text.contains(r#""gid":"000000000000002a""#));
}
other => panic!("unexpected bridged notification frame: {other:?}"),
}
}
#[test]
/// Verifies that `addUri` registration alone does not emit a start notification.
fn runtime_event_bridge_does_not_treat_add_uri_as_download_start() {
let sessions = Arc::new(Mutex::new(WebSocketSessionRegistry::default()));
sessions
.lock()
.expect("sessions lock should succeed")
.connect("sess-a");
let bridge = RuntimeEventWebSocketBridge::new(Arc::clone(&sessions));
let mut dispatcher = InProcessRpcDispatcher::new();
dispatcher.register_runtime_listener(bridge);
let response = dispatcher.dispatch_json(JsonRpcRequest {
jsonrpc: Some("2.0".to_owned()),
id: Some(crate::jsonrpc::JsonRpcId::Number(1)),
method: "aria2.addUri".to_owned(),
params: vec![RpcValue::String("https://example.org/file.iso".to_owned())],
meta: crate::model::RpcMeta::default(),
});
assert!(response.error.is_none());
assert!(
sessions
.lock()
.expect("sessions lock should succeed")
.pop_frame("sess-a")
.is_none(),
"addUri should not emit aria2.onDownloadStart before the download actually starts"
);
}
#[test]
/// Verifies that start notifications only appear after the scheduler activates the download.
fn runtime_event_bridge_emits_start_only_when_scheduler_activates_download() {
let sessions = Arc::new(Mutex::new(WebSocketSessionRegistry::default()));
sessions
.lock()
.expect("sessions lock should succeed")
.connect("sess-a");
let mut engine = DownloadEngine::new();
engine.register_listener(RuntimeEventWebSocketBridge::new(Arc::clone(&sessions)));
let gid = engine.add_uri("https://example.org/file.iso").gid();
assert!(
sessions
.lock()
.expect("sessions lock should succeed")
.pop_frame("sess-a")
.is_none(),
"registration alone should not emit a start notification"
);
let _ = engine.schedule_once();
let frame = sessions
.lock()
.expect("sessions lock should succeed")
.pop_frame("sess-a")
.expect("scheduler activation should emit start notification");
match frame {
RpcWebSocketFrame::Text(text) => {
assert!(text.contains("aria2.onDownloadStart"));
assert!(text.contains(&format!(r#""gid":"{gid}""#)));
}
other => panic!("unexpected frame: {other:?}"),
}
}
#[test]
/// Verifies that resumed downloads emit start only after they become active again.
fn runtime_event_bridge_does_not_emit_start_until_resumed_download_is_active_again() {
let sessions = Arc::new(Mutex::new(WebSocketSessionRegistry::default()));
sessions
.lock()
.expect("sessions lock should succeed")
.connect("sess-a");
let mut engine = DownloadEngine::new();
engine.register_listener(RuntimeEventWebSocketBridge::new(Arc::clone(&sessions)));
let gid = engine.add_uri("https://example.org/file.iso").gid();
let _ = engine.schedule_once();
let _ = sessions
.lock()
.expect("sessions lock should succeed")
.pop_frame("sess-a");
engine.pause(gid).expect("pause should succeed");
let pause = sessions
.lock()
.expect("sessions lock should succeed")
.pop_frame("sess-a")
.expect("pause should emit pause notification");
match pause {
RpcWebSocketFrame::Text(text) => {
assert!(text.contains("aria2.onDownloadPause"));
}
other => panic!("unexpected pause frame: {other:?}"),
}
engine
.resume(gid)
.expect("resume should move download back to waiting");
assert!(
sessions
.lock()
.expect("sessions lock should succeed")
.pop_frame("sess-a")
.is_none(),
"resume should not emit start notification before reactivation"
);
let _ = engine.schedule_once();
let frame = sessions
.lock()
.expect("sessions lock should succeed")
.pop_frame("sess-a")
.expect("reactivation should emit start notification");
match frame {
RpcWebSocketFrame::Text(text) => {
assert!(text.contains("aria2.onDownloadStart"));
assert!(text.contains(&format!(r#""gid":"{gid}""#)));
}
other => panic!("unexpected reactivation frame: {other:?}"),
}
}