From 64d3bfc0035807d7d15f723bd0b44be7c60f7449 Mon Sep 17 00:00:00 2001 From: reverse Date: Sun, 16 Aug 2026 13:16:09 +0000 Subject: [PATCH] =?UTF-8?q?test:=20cover=20IJOIN=20status-mode=20applicati?= =?UTF-8?q?on=20(bot=20joins=20+ao)=20=E2=80=94=20regression=20guard=20for?= =?UTF-8?q?=20the=20S2S=20ijoin=20handler=20that=20previously=20had=20none?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/link.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/link.rs b/src/link.rs index 489b41b..7319c37 100644 --- a/src/link.rs +++ b/src/link.rs @@ -1817,4 +1817,39 @@ mod tests { assert!(!valid_sid("0A")); // too short assert!(!valid_sid("0ABC")); // too long } + + // A services bot IJOINing an existing channel with a status token (e.g. "ao") + // must join holding those prefix modes. Regression: an early S2S build accepted + // the IJOIN but ignored the token, so BotServ bots joined bare and had to be + // opped by hand. + #[test] + fn ijoin_applies_status_modes() { + use crate::config::Config; + use std::sync::atomic::AtomicU64; + use std::sync::{mpsc, Arc}; + let (tx, _rx) = mpsc::channel(); + let mut s = Server::new(Config::default(), tx, Arc::new(AtomicU64::new(1))); + // a services bot the network already knows about + s.remote_users.insert( + "42SB00000".to_string(), + RemoteUser { + uuid: "42SB00000".to_string(), + nick: "echoIRCd".into(), + ident: "echo".into(), + host: "services".into(), + realname: "bot".into(), + account: None, + ip: String::new(), + modes: "iHkB".into(), + sid: "42S".into(), + via: 1, + }, + ); + // echo joins it to an existing channel as protected admin + op (+ao) + let msg = crate::message::parse(":42SB00000 IJOIN #echoircd 16 1 ao").unwrap(); + s.link_ijoin_recv(1, &msg); + let m = &s.channels["#echoircd"].rmembers["42SB00000"]; + assert!(m.admin, "bot should hold +a (&) from the IJOIN status token"); + assert!(m.op, "bot should hold +o (@) from the IJOIN status token"); + } }