use indoc::indoc;
use liwe::model::config::{Configuration, LibraryOptions, MarkdownOptions};
use std::fs::{create_dir_all, read_to_string, write};
use std::process::Command;
use tempfile::TempDir;
mod common;
#[test]
fn test_inline_list_references() {
let temp_dir = setup_workspace_with_docs(vec
[Database](db)
"}),
("arch", "# Architecture"),
("db", "# Database"),
]);
let temp_path = temp_dir.path();
let output = run_inline_command(temp_path, &["index", "--list"]);
assert!(output.status.success());
let stdout = String::from_utf8(output.stdout).unwrap();
assert_eq!(
stdout,
indoc! {"
1: [Architecture](arch)
2: [Database](db)
"}
);
}
#[test]
fn test_inline_by_reference() {
let temp_dir = setup_workspace_with_docs(vec
"}),
("arch", indoc! {"
# Architecture
Architecture content here.
"}),
]);
let temp_path = temp_dir.path();
let output = run_inline_command(temp_path, &["index", "--reference", "arch"]);
assert!(output.status.success());
assert!(!temp_path.join("arch.md").exists(), "Target file should be deleted");
let index_content = read_to_string(temp_path.join("index.md")).unwrap();
assert_eq!(
index_content,
indoc! {"
# Index
## Architecture
Architecture content here.
"}
);
}
#[test]
fn test_inline_by_block_number() {
let temp_dir = setup_workspace_with_docs(vec
[Second](second)
"}),
("first", "# First\n\nFirst content"),
("second", "# Second\n\nSecond content"),
]);
let temp_path = temp_dir.path();
let output = run_inline_command(temp_path, &["index", "--block", "1"]);
assert!(output.status.success());
assert!(!temp_path.join("first.md").exists(), "First file should be deleted");
assert!(temp_path.join("second.md").exists(), "Second file should remain");
let index_content = read_to_string(temp_path.join("index.md")).unwrap();
assert_eq!(
index_content,
indoc! {"
# Index
[Second](second)
## First
First content
"}
);
}
#[test]
fn test_inline_keep_target() {
let temp_dir = setup_workspace_with_docs(vec
"}),
("arch", indoc! {"
# Architecture
Architecture content.
"}),
]);
let temp_path = temp_dir.path();
let output = run_inline_command(temp_path, &["index", "--reference", "arch", "--keep-target"]);
assert!(output.status.success());
assert!(temp_path.join("arch.md").exists(), "Target file should be kept");
let index_content = read_to_string(temp_path.join("index.md")).unwrap();
assert_eq!(
index_content,
indoc! {"
# Index
## Architecture
Architecture content.
"}
);
}
#[test]
fn test_inline_as_quote() {
let temp_dir = setup_workspace_with_docs(vec
"}),
("quote", indoc! {"
Quote paragraph 1
Quote paragraph 2
"}),
]);
let temp_path = temp_dir.path();
let output = run_inline_command(temp_path, &["index", "--reference", "quote", "--as-quote"]);
assert!(output.status.success());
let index_content = read_to_string(temp_path.join("index.md")).unwrap();
assert_eq!(
index_content,
indoc! {"
# Index
> Quote paragraph 1
>
> Quote paragraph 2
"}
);
}
#[test]
fn test_inline_cleans_other_references() {
let temp_dir = setup_workspace_with_docs(vec
"}),
("other", indoc! {"
# Other
[Also refs target](target)
"}),
("target", "# Target\n\nContent"),
]);
let temp_path = temp_dir.path();
let output = run_inline_command(temp_path, &["index", "--reference", "target"]);
assert!(output.status.success());
let other_content = read_to_string(temp_path.join("other.md")).unwrap();
assert_eq!(other_content, "# Other\n");
}
#[test]
fn test_inline_ambiguous_reference_fails() {
let temp_dir = setup_workspace_with_docs(vec
[Notes B](notes-b)
"}),
("notes-a", "# Notes A"),
("notes-b", "# Notes B"),
]);
let temp_path = temp_dir.path();
let output = run_inline_command(temp_path, &["index", "--reference", "notes"]);
assert!(!output.status.success());
let stderr = String::from_utf8(output.stderr).unwrap();
assert_eq!(
stderr,
indoc! {"
Error: Multiple references match 'notes':
1: [Notes A](notes-a)
2: [Notes B](notes-b)
Use --block <n> to select a specific reference.
"}
);
}
#[test]
fn test_inline_nonexistent_key() {
let temp_dir = setup_workspace_with_docs(vec![
("a", "# Doc A"),
]);
let temp_path = temp_dir.path();
let output = run_inline_command(temp_path, &["nonexistent", "--block", "1"]);
assert!(!output.status.success());
let stderr = String::from_utf8(output.stderr).unwrap();
assert_eq!(stderr, "Error: Document 'nonexistent' not found\n");
}
#[test]
fn test_inline_reference_not_found() {
let temp_dir = setup_workspace_with_docs(vec
"}),
("target", "# Target"),
]);
let temp_path = temp_dir.path();
let output = run_inline_command(temp_path, &["index", "--reference", "nonexistent"]);
assert!(!output.status.success());
let stderr = String::from_utf8(output.stderr).unwrap();
assert_eq!(stderr, "Error: No reference matches 'nonexistent'\n");
}
#[test]
fn test_inline_block_out_of_range() {
let temp_dir = setup_workspace_with_docs(vec
"}),
("target", "# Target"),
]);
let temp_path = temp_dir.path();
let output = run_inline_command(temp_path, &["index", "--block", "10"]);
assert!(!output.status.success());
let stderr = String::from_utf8(output.stderr).unwrap();
assert_eq!(stderr, "Error: Block number 10 out of range (1-1)\n");
}
#[test]
fn test_inline_dry_run() {
let temp_dir = setup_workspace_with_docs(vec
"}),
("target", "# Target\n\nContent"),
]);
let temp_path = temp_dir.path();
let original_content = read_to_string(temp_path.join("index.md")).unwrap();
let output = run_inline_command(temp_path, &["index", "--reference", "target", "--dry-run"]);
assert!(output.status.success());
let after_content = read_to_string(temp_path.join("index.md")).unwrap();
assert_eq!(original_content, after_content, "File should not be modified");
assert!(temp_path.join("target.md").exists(), "Target should still exist");
}
#[test]
fn test_inline_keys_output() {
let temp_dir = setup_workspace_with_docs(vec"),
("target", "# Target"),
]);
let temp_path = temp_dir.path();
let output = run_inline_command(temp_path, &["index", "--reference", "target", "--keys", "--dry-run"]);
let stderr = String::from_utf8(output.stderr.clone()).unwrap();
assert!(output.status.success(), "Command failed: {}", stderr);
let stdout = String::from_utf8(output.stdout).unwrap();
assert_eq!(stdout, "index\ntarget\n");
}
#[test]
fn test_inline_quiet_mode() {
let temp_dir = setup_workspace_with_docs(vec"),
("target", "# Target"),
]);
let temp_path = temp_dir.path();
let output = run_inline_command(temp_path, &["index", "--reference", "target", "--quiet"]);
let stderr = String::from_utf8(output.stderr.clone()).unwrap();
assert!(output.status.success(), "Command failed: {}", stderr);
let stdout = String::from_utf8(output.stdout).unwrap();
assert!(stdout.trim().is_empty(), "Quiet mode should suppress output");
}
#[test]
fn test_inline_no_references() {
let temp_dir = setup_workspace_with_docs(vec![
("index", indoc! {"
# Index
Just regular content, no references.
"}),
]);
let temp_path = temp_dir.path();
let output = run_inline_command(temp_path, &["index", "--list"]);
assert!(output.status.success());
let stdout = String::from_utf8(output.stdout).unwrap();
assert!(stdout.trim().is_empty(), "Should have no references to list");
}
fn setup_workspace_with_docs(docs: Vec<(&str, &str)>) -> TempDir {
let temp_dir = TempDir::new().expect("Failed to create temp directory");
let temp_path = temp_dir.path();
setup_iwe_config(temp_path);
for (key, content) in docs {
write(temp_path.join(format!("{}.md", key)), content).expect("Should write file");
}
temp_dir
}
fn setup_iwe_config(temp_path: &std::path::Path) {
create_dir_all(temp_path.join(".iwe")).expect("Failed to create .iwe directory");
let config = Configuration {
library: LibraryOptions {
path: "".to_string(),
..Default::default()
},
markdown: MarkdownOptions {
refs_extension: "".to_string(),
..Default::default()
},
..Default::default()
};
let config_content = toml::to_string(&config).expect("Failed to serialize config");
write(temp_path.join(".iwe").join("config.toml"), config_content).expect("Should write config");
}
fn run_inline_command(work_dir: &std::path::Path, args: &[&str]) -> std::process::Output {
let mut command = Command::new(common::get_iwe_binary_path());
command.arg("inline").current_dir(work_dir);
for arg in args {
command.arg(arg);
}
command.output().expect("Failed to execute iwe inline")
}