use super::*;
fn finding(id: &str, severity: Severity) -> Finding {
Finding {
id: id.into(),
severity,
title: "Sleep instead of a lock".into(),
detail: Some("cart.rs:88 sleeps before commit".into()),
location: Some("shop/src/cart.rs:88".into()),
}
}
#[test]
fn the_builder_notice_names_the_rounds_and_the_landing_mode() {
let none = builder_notice(LandMode::NoLanding, 3);
assert!(none.contains("up to 3 rounds"), "{none}");
assert!(none.contains("```scv-landing\n"), "{none}");
assert!(none.ends_with("message anyone in this job."), "{none}");
let after = builder_notice(LandMode::AfterApproval, 5);
assert!(after.contains("name its base and head commits"), "{after}");
let before = builder_notice(LandMode::BeforeReview, 3);
assert!(
before.contains("authorizes landing before review"),
"{before}"
);
}
#[test]
fn the_reviewer_is_never_told_the_round_limit() {
let open = [finding("1.1", Severity::Blocking)];
for land in [
LandMode::NoLanding,
LandMode::AfterApproval,
LandMode::BeforeReview,
] {
let prompt = reviewer_prompt(&ReviewerBrief {
land,
prompt: "Fix the flaky checkout test.",
focus: Some("no sleeps"),
reply: "Replaced the sleep with a lock.",
open: &open,
landed: None,
});
for told in ["3 rounds", " of 3", "round 1", "round 2", "rounds left"] {
assert!(!prompt.contains(told), "{told}: {prompt}");
}
assert!(prompt.contains("1.1 [blocking] \"Sleep instead of a lock\""));
assert!(prompt.contains("Focus from the user: no sleeps"));
assert!(prompt.contains("```scv-verdict\n"));
assert!(
prompt
.contains("Commits the brief names as landed before this job are not a violation.")
);
assert!(prompt.contains("untrusted"));
}
}
#[test]
fn the_reviewer_checks_a_reported_landing_and_names_what_it_approves() {
let commits = ["4f2a9c1".to_owned()];
let prompt = reviewer_prompt(&ReviewerBrief {
land: LandMode::BeforeReview,
prompt: "Hotfix it.",
focus: None,
reply: "Landed.",
open: &[],
landed: Some(("origin/main", &commits)),
});
assert!(prompt.contains("landed 4f2a9c1 on origin/main"), "{prompt}");
assert!(!prompt.contains("settle every open finding"));
let after = reviewer_prompt(&ReviewerBrief {
land: LandMode::AfterApproval,
prompt: "Fix it.",
focus: None,
reply: "Done.",
open: &[],
landed: None,
});
assert!(after.contains("\"approved\""), "{after}");
}
#[test]
fn an_empty_open_list_omits_prior_and_a_listed_one_is_settled_by_id() {
let empty = reviewer_prompt(&ReviewerBrief {
land: LandMode::NoLanding,
prompt: "Fix the flaky checkout test.",
focus: None,
reply: "Replaced the sleep with a lock.",
open: &[],
landed: None,
});
assert!(
empty.contains("There are no open findings, so omit \"prior\" or set it to []."),
"{empty}"
);
assert!(
empty.contains("Do not invent finding ids such as \"1\" or \"1.1\"."),
"{empty}"
);
assert!(
empty.contains("A \"prior\" entry is valid only for an id listed under open findings."),
"{empty}"
);
assert!(!empty.contains("settle every open finding"), "{empty}");
assert!(empty.contains("\"prior\":[]"), "{empty}");
assert!(!empty.contains("<open finding ID>"), "{empty}");
assert!(
empty.contains(
"\"prior\" is required only when open findings were listed, one entry per listed id"
),
"{empty}"
);
assert!(
empty.contains("otherwise omit \"prior\" or set it to []"),
"{empty}"
);
let open = [
finding("1.1", Severity::Blocking),
finding("1.2", Severity::Minor),
];
let later = reviewer_prompt(&ReviewerBrief {
land: LandMode::NoLanding,
prompt: "Fix the flaky checkout test.",
focus: None,
reply: "Replaced the sleep with a lock.",
open: &open,
landed: None,
});
assert!(
later.contains("First settle every open finding below by its ID in \"prior\""),
"{later}"
);
assert!(later.contains("- 1.1 [blocking]"), "{later}");
assert!(later.contains("- 1.2 [minor]"), "{later}");
assert!(
later.contains(
"\"prior\" is required only when open findings were listed, one entry per listed id"
),
"{later}"
);
assert!(!later.contains("There are no open findings"), "{later}");
assert!(!later.contains("<open finding ID>"), "{later}");
}
#[test]
fn the_repair_prompt_keeps_the_parser_error_and_forbids_an_invented_prior_id() {
let clause = "If the error is that \"prior\" names an id that is not an open finding, omit \
\"prior\" or send [] unless the review listed open findings, and never invent \
an id.";
let missing = repair_prompt("no scv-verdict block");
assert!(missing.starts_with(
"Your reply had no valid scv-verdict block (no scv-verdict block). Reply with only that \
block,"
));
assert!(missing.contains(clause), "{missing}");
let named = repair_prompt("prior names \"1.1\", which is not an open finding");
assert!(
named.contains("(prior names \"1.1\", which is not an open finding)"),
"{named}"
);
assert!(named.contains(clause), "{named}");
}
#[test]
fn quoted_text_is_bounded() {
let long = "x".repeat(20 * 1024);
let prompt = reviewer_prompt(&ReviewerBrief {
land: LandMode::NoLanding,
prompt: &long,
focus: None,
reply: &long,
open: &[],
landed: None,
});
assert!(prompt.len() < 20 * 1024, "{}", prompt.len());
assert_eq!(prompt.matches("[cut at 8 KiB]").count(), 2);
}
fn fix<'a>(
land: LandMode,
report: &'a str,
findings: &'a [Finding],
landed: Option<(&'a str, &'a [String])>,
) -> FixBrief<'a> {
FixBrief {
round: 2,
rounds: 3,
land,
prompt: "Fix the flaky checkout test.",
report,
findings,
landed,
}
}
#[test]
fn the_fix_prompt_lists_findings_and_what_already_landed() {
let findings = [
finding("1.1", Severity::Blocking),
finding("1.2", Severity::Minor),
];
let commits = ["4f2a9c1".to_owned()];
let prompt = fix_prompt(&fix(
LandMode::BeforeReview,
"Committed on fix/race.",
&findings,
Some(("origin/main", &commits)),
));
assert!(prompt.starts_with("[SCV review, round 2 of 3]"), "{prompt}");
assert!(prompt.contains("in a new conversation"));
assert!(prompt.contains("already landed on origin/main (4f2a9c1)"));
assert!(prompt.contains("Land the fix as before"));
assert!(prompt.contains("- 1.1 [blocking]"));
assert!(prompt.contains("- 1.2 [minor]"));
assert!(prompt.contains("The task, as it was first given:\nFix the flaky checkout test."));
assert!(prompt.contains("up to 3 rounds"));
assert!(prompt.contains("Committed on fix/race."));
let held = fix_prompt(&fix(
LandMode::NoLanding,
"",
&findings,
Some(("origin/main", &commits)),
));
assert!(held.contains("Do not land the fix now."), "{held}");
}
#[test]
fn the_fix_prompt_quotes_only_a_bounded_last_report() {
let findings = [finding("1.1", Severity::Blocking)];
let report = format!("{}LAST LINE", "x".repeat(100 * 1024));
let prompt = fix_prompt(&fix(LandMode::NoLanding, &report, &findings, None));
assert!(prompt.len() < 16 * 1024, "{}", prompt.len());
assert!(prompt.contains("[cut at 8 KiB]"));
assert!(!prompt.contains("LAST LINE"));
}
#[test]
fn the_confirmation_names_the_approved_range_and_the_ref() {
let prompt = confirmation_prompt(
("1b2c3d4", "7e8f9a0"),
1,
r#"{"status":"landed","ref":"origin/main","commits":["4f2a9c1"]}"#,
"origin/main",
);
assert!(
prompt.contains("You approved 1b2c3d4..7e8f9a0 in round 1"),
"{prompt}"
);
assert!(prompt.contains("```scv-landing-check\n"));
assert!(prompt.contains("Do not edit, commit, land, revert, push"));
assert!(repair_prompt("no scv-verdict block").contains("(no scv-verdict block)"));
}
fn many(round: u32, count: usize, title: &str, detail: usize) -> Vec<Finding> {
(1..=count)
.map(|n| Finding {
id: format!("{round}.{n}"),
severity: Severity::Blocking,
title: format!("{title} {n}"),
detail: Some("d".repeat(detail)),
location: Some(format!("src/lib.rs:{n}")),
})
.collect()
}
fn listed(text: &str, findings: &[Finding]) -> Vec<String> {
findings
.iter()
.filter(|finding| !text.contains(&format!("- {} [blocking] \"", finding.id)))
.map(|finding| finding.id.clone())
.collect()
}
#[test]
fn every_open_finding_reaches_the_next_reviewer_and_the_builder() {
let mut open = many(1, 10, "Defect", 1300);
open.extend(many(2, 10, "Defect", 1300));
let prompt = reviewer_prompt(&ReviewerBrief {
land: LandMode::NoLanding,
prompt: "Fix the project.",
focus: None,
reply: "I fixed the findings.",
open: &open,
landed: None,
});
assert!(
listed(&prompt, &open).is_empty(),
"{:?}",
listed(&prompt, &open)
);
let fix = fix_prompt(&FixBrief {
round: 3,
rounds: 5,
land: LandMode::NoLanding,
prompt: "Fix the project.",
report: "I fixed the findings.",
findings: &open,
landed: None,
});
assert_eq!(listed(&fix, &open), [] as [String; 0]);
let open: Vec<Finding> = (1..=6)
.flat_map(|round| many(round, 20, "Defect", 2000))
.collect();
let list = finding_list(&open, FINDINGS_BYTES);
assert!(list.len() <= FINDINGS_BYTES, "{}", list.len());
assert!(list.starts_with("(Details are shortened to fit"));
assert_eq!(listed(&list, &open), [] as [String; 0]);
assert!(list.contains("(src/lib.rs:20)"));
let title = "é".repeat(200);
let open: Vec<Finding> = (1..=20)
.flat_map(|round| many(round, 20, &title, 2000))
.collect();
let list = finding_list(&open, FINDINGS_BYTES);
assert!(list.len() <= FINDINGS_BYTES, "{}", list.len());
assert!(list.starts_with("(Too many findings to quote in full"));
assert_eq!(listed(&list, &open), [] as [String; 0]);
assert!(!list.contains("dddd"));
}