use crate::error::Result;
use crate::storage::Store;
pub fn run(store: &Store, a: &str, b: &str) -> Result<()> {
let (issue_a, issue_b) = store.link(a, b)?;
println!("Linked #{} ↔ #{}", issue_a.number, issue_b.number);
Ok(())
}
pub fn unlink_cli(store: &Store, a: &str, b: &str) -> Result<()> {
let (issue_a, issue_b) = store.unlink(a, b)?;
println!("Unlinked #{} ↔ #{}", issue_a.number, issue_b.number);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::commands::create::create;
use crate::model::Issue;
use crate::storage::IssueSelector;
use std::fs;
use tempfile::TempDir;
fn make_store() -> (TempDir, Store) {
let tmp = TempDir::new().unwrap();
let dir = tmp.path().join("issues");
fs::create_dir_all(&dir).unwrap();
let config_path = tmp.path().join("tix.toml");
let config = crate::config::Config {
issues_dir: dir.clone(),
..crate::config::Config::default()
};
config.save(&config_path).unwrap();
let config = crate::config::Config::load_with_warning(&config_path, |_| {}).unwrap();
(
tmp,
Store {
issues_dir: dir,
config,
},
)
}
fn seed(store: &Store, title: &str) -> Issue {
create(
&store.config,
store,
title,
Some("body"),
None,
None,
Some(""),
None,
None,
None,
None,
)
.unwrap()
}
#[test]
fn link_cli_links_two_issues_by_number() {
let (tmp, store) = make_store();
let _ = tmp;
let a = seed(&store, "A");
let b = seed(&store, "B");
run(&store, &a.number.to_string(), &b.number.to_string()).unwrap();
let reread_a = store.get(IssueSelector::Number(a.number)).unwrap();
let reread_b = store.get(IssueSelector::Number(b.number)).unwrap();
assert_eq!(reread_a.links, [b.number.to_string()]);
assert_eq!(reread_b.links, [a.number.to_string()]);
}
#[test]
fn link_cli_errors_on_self_link() {
let (tmp, store) = make_store();
let _ = tmp;
let a = seed(&store, "A");
let err = run(&store, &a.number.to_string(), &a.number.to_string()).unwrap_err();
assert!(matches!(err, crate::error::TixError::InvalidInput(_)));
}
#[test]
fn unlink_cli_removes_symmetric_link() {
let (tmp, store) = make_store();
let _ = tmp;
let a = seed(&store, "A");
let b = seed(&store, "B");
run(&store, &a.number.to_string(), &b.number.to_string()).unwrap();
unlink_cli(&store, &a.number.to_string(), &b.number.to_string()).unwrap();
let reread_a = store.get(IssueSelector::Number(a.number)).unwrap();
let reread_b = store.get(IssueSelector::Number(b.number)).unwrap();
assert!(reread_a.links.is_empty());
assert!(reread_b.links.is_empty());
}
#[test]
fn unlink_cli_never_linked_pair_is_noop() {
let (tmp, store) = make_store();
let _ = tmp;
let a = seed(&store, "A");
let b = seed(&store, "B");
unlink_cli(&store, &a.number.to_string(), &b.number.to_string()).unwrap();
let reread_a = store.get(IssueSelector::Number(a.number)).unwrap();
assert!(reread_a.links.is_empty());
}
}