Rust learning: lessons, notes, and exercise crates
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
// 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<String> {
|
||||
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<E: Error + Send + Sync + 'static>() {}
|
||||
assert_usable_as_error::<TaskError>();
|
||||
|
||||
let boxed: Box<dyn Error> = Box::new(TaskError::NotFound(1));
|
||||
assert_eq!(boxed.to_string(), "no task with id 1");
|
||||
}
|
||||
Reference in New Issue
Block a user