use indoc::indoc;
use liwe::model::config::{Configuration, LibraryOptions, MarkdownOptions};
use std::fs::{create_dir_all, write};
use std::process::Command;
use tempfile::TempDir;
#[test]
fn test_schema_markdown_output() {
let temp_dir = setup_workspace();
let output = run_schema(&temp_dir, &[]);
assert!(output.status.success(), "Command should succeed");
let stdout = String::from_utf8(output.stdout).expect("Valid UTF-8 output");
let expected = indoc! {"
| Field | Types | Coverage | Distinct | Values |
| ------ | ------------------------ | -------- | -------- | --- |
| status | string (100%) | 3 (100%) | 2 | draft (2), published (1) |
| type | string (100%) | 3 (100%) | 3 | external (1), hub (1), post (1) |
| url | null (50%), string (50%) | 2 (67%) | 1 | null (1) |
"};
assert_eq!(stdout, expected);
}
#[test]
fn test_schema_json_output() {
let temp_dir = setup_workspace();
let output = run_schema(&temp_dir, &["-f", "json"]);
assert!(output.status.success(), "Command should succeed");
let stdout = String::from_utf8(output.stdout).expect("Valid UTF-8 output");
let parsed: serde_json::Value = serde_json::from_str(&stdout).expect("Valid JSON");
let fields = parsed.as_array().expect("top-level is array");
assert_eq!(fields.len(), 3);
let type_field = fields.iter().find(|f| f["field"] == "type").expect("type field");
assert_eq!(type_field["coverage"]["count"], 3);
assert_eq!(type_field["coverage"]["percentage"], 100.0);
assert_eq!(type_field["types"][0]["type"], "string");
}
#[test]
fn test_schema_yaml_output() {
let temp_dir = setup_workspace();
let output = run_schema(&temp_dir, &["-f", "yaml"]);
assert!(output.status.success(), "Command should succeed");
let stdout = String::from_utf8(output.stdout).expect("Valid UTF-8 output");
let parsed: serde_yaml::Value = serde_yaml::from_str(&stdout).expect("Valid YAML");
let fields = parsed.as_sequence().expect("top-level is sequence");
assert_eq!(fields.len(), 3);
}
#[test]
fn test_schema_with_filter() {
let temp_dir = setup_workspace();
let output = run_schema(&temp_dir, &["--filter", "type: post"]);
assert!(output.status.success(), "Command should succeed");
let stdout = String::from_utf8(output.stdout).expect("Valid UTF-8 output");
let expected = indoc! {"
| Field | Types | Coverage | Distinct | Values |
| ------ | ------------- | -------- | -------- | --- |
| status | string (100%) | 1 (100%) | 1 | published (1) |
| type | string (100%) | 1 (100%) | 1 | post (1) |
| url | string (100%) | 1 (100%) | 0 | --- |
"};
assert_eq!(stdout, expected);
}
#[test]
fn test_schema_field_drilldown() {
let temp_dir = setup_workspace();
let output = run_schema(&temp_dir, &["--field", "status", "-f", "json"]);
assert!(output.status.success(), "Command should succeed");
let stdout = String::from_utf8(output.stdout).expect("Valid UTF-8 output");
let parsed: serde_json::Value = serde_json::from_str(&stdout).expect("Valid JSON");
let fields = parsed.as_array().expect("top-level is array");
assert_eq!(fields.len(), 1);
assert_eq!(fields[0]["field"], "status");
}
#[test]
fn test_schema_nested_fields() {
let temp_dir = setup_workspace_with_nested();
let output = run_schema(&temp_dir, &["-f", "json"]);
assert!(output.status.success(), "Command should succeed");
let stdout = String::from_utf8(output.stdout).expect("Valid UTF-8 output");
let parsed: serde_json::Value = serde_json::from_str(&stdout).expect("Valid JSON");
let fields = parsed.as_array().expect("top-level is array");
let names: Vec<&str> = fields.iter().map(|f| f["field"].as_str().unwrap()).collect();
assert!(names.contains(&"engagement"));
assert!(names.contains(&"engagement.upvotes"));
assert!(names.contains(&"engagement.comments"));
let engagement = fields.iter().find(|f| f["field"] == "engagement").unwrap();
assert_eq!(engagement["types"][0]["type"], "object");
}
#[test]
fn test_schema_field_drilldown_nested() {
let temp_dir = setup_workspace_with_nested();
let output = run_schema(&temp_dir, &["--field", "engagement", "-f", "json"]);
assert!(output.status.success(), "Command should succeed");
let stdout = String::from_utf8(output.stdout).expect("Valid UTF-8 output");
let parsed: serde_json::Value = serde_json::from_str(&stdout).expect("Valid JSON");
let fields = parsed.as_array().expect("top-level is array");
let names: Vec<&str> = fields.iter().map(|f| f["field"].as_str().unwrap()).collect();
assert_eq!(names, vec!["engagement", "engagement.comments", "engagement.upvotes"]);
}
#[test]
fn test_schema_empty_workspace() {
let temp_dir = TempDir::new().expect("Failed to create temp directory");
let temp_path = temp_dir.path();
create_dir_all(temp_path.join(".iwe")).unwrap();
write_config(temp_path);
let output = run_schema(&temp_dir, &["-f", "json"]);
assert!(output.status.success(), "Command should succeed");
let stdout = String::from_utf8(output.stdout).expect("Valid UTF-8 output");
let parsed: serde_json::Value = serde_json::from_str(&stdout).expect("Valid JSON");
let fields = parsed.as_array().expect("top-level is array");
assert_eq!(fields.len(), 0);
}
fn setup_workspace() -> TempDir {
let temp_dir = TempDir::new().expect("Failed to create temp directory");
let temp_path = temp_dir.path();
create_dir_all(temp_path.join(".iwe")).unwrap();
write_config(temp_path);
write(
temp_path.join("post1.md"),
indoc! {"
---
type: post
status: published
url: https://example.com
---
# Post One
"},
)
.unwrap();
write(
temp_path.join("hub1.md"),
indoc! {"
---
type: hub
status: draft
---
# Hub
"},
)
.unwrap();
write(
temp_path.join("ext1.md"),
indoc! {"
---
type: external
status: draft
url: null
---
# External
"},
)
.unwrap();
temp_dir
}
fn setup_workspace_with_nested() -> TempDir {
let temp_dir = TempDir::new().expect("Failed to create temp directory");
let temp_path = temp_dir.path();
create_dir_all(temp_path.join(".iwe")).unwrap();
write_config(temp_path);
write(
temp_path.join("doc1.md"),
indoc! {"
---
type: post
engagement:
upvotes: 10
comments: 5
---
# Doc One
"},
)
.unwrap();
write(
temp_path.join("doc2.md"),
indoc! {"
---
type: post
engagement:
upvotes: null
comments: 3
---
# Doc Two
"},
)
.unwrap();
temp_dir
}
fn write_config(path: &std::path::Path) {
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(path.join(".iwe/config.toml"), config_content).unwrap();
}
fn run_schema(temp_dir: &TempDir, args: &[&str]) -> std::process::Output {
let binary_path = crate::common::get_iwe_binary_path();
let mut cmd = Command::new(binary_path);
cmd.current_dir(temp_dir.path()).arg("schema");
for arg in args {
cmd.arg(arg);
}
cmd.output().expect("Failed to execute schema command")
}