Rust learning: lessons, notes, and exercise crates
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
// The specification, as executable tests. Do not edit this file — make it pass.
|
||||
// Copy to: tasks/tests/spec.rs
|
||||
// Run with: cargo test
|
||||
|
||||
use tasks::command::Command;
|
||||
use tasks::store::Store;
|
||||
use tasks::task::{Priority, Status, Task};
|
||||
|
||||
/// Helper: build an argument list the way main() would pass it in
|
||||
/// (program name already stripped).
|
||||
fn args(list: &[&str]) -> Vec<String> {
|
||||
list.iter().map(|s| s.to_string()).collect()
|
||||
}
|
||||
|
||||
// ---------- task.rs ----------
|
||||
|
||||
#[test]
|
||||
fn new_task_starts_as_todo() {
|
||||
let task = Task::new(1, "write the spec", Priority::High);
|
||||
assert_eq!(task.id, 1);
|
||||
assert_eq!(task.title, "write the spec");
|
||||
assert_eq!(task.status, Status::Todo);
|
||||
assert_eq!(task.priority, Priority::High);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn status_labels() {
|
||||
assert_eq!(Status::Todo.label(), "todo");
|
||||
assert_eq!(Status::InProgress.label(), "in-progress");
|
||||
assert_eq!(Status::Done.label(), "done");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn priority_labels() {
|
||||
assert_eq!(Priority::Low.label(), "low");
|
||||
assert_eq!(Priority::Medium.label(), "medium");
|
||||
assert_eq!(Priority::High.label(), "high");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn priority_parses_known_words_only() {
|
||||
assert_eq!(Priority::parse("low"), Some(Priority::Low));
|
||||
assert_eq!(Priority::parse("medium"), Some(Priority::Medium));
|
||||
assert_eq!(Priority::parse("high"), Some(Priority::High));
|
||||
assert_eq!(Priority::parse("urgent"), None);
|
||||
assert_eq!(Priority::parse(""), None);
|
||||
}
|
||||
|
||||
// ---------- command.rs ----------
|
||||
|
||||
#[test]
|
||||
fn parses_add_with_default_priority() {
|
||||
let command = Command::parse(&args(&["add", "buy milk"])).unwrap();
|
||||
assert_eq!(
|
||||
command,
|
||||
Command::Add {
|
||||
title: "buy milk".to_string(),
|
||||
priority: Priority::Medium,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_add_with_explicit_priority() {
|
||||
let command = Command::parse(&args(&["add", "ship it", "high"])).unwrap();
|
||||
assert_eq!(
|
||||
command,
|
||||
Command::Add {
|
||||
title: "ship it".to_string(),
|
||||
priority: Priority::High,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_list_done_and_remove() {
|
||||
assert_eq!(Command::parse(&args(&["list"])).unwrap(), Command::List);
|
||||
assert_eq!(
|
||||
Command::parse(&args(&["done", "7"])).unwrap(),
|
||||
Command::Done { id: 7 }
|
||||
);
|
||||
assert_eq!(
|
||||
Command::parse(&args(&["remove", "12"])).unwrap(),
|
||||
Command::Remove { id: 12 }
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_bad_input() {
|
||||
assert!(Command::parse(&args(&[])).is_err(), "no command at all");
|
||||
assert!(Command::parse(&args(&["fly"])).is_err(), "unknown command");
|
||||
assert!(Command::parse(&args(&["add"])).is_err(), "add with no title");
|
||||
assert!(
|
||||
Command::parse(&args(&["add", "x", "urgent"])).is_err(),
|
||||
"unknown priority word"
|
||||
);
|
||||
assert!(Command::parse(&args(&["done"])).is_err(), "done with no id");
|
||||
assert!(
|
||||
Command::parse(&args(&["done", "abc"])).is_err(),
|
||||
"id is not a number"
|
||||
);
|
||||
assert!(
|
||||
Command::parse(&args(&["remove", "-1"])).is_err(),
|
||||
"negative id is not a u32"
|
||||
);
|
||||
}
|
||||
|
||||
// ---------- store.rs ----------
|
||||
|
||||
#[test]
|
||||
fn add_returns_ids_starting_at_one() {
|
||||
let mut store = Store::new();
|
||||
assert_eq!(store.add("first", Priority::Low), 1);
|
||||
assert_eq!(store.add("second", Priority::Low), 2);
|
||||
assert_eq!(store.add("third", Priority::Low), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tasks_come_back_in_insertion_order() {
|
||||
let mut store = Store::new();
|
||||
store.add("first", Priority::Low);
|
||||
store.add("second", Priority::High);
|
||||
|
||||
let listed = store.tasks();
|
||||
assert_eq!(listed.len(), 2);
|
||||
assert_eq!(listed[0].title, "first");
|
||||
assert_eq!(listed[1].title, "second");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_store_is_empty() {
|
||||
let store = Store::new();
|
||||
assert!(store.tasks().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn complete_sets_status_to_done() {
|
||||
let mut store = Store::new();
|
||||
let id = store.add("do the thing", Priority::Medium);
|
||||
|
||||
assert_eq!(store.find(id).unwrap().status, Status::Todo);
|
||||
assert!(store.complete(id).is_ok());
|
||||
assert_eq!(store.find(id).unwrap().status, Status::Done);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn complete_unknown_id_is_an_error() {
|
||||
let mut store = Store::new();
|
||||
store.add("only task", Priority::Low);
|
||||
assert!(store.complete(99).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_deletes_only_that_task() {
|
||||
let mut store = Store::new();
|
||||
let first = store.add("first", Priority::Low);
|
||||
let second = store.add("second", Priority::Low);
|
||||
|
||||
assert!(store.remove(first).is_ok());
|
||||
assert_eq!(store.tasks().len(), 1);
|
||||
assert!(store.find(first).is_none());
|
||||
assert!(store.find(second).is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_unknown_id_is_an_error() {
|
||||
let mut store = Store::new();
|
||||
assert!(store.remove(1).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn find_returns_none_for_missing_id() {
|
||||
let store = Store::new();
|
||||
assert!(store.find(1).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ids_are_not_reused_after_remove() {
|
||||
let mut store = Store::new();
|
||||
let first = store.add("first", Priority::Low);
|
||||
store.remove(first).unwrap();
|
||||
let second = store.add("second", Priority::Low);
|
||||
assert_ne!(first, second, "a removed id must not be handed out again");
|
||||
}
|
||||
Reference in New Issue
Block a user