use std::fs;
use std::os::unix::fs::PermissionsExt;
use std::process::{Command, Output};
fn note(home: &tempfile::TempDir) -> Command {
let mut command = Command::new(env!("CARGO_BIN_EXE_note"));
command
.env("NOTE_TO_SELF_HOME", home.path())
.env_remove("NOTE_TO_SELF_PASSWORD")
.env_remove("JRNL2_PASSWORD")
.env_remove("NOTE_TO_SELF_JOURNAL_PASSWORD")
.env_remove("NOTE_TO_SELF_LOCK_PASSWORD");
command
}
fn assert_success(output: Output) -> String {
if !output.status.success() {
panic!(
"command failed\nstatus: {}\nstdout:\n{}\nstderr:\n{}",
output.status,
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr),
);
}
String::from_utf8(output.stdout).expect("stdout should be utf-8")
}
fn empty_editor(home: &tempfile::TempDir) -> std::path::PathBuf {
let path = home.path().join("empty-editor.sh");
fs::write(&path, "#!/usr/bin/env sh\nexit 0\n").unwrap();
let mut permissions = fs::metadata(&path).unwrap().permissions();
permissions.set_mode(0o755);
fs::set_permissions(&path, permissions).unwrap();
path
}
fn sample_png(home: &tempfile::TempDir) -> std::path::PathBuf {
let path = home.path().join("pixel.png");
fs::write(
&path,
[
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48,
0x44, 0x52,
],
)
.unwrap();
path
}
#[test]
fn journal_default_without_name_prints_current_default() {
let home = tempfile::tempdir().unwrap();
assert_success(
note(&home)
.args([
"init",
"--local",
"--username",
"alice",
"--journal",
"work",
])
.output()
.unwrap(),
);
let stdout = assert_success(note(&home).args(["journal", "default"]).output().unwrap());
assert_eq!(stdout.trim(), "work");
}
#[test]
fn empty_editor_for_default_new_note_exits_without_error() {
let home = tempfile::tempdir().unwrap();
assert_success(
note(&home)
.args([
"init",
"--local",
"--username",
"alice",
"--journal",
"personal",
])
.output()
.unwrap(),
);
let editor = empty_editor(&home);
let mut create = note(&home);
create
.env("EDITOR", editor)
.env("NOTE_TO_SELF_PASSWORD", "correct horse");
let stdout = assert_success(create.output().unwrap());
assert_eq!(stdout.trim(), "");
let stdout = assert_success(note(&home).args(["list"]).output().unwrap());
assert_eq!(stdout.trim(), "");
}
#[test]
fn account_password_is_cached_after_first_use() {
let home = tempfile::tempdir().unwrap();
assert_success(
note(&home)
.args([
"init",
"--local",
"--username",
"alice",
"--journal",
"personal",
])
.output()
.unwrap(),
);
let mut create = note(&home);
create.env("NOTE_TO_SELF_PASSWORD", "correct horse");
assert_success(create.args(["cached account key entry"]).output().unwrap());
let stdout = assert_success(note(&home).args(["list"]).output().unwrap());
assert!(stdout.contains("cached account key entry"));
}
#[test]
fn image_upload_creates_entry_with_encrypted_attachment() {
let home = tempfile::tempdir().unwrap();
let image = sample_png(&home);
assert_success(
note(&home)
.args([
"init",
"--local",
"--username",
"alice",
"--journal",
"personal",
])
.output()
.unwrap(),
);
let mut create = note(&home);
create.env("NOTE_TO_SELF_PASSWORD", "correct horse");
let stdout = assert_success(
create
.args(["--img", image.to_str().unwrap(), "trip", "photo"])
.output()
.unwrap(),
);
assert!(stdout.contains("created"));
assert!(stdout.contains("[image: pixel.png"));
let stdout = assert_success(note(&home).args(["list"]).output().unwrap());
assert!(stdout.contains("trip photo"));
assert!(stdout.contains("[image: pixel.png"));
let export = assert_success(note(&home).args(["export"]).output().unwrap());
assert!(export.contains("\"attachments\""));
assert!(export.contains("\"file_name\": \"pixel.png\""));
assert!(export.contains("note-image:"));
}
#[test]
fn todo_text_creates_todo_with_metadata_without_subcommands() {
let home = tempfile::tempdir().unwrap();
assert_success(
note(&home)
.args([
"init",
"--local",
"--username",
"alice",
"--journal",
"personal",
])
.output()
.unwrap(),
);
let mut create = note(&home);
create.env("NOTE_TO_SELF_PASSWORD", "correct horse");
let stdout = assert_success(
create
.args([
"todo",
"--priority",
"high",
"--due",
"2026-06-01",
"renew",
"passport",
"#errand",
])
.output()
.unwrap(),
);
assert!(stdout.contains("created todo"));
let stdout = assert_success(note(&home).args(["search", "passport"]).output().unwrap());
assert!(stdout.contains("[ ]"));
assert!(stdout.contains("p:high"));
assert!(stdout.contains("due:2026-06-01"));
assert!(stdout.contains("renew passport"));
assert!(stdout.contains("#errand"));
}