Implement native IRC gateway
All checks were successful
Native Rust workspace compile / compile (push) Successful in 21m47s

This commit is contained in:
2026-08-11 09:38:45 +00:00
parent bb7f419ca0
commit d1e05d1b30
6 changed files with 1849 additions and 3 deletions

View File

@@ -0,0 +1,222 @@
use std::fs;
use std::path::{Path, PathBuf};
use std::process::{Command, Output, Stdio};
use std::sync::atomic::{AtomicU64, Ordering};
const MASTER: &str = "11111111-2222-3333-4444-555555555555";
const SESSION: &str = "22222222-3333-4444-5555-666666666666";
const OTHER: &str = "bbbbbbbb-cccc-dddd-eeee-ffffffffffff";
const SELF_ID: &str = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee";
const EXIT_USAGE: i32 = 2;
const EXIT_INPUT: i32 = 3;
static TEMP_ID: AtomicU64 = AtomicU64::new(0);
struct TestDir(PathBuf);
impl TestDir {
fn new(name: &str) -> Self {
let id = TEMP_ID.fetch_add(1, Ordering::Relaxed);
let path = std::env::temp_dir().join(format!(
"metacrate-irc-gateway-{name}-{}-{id}",
std::process::id()
));
fs::create_dir(&path).expect("create test directory");
Self(path)
}
fn write(&self, contents: &str) -> PathBuf {
let path = self.0.join("bridge.tsv");
fs::write(&path, contents).expect("write bridge script");
path
}
}
impl Drop for TestDir {
fn drop(&mut self) {
let _ = fs::remove_dir_all(&self.0);
}
}
fn path_text(path: &Path) -> &str {
path.to_str().expect("UTF-8 path")
}
fn run(args: &[&str]) -> Output {
Command::new(env!("CARGO_BIN_EXE_irc-gateway"))
.args(args)
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output()
.expect("run irc-gateway")
}
fn stdout(output: &Output) -> &str {
std::str::from_utf8(&output.stdout).expect("UTF-8 stdout")
}
fn stderr(output: &Output) -> &str {
std::str::from_utf8(&output.stderr).expect("UTF-8 stderr")
}
fn assert_exit(output: &Output, code: i32) {
assert_eq!(
output.status.code(),
Some(code),
"stdout:\n{}\nstderr:\n{}",
stdout(output),
stderr(output)
);
}
#[test]
fn help_preserves_upstream_arguments_and_documents_controls() {
let output = run(&["--help"]);
assert!(output.status.success());
let help = stdout(&output);
for marker in [
"[FIRSTNAME]",
"[LASTNAME]",
"[PASSWORD]",
"[MASTER_UUID]",
"[IRC_HOST]",
"[IRC_PORT]",
"[#CHANNEL]",
"--fake-script",
"--messages-per-second",
"--burst",
"irc-connect",
"grid-chat",
"teleport",
] {
assert!(help.contains(marker), "help omitted {marker}:\n{help}");
}
let output = run(&[]);
assert_exit(&output, EXIT_USAGE);
}
#[test]
fn scripted_bidirectional_bridge_filters_maps_and_accepts_only_master() {
let directory = TestDir::new("bidirectional");
let script = directory.write(&format!(
"irc-connect\n\
grid-login\tconnected to https://grid.invalid/cap/private\n\
grid-chat\t{OTHER}\tAlice <Resident>\tnormal\tfull\thello IRC ☃\n\
grid-chat\t{OTHER}\tAlice <Resident>\twhisper\tfull\tignored type\n\
grid-chat\t{OTHER}\tAlice <Resident>\tnormal\tpartial\tignored range\n\
grid-chat\t{SELF_ID}\tGateway Resident\tnormal\tfull\tignored self\n\
irc\t#metacrate\tBob\thello grid ☃\n\
irc\t#other\tMallory\tignored channel\n\
irc\t#metacrate\tSLGateway\tignored self\n\
teleport\t{OTHER}\t{SESSION}\n\
teleport\t{MASTER}\t{SESSION}\n\
shutdown\n"
));
let output = run(&["--fake-script", path_text(&script)]);
assert!(output.status.success(), "{}", stderr(&output));
assert!(output.stderr.is_empty());
let text = stdout(&output);
for expected in [
"CALL irc-join #metacrate",
"CALL irc-privmsg #metacrate <Alice _Resident_> hello IRC ☃",
"CALL grid-chat channel=0 type=Normal <Bob> hello grid ☃",
&format!("CALL teleport-lure-respond {MASTER} {SESSION} accept=true"),
"active-transports=0 active-tasks=0 shutdown=true",
] {
assert!(text.contains(expected), "missing {expected}:\n{text}");
}
for rejected in [
"ignored type",
"ignored range",
"ignored self",
"ignored channel",
&format!("teleport-lure-respond {OTHER}"),
"grid.invalid",
"/cap/private",
] {
assert!(
!text.contains(rejected),
"leaked/forwarded {rejected}:\n{text}"
);
}
assert!(text.contains("<redacted-url>"));
}
#[test]
fn reconnect_rate_limit_duplicate_and_loop_scenarios_are_deterministic() {
let directory = TestDir::new("resilience");
let script = directory.write(&format!(
"irc-connect\n\
grid-chat\t{OTHER}\tAlice\tnormal\tfull\tone\n\
grid-chat\t{OTHER}\tAlice\tnormal\tfull\ttwo\n\
grid-chat\t{OTHER}\tAlice\tnormal\tfull\ttwo\n\
irc\t#metacrate\tRelay\t<Alice> one\n\
irc-disconnect\n\
grid-chat\t{OTHER}\tAlice\tnormal\tfull\twhile offline\n\
advance\t1000\n\
irc-connect\n\
advance\t1000\n\
shutdown\n"
));
let output = run(&[
"--fake-script",
path_text(&script),
"--messages-per-second",
"1",
"--burst",
"1",
]);
assert!(output.status.success(), "{}", stderr(&output));
let text = stdout(&output);
assert_eq!(
text.matches("CALL irc-join #metacrate").count(),
2,
"{text}"
);
assert_eq!(
text.matches("<Alice> two").count(),
2,
"one log and one transport call expected:\n{text}"
);
assert_eq!(
text.matches("<Alice> one").count(),
2,
"one log and one call expected:\n{text}"
);
assert!(
!text.contains("CALL grid-chat"),
"loop was reflected:\n{text}"
);
assert!(
text.contains("CALL irc-privmsg #metacrate <Alice> while offline"),
"{text}"
);
assert!(text.contains("queued=0"), "{text}");
}
#[test]
fn malformed_and_oversized_inputs_fail_without_network_access() {
let directory = TestDir::new("invalid");
let invalid = directory.write("irc\tmissing fields\n");
let output = run(&["--fake-script", path_text(&invalid)]);
assert_exit(&output, EXIT_INPUT);
assert!(stderr(&output).contains("line 1"));
let oversized = directory.write(&format!("grid-login\t{}\n", "x".repeat(4097)));
let output = run(&["--fake-script", path_text(&oversized)]);
assert_exit(&output, EXIT_INPUT);
let output = run(&[
"First",
"Last",
"secret",
"not-a-uuid",
"irc.invalid",
"6667",
"#room",
]);
assert_exit(&output, EXIT_USAGE);
assert!(!stderr(&output).contains("secret"));
}