use super::common::{hyalo_no_hints, md, write_md};
use std::fs;
use tempfile::TempDir;
fn setup() -> TempDir {
let tmp = TempDir::new().unwrap();
write_md(
tmp.path(),
"note.md",
md!(r"
---
title: Test Note
status: draft
tags:
- rust
---
# Body
Some content here.
## Tasks
- [ ] First task
- [x] Second task
"),
);
write_md(
tmp.path(),
"other.md",
md!(r"
---
title: Other
---
See [[note]] for details.
"),
);
tmp
}
#[test]
fn read_positional_file() {
let tmp = setup();
let output = hyalo_no_hints()
.args(["--dir", tmp.path().to_str().unwrap()])
.args(["read", "note.md", "--format", "text"])
.output()
.unwrap();
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(stdout.contains("Some content here."));
}
#[test]
fn read_positional_matches_flag() {
let tmp = setup();
let positional = hyalo_no_hints()
.args(["--dir", tmp.path().to_str().unwrap()])
.args(["read", "note.md"])
.output()
.unwrap();
let flag = hyalo_no_hints()
.args(["--dir", tmp.path().to_str().unwrap()])
.args(["read", "--file", "note.md"])
.output()
.unwrap();
assert!(positional.status.success());
assert!(flag.status.success());
assert_eq!(positional.stdout, flag.stdout);
}
#[test]
fn read_positional_and_flag_conflicts() {
let tmp = setup();
let output = hyalo_no_hints()
.args(["--dir", tmp.path().to_str().unwrap()])
.args(["read", "note.md", "--file", "note.md"])
.output()
.unwrap();
assert!(!output.status.success());
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("cannot be used with"),
"expected conflict error, got: {stderr}"
);
}
#[test]
fn read_no_file_at_all_errors() {
let tmp = setup();
let output = hyalo_no_hints()
.args(["--dir", tmp.path().to_str().unwrap()])
.args(["read"])
.output()
.unwrap();
assert!(!output.status.success());
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("required argument missing"),
"expected missing file error, got: {stderr}"
);
}
#[test]
fn find_positional_file_and_flag_conflicts() {
let tmp = setup();
let output = hyalo_no_hints()
.args(["--dir", tmp.path().to_str().unwrap()])
.args(["find", "pattern", "note.md", "--file", "other.md"])
.output()
.unwrap();
assert!(!output.status.success());
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("cannot be used with"),
"expected conflict error, got: {stderr}"
);
}
#[test]
fn backlinks_positional_file() {
let tmp = setup();
let output = hyalo_no_hints()
.args(["--dir", tmp.path().to_str().unwrap()])
.args(["backlinks", "note.md"])
.output()
.unwrap();
assert!(output.status.success());
let envelope: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
assert_eq!(envelope["total"], 1);
}
#[test]
fn mv_positional_file() {
let tmp = setup();
let output = hyalo_no_hints()
.args(["--dir", tmp.path().to_str().unwrap()])
.args(["mv", "note.md", "--to", "archive/note.md"])
.output()
.unwrap();
assert!(
output.status.success(),
"stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
let envelope: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
assert_eq!(envelope["results"]["from"], "note.md");
assert_eq!(envelope["results"]["to"], "archive/note.md");
assert!(tmp.path().join("archive/note.md").exists());
assert!(!tmp.path().join("note.md").exists());
}
#[test]
fn set_positional_file() {
let tmp = setup();
let output = hyalo_no_hints()
.args(["--dir", tmp.path().to_str().unwrap()])
.args(["set", "note.md", "--property", "priority=5"])
.output()
.unwrap();
assert!(
output.status.success(),
"stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
let content = fs::read_to_string(tmp.path().join("note.md")).unwrap();
assert!(content.contains("priority: 5"));
}
#[test]
fn remove_positional_file() {
let tmp = setup();
let output = hyalo_no_hints()
.args(["--dir", tmp.path().to_str().unwrap()])
.args(["remove", "note.md", "--property", "status"])
.output()
.unwrap();
assert!(
output.status.success(),
"stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
let content = fs::read_to_string(tmp.path().join("note.md")).unwrap();
assert!(!content.contains("status:"));
}
#[test]
fn append_positional_file() {
let tmp = setup();
let output = hyalo_no_hints()
.args(["--dir", tmp.path().to_str().unwrap()])
.args(["append", "note.md", "--property", "tags=cli"])
.output()
.unwrap();
assert!(
output.status.success(),
"stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
let content = fs::read_to_string(tmp.path().join("note.md")).unwrap();
assert!(content.contains("cli"));
assert!(content.contains("rust"));
}
#[test]
fn find_positional_pattern_and_file() {
let tmp = setup();
let output = hyalo_no_hints()
.args(["--dir", tmp.path().to_str().unwrap()])
.args(["find", "content", "note.md"])
.output()
.unwrap();
assert!(output.status.success());
let envelope: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
assert_eq!(envelope["total"], 1);
assert_eq!(envelope["results"][0]["file"], "note.md");
}
#[test]
fn find_positional_file_only_no_pattern() {
let tmp = setup();
let output = hyalo_no_hints()
.args(["--dir", tmp.path().to_str().unwrap()])
.args(["find", "--file", "note.md"])
.output()
.unwrap();
assert!(output.status.success());
let envelope: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
assert_eq!(envelope["total"], 1);
}
#[test]
fn task_read_positional_file() {
let tmp = setup();
let output = hyalo_no_hints()
.args(["--dir", tmp.path().to_str().unwrap()])
.args(["task", "read", "note.md", "--all"])
.output()
.unwrap();
assert!(
output.status.success(),
"stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
let envelope: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
let results = envelope["results"].as_array().unwrap();
assert_eq!(results.len(), 2);
}
#[test]
fn task_toggle_positional_file() {
let tmp = setup();
let output = hyalo_no_hints()
.args(["--dir", tmp.path().to_str().unwrap()])
.args(["task", "toggle", "note.md", "--line", "13"])
.output()
.unwrap();
assert!(
output.status.success(),
"stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
let content = fs::read_to_string(tmp.path().join("note.md")).unwrap();
assert!(content.contains("- [x] First task"));
}
#[test]
fn task_set_status_positional_file() {
let tmp = setup();
let output = hyalo_no_hints()
.args(["--dir", tmp.path().to_str().unwrap()])
.args(["task", "set", "note.md", "--line", "13", "--status", "?"])
.output()
.unwrap();
assert!(
output.status.success(),
"stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
let content = fs::read_to_string(tmp.path().join("note.md")).unwrap();
assert!(content.contains("- [?] First task"));
}
#[test]
fn find_two_positionals_first_is_pattern() {
let tmp = setup();
let output = hyalo_no_hints()
.args(["--dir", tmp.path().to_str().unwrap()])
.args(["find", "xyz_notpresent_anywhere", "other.md"])
.output()
.unwrap();
assert!(output.status.success());
let envelope: serde_json::Value = serde_json::from_slice(&output.stdout).unwrap();
assert_eq!(
envelope["total"], 0,
"pattern not found in scoped file: {envelope}"
);
let output2 = hyalo_no_hints()
.args(["--dir", tmp.path().to_str().unwrap()])
.args(["find", "note", "other.md"])
.output()
.unwrap();
assert!(output2.status.success());
let envelope2: serde_json::Value = serde_json::from_slice(&output2.stdout).unwrap();
assert_eq!(
envelope2["total"], 1,
"note found in other.md (file-scoped search): {envelope2}"
);
let results = envelope2["results"].as_array().unwrap();
assert!(results[0]["file"].as_str().unwrap().ends_with("other.md"));
}
#[test]
fn set_multiple_positional_files() {
let tmp = setup();
let output = hyalo_no_hints()
.args(["--dir", tmp.path().to_str().unwrap()])
.args(["set", "note.md", "other.md", "--property", "reviewed=true"])
.output()
.unwrap();
assert!(
output.status.success(),
"stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
let note = fs::read_to_string(tmp.path().join("note.md")).unwrap();
let other = fs::read_to_string(tmp.path().join("other.md")).unwrap();
assert!(
note.contains("reviewed: true"),
"note.md not updated: {note}"
);
assert!(
other.contains("reviewed: true"),
"other.md not updated: {other}"
);
}
#[test]
fn set_positional_conflicts_with_file_flag() {
let tmp = setup();
let output = hyalo_no_hints()
.args(["--dir", tmp.path().to_str().unwrap()])
.args(["set", "note.md", "--file", "other.md", "--property", "k=v"])
.output()
.unwrap();
assert!(!output.status.success());
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("cannot be used with"),
"expected conflict error, got: {stderr}"
);
}