diff --git a/FluentAAS/FluentAAS.Builder.Tests/AasBuilderTests.cs b/FluentAAS/FluentAAS.Builder.Tests/AasBuilderTests.cs index c8c2bc6..1a22009 100644 --- a/FluentAAS/FluentAAS.Builder.Tests/AasBuilderTests.cs +++ b/FluentAAS/FluentAAS.Builder.Tests/AasBuilderTests.cs @@ -266,6 +266,93 @@ public void AddSubmodelFragment_WithoutBaseSubmodel_ShouldThrowInvalidOperationE ex.Message.ShouldContain("No base submodel"); } + [Fact] + public void Build_WithShellReferencingKnownSubmodelId_ShouldSucceed() + { + // Arrange + var builder = CreateSut(); + var submodelId = _fixture.Create()!; + builder.AddSubmodel(new Submodel(id: submodelId, idShort: "known-submodel")); + + var shell = new AssetAdministrationShell( + id: _fixture.Create()!, + assetInformation: new AssetInformation(AssetKind.Instance)) + { + IdShort = "shell-with-known-ref", + Submodels = + [ + new Reference( + ReferenceTypes.ModelReference, + [new Key(KeyTypes.Submodel, submodelId)]) + ] + }; + + builder.AddShellInternal(shell); + + // Act + var environment = builder.Build(); + + // Assert + var builtShell = environment.AssetAdministrationShells!.OfType().Single(); + builtShell.Submodels.ShouldNotBeNull(); + builtShell.Submodels.Count.ShouldBe(1); + builtShell.Submodels.Single().Keys.Single().Value.ShouldBe(submodelId); + } + + [Fact] + public void Build_WithShellReferencingUnknownSubmodelId_ShouldThrowInvalidOperationException() + { + // Arrange + var builder = CreateSut(); + var unknownSubmodelId = _fixture.Create()!; + + var shell = new AssetAdministrationShell( + id: _fixture.Create()!, + assetInformation: new AssetInformation(AssetKind.Instance)) + { + IdShort = "shell-with-unknown-ref", + Submodels = + [ + new Reference( + ReferenceTypes.ModelReference, + [new Key(KeyTypes.Submodel, unknownSubmodelId)]) + ] + }; + + builder.AddShellInternal(shell); + + // Act + var act = () => builder.Build(); + + // Assert + var ex = Should.Throw(act); + ex.Message.ShouldContain($"references unknown submodel id '{unknownSubmodelId}'"); + } + + [Fact] + public void Build_WhenFragmentBatchFails_ShouldRollbackAppliedFragmentsBeforeRetry() + { + // Arrange + var builder = CreateSut(); + var existingSubmodelId = _fixture.Create(); + var missingSubmodelId = _fixture.Create(); + + builder.AddSubmodel(new Submodel(id: existingSubmodelId, idShort: "existing")); + builder.AddSubmodelFragment(existingSubmodelId, fragment => fragment.AddProperty("temperature", "21.5")); + builder.AddSubmodelFragment(missingSubmodelId, fragment => fragment.AddProperty("pressure", "1.0")); + + // Act + Should.Throw(() => builder.Build()); + + builder.AddSubmodel(new Submodel(id: missingSubmodelId, idShort: "missing-now-added")); + var env = builder.Build(); + + // Assert + var existingSubmodel = env.Submodels!.OfType().Single(s => s.Id == existingSubmodelId); + existingSubmodel.SubmodelElements.ShouldNotBeNull(); + existingSubmodel.SubmodelElements.Count(e => e.IdShort == "temperature").ShouldBe(1); + } + [Fact] public void Build_WhenCalledMultipleTimes_ShouldReturnNewEnvironmentInstancesWithSameContent() { diff --git a/README.md b/README.md index af2c86c..b6c2ec5 100644 --- a/README.md +++ b/README.md @@ -208,7 +208,7 @@ var environment = builder.Build(); - Fragment targets must exist. - Shell submodel references must resolve to known submodels. -> Backward compatibility note: `AddExistingSubmodel(...)` still works and delegates to `AddSubmodel(...)`. +> Backward compatibility note: `AddExistingSubmodel(...)` still works, but it delegates to `AddSubmodelInternal(...)` (not `AddSubmodel(...)`) and therefore skips the additional public `IdShort` validation performed by `AddSubmodel(...)`. ---