use std::path::{Path, PathBuf};
use std::process::{Command, Output};
fn blue() -> Command {
let mut c = Command::new(env!("CARGO_BIN_EXE_blue"));
c.env_remove("BLUE_PATH")
.env_remove("BLUE_CONFIG")
.env_remove("BLUE_TIER")
.env_remove("BLUE_LANG");
c
}
fn scratch(name: &str) -> PathBuf {
let dir = std::env::temp_dir().join(format!("blue-query-{name}-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).expect("scratch dir");
dir.canonicalize().expect("canonical scratch dir")
}
fn put(path: &Path, text: &str) {
std::fs::create_dir_all(path.parent().unwrap()).expect("mkdir");
std::fs::write(path, text).expect("write fixture");
}
fn out(o: &Output) -> String {
String::from_utf8_lossy(&o.stdout).into_owned()
}
fn err(o: &Output) -> String {
String::from_utf8_lossy(&o.stderr).into_owned()
}
const PROGRAM: &str = "def load(path)\n read_file(path)\nend\n\n\
def boot()\n load(\"/etc/app\")\nend\n\n\
def pure(x)\n x + 1\nend\n";
#[test]
fn a_query_named_in_the_bluefile_runs_by_its_name() {
let dir = scratch("named");
put(
&dir.join("Bluefile"),
"package(\"app\", \"0.1.0\")\nquery(\"touches_disk\", \"(reaches filesystem) (unannotated)\")\n",
);
put(&dir.join("src/app.b"), PROGRAM);
let o = blue()
.current_dir(dir.join("src"))
.args(["query", "touches_disk", "app.b"])
.output()
.expect("blue runs");
assert!(o.status.success(), "{}", err(&o));
assert_eq!(
out(&o),
"app.b:1:1: load filesystem via read_file\n\
app.b:5:1: boot filesystem via %root/load -> read_file\n"
);
let o = blue()
.current_dir(&dir)
.args(["query", "nothing", "src"])
.output()
.expect("blue runs");
assert!(!o.status.success());
assert!(
err(&o).contains("declares no query `nothing`; it declares: touches_disk"),
"{}",
err(&o)
);
}
#[test]
fn json_is_one_object_per_hit_with_the_bindings() {
let dir = scratch("json");
put(&dir.join("app.b"), PROGRAM);
let o = blue()
.current_dir(&dir)
.args(["query", "--json", "(match `(%root/load ,path))", "app.b"])
.output()
.expect("blue runs");
assert!(o.status.success(), "{}", err(&o));
let lines: Vec<serde_json::Value> = out(&o)
.lines()
.map(|l| serde_json::from_str(l).expect("a JSON line"))
.collect();
assert_eq!(
lines,
[serde_json::json!({
"file": "app.b",
"line": 6,
"column": 3,
"definition": "boot",
"key": "%root/boot",
"kind": "function",
"bindings": {"path": "\"/etc/app\""},
"reach": {"filesystem": ["%root/load", "read_file"]},
})]
);
}
#[test]
fn a_file_that_cannot_be_resolved_is_named_and_fails_the_run() {
let dir = scratch("skipped");
put(&dir.join("app.b"), PROGRAM);
put(
&dir.join("broken.b"),
"use(\"no_such_bidama\")\n\ndef f()\n 1\nend\n",
);
let o = blue()
.current_dir(&dir)
.args(["query", "(kind function)", "."])
.output()
.expect("blue runs");
assert!(!o.status.success());
assert!(
err(&o).contains("broken.b: skipped, its imports do not load"),
"{}",
err(&o)
);
assert!(
err(&o).contains("3 hit(s) in 1 file(s), 1 skipped"),
"{}",
err(&o)
);
}
#[test]
fn which_bidama_definitions_reach_the_filesystem() {
let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
let o = blue()
.current_dir(&root)
.args(["query", "--json", "(reaches filesystem)", "bidamas"])
.output()
.expect("blue runs");
assert!(o.status.success(), "{}", err(&o));
let hits: Vec<serde_json::Value> = out(&o)
.lines()
.map(|l| serde_json::from_str(l).expect("a JSON line"))
.collect();
assert!(!hits.is_empty());
for h in &hits {
let via = h["reach"]["filesystem"].as_array().expect("a chain");
let primitive = via.last().and_then(|p| p.as_str()).expect("a primitive");
assert!(
blue_lang_waku::Capability::FileSystem.grants(primitive),
"{h}"
);
}
}