forked from lavina/lavina
irc: improve tests and remove tail space in chan member list
This commit is contained in:
parent
d436631450
commit
a325c7307c
|
@ -48,6 +48,31 @@ impl<'a> TestScope<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
async fn expect_that(&mut self, validate: impl FnOnce(&str) -> bool) -> Result<()> {
|
||||
let len = tokio::time::timeout(self.timeout, read_irc_message(&mut self.reader, &mut self.buffer)).await??;
|
||||
let msg = std::str::from_utf8(&self.buffer[..len - 2])?;
|
||||
if !validate(msg) {
|
||||
return Err(anyhow!("unexpected message: {:?}", msg));
|
||||
}
|
||||
self.buffer.clear();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn expect_server_introduction(&mut self, nick: &str) -> Result<()> {
|
||||
self.expect(&format!(":testserver 001 {nick} :Welcome to testserver Server")).await?;
|
||||
self.expect(&format!(":testserver 002 {nick} :Welcome to testserver Server")).await?;
|
||||
self.expect(&format!(":testserver 003 {nick} :Welcome to testserver Server")).await?;
|
||||
self.expect(&format!(
|
||||
":testserver 004 {nick} testserver {APP_VERSION} r CFILPQbcefgijklmnopqrstvz"
|
||||
))
|
||||
.await?;
|
||||
self.expect(&format!(
|
||||
":testserver 005 {nick} CHANTYPES=# :are supported by this server"
|
||||
))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn expect_eof(&mut self) -> Result<()> {
|
||||
let mut buf = [0; 1];
|
||||
let len = tokio::time::timeout(self.timeout, self.reader.read(&mut buf)).await??;
|
||||
|
@ -113,18 +138,7 @@ async fn scenario_basic() -> Result<()> {
|
|||
s.send("PASS password").await?;
|
||||
s.send("NICK tester").await?;
|
||||
s.send("USER UserName 0 * :Real Name").await?;
|
||||
s.expect(":testserver 001 tester :Welcome to testserver Server").await?;
|
||||
s.expect(":testserver 002 tester :Welcome to testserver Server").await?;
|
||||
s.expect(":testserver 003 tester :Welcome to testserver Server").await?;
|
||||
s.expect(
|
||||
format!(
|
||||
":testserver 004 tester testserver {} r CFILPQbcefgijklmnopqrstvz",
|
||||
&APP_VERSION
|
||||
)
|
||||
.as_str(),
|
||||
)
|
||||
.await?;
|
||||
s.expect(":testserver 005 tester CHANTYPES=# :are supported by this server").await?;
|
||||
s.expect_server_introduction("tester").await?;
|
||||
s.expect_nothing().await?;
|
||||
s.send("QUIT :Leaving").await?;
|
||||
s.expect(":testserver ERROR :Leaving the server").await?;
|
||||
|
@ -138,6 +152,133 @@ async fn scenario_basic() -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn scenario_force_join_msg() -> Result<()> {
|
||||
let mut server = TestServer::start().await?;
|
||||
|
||||
// test scenario
|
||||
|
||||
server.storage.create_user("tester").await?;
|
||||
server.storage.set_password("tester", "password").await?;
|
||||
|
||||
let mut stream1 = TcpStream::connect(server.server.addr).await?;
|
||||
let mut s1 = TestScope::new(&mut stream1);
|
||||
let mut stream2 = TcpStream::connect(server.server.addr).await?;
|
||||
let mut s2 = TestScope::new(&mut stream2);
|
||||
|
||||
s1.send("PASS password").await?;
|
||||
s1.send("NICK tester").await?;
|
||||
s1.send("USER UserName 0 * :Real Name").await?;
|
||||
s1.expect_server_introduction("tester").await?;
|
||||
s1.expect_nothing().await?;
|
||||
|
||||
s2.send("PASS password").await?;
|
||||
s2.send("NICK tester").await?;
|
||||
s2.send("USER UserName 0 * :Real Name").await?;
|
||||
s2.expect_server_introduction("tester").await?;
|
||||
s2.expect_nothing().await?;
|
||||
|
||||
// We join the channel from the first connection
|
||||
s1.send("JOIN #test").await?;
|
||||
s1.expect(":tester JOIN #test").await?;
|
||||
s1.expect(":testserver 332 tester #test :New room").await?;
|
||||
s1.expect(":testserver 353 tester = #test :tester").await?;
|
||||
s1.expect(":testserver 366 tester #test :End of /NAMES list").await?;
|
||||
|
||||
// And the second connection should receive the JOIN message (forced JOIN)
|
||||
s2.expect(":tester JOIN #test").await?;
|
||||
s2.expect(":testserver 332 tester #test :New room").await?;
|
||||
s2.expect(":testserver 353 tester = #test :tester").await?;
|
||||
s2.expect(":testserver 366 tester #test :End of /NAMES list").await?;
|
||||
|
||||
// We send a message to the channel from the second connection
|
||||
s2.send("PRIVMSG #test :Hello").await?;
|
||||
// We should not receive an acknowledgement from the server
|
||||
s2.expect_nothing().await?;
|
||||
// But we should receive this message from the first connection
|
||||
s1.expect(":tester PRIVMSG #test :Hello").await?;
|
||||
|
||||
s1.send("QUIT :Leaving").await?;
|
||||
s1.expect(":testserver ERROR :Leaving the server").await?;
|
||||
s1.expect_eof().await?;
|
||||
|
||||
// Closing a connection does not kick you from the channel on a different connection
|
||||
s2.expect_nothing().await?;
|
||||
|
||||
s2.send("QUIT :Leaving").await?;
|
||||
s2.expect(":testserver ERROR :Leaving the server").await?;
|
||||
s2.expect_eof().await?;
|
||||
|
||||
stream1.shutdown().await?;
|
||||
stream2.shutdown().await?;
|
||||
|
||||
// wrap up
|
||||
|
||||
server.server.terminate().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn scenario_two_users() -> Result<()> {
|
||||
let mut server = TestServer::start().await?;
|
||||
|
||||
// test scenario
|
||||
|
||||
server.storage.create_user("tester1").await?;
|
||||
server.storage.set_password("tester1", "password").await?;
|
||||
server.storage.create_user("tester2").await?;
|
||||
server.storage.set_password("tester2", "password").await?;
|
||||
|
||||
let mut stream1 = TcpStream::connect(server.server.addr).await?;
|
||||
let mut s1 = TestScope::new(&mut stream1);
|
||||
let mut stream2 = TcpStream::connect(server.server.addr).await?;
|
||||
let mut s2 = TestScope::new(&mut stream2);
|
||||
|
||||
s1.send("PASS password").await?;
|
||||
s1.send("NICK tester1").await?;
|
||||
s1.send("USER UserName 0 * :Real Name").await?;
|
||||
s1.expect_server_introduction("tester1").await?;
|
||||
s1.expect_nothing().await?;
|
||||
|
||||
s2.send("PASS password").await?;
|
||||
s2.send("NICK tester2").await?;
|
||||
s2.send("USER UserName 0 * :Real Name").await?;
|
||||
s2.expect_server_introduction("tester2").await?;
|
||||
s2.expect_nothing().await?;
|
||||
|
||||
// Join the channel from the first user
|
||||
s1.send("JOIN #test").await?;
|
||||
s1.expect(":tester1 JOIN #test").await?;
|
||||
s1.expect(":testserver 332 tester1 #test :New room").await?;
|
||||
s1.expect(":testserver 353 tester1 = #test :tester1").await?;
|
||||
s1.expect(":testserver 366 tester1 #test :End of /NAMES list").await?;
|
||||
// Then join the channel from the second user
|
||||
s2.send("JOIN #test").await?;
|
||||
s2.expect(":tester2 JOIN #test").await?;
|
||||
s2.expect(":testserver 332 tester2 #test :New room").await?;
|
||||
s2.expect_that(|msg| {
|
||||
msg == ":testserver 353 tester2 = #test :tester1 tester2"
|
||||
|| msg == ":testserver 353 tester2 = #test :tester2 tester1"
|
||||
})
|
||||
.await?;
|
||||
s2.expect(":testserver 366 tester2 #test :End of /NAMES list").await?;
|
||||
// The first user should receive the JOIN message from the second user
|
||||
s1.expect(":tester2 JOIN #test").await?;
|
||||
s1.expect_nothing().await?;
|
||||
s2.expect_nothing().await?;
|
||||
// Send a message from the second user
|
||||
s2.send("PRIVMSG #test :Hello").await?;
|
||||
// The first user should receive the message
|
||||
s1.expect(":tester2 PRIVMSG #test :Hello").await?;
|
||||
// Leave the channel from the first user
|
||||
// TODO implement irc PART command
|
||||
// s1.send("PART #test").await?;
|
||||
// s1.expect(":tester1 PART #test").await?;
|
||||
// The second user should receive the PART message
|
||||
// s2.expect(":tester1 PART #test").await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/*
|
||||
IRC SASL doc: https://ircv3.net/specs/extensions/sasl-3.1.html
|
||||
AUTHENTICATE doc: https://modern.ircdocs.horse/#authenticate-message
|
||||
|
@ -168,18 +309,7 @@ async fn scenario_cap_full_negotiation() -> Result<()> {
|
|||
|
||||
s.send("CAP END").await?;
|
||||
|
||||
s.expect(":testserver 001 tester :Welcome to testserver Server").await?;
|
||||
s.expect(":testserver 002 tester :Welcome to testserver Server").await?;
|
||||
s.expect(":testserver 003 tester :Welcome to testserver Server").await?;
|
||||
s.expect(
|
||||
format!(
|
||||
":testserver 004 tester testserver {} r CFILPQbcefgijklmnopqrstvz",
|
||||
&APP_VERSION
|
||||
)
|
||||
.as_str(),
|
||||
)
|
||||
.await?;
|
||||
s.expect(":testserver 005 tester CHANTYPES=# :are supported by this server").await?;
|
||||
s.expect_server_introduction("tester").await?;
|
||||
s.expect_nothing().await?;
|
||||
s.send("QUIT :Leaving").await?;
|
||||
s.expect(":testserver ERROR :Leaving the server").await?;
|
||||
|
@ -217,18 +347,7 @@ async fn scenario_cap_short_negotiation() -> Result<()> {
|
|||
|
||||
s.send("CAP END").await?;
|
||||
|
||||
s.expect(":testserver 001 tester :Welcome to testserver Server").await?;
|
||||
s.expect(":testserver 002 tester :Welcome to testserver Server").await?;
|
||||
s.expect(":testserver 003 tester :Welcome to testserver Server").await?;
|
||||
s.expect(
|
||||
format!(
|
||||
":testserver 004 tester testserver {} r CFILPQbcefgijklmnopqrstvz",
|
||||
&APP_VERSION
|
||||
)
|
||||
.as_str(),
|
||||
)
|
||||
.await?;
|
||||
s.expect(":testserver 005 tester CHANTYPES=# :are supported by this server").await?;
|
||||
s.expect_server_introduction("tester").await?;
|
||||
s.expect_nothing().await?;
|
||||
s.send("QUIT :Leaving").await?;
|
||||
s.expect(":testserver ERROR :Leaving the server").await?;
|
||||
|
@ -272,18 +391,7 @@ async fn scenario_cap_sasl_fail() -> Result<()> {
|
|||
|
||||
s.send("CAP END").await?;
|
||||
|
||||
s.expect(":testserver 001 tester :Welcome to testserver Server").await?;
|
||||
s.expect(":testserver 002 tester :Welcome to testserver Server").await?;
|
||||
s.expect(":testserver 003 tester :Welcome to testserver Server").await?;
|
||||
s.expect(
|
||||
format!(
|
||||
":testserver 004 tester testserver {} r CFILPQbcefgijklmnopqrstvz",
|
||||
&APP_VERSION
|
||||
)
|
||||
.as_str(),
|
||||
)
|
||||
.await?;
|
||||
s.expect(":testserver 005 tester CHANTYPES=# :are supported by this server").await?;
|
||||
s.expect_server_introduction("tester").await?;
|
||||
s.expect_nothing().await?;
|
||||
s.send("QUIT :Leaving").await?;
|
||||
s.expect(":testserver ERROR :Leaving the server").await?;
|
||||
|
|
|
@ -317,10 +317,15 @@ impl ServerMessageBody {
|
|||
writer.write_all(b" = ").await?;
|
||||
chan.write_async(writer).await?;
|
||||
writer.write_all(b" :").await?;
|
||||
for member in members {
|
||||
{
|
||||
let member = &members.head;
|
||||
writer.write_all(member.prefix.to_string().as_bytes()).await?;
|
||||
writer.write_all(member.nick.as_bytes()).await?;
|
||||
}
|
||||
for member in &members.tail {
|
||||
writer.write_all(b" ").await?;
|
||||
writer.write_all(member.prefix.to_string().as_bytes()).await?;
|
||||
writer.write_all(member.nick.as_bytes()).await?;
|
||||
}
|
||||
}
|
||||
ServerMessageBody::N366NamesReplyEnd { client, chan } => {
|
||||
|
|
Loading…
Reference in New Issue