@@ -1316,8 +1316,8 @@ The config file should contain every possible key for documentation purposes."
13161316 "Missing OK entry in cache"
13171317 ) ;
13181318 assert ! (
1319- data. contains( & format!( "{}/,404" , mock_server_err. uri( ) ) ) ,
1320- "Missing error entry in cache "
1319+ ! data. contains( & format!( "{}/,404" , mock_server_err. uri( ) ) ) ,
1320+ "Error entry should not be cached "
13211321 ) ;
13221322
13231323 // Run again to verify cache behavior
@@ -1327,7 +1327,7 @@ The config file should contain every possible key for documentation purposes."
13271327 mock_server_ok. uri( )
13281328 ) ) )
13291329 . stderr ( contains ( format ! (
1330- "[404] {}/ (at 2:1) | Error (cached )\n " ,
1330+ "[404] {}/ (at 2:1) | Rejected status code: 404 Not Found (configurable with \" accept \" option )\n " ,
13311331 mock_server_err. uri( )
13321332 ) ) ) ;
13331333
@@ -1454,8 +1454,10 @@ The config file should contain every possible key for documentation purposes."
14541454 // check content of cache file
14551455 let data = fs:: read_to_string ( & cache_file) ?;
14561456 assert ! ( data. contains( & format!( "{}/,200" , mock_server_ok. uri( ) ) ) ) ;
1457- assert ! ( data. contains( & format!( "{}/,418" , mock_server_teapot. uri( ) ) ) ) ;
1458- assert ! ( data. contains( & format!( "{}/,500" , mock_server_server_error. uri( ) ) ) ) ;
1457+ // Because the first run DID NOT use `--accept`, 418 and 500 were both treated as errors.
1458+ // With our new error dropping logic, NEITHER gets cached in the first run.
1459+ assert ! ( !data. contains( & format!( "{}/,418" , mock_server_teapot. uri( ) ) ) ) ;
1460+ assert ! ( !data. contains( & format!( "{}/,500" , mock_server_server_error. uri( ) ) ) ) ;
14591461
14601462 // run again to verify cache behavior
14611463 // this time accept 418 and 500 as valid status codes
@@ -1466,11 +1468,11 @@ The config file should contain every possible key for documentation purposes."
14661468 . assert ( )
14671469 . success ( )
14681470 . stderr ( contains ( format ! (
1469- "[418] {}/ (at 2:1) | OK (cached) " ,
1471+ "[418] {}/ (at 2:1) | 418 I'm a teapot: I'm a teapot " ,
14701472 mock_server_teapot. uri( )
14711473 ) ) )
14721474 . stderr ( contains ( format ! (
1473- "[500] {}/ (at 3:1) | OK (cached) " ,
1475+ "[500] {}/ (at 3:1) | 500 Internal Server Error: Internal Server Error " ,
14741476 mock_server_server_error. uri( )
14751477 ) ) ) ;
14761478
@@ -1601,8 +1603,11 @@ The config file should contain every possible key for documentation purposes."
16011603 // If the status code was 999, the cache file should be empty
16021604 // because we do not want to cache unknown status codes
16031605 let buf = fs:: read ( & cache_file) . unwrap ( ) ;
1604- let data = String :: from_utf8 ( buf) ?;
1605- assert ! ( data. contains( ",999," ) ) ;
1606+ assert ! (
1607+ buf. is_empty( ) ,
1608+ "cache file should be empty, but was {}" ,
1609+ String :: from_utf8_lossy( & buf)
1610+ ) ;
16061611
16071612 Ok ( ( ) )
16081613 }
@@ -3753,4 +3758,42 @@ https://lychee.cli.rs/guides/cli/#fragments-ignored
37533758 . assert ( )
37543759 . success ( ) ;
37553760 }
3761+
3762+ /// Verifies that loading an older, legacy `.lycheecache` file containing a cached error
3763+ /// correctly drops the error and successfully retries the link.
3764+ /// This ensures we don't break existing user CI workflows that have older cache files
3765+ /// stored before we stopped caching errors.
3766+ #[ tokio:: test]
3767+ async fn test_legacy_cache_file_ignores_errors ( ) -> Result < ( ) > {
3768+ let ts = std:: time:: SystemTime :: now ( )
3769+ . duration_since ( std:: time:: UNIX_EPOCH )
3770+ . unwrap ( )
3771+ . as_secs ( ) ;
3772+ let dir = tempfile:: tempdir ( ) ?;
3773+ let base_path = dir. path ( ) ;
3774+ let cache_file = base_path. join ( LYCHEE_CACHE_FILE ) ;
3775+
3776+ // A server that is currently returning OK
3777+ let mock_server_ok = mock_server ! ( StatusCode :: OK ) ;
3778+
3779+ // Simulate an older `.lycheecache` where this exact URL previously failed with 404
3780+ // We use a future timestamp ({ts}) so it doesn't expire
3781+ fs:: write ( & cache_file, format ! ( "{},404,{ts}\n " , mock_server_ok. uri( ) ) ) ?;
3782+
3783+ let mut file = File :: create ( dir. path ( ) . join ( "input.txt" ) ) ?;
3784+ writeln ! ( file, "{}" , mock_server_ok. uri( ) ) ?;
3785+
3786+ // Run lychee. It should ignore the 404 from the cache, actually check the mock server (which returns 200), and succeed.
3787+ cargo_bin_cmd ! ( )
3788+ . current_dir ( base_path)
3789+ . arg ( "input.txt" )
3790+ . arg ( "--cache" )
3791+ . arg ( "--verbose" )
3792+ . assert ( )
3793+ . success ( )
3794+ . stdout ( contains ( "1 OK" ) )
3795+ . stdout ( contains ( "0 Errors" ) ) ;
3796+
3797+ Ok ( ( ) )
3798+ }
37563799}
0 commit comments