use std::collections::BTreeMap; use std::path::PathBuf; use rustbot::bot::module::{Action, Command, Module}; use rustbot::bot::modules::weather::{format_weather, parse_db, to_json, Weather}; fn cmd<'a>(sender: &'a str, name: &'a str, args: &'a [&'a str]) -> Command<'a> { Command { sender, reply_to: "#chan", name, args, prefix: ">", network: "test", } } fn reply(actions: &[Action]) -> &str { match actions { [Action::Reply(s)] => s, _ => panic!("expected exactly one Reply"), } } fn temp(name: &str) -> PathBuf { let p = std::env::temp_dir().join(format!("rubot-test-{name}.json")); let _ = std::fs::remove_file(&p); p } #[test] fn json_round_trip_handles_special_chars() { let mut m = BTreeMap::new(); m.insert("alice".to_string(), "73700".to_string()); m.insert("bob".to_string(), "Saint-Étienne, FR".to_string()); m.insert("carol".to_string(), "a\"b\\c\nd".to_string()); let back = parse_db(&to_json(&m)); assert_eq!(m, back); } #[test] fn parse_db_tolerates_junk() { assert!(parse_db("").is_empty()); assert!(parse_db("not json").is_empty()); assert_eq!(parse_db("{}").len(), 0); assert_eq!( parse_db("{\"a\":\"b\"}").get("a").map(String::as_str), Some("b") ); } #[test] fn add_saves_and_persists_case_insensitively() { let path = temp("weather-add"); let mut w = Weather::with_path(path.clone()); let r = reply(&w.on_command(&cmd("Alice", "add", &["73700"]))).to_string(); assert!(r.contains("73700"), "reply: {r:?}"); let w2 = Weather::with_path(path.clone()); assert_eq!(w2.location_of("alice"), Some("73700")); assert_eq!(w2.location_of("ALICE"), Some("73700")); let _ = std::fs::remove_file(&path); } #[test] fn add_without_arg_shows_usage() { let mut w = Weather::with_path(temp("weather-usage")); let r = reply(&w.on_command(&cmd("alice", "add", &[]))).to_string(); assert!(r.contains("usage"), "reply: {r:?}"); } #[test] fn w_without_saved_location_hints_to_add() { let mut w = Weather::with_path(temp("weather-hint")); let r = reply(&w.on_command(&cmd("nobody", "w", &[]))).to_string(); assert!(r.contains("add"), "reply: {r:?}"); } #[test] fn format_weather_builds_friendly_line() { let out = format_weather("73700|☀️ |Sunny|+23°C|+18°C|↘8km/h|45%", "73700"); assert!(out.contains("☀️"), "{out}"); assert!(out.contains("73700: Sunny"), "{out}"); assert!(out.contains("23°C") && !out.contains("+23"), "{out}"); assert!(out.contains("feels like 18°C"), "{out}"); assert!(out.contains("45%"), "{out}"); } #[test] fn format_weather_friendly_not_found() { let out = format_weather("location not found: upstream error", "zzz"); assert!(out.contains("couldn't find 'zzz'"), "{out}"); }