// The 0006 specification, as executable tests. Do not edit this file — make it pass. // Copy to: tasks/tests/errors.rs // Run with: cargo test use std::error::Error; use tasks::command::Command; use tasks::error::TaskError; use tasks::store::Store; use tasks::task::Priority; fn args(list: &[&str]) -> Vec { list.iter().map(|s| s.to_string()).collect() } // ---------- one variant per way of failing ---------- #[test] fn parse_errors_name_the_exact_failure() { assert_eq!(Command::parse(&args(&[])).unwrap_err(), TaskError::NoCommand); assert_eq!( Command::parse(&args(&["fly"])).unwrap_err(), TaskError::UnknownCommand("fly".to_string()) ); assert_eq!( Command::parse(&args(&["add"])).unwrap_err(), TaskError::MissingTitle ); assert_eq!( Command::parse(&args(&["add", "x", "urgent"])).unwrap_err(), TaskError::BadPriority("urgent".to_string()) ); assert_eq!( Command::parse(&args(&["done"])).unwrap_err(), TaskError::MissingId ); } #[test] fn store_reports_which_id_was_missing() { let mut store = Store::new(); store.add("only task", Priority::Low); assert_eq!(store.complete(99).unwrap_err(), TaskError::NotFound(99)); assert_eq!(store.remove(7).unwrap_err(), TaskError::NotFound(7)); } // ---------- Display: the sentence the user reads ---------- #[test] fn messages_are_lowercase_and_unpunctuated() { assert_eq!(TaskError::NoCommand.to_string(), "no command given"); assert_eq!( TaskError::UnknownCommand("fly".to_string()).to_string(), "unknown command: fly" ); assert_eq!(TaskError::MissingTitle.to_string(), "add needs a title"); assert_eq!( TaskError::MissingId.to_string(), "this command needs a task id" ); assert_eq!( TaskError::BadPriority("urgent".to_string()).to_string(), "unknown priority: urgent" ); assert_eq!(TaskError::NotFound(9).to_string(), "no task with id 9"); } // ---------- From + ? : the parse error is wrapped, not thrown away ---------- #[test] fn a_bad_id_wraps_the_parse_error() { let err = Command::parse(&args(&["done", "abc"])).unwrap_err(); assert!(matches!(err, TaskError::BadId(_)), "expected BadId"); assert!( Command::parse(&args(&["remove", "-1"])).is_err(), "a negative id is not a u32" ); } #[test] fn a_wrapped_error_is_reported_once_and_reachable() { let err = Command::parse(&args(&["done", "abc"])).unwrap_err(); // Display says your sentence and does not repeat std's. assert_eq!(err.to_string(), "task id must be a number"); // std's sentence is still reachable, through Error::source(). let inner = err.source().expect("BadId must expose its source"); assert_eq!(inner.to_string(), "invalid digit found in string"); } #[test] fn errors_that_carry_nothing_have_no_source() { assert!(TaskError::NoCommand.source().is_none()); assert!(TaskError::NotFound(1).source().is_none()); } // ---------- the Error trait, and the bounds the ecosystem expects ---------- #[test] fn task_error_is_a_real_error() { fn assert_usable_as_error() {} assert_usable_as_error::(); let boxed: Box = Box::new(TaskError::NotFound(1)); assert_eq!(boxed.to_string(), "no task with id 1"); }