use std::path::{Path, PathBuf};
use std::process::{Command, Output};
use coding_tools::okf;
use coding_tools::rules::ProbeOutcome;
fn scratch(tag: &str) -> PathBuf {
let dir = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("target/test-tmp/okf")
.join(tag);
std::fs::create_dir_all(&dir).unwrap();
dir
}
fn code(out: &Output) -> i32 {
out.status.code().expect("child exited via a signal")
}
fn stdout(out: &Output) -> String {
String::from_utf8_lossy(&out.stdout).into_owned()
}
fn stderr(out: &Output) -> String {
String::from_utf8_lossy(&out.stderr).into_owned()
}
fn write(path: &Path, body: &str) {
if let Some(dir) = path.parent() {
std::fs::create_dir_all(dir).unwrap();
}
std::fs::write(path, body).unwrap();
}
fn bundle(tag: &str) -> PathBuf {
let dir = scratch(tag);
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(dir.join("tables")).unwrap();
write(
&dir.join("tables/customers.md"),
"---\ntype: BigQuery Table\ntitle: Customers\ntags: [core, pii]\n---\n# Schema\nid, name\n",
);
write(
&dir.join("tables/orders.md"),
"---\ntype: BigQuery Table\ntitle: Orders\ntags: [core]\n---\n# Schema\nid, total\n",
);
write(
&dir.join("index.md"),
"# Index\n\n* [Customers](tables/customers.md)\n",
);
dir
}
fn ct_okf(dir: &Path) -> Command {
let mut c = Command::new(env!("CARGO_BIN_EXE_ct-okf"));
c.arg("--base").arg(dir);
c
}
#[test]
fn builtin_check_holds_for_a_conformant_bundle() {
let dir = bundle("check-holds");
let (o, reason, _) = okf::check(&["--base".into(), ".".into()], &dir, None);
assert_eq!(o, ProbeOutcome::Holds, "{reason}");
assert!(reason.contains("conform"), "{reason:?}");
}
#[test]
fn builtin_check_violated_when_a_concept_lacks_type() {
let dir = bundle("check-notype");
write(
&dir.join("tables/orphan.md"),
"---\ntitle: Orphan\n---\nbody\n",
);
let (o, _reason, report) = okf::check(&["--base".into(), ".".into()], &dir, None);
assert_eq!(o, ProbeOutcome::Violated);
assert!(report.contains("orphan.md"), "{report:?}");
assert!(report.contains("type"), "{report:?}");
}
#[test]
fn builtin_check_strict_flags_broken_links() {
let dir = bundle("check-links");
write(
&dir.join("tables/with_link.md"),
"---\ntype: Note\n---\nsee [gone](/tables/gone.md)\n",
);
assert_eq!(
okf::check(&["--base".into(), ".".into()], &dir, None).0,
ProbeOutcome::Holds
);
let (o, _r, report) = okf::check(
&["--base".into(), ".".into(), "--strict".into()],
&dir,
None,
);
assert_eq!(o, ProbeOutcome::Violated);
assert!(report.contains("gone.md"), "{report:?}");
}
#[test]
fn builtin_check_broken_for_a_missing_base() {
let dir = scratch("check-missing");
let (o, reason, _) = okf::check(&["--base".into(), "nope".into()], &dir, None);
assert_eq!(o, ProbeOutcome::Broken);
assert!(reason.contains("does not exist"), "{reason:?}");
}
#[test]
fn validate_passes_then_fails_on_a_nonconformant_concept() {
let dir = bundle("validate");
let ok = ct_okf(&dir).arg("validate").output().unwrap();
assert_eq!(code(&ok), 0, "stderr: {}", stderr(&ok));
write(&dir.join("tables/orphan.md"), "no frontmatter here\n");
let bad = ct_okf(&dir).arg("validate").output().unwrap();
assert_eq!(code(&bad), 1, "stdout: {}", stdout(&bad));
assert!(stdout(&bad).contains("ERROR"), "{}", stdout(&bad));
}
#[test]
fn list_json_filters_by_type_and_tag() {
let dir = bundle("list");
let out = ct_okf(&dir)
.args(["find", "--okf-tag", "pii", "--json"])
.output()
.unwrap();
assert_eq!(code(&out), 2);
let out = ct_okf(&dir)
.args(["find", "--tag", "pii", "--json"])
.output()
.unwrap();
assert_eq!(code(&out), 0, "stderr: {}", stderr(&out));
let v: serde_json::Value = serde_json::from_str(&stdout(&out)).unwrap();
assert_eq!(v["count"], 1);
assert_eq!(v["concepts"][0]["title"], "Customers");
assert_eq!(v["concepts"][0]["type"], "BigQuery Table");
let out = ct_okf(&dir)
.args(["find", "--type", "BigQuery Table", "--json"])
.output()
.unwrap();
let v: serde_json::Value = serde_json::from_str(&stdout(&out)).unwrap();
assert_eq!(v["count"], 2);
}
#[test]
fn show_reports_one_concepts_frontmatter() {
let dir = bundle("show");
let path = dir.join("tables/customers.md");
let out = ct_okf(&dir)
.args(["show".as_ref(), path.as_os_str(), "--json".as_ref()])
.output()
.unwrap();
assert_eq!(code(&out), 0, "stderr: {}", stderr(&out));
let v: serde_json::Value = serde_json::from_str(&stdout(&out)).unwrap();
assert_eq!(v["type"], "BigQuery Table");
assert_eq!(v["tags"][1], "pii");
}
#[test]
fn links_verdict_is_clean_then_broken() {
let dir = bundle("links");
assert_eq!(code(&ct_okf(&dir).arg("links").output().unwrap()), 0);
write(
&dir.join("tables/bad.md"),
"---\ntype: Note\n---\n[x](/tables/missing.md)\n",
);
let out = ct_okf(&dir).arg("links").output().unwrap();
assert_eq!(code(&out), 1);
assert!(stdout(&out).contains("missing.md"), "{}", stdout(&out));
}
const SCRIPT: &str = "\
#% new file=tables/customers.md type=\"BigQuery Table\" title=Customers
#% description
The customers dimension.
#% tags
core
pii
#% new file=tables/orders.md type=\"BigQuery Table\" title=Orders
#% index base=tables
#% set file=tables/customers.md field=resource value=bq://proj.ds.customers
#% log kind=Creation
#% message
scaffolded customers + orders
";
#[test]
fn script_dry_run_writes_nothing_then_applies_atomically() {
let dir = scratch("script-apply");
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
let script = dir.join("batch.ctb");
write(&script, SCRIPT);
let dry = ct_okf(&dir)
.args(["script".as_ref(), script.as_os_str(), "--dry-run".as_ref()])
.output()
.unwrap();
assert_eq!(code(&dry), 0, "stderr: {}", stderr(&dry));
assert!(stdout(&dry).contains("would new"), "{}", stdout(&dry));
assert!(
!dir.join("tables/customers.md").exists(),
"dry-run wrote a file"
);
let run = ct_okf(&dir)
.args(["script".as_ref(), script.as_os_str()])
.output()
.unwrap();
assert_eq!(code(&run), 0, "stderr: {}", stderr(&run));
let customers = std::fs::read_to_string(dir.join("tables/customers.md")).unwrap();
assert!(
customers.contains("resource: bq://proj.ds.customers"),
"{customers}"
);
assert!(customers.contains("tags: [core, pii]"), "{customers}");
let index = std::fs::read_to_string(dir.join("tables/index.md")).unwrap();
assert!(
index.contains("[Customers](customers.md) - The customers dimension."),
"{index}"
);
assert!(index.contains("[Orders](orders.md)"), "{index}");
let log = std::fs::read_to_string(dir.join("log.md")).unwrap();
assert!(
log.contains("**Creation**: scaffolded customers + orders"),
"{log}"
);
assert_eq!(code(&ct_okf(&dir).arg("validate").output().unwrap()), 0);
}
#[test]
fn script_is_atomic_a_failing_op_writes_nothing() {
let dir = scratch("script-atomic");
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(dir.join("tables")).unwrap();
write(
&dir.join("tables/customers.md"),
"---\ntype: Note\ntitle: Existing\n---\nbody\n",
);
let script = dir.join("batch.ctb");
write(
&script,
"#% new file=tables/fresh.md type=Note title=Fresh\n\
#% new file=tables/customers.md type=Note title=Dup\n\
#% log\n#% message\nshould not run\n",
);
let out = ct_okf(&dir)
.args(["script".as_ref(), script.as_os_str()])
.output()
.unwrap();
assert_eq!(code(&out), 2, "stdout: {}", stdout(&out));
assert!(stderr(&out).contains("already exists"), "{}", stderr(&out));
assert!(
!dir.join("tables/fresh.md").exists(),
"atomic guarantee broken"
);
assert!(!dir.join("log.md").exists(), "atomic guarantee broken");
}
#[test]
fn authoring_new_index_log_set_then_revalidate() {
let dir = bundle("author");
let concept = dir.join("tables/products.md");
let out = ct_okf(&dir)
.args([
"add".as_ref(),
concept.as_os_str(),
"--type".as_ref(),
"BigQuery Table".as_ref(),
"--title".as_ref(),
"Products".as_ref(),
"--tag".as_ref(),
"core".as_ref(),
])
.output()
.unwrap();
assert_eq!(code(&out), 0, "stderr: {}", stderr(&out));
assert!(concept.is_file());
let clob = ct_okf(&dir)
.args([
"add".as_ref(),
concept.as_os_str(),
"--type".as_ref(),
"X".as_ref(),
])
.output()
.unwrap();
assert_eq!(code(&clob), 2);
assert!(stderr(&clob).contains("refusing to overwrite"));
let setc = ct_okf(&dir)
.args([
"set".as_ref(),
"timestamp=2026-06-27".as_ref(),
"--file".as_ref(),
concept.as_os_str(),
])
.output()
.unwrap();
assert_eq!(code(&setc), 0, "stderr: {}", stderr(&setc));
let shown = ct_okf(&dir)
.args(["show".as_ref(), concept.as_os_str()])
.output()
.unwrap();
assert!(stdout(&shown).contains("timestamp: 2026-06-27"));
let tables = dir.join("tables");
let idx = Command::new(env!("CARGO_BIN_EXE_ct-okf"))
.arg("--base")
.arg(&tables)
.arg("gen-index")
.output()
.unwrap();
assert_eq!(code(&idx), 0, "stderr: {}", stderr(&idx));
let index_md = std::fs::read_to_string(tables.join("index.md")).unwrap();
assert!(index_md.contains("[Products](products.md)"), "{index_md}");
let logc = ct_okf(&dir)
.args(["log", "Added products", "--kind", "Creation"])
.output()
.unwrap();
assert_eq!(code(&logc), 0, "stderr: {}", stderr(&logc));
let log_md = std::fs::read_to_string(dir.join("log.md")).unwrap();
assert!(log_md.contains("**Creation**: Added products"), "{log_md}");
assert_eq!(code(&ct_okf(&dir).arg("validate").output().unwrap()), 0);
}
fn project(tag: &str) -> PathBuf {
let dir = scratch(tag);
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(dir.join(".ct")).unwrap();
write(
&dir.join("kb/customers.md"),
"---\ntype: BigQuery Table\ntitle: Customers\ndescription: the customer dimension\ntags: [core, pii]\n---\n# Customers\none row per customer_id.\n",
);
write(
&dir.join("kb/orders.md"),
"---\ntype: BigQuery Table\ntitle: Orders\ndescription: the orders fact table\ntags: [core]\n---\n# Orders\nsee [customers](/kb/customers.md).\n",
);
dir
}
#[test]
fn init_then_search_roots_and_index() {
let dir = project("init-search");
let init = ct_okf(&dir).arg("init").output().unwrap();
assert_eq!(code(&init), 0, "stderr: {}", stderr(&init));
assert!(dir.join(".ct/okf.jsonc").is_file(), "config not written");
assert!(dir.join(".ct/okf").is_dir(), "index dir not created");
let roots = ct_okf(&dir)
.args(["roots", "list", "--json"])
.output()
.unwrap();
let v: serde_json::Value = serde_json::from_str(&stdout(&roots)).unwrap();
assert_eq!(v["roots"][0]["key"], "kb");
let exact = ct_okf(&dir)
.args(["search", "customer", "--json"])
.output()
.unwrap();
assert_eq!(code(&exact), 0, "stderr: {}", stderr(&exact));
let v: serde_json::Value = serde_json::from_str(&stdout(&exact)).unwrap();
assert_eq!(v["count"], 1);
assert_eq!(v["hits"][0]["path"], "kb/customers.md");
assert!(stdout(&ct_okf(&dir).args(["search", "ord*"]).output().unwrap()).contains("orders.md"));
assert!(
stdout(&ct_okf(&dir).args(["search", "custmer~"]).output().unwrap())
.contains("customers.md")
);
let status = ct_okf(&dir)
.args(["index", "status", "--json"])
.output()
.unwrap();
let v: serde_json::Value = serde_json::from_str(&stdout(&status)).unwrap();
assert_eq!(v["roots"], 1);
assert_eq!(v["documents"], 2);
}
#[test]
fn search_lazily_reflects_edits_and_mv_fixes_links() {
let dir = project("lazy-mv");
ct_okf(&dir).arg("init").output().unwrap();
write(
&dir.join("kb/orders.md"),
"---\ntype: BigQuery Table\ntitle: Orders\ndescription: zephyrmark fact table\ntags: [core]\n---\n# Orders\nsee [customers](/kb/customers.md).\n",
);
let lazy = ct_okf(&dir)
.args(["search", "zephyrmark"])
.output()
.unwrap();
assert!(
stdout(&lazy).contains("orders.md"),
"lazy update missed: {}",
stdout(&lazy)
);
let src = dir.join("kb/customers.md");
let dst = dir.join("kb/dims/customers.md");
let mv = ct_okf(&dir)
.args(["mv".as_ref(), src.as_os_str(), dst.as_os_str()])
.output()
.unwrap();
assert_eq!(code(&mv), 0, "stderr: {}", stderr(&mv));
let orders = std::fs::read_to_string(dir.join("kb/orders.md")).unwrap();
assert!(
orders.contains("/kb/dims/customers.md"),
"link not fixed: {orders}"
);
let after = ct_okf(&dir)
.args(["search", "customer_id"])
.output()
.unwrap();
assert!(
stdout(&after).contains("kb/dims/customers.md"),
"{}",
stdout(&after)
);
assert_eq!(
code(&ct_okf(&dir).args(["index", "condense"]).output().unwrap()),
0
);
assert!(
stdout(&ct_okf(&dir).args(["search", "orders"]).output().unwrap()).contains("orders.md")
);
}
#[test]
fn okf_check_records_and_enforces_through_the_store() {
let root = scratch("invariant");
let _ = std::fs::remove_dir_all(&root);
std::fs::create_dir_all(root.join(".ct")).unwrap();
write(
&root.join("bundle/notes.md"),
"---\ntype: Note\ntitle: N\n---\nbody\n",
);
let add = Command::new(env!("CARGO_BIN_EXE_ct-rules"))
.current_dir(&root)
.args([
"--add",
"okf-conformant",
"--question",
"Is the bundle OKF-conformant?",
"--",
"okf",
"--base",
"bundle",
])
.output()
.unwrap();
assert_eq!(code(&add), 0, "stderr: {}", stderr(&add));
let ok = Command::new(env!("CARGO_BIN_EXE_ct-check"))
.current_dir(&root)
.arg("--quiet")
.output()
.unwrap();
assert_eq!(code(&ok), 0, "stderr: {}", stderr(&ok));
write(
&root.join("bundle/orphan.md"),
"---\ntitle: no type\n---\nx\n",
);
let bad = Command::new(env!("CARGO_BIN_EXE_ct-check"))
.current_dir(&root)
.output()
.unwrap();
assert_eq!(code(&bad), 1, "stdout: {}", stdout(&bad));
}
#[test]
fn search_filters_by_okf_tag_and_type() {
let dir = bundle("aware-search");
let out = Command::new(env!("CARGO_BIN_EXE_ct-search"))
.args([
"--base".as_ref(),
dir.as_os_str(),
"--okf-tag".as_ref(),
"pii".as_ref(),
])
.output()
.unwrap();
assert_eq!(code(&out), 0, "stderr: {}", stderr(&out));
let text = stdout(&out);
assert!(text.contains("customers.md"), "{text}");
assert!(!text.contains("orders.md"), "{text}");
let none = Command::new(env!("CARGO_BIN_EXE_ct-search"))
.args([
"--base".as_ref(),
dir.as_os_str(),
"--okf-type".as_ref(),
"Playbook".as_ref(),
])
.output()
.unwrap();
assert_eq!(code(&none), 1);
}
#[test]
fn tree_groups_by_okf_type() {
let dir = bundle("aware-tree");
let out = Command::new(env!("CARGO_BIN_EXE_ct-tree"))
.args([
"--base".as_ref(),
dir.as_os_str(),
"--summary".as_ref(),
"--group".as_ref(),
"okf-type".as_ref(),
])
.output()
.unwrap();
assert_eq!(code(&out), 0, "stderr: {}", stderr(&out));
let text = stdout(&out);
assert!(text.contains("BigQuery Table"), "{text}");
assert!(text.contains("(none)"), "{text}");
}
#[test]
fn view_isolates_frontmatter_both_ways() {
let dir = bundle("aware-view");
let path = dir.join("tables/customers.md");
let fm = Command::new(env!("CARGO_BIN_EXE_ct-view"))
.args([
path.as_os_str(),
"--frontmatter".as_ref(),
"--plain".as_ref(),
])
.output()
.unwrap();
let fm_text = stdout(&fm);
assert!(fm_text.contains("type: BigQuery Table"), "{fm_text}");
assert!(!fm_text.contains("# Schema"), "{fm_text}");
let body = Command::new(env!("CARGO_BIN_EXE_ct-view"))
.args([
path.as_os_str(),
"--no-frontmatter".as_ref(),
"--plain".as_ref(),
])
.output()
.unwrap();
let body_text = stdout(&body);
assert!(body_text.contains("# Schema"), "{body_text}");
assert!(!body_text.contains("type: BigQuery Table"), "{body_text}");
}
#[test]
fn outline_frontmatter_is_opt_in() {
let dir = bundle("aware-outline");
let path = dir.join("tables/customers.md");
let plain = Command::new(env!("CARGO_BIN_EXE_ct-outline"))
.args(["--base".as_ref(), path.as_os_str()])
.output()
.unwrap();
assert!(!stdout(&plain).contains("meta:"), "{}", stdout(&plain));
let meta = Command::new(env!("CARGO_BIN_EXE_ct-outline"))
.args([
"--base".as_ref(),
path.as_os_str(),
"--frontmatter".as_ref(),
"--kind".as_ref(),
"meta:type".as_ref(),
"--flat".as_ref(),
])
.output()
.unwrap();
assert_eq!(code(&meta), 0, "stderr: {}", stderr(&meta));
let text = stdout(&meta);
assert!(text.contains("meta:type:BigQuery Table"), "{text}");
}
#[test]
fn search_without_roots_is_a_clear_error() {
let dir = scratch("no-roots");
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(dir.join(".ct")).unwrap();
let out = ct_okf(&dir).args(["search", "anything"]).output().unwrap();
assert_eq!(code(&out), 2);
assert!(stderr(&out).contains("content roots"), "{}", stderr(&out));
}
#[test]
fn mv_refuses_existing_dst_and_nonfile_src() {
let dir = project("mv-errors");
let a = dir.join("kb/customers.md");
let b = dir.join("kb/orders.md");
let out = ct_okf(&dir)
.args(["mv".as_ref(), a.as_os_str(), b.as_os_str()])
.output()
.unwrap();
assert_eq!(code(&out), 2);
assert!(stderr(&out).contains("already exists"), "{}", stderr(&out));
let missing = dir.join("kb/nope.md");
let dst = dir.join("kb/x.md");
let out = ct_okf(&dir)
.args(["mv".as_ref(), missing.as_os_str(), dst.as_os_str()])
.output()
.unwrap();
assert_eq!(code(&out), 2);
assert!(stderr(&out).contains("not a file"), "{}", stderr(&out));
}
#[test]
fn set_and_show_reject_bad_input() {
let dir = bundle("edge-author");
let concept = dir.join("tables/customers.md");
let out = ct_okf(&dir)
.args([
"set".as_ref(),
"noequals".as_ref(),
"--file".as_ref(),
concept.as_os_str(),
])
.output()
.unwrap();
assert_eq!(code(&out), 2);
assert!(stderr(&out).contains("FIELD=VALUE"), "{}", stderr(&out));
let plain = dir.join("tables/plain.md");
write(&plain, "no frontmatter here\n");
let out = ct_okf(&dir)
.args(["show".as_ref(), plain.as_os_str()])
.output()
.unwrap();
assert_eq!(code(&out), 2);
assert!(stderr(&out).contains("no frontmatter"), "{}", stderr(&out));
}
#[test]
fn roots_add_scan_write_and_rm() {
let dir = project("roots-mgmt");
let add = ct_okf(&dir)
.args(["roots", "add", "kb", "--marker"])
.output()
.unwrap();
assert_eq!(code(&add), 0, "stderr: {}", stderr(&add));
assert!(dir.join("kb/.okf").is_file(), "marker not written");
assert!(
std::fs::read_to_string(dir.join(".ct/okf.jsonc"))
.unwrap()
.contains("kb")
);
let scan = ct_okf(&dir)
.args(["roots", "scan", "--write", "--json"])
.output()
.unwrap();
assert_eq!(code(&scan), 0, "stderr: {}", stderr(&scan));
let v: serde_json::Value = serde_json::from_str(&stdout(&scan)).unwrap();
assert_eq!(v["written"], true);
assert!(v["scanned"].as_array().unwrap().iter().any(|k| k == "kb"));
let rm = ct_okf(&dir).args(["roots", "rm", "kb"]).output().unwrap();
assert_eq!(code(&rm), 0, "stderr: {}", stderr(&rm));
let cfg = std::fs::read_to_string(dir.join(".ct/okf.jsonc")).unwrap();
assert!(!cfg.contains("\"kb\""), "kb still configured: {cfg}");
}
#[test]
fn index_update_and_rebuild_reflect_disk() {
let dir = project("index-mgmt");
ct_okf(&dir).arg("init").output().unwrap();
write(
&dir.join("kb/products.md"),
"---\ntype: Table\ntitle: Products\n---\n# Products\nwidget catalog.\n",
);
let upd = ct_okf(&dir)
.args(["index", "update", "--json"])
.output()
.unwrap();
let v: serde_json::Value = serde_json::from_str(&stdout(&upd)).unwrap();
assert_eq!(v["added"], 1, "{}", stdout(&upd));
let reb = ct_okf(&dir).args(["index", "rebuild"]).output().unwrap();
assert_eq!(code(&reb), 0, "stderr: {}", stderr(&reb));
let status = ct_okf(&dir)
.args(["index", "status", "--json"])
.output()
.unwrap();
let v: serde_json::Value = serde_json::from_str(&stdout(&status)).unwrap();
assert_eq!(v["documents"], 3);
assert_eq!(v["segments"], 1, "rebuild should collapse to one segment");
}
#[test]
fn gen_index_scaffold_writes_okf_version() {
let dir = scratch("scaffold");
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
let out = ct_okf(&dir)
.args(["gen-index", "--scaffold"])
.output()
.unwrap();
assert_eq!(code(&out), 0, "stderr: {}", stderr(&out));
assert!(
std::fs::read_to_string(dir.join("index.md"))
.unwrap()
.contains("okf_version"),
"scaffold missing okf_version"
);
}
#[test]
fn aliases_new_and_rename_work() {
let dir = project("aliases");
let concept = dir.join("kb/products.md");
let out = ct_okf(&dir)
.args([
"new".as_ref(),
concept.as_os_str(),
"--type".as_ref(),
"Table".as_ref(),
])
.output()
.unwrap();
assert_eq!(code(&out), 0, "stderr: {}", stderr(&out));
assert!(concept.is_file());
let dst = dir.join("kb/catalog/products.md");
let out = ct_okf(&dir)
.args(["rename".as_ref(), concept.as_os_str(), dst.as_os_str()])
.output()
.unwrap();
assert_eq!(code(&out), 0, "stderr: {}", stderr(&out));
assert!(dst.is_file() && !concept.exists());
}
#[test]
fn search_limit_regex_and_tag_filter_via_cli() {
let dir = project("search-modes");
ct_okf(&dir).arg("init").output().unwrap();
let lim = ct_okf(&dir)
.args(["search", "customers", "--limit", "1", "--json"])
.output()
.unwrap();
let v: serde_json::Value = serde_json::from_str(&stdout(&lim)).unwrap();
assert_eq!(v["count"], 1);
let rx = ct_okf(&dir)
.args(["search", "/.*dimension.*/", "--json"])
.output()
.unwrap();
let v: serde_json::Value = serde_json::from_str(&stdout(&rx)).unwrap();
assert_eq!(v["count"], 1);
assert_eq!(v["hits"][0]["path"], "kb/customers.md");
let tag = ct_okf(&dir)
.args(["search", "customers", "--tag", "pii", "--json"])
.output()
.unwrap();
let v: serde_json::Value = serde_json::from_str(&stdout(&tag)).unwrap();
assert_eq!(v["count"], 1);
assert_eq!(v["hits"][0]["path"], "kb/customers.md");
}