@@ -2,6 +2,7 @@ use fluxheim_common::test_support::{safe_child_path, unique_temp_path};
22use fluxheim_config:: { Config , ProxyConfig } ;
33use std:: sync:: { Arc , Barrier } ;
44
5+ use crate :: recovery:: { MAX_RUNTIME_DIAGNOSTIC_BYTES , MAX_RUNTIME_STATE_BYTES } ;
56use crate :: store:: MAX_SNAPSHOT_STORE_ENTRIES ;
67#[ cfg( unix) ]
78use crate :: store_fs:: set_open_private_file_mode_for_test;
@@ -50,6 +51,7 @@ fn concurrent_snapshot_creation_cannot_exceed_capacity() {
5051 let dir = TestDir :: new ( "snapshot-concurrent-capacity" ) ;
5152 let configs = dir. child ( "configs" ) ;
5253 std:: fs:: create_dir ( & configs) . unwrap ( ) ;
54+ set_private_test_directory ( & configs) ;
5355 for index in 0 ..MAX_SNAPSHOT_STORE_ENTRIES - 1 {
5456 std:: fs:: write (
5557 safe_child_path ( & configs, & format ! ( "legacy-{index:04}.toml" ) ) ,
@@ -378,6 +380,61 @@ fn self_healing_state_persists_pending_and_failed_rollback() {
378380 assert ! ( restored_pending. last_rollback_failure. is_some( ) ) ;
379381}
380382
383+ #[ test]
384+ fn recovery_diagnostic_limits_preserve_previous_state ( ) {
385+ let dir = TestDir :: new ( "snapshot-recovery-diagnostic-limit" ) ;
386+ let store = SnapshotStore :: new ( dir. path ( ) ) ;
387+ let snapshot = store. snapshot_config ( & Config :: default ( ) , None ) . unwrap ( ) ;
388+ let valid = SnapshotRuntimeState {
389+ runtime_snapshot : Some ( snapshot. id . clone ( ) ) ,
390+ known_good_snapshot : Some ( snapshot. id . clone ( ) ) ,
391+ pending_validation : None ,
392+ } ;
393+ store. save_runtime_state ( & valid) . unwrap ( ) ;
394+ let state_path = store. runtime_state_path ( ) ;
395+ let before = std:: fs:: read ( & state_path) . unwrap ( ) ;
396+
397+ for ( impact, failure) in [
398+ ( "x" . repeat ( MAX_RUNTIME_DIAGNOSTIC_BYTES + 1 ) , None ) ,
399+ ( "invalid\n impact" . to_owned ( ) , None ) ,
400+ (
401+ "snapshot" . to_owned ( ) ,
402+ Some ( "x" . repeat ( MAX_RUNTIME_DIAGNOSTIC_BYTES + 1 ) ) ,
403+ ) ,
404+ ] {
405+ let invalid = SnapshotRuntimeState {
406+ runtime_snapshot : Some ( snapshot. id . clone ( ) ) ,
407+ known_good_snapshot : Some ( snapshot. id . clone ( ) ) ,
408+ pending_validation : Some ( PendingValidation {
409+ target_snapshot : snapshot. id . clone ( ) ,
410+ previous_snapshot : Some ( snapshot. id . clone ( ) ) ,
411+ impact,
412+ expires_unix_secs : 100 ,
413+ successful_checks : 0 ,
414+ failed_checks : 0 ,
415+ rollback_attempts : 0 ,
416+ last_rollback_failure : failure,
417+ } ) ,
418+ } ;
419+ assert ! ( store. save_runtime_state( & invalid) . is_err( ) ) ;
420+ assert_eq ! ( std:: fs:: read( & state_path) . unwrap( ) , before) ;
421+ }
422+ }
423+
424+ #[ test]
425+ fn recovery_reader_rejects_file_above_dedicated_limit ( ) {
426+ let dir = TestDir :: new ( "snapshot-recovery-file-limit" ) ;
427+ let store = SnapshotStore :: new ( dir. path ( ) ) ;
428+ store. snapshot_config ( & Config :: default ( ) , None ) . unwrap ( ) ;
429+ write_atomically (
430+ & store. runtime_state_path ( ) ,
431+ & vec ! [ b'x' ; ( MAX_RUNTIME_STATE_BYTES + 1 ) as usize ] ,
432+ )
433+ . unwrap ( ) ;
434+
435+ assert ! ( store. load_runtime_state( ) . is_err( ) ) ;
436+ }
437+
381438#[ test]
382439fn failed_create_new_atomic_write_removes_temporary_file ( ) {
383440 let dir = TestDir :: new ( "snapshot-temp-cleanup" ) ;
@@ -451,6 +508,7 @@ impl TestDir {
451508 fn new ( name : & str ) -> Self {
452509 let path = unique_temp_path ( name) ;
453510 std:: fs:: create_dir ( & path) . unwrap ( ) ;
511+ set_private_test_directory ( & path) ;
454512 Self { path }
455513 }
456514
@@ -463,6 +521,16 @@ impl TestDir {
463521 }
464522}
465523
524+ fn set_private_test_directory ( path : & std:: path:: Path ) {
525+ #[ cfg( unix) ]
526+ {
527+ use std:: os:: unix:: fs:: PermissionsExt as _;
528+ std:: fs:: set_permissions ( path, std:: fs:: Permissions :: from_mode ( 0o700 ) ) . unwrap ( ) ;
529+ }
530+ #[ cfg( not( unix) ) ]
531+ let _ = path;
532+ }
533+
466534impl Drop for TestDir {
467535 fn drop ( & mut self ) {
468536 let _ = std:: fs:: remove_dir_all ( & self . path ) ;
0 commit comments