@@ -243,6 +243,24 @@ impl Context {
243243 let agent = self . agent . create ( id, url) . await ?;
244244 Ok ( agent)
245245 }
246+
247+ #[ cfg( test) ]
248+ /// Creates a test context with all mocks
249+ pub ( crate ) fn mocked ( ) -> Context {
250+ Context {
251+ term : Term :: stderr ( ) ,
252+ dirs : Arc :: new ( icp:: directories:: UnimplementedMockDirs ) ,
253+ ids : Arc :: new ( crate :: store_id:: MockInMemoryIdStore :: new ( ) ) ,
254+ artifacts : Arc :: new ( crate :: store_artifact:: MockInMemoryArtifactStore :: new ( ) ) ,
255+ project : Arc :: new ( icp:: MockProjectLoader :: minimal ( ) ) ,
256+ identity : Arc :: new ( icp:: identity:: MockIdentityLoader :: anonymous ( ) ) ,
257+ network : Arc :: new ( icp:: network:: MockNetworkAccessor :: localhost ( ) ) ,
258+ agent : Arc :: new ( icp:: agent:: Creator ) ,
259+ builder : Arc :: new ( icp:: canister:: build:: UnimplementedMockBuilder ) ,
260+ syncer : Arc :: new ( icp:: canister:: sync:: UnimplementedMockSyncer ) ,
261+ debug : false ,
262+ }
263+ }
246264}
247265
248266#[ derive( Debug , Snafu ) ]
@@ -337,3 +355,290 @@ pub(crate) enum GetAgentForUrlError {
337355 #[ snafu( transparent) ]
338356 AgentCreate { source : icp:: agent:: CreateError } ,
339357}
358+
359+ #[ cfg( test) ]
360+ mod context_tests {
361+ use super :: * ;
362+ use crate :: store_id:: MockInMemoryIdStore ;
363+ use icp:: { MockProjectLoader , identity:: MockIdentityLoader , network:: MockNetworkAccessor } ;
364+
365+ #[ tokio:: test]
366+ async fn test_get_identity_default ( ) {
367+ let ctx = Context :: mocked ( ) ;
368+
369+ let result = ctx. get_identity ( & IdentitySelection :: Default ) . await ;
370+
371+ assert ! ( result. is_ok( ) ) ;
372+ }
373+
374+ #[ tokio:: test]
375+ async fn test_get_identity_anonymous ( ) {
376+ let ctx = Context :: mocked ( ) ;
377+
378+ let result = ctx. get_identity ( & IdentitySelection :: Anonymous ) . await ;
379+
380+ assert ! ( result. is_ok( ) ) ;
381+ }
382+
383+ #[ tokio:: test]
384+ async fn test_get_identity_named ( ) {
385+ let alice_identity: Arc < dyn Identity > = Arc :: new ( ic_agent:: identity:: AnonymousIdentity ) ;
386+
387+ let ctx = Context {
388+ identity : Arc :: new (
389+ MockIdentityLoader :: anonymous ( ) . with_identity ( "alice" , Arc :: clone ( & alice_identity) ) ,
390+ ) ,
391+ ..Context :: mocked ( )
392+ } ;
393+
394+ let result = ctx
395+ . get_identity ( & IdentitySelection :: Named ( "alice" . to_string ( ) ) )
396+ . await ;
397+
398+ assert ! ( result. is_ok( ) ) ;
399+ }
400+
401+ #[ tokio:: test]
402+ async fn test_get_identity_named_not_found ( ) {
403+ let ctx = Context :: mocked ( ) ;
404+
405+ let result = ctx
406+ . get_identity ( & IdentitySelection :: Named ( "nonexistent" . to_string ( ) ) )
407+ . await ;
408+
409+ assert ! ( matches!(
410+ result,
411+ Err ( GetIdentityError :: IdentityLoad {
412+ identity: IdentitySelection :: Named ( _) ,
413+ source: icp:: identity:: LoadError :: LoadIdentity ( _)
414+ } )
415+ ) ) ;
416+ }
417+
418+ #[ tokio:: test]
419+ async fn test_get_environment_success ( ) {
420+ let ctx = Context {
421+ project : Arc :: new ( MockProjectLoader :: complex ( ) ) ,
422+ ..Context :: mocked ( )
423+ } ;
424+
425+ let env = ctx. get_environment ( "dev" ) . await . unwrap ( ) ;
426+
427+ assert_eq ! ( env. name, "dev" ) ;
428+ }
429+
430+ #[ tokio:: test]
431+ async fn test_get_environment_not_found ( ) {
432+ let ctx = Context :: mocked ( ) ;
433+
434+ let result = ctx. get_environment ( "nonexistent" ) . await ;
435+
436+ assert ! ( matches!(
437+ result,
438+ Err ( GetEnvironmentError :: EnvironmentNotFound { ref name } ) if name == "nonexistent"
439+ ) ) ;
440+ }
441+
442+ #[ tokio:: test]
443+ async fn test_get_network_success ( ) {
444+ let ctx = Context {
445+ project : Arc :: new ( MockProjectLoader :: complex ( ) ) ,
446+ ..Context :: mocked ( )
447+ } ;
448+
449+ let network = ctx. get_network ( "local" ) . await . unwrap ( ) ;
450+
451+ assert_eq ! ( network. name, "local" ) ;
452+ }
453+
454+ #[ tokio:: test]
455+ async fn test_get_network_not_found ( ) {
456+ let ctx = Context :: mocked ( ) ;
457+
458+ let result = ctx. get_network ( "nonexistent" ) . await ;
459+
460+ assert ! ( matches!(
461+ result,
462+ Err ( GetNetworkError :: NetworkNotFound { ref name } ) if name == "nonexistent"
463+ ) ) ;
464+ }
465+
466+ #[ tokio:: test]
467+ async fn test_get_canister_id_for_env_success ( ) {
468+ use crate :: store_id:: { Access as IdAccess , Key } ;
469+ use candid:: Principal ;
470+
471+ let ids_store = Arc :: new ( MockInMemoryIdStore :: new ( ) ) ;
472+
473+ // Register a canister ID for the dev environment
474+ let canister_id = Principal :: from_text ( "rrkah-fqaaa-aaaaa-aaaaq-cai" ) . unwrap ( ) ;
475+ ids_store
476+ . register (
477+ & Key {
478+ network : "local" . to_string ( ) ,
479+ environment : "dev" . to_string ( ) ,
480+ canister : "backend" . to_string ( ) ,
481+ } ,
482+ & canister_id,
483+ )
484+ . unwrap ( ) ;
485+
486+ let ctx = Context {
487+ project : Arc :: new ( MockProjectLoader :: complex ( ) ) ,
488+ ids : ids_store,
489+ ..Context :: mocked ( )
490+ } ;
491+
492+ let cid = ctx. get_canister_id_for_env ( "backend" , "dev" ) . await . unwrap ( ) ;
493+
494+ assert_eq ! ( cid, canister_id) ;
495+ }
496+
497+ #[ tokio:: test]
498+ async fn test_get_canister_id_for_env_canister_not_in_env ( ) {
499+ let ctx = Context {
500+ project : Arc :: new ( MockProjectLoader :: complex ( ) ) ,
501+ ..Context :: mocked ( )
502+ } ;
503+
504+ // "database" is only in "dev" environment, not in "test"
505+ let result = ctx. get_canister_id_for_env ( "database" , "test" ) . await ;
506+
507+ assert ! ( matches!(
508+ result,
509+ Err ( GetCanisterIdForEnvError :: CanisterNotFoundInEnv {
510+ ref canister_name,
511+ ref environment_name,
512+ } ) if canister_name == "database" && environment_name == "test"
513+ ) ) ;
514+ }
515+
516+ #[ tokio:: test]
517+ async fn test_get_canister_id_for_env_id_not_registered ( ) {
518+ let ctx = Context {
519+ project : Arc :: new ( MockProjectLoader :: complex ( ) ) ,
520+ ..Context :: mocked ( )
521+ } ;
522+
523+ // Environment exists and canister is in it, but ID not registered
524+ let result = ctx. get_canister_id_for_env ( "backend" , "dev" ) . await ;
525+
526+ assert ! ( matches!(
527+ result,
528+ Err ( GetCanisterIdForEnvError :: CanisterIdLookup {
529+ ref canister_name,
530+ ref environment_name,
531+ ..
532+ } ) if canister_name == "backend" && environment_name == "dev"
533+ ) ) ;
534+ }
535+
536+ #[ tokio:: test]
537+ async fn test_get_agent_for_env_uses_environment_network ( ) {
538+ use icp:: network:: access:: NetworkAccess ;
539+
540+ let staging_root_key = vec ! [ 1 , 2 , 3 ] ;
541+
542+ // Complex project has "test" environment which uses "staging" network
543+ let ctx = Context {
544+ project : Arc :: new ( MockProjectLoader :: complex ( ) ) ,
545+ network : Arc :: new (
546+ MockNetworkAccessor :: localhost ( )
547+ . with_network (
548+ "local" ,
549+ NetworkAccess {
550+ default_effective_canister_id : None ,
551+ root_key : None ,
552+ url : "http://localhost:8000" . to_string ( ) ,
553+ } ,
554+ )
555+ . with_network (
556+ "staging" ,
557+ NetworkAccess {
558+ default_effective_canister_id : None ,
559+ root_key : Some ( staging_root_key. clone ( ) ) ,
560+ url : "http://staging:9000" . to_string ( ) ,
561+ } ,
562+ ) ,
563+ ) ,
564+ ..Context :: mocked ( )
565+ } ;
566+
567+ let agent = ctx
568+ . get_agent_for_env ( & IdentitySelection :: Anonymous , "test" )
569+ . await
570+ . unwrap ( ) ;
571+
572+ assert_eq ! ( agent. read_root_key( ) , staging_root_key) ;
573+ }
574+
575+ #[ tokio:: test]
576+ async fn test_get_agent_for_env_environment_not_found ( ) {
577+ let ctx = Context :: mocked ( ) ;
578+
579+ let result = ctx
580+ . get_agent_for_env ( & IdentitySelection :: Anonymous , "nonexistent" )
581+ . await ;
582+
583+ assert ! ( matches!(
584+ result,
585+ Err ( GetAgentForEnvError :: GetEnvironment {
586+ source: GetEnvironmentError :: EnvironmentNotFound { .. }
587+ } )
588+ ) ) ;
589+ }
590+
591+ #[ tokio:: test]
592+ async fn test_get_agent_for_network_success ( ) {
593+ use icp:: network:: access:: NetworkAccess ;
594+
595+ let root_key = vec ! [ 1 , 2 , 3 ] ;
596+
597+ let ctx = Context {
598+ project : Arc :: new ( MockProjectLoader :: complex ( ) ) ,
599+ network : Arc :: new ( MockNetworkAccessor :: localhost ( ) . with_network (
600+ "local" ,
601+ NetworkAccess {
602+ default_effective_canister_id : None ,
603+ root_key : Some ( root_key. clone ( ) ) ,
604+ url : "http://localhost:8000" . to_string ( ) ,
605+ } ,
606+ ) ) ,
607+ ..Context :: mocked ( )
608+ } ;
609+
610+ let agent = ctx
611+ . get_agent_for_network ( & IdentitySelection :: Anonymous , "local" )
612+ . await
613+ . unwrap ( ) ;
614+
615+ assert_eq ! ( agent. read_root_key( ) , root_key) ;
616+ }
617+
618+ #[ tokio:: test]
619+ async fn test_get_agent_for_network_network_not_found ( ) {
620+ let ctx = Context :: mocked ( ) ;
621+
622+ let result = ctx
623+ . get_agent_for_network ( & IdentitySelection :: Anonymous , "nonexistent" )
624+ . await ;
625+
626+ assert ! ( matches!(
627+ result,
628+ Err ( GetAgentForNetworkError :: GetNetwork {
629+ source: GetNetworkError :: NetworkNotFound { .. }
630+ } )
631+ ) ) ;
632+ }
633+
634+ #[ tokio:: test]
635+ async fn test_get_agent_for_url_success ( ) {
636+ let ctx = Context :: mocked ( ) ;
637+
638+ let result = ctx
639+ . get_agent_for_url ( & IdentitySelection :: Anonymous , "http://localhost:8000" )
640+ . await ;
641+
642+ assert ! ( result. is_ok( ) ) ;
643+ }
644+ }
0 commit comments