use super::*;
use crate::standards_review_mock::{Answer, Mock, http_reply};
use crate::standards_review_input::CODE_SUFFIXES;
use crate::test_support::{fixture, remove};
use std::cell::Cell;
use std::time::Instant;
const ELAPSED_MS: u64 = 1250;
const START_MS: u64 = 1_791_344_041_123;
const SHORT: Duration = Duration::from_millis(400);
fn clock() -> impl Fn() -> u64 {
let readings: Cell<u64> = Cell::new(0);
return move || {
let count: u64 = readings.get();
readings.set(count + 1);
return START_MS + if count == 0 { 0 } else { ELAPSED_MS };
};
}
fn code_names() -> Vec<String> {
return vec![format!("src/a{}", CODE_SUFFIXES[0]), String::from("README.md")];
}
fn reader(names: &[String]) -> Result<LandedCommit, String> {
let mut commit: LandedCommit = LandedCommit { sha: String::from("sha"), subject: String::from("s"), patch: String::from("patch\n"), ..LandedCommit::default() };
commit.contents.insert(names[0].clone(), String::from("code\n"));
return Ok(commit);
}
fn options(directory: &Path, endpoint: &str, key: Option<&str>) -> ReviewOptions {
if !directory.join("log.md").exists() {
std::fs::write(directory.join("log.md"), "").expect("an empty log");
}
return ReviewOptions { log_path: directory.join("log.md"), api_key: key.map(String::from), endpoint: String::from(endpoint), deadline: SHORT, trusted_pem: None, spool: None };
}
#[test]
fn a_review_sends_the_built_input_and_logs_the_verdict() {
let directory: PathBuf = fixture("run-review");
let answer: &str = r#"{"answers":[{"choice":"partial","probabilities":[{"value":"yes","probability":0.3},{"value":"partial","probability":0.6},{"value":"no","probability":0.1}]}]}"#;
let mock: Mock = Mock::start(Answer::Bytes(http_reply("200 OK", answer)));
let opts: ReviewOptions = options(directory.as_path(), mock.url.as_str(), Some("k"));
let names: Vec<String> = code_names();
let commit: LandedCommit = reader(&names[..1]).expect("commit");
let expected_body: String = request_body(build_input(&commit, &names[..1]).model_input.as_str());
let outcome: ReviewOutcome = review_landed(&names, "0123456789abcdefff", "the subject", &|files| return reader(files), &opts, &clock());
assert!(matches!(&outcome, ReviewOutcome::Reviewed(judged) if judged.top == "partial"), "{outcome:?}");
assert!(mock.finish().ends_with(expected_body.as_str()));
let log: String = std::fs::read_to_string(opts.log_path.as_path()).expect("log");
assert!(log.contains(&format!(" {}\n", format_iso_milliseconds(i64::try_from(START_MS + ELAPSED_MS).expect("fits")))));
assert!(log.contains(&format!(" {} s\n", seconds_with_tenth(ELAPSED_MS))));
assert!(log.contains("cost_verdict: partial (yes 0.3 partial 0.6 no 0.1)"));
assert!(log.contains(" 0123456789ab `the subject`\n"));
remove(directory.as_path());
}
#[test]
fn failures_are_logged_as_entries() {
let directory: PathBuf = fixture("run-failures");
let names: Vec<String> = code_names();
for (key, sha) in [(None, "aaaaaaaaaaaa"), (Some(""), "bbbbbbbbbbbb")] {
let opts: ReviewOptions = options(directory.as_path(), "http://127.0.0.1:1/unused", key);
let outcome: ReviewOutcome = review_landed(&names, sha, "s", &|files| return reader(files), &opts, &clock());
assert!(matches!(&outcome, ReviewOutcome::Reviewed(judged) if judged.outcome == format!("Luna failed: {NO_KEY_MESSAGE}")));
}
let mock: Mock = Mock::start(Answer::Bytes(http_reply("500 Internal Server Error", "boom")));
let opts: ReviewOptions = options(directory.as_path(), mock.url.as_str(), Some("k"));
review_landed(&names, "cccccccccccc", "s", &|files| return reader(files), &opts, &clock());
mock.finish();
let log: String = std::fs::read_to_string(opts.log_path.as_path()).expect("log");
assert_eq!(log.matches("Named superlinear cost:\n unjudged\n").count(), 3);
assert!(log.contains("Luna failed: HTTP 500 boom"));
remove(directory.as_path());
}
#[test]
fn a_silent_gateway_costs_at_most_the_deadline() {
let directory: PathBuf = fixture("run-silent");
let mock: Mock = Mock::start(Answer::Silent(SHORT * 4));
let opts: ReviewOptions = options(directory.as_path(), mock.url.as_str(), Some("k"));
let started: Instant = Instant::now();
review_landed(&code_names(), "sha", "s", &|files| return reader(files), &opts, &clock());
assert!(started.elapsed() < SHORT * 3);
mock.finish();
assert!(std::fs::read_to_string(opts.log_path.as_path()).expect("log").contains("Luna failed: The operation was aborted due to timeout"));
remove(directory.as_path());
}
#[test]
fn silent_and_skipped_commits_read_nothing() {
let directory: PathBuf = fixture("run-skip");
let opts: ReviewOptions = options(directory.as_path(), "http://127.0.0.1:1/unused", Some("k"));
let forbidden = |_: &[String]| -> Result<LandedCommit, String> { panic!("the reader must not be called") };
assert_eq!(review_landed(&[], "sha", "s", &forbidden, &opts, &clock()), ReviewOutcome::NoEntry);
assert_eq!(review_landed(&[String::from(LOG_RELATIVE)], "sha", "s", &forbidden, &opts, &clock()), ReviewOutcome::NoEntry);
assert_eq!(std::fs::read_to_string(opts.log_path.as_path()).expect("log"), "");
assert_eq!(review_landed(&[String::from("doc/a.md")], "sha", "s", &forbidden, &opts, &clock()), ReviewOutcome::Skipped);
assert!(std::fs::read_to_string(opts.log_path.as_path()).expect("log").starts_with("- Skipped:\n"));
remove(directory.as_path());
}
#[test]
fn read_and_write_failures_are_outcomes() {
let directory: PathBuf = fixture("run-faults");
let opts: ReviewOptions = options(directory.as_path(), "http://127.0.0.1:1/unused", None);
let unread = review_landed(&code_names(), "sha", "s", &|_| return Err(String::from("no such commit")), &opts, &clock());
assert_eq!(unread, ReviewOutcome::Unread(String::from("no such commit")));
assert_eq!(std::fs::read_to_string(opts.log_path.as_path()).expect("log"), "");
let mut blocked: ReviewOptions = opts.clone();
blocked.log_path = directory.join("missing-directory").join("log.md");
assert!(matches!(review_landed(&code_names(), "sha", "s", &|files| return reader(files), &blocked, &clock()), ReviewOutcome::Unwritable(_)));
remove(directory.as_path());
}
#[test]
fn options_follow_the_script_environment() {
let root: &Path = Path::new("/repo");
let none = |_: &str| -> Option<String> { return None };
let defaults: ReviewOptions = options_from_environment(root, &none);
assert_eq!(defaults.log_path, root.join(LOG_RELATIVE));
assert_eq!((defaults.api_key.clone(), defaults.endpoint.as_str(), defaults.deadline), (None, ENDPOINT, SCRIPT_DEADLINE));
let set = |name: &str| -> Option<String> {
if name == LOG_VARIABLE {
return Some(String::from("/elsewhere.md"));
}
if name == KEY_VARIABLES[0] {
return Some(String::new());
}
if name == KEY_VARIABLES[1] {
return Some(String::from("fallback"));
}
return None;
};
let given: ReviewOptions = options_from_environment(root, &set);
assert_eq!(given.log_path, PathBuf::from("/elsewhere.md"));
assert_eq!(given.api_key.as_deref(), Some(""), "an empty first key does not fall back");
let only_second = |name: &str| -> Option<String> { return (name == KEY_VARIABLES[1]).then(|| return String::from("fallback")) };
assert_eq!(options_from_environment(root, &only_second).api_key.as_deref(), Some("fallback"));
let allow = |name: &str| -> Option<String> { return (name == ALLOW_VARIABLE).then(|| return String::from("1")) };
assert!(full_review_requested(&allow));
assert!(!full_review_requested(&none));
}
#[test]
fn the_runner_is_bounded() {
assert_eq!(run_within(Duration::from_secs(5), || return 7_u8), Some(7));
let started: Instant = Instant::now();
assert_eq!(run_within(SHORT, || { std::thread::sleep(SHORT * 4); return 1_u8; }), None);
assert!(started.elapsed() < SHORT * 3);
assert_eq!(run_within(Duration::from_secs(5), || -> u8 { panic!("job failure") }), None);
}
#[test]
fn a_repeated_item_asks_nothing_and_a_missing_log_is_not_created() {
let directory: PathBuf = fixture("run-repeat");
let mock: Mock = Mock::start(Answer::Bytes(http_reply("200 OK", r#"{"answers":[{"choice":"no"}]}"#)));
let mut opts: ReviewOptions = options(directory.as_path(), mock.url.as_str(), Some("k"));
opts.spool = Some(directory.join("spool"));
let names: Vec<String> = code_names();
let first = review_landed(&names, "0123456789abcdefff", "s", &|files| return reader(files), &opts, &clock());
assert!(matches!(first, ReviewOutcome::Reviewed(_)));
mock.finish();
let again = review_landed(&names, "0123456789abcdefff", "s", &|_| panic!("a logged commit is not read again"), &opts, &clock());
assert_eq!(again, ReviewOutcome::AlreadyLogged);
assert_eq!(std::fs::read_dir(directory.join("spool")).expect("spool").count(), 1);
let mut absent: ReviewOptions = opts.clone();
absent.log_path = directory.join("never-created.md");
let outcome = review_landed(&names, "feedfacefeedfaceff", "s", &|files| return reader(files), &absent, &clock());
assert!(matches!(outcome, ReviewOutcome::Unwritable(_)), "{outcome:?}");
assert!(!absent.log_path.exists());
remove(directory.as_path());
}