Skip to content

Commit c90821c

Browse files
authored
Merge branch 'develop' into PlaywrightTest-FileAttachment-DataEngine
2 parents 35fb52b + ec94166 commit c90821c

30 files changed

Lines changed: 857 additions & 145 deletions

File tree

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
meta {
2+
name: Get Thumbnail By Id
3+
type: http
4+
seq: 2
5+
}
6+
7+
get {
8+
url: {{DataEngineBaseUrl}}/shells/:aasIdentifier/asset-information/thumbnail
9+
body: none
10+
auth: inherit
11+
}
12+
13+
params:path {
14+
aasIdentifier: {{aasIdentifier-1}}
15+
}
16+
17+
settings {
18+
encodeUrl: true
19+
timeout: 0
20+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
meta {
2+
name: Get Thumbnail By Id
3+
type: http
4+
seq: 2
5+
}
6+
7+
get {
8+
url: {{DataEngineBaseUrl}}/shells/:aasIdentifier/asset-information/thumbnail
9+
body: none
10+
auth: inherit
11+
}
12+
13+
params:path {
14+
aasIdentifier: {{aasIdentifier-2}}
15+
}
16+
17+
settings {
18+
encodeUrl: true
19+
timeout: 0
20+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
meta {
2+
name: Get Thumbnail By Id
3+
type: http
4+
seq: 2
5+
}
6+
7+
get {
8+
url: {{DataEngineBaseUrl}}/shells/:aasIdentifier/asset-information/thumbnail
9+
body: none
10+
auth: inherit
11+
}
12+
13+
params:path {
14+
aasIdentifier: {{aasIdentifier-3}}
15+
}
16+
17+
settings {
18+
encodeUrl: true
19+
timeout: 0
20+
}

source/AAS.TwinEngine.DataEngine.ModuleTests/Api/Services/AasRepository/AasRepositoryControllerTests.cs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.AasEnvironment.Providers;
99
using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.Plugin;
1010
using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.Plugin.Providers;
11+
using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.Shared.Providers;
12+
using AAS.TwinEngine.DataEngine.DomainModel.Shared;
1113
using AAS.TwinEngine.DataEngine.Infrastructure.Http.Clients;
1214
using AAS.TwinEngine.DataEngine.ModuleTests.Common;
1315
using AAS.TwinEngine.DataEngine.ServiceConfiguration.Config;
@@ -26,12 +28,14 @@ public abstract class AasRepositoryControllerTests : IDisposable
2628
{
2729
private readonly ConfigTestFactory _factory;
2830
private readonly ITemplateProvider _mockTemplateProvider;
31+
private readonly IFileContentProvider _fileContentProvider;
2932
private readonly HttpClient _client;
3033
private readonly ICreateClient _httpClientFactory;
3134

3235
protected AasRepositoryControllerTests(string configDir)
3336
{
3437
_mockTemplateProvider = Substitute.For<ITemplateProvider>();
38+
_fileContentProvider = Substitute.For<IFileContentProvider>();
3539
var mockPluginManifestProvider = Substitute.For<IPluginManifestProvider>();
3640
var mockPluginManifestConflictHandler = Substitute.For<IPluginManifestConflictHandler>();
3741
_httpClientFactory = Substitute.For<ICreateClient>();
@@ -42,6 +46,7 @@ protected AasRepositoryControllerTests(string configDir)
4246
_ = services.AddSingleton(mockPluginManifestConflictHandler);
4347
_ = services.AddSingleton(_httpClientFactory);
4448
_ = services.AddSingleton(_mockTemplateProvider);
49+
_ = services.AddSingleton(_fileContentProvider);
4550
});
4651

4752
_client = _factory.CreateClient();
@@ -549,6 +554,83 @@ private void SetupTemplateProvider()
549554
});
550555
}
551556

557+
[Fact]
558+
public async Task GetThumbnailAsync_ShouldReturn200OKWithStream_WhenThumbnailExists()
559+
{
560+
const string AasIdentifier = "aHR0cHM6Ly9leGFtcGxlLmNvbS9pZHMvYWFzLzExNzBfMTE2MF8zMDUyXzY1Njg=";
561+
562+
var messageHandler = new FakeHttpMessageHandler((request, token) =>
563+
{
564+
var httpResponse = new HttpResponseMessage(HttpStatusCode.OK)
565+
{
566+
Content = new StringContent(TestData.CreatePluginResponseForAssetinformation())
567+
};
568+
return Task.FromResult(httpResponse);
569+
});
570+
571+
using var httpClient = new HttpClient(messageHandler);
572+
httpClient.BaseAddress = new Uri("https://testendpoint.com");
573+
574+
const string HttpClientName = $"{HttpClientNames.PluginDataProviderPrefix}TestPlugin1";
575+
_ = _httpClientFactory.CreateClient(HttpClientName).Returns(httpClient);
576+
577+
var expectedAssetInformation = TestData.CreateAssetInformationTemplate();
578+
_ = _mockTemplateProvider.GetAssetInformationTemplateAsync(Arg.Any<string>(), Arg.Any<CancellationToken>()).Returns(expectedAssetInformation);
579+
580+
var stream = new MemoryStream("test-bytes"u8.ToArray());
581+
var fileContentResponse = new FileContentResponse(stream);
582+
_ = _fileContentProvider.GetFileContentAsync(Arg.Any<string>(), Arg.Any<CancellationToken>())
583+
.Returns(fileContentResponse);
584+
585+
var response = await _client.GetAsync($"/shells/{AasIdentifier}/asset-information/thumbnail");
586+
587+
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
588+
Assert.Equal(expectedAssetInformation.DefaultThumbnail.ContentType, response.Content.Headers.ContentType?.MediaType);
589+
var bytes = await response.Content.ReadAsByteArrayAsync();
590+
Assert.Equal("test-bytes"u8.ToArray(), bytes);
591+
}
592+
593+
[Fact]
594+
public async Task GetThumbnailAsync_ShouldReturn404_WhenThumbnailIsMissing()
595+
{
596+
const string AasIdentifier = "aHR0cHM6Ly9leGFtcGxlLmNvbS9pZHMvYWFzLzExNzBfMTE2MF8zMDUyXzY1Njg=";
597+
598+
var mockAssetInformationTemplate = new AssetInformation(
599+
AssetKind.Instance,
600+
"https://example.com/ids/asset/123",
601+
[],
602+
defaultThumbnail: null
603+
);
604+
605+
var messageHandler = new FakeHttpMessageHandler((request, token) =>
606+
{
607+
var httpResponse = new HttpResponseMessage(HttpStatusCode.OK)
608+
{
609+
Content = new StringContent("""
610+
{
611+
"assetKind": "Type",
612+
"globalAssetId": "https://example.com/ids/asset/123",
613+
"specificAssetIds": [],
614+
"defaultThumbnail": null
615+
}
616+
""")
617+
};
618+
return Task.FromResult(httpResponse);
619+
});
620+
621+
using var httpClient = new HttpClient(messageHandler);
622+
httpClient.BaseAddress = new Uri("https://testendpoint.com");
623+
624+
const string HttpClientName = $"{HttpClientNames.PluginDataProviderPrefix}TestPlugin1";
625+
_ = _httpClientFactory.CreateClient(HttpClientName).Returns(httpClient);
626+
627+
_ = _mockTemplateProvider.GetAssetInformationTemplateAsync(Arg.Any<string>(), Arg.Any<CancellationToken>()).Returns(mockAssetInformationTemplate);
628+
629+
var response = await _client.GetAsync($"/shells/{AasIdentifier}/asset-information/thumbnail");
630+
631+
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
632+
}
633+
552634
private static string EncodeBase64Url(string plainText)
553635
{
554636
if (string.IsNullOrWhiteSpace(plainText))

source/AAS.TwinEngine.DataEngine.ModuleTests/Api/Services/SubmodelRepository/SubmodelRepositoryControllerTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.AasEnvironment.Providers;
99
using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.Plugin;
1010
using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.Plugin.Providers;
11-
using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.SubmodelRepository.Providers;
11+
using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.Shared.Providers;
1212
using AAS.TwinEngine.DataEngine.Infrastructure.Http.Clients;
1313
using AAS.TwinEngine.DataEngine.ModuleTests.Common;
1414

source/AAS.TwinEngine.DataEngine.UnitTests/Api/AasRepository/AasRepositoryControllerTests.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
using AAS.TwinEngine.DataEngine.Api.AasRepository.Requests;
88
using AAS.TwinEngine.DataEngine.Api.AasRepository.Responses;
99
using AAS.TwinEngine.DataEngine.Api.Shared;
10+
using AAS.TwinEngine.DataEngine.Api.Shared.Results;
1011
using AAS.TwinEngine.DataEngine.ApplicationLogic.Extensions;
12+
using AAS.TwinEngine.DataEngine.DomainModel.Shared;
1113

1214
using AasCore.Aas3_1;
1315

@@ -274,5 +276,32 @@ private static SubmodelRefDto CreateSubmodelRefDto()
274276
};
275277
}
276278

279+
[Fact]
280+
public async Task GetThumbnailAsync_ShouldReturnFileContentStreamResult_WhenHandlerCompletes()
281+
{
282+
var expectedStream = new MemoryStream("test-image"u8.ToArray());
283+
var attachmentResult = new FileAttachmentResult(expectedStream, "image/png", "thumbnail.png", 100 * 1024 * 1024);
284+
_handler.GetThumbnailAsync(Arg.Any<GetThumbnailRequest>(), Arg.Any<CancellationToken>())
285+
.Returns(attachmentResult);
286+
287+
var response = await _sut.GetThumbnailAsync(AasIdentifier, CancellationToken.None);
288+
289+
Assert.IsType<FileContentStreamResult>(response);
290+
}
291+
292+
[Fact]
293+
public async Task GetThumbnailAsync_PassesRouteValuesToHandler()
294+
{
295+
_handler.GetThumbnailAsync(Arg.Any<GetThumbnailRequest>(), Arg.Any<CancellationToken>())
296+
.Returns(new FileAttachmentResult(Stream.Null, "image/png", "thumbnail.png", 100 * 1024 * 1024));
297+
298+
await _sut.GetThumbnailAsync(AasIdentifier, CancellationToken.None);
299+
300+
await _handler.Received(1)
301+
.GetThumbnailAsync(
302+
Arg.Is<GetThumbnailRequest>(r => r.AasIdentifier == AasIdentifier),
303+
Arg.Any<CancellationToken>());
304+
}
277305
}
278306

307+

source/AAS.TwinEngine.DataEngine.UnitTests/Api/AasRepository/Handler/AasRepositoryHandlerTests.cs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
using AasCore.Aas3_1;
1212

13+
using Microsoft.AspNetCore.Http;
1314
using Microsoft.Extensions.Logging;
1415

1516
using NSubstitute;
@@ -249,6 +250,72 @@ public async Task GetSubmodelRefByIdAsync_InvalidBase64_ThrowsInvalidUserInputEx
249250
await Assert.ThrowsAsync<InvalidUserInputException>(() => _sut.GetSubmodelRefByIdAsync(request, CancellationToken.None));
250251
}
251252

253+
[Fact]
254+
public async Task GetThumbnailAsync_ShouldReturnFileAttachmentResult()
255+
{
256+
var expectedResult = new FileAttachmentResult(new MemoryStream(), "image/png", "test.png", 100 * 1024 * 1024);
257+
_aasRepositoryService.GetThumbnailAsync("https://example.com/aas", Arg.Any<CancellationToken>())
258+
.Returns(expectedResult);
259+
260+
var request = new GetThumbnailRequest("aHR0cHM6Ly9leGFtcGxlLmNvbS9hYXM");
261+
var result = await _sut.GetThumbnailAsync(request, CancellationToken.None);
262+
263+
Assert.Equal(expectedResult, result);
264+
}
265+
266+
[Fact]
267+
public async Task GetThumbnailAsync_CallsServiceWithDecodedAasIdentifier_WhenInputIsValid()
268+
{
269+
const string aasIdentifier = "https://example.com/aas";
270+
var encodedId = aasIdentifier.EncodeBase64Url();
271+
var request = new GetThumbnailRequest(encodedId);
272+
var expectedResult = new FileAttachmentResult(Stream.Null, "image/png", "thumbnail.png", 100 * 1024 * 1024);
273+
274+
_aasRepositoryService
275+
.GetThumbnailAsync(aasIdentifier, Arg.Any<CancellationToken>())
276+
.Returns(expectedResult);
277+
278+
await _sut.GetThumbnailAsync(request, CancellationToken.None);
279+
280+
await _aasRepositoryService.Received(1)
281+
.GetThumbnailAsync(aasIdentifier, Arg.Any<CancellationToken>());
282+
}
283+
284+
[Fact]
285+
public async Task GetThumbnailAsync_InvalidBase64AasIdentifier_ThrowsInvalidUserInputException()
286+
{
287+
const string invalidEncodedId = "!!invalid_base64@@";
288+
var request = new GetThumbnailRequest(invalidEncodedId);
289+
290+
await Assert.ThrowsAsync<InvalidUserInputException>(() =>
291+
_sut.GetThumbnailAsync(request, CancellationToken.None));
292+
}
293+
294+
[Theory]
295+
[InlineData("../../../etc/passwd")]
296+
[InlineData("..\\..\\..\\windows\\system32")]
297+
public async Task GetThumbnailAsync_MaliciousDecodedIdentifier_ThrowsInvalidUserInputException(string maliciousIdentifier)
298+
{
299+
var request = new GetThumbnailRequest(maliciousIdentifier.EncodeBase64Url());
300+
301+
await Assert.ThrowsAsync<InvalidUserInputException>(() =>
302+
_sut.GetThumbnailAsync(request, CancellationToken.None));
303+
}
304+
305+
[Fact]
306+
public async Task GetThumbnailAsync_ServiceReturnsNull_ThrowsTemplateNotFoundException()
307+
{
308+
const string aasIdentifier = "https://example.com/aas";
309+
var request = new GetThumbnailRequest(aasIdentifier.EncodeBase64Url());
310+
311+
_aasRepositoryService
312+
.GetThumbnailAsync(aasIdentifier, Arg.Any<CancellationToken>())!
313+
.Returns((FileAttachmentResult)null!);
314+
315+
await Assert.ThrowsAsync<TemplateNotFoundException>(() =>
316+
_sut.GetThumbnailAsync(request, CancellationToken.None));
317+
}
318+
252319
private static AssetInformation CreateAssetInformation()
253320
{
254321
var thumbnail = Substitute.For<IResource>();

0 commit comments

Comments
 (0)