@@ -336,5 +336,290 @@ public void Invoke_WithInvalidSinkExtension_ThrowsException()
336336 // Should throw exception when sink extension is not found
337337 Assert . ThrowsException < InvalidOperationException > ( ( ) => handler . Invoke ( new InvocationContext ( parseResult ) ) ) ;
338338 }
339+
340+ [ TestMethod ]
341+ public void Invoke_WithSameCosmosSourceAndSinkWithRecreateContainer_ThrowsException ( )
342+ {
343+ const string cosmosExtension = "Cosmos-nosql" ;
344+ const string connectionString = "AccountEndpoint=https://test.documents.azure.com:443/;AccountKey=test" ;
345+ const string database = "testDb" ;
346+ const string container = "testContainer" ;
347+
348+ IConfigurationRoot configuration = new ConfigurationBuilder ( )
349+ . AddInMemoryCollection ( new Dictionary < string , string >
350+ {
351+ { "Source" , cosmosExtension } ,
352+ { "Sink" , cosmosExtension } ,
353+ { "SourceSettings:ConnectionString" , connectionString } ,
354+ { "SourceSettings:Database" , database } ,
355+ { "SourceSettings:Container" , container } ,
356+ { "SinkSettings:ConnectionString" , connectionString } ,
357+ { "SinkSettings:Database" , database } ,
358+ { "SinkSettings:Container" , container } ,
359+ { "SinkSettings:RecreateContainer" , "true" } ,
360+ } )
361+ . Build ( ) ;
362+
363+ var loader = new Mock < IExtensionLoader > ( ) ;
364+ var sourceExtension = new Mock < IDataSourceExtension > ( ) ;
365+ sourceExtension . SetupGet ( ds => ds . DisplayName ) . Returns ( cosmosExtension ) ;
366+ loader
367+ . Setup ( l => l . LoadExtensions < IDataSourceExtension > ( It . IsAny < CompositionContainer > ( ) ) )
368+ . Returns ( new List < IDataSourceExtension > { sourceExtension . Object } ) ;
369+
370+ var sinkExtension = new Mock < IDataSinkExtension > ( ) ;
371+ sinkExtension . SetupGet ( ds => ds . DisplayName ) . Returns ( cosmosExtension ) ;
372+ loader
373+ . Setup ( l => l . LoadExtensions < IDataSinkExtension > ( It . IsAny < CompositionContainer > ( ) ) )
374+ . Returns ( new List < IDataSinkExtension > { sinkExtension . Object } ) ;
375+
376+ var handler = new RunCommand . CommandHandler ( loader . Object ,
377+ configuration ,
378+ NullLoggerFactory . Instance ) ;
379+
380+ var parseResult = new RootCommand ( ) . Parse ( Array . Empty < string > ( ) ) ;
381+
382+ // Should throw exception when same container is used for source and sink with RecreateContainer
383+ var exception = Assert . ThrowsException < InvalidOperationException > ( ( ) => handler . Invoke ( new InvocationContext ( parseResult ) ) ) ;
384+ Assert . IsTrue ( exception . Message . Contains ( "same Cosmos DB container" ) ) ;
385+ Assert . IsTrue ( exception . Message . Contains ( "RecreateContainer" ) ) ;
386+ }
387+
388+ [ TestMethod ]
389+ public void Invoke_WithSameCosmosSourceAndSinkWithoutRecreateContainer_Succeeds ( )
390+ {
391+ const string cosmosExtension = "Cosmos-nosql" ;
392+ const string connectionString = "AccountEndpoint=https://test.documents.azure.com:443/;AccountKey=test" ;
393+ const string database = "testDb" ;
394+ const string container = "testContainer" ;
395+
396+ IConfigurationRoot configuration = new ConfigurationBuilder ( )
397+ . AddInMemoryCollection ( new Dictionary < string , string >
398+ {
399+ { "Source" , cosmosExtension } ,
400+ { "Sink" , cosmosExtension } ,
401+ { "SourceSettings:ConnectionString" , connectionString } ,
402+ { "SourceSettings:Database" , database } ,
403+ { "SourceSettings:Container" , container } ,
404+ { "SinkSettings:ConnectionString" , connectionString } ,
405+ { "SinkSettings:Database" , database } ,
406+ { "SinkSettings:Container" , container } ,
407+ { "SinkSettings:RecreateContainer" , "false" } ,
408+ } )
409+ . Build ( ) ;
410+
411+ var loader = new Mock < IExtensionLoader > ( ) ;
412+ var sourceExtension = new Mock < IDataSourceExtension > ( ) ;
413+ sourceExtension . SetupGet ( ds => ds . DisplayName ) . Returns ( cosmosExtension ) ;
414+ loader
415+ . Setup ( l => l . LoadExtensions < IDataSourceExtension > ( It . IsAny < CompositionContainer > ( ) ) )
416+ . Returns ( new List < IDataSourceExtension > { sourceExtension . Object } ) ;
417+
418+ var sinkExtension = new Mock < IDataSinkExtension > ( ) ;
419+ sinkExtension . SetupGet ( ds => ds . DisplayName ) . Returns ( cosmosExtension ) ;
420+ loader
421+ . Setup ( l => l . LoadExtensions < IDataSinkExtension > ( It . IsAny < CompositionContainer > ( ) ) )
422+ . Returns ( new List < IDataSinkExtension > { sinkExtension . Object } ) ;
423+
424+ var handler = new RunCommand . CommandHandler ( loader . Object ,
425+ configuration ,
426+ NullLoggerFactory . Instance ) ;
427+
428+ var parseResult = new RootCommand ( ) . Parse ( Array . Empty < string > ( ) ) ;
429+ var result = handler . Invoke ( new InvocationContext ( parseResult ) ) ;
430+
431+ // Should succeed when RecreateContainer is false even with same container
432+ Assert . AreEqual ( 0 , result ) ;
433+ sourceExtension . Verify ( se => se . ReadAsync ( It . IsAny < IConfiguration > ( ) , It . IsAny < ILogger > ( ) , It . IsAny < CancellationToken > ( ) ) , Times . Once ) ;
434+ }
435+
436+ [ TestMethod ]
437+ public void Invoke_WithSameCosmosSourceAndSinkDifferentDatabase_Succeeds ( )
438+ {
439+ const string cosmosExtension = "Cosmos-nosql" ;
440+ const string connectionString = "AccountEndpoint=https://test.documents.azure.com:443/;AccountKey=test" ;
441+ const string sourceDatabase = "sourceDb" ;
442+ const string sinkDatabase = "sinkDb" ;
443+ const string container = "testContainer" ;
444+
445+ IConfigurationRoot configuration = new ConfigurationBuilder ( )
446+ . AddInMemoryCollection ( new Dictionary < string , string >
447+ {
448+ { "Source" , cosmosExtension } ,
449+ { "Sink" , cosmosExtension } ,
450+ { "SourceSettings:ConnectionString" , connectionString } ,
451+ { "SourceSettings:Database" , sourceDatabase } ,
452+ { "SourceSettings:Container" , container } ,
453+ { "SinkSettings:ConnectionString" , connectionString } ,
454+ { "SinkSettings:Database" , sinkDatabase } ,
455+ { "SinkSettings:Container" , container } ,
456+ { "SinkSettings:RecreateContainer" , "true" } ,
457+ } )
458+ . Build ( ) ;
459+
460+ var loader = new Mock < IExtensionLoader > ( ) ;
461+ var sourceExtension = new Mock < IDataSourceExtension > ( ) ;
462+ sourceExtension . SetupGet ( ds => ds . DisplayName ) . Returns ( cosmosExtension ) ;
463+ loader
464+ . Setup ( l => l . LoadExtensions < IDataSourceExtension > ( It . IsAny < CompositionContainer > ( ) ) )
465+ . Returns ( new List < IDataSourceExtension > { sourceExtension . Object } ) ;
466+
467+ var sinkExtension = new Mock < IDataSinkExtension > ( ) ;
468+ sinkExtension . SetupGet ( ds => ds . DisplayName ) . Returns ( cosmosExtension ) ;
469+ loader
470+ . Setup ( l => l . LoadExtensions < IDataSinkExtension > ( It . IsAny < CompositionContainer > ( ) ) )
471+ . Returns ( new List < IDataSinkExtension > { sinkExtension . Object } ) ;
472+
473+ var handler = new RunCommand . CommandHandler ( loader . Object ,
474+ configuration ,
475+ NullLoggerFactory . Instance ) ;
476+
477+ var parseResult = new RootCommand ( ) . Parse ( Array . Empty < string > ( ) ) ;
478+ var result = handler . Invoke ( new InvocationContext ( parseResult ) ) ;
479+
480+ // Should succeed when database is different
481+ Assert . AreEqual ( 0 , result ) ;
482+ sourceExtension . Verify ( se => se . ReadAsync ( It . IsAny < IConfiguration > ( ) , It . IsAny < ILogger > ( ) , It . IsAny < CancellationToken > ( ) ) , Times . Once ) ;
483+ }
484+
485+ [ TestMethod ]
486+ public void Invoke_WithSameCosmosSourceAndSinkDifferentContainer_Succeeds ( )
487+ {
488+ const string cosmosExtension = "Cosmos-nosql" ;
489+ const string connectionString = "AccountEndpoint=https://test.documents.azure.com:443/;AccountKey=test" ;
490+ const string database = "testDb" ;
491+ const string sourceContainer = "sourceContainer" ;
492+ const string sinkContainer = "sinkContainer" ;
493+
494+ IConfigurationRoot configuration = new ConfigurationBuilder ( )
495+ . AddInMemoryCollection ( new Dictionary < string , string >
496+ {
497+ { "Source" , cosmosExtension } ,
498+ { "Sink" , cosmosExtension } ,
499+ { "SourceSettings:ConnectionString" , connectionString } ,
500+ { "SourceSettings:Database" , database } ,
501+ { "SourceSettings:Container" , sourceContainer } ,
502+ { "SinkSettings:ConnectionString" , connectionString } ,
503+ { "SinkSettings:Database" , database } ,
504+ { "SinkSettings:Container" , sinkContainer } ,
505+ { "SinkSettings:RecreateContainer" , "true" } ,
506+ } )
507+ . Build ( ) ;
508+
509+ var loader = new Mock < IExtensionLoader > ( ) ;
510+ var sourceExtension = new Mock < IDataSourceExtension > ( ) ;
511+ sourceExtension . SetupGet ( ds => ds . DisplayName ) . Returns ( cosmosExtension ) ;
512+ loader
513+ . Setup ( l => l . LoadExtensions < IDataSourceExtension > ( It . IsAny < CompositionContainer > ( ) ) )
514+ . Returns ( new List < IDataSourceExtension > { sourceExtension . Object } ) ;
515+
516+ var sinkExtension = new Mock < IDataSinkExtension > ( ) ;
517+ sinkExtension . SetupGet ( ds => ds . DisplayName ) . Returns ( cosmosExtension ) ;
518+ loader
519+ . Setup ( l => l . LoadExtensions < IDataSinkExtension > ( It . IsAny < CompositionContainer > ( ) ) )
520+ . Returns ( new List < IDataSinkExtension > { sinkExtension . Object } ) ;
521+
522+ var handler = new RunCommand . CommandHandler ( loader . Object ,
523+ configuration ,
524+ NullLoggerFactory . Instance ) ;
525+
526+ var parseResult = new RootCommand ( ) . Parse ( Array . Empty < string > ( ) ) ;
527+ var result = handler . Invoke ( new InvocationContext ( parseResult ) ) ;
528+
529+ // Should succeed when container is different
530+ Assert . AreEqual ( 0 , result ) ;
531+ sourceExtension . Verify ( se => se . ReadAsync ( It . IsAny < IConfiguration > ( ) , It . IsAny < ILogger > ( ) , It . IsAny < CancellationToken > ( ) ) , Times . Once ) ;
532+ }
533+
534+ [ TestMethod ]
535+ public void Invoke_WithDifferentExtensionTypesAndRecreateContainer_Succeeds ( )
536+ {
537+ const string sourceExtension = "Json" ;
538+ const string sinkExtension = "Cosmos-nosql" ;
539+
540+ IConfigurationRoot configuration = new ConfigurationBuilder ( )
541+ . AddInMemoryCollection ( new Dictionary < string , string >
542+ {
543+ { "Source" , sourceExtension } ,
544+ { "Sink" , sinkExtension } ,
545+ { "SourceSettings:FilePath" , "test.json" } ,
546+ { "SinkSettings:ConnectionString" , "AccountEndpoint=https://test.documents.azure.com:443/;AccountKey=test" } ,
547+ { "SinkSettings:Database" , "testDb" } ,
548+ { "SinkSettings:Container" , "testContainer" } ,
549+ { "SinkSettings:RecreateContainer" , "true" } ,
550+ } )
551+ . Build ( ) ;
552+
553+ var loader = new Mock < IExtensionLoader > ( ) ;
554+ var source = new Mock < IDataSourceExtension > ( ) ;
555+ source . SetupGet ( ds => ds . DisplayName ) . Returns ( sourceExtension ) ;
556+ loader
557+ . Setup ( l => l . LoadExtensions < IDataSourceExtension > ( It . IsAny < CompositionContainer > ( ) ) )
558+ . Returns ( new List < IDataSourceExtension > { source . Object } ) ;
559+
560+ var sink = new Mock < IDataSinkExtension > ( ) ;
561+ sink . SetupGet ( ds => ds . DisplayName ) . Returns ( sinkExtension ) ;
562+ loader
563+ . Setup ( l => l . LoadExtensions < IDataSinkExtension > ( It . IsAny < CompositionContainer > ( ) ) )
564+ . Returns ( new List < IDataSinkExtension > { sink . Object } ) ;
565+
566+ var handler = new RunCommand . CommandHandler ( loader . Object ,
567+ configuration ,
568+ NullLoggerFactory . Instance ) ;
569+
570+ var parseResult = new RootCommand ( ) . Parse ( Array . Empty < string > ( ) ) ;
571+ var result = handler . Invoke ( new InvocationContext ( parseResult ) ) ;
572+
573+ // Should succeed when source and sink are different extension types
574+ Assert . AreEqual ( 0 , result ) ;
575+ source . Verify ( se => se . ReadAsync ( It . IsAny < IConfiguration > ( ) , It . IsAny < ILogger > ( ) , It . IsAny < CancellationToken > ( ) ) , Times . Once ) ;
576+ }
577+
578+ [ TestMethod ]
579+ public void Invoke_WithSameCosmosSourceAndSinkUsingAccountEndpoint_ThrowsException ( )
580+ {
581+ const string cosmosExtension = "Cosmos-nosql" ;
582+ const string accountEndpoint = "https://test.documents.azure.com:443/" ;
583+ const string database = "testDb" ;
584+ const string container = "testContainer" ;
585+
586+ IConfigurationRoot configuration = new ConfigurationBuilder ( )
587+ . AddInMemoryCollection ( new Dictionary < string , string >
588+ {
589+ { "Source" , cosmosExtension } ,
590+ { "Sink" , cosmosExtension } ,
591+ { "SourceSettings:AccountEndpoint" , accountEndpoint } ,
592+ { "SourceSettings:Database" , database } ,
593+ { "SourceSettings:Container" , container } ,
594+ { "SinkSettings:AccountEndpoint" , accountEndpoint } ,
595+ { "SinkSettings:Database" , database } ,
596+ { "SinkSettings:Container" , container } ,
597+ { "SinkSettings:RecreateContainer" , "true" } ,
598+ } )
599+ . Build ( ) ;
600+
601+ var loader = new Mock < IExtensionLoader > ( ) ;
602+ var sourceExtension = new Mock < IDataSourceExtension > ( ) ;
603+ sourceExtension . SetupGet ( ds => ds . DisplayName ) . Returns ( cosmosExtension ) ;
604+ loader
605+ . Setup ( l => l . LoadExtensions < IDataSourceExtension > ( It . IsAny < CompositionContainer > ( ) ) )
606+ . Returns ( new List < IDataSourceExtension > { sourceExtension . Object } ) ;
607+
608+ var sinkExtension = new Mock < IDataSinkExtension > ( ) ;
609+ sinkExtension . SetupGet ( ds => ds . DisplayName ) . Returns ( cosmosExtension ) ;
610+ loader
611+ . Setup ( l => l . LoadExtensions < IDataSinkExtension > ( It . IsAny < CompositionContainer > ( ) ) )
612+ . Returns ( new List < IDataSinkExtension > { sinkExtension . Object } ) ;
613+
614+ var handler = new RunCommand . CommandHandler ( loader . Object ,
615+ configuration ,
616+ NullLoggerFactory . Instance ) ;
617+
618+ var parseResult = new RootCommand ( ) . Parse ( Array . Empty < string > ( ) ) ;
619+
620+ // Should throw exception when same container is used with AccountEndpoint
621+ var exception = Assert . ThrowsException < InvalidOperationException > ( ( ) => handler . Invoke ( new InvocationContext ( parseResult ) ) ) ;
622+ Assert . IsTrue ( exception . Message . Contains ( "same Cosmos DB container" ) ) ;
623+ }
339624 }
340625}
0 commit comments