Merge branch 'develop'
This commit is contained in:
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
use common::{CLI_ENV_VARS, Soulfind, free_port, login, settle};
|
use common::{CLI_ENV_VARS, Soulfind, free_port, login, settle, silent_peer};
|
||||||
use soulseek_rs::Client;
|
use soulseek_rs::Client;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::net::{TcpStream, ToSocketAddrs};
|
use std::net::{TcpStream, ToSocketAddrs};
|
||||||
@@ -1560,12 +1560,14 @@ fn transfer_cancel_stops_a_download_another_client_is_waiting_on() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let server = server_or_skip!();
|
let server = server_or_skip!();
|
||||||
|
let _peer =
|
||||||
|
silent_peer(&server.host, server.port, "cli_e2e_cancel_silent_peer");
|
||||||
let mut all = server.args("cli_e2e_cancel_waiter");
|
let mut all = server.args("cli_e2e_cancel_waiter");
|
||||||
all.extend(
|
all.extend(
|
||||||
[
|
[
|
||||||
"download",
|
"download",
|
||||||
"e2e_ghost_peer",
|
"cli_e2e_cancel_silent_peer",
|
||||||
"@@ghost\\never.mp3",
|
"@@silent\\never.mp3",
|
||||||
"--size",
|
"--size",
|
||||||
"10",
|
"10",
|
||||||
"--timeout",
|
"--timeout",
|
||||||
@@ -1587,7 +1589,12 @@ fn transfer_cancel_stops_a_download_another_client_is_waiting_on() {
|
|||||||
let output = cli(
|
let output = cli(
|
||||||
&server,
|
&server,
|
||||||
"cli_e2e_cancel_waiter",
|
"cli_e2e_cancel_waiter",
|
||||||
&["transfer", "cancel", "e2e_ghost_peer", "@@ghost\\never.mp3"],
|
&[
|
||||||
|
"transfer",
|
||||||
|
"cancel",
|
||||||
|
"cli_e2e_cancel_silent_peer",
|
||||||
|
"@@silent\\never.mp3",
|
||||||
|
],
|
||||||
);
|
);
|
||||||
if code(&output) == EXIT_OK {
|
if code(&output) == EXIT_OK {
|
||||||
cancelled = Some(output);
|
cancelled = Some(output);
|
||||||
|
|||||||
@@ -3,8 +3,10 @@
|
|||||||
|
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use soulseek_rs::message::server::MessageFactory;
|
||||||
use soulseek_rs::{Client, ClientSettings, PeerAddress};
|
use soulseek_rs::{Client, ClientSettings, PeerAddress};
|
||||||
use std::net::TcpStream;
|
use std::io::{Read, Write};
|
||||||
|
use std::net::{TcpListener, TcpStream};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::{Child, Command, Stdio};
|
use std::process::{Child, Command, Stdio};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
@@ -145,6 +147,57 @@ pub fn login(host: &str, port: u16, user: &str, shares: Vec<String>) -> Client {
|
|||||||
client
|
client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A user who is online and reachable but never answers, held for as long as
|
||||||
|
/// the value lives.
|
||||||
|
///
|
||||||
|
/// Their listener accepts a connection and then says nothing, so a download
|
||||||
|
/// from them waits in the queue the way one waits behind a busy peer, with no
|
||||||
|
/// timer to race. A user who does not exist cannot stand in for this: the
|
||||||
|
/// server refuses to broker a connection to them, and their download fails
|
||||||
|
/// before a test gets to act on it.
|
||||||
|
pub struct SilentPeer {
|
||||||
|
_session: TcpStream,
|
||||||
|
_listener: TcpListener,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn silent_peer(host: &str, port: u16, user: &str) -> SilentPeer {
|
||||||
|
// Not loopback: soulfind hands a loopback login's address out as the
|
||||||
|
// host's LAN address, where a 127.0.0.1 listener is never reached.
|
||||||
|
let listener = TcpListener::bind("0.0.0.0:0").expect("silent listener");
|
||||||
|
let wait_port = listener.local_addr().expect("listener address").port();
|
||||||
|
let mut session = TcpStream::connect((host, port)).expect("server");
|
||||||
|
let login = MessageFactory::build_login_message(
|
||||||
|
user,
|
||||||
|
"pw",
|
||||||
|
soulseek_rs::ClientVersion::default(),
|
||||||
|
);
|
||||||
|
session
|
||||||
|
.write_all(&login.get_buffer())
|
||||||
|
.expect("silent login");
|
||||||
|
// soulfind drops everything but a login until the login has landed, so a
|
||||||
|
// port announced any sooner is announced to nobody.
|
||||||
|
session
|
||||||
|
.set_read_timeout(Some(Duration::from_secs(10)))
|
||||||
|
.expect("read timeout");
|
||||||
|
loop {
|
||||||
|
let mut len = [0; 4];
|
||||||
|
session.read_exact(&mut len).expect("login reply");
|
||||||
|
let mut body = vec![0; u32::from_le_bytes(len) as usize];
|
||||||
|
session.read_exact(&mut body).expect("login reply");
|
||||||
|
if body[..4] == 1u32.to_le_bytes() {
|
||||||
|
assert_eq!(body[4], 1, "{user} should log in");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let wait = MessageFactory::build_set_wait_port_message(wait_port);
|
||||||
|
session.write_all(&wait.get_buffer()).expect("wait port");
|
||||||
|
settle();
|
||||||
|
SilentPeer {
|
||||||
|
_session: session,
|
||||||
|
_listener: listener,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Wait out the SetWaitPort registrations so peer lookups resolve.
|
/// Wait out the SetWaitPort registrations so peer lookups resolve.
|
||||||
pub fn settle() {
|
pub fn settle() {
|
||||||
std::thread::sleep(Duration::from_secs(1));
|
std::thread::sleep(Duration::from_secs(1));
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
use common::{CLI_ENV_VARS, Soulfind, free_port};
|
use common::{CLI_ENV_VARS, Soulfind, free_port, silent_peer};
|
||||||
use std::io::{BufRead, BufReader, Write};
|
use std::io::{BufRead, BufReader, Write};
|
||||||
use std::net::TcpStream;
|
use std::net::TcpStream;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
@@ -838,19 +838,21 @@ fn a_wishlist_search_is_reported_as_just_started() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn a_queued_download_is_cancelled_and_listed_as_such() {
|
fn a_queued_download_is_cancelled_and_listed_as_such() {
|
||||||
let daemon = daemon_or_skip!();
|
let daemon = daemon_or_skip!();
|
||||||
|
let _peer =
|
||||||
|
silent_peer("127.0.0.1", daemon.server.port(), "cli_e2e_silent_peer");
|
||||||
let mut connection = daemon.connect_tcp().expect("tcp");
|
let mut connection = daemon.connect_tcp().expect("tcp");
|
||||||
connection
|
connection
|
||||||
.authenticate(Some(&daemon.token()))
|
.authenticate(Some(&daemon.token()))
|
||||||
.expect("an answer");
|
.expect("an answer");
|
||||||
let started = connection
|
let started = connection
|
||||||
.call(
|
.call(
|
||||||
r#"{"jsonrpc":"2.0","id":2,"method":"download.start","params":{"username":"e2e_ghost_peer","filename":"@@ghost\\never.mp3","size":10}}"#,
|
r#"{"jsonrpc":"2.0","id":2,"method":"download.start","params":{"username":"cli_e2e_silent_peer","filename":"@@silent\\never.mp3","size":10}}"#,
|
||||||
)
|
)
|
||||||
.expect("an answer");
|
.expect("an answer");
|
||||||
assert!(!started.contains("error"), "got {started}");
|
assert!(!started.contains("error"), "got {started}");
|
||||||
let reply = connection
|
let reply = connection
|
||||||
.call(
|
.call(
|
||||||
r#"{"jsonrpc":"2.0","id":3,"method":"download.cancel","params":{"username":"e2e_ghost_peer","filename":"@@ghost\\never.mp3"}}"#,
|
r#"{"jsonrpc":"2.0","id":3,"method":"download.cancel","params":{"username":"cli_e2e_silent_peer","filename":"@@silent\\never.mp3"}}"#,
|
||||||
)
|
)
|
||||||
.expect("an answer");
|
.expect("an answer");
|
||||||
assert!(reply.contains(r#""ok":true"#), "got {reply}");
|
assert!(reply.contains(r#""ok":true"#), "got {reply}");
|
||||||
|
|||||||
Reference in New Issue
Block a user