use anyhow::Context;
use chrono::Utc;
use planter_core::{
person::Person,
project::{Project, TimeRelationship},
resources::{Consumable, Material, NonConsumable, Resource},
stakeholders::Stakeholder,
task::Task,
};
#[test]
fn test_project() -> anyhow::Result<()> {
let start_date = Utc::now();
let mut project = Project::builder()
.name("World domination")
.description("My second attempt to conquer the world with a crowbar and a stimpack")
.start_date(start_date)
.build();
let crowbar_id = project.add_task(Task::new("Find a crowbar"));
let stimpack_id = project.add_task(Task::new("Find a stimpack"));
let software_id = project.add_task(Task::new("Open a proprietary software house"));
let prey_id = project.add_task(Task::new("Prey on free software projects"));
let profit_id = project.add_task(Task::new("Profit"));
assert_eq!(project.tasks().count(), 5);
project
.add_subtask(stimpack_id, software_id)
.context("Failed to add subtask")?;
project
.add_subtask(stimpack_id, prey_id)
.context("Failed to add subtask")?;
assert_eq!(project.subtasks(stimpack_id).count(), 2);
project
.add_time_relationship(crowbar_id, stimpack_id, TimeRelationship::StartToFinish)
.context("Tasks don't exist or circular dependencies detected")?;
project
.add_time_relationship(crowbar_id, profit_id, TimeRelationship::StartToFinish)
.context("Tasks don't exist or circular dependencies detected")?;
let mut crowbar_mat = Material::Consumable(Consumable::new("Crowbar"));
crowbar_mat.update_quantity(5);
crowbar_mat.update_cost_per_unit(150);
project.add_resource(Resource::Material(crowbar_mat));
project
.res_into_nonconsumable(0)
.context("Failed to convert consumable into non consumable")?;
if let Resource::Material(ref m) = project.resources()[0] {
assert_eq!(m.name(), "Crowbar");
assert_eq!(m.quantity(), Some(5));
assert_eq!(m.cost_per_unit(), Some(150));
} else {
panic!("Expected Material after conversion");
}
project.add_resource(Resource::Material(Material::NonConsumable(
NonConsumable::new("Stimpack"),
)));
project
.res_into_consumable(0)
.context("Failed to convert non consumable into consumable")?;
if let Resource::Material(ref m) = project.resources()[0] {
assert_eq!(m.name(), "Crowbar");
assert_eq!(m.quantity(), Some(5));
assert_eq!(m.cost_per_unit(), Some(150));
} else {
panic!("Expected Material after conversion");
}
project.add_resource(Resource::Personnel {
person: Person::new("Sebastiano", "Giordano").context("Failed to parse a name.")?,
hourly_rate: None,
});
assert_eq!(project.resources().len(), 3);
let _ = project.rm_resource(1);
assert_eq!(project.resources().len(), 2);
let person = Person::new("Margherita", "Hack").context("Failed to parse a name")?;
project.add_stakeholder(Stakeholder::Individual {
person,
description: Some("She could try to stop me".to_owned()),
});
project.add_stakeholder(Stakeholder::Organization {
name: "Acme".to_owned(),
description: Some("They might decide to buy me more stimpacks".to_owned()),
});
assert_eq!(project.stakeholders().len(), 2);
assert_eq!(project.task_parent(software_id), Some(stimpack_id));
assert!(project.task_parent(crowbar_id).is_none());
project.remove_subtask(software_id)?;
assert!(project.task_parent(software_id).is_none());
assert_eq!(project.subtasks(stimpack_id).count(), 1);
project.add_subtask(stimpack_id, software_id)?;
assert_eq!(project.subtasks(stimpack_id).count(), 2);
let sibling = project.add_sibling_before(Task::new("Sibling before"), profit_id);
let order: Vec<_> = project.tasks().map(|t| t.id()).collect();
assert!(
order.iter().position(|&id| id == sibling).unwrap()
< order.iter().position(|&id| id == profit_id).unwrap()
);
project.move_task_after(profit_id, crowbar_id);
let order: Vec<_> = project.tasks().map(|t| t.id()).collect();
assert_eq!(
order.iter().position(|&id| id == profit_id).unwrap(),
order.iter().position(|&id| id == crowbar_id).unwrap() + 1
);
let now = Utc::now();
project.task_mut(prey_id).unwrap().edit_start(now)?;
project.sync_parent_dates(stimpack_id)?;
assert_eq!(project.task(stimpack_id).unwrap().start(), Some(now));
Ok(())
}