Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
eb6ba2e
refactor: Change public methods to private in SemanticIdHandler for e…
mm-kgi Mar 5, 2026
21cd268
added implementation for refactoring semanticidhandler
mm-kgi Mar 6, 2026
332a6dd
Added Refactored SemanticIdHandler
mm-kgi Mar 12, 2026
23f6f39
git merge with parent branch
mm-kgi Mar 18, 2026
f92778a
git merge from parent barnch
mm-kgi Mar 18, 2026
4ad1241
Refactor the code to remove warnings
mm-kgi Mar 18, 2026
040b9ca
refactor to remove warnings
mm-kgi Mar 18, 2026
c75f9d6
remove internal methods test cases
mm-kgi Mar 18, 2026
91a1d0e
change dataengine image
mm-kgi Mar 18, 2026
1501c54
remove cognitive complexity inside submodel filler
mm-kgi Mar 18, 2026
30fc78e
remove unused using
mm-kgi Mar 18, 2026
b7ee7c5
Added internal semantic id removal logic from submodel qualifiers
mm-kgi Mar 20, 2026
c8df6fe
remove unused var
mm-kgi Mar 20, 2026
9c3cff8
Merge branch 'develop' into feature/128-refactor-semantic-id-handler-…
mm-kgi Mar 20, 2026
dd01f18
remove internal semantic from test data
mm-kgi Mar 20, 2026
f2b9362
Revert "remove internal semantic from test data"
mm-kgi Mar 20, 2026
60432c8
remove internal semantic id from qualifires in testdata
mm-kgi Mar 20, 2026
54fe06d
remove internal semantic ids from testdata of playwrite tests
mm-kgi Mar 20, 2026
d414972
remove extra property added in testdata json
mm-kgi Mar 20, 2026
7d24721
Merge branch 'develop' into feature/128-refactor-semantic-id-handler-…
mm-kgi Mar 20, 2026
a57bd66
Merge branch 'develop' into feature/128-refactor-semantic-id-handler-…
mm-kgi Mar 24, 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
Expand Up @@ -73,7 +73,7 @@ public async Task GetSubmodelAsync_WithValidIdentifier_ReturnsOkAsync()
_ = _httpClientFactory.CreateClient(HttpClientNamePlugin1).Returns(httpClientPlugin1);

const string HttpClientNamePlugin2 = $"{PluginConfig.HttpClientNamePrefix}TestPlugin2";
_httpClientFactory.CreateClient(HttpClientNamePlugin2).Returns(httpClientPlugin2);
_ = _httpClientFactory.CreateClient(HttpClientNamePlugin2).Returns(httpClientPlugin2);

const string SubmodelId = "Q29udGFjdEluZm9ybWF0aW9u";
var mockSubmodel = TestData.CreateSubmodel();
Expand Down Expand Up @@ -133,7 +133,7 @@ public async Task GetSubmodelElementAsync_ReturnsOkAsync()
const string SubmodelId = "Q29udGFjdEluZm9ybWF0aW9u";
const string IdShortPath = "ContactName";
var mockSubmodel = TestData.CreateSubmodel();
TestData.CreatePluginResponseForSubmodelElement();
_ = TestData.CreatePluginResponseForSubmodelElement();

using var messageHandler = new FakeHttpMessageHandler((_, _) => Task.FromResult(new HttpResponseMessage
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.SubmodelRepository.SemanticId.ElementHandlers;
using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.SubmodelRepository.SemanticId.Helpers.Interfaces;
using AAS.TwinEngine.DataEngine.DomainModel.SubmodelRepository;

using AasCore.Aas3_0;

using NSubstitute;

using static Xunit.Assert;

namespace AAS.TwinEngine.DataEngine.UnitTests.ApplicationLogic.Services.SubmodelRepository.SemanticId.ElementHandlers;

public class BlobHandlerTests
{
private readonly BlobHandler _sut;
private readonly ISemanticIdResolver _resolver;

public BlobHandlerTests()
{
_resolver = Substitute.For<ISemanticIdResolver>();
_sut = new BlobHandler(_resolver);
}

[Fact]
public void CanHandle_Blob_ReturnsTrue()
{
var blob = new Blob(contentType: "application/octet-stream", idShort: "Test");

True(_sut.CanHandle(blob));
}

[Fact]
public void CanHandle_NonBlob_ReturnsFalse()
{
var property = new Property(idShort: "Test", valueType: DataTypeDefXsd.String);

False(_sut.CanHandle(property));
}

[Fact]
public void Extract_ReturnsLeafNode()
{
var blob = new Blob(contentType: "image/png", idShort: "MyBlob");
_resolver.ResolveElementSemanticId(blob, "MyBlob").Returns("http://test/blob");
_resolver.GetValueType(blob).Returns(DataType.String);
_resolver.GetCardinality(blob).Returns(Cardinality.One);

var result = _sut.Extract(blob, _ => null);

var leaf = IsType<SemanticLeafNode>(result);
Equal("http://test/blob", leaf.SemanticId);
Equal(DataType.String, leaf.DataType);
}

[Fact]
public void FillOut_WithLeafNode_SetsBase64Value()
{
var blob = new Blob(contentType: "image/png", idShort: "MyBlob");
var base64 = Convert.ToBase64String(new byte[] { 1, 2, 3 });
var values = new SemanticLeafNode("http://test/blob", base64, DataType.String, Cardinality.One);

_sut.FillOut(blob, values, (_, _, _) => { });

NotNull(blob.Value);
Equal([1, 2, 3], blob.Value);
}

[Fact]
public void FillOut_WithBranchNode_DoesNotModifyValue()
{
var originalBytes = new byte[] { 10, 20, 30 };
var blob = new Blob(contentType: "image/png", idShort: "MyBlob", value: originalBytes);
var values = new SemanticBranchNode("http://test/blob", Cardinality.One);

_sut.FillOut(blob, values, (_, _, _) => { });

Equal(originalBytes, blob.Value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.SubmodelRepository.SemanticId.ElementHandlers;
using AAS.TwinEngine.DataEngine.ApplicationLogic.Services.SubmodelRepository.SemanticId.Helpers.Interfaces;
using AAS.TwinEngine.DataEngine.DomainModel.SubmodelRepository;

using AasCore.Aas3_0;

using Microsoft.Extensions.Logging;

using NSubstitute;

using static Xunit.Assert;

namespace AAS.TwinEngine.DataEngine.UnitTests.ApplicationLogic.Services.SubmodelRepository.SemanticId.ElementHandlers;

public class CollectionHandlerTests
{
private readonly CollectionHandler _sut;
private readonly ISemanticIdResolver _resolver;
private readonly ILogger<CollectionHandler> _logger;

public CollectionHandlerTests()
{
_resolver = Substitute.For<ISemanticIdResolver>();
_logger = Substitute.For<ILogger<CollectionHandler>>();
_sut = new CollectionHandler(_resolver, _logger);
}

[Fact]
public void CanHandle_Collection_ReturnsTrue()
{
var collection = new SubmodelElementCollection(idShort: "Test");

True(_sut.CanHandle(collection));
}

[Fact]
public void CanHandle_NonCollection_ReturnsFalse()
{
var property = new Property(idShort: "Test", valueType: DataTypeDefXsd.String);

False(_sut.CanHandle(property));
}

[Fact]
public void Extract_WithChildren_ReturnsBranchNodeWithChildren()
{
var child = new Property(idShort: "Child", valueType: DataTypeDefXsd.String);
var collection = new SubmodelElementCollection(idShort: "MyCollection", value: [child]);
_resolver.ResolveElementSemanticId(collection, "MyCollection").Returns("http://test/collection");
_resolver.GetCardinality(collection).Returns(Cardinality.ZeroToMany);

var childNode = new SemanticLeafNode("http://test/child", "", DataType.String, Cardinality.One);
SemanticTreeNode? extractChild(ISubmodelElement _) => childNode;

var result = _sut.Extract(collection, extractChild);

var branch = IsType<SemanticBranchNode>(result);
Equal("http://test/collection", branch.SemanticId);
Equal(Cardinality.ZeroToMany, branch.Cardinality);
Single(branch.Children);
}

[Fact]
public void Extract_WithNullValue_ReturnsBranchNodeAndLogsWarning()
{
var collection = new SubmodelElementCollection(idShort: "EmptyCollection", value: null);
_resolver.ResolveElementSemanticId(collection, "EmptyCollection").Returns("http://test/empty");
_resolver.GetCardinality(collection).Returns(Cardinality.Unknown);

var result = _sut.Extract(collection, _ => null);

var branch = IsType<SemanticBranchNode>(result);
Empty(branch.Children);
_logger.Received(1).Log(
LogLevel.Warning,
Arg.Any<EventId>(),
Arg.Is<object>(state => state.ToString()!.Contains("No elements defined in SubmodelElementCollection EmptyCollection")),
null,
Arg.Any<Func<object, Exception?, string>>()!
);
}

[Fact]
public void Extract_WithEmptyValue_ReturnsBranchNodeAndLogsWarning()
{
var collection = new SubmodelElementCollection(idShort: "EmptyCollection", value: []);
_resolver.ResolveElementSemanticId(collection, "EmptyCollection").Returns("http://test/empty");
_resolver.GetCardinality(collection).Returns(Cardinality.Unknown);

var result = _sut.Extract(collection, _ => null);

var branch = IsType<SemanticBranchNode>(result);
Empty(branch.Children);
_logger.Received(1).Log(
LogLevel.Warning,
Arg.Any<EventId>(),
Arg.Is<object>(state => state.ToString()!.Contains("No elements defined in SubmodelElementCollection EmptyCollection")),
null,
Arg.Any<Func<object, Exception?, string>>()!
);
}

[Fact]
public void FillOut_WithChildren_DelegatesToFillOutChildren()
{
var child = new Property(idShort: "Child", valueType: DataTypeDefXsd.String);
var collection = new SubmodelElementCollection(idShort: "Col", value: [child]);
var values = new SemanticBranchNode("http://test/col", Cardinality.One);
var fillOutCalled = false;

_sut.FillOut(collection, values, (elements, node, updateIdShort) =>
{
fillOutCalled = true;
True(updateIdShort);
Same(collection.Value, elements);
});

True(fillOutCalled);
}

[Fact]
public void FillOut_WithNullValue_DoesNotCallFillOutChildren()
{
var collection = new SubmodelElementCollection(idShort: "Col", value: null);
var values = new SemanticBranchNode("http://test/col", Cardinality.One);

_sut.FillOut(collection, values, (_, _, _) => Fail("Should not be called"));
}

[Fact]
public void FillOut_WithEmptyValue_DoesNotCallFillOutChildren()
{
var collection = new SubmodelElementCollection(idShort: "Col", value: []);
var values = new SemanticBranchNode("http://test/col", Cardinality.One);

_sut.FillOut(collection, values, (_, _, _) => Fail("Should not be called"));
}
}
Loading
Loading