@@ -125,7 +125,7 @@ fn observed_project_local_eslint_candidate_is_not_treated_as_missing_native_lint
125125 . unwrap ( ) ;
126126 assert_eq ! ( output. status. code( ) , Some ( 3 ) ) ;
127127 let report: Value = serde_json:: from_slice ( & output. stdout ) . unwrap ( ) ;
128- assert_eq ! ( report[ "reason" ] , "eslint_execution_context_missing " ) ;
128+ assert_eq ! ( report[ "reason" ] , "eslint_node_runtime_unresolved " ) ;
129129 assert ! ( report. get( "syntax_precheck" ) . is_none( ) ) ;
130130 assert ! (
131131 report[ "next_action" ]
@@ -219,6 +219,75 @@ fn available_project_local_eslint_runs_before_wasm_and_preserves_native_finding(
219219 assert ! ( mismatch. get( "syntax_precheck" ) . is_none( ) ) ;
220220}
221221
222+ #[ test]
223+ fn unresolved_node_updates_one_stable_environment_task_with_specific_guidance ( ) {
224+ let project = Project :: new ( ) ;
225+ let root = project. 0 . canonicalize ( ) . unwrap ( ) ;
226+ let source = root. join ( "app.ts" ) ;
227+ fs:: write ( & source, "const value: number = 1;\n " ) . unwrap ( ) ;
228+ fs:: write (
229+ root. join ( "package.json" ) ,
230+ r#"{"devDependencies":{"eslint":"10.0.0"}}"# ,
231+ )
232+ . unwrap ( ) ;
233+ fs:: write ( root. join ( "eslint.config.cjs" ) , "module.exports = [];\n " ) . unwrap ( ) ;
234+ fs:: create_dir_all ( root. join ( "node_modules/eslint/bin" ) ) . unwrap ( ) ;
235+ fs:: write (
236+ root. join ( "node_modules/eslint/package.json" ) ,
237+ r#"{"name":"eslint","version":"10.0.0","bin":{"eslint":"bin/eslint.js"}}"# ,
238+ )
239+ . unwrap ( ) ;
240+ fs:: write ( root. join ( "node_modules/eslint/bin/eslint.js" ) , "fixture\n " ) . unwrap ( ) ;
241+ let initialized = Command :: new ( env ! ( "CARGO_BIN_EXE_codeguard" ) )
242+ . args ( [
243+ "init" ,
244+ root. to_str ( ) . unwrap ( ) ,
245+ "--apply" ,
246+ "--format" ,
247+ "json" ,
248+ ] )
249+ . output ( )
250+ . unwrap ( ) ;
251+ assert_eq ! ( initialized. status. code( ) , Some ( 3 ) ) ;
252+
253+ let mut task_id = None ;
254+ for round in 0 ..2 {
255+ let output = Command :: new ( env ! ( "CARGO_BIN_EXE_codeguard" ) )
256+ . args ( [
257+ "lint" ,
258+ "typescript" ,
259+ source. to_str ( ) . unwrap ( ) ,
260+ "--workspace" ,
261+ root. to_str ( ) . unwrap ( ) ,
262+ "--format" ,
263+ "json" ,
264+ ] )
265+ . env ( "PATH" , "" )
266+ . output ( )
267+ . unwrap ( ) ;
268+ assert_eq ! ( output. status. code( ) , Some ( 3 ) ) ;
269+ let report: Value = serde_json:: from_slice ( & output. stdout ) . unwrap ( ) ;
270+ assert_eq ! ( report[ "reason" ] , "eslint_node_runtime_unresolved" ) ;
271+ assert_eq ! ( report[ "workbench_status" ] , "synced_partial" , "{report}" ) ;
272+ assert_eq ! ( report[ "workbench" ] [ "new_blockers" ] , 1 - round) ;
273+ assert ! ( report[ "next_action" ] . as_str( ) . unwrap( ) . contains( "Node" ) ) ;
274+ assert ! (
275+ report[ "next_action" ]
276+ . as_str( )
277+ . unwrap( )
278+ . contains( "不重复安装 ESLint" )
279+ ) ;
280+ let current = report[ "workbench" ] [ "next" ] [ "repair_brief" ] [ "task_id" ]
281+ . as_str ( )
282+ . unwrap ( )
283+ . to_owned ( ) ;
284+ if let Some ( previous) = & task_id {
285+ assert_eq ! ( & current, previous) ;
286+ }
287+ task_id = Some ( current) ;
288+ }
289+ }
290+
222291#[ test]
223292fn configured_but_uninstalled_eslint_still_allows_candidate_precheck ( ) {
224293 let project = Project :: new ( ) ;
0 commit comments