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,106 @@ 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+ }
310420
311- #region Test Data Helpers
312421
313422 private static List < ShellDescriptorMetaData > GetShellDescriptorDataList ( )
314423 => [
@@ -391,6 +500,4 @@ private static List<ShellDescriptor> GetExpectedShellDescriptors() => [
391500 ]
392501 }
393502 ] ;
394-
395- #endregion
396503}
0 commit comments