@@ -247,29 +247,41 @@ pub fn build_config_name(name: &str) -> String {
247247
248248#[ cfg( test) ]
249249mod tests {
250- use std:: { io, path:: PathBuf , sync:: Arc } ;
250+ use std:: { collections :: HashMap , io, path:: PathBuf , sync:: Arc } ;
251251
252+ use assert_matches:: assert_matches;
252253 use fs:: {
253254 directory_manager:: { DirectoryManager , mock:: MockDirectoryManager } ,
254255 file_reader:: FileReader ,
255256 mock:: MockLocalFile ,
256257 writer_file:: FileWriter ,
257258 } ;
258259 use mockall:: predicate;
260+ use rstest:: { fixture, rstest} ;
261+ use serde_yaml:: Value ;
259262
260263 use crate :: {
261264 agent_control:: {
262265 agent_id:: AgentID ,
263266 defaults:: {
264267 INSTANCE_ID_FILENAME , STORE_KEY_INSTANCE_ID , STORE_KEY_LOCAL_DATA_CONFIG ,
265- STORE_KEY_OPAMP_DATA_CONFIG ,
268+ STORE_KEY_OPAMP_DATA_CONFIG , default_capabilities ,
266269 } ,
267270 } ,
268- opamp:: instance_id:: {
269- InstanceID ,
270- getter:: DataStored ,
271- on_host:: identifiers:: Identifiers ,
272- storer:: { GenericStorer , InstanceIDStorer } ,
271+ opamp:: {
272+ instance_id:: {
273+ InstanceID ,
274+ getter:: DataStored ,
275+ on_host:: identifiers:: Identifiers ,
276+ storer:: { GenericStorer , InstanceIDStorer } ,
277+ } ,
278+ remote_config:: hash:: { ConfigState , Hash } ,
279+ } ,
280+ values:: {
281+ GenericConfigRepository ,
282+ config:: RemoteConfig ,
283+ config_repository:: { ConfigRepository , ConfigRepositoryError } ,
284+ yaml_config:: YAMLConfig ,
273285 } ,
274286 } ;
275287
@@ -474,6 +486,291 @@ mod tests {
474486 ) ;
475487 }
476488
489+ #[ fixture]
490+ fn agent_id ( ) -> AgentID {
491+ AgentID :: try_from ( "some-agent-id" ) . unwrap ( )
492+ }
493+
494+ #[ rstest]
495+ #[ case:: remote_enabled( true ) ]
496+ #[ case:: remote_disabled( false ) ]
497+ fn test_load_with ( #[ case] remote_enabled : bool , agent_id : AgentID ) {
498+ let mut yaml_config_content = "some_config: true\n another_item: false" ;
499+ if remote_enabled {
500+ yaml_config_content = r#"
501+ config:
502+ some_config: true
503+ another_item: false
504+ hash: a-hash
505+ state: applied
506+ "# ;
507+ }
508+
509+ let mut file_rw = MockLocalFile :: new ( ) ;
510+ let dir_manager = MockDirectoryManager :: new ( ) ;
511+ let remote_dir_path = RemoteDir :: from ( PathBuf :: from ( "some/remote/path/" ) ) ;
512+ let local_dir_path = LocalDir :: from ( PathBuf :: from ( "some/local/path/" ) ) ;
513+ let test_path = if remote_enabled {
514+ remote_dir_path. get_remote_file_path ( & agent_id, STORE_KEY_OPAMP_DATA_CONFIG )
515+ } else {
516+ local_dir_path. get_local_file_path ( & agent_id, STORE_KEY_LOCAL_DATA_CONFIG )
517+ } ;
518+
519+ // Expectations
520+ file_rw. should_read ( & test_path, yaml_config_content. to_string ( ) ) ;
521+
522+ let file_store = Arc :: new ( FileStore :: new (
523+ file_rw,
524+ dir_manager,
525+ local_dir_path. into ( ) ,
526+ remote_dir_path. into ( ) ,
527+ ) ) ;
528+ let repo = if remote_enabled {
529+ GenericConfigRepository :: new ( file_store) . with_remote ( )
530+ } else {
531+ GenericConfigRepository :: new ( file_store)
532+ } ;
533+
534+ let config = repo
535+ . load_remote_fallback_local ( & agent_id, & default_capabilities ( ) )
536+ . expect ( "unexpected error loading config" )
537+ . expect ( "expected some configuration, got None" ) ;
538+
539+ assert_eq ! (
540+ config. get_yaml_config( ) . get( "some_config" ) . unwrap( ) ,
541+ & Value :: Bool ( true )
542+ ) ;
543+ assert_eq ! (
544+ config. get_yaml_config( ) . get( "another_item" ) . unwrap( ) ,
545+ & Value :: Bool ( false )
546+ ) ;
547+ }
548+
549+ #[ rstest]
550+ fn test_load_when_remote_enabled_file_not_found_fallbacks_to_local ( agent_id : AgentID ) {
551+ let mut file_rw = MockLocalFile :: new ( ) ;
552+ let dir_manager = MockDirectoryManager :: new ( ) ;
553+ let remote_dir_path = RemoteDir :: from ( PathBuf :: from ( "some/remote/path/" ) ) ;
554+ let local_dir_path = LocalDir :: from ( PathBuf :: from ( "some/local/path/" ) ) ;
555+ let remote_path =
556+ remote_dir_path. get_remote_file_path ( & agent_id, STORE_KEY_OPAMP_DATA_CONFIG ) ;
557+ let local_path = local_dir_path. get_local_file_path ( & agent_id, STORE_KEY_LOCAL_DATA_CONFIG ) ;
558+
559+ // Expectations
560+ file_rw. should_not_read_file_not_found ( & remote_path, "some_error_message" . to_string ( ) ) ;
561+
562+ let yaml_config_content = "some_config: true\n another_item: false" ;
563+ file_rw. should_read ( & local_path, yaml_config_content. to_string ( ) ) ;
564+
565+ let file_store = Arc :: new ( FileStore :: new (
566+ file_rw,
567+ dir_manager,
568+ local_dir_path. into ( ) ,
569+ remote_dir_path. into ( ) ,
570+ ) ) ;
571+ let repo = GenericConfigRepository :: new ( file_store) . with_remote ( ) ;
572+
573+ let config = repo
574+ . load_remote_fallback_local ( & agent_id, & default_capabilities ( ) )
575+ . expect ( "unexpected error loading config" )
576+ . expect ( "expected some configuration, got None" ) ;
577+
578+ assert_eq ! (
579+ config. get_yaml_config( ) . get( "some_config" ) . unwrap( ) ,
580+ & Value :: Bool ( true )
581+ ) ;
582+ assert_eq ! (
583+ config. get_yaml_config( ) . get( "another_item" ) . unwrap( ) ,
584+ & Value :: Bool ( false )
585+ ) ;
586+ }
587+
588+ #[ rstest]
589+ fn test_load_local_file_not_found_should_return_none ( agent_id : AgentID ) {
590+ let mut file_rw = MockLocalFile :: new ( ) ;
591+ let dir_manager = MockDirectoryManager :: new ( ) ;
592+ let remote_dir_path = PathBuf :: from ( "some/remote/path/" ) ;
593+ let local_dir_path = LocalDir :: from ( PathBuf :: from ( "some/local/path/" ) ) ;
594+ let local_path = local_dir_path. get_local_file_path ( & agent_id, STORE_KEY_LOCAL_DATA_CONFIG ) ;
595+
596+ // Expectations
597+ file_rw. should_not_read_file_not_found ( & local_path, "some message" . to_string ( ) ) ;
598+
599+ let file_store = Arc :: new ( FileStore :: new (
600+ file_rw,
601+ dir_manager,
602+ local_dir_path. into ( ) ,
603+ remote_dir_path,
604+ ) ) ;
605+ let repo = GenericConfigRepository :: new ( file_store) ;
606+
607+ let yaml_config = repo
608+ . load_remote_fallback_local ( & agent_id, & default_capabilities ( ) )
609+ . unwrap ( ) ;
610+
611+ assert ! ( yaml_config. is_none( ) ) ;
612+ }
613+
614+ #[ rstest]
615+ #[ case:: remote_enabled( true ) ]
616+ #[ case:: remote_disabled( false ) ]
617+ fn test_load_io_error ( #[ case] remote_enabled : bool , agent_id : AgentID ) {
618+ let mut file_rw = MockLocalFile :: new ( ) ;
619+ let dir_manager = MockDirectoryManager :: new ( ) ;
620+ let remote_dir_path = RemoteDir :: from ( PathBuf :: from ( "some/remote/path/" ) ) ;
621+ let local_dir_path = LocalDir :: from ( PathBuf :: from ( "some/local/path/" ) ) ;
622+ let remote_test_path =
623+ remote_dir_path. get_remote_file_path ( & agent_id, STORE_KEY_OPAMP_DATA_CONFIG ) ;
624+ let local_test_path =
625+ local_dir_path. get_local_file_path ( & agent_id, STORE_KEY_LOCAL_DATA_CONFIG ) ;
626+
627+ // Expectations
628+ if remote_enabled {
629+ file_rw. should_not_read_io_error ( & remote_test_path) ;
630+ } else {
631+ file_rw. should_not_read_io_error ( & local_test_path) ;
632+ }
633+
634+ let file_store = Arc :: new ( FileStore :: new (
635+ file_rw,
636+ dir_manager,
637+ local_dir_path. into ( ) ,
638+ remote_dir_path. into ( ) ,
639+ ) ) ;
640+ let repo = if remote_enabled {
641+ GenericConfigRepository :: new ( file_store) . with_remote ( )
642+ } else {
643+ GenericConfigRepository :: new ( file_store)
644+ } ;
645+
646+ let result = repo. load_remote_fallback_local ( & agent_id, & default_capabilities ( ) ) ;
647+ let err = result. unwrap_err ( ) ;
648+ assert_matches ! ( err, ConfigRepositoryError :: LoadError ( s) => {
649+ assert!( s. contains( "permission denied" ) ) ; // the error returned by `should_not_read_io_error`
650+ } ) ;
651+ }
652+
653+ #[ rstest]
654+ fn test_store_remote ( agent_id : AgentID ) {
655+ let mut file_rw = MockLocalFile :: new ( ) ;
656+ let mut dir_manager = MockDirectoryManager :: new ( ) ;
657+ let remote_dir_path = RemoteDir :: from ( PathBuf :: from ( "some/remote/path/" ) ) ;
658+ let local_dir_path = LocalDir :: from ( PathBuf :: from ( "some/local/path/" ) ) ;
659+ let remote_path =
660+ remote_dir_path. get_remote_file_path ( & agent_id, STORE_KEY_OPAMP_DATA_CONFIG ) ;
661+
662+ // Expectations
663+ dir_manager. should_create ( remote_path. parent ( ) . unwrap ( ) ) ;
664+ file_rw. should_write (
665+ & remote_path,
666+ "config:\n one_item: one value\n hash: a-hash\n state: applying\n " . to_string ( ) ,
667+ ) ;
668+
669+ let file_store = Arc :: new ( FileStore :: new (
670+ file_rw,
671+ dir_manager,
672+ local_dir_path. into ( ) ,
673+ remote_dir_path. into ( ) ,
674+ ) ) ;
675+
676+ let repo = GenericConfigRepository :: new ( file_store) ;
677+
678+ let yaml_config = YAMLConfig :: new ( HashMap :: from ( [ ( "one_item" . into ( ) , "one value" . into ( ) ) ] ) ) ;
679+ let remote_config = RemoteConfig {
680+ config : yaml_config,
681+ hash : Hash :: from ( "a-hash" ) ,
682+ state : ConfigState :: Applying ,
683+ } ;
684+ repo. store_remote ( & agent_id, & remote_config) . unwrap ( ) ;
685+ }
686+
687+ #[ rstest]
688+ fn test_store_remote_error_creating_dir ( agent_id : AgentID ) {
689+ let file_rw = MockLocalFile :: new ( ) ;
690+ let mut dir_manager = MockDirectoryManager :: new ( ) ;
691+ let remote_dir_path = RemoteDir :: from ( PathBuf :: from ( "some/remote/path/" ) ) ;
692+ let local_dir_path = LocalDir :: from ( PathBuf :: from ( "some/local/path/" ) ) ;
693+ let remote_path =
694+ remote_dir_path. get_remote_file_path ( & agent_id, STORE_KEY_OPAMP_DATA_CONFIG ) ;
695+
696+ // Expectations
697+ dir_manager. should_not_create (
698+ remote_path. parent ( ) . unwrap ( ) ,
699+ DirectoryManagementError :: ErrorCreatingDirectory (
700+ "dir name" . to_string ( ) ,
701+ "oh now..." . to_string ( ) ,
702+ ) ,
703+ ) ;
704+
705+ let file_store = Arc :: new ( FileStore :: new (
706+ file_rw,
707+ dir_manager,
708+ local_dir_path. into ( ) ,
709+ remote_dir_path. into ( ) ,
710+ ) ) ;
711+ let repo = GenericConfigRepository :: new ( file_store) ;
712+
713+ let yaml_config = YAMLConfig :: new ( HashMap :: from ( [ ( "one_item" . into ( ) , "one value" . into ( ) ) ] ) ) ;
714+ let remote_config = RemoteConfig {
715+ config : yaml_config,
716+ hash : Hash :: from ( "a-hash" ) ,
717+ state : ConfigState :: Applying ,
718+ } ;
719+ let result = repo. store_remote ( & agent_id, & remote_config) ;
720+ assert_matches ! ( result, Err ( ConfigRepositoryError :: StoreError ( _) ) ) ;
721+ }
722+
723+ #[ rstest]
724+ fn test_store_remote_error_writing_file ( agent_id : AgentID ) {
725+ let mut file_rw = MockLocalFile :: new ( ) ;
726+ let mut dir_manager = MockDirectoryManager :: new ( ) ;
727+ let remote_dir_path = RemoteDir :: from ( PathBuf :: from ( "some/remote/path/" ) ) ;
728+ let local_dir_path = LocalDir :: from ( PathBuf :: from ( "some/local/path/" ) ) ;
729+ let remote_path =
730+ remote_dir_path. get_remote_file_path ( & agent_id, STORE_KEY_OPAMP_DATA_CONFIG ) ;
731+
732+ // Expectations
733+ dir_manager. should_create ( remote_path. parent ( ) . unwrap ( ) ) ;
734+ file_rw. should_not_write (
735+ & remote_path,
736+ "config:\n one_item: one value\n hash: a-hash\n state: applying\n " . to_string ( ) ,
737+ ) ;
738+
739+ let file_store = Arc :: new ( FileStore :: new (
740+ file_rw,
741+ dir_manager,
742+ local_dir_path. into ( ) ,
743+ remote_dir_path. into ( ) ,
744+ ) ) ;
745+ let repo = GenericConfigRepository :: new ( file_store) ;
746+
747+ let yaml_config = YAMLConfig :: new ( HashMap :: from ( [ ( "one_item" . into ( ) , "one value" . into ( ) ) ] ) ) ;
748+ let remote_config = RemoteConfig {
749+ config : yaml_config,
750+ hash : Hash :: from ( "a-hash" ) ,
751+ state : ConfigState :: Applying ,
752+ } ;
753+ let result = repo. store_remote ( & agent_id, & remote_config) ;
754+ assert_matches ! ( result, Err ( ConfigRepositoryError :: StoreError ( _) ) ) ;
755+ }
756+
757+ #[ rstest]
758+ fn test_delete_remote ( agent_id : AgentID ) {
759+ // TODO add a test without mocks checking actual deletion
760+ let file_rw = MockLocalFile :: default ( ) ;
761+ let dir_manager = MockDirectoryManager :: new ( ) ;
762+ let remote_dir_path = PathBuf :: from ( "some/remote/path/" ) ;
763+ let local_dir_path = PathBuf :: from ( "some/local/path/" ) ;
764+ let file_store = Arc :: new ( FileStore :: new (
765+ file_rw,
766+ dir_manager,
767+ local_dir_path,
768+ remote_dir_path,
769+ ) ) ;
770+ let repo = GenericConfigRepository :: new ( file_store) ;
771+ repo. delete_remote ( & agent_id) . unwrap ( ) ;
772+ }
773+
477774 // HELPERS
478775
479776 const HOSTNAME : & str = "test-hostname" ;
0 commit comments