use std::path::PathBuf;
use std::process::{Command, Output};
mod pglib;
fn fixture(name: &str) -> PathBuf {
pglib::path(name, ".json")
}
fn run(args: &[&str]) -> Output {
Command::new(env!("CARGO_BIN_EXE_gridoxide"))
.args(args)
.output()
.expect("failed to run the gridoxide binary")
}
fn opf(case: &str, extra: &[&str]) -> Output {
let path = fixture(case);
let mut args = vec!["opf", path.to_str().unwrap()];
args.extend_from_slice(extra);
run(&args)
}
fn stdout_of(out: &Output) -> String {
assert!(
out.status.success(),
"{}",
String::from_utf8_lossy(&out.stderr)
);
String::from_utf8_lossy(&out.stdout).into_owned()
}
#[test]
fn the_companion_document_is_found_by_convention() {
let text = stdout_of(&opf("pglib_opf_case5_pjm", &[]));
assert!(text.contains("total cost:"), "{text}");
assert!(text.contains("dispatch (MW):"), "{text}");
assert!(text.contains("locational marginal price"), "{text}");
}
#[test]
fn a_congested_case_reports_the_spread_and_what_binds() {
let text = stdout_of(&opf("pglib_opf_case5_pjm", &[]));
assert!(text.contains("spread:"), "{text}");
assert!(text.contains("binding branch limits:"), "{text}");
assert!(text.contains("to relieve"), "{text}");
assert!(text.contains("total cost: 17479."), "{text}");
}
#[test]
fn an_uncongested_case_says_the_price_is_uniform() {
let text = stdout_of(&opf("pglib_opf_case14_ieee", &[]));
assert!(text.contains("uniform at"), "{text}");
assert!(text.contains("no binding branch limits"), "{text}");
}
#[test]
fn generators_at_a_limit_are_marked() {
let text = stdout_of(&opf("pglib_opf_case5_pjm", &[]));
assert!(text.contains("(at max)"), "{text}");
}
#[test]
fn the_companion_document_can_be_given_explicitly() {
let data = pglib::path("pglib_opf_case5_pjm", ".opf.json");
let explicit = stdout_of(&opf(
"pglib_opf_case5_pjm",
&["--data", data.to_str().unwrap()],
));
let implicit = stdout_of(&opf("pglib_opf_case5_pjm", &[]));
assert_eq!(explicit, implicit);
}
#[test]
fn shedding_can_be_switched_off() {
let text = stdout_of(&opf("pglib_opf_case5_pjm", &["--no-shedding"]));
assert!(text.contains("total cost: 17479."), "{text}");
}
#[test]
fn the_shed_price_is_configurable_and_validated() {
let text = stdout_of(&opf("pglib_opf_case5_pjm", &["--shed-price", "5000"]));
assert!(text.contains("total cost:"), "{text}");
let out = opf("pglib_opf_case5_pjm", &["--shed-price", "cheap"]);
assert!(!out.status.success());
assert!(String::from_utf8_lossy(&out.stderr).contains("--shed-price"));
}
#[test]
fn a_missing_companion_document_explains_itself() {
let out = opf("pglib_opf_case5_pjm", &["--data", "/nonexistent/opf.json"]);
assert!(!out.status.success());
let err = String::from_utf8_lossy(&out.stderr);
assert!(err.contains("--data"), "{err}");
}
#[test]
fn needs_a_network_path() {
let out = run(&["opf"]);
assert!(!out.status.success());
assert!(String::from_utf8_lossy(&out.stderr).contains("needs a network path"));
}