@@ -130,6 +130,9 @@ async fn do_pin(
130130 // Write the version to .node-version
131131 tokio:: fs:: write ( & node_version_path, format ! ( "{resolved_version}\n " ) ) . await ?;
132132
133+ // Invalidate resolve cache so the pinned version takes effect immediately
134+ crate :: shim:: invalidate_cache ( ) ;
135+
133136 // Print success message
134137 if was_alias {
135138 output:: success ( & format ! (
@@ -206,13 +209,18 @@ pub async fn do_unpin(cwd: &AbsolutePathBuf) -> Result<ExitStatus, Error> {
206209 }
207210
208211 tokio:: fs:: remove_file ( & node_version_path) . await ?;
212+
213+ // Invalidate resolve cache so the unpinned version falls back correctly
214+ crate :: shim:: invalidate_cache ( ) ;
215+
209216 output:: success ( & format ! ( "Removed {} from {}" , NODE_VERSION_FILE , cwd. as_path( ) . display( ) ) ) ;
210217
211218 Ok ( ExitStatus :: default ( ) )
212219}
213220
214221#[ cfg( test) ]
215222mod tests {
223+ use serial_test:: serial;
216224 use tempfile:: TempDir ;
217225 use vite_path:: AbsolutePathBuf ;
218226
@@ -275,6 +283,90 @@ mod tests {
275283 assert ! ( !tokio:: fs:: try_exists( & node_version_path) . await . unwrap( ) ) ;
276284 }
277285
286+ #[ tokio:: test]
287+ // Run serially: mutates VITE_PLUS_HOME env var which affects invalidate_cache()
288+ #[ serial]
289+ async fn test_do_unpin_invalidates_cache ( ) {
290+ let temp_dir = TempDir :: new ( ) . unwrap ( ) ;
291+ let temp_path = AbsolutePathBuf :: new ( temp_dir. path ( ) . to_path_buf ( ) ) . unwrap ( ) ;
292+
293+ // Point VITE_PLUS_HOME to temp dir
294+ unsafe {
295+ std:: env:: set_var ( vite_shared:: env_vars:: VITE_PLUS_HOME , temp_path. as_path ( ) ) ;
296+ }
297+
298+ // Create cache file manually
299+ let cache_dir = temp_path. join ( "cache" ) ;
300+ std:: fs:: create_dir_all ( & cache_dir) . unwrap ( ) ;
301+ let cache_file = cache_dir. join ( "resolve_cache.json" ) ;
302+ std:: fs:: write ( & cache_file, r#"{"version":2,"entries":{}}"# ) . unwrap ( ) ;
303+ assert ! (
304+ std:: fs:: metadata( cache_file. as_path( ) ) . is_ok( ) ,
305+ "Cache file should exist before unpin"
306+ ) ;
307+
308+ // Create .node-version and unpin
309+ let node_version_path = temp_path. join ( ".node-version" ) ;
310+ tokio:: fs:: write ( & node_version_path, "20.18.0\n " ) . await . unwrap ( ) ;
311+ let result = do_unpin ( & temp_path) . await ;
312+ assert ! ( result. is_ok( ) ) ;
313+
314+ // Cache file should be removed by invalidate_cache()
315+ assert ! (
316+ std:: fs:: metadata( cache_file. as_path( ) ) . is_err( ) ,
317+ "Cache file should be removed after unpin"
318+ ) ;
319+
320+ // Cleanup
321+ unsafe {
322+ std:: env:: remove_var ( vite_shared:: env_vars:: VITE_PLUS_HOME ) ;
323+ }
324+ }
325+
326+ // Run serially: mutates VITE_PLUS_HOME env var which affects invalidate_cache()
327+ #[ tokio:: test]
328+ #[ serial]
329+ async fn test_do_pin_invalidates_cache ( ) {
330+ let temp_dir = TempDir :: new ( ) . unwrap ( ) ;
331+ let temp_path = AbsolutePathBuf :: new ( temp_dir. path ( ) . to_path_buf ( ) ) . unwrap ( ) ;
332+
333+ // Point VITE_PLUS_HOME to temp dir
334+ unsafe {
335+ std:: env:: set_var ( vite_shared:: env_vars:: VITE_PLUS_HOME , temp_path. as_path ( ) ) ;
336+ }
337+
338+ // Create cache file manually
339+ let cache_dir = temp_path. join ( "cache" ) ;
340+ std:: fs:: create_dir_all ( & cache_dir) . unwrap ( ) ;
341+ let cache_file = cache_dir. join ( "resolve_cache.json" ) ;
342+ std:: fs:: write ( & cache_file, r#"{"version":2,"entries":{}}"# ) . unwrap ( ) ;
343+ assert ! (
344+ std:: fs:: metadata( cache_file. as_path( ) ) . is_ok( ) ,
345+ "Cache file should exist before pin"
346+ ) ;
347+
348+ // Pin an exact version (no_install=true to skip download, force=true to skip prompt)
349+ let result = do_pin ( & temp_path, "20.18.0" , true , true ) . await ;
350+ assert ! ( result. is_ok( ) ) ;
351+
352+ // .node-version should be created
353+ let node_version_path = temp_path. join ( ".node-version" ) ;
354+ assert ! ( tokio:: fs:: try_exists( & node_version_path) . await . unwrap( ) ) ;
355+ let content = tokio:: fs:: read_to_string ( & node_version_path) . await . unwrap ( ) ;
356+ assert_eq ! ( content. trim( ) , "20.18.0" ) ;
357+
358+ // Cache file should be removed by invalidate_cache()
359+ assert ! (
360+ std:: fs:: metadata( cache_file. as_path( ) ) . is_err( ) ,
361+ "Cache file should be removed after pin"
362+ ) ;
363+
364+ // Cleanup
365+ unsafe {
366+ std:: env:: remove_var ( vite_shared:: env_vars:: VITE_PLUS_HOME ) ;
367+ }
368+ }
369+
278370 #[ tokio:: test]
279371 async fn test_do_unpin_no_file ( ) {
280372 let temp_dir = TempDir :: new ( ) . unwrap ( ) ;
0 commit comments