Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
38ca92c
feat: implement CachedGetRequestClient with hybrid caching and user-s…
mm-kgi Jul 23, 2026
2a99f7a
feat: implement ShellDescriptorService to retrieve and populate AAS s…
mm-kgi Jul 23, 2026
bc8e4db
feat: implement CachedGetRequestClient for resilient HTTP requests wi…
mm-kgi Jul 23, 2026
7d096ae
feat: implement ShellDescriptorService to retrieve and construct shel…
mm-kgi Jul 23, 2026
1562e65
refactor: remove redundant test for skipping failed shell descriptors…
mm-kgi Jul 23, 2026
2dd641c
refactor: simplify SendGetRequestAsync method implementation
mm-kgi Jul 23, 2026
0b1f0e0
feat: implement CachedGetRequestClient with HybridCache and user-cont…
mm-kgi Jul 23, 2026
8e1e3e2
Merge branch 'develop' into 1474-caching-template-retrieval
mm-kgi Jul 23, 2026
1595b62
For testing, will be removed
mm-kgi Jul 24, 2026
9b28a13
removed extra auth added for testing
mm-kgi Jul 24, 2026
1d15a08
feat: implement plugin-based data retrieval and submodel repository p…
mm-kgi Jul 31, 2026
77a54fb
Refactor SubmodelRepositoryService and related components for paginat…
mm-hsh Jul 31, 2026
ef0393d
refactor: rewrite submodel pagination concept documentation to detail…
mm-kgi Jul 31, 2026
cd95786
Revert "Refactor SubmodelRepositoryService and related components for…
mm-kgi Jul 31, 2026
196fd34
Refactor SubmodelRepositoryService and related components for paginat…
mm-hsh Jul 31, 2026
dd828fa
chore: remove outdated Submodel Pagination Cursor Concept documentation
mm-kgi Aug 1, 2026
97d2cf7
refactor: enhance pagination logic and update submodel ID handling in…
mm-kgi Aug 3, 2026
f3409a0
Implement two-field cursor-based pagination
mm-kgi Aug 3, 2026
7405a8b
feat: implement SubmodelDescriptorService and SubmodelRepositoryServi…
mm-kgi Aug 4, 2026
d628558
merge from parent branch
mm-kgi Aug 4, 2026
b22b66a
feat: implement plugin data provider interface and handler for managi…
mm-kgi Aug 4, 2026
78bc905
feat: enhance pagination and data retrieval logic across services and…
mm-kgi Aug 16, 2026
ceb2907
parent branch merge
mm-kgi Aug 16, 2026
5383bea
Merge branch 'develop' into pagination-tests
mm-kgi Aug 19, 2026
5f84d22
feat: refactor pagination logic and introduce SubmodelPaginationState…
mm-kgi Aug 19, 2026
d4a5c3a
fix: update null handling in results and improve query parameter usage
mm-kgi Aug 19, 2026
88e6d2e
feat: enhance metadata retrieval by adding pagination parameters to G…
mm-kgi Aug 19, 2026
fd7b0b8
resolve semantic id filter issue
mm-kgi Aug 20, 2026
0e9bbf9
refactor: clean up code formatting and remove unnecessary null-forgiv…
mm-kgi Aug 20, 2026
fb7a142
refactor: update GetAllSubmodelsRequest to use constructor parameters…
mm-kgi Aug 21, 2026
703c919
feat: implement default pagination limit in various services and hand…
mm-kgi Aug 26, 2026
27dc822
fix: update cursor in paging metadata for GetAllShellDescriptors_Expe…
mm-kgi Aug 26, 2026
58e184d
fix: update idShort values from "M&M" to "MM" across various test fil…
mm-kgi Aug 27, 2026
7c262bc
fix: add missing newline at the end of Get All Submodels - by idShort…
mm-kgi Aug 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Net;
using System.Net;
using System.Net.Http.Json;
using System.Text;
using System.Text.Json.Nodes;
Expand Down Expand Up @@ -61,7 +61,7 @@ public async Task GetAllSubmodelDescriptorsAsync_ReturnsOkAsync()
CreateShell("Nameplate")
]
};
_ = _mockAasRepositoryService.GetShellsByFiltersAsync(null, null, null, Arg.Any<CancellationToken>())
_ = _mockAasRepositoryService.GetShellsByFiltersAsync(null, Arg.Any<int?>(), Arg.Any<string?>(), Arg.Any<CancellationToken>())
.Returns(shells);

_ = _mockSubmodelDescriptorProvider.GetDataForSubmodelDescriptorByIdAsync(Arg.Any<string>(), Arg.Any<CancellationToken>())
Expand Down Expand Up @@ -95,7 +95,7 @@ public async Task GetAllSubmodelDescriptorsAsync_WithNotFound_Returns404Async()
CreateShell("MissingSubmodel")
]
};
_ = _mockAasRepositoryService.GetShellsByFiltersAsync(null, null, null, Arg.Any<CancellationToken>())
_ = _mockAasRepositoryService.GetShellsByFiltersAsync(null, Arg.Any<int?>(), Arg.Any<string?>(), Arg.Any<CancellationToken>())
.Returns(shells);
_ = _mockSubmodelDescriptorProvider.GetDataForSubmodelDescriptorByIdAsync(Arg.Any<string>(), Arg.Any<CancellationToken>())
.Throws(new ResourceNotFoundException());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ public async Task HandleSubmodelElement_NullOrEmptyIdShortPath_ThrowsInvalidUser
[Fact]
public async Task GetAllSubmodels_ReturnsSubmodelsDto_WhenServiceSucceeds()
{
var request = new GetAllSubmodelsRequest();
var request = new GetAllSubmodelsRequest(null, null, null, null, null, null);
var submodelList = new SubmodelList { PagingMetaData = new DomainModel.Shared.PagingMetaData(), Result = [] };
_submodelRepository.GetAllSubmodelsAsync(Arg.Any<SubmodelSearchFilter?>(), Arg.Any<SubmodelQueryOptions?>(), null, null, Arg.Any<CancellationToken>())
.Returns(submodelList);
Expand All @@ -323,7 +323,7 @@ public async Task GetAllSubmodels_ReturnsSubmodelsDto_WhenServiceSucceeds()
[Fact]
public async Task GetAllSubmodels_WithInvalidLimit_ThrowsInvalidUserInputException()
{
var request = new GetAllSubmodelsRequest { Limit = 0 };
var request = new GetAllSubmodelsRequest(null, null, 0, null, null, null);

await Assert.ThrowsAsync<InvalidUserInputException>(() => _sut.GetAllSubmodels(request, CancellationToken.None));
}
Expand All @@ -333,7 +333,7 @@ public async Task GetAllSubmodels_BuildsFilterWithSemanticIdAndIdShort()
{
const string SemanticId = "https://example.com/semanticId";
const string IdShort = "Nameplate";
var request = new GetAllSubmodelsRequest { SemanticId = SemanticId, IdShort = IdShort };
var request = new GetAllSubmodelsRequest(SemanticId, IdShort, null, null, null, null);
var submodelList = new SubmodelList { PagingMetaData = new DomainModel.Shared.PagingMetaData(), Result = [] };
_submodelRepository.GetAllSubmodelsAsync(
Arg.Is<SubmodelSearchFilter>(f => f.SemanticId == SemanticId && f.IdShort == IdShort),
Expand All @@ -356,7 +356,7 @@ await _submodelRepository.Received(1).GetAllSubmodelsAsync(
[Fact]
public async Task GetAllSubmodels_WhenLevelAndExtentSet_BuildsQueryOptions()
{
var request = new GetAllSubmodelsRequest { Level = Level.deep, Extent = Extent.withBlobValue };
var request = new GetAllSubmodelsRequest(null, null, null, null, Level.deep, Extent.withBlobValue);
var submodelList = new SubmodelList { PagingMetaData = new DomainModel.Shared.PagingMetaData(), Result = [] };
_submodelRepository.GetAllSubmodelsAsync(
Arg.Any<SubmodelSearchFilter?>(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,7 @@ public async Task GetAllSubmodelsAsync_PassesQueryParamsToHandler()
var expectedDto = new SubmodelsDto { PagingMetaData = new AAS.TwinEngine.DataEngine.Api.Shared.PagingMetaDataDto(), Result = [] };
_handler.GetAllSubmodels(Arg.Any<GetAllSubmodelsRequest>(), Arg.Any<CancellationToken>())
.Returns(expectedDto);
var request = new GetAllSubmodelsRequest
{
SemanticId = SemanticId,
IdShort = IdShort,
Limit = Limit
};
var request = new GetAllSubmodelsRequest(SemanticId, IdShort, Limit, null, null, null);

await _sut.GetAllSubmodelsAsync(SemanticId, IdShort, Limit, null, CancellationToken.None);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using AAS.TwinEngine.DataEngine.ApplicationLogic.Exceptions.Application;
using AAS.TwinEngine.DataEngine.ApplicationLogic.Exceptions.Application;
using AAS.TwinEngine.DataEngine.ApplicationLogic.Exceptions.Infrastructure;
using AAS.TwinEngine.DataEngine.ApplicationLogic.Extensions;
using AAS.TwinEngine.DataEngine.Api.SubmodelRepository.Requests;
Expand Down Expand Up @@ -407,6 +407,8 @@ public async Task GetShellsByFiltersAsync_WithIdShort_QueriesFilteredShellMetada
.GetDataForShellsByAssetIdsAsync(
manifests,
Arg.Is<ShellSearchFilter>(f => f != null && f.IdShort == targetIdShort),
Arg.Any<int?>(),
Arg.Any<string?>(),
cancellationToken)
.Returns(new ShellDescriptorsMetaData
{
Expand All @@ -426,7 +428,7 @@ public async Task GetShellsByFiltersAsync_WithIdShort_QueriesFilteredShellMetada
Assert.Single(result.Result);
Assert.Equal("aas-1", result.Result[0].Id);
await _pluginDataHandler.Received(1)
.GetDataForShellsByAssetIdsAsync(manifests, Arg.Is<ShellSearchFilter>(f => f != null && f.IdShort == targetIdShort), cancellationToken);
.GetDataForShellsByAssetIdsAsync(manifests, Arg.Is<ShellSearchFilter>(f => f != null && f.IdShort == targetIdShort), Arg.Any<int?>(), Arg.Any<string?>(), cancellationToken);
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using AAS.TwinEngine.DataEngine.ApplicationLogic.Exceptions.Application;
using AAS.TwinEngine.DataEngine.ApplicationLogic.Exceptions.Application;
using AAS.TwinEngine.DataEngine.ApplicationLogic.Exceptions.Base;
using AAS.TwinEngine.DataEngine.ApplicationLogic.Exceptions.Infrastructure;
using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.AasRepository;
Expand Down Expand Up @@ -51,6 +51,8 @@ public async Task SearchShellsByAssetLinkAsync_ReturnsAasIds()
_ = _pluginDataHandler.GetDataForShellsByAssetIdsAsync(
Arg.Any<IReadOnlyList<PluginManifest>>(),
Arg.Any<ShellSearchFilter?>(),
Arg.Any<int?>(),
Arg.Any<string?>(),
Arg.Any<CancellationToken>())
.Returns(metadata);

Expand Down Expand Up @@ -83,6 +85,8 @@ public async Task SearchShellsByAssetLinkAsync_WithPagination_ReturnsPagedResult
_ = _pluginDataHandler.GetDataForShellsByAssetIdsAsync(
Arg.Any<IReadOnlyList<PluginManifest>>(),
Arg.Any<ShellSearchFilter?>(),
Arg.Any<int?>(),
Arg.Any<string?>(),
Arg.Any<CancellationToken>())
.Returns(metadata);

Expand All @@ -102,6 +106,8 @@ public async Task SearchShellsByAssetLinkAsync_WhenPluginTimeout_ThrowsPluginNot
_ = _pluginDataHandler.GetDataForShellsByAssetIdsAsync(
Arg.Any<IReadOnlyList<PluginManifest>>(),
Arg.Any<ShellSearchFilter?>(),
Arg.Any<int?>(),
Arg.Any<string?>(),
Arg.Any<CancellationToken>())
.Throws(new RequestTimeoutException());

Expand All @@ -119,6 +125,8 @@ public async Task SearchShellsByAssetLinkAsync_WhenUnauthorized_ThrowsServiceUnA
_ = _pluginDataHandler.GetDataForShellsByAssetIdsAsync(
Arg.Any<IReadOnlyList<PluginManifest>>(),
Arg.Any<ShellSearchFilter?>(),
Arg.Any<int?>(),
Arg.Any<string?>(),
Arg.Any<CancellationToken>())
.Throws(new AAS.TwinEngine.DataEngine.ApplicationLogic.Exceptions.Infrastructure.UnauthorizedAccessException());

Expand All @@ -136,6 +144,8 @@ public async Task SearchShellsByAssetLinkAsync_WhenResponseParsingError_ThrowsIn
_ = _pluginDataHandler.GetDataForShellsByAssetIdsAsync(
Arg.Any<IReadOnlyList<PluginManifest>>(),
Arg.Any<ShellSearchFilter?>(),
Arg.Any<int?>(),
Arg.Any<string?>(),
Arg.Any<CancellationToken>())
.Throws(new ResponseParsingException());

Expand All @@ -154,6 +164,8 @@ public async Task SearchShellsByAssetLinkAsync_WhenMultiPluginConflict_ThrowsInt
_ = _pluginDataHandler.GetDataForShellsByAssetIdsAsync(
Arg.Any<IReadOnlyList<PluginManifest>>(),
Arg.Any<ShellSearchFilter?>(),
Arg.Any<int?>(),
Arg.Any<string?>(),
Arg.Any<CancellationToken>())
.Throws(new MultiPluginConflictException());

Expand All @@ -171,6 +183,8 @@ public async Task SearchShellsByAssetLinkAsync_WhenResourceNotFound_ThrowsIntern
_ = _pluginDataHandler.GetDataForShellsByAssetIdsAsync(
Arg.Any<IReadOnlyList<PluginManifest>>(),
Arg.Any<ShellSearchFilter?>(),
Arg.Any<int?>(),
Arg.Any<string?>(),
Arg.Any<CancellationToken>())
.Throws(new ResourceNotFoundException());

Expand Down Expand Up @@ -200,14 +214,16 @@ public async Task SearchShellsByAssetLinkAsync_FiltersOutEmptyIds()
_ = _pluginDataHandler.GetDataForShellsByAssetIdsAsync(
Arg.Any<IReadOnlyList<PluginManifest>>(),
Arg.Any<ShellSearchFilter?>(),
Arg.Any<int?>(),
Arg.Any<string?>(),
Arg.Any<CancellationToken>())
.Returns(metadata);

var result3 = await _sut.SearchShellsByAssetLinkAsync(assetLinks, null, null, CancellationToken.None);
var result = await _sut.SearchShellsByAssetLinkAsync(assetLinks, null, null, CancellationToken.None);

Assert.Equal(2, result3.Result!.Count);
Assert.Equal("urn:example:aas:001", result3.Result![0]);
Assert.Equal("urn:example:aas:002", result3.Result![1]);
Assert.Equal(2, result.Result!.Count);
Assert.Equal("urn:example:aas:001", result.Result![0]);
Assert.Equal("urn:example:aas:002", result.Result![1]);
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using AAS.TwinEngine.DataEngine.DomainModel.AasRepository;
using AAS.TwinEngine.DataEngine.DomainModel.Shared;
using AAS.TwinEngine.DataEngine.DomainModel.SubmodelRegistry;
using AAS.TwinEngine.DataEngine.DomainModel.SubmodelRepository;
using AAS.TwinEngine.DataEngine.ServiceConfiguration.Config;

using AasCore.Aas3_1;
Expand Down Expand Up @@ -56,7 +57,7 @@ public async Task GetAllSubmodelDescriptorsAsync_ReturnsPagedDescriptorsDerivedF
CreateShell("Nameplate")
]
};
_aasRepositoryService.GetShellsByFiltersAsync(null, null, null, Arg.Any<CancellationToken>())
_aasRepositoryService.GetShellsByFiltersAsync(null, Arg.Any<int?>(), Arg.Any<string?>(), Arg.Any<CancellationToken>())
.Returns(shells);
_submodelTemplateMappingProvider.GetTemplateId("ContactInformation").Returns("ContactInformation");
_submodelTemplateMappingProvider.GetTemplateId("Nameplate").Returns("Nameplate");
Expand Down Expand Up @@ -85,7 +86,7 @@ public async Task GetAllSubmodelDescriptorsAsync_SkipsDescriptorWhenSingleDescri
CreateShell("MissingSubmodel")
]
};
_aasRepositoryService.GetShellsByFiltersAsync(null, null, null, Arg.Any<CancellationToken>())
_aasRepositoryService.GetShellsByFiltersAsync(null, Arg.Any<int?>(), Arg.Any<string?>(), Arg.Any<CancellationToken>())
.Returns(shells);
_submodelTemplateMappingProvider.GetTemplateId("ValidSubmodelId").Returns("ValidSubmodelId");
_submodelTemplateMappingProvider.GetTemplateId("MissingSubmodel").Returns("MissingSubmodel");
Expand All @@ -111,7 +112,7 @@ public async Task GetAllSubmodelDescriptorsAsync_ThrowsSubmodelDescriptorNotFoun
CreateShell("MissingSubmodel2")
]
};
_aasRepositoryService.GetShellsByFiltersAsync(null, null, null, Arg.Any<CancellationToken>())
_aasRepositoryService.GetShellsByFiltersAsync(null, Arg.Any<int?>(), Arg.Any<string?>(), Arg.Any<CancellationToken>())
.Returns(shells);
_submodelTemplateMappingProvider.GetTemplateId("MissingSubmodel1").Returns("MissingSubmodel1");
_submodelTemplateMappingProvider.GetTemplateId("MissingSubmodel2").Returns("MissingSubmodel2");
Expand All @@ -131,7 +132,7 @@ public async Task GetAllSubmodelDescriptorsAsync_ReturnsEmptyResult_WhenShellsHa
{
Result = []
};
_aasRepositoryService.GetShellsByFiltersAsync(null, null, null, Arg.Any<CancellationToken>())
_aasRepositoryService.GetShellsByFiltersAsync(null, Arg.Any<int?>(), Arg.Any<string?>(), Arg.Any<CancellationToken>())
.Returns(shells);

var result = await _sut.GetAllSubmodelDescriptorsAsync(5, null, CancellationToken.None);
Expand Down Expand Up @@ -277,10 +278,123 @@ public async Task GetSubmodelDescriptorByIdAsync_ThrowsRegistryNotAvailable_When
Assert.IsType<RegistryNotAvailableException>(ex);
}

private static AssetAdministrationShell CreateShell(string submodelId)
[Fact]
public async Task GetAllSubmodelDescriptorsAsync_ResumesCorrectly_WithTwoFieldCompositeCursor()
{
var shell1 = CreateShell("ContactInformation", "shell-1");
var shell2 = CreateShell("Nameplate", "shell-2");

var shellsPage1 = new Shells { Result = [shell1, shell2] };
var shellsPage2 = new Shells { Result = [shell2] };

_aasRepositoryService.GetShellsByFiltersAsync(null, 1, Arg.Is<string?>(s => s == null), Arg.Any<CancellationToken>())
.Returns(shellsPage1);
_aasRepositoryService.GetShellsByFiltersAsync(null, 1, Arg.Is<string?>(s => s != null), Arg.Any<CancellationToken>())
.Returns(shellsPage2);

_submodelTemplateMappingProvider.GetTemplateId("ContactInformation").Returns("ContactInformation");
_submodelTemplateMappingProvider.GetTemplateId("Nameplate").Returns("Nameplate");
_provider.GetDataForSubmodelDescriptorByIdAsync("ContactInformation", Arg.Any<CancellationToken>())
.Returns(new SubmodelDescriptor { Id = "ContactInformation" });
_provider.GetDataForSubmodelDescriptorByIdAsync("Nameplate", Arg.Any<CancellationToken>())
.Returns(new SubmodelDescriptor { Id = "Nameplate" });

var page1 = await _sut.GetAllSubmodelDescriptorsAsync(1, null, CancellationToken.None);

Assert.NotNull(page1);
Assert.Single(page1.Result!);
Assert.Equal("ContactInformation", page1.Result![0].Id);
Assert.NotNull(page1.PagingMetaData?.Cursor);

var decodedCursor = SubmodelPaginationCursor.Decode(page1.PagingMetaData.Cursor);
Assert.NotNull(decodedCursor);
Assert.Equal("ContactInformation", decodedCursor.SubmodelId);
Assert.Equal("shell-1", decodedCursor.AasId);

var page2 = await _sut.GetAllSubmodelDescriptorsAsync(1, page1.PagingMetaData.Cursor, CancellationToken.None);

Assert.NotNull(page2);
Assert.Single(page2.Result!);
Assert.Equal("Nameplate", page2.Result![0].Id);
}

[Fact]
public async Task GetAllSubmodelDescriptorsAsync_SkipsShellsWithNullOrEmptyId()
{
var validShell = CreateShell("Nameplate", "valid-shell");
var nullIdShell = new AssetAdministrationShell(
id: null!,
assetInformation: new AssetInformation(assetKind: AssetKind.Instance, globalAssetId: null),
submodels: [new Reference(ReferenceTypes.ModelReference, [new Key(KeyTypes.Submodel, "ShouldBeSkipped")])]);

var shells = new Shells { Result = [nullIdShell, validShell] };
_aasRepositoryService.GetShellsByFiltersAsync(null, Arg.Any<int?>(), Arg.Any<string?>(), Arg.Any<CancellationToken>())
.Returns(shells);
_submodelTemplateMappingProvider.GetTemplateId("Nameplate").Returns("Nameplate");
_provider.GetDataForSubmodelDescriptorByIdAsync("Nameplate", Arg.Any<CancellationToken>())
.Returns(new SubmodelDescriptor { Id = "Nameplate" });

var result = await _sut.GetAllSubmodelDescriptorsAsync(5, null, CancellationToken.None);

Assert.Single(result.Result!);
Assert.Equal("Nameplate", result.Result![0].Id);
}

[Fact]
public async Task GetAllSubmodelDescriptorsAsync_WhenLimitReachedAtAasBoundary_CursorAasIdIsConsumedAas()
{
var shell1 = CreateShellWithMultipleSubmodels("shell-1", "sm-1", "sm-2", "sm-3");
var shell2 = CreateShellWithMultipleSubmodels("shell-2", "sm-4", "sm-5");

var shells = new Shells { Result = [shell1, shell2] };
_aasRepositoryService.GetShellsByFiltersAsync(null, Arg.Any<int?>(), Arg.Any<string?>(), Arg.Any<CancellationToken>())
.Returns(shells);

_submodelTemplateMappingProvider.GetTemplateId(Arg.Any<string>()).Returns(x => (string)x[0]);
_provider.GetDataForSubmodelDescriptorByIdAsync(Arg.Any<string>(), Arg.Any<CancellationToken>())
.Returns(x => new SubmodelDescriptor { Id = (string)x[0] });

var result = await _sut.GetAllSubmodelDescriptorsAsync(5, null, CancellationToken.None);

Assert.Equal(5, result.Result!.Count);
Assert.NotNull(result.PagingMetaData?.Cursor);

var decoded = SubmodelPaginationCursor.Decode(result.PagingMetaData!.Cursor!);
Assert.Equal("sm-5", decoded!.SubmodelId);
Assert.Equal("shell-2", decoded.AasId);
}

[Fact]
public async Task GetAllSubmodelDescriptorsAsync_WhenLimitReachedAtFirstAasBoundary_CursorAasIdIsConsumedAas()
{
var shell1 = CreateShellWithMultipleSubmodels("shell-1", "sm-1", "sm-2");

var shells = new Shells
{
Result = [shell1],
PagingMetaData = new PagingMetaData { Cursor = "next-page-token" }
};
_aasRepositoryService.GetShellsByFiltersAsync(null, Arg.Any<int?>(), Arg.Any<string?>(), Arg.Any<CancellationToken>())
.Returns(shells);

_submodelTemplateMappingProvider.GetTemplateId(Arg.Any<string>()).Returns(x => (string)x[0]);
_provider.GetDataForSubmodelDescriptorByIdAsync(Arg.Any<string>(), Arg.Any<CancellationToken>())
.Returns(x => new SubmodelDescriptor { Id = (string)x[0] });

var result = await _sut.GetAllSubmodelDescriptorsAsync(2, null, CancellationToken.None);

Assert.Equal(2, result.Result!.Count);
Assert.NotNull(result.PagingMetaData?.Cursor);

var decoded = SubmodelPaginationCursor.Decode(result.PagingMetaData!.Cursor!);
Assert.Equal("sm-2", decoded!.SubmodelId);
Assert.Equal("shell-1", decoded.AasId);
}

private static AssetAdministrationShell CreateShell(string submodelId, string shellId = "shell-id")
{
return new AssetAdministrationShell(
id: "shell-id",
id: shellId,
assetInformation: new AssetInformation(assetKind: AssetKind.Instance, globalAssetId: null),
submodels:
[
Expand All @@ -290,4 +404,17 @@ private static AssetAdministrationShell CreateShell(string submodelId)
referredSemanticId: null)
]);
}

private static AssetAdministrationShell CreateShellWithMultipleSubmodels(string shellId, params string[] submodelIds)
{
var refs = submodelIds.Select(id => (IReference)new Reference(
type: ReferenceTypes.ModelReference,
keys: [new Key(KeyTypes.Submodel, id)],
referredSemanticId: null)).ToList();

return new AssetAdministrationShell(
id: shellId,
assetInformation: new AssetInformation(assetKind: AssetKind.Instance, globalAssetId: null),
submodels: refs);
}
}
Loading
Loading