diff --git a/SysML2.NET.Tests/Extend/EndFeatureMembershipExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/EndFeatureMembershipExtensionsTestFixture.cs
index 8a121303..90be40c4 100644
--- a/SysML2.NET.Tests/Extend/EndFeatureMembershipExtensionsTestFixture.cs
+++ b/SysML2.NET.Tests/Extend/EndFeatureMembershipExtensionsTestFixture.cs
@@ -1,38 +1,88 @@
-// -------------------------------------------------------------------------------------------------
+// -------------------------------------------------------------------------------------------------
//
-//
+//
// Copyright 2022-2026 Starion Group S.A.
-//
+//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
-//
+//
// http://www.apache.org/licenses/LICENSE-2.0
-//
+//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
-//
+//
//
// ------------------------------------------------------------------------------------------------
namespace SysML2.NET.Tests.Extend
{
using System;
-
+
using NUnit.Framework;
-
+
using SysML2.NET.Core.POCO.Core.Features;
+ using SysML2.NET.Core.POCO.Root.Elements;
+ using SysML2.NET.Core.POCO.Root.Namespaces;
+ using SysML2.NET.Exceptions;
+ using SysML2.NET.Extensions;
+
+ using Type = SysML2.NET.Core.POCO.Core.Types.Type;
[TestFixture]
public class EndFeatureMembershipExtensionsTestFixture
{
[Test]
- public void ComputeOwnedMemberFeature_ThrowsNotSupportedException()
+ public void VerifyComputeOwnedMemberFeature()
{
- Assert.That(() => ((IEndFeatureMembership)null).ComputeOwnedMemberFeature(), Throws.TypeOf());
+ Assert.That(() => ((IEndFeatureMembership)null).ComputeOwnedMemberFeature(), Throws.TypeOf());
+
+ // Empty OwnedRelatedElement → [1..1] violation: throws IncompleteModelException.
+ var endFeatureMembership = new EndFeatureMembership();
+
+ Assert.That(() => endFeatureMembership.ComputeOwnedMemberFeature(), Throws.TypeOf());
+
+ // Single IFeature wired via the public API → returned.
+ var owningType = new Type();
+ var feature = new Feature();
+
+ owningType.AssignOwnership(endFeatureMembership, feature);
+
+ Assert.That(endFeatureMembership.ComputeOwnedMemberFeature(), Is.SameAs(feature));
+
+ // Two IFeatures in OwnedRelatedElement → [1..1] violation: throws IncompleteModelException.
+ var twoFeatureMembership = new EndFeatureMembership();
+ var firstFeature = new Feature();
+ var secondFeature = new Feature();
+
+ ((IContainedRelationship)twoFeatureMembership).OwnedRelatedElement.Add(firstFeature);
+ ((IContainedRelationship)twoFeatureMembership).OwnedRelatedElement.Add(secondFeature);
+
+ Assert.That(() => twoFeatureMembership.ComputeOwnedMemberFeature(), Throws.TypeOf());
+
+ // Mixed-type owned related elements: exactly one IFeature alongside a non-IFeature (Namespace).
+ // The RequireSingleOfType projection MUST pick out the IFeature regardless of its position
+ // (this is the core robustness guarantee — never positionally index the unfiltered collection).
+ var mixedMembership = new EndFeatureMembership();
+ var siblingNonFeature = new Namespace();
+ var mixedFeature = new Feature();
+
+ ((IContainedRelationship)mixedMembership).OwnedRelatedElement.Add(siblingNonFeature);
+ ((IContainedRelationship)mixedMembership).OwnedRelatedElement.Add(mixedFeature);
+
+ Assert.That(mixedMembership.ComputeOwnedMemberFeature(), Is.SameAs(mixedFeature));
+
+ // OwnedRelatedElement populated with non-IFeature element(s) only → no IFeature match:
+ // [1..1] violation, throws IncompleteModelException.
+ var nonFeatureMembership = new EndFeatureMembership();
+ var nonFeatureElement = new Namespace();
+
+ ((IContainedRelationship)nonFeatureMembership).OwnedRelatedElement.Add(nonFeatureElement);
+
+ Assert.That(() => nonFeatureMembership.ComputeOwnedMemberFeature(), Throws.TypeOf());
}
}
}
diff --git a/SysML2.NET/Extend/EndFeatureMembershipExtensions.cs b/SysML2.NET/Extend/EndFeatureMembershipExtensions.cs
index 538a5844..1e5ef201 100644
--- a/SysML2.NET/Extend/EndFeatureMembershipExtensions.cs
+++ b/SysML2.NET/Extend/EndFeatureMembershipExtensions.cs
@@ -21,13 +21,8 @@
namespace SysML2.NET.Core.POCO.Core.Features
{
using System;
- using System.Collections.Generic;
- using SysML2.NET.Core.Root.Namespaces;
- using SysML2.NET.Core.POCO.Core.Types;
- using SysML2.NET.Core.POCO.Root.Annotations;
- using SysML2.NET.Core.POCO.Root.Elements;
- using SysML2.NET.Core.POCO.Root.Namespaces;
+ using SysML2.NET.Extensions;
///
/// The class provides extensions methods for
@@ -44,11 +39,14 @@ internal static class EndFeatureMembershipExtensions
///
/// the computed result
///
- [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
internal static IFeature ComputeOwnedMemberFeature(this IEndFeatureMembership endFeatureMembershipSubject)
{
- throw new NotSupportedException("Create a GitHub issue when this method is required");
- }
+ if (endFeatureMembershipSubject == null)
+ {
+ throw new ArgumentNullException(nameof(endFeatureMembershipSubject));
+ }
+ return endFeatureMembershipSubject.OwnedRelatedElement.RequireSingleOfType(nameof(endFeatureMembershipSubject));
+ }
}
}