66using AAS . TwinEngine . DataEngine . DomainModel . AasRegistry ;
77using AAS . TwinEngine . DataEngine . DomainModel . Plugin ;
88using AAS . TwinEngine . DataEngine . DomainModel . Shared ;
9+ using AAS . TwinEngine . DataEngine . ServiceConfiguration . Config ;
910
1011using AasCore . Aas3_1 ;
1112
1213using Microsoft . Extensions . Logging ;
14+ using Microsoft . Extensions . Options ;
1315
1416using NSubstitute ;
1517using NSubstitute . ExceptionExtensions ;
@@ -24,9 +26,18 @@ public class ShellDescriptorServiceTests
2426 private readonly IShellDescriptorDataHandler _dataHandler = Substitute . For < IShellDescriptorDataHandler > ( ) ;
2527 private readonly IPluginManifestConflictHandler _pluginManifestConflictHandler = Substitute . For < IPluginManifestConflictHandler > ( ) ;
2628 private readonly ILogger < ShellDescriptorService > _logger = Substitute . For < ILogger < ShellDescriptorService > > ( ) ;
29+ private readonly IOptions < TemplateManagementConfig > _templateManagementConfig ;
2730 private readonly ShellDescriptorService _sut ;
2831
29- public ShellDescriptorServiceTests ( ) => _sut = new ShellDescriptorService ( _templateProvider , _shellTemplateMappingProvider , _dataHandler , _pluginDataHandler , _pluginManifestConflictHandler , _logger ) ;
32+ public ShellDescriptorServiceTests ( )
33+ {
34+ var config = new TemplateManagementConfig
35+ {
36+ AasTemplateRegistry = new ServiceInstance { ConcurrentOperationsLimit = 10 }
37+ } ;
38+ _templateManagementConfig = Options . Create ( config ) ;
39+ _sut = new ShellDescriptorService ( _templateProvider , _shellTemplateMappingProvider , _dataHandler , _pluginDataHandler , _pluginManifestConflictHandler , _logger , _templateManagementConfig ) ;
40+ }
3041
3142 [ Fact ]
3243 public async Task GetAllShellDescriptorsAsync_ReturnsFilledShellDescriptors ( )
@@ -307,8 +318,152 @@ public async Task GetShellDescriptorByIdAsync_ShouldThrowShellDescriptorNotFound
307318
308319 await Assert . ThrowsAsync < ShellDescriptorNotFoundException > ( ( ) => _sut . GetShellDescriptorByIdAsync ( id , cancellationToken ) ) ;
309320 }
321+ [ Fact ]
322+ public async Task GetAllShellDescriptorsAsync_BuildsDescriptorsInParallel ( )
323+ {
324+ var cancellationToken = CancellationToken . None ;
325+ var metadataList = Enumerable . Range ( 1 , 5 )
326+ . Select ( i => new ShellDescriptorMetaData { Id = $ "id{ i } " } )
327+ . ToList ( ) ;
328+ var metaData = new ShellDescriptorsMetaData
329+ {
330+ PagingMetaData = null ,
331+ ShellDescriptors = metadataList
332+ } ;
333+
334+ _pluginManifestConflictHandler . Manifests . Returns ( new List < PluginManifest > ( ) ) ;
335+ _pluginDataHandler . GetDataForAllShellDescriptorsAsync ( null , null , Arg . Any < IReadOnlyList < PluginManifest > > ( ) , cancellationToken )
336+ . Returns ( metaData ) ;
337+ _shellTemplateMappingProvider . GetTemplateId ( Arg . Any < string > ( ) ) . Returns ( "template-1" ) ;
338+ _templateProvider . GetShellDescriptorTemplateAsync ( "template-1" , cancellationToken ) . Returns ( GetShellDescriptorTemplate ( ) ) ;
339+ _dataHandler . FillOut ( Arg . Any < ShellDescriptor > ( ) , Arg . Any < ShellDescriptorMetaData > ( ) )
340+ . Returns ( callInfo =>
341+ {
342+ var value = callInfo . ArgAt < ShellDescriptorMetaData > ( 1 ) ;
343+ return new ShellDescriptor { Id = value . Id } ;
344+ } ) ;
345+
346+ var result = await _sut . GetAllShellDescriptorsAsync ( null , null , cancellationToken ) ;
347+
348+ Assert . NotNull ( result ) ;
349+ Assert . NotNull ( result . Result ) ;
350+ Assert . Equal ( 5 , result . Result . Count ) ;
351+ Assert . All ( result . Result , descriptor => Assert . False ( string . IsNullOrWhiteSpace ( descriptor . Id ) ) ) ;
352+ }
353+
354+ [ Fact ]
355+ public async Task GetAllShellDescriptorsAsync_RespectsMaxConcurrency ( )
356+ {
357+ var cancellationToken = CancellationToken . None ;
358+ const int concurrencyLimit = 2 ;
359+ var config = new TemplateManagementConfig
360+ {
361+ AasTemplateRegistry = new ServiceInstance { ConcurrentOperationsLimit = concurrencyLimit }
362+ } ;
363+ var sut = new ShellDescriptorService (
364+ _templateProvider , _shellTemplateMappingProvider , _dataHandler ,
365+ _pluginDataHandler , _pluginManifestConflictHandler , _logger ,
366+ Options . Create ( config ) ) ;
367+
368+ var currentConcurrency = 0 ;
369+ var maxObservedConcurrency = 0 ;
370+ var lockObj = new object ( ) ;
371+
372+ var metadataList = Enumerable . Range ( 1 , 6 )
373+ . Select ( i => new ShellDescriptorMetaData { Id = $ "id{ i } " } )
374+ . ToList ( ) ;
375+ var metaData = new ShellDescriptorsMetaData
376+ {
377+ PagingMetaData = null ,
378+ ShellDescriptors = metadataList
379+ } ;
380+
381+ _pluginManifestConflictHandler . Manifests . Returns ( new List < PluginManifest > ( ) ) ;
382+ _pluginDataHandler . GetDataForAllShellDescriptorsAsync ( null , null , Arg . Any < IReadOnlyList < PluginManifest > > ( ) , cancellationToken )
383+ . Returns ( metaData ) ;
384+ _shellTemplateMappingProvider . GetTemplateId ( Arg . Any < string > ( ) ) . Returns ( "template-1" ) ;
385+
386+ _templateProvider . GetShellDescriptorTemplateAsync ( "template-1" , cancellationToken )
387+ . Returns ( async callInfo =>
388+ {
389+ lock ( lockObj )
390+ {
391+ currentConcurrency ++ ;
392+ if ( currentConcurrency > maxObservedConcurrency )
393+ maxObservedConcurrency = currentConcurrency ;
394+ }
395+
396+ await Task . Delay ( 50 , cancellationToken ) ;
397+
398+ lock ( lockObj )
399+ {
400+ currentConcurrency -- ;
401+ }
402+
403+ return GetShellDescriptorTemplate ( ) ;
404+ } ) ;
405+
406+ _dataHandler . FillOut ( Arg . Any < ShellDescriptor > ( ) , Arg . Any < ShellDescriptorMetaData > ( ) )
407+ . Returns ( callInfo =>
408+ {
409+ var value = callInfo . ArgAt < ShellDescriptorMetaData > ( 1 ) ;
410+ return new ShellDescriptor { Id = value . Id } ;
411+ } ) ;
412+
413+ var result = await sut . GetAllShellDescriptorsAsync ( null , null , cancellationToken ) ;
414+
415+ Assert . NotNull ( result ) ;
416+ Assert . Equal ( 6 , result . Result ! . Count ) ;
417+ Assert . True ( maxObservedConcurrency <= concurrencyLimit ,
418+ $ "Expected max concurrency <= { concurrencyLimit } , but observed { maxObservedConcurrency } ") ;
419+ }
420+
421+ [ Fact ]
422+ public async Task GetAllShellDescriptorsAsync_SkipsFailedDescriptors_InParallelExecution ( )
423+ {
424+ var cancellationToken = CancellationToken . None ;
425+ var metadataList = new List < ShellDescriptorMetaData >
426+ {
427+ new ( ) { Id = "good-1" } ,
428+ new ( ) { Id = "bad-1" } ,
429+ new ( ) { Id = "good-2" } ,
430+ new ( ) { Id = null } ,
431+ new ( ) { Id = "good-3" }
432+ } ;
433+ var metaData = new ShellDescriptorsMetaData
434+ {
435+ PagingMetaData = null ,
436+ ShellDescriptors = metadataList
437+ } ;
438+
439+ _pluginManifestConflictHandler . Manifests . Returns ( new List < PluginManifest > ( ) ) ;
440+ _pluginDataHandler . GetDataForAllShellDescriptorsAsync ( null , null , Arg . Any < IReadOnlyList < PluginManifest > > ( ) , cancellationToken )
441+ . Returns ( metaData ) ;
442+
443+ _shellTemplateMappingProvider . GetTemplateId ( "good-1" ) . Returns ( "template-1" ) ;
444+ _shellTemplateMappingProvider . GetTemplateId ( "good-2" ) . Returns ( "template-1" ) ;
445+ _shellTemplateMappingProvider . GetTemplateId ( "good-3" ) . Returns ( "template-1" ) ;
446+ _shellTemplateMappingProvider . GetTemplateId ( "bad-1" ) . Throws ( new ResourceNotFoundException ( ) ) ;
447+
448+ _templateProvider . GetShellDescriptorTemplateAsync ( "template-1" , cancellationToken ) . Returns ( GetShellDescriptorTemplate ( ) ) ;
449+ _dataHandler . FillOut ( Arg . Any < ShellDescriptor > ( ) , Arg . Any < ShellDescriptorMetaData > ( ) )
450+ . Returns ( callInfo =>
451+ {
452+ var value = callInfo . ArgAt < ShellDescriptorMetaData > ( 1 ) ;
453+ return new ShellDescriptor { Id = value . Id } ;
454+ } ) ;
455+
456+ var result = await _sut . GetAllShellDescriptorsAsync ( null , null , cancellationToken ) ;
457+
458+ Assert . NotNull ( result ) ;
459+ Assert . NotNull ( result . Result ) ;
460+ Assert . Equal ( 3 , result . Result . Count ) ;
461+ Assert . Contains ( result . Result , d => d . Id == "good-1" ) ;
462+ Assert . Contains ( result . Result , d => d . Id == "good-2" ) ;
463+ Assert . Contains ( result . Result , d => d . Id == "good-3" ) ;
464+ Assert . DoesNotContain ( result . Result , d => d . Id == "bad-1" ) ;
465+ }
310466
311- #region Test Data Helpers
312467
313468 private static List < ShellDescriptorMetaData > GetShellDescriptorDataList ( )
314469 => [
@@ -391,6 +546,4 @@ private static List<ShellDescriptor> GetExpectedShellDescriptors() => [
391546 ]
392547 }
393548 ] ;
394-
395- #endregion
396549}
0 commit comments