Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions src-tauri/src/brief_facts_build.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// SPDX-License-Identifier: FSL-1.1-Apache-2.0
//! Brief facts, DB-backed builders: the security, upgrade and worth-knowing
//! lanes read from the Preemption feed, the lockfile graph, the registry rows
Expand Down Expand Up @@ -415,10 +415,7 @@
&to,
)
})
.map(|f| match f {
crate::osv::direct_upgrade::DirectFix::Command(c) => format!("run `{c}`"),
crate::osv::direct_upgrade::DirectFix::Instruction(i) => i,
});
.map(|f| f.brief_clause());
FixPath::Bump { to, fix }
}

Expand Down
65 changes: 43 additions & 22 deletions src-tauri/src/brief_facts_build_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ fn a_bump_names_the_command_for_its_declaration() {
);
assert_eq!(
crate::brief_facts::fix_clause("minimist", &path),
"bump minimist to >= 1.2.6: run `npm install \"minimist@~1.2.6\"`"
"bump minimist to >= 1.2.6: within its declared range, run `npm update minimist`"
);
// Not declared here: plain "bump", nothing invented.
let plain = with_direct_fix(
Expand Down Expand Up @@ -108,7 +108,10 @@ fn a_bump_without_a_confirmed_copy_or_known_ecosystem_names_nothing() {
/// One fixture project, seeded the way the scanner seeds it: the Brief's
/// direct row and the Upgrade Plan's direct row must name the SAME command
/// (review of #982, SF3) — the property audit E8's two halves share.
fn seeded_project(tag: &str) -> Option<(tempfile::TempDir, String, crate::db::Database)> {
fn seeded_project(
tag: &str,
declared: &str,
) -> Option<(tempfile::TempDir, String, crate::db::Database)> {
let dir = tempfile::Builder::new()
.prefix(&format!("brief-{tag}-"))
.tempdir_in(env!("CARGO_MANIFEST_DIR"))
Expand All @@ -118,14 +121,22 @@ fn seeded_project(tag: &str) -> Option<(tempfile::TempDir, String, crate::db::Da
eprintln!("SKIPPED brief/plan parity: {project} is excluded from DB writes here");
return None;
}
let deps = serde_json::json!({ "minimist": declared });
std::fs::write(
dir.path().join("package.json"),
r#"{"name": "app", "dependencies": {"minimist": "~1.2.5"}}"#,
serde_json::json!({ "name": "app", "dependencies": deps }).to_string(),
)
.unwrap();
std::fs::write(
dir.path().join("package-lock.json"),
r#"{"lockfileVersion": 3, "packages": {"": {"dependencies": {"minimist": "~1.2.5"}}, "node_modules/minimist": {"version": "1.2.5"}}}"#,
serde_json::json!({
"lockfileVersion": 3,
"packages": {
"": { "dependencies": deps },
"node_modules/minimist": { "version": "1.2.5" }
}
})
.to_string(),
)
.unwrap();
let db = crate::test_utils::test_db();
Expand Down Expand Up @@ -210,31 +221,41 @@ fn brief_clause(db: &crate::db::Database, project: &str) -> String {

#[test]
fn the_brief_and_the_plan_name_the_same_command() {
let Some((_dir, project, db)) = seeded_project("parity") else {
return;
};
seed_copy(&db, &project);
let plan = crate::evidence::build_upgrade_plan(&db).items;
let row = plan
.iter()
.find(|i| i.title.contains("minimist"))
.expect("a minimist row");
let command = "`npm install \"minimist@~1.2.6\"`";
assert!(
row.explanation.contains(command),
"plan: {}",
row.explanation
);
let clause = brief_clause(&db, &project);
assert!(clause.contains(command), "brief: {clause}");
// An exact pin moves by the install that keeps it exact; a tilde that
// already admits the fix moves by the in-range refresh (#993).
for (tag, declared, command) in [
(
"parity",
"1.2.5",
"`npm install minimist@1.2.6 --save-exact`",
),
("parity-tilde", "~1.2.5", "`npm update minimist`"),
] {
let Some((_dir, project, db)) = seeded_project(tag, declared) else {
return;
};
seed_copy(&db, &project);
let plan = crate::evidence::build_upgrade_plan(&db).items;
let row = plan
.iter()
.find(|i| i.title.contains("minimist"))
.expect("a minimist row");
assert!(
row.explanation.contains(command),
"{declared} plan: {}",
row.explanation
);
let clause = brief_clause(&db, &project);
assert!(clause.contains(command), "{declared} brief: {clause}");
}
}

/// SF1: with no version-confirmed copy the version and target are the
/// alert's fallbacks (the raw highest OSV fix, which skips the withdrawn-
/// release rule); the plan names no command there, and neither does the Brief.
#[test]
fn without_a_confirmed_copy_the_brief_names_no_command() {
let Some((_dir, project, db)) = seeded_project("noconfirm") else {
let Some((_dir, project, db)) = seeded_project("noconfirm", "1.2.5") else {
return;
};
let clause = brief_clause(&db, &project);
Expand Down
50 changes: 48 additions & 2 deletions src-tauri/src/evidence/upgrade_parent_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -568,14 +568,60 @@ fn a_direct_row_names_the_command_for_its_declaration() {
eprintln!("SKIPPED a_direct_row_names_the_command_for_its_declaration: {project} is excluded from DB writes here");
return;
}
std::fs::write(
dir.path().join("package.json"),
r#"{"name": "app", "dependencies": {"minimist": "1.2.5"}}"#,
)
.unwrap();
std::fs::write(
dir.path().join("package-lock.json"),
r#"{"lockfileVersion": 3, "packages": {"": {"dependencies": {"minimist": "1.2.5"}}, "node_modules/minimist": {"version": "1.2.5"}}}"#,
)
.unwrap();
let db = test_db();
db.store_dependency(&project, "minimist", Some("1.2.5"), "npm", false, None)
.unwrap();
db.store_dependency_instances(
&project,
"npm",
&[instance("minimist", "1.2.5", true, false)],
)
.unwrap();
advisory(&db, "GHSA-xvch-5gv4-984h", "minimist", "npm", "1.2.6", 9.8);
let plan = plan(&db);
let row = row(&plan, "minimist");
validate_item(row).expect("schema-valid");
assert!(
row.explanation.contains(
"Fixable now via a direct dependency bump: `npm install minimist@1.2.6 --save-exact`"
),
"{}",
row.explanation
);
}

/// Live 2026-10-18 (navcal sharp, next, vite): a declared `~1.2.5` already admits the
/// fix 1.2.6, so the row names the in-range refresh, not an install that
/// rewrites the manifest.
#[test]
fn a_direct_row_whose_range_admits_the_fix_names_the_refresh() {
let dir = tempfile::Builder::new()
.prefix("direct-refresh-")
.tempdir_in(env!("CARGO_MANIFEST_DIR"))
.expect("tempdir");
let project = dir.path().to_string_lossy().replace('\\', "/");
if crate::project_inclusion::is_hard_excluded(&project) {
eprintln!("SKIPPED a_direct_row_whose_range_admits_the_fix_names_the_refresh: {project} is excluded from DB writes here");
return;
}
std::fs::write(
dir.path().join("package.json"),
r#"{"name": "app", "dependencies": {"minimist": "~1.2.5"}}"#,
)
.unwrap();
std::fs::write(
dir.path().join("package-lock.json"),
r#"{"lockfileVersion": 3, "packages": {"": {"dependencies": {"minimist": "~1.2.5"}}, "node_modules/minimist": {"version": "1.2.5"}}}"#,
r#"{"lockfileVersion": 3, "packages": {"": {"dependencies": {"minimist": "1.2.5"}}, "node_modules/minimist": {"version": "1.2.5"}}}"#,
)
.unwrap();
let db = test_db();
Expand All @@ -593,7 +639,7 @@ fn a_direct_row_names_the_command_for_its_declaration() {
validate_item(row).expect("schema-valid");
assert!(
row.explanation.contains(
"Fixable now via a direct dependency bump: `npm install \"minimist@~1.2.6\"`"
"Fixable now with a lockfile refresh (the declared range already admits the fix): `npm update minimist`"
),
"{}",
row.explanation
Expand Down
11 changes: 8 additions & 3 deletions src-tauri/src/evidence/upgrade_plan.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// SPDX-License-Identifier: FSL-1.1-Apache-2.0
//! Upgrade Plan — the ranked "which dependency upgrade actually matters" brain
//! (Phase 1 of the dependency-intelligence launch;
Expand Down Expand Up @@ -452,14 +452,19 @@
.iter()
.any(|l| l.target_version.is_some() && l.sites.iter().any(|s| !s.is_direct));
let how = crate::osv::direct_upgrade::direct_fix_note(&self.direct_fixes, m > 1);
// The declared range already admits the fix: a refresh, not a bump.
let lead = if crate::osv::direct_upgrade::all_refresh(&self.direct_fixes, &self.lines) {
"Fixable now with a lockfile refresh (the declared range already admits the fix)"
} else {
"Fixable now via a direct dependency bump"
};
if transitive {
format!(
"Fixable now via a direct dependency bump where it is declared{how}. \
Elsewhere it is transitive — {}",
"{lead} where it is declared{how}. Elsewhere it is transitive — {}",
super::upgrade_parent::upstream_note(&self.package, &self.routes, m > 1)
)
} else {
format!("Fixable now via a direct dependency bump{how}")
format!("{lead}{how}")
}
} else {
super::upgrade_parent::upstream_note(&self.package, &self.routes, m > 1)
Expand Down
Loading
Loading