From ab338d6e1aee927484ffbde74237b83a5cdca097 Mon Sep 17 00:00:00 2001 From: Bernhard Schmitt Date: Sun, 15 Dec 2024 22:37:46 +0100 Subject: [PATCH 1/9] 5054 - WIP: Implement constraint checks for node variation with custom destination --- ...CreateNodeVariant_ConstraintChecks.feature | 104 +++++++++++++++++- .../Command/CreateNodeVariant.php | 18 ++- .../Feature/NodeVariation/NodeVariation.php | 44 +++++++- 3 files changed, 156 insertions(+), 10 deletions(-) diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/01-CreateNodeVariant_ConstraintChecks.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/01-CreateNodeVariant_ConstraintChecks.feature index 5df29d7fe45..dc98a1ccc1a 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/01-CreateNodeVariant_ConstraintChecks.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/01-CreateNodeVariant_ConstraintChecks.feature @@ -15,14 +15,24 @@ Feature: Create node variant tethered: type: 'Neos.ContentRepository.Testing:Tethered' 'Neos.ContentRepository.Testing:Tethered': [] + 'Neos.ContentRepository.Testing:RestrictiveDocument': + constraints: + nodeTypes: + 'Neos.ContentRepository.Testing:Document': false + childNodes: + tethered: + type: 'Neos.ContentRepository.Testing:Tethered' + constraints: + nodeTypes: + 'Neos.ContentRepository.Testing:Document': false """ And using identifier "default", I define a content repository And I am in content repository "default" And I am user identified by "initiating-user-identifier" And the command CreateRootWorkspace is executed with payload: - | Key | Value | - | workspaceName | "live" | - | newContentStreamId | "cs-identifier" | + | Key | Value | + | workspaceName | "live" | + | newContentStreamId | "cs-identifier" | And I am in workspace "live" and dimension space point {"market":"DE", "language":"gsw"} And the command CreateRootNodeAggregateWithNode is executed with payload: | Key | Value | @@ -36,6 +46,15 @@ Feature: Create node variant # We have to add yet another node since we need test cases with a partially covering parent node # Node /document/child | nody-mc-nodeface | child | sir-david-nodenborough | Neos.ContentRepository.Testing:Document | {} | + And I am in workspace "live" and dimension space point {"market":"DE", "language":"de"} + # We have to add yet another node that could be varied but not to a different parent + And the following CreateNodeAggregateWithNode commands are executed: + | nodeAggregateId | nodeName | parentNodeAggregateId | nodeTypeName | + | polyglot-mc-nodeface | polyglot-child | lady-eleonode-rootford | Neos.ContentRepository.Testing:Document | + # ...and we have to add yet another node for node type constraint checks + And the following CreateNodeAggregateWithNode commands are executed: + | nodeAggregateId | nodeName | parentNodeAggregateId | nodeTypeName | tetheredDescendantNodeAggregateIds | + | the-governode | governode | lady-eleonode-rootford | Neos.ContentRepository.Testing:RestrictiveDocument | {"tethered": "nodimer-tetherton"} | Scenario: Try to create a variant in a workspace that does not exist When the command CreateNodeVariant is executed with payload and exceptions are caught: @@ -120,3 +139,82 @@ Feature: Create node variant | sourceOrigin | {"market":"DE", "language":"gsw"} | | targetOrigin | {"market":"DE", "language":"de"} | Then the last command should have thrown an exception of type "NodeAggregateDoesCurrentlyNotCoverDimensionSpacePoint" + + Scenario: Try to create a variant as a child of a different parent aggregate that does not exist + When the command CreateNodeVariant is executed with payload and exceptions are caught: + | Key | Value | + | nodeAggregateId | "polyglot-mc-nodeface" | + | sourceOrigin | {"market":"DE", "language":"de"} | + | targetOrigin | {"market":"DE", "language":"gsw"} | + | parentNodeAggregateId | "i-do-not-exist" | + Then the last command should have thrown an exception of type "NodeAggregateCurrentlyDoesNotExist" + + Scenario: Try to create a variant as a sibling of a non-existing succeeding sibling + When the command CreateNodeVariant is executed with payload and exceptions are caught: + | Key | Value | + | nodeAggregateId | "polyglot-mc-nodeface" | + | sourceOrigin | {"market":"DE", "language":"de"} | + | targetOrigin | {"market":"DE", "language":"gsw"} | + | parentNodeAggregateId | "nody-mc-nodeface" | + | succeedingSiblingNodeAggregateId | "i-do-not-exist" | + Then the last command should have thrown an exception of type "NodeAggregateCurrentlyDoesNotExist" + + Scenario: Try to create a variant before a sibling which is not a child of the new parent + When the command CreateNodeVariant is executed with payload and exceptions are caught: + | Key | Value | + | nodeAggregateId | "polyglot-mc-nodeface" | + | sourceOrigin | {"market":"DE", "language":"de"} | + | targetOrigin | {"market":"DE", "language":"gsw"} | + | parentNodeAggregateId | "nody-mc-nodeface" | + | succeedingSiblingNodeAggregateId | "sir-david-nodenborough" | + Then the last command should have thrown an exception of type "NodeAggregateIsNoChild" + + Scenario: Try to create a variant before a sibling which is none (no new parent case) + When the command CreateNodeVariant is executed with payload and exceptions are caught: + | Key | Value | + | nodeAggregateId | "polyglot-mc-nodeface" | + | sourceOrigin | {"market":"DE", "language":"de"} | + | targetOrigin | {"market":"DE", "language":"gsw"} | + | succeedingSiblingNodeAggregateId | "nody-mc-nodeface" | + Then the last command should have thrown an exception of type "NodeAggregateIsNoSibling" + + Scenario: Try to create a variant as a child of a different parent aggregate that does not cover the requested DSP + When the command CreateNodeVariant is executed with payload and exceptions are caught: + | Key | Value | + | nodeAggregateId | "polyglot-mc-nodeface" | + | sourceOrigin | {"market":"DE", "language":"de"} | + | targetOrigin | {"market":"CH", "language":"de"} | + | parentNodeAggregateId | "sir-david-nodenborough" | + Then the last command should have thrown an exception of type "NodeAggregateDoesCurrentlyNotCoverDimensionSpacePoint" + + Scenario: Try to create a variant of a node having a name that is already taken by one of the variant's siblings + Given I am in workspace "live" and dimension space point {"market":"DE", "language":"gsw"} + And the following CreateNodeAggregateWithNode commands are executed: + | nodeAggregateId | nodeName | parentNodeAggregateId | nodeTypeName | + | evil-occupant | polyglot-child | nody-mc-nodeface | Neos.ContentRepository.Testing:Document | + + When the command CreateNodeVariant is executed with payload and exceptions are caught: + | Key | Value | + | nodeAggregateId | "polyglot-mc-nodeface" | + | sourceOrigin | {"market":"DE", "language":"de"} | + | targetOrigin | {"market":"DE", "language":"gsw"} | + | parentNodeAggregateId | "nody-mc-nodeface" | + Then the last command should have thrown an exception of type "NodeNameIsAlreadyCovered" + + Scenario: Try to vary a node as a child of another parent whose node type does not allow child nodes of the variant's type + When the command CreateNodeVariant is executed with payload and exceptions are caught: + | Key | Value | + | nodeAggregateId | "polyglot-mc-nodeface" | + | sourceOrigin | {"market":"DE", "language":"de"} | + | targetOrigin | {"market":"DE", "language":"gsw"} | + | parentNodeAggregateId | "the-governode" | + Then the last command should have thrown an exception of type "NodeConstraintException" + + Scenario: Try to vary a node as a child of another parent whose parent's node type does not allow grand child nodes of the variant's type + When the command CreateNodeVariant is executed with payload and exceptions are caught: + | Key | Value | + | nodeAggregateId | "polyglot-mc-nodeface" | + | sourceOrigin | {"market":"DE", "language":"de"} | + | targetOrigin | {"market":"DE", "language":"gsw"} | + | parentNodeAggregateId | "nodimer-tetherton" | + Then the last command should have thrown an exception of type "NodeConstraintException" diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Command/CreateNodeVariant.php b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Command/CreateNodeVariant.php index 581bfd8b6d8..5eb6c9f1b2d 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Command/CreateNodeVariant.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Command/CreateNodeVariant.php @@ -37,12 +37,16 @@ * @param NodeAggregateId $nodeAggregateId The identifier of the affected node aggregate * @param OriginDimensionSpacePoint $sourceOrigin Dimension Space Point from which the node is to be copied from * @param OriginDimensionSpacePoint $targetOrigin Dimension Space Point to which the node is to be copied to + * @param ?NodeAggregateId $parentNodeAggregateId The optional id of the node aggregate to be used as the variant's parent + * @param ?NodeAggregateId $succeedingSiblingNodeAggregateId The optional id of the node aggregate to be used as the variant's succeeding sibling */ private function __construct( public WorkspaceName $workspaceName, public NodeAggregateId $nodeAggregateId, public OriginDimensionSpacePoint $sourceOrigin, public OriginDimensionSpacePoint $targetOrigin, + public ?NodeAggregateId $parentNodeAggregateId, + public ?NodeAggregateId $succeedingSiblingNodeAggregateId, ) { } @@ -51,10 +55,12 @@ private function __construct( * @param NodeAggregateId $nodeAggregateId The identifier of the affected node aggregate * @param OriginDimensionSpacePoint $sourceOrigin Dimension Space Point from which the node is to be copied from * @param OriginDimensionSpacePoint $targetOrigin Dimension Space Point to which the node is to be copied to + * @param ?NodeAggregateId $parentNodeAggregateId The id of the node aggregate to be used as the variant's parent + * @param ?NodeAggregateId $succeedingSiblingNodeAggregateId The optional id of the node aggregate to be used as the variant's succeeding sibling */ - public static function create(WorkspaceName $workspaceName, NodeAggregateId $nodeAggregateId, OriginDimensionSpacePoint $sourceOrigin, OriginDimensionSpacePoint $targetOrigin): self + public static function create(WorkspaceName $workspaceName, NodeAggregateId $nodeAggregateId, OriginDimensionSpacePoint $sourceOrigin, OriginDimensionSpacePoint $targetOrigin, ?NodeAggregateId $parentNodeAggregateId = null, ?NodeAggregateId $succeedingSiblingNodeAggregateId = null): self { - return new self($workspaceName, $nodeAggregateId, $sourceOrigin, $targetOrigin); + return new self($workspaceName, $nodeAggregateId, $sourceOrigin, $targetOrigin, $parentNodeAggregateId, $succeedingSiblingNodeAggregateId); } public static function fromArray(array $array): self @@ -64,6 +70,12 @@ public static function fromArray(array $array): self NodeAggregateId::fromString($array['nodeAggregateId']), OriginDimensionSpacePoint::fromArray($array['sourceOrigin']), OriginDimensionSpacePoint::fromArray($array['targetOrigin']), + array_key_exists('parentNodeAggregateId', $array) + ? NodeAggregateId::fromString($array['parentNodeAggregateId']) + : null, + array_key_exists('succeedingSiblingNodeAggregateId', $array) + ? NodeAggregateId::fromString($array['succeedingSiblingNodeAggregateId']) + : null, ); } @@ -83,6 +95,8 @@ public function createCopyForWorkspace( $this->nodeAggregateId, $this->sourceOrigin, $this->targetOrigin, + $this->parentNodeAggregateId, + $this->succeedingSiblingNodeAggregateId, ); } } diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/NodeVariation.php b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/NodeVariation.php index 1e01e281265..e6a0b4ee46d 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/NodeVariation.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/NodeVariation.php @@ -65,11 +65,45 @@ private function handleCreateNodeVariant( $this->requireNodeAggregateToBeUntethered($nodeAggregate); $this->requireNodeAggregateToOccupyDimensionSpacePoint($nodeAggregate, $command->sourceOrigin); $this->requireNodeAggregateToNotOccupyDimensionSpacePoint($nodeAggregate, $command->targetOrigin); - $parentNodeAggregate = $this->requireProjectedParentNodeAggregate( - $contentGraph, - $command->nodeAggregateId, - $command->sourceOrigin - ); + + if ($command->succeedingSiblingNodeAggregateId) { + $this->requireProjectedNodeAggregate($contentGraph, $command->succeedingSiblingNodeAggregateId); + } + if ($command->parentNodeAggregateId) { + $parentNodeAggregate = $this->requireProjectedNodeAggregate($contentGraph, $command->parentNodeAggregateId); + if ($command->succeedingSiblingNodeAggregateId) { + $this->requireNodeAggregateToBeChild( + $contentGraph, + $command->succeedingSiblingNodeAggregateId, + $command->parentNodeAggregateId, + $command->targetOrigin->toDimensionSpacePoint(), + ); + } + if ($nodeAggregate->nodeName) { + $this->requireNodeNameToBeUncovered($contentGraph, $nodeAggregate->nodeName, $command->parentNodeAggregateId); + $this->requireNodeTypeNotToDeclareTetheredChildNodeName($parentNodeAggregate->nodeTypeName, $nodeAggregate->nodeName); + } + $this->requireConstraintsImposedByAncestorsAreMet( + $contentGraph, + $this->requireNodeType($nodeAggregate->nodeTypeName), + [$command->parentNodeAggregateId], + ); + } else { + $parentNodeAggregate = $this->requireProjectedParentNodeAggregate( + $contentGraph, + $command->nodeAggregateId, + $command->sourceOrigin + ); + if ($command->succeedingSiblingNodeAggregateId) { + $this->requireNodeAggregateToBeSibling( + $contentGraph, + $command->nodeAggregateId, + $command->succeedingSiblingNodeAggregateId, + $command->targetOrigin->toDimensionSpacePoint(), + ); + } + } + $this->requireNodeAggregateToCoverDimensionSpacePoint( $parentNodeAggregate, $command->targetOrigin->toDimensionSpacePoint() From adbecefea4ad890b297cff57d2a9a7e9a81ca57a Mon Sep 17 00:00:00 2001 From: Bernhard Schmitt Date: Fri, 24 Oct 2025 15:35:50 +0200 Subject: [PATCH 2/9] 5054 - streamline dimension value names --- ...02-CreateNodeSpecializationVariant.feature | 456 +++++++++--------- 1 file changed, 228 insertions(+), 228 deletions(-) diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/02-CreateNodeSpecializationVariant.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/02-CreateNodeSpecializationVariant.feature index 16ce30b634d..a9949a92c06 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/02-CreateNodeSpecializationVariant.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/02-CreateNodeSpecializationVariant.feature @@ -6,7 +6,7 @@ Feature: Create node specialization Background: Given using the following content dimensions: | Identifier | Values | Generalizations | - | example | source, spec, leafSpec | leafSpec->spec->source | + | example | general, source, spec | spec->source->general | And using the following node types: """yaml 'Neos.ContentRepository.Testing:Document': @@ -27,7 +27,7 @@ Feature: Create node specialization | Key | Value | | workspaceName | "live" | | newContentStreamId | "cs-identifier" | - And I am in workspace "live" and dimension space point {"example":"source"} + And I am in workspace "live" and dimension space point {"example":"general"} And the command CreateRootNodeAggregateWithNode is executed with payload: | Key | Value | | nodeAggregateId | "lady-eleonode-rootford" | @@ -45,7 +45,7 @@ Feature: Create node specialization | invariable-mc-nodeface | invariable-document | nody-mc-nodeface | | Neos.ContentRepository.Testing:LeafDocument | {} | Scenario: check the tree state before the specialization - When I am in workspace "live" and dimension space point {"example":"source"} + When I am in workspace "live" and dimension space point {"example":"general"} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 3 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -62,83 +62,83 @@ Feature: Create node specialization When the command CreateNodeVariant is executed with payload: | Key | Value | | nodeAggregateId | "nody-mc-nodeface" | - | sourceOrigin | {"example":"source"} | - | targetOrigin | {"example":"spec"} | + | sourceOrigin | {"example":"general"} | + | targetOrigin | {"example":"source"} | Then I expect exactly 13 events to be published on stream "ContentStream:cs-identifier" And event at index 10 is of type "NodeSpecializationVariantWasCreated" with payload: | Key | Expected | | contentStreamId | "cs-identifier" | | nodeAggregateId | "nody-mc-nodeface" | - | sourceOrigin | {"example":"source"} | - | specializationOrigin | {"example":"spec"} | - | specializationSiblings | [{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"younger-mc-nodeface"},{"dimensionSpacePoint":{"example":"leafSpec"},"nodeAggregateId":"younger-mc-nodeface"}] | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"younger-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"younger-mc-nodeface"}] | And event at index 11 is of type "NodeSpecializationVariantWasCreated" with payload: | Key | Expected | | contentStreamId | "cs-identifier" | | nodeAggregateId | "nodewyn-tetherton" | - | sourceOrigin | {"example":"source"} | - | specializationOrigin | {"example":"spec"} | - | specializationSiblings | [{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"invariable-mc-nodeface"},{"dimensionSpacePoint":{"example":"leafSpec"},"nodeAggregateId":"invariable-mc-nodeface"}] | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"invariable-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"invariable-mc-nodeface"}] | And event at index 12 is of type "NodeSpecializationVariantWasCreated" with payload: | Key | Expected | | contentStreamId | "cs-identifier" | | nodeAggregateId | "nodimer-tetherton" | - | sourceOrigin | {"example":"source"} | - | specializationOrigin | {"example":"spec"} | - | specializationSiblings | [{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"leafSpec"},"nodeAggregateId":null}] | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | Then I expect the graph projection to consist of exactly 12 nodes And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"general"} to exist in the content graph And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"source"} to exist in the content graph - And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"spec"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"general"} to exist in the content graph And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"source"} to exist in the content graph - And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"spec"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"general"} to exist in the content graph And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"source"} to exist in the content graph - And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"spec"} to exist in the content graph - And I expect a node identified by cs-identifier;eldest-mc-nodeface;{"example":"source"} to exist in the content graph - And I expect a node identified by cs-identifier;elder-mc-nodeface;{"example":"source"} to exist in the content graph - And I expect a node identified by cs-identifier;younger-mc-nodeface;{"example":"source"} to exist in the content graph - And I expect a node identified by cs-identifier;youngest-mc-nodeface;{"example":"source"} to exist in the content graph - And I expect a node identified by cs-identifier;invariable-mc-nodeface;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;eldest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;elder-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;younger-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;youngest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;invariable-mc-nodeface;{"example":"general"} to exist in the content graph When I am in workspace "live" Then I expect the node aggregate "lady-eleonode-rootford" to exist And I expect this node aggregate to occupy dimension space points [{}] - And I expect this node aggregate to cover dimension space points [{"example":"source"},{"example":"spec"},{"example":"leafSpec"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] And I expect the node aggregate "eldest-mc-nodeface" to exist - And I expect this node aggregate to occupy dimension space points [{"example":"source"}] - And I expect this node aggregate to cover dimension space points [{"example":"source"},{"example":"spec"},{"example":"leafSpec"}] + And I expect this node aggregate to occupy dimension space points [{"example":"general"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] And I expect the node aggregate "elder-mc-nodeface" to exist - And I expect this node aggregate to occupy dimension space points [{"example":"source"}] - And I expect this node aggregate to cover dimension space points [{"example":"source"},{"example":"spec"},{"example":"leafSpec"}] + And I expect this node aggregate to occupy dimension space points [{"example":"general"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] And I expect the node aggregate "nody-mc-nodeface" to exist - And I expect this node aggregate to occupy dimension space points [{"example":"source"},{"example":"spec"}] - And I expect this node aggregate to cover dimension space points [{"example":"source"},{"example":"spec"},{"example":"leafSpec"}] + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] And I expect the node aggregate "nodewyn-tetherton" to exist - And I expect this node aggregate to occupy dimension space points [{"example":"source"},{"example":"spec"}] - And I expect this node aggregate to cover dimension space points [{"example":"source"},{"example":"spec"},{"example":"leafSpec"}] + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] And I expect the node aggregate "nodimer-tetherton" to exist - And I expect this node aggregate to occupy dimension space points [{"example":"source"},{"example":"spec"}] - And I expect this node aggregate to cover dimension space points [{"example":"source"},{"example":"spec"},{"example":"leafSpec"}] + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] And I expect the node aggregate "invariable-mc-nodeface" to exist - And I expect this node aggregate to occupy dimension space points [{"example":"source"}] - And I expect this node aggregate to cover dimension space points [{"example":"source"},{"example":"spec"},{"example":"leafSpec"}] + And I expect this node aggregate to occupy dimension space points [{"example":"general"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] And I expect the node aggregate "younger-mc-nodeface" to exist - And I expect this node aggregate to occupy dimension space points [{"example":"source"}] - And I expect this node aggregate to cover dimension space points [{"example":"source"},{"example":"spec"},{"example":"leafSpec"}] + And I expect this node aggregate to occupy dimension space points [{"example":"general"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] And I expect the node aggregate "youngest-mc-nodeface" to exist - And I expect this node aggregate to occupy dimension space points [{"example":"source"}] - And I expect this node aggregate to cover dimension space points [{"example":"source"},{"example":"spec"},{"example":"leafSpec"}] + And I expect this node aggregate to occupy dimension space points [{"example":"general"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] - When I am in workspace "live" and dimension space point {"example":"spec"} + When I am in workspace "live" and dimension space point {"example":"source"} Then I expect the subgraph projection to consist of exactly 9 nodes Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 3 levels deep should be: @@ -154,343 +154,343 @@ Feature: Create node specialization | 1 | youngest-mc-nodeface | And I expect this node to have the following child nodes: | Name | NodeDiscriminator | - | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"source"} | - | elder-document | cs-identifier;elder-mc-nodeface;{"example":"source"} | - | document | cs-identifier;nody-mc-nodeface;{"example":"spec"} | - | younger-document | cs-identifier;younger-mc-nodeface;{"example":"source"} | - | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"source"} | - And I expect node aggregate identifier "eldest-mc-nodeface" and node path "eldest-document" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"source"} + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" and node path "eldest-document" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: | NodeDiscriminator | - | cs-identifier;elder-mc-nodeface;{"example":"source"} | - | cs-identifier;nody-mc-nodeface;{"example":"spec"} | - | cs-identifier;younger-mc-nodeface;{"example":"source"} | - | cs-identifier;youngest-mc-nodeface;{"example":"source"} | - And I expect node aggregate identifier "elder-mc-nodeface" and node path "elder-document" to lead to node cs-identifier;elder-mc-nodeface;{"example":"source"} + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" and node path "elder-document" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | - | cs-identifier;eldest-mc-nodeface;{"example":"source"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have the following succeeding siblings: | NodeDiscriminator | - | cs-identifier;nody-mc-nodeface;{"example":"spec"} | - | cs-identifier;younger-mc-nodeface;{"example":"source"} | - | cs-identifier;youngest-mc-nodeface;{"example":"source"} | - And I expect node aggregate identifier "nody-mc-nodeface" and node path "document" to lead to node cs-identifier;nody-mc-nodeface;{"example":"spec"} + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" and node path "document" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | - | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"spec"} | - | invariable-document | cs-identifier;invariable-mc-nodeface;{"example":"source"} | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable-document | cs-identifier;invariable-mc-nodeface;{"example":"general"} | And I expect this node to have the following preceding siblings: | NodeDiscriminator | - | cs-identifier;elder-mc-nodeface;{"example":"source"} | - | cs-identifier;eldest-mc-nodeface;{"example":"source"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have the following succeeding siblings: | NodeDiscriminator | - | cs-identifier;younger-mc-nodeface;{"example":"source"} | - | cs-identifier;youngest-mc-nodeface;{"example":"source"} | - And I expect node aggregate identifier "nodewyn-tetherton" and node path "document/tethered-node" to lead to node cs-identifier;nodewyn-tetherton;{"example":"spec"} + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nodewyn-tetherton" and node path "document/tethered-node" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | - | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"spec"} | - And I expect node aggregate identifier "nodimer-tetherton" and node path "document/tethered-node/tethered-leaf" to lead to node cs-identifier;nodimer-tetherton;{"example":"spec"} - And I expect node aggregate identifier "invariable-mc-nodeface" and node path "document/invariable-document" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"source"} - And I expect node aggregate identifier "younger-mc-nodeface" and node path "younger-document" to lead to node cs-identifier;younger-mc-nodeface;{"example":"source"} + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" and node path "document/tethered-node/tethered-leaf" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" and node path "document/invariable-document" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" and node path "younger-document" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | - | cs-identifier;nody-mc-nodeface;{"example":"spec"} | - | cs-identifier;elder-mc-nodeface;{"example":"source"} | - | cs-identifier;eldest-mc-nodeface;{"example":"source"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have the following succeeding siblings: | NodeDiscriminator | - | cs-identifier;youngest-mc-nodeface;{"example":"source"} | - And I expect node aggregate identifier "youngest-mc-nodeface" and node path "youngest-document" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"source"} + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" and node path "youngest-document" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | - | cs-identifier;younger-mc-nodeface;{"example":"source"} | - | cs-identifier;nody-mc-nodeface;{"example":"spec"} | - | cs-identifier;elder-mc-nodeface;{"example":"source"} | - | cs-identifier;eldest-mc-nodeface;{"example":"source"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have no succeeding siblings - When I am in workspace "live" and dimension space point {"example":"leafSpec"} + When I am in workspace "live" and dimension space point {"example":"spec"} Then I expect the subgraph projection to consist of exactly 9 nodes Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | - | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"source"} | - | elder-document | cs-identifier;elder-mc-nodeface;{"example":"source"} | - | document | cs-identifier;nody-mc-nodeface;{"example":"spec"} | - | younger-document | cs-identifier;younger-mc-nodeface;{"example":"source"} | - | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"source"} | - And I expect node aggregate identifier "eldest-mc-nodeface" and node path "eldest-document" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"source"} + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" and node path "eldest-document" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: | NodeDiscriminator | - | cs-identifier;elder-mc-nodeface;{"example":"source"} | - | cs-identifier;nody-mc-nodeface;{"example":"spec"} | - | cs-identifier;younger-mc-nodeface;{"example":"source"} | - | cs-identifier;youngest-mc-nodeface;{"example":"source"} | - And I expect node aggregate identifier "elder-mc-nodeface" and node path "elder-document" to lead to node cs-identifier;elder-mc-nodeface;{"example":"source"} + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" and node path "elder-document" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | - | cs-identifier;eldest-mc-nodeface;{"example":"source"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have the following succeeding siblings: | NodeDiscriminator | - | cs-identifier;nody-mc-nodeface;{"example":"spec"} | - | cs-identifier;younger-mc-nodeface;{"example":"source"} | - | cs-identifier;youngest-mc-nodeface;{"example":"source"} | - And I expect node aggregate identifier "nody-mc-nodeface" and node path "document" to lead to node cs-identifier;nody-mc-nodeface;{"example":"spec"} + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" and node path "document" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | - | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"spec"} | - | invariable-document | cs-identifier;invariable-mc-nodeface;{"example":"source"} | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable-document | cs-identifier;invariable-mc-nodeface;{"example":"general"} | And I expect this node to have the following preceding siblings: | NodeDiscriminator | - | cs-identifier;elder-mc-nodeface;{"example":"source"} | - | cs-identifier;eldest-mc-nodeface;{"example":"source"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have the following succeeding siblings: | NodeDiscriminator | - | cs-identifier;younger-mc-nodeface;{"example":"source"} | - | cs-identifier;youngest-mc-nodeface;{"example":"source"} | - And I expect node aggregate identifier "nodewyn-tetherton" and node path "document/tethered-node" to lead to node cs-identifier;nodewyn-tetherton;{"example":"spec"} + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nodewyn-tetherton" and node path "document/tethered-node" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | - | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"spec"} | - And I expect node aggregate identifier "nodimer-tetherton" and node path "document/tethered-node/tethered-leaf" to lead to node cs-identifier;nodimer-tetherton;{"example":"spec"} - And I expect node aggregate identifier "invariable-mc-nodeface" and node path "document/invariable-document" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"source"} - And I expect node aggregate identifier "younger-mc-nodeface" and node path "younger-document" to lead to node cs-identifier;younger-mc-nodeface;{"example":"source"} + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" and node path "document/tethered-node/tethered-leaf" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" and node path "document/invariable-document" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" and node path "younger-document" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | - | cs-identifier;nody-mc-nodeface;{"example":"spec"} | - | cs-identifier;elder-mc-nodeface;{"example":"source"} | - | cs-identifier;eldest-mc-nodeface;{"example":"source"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have the following succeeding siblings: | NodeDiscriminator | - | cs-identifier;youngest-mc-nodeface;{"example":"source"} | - And I expect node aggregate identifier "youngest-mc-nodeface" and node path "youngest-document" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"source"} + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" and node path "youngest-document" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | - | cs-identifier;younger-mc-nodeface;{"example":"source"} | - | cs-identifier;nody-mc-nodeface;{"example":"spec"} | - | cs-identifier;elder-mc-nodeface;{"example":"source"} | - | cs-identifier;eldest-mc-nodeface;{"example":"source"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have no succeeding siblings Scenario: Create specialization of node to dimension space point with specializations that are partially occupied Given the command CreateNodeVariant is executed with payload: | Key | Value | | nodeAggregateId | "nody-mc-nodeface" | - | sourceOrigin | {"example":"source"} | - | targetOrigin | {"example":"leafSpec"} | + | sourceOrigin | {"example":"general"} | + | targetOrigin | {"example":"spec"} | When the command CreateNodeVariant is executed with payload: | Key | Value | | nodeAggregateId | "nody-mc-nodeface" | - | sourceOrigin | {"example":"source"} | - | targetOrigin | {"example":"spec"} | + | sourceOrigin | {"example":"general"} | + | targetOrigin | {"example":"source"} | Then I expect exactly 16 events to be published on stream "ContentStream:cs-identifier" And event at index 13 is of type "NodeSpecializationVariantWasCreated" with payload: | Key | Expected | | contentStreamId | "cs-identifier" | | nodeAggregateId | "nody-mc-nodeface" | - | sourceOrigin | {"example":"source"} | - | specializationOrigin | {"example":"spec"} | - | specializationSiblings | [{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"younger-mc-nodeface"}] | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"younger-mc-nodeface"}] | And event at index 14 is of type "NodeSpecializationVariantWasCreated" with payload: | Key | Expected | | contentStreamId | "cs-identifier" | | nodeAggregateId | "nodewyn-tetherton" | - | sourceOrigin | {"example":"source"} | - | specializationOrigin | {"example":"spec"} | - | specializationSiblings | [{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"invariable-mc-nodeface"}] | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"invariable-mc-nodeface"}] | And event at index 15 is of type "NodeSpecializationVariantWasCreated" with payload: | Key | Expected | | contentStreamId | "cs-identifier" | | nodeAggregateId | "nodimer-tetherton" | - | sourceOrigin | {"example":"source"} | - | specializationOrigin | {"example":"spec"} | - | specializationSiblings | [{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":null}] | Then I expect the graph projection to consist of exactly 15 nodes And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph - And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"source"} to exist in the content graph - And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"leafSpec"} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"general"} to exist in the content graph And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"spec"} to exist in the content graph - And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"source"} to exist in the content graph - And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"leafSpec"} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"general"} to exist in the content graph And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"spec"} to exist in the content graph - And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"source"} to exist in the content graph - And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"leafSpec"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"general"} to exist in the content graph And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"spec"} to exist in the content graph - And I expect a node identified by cs-identifier;eldest-mc-nodeface;{"example":"source"} to exist in the content graph - And I expect a node identified by cs-identifier;elder-mc-nodeface;{"example":"source"} to exist in the content graph - And I expect a node identified by cs-identifier;younger-mc-nodeface;{"example":"source"} to exist in the content graph - And I expect a node identified by cs-identifier;youngest-mc-nodeface;{"example":"source"} to exist in the content graph - And I expect a node identified by cs-identifier;invariable-mc-nodeface;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;eldest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;elder-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;younger-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;youngest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;invariable-mc-nodeface;{"example":"general"} to exist in the content graph When I am in workspace "live" Then I expect the node aggregate "lady-eleonode-rootford" to exist And I expect this node aggregate to occupy dimension space points [{}] - And I expect this node aggregate to cover dimension space points [{"example":"source"},{"example":"spec"},{"example":"leafSpec"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] And I expect the node aggregate "eldest-mc-nodeface" to exist - And I expect this node aggregate to occupy dimension space points [{"example":"source"}] - And I expect this node aggregate to cover dimension space points [{"example":"source"},{"example":"spec"},{"example":"leafSpec"}] + And I expect this node aggregate to occupy dimension space points [{"example":"general"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] And I expect the node aggregate "elder-mc-nodeface" to exist - And I expect this node aggregate to occupy dimension space points [{"example":"source"}] - And I expect this node aggregate to cover dimension space points [{"example":"source"},{"example":"spec"},{"example":"leafSpec"}] + And I expect this node aggregate to occupy dimension space points [{"example":"general"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] And I expect the node aggregate "nody-mc-nodeface" to exist - And I expect this node aggregate to occupy dimension space points [{"example":"source"},{"example":"spec"},{"example":"leafSpec"}] - And I expect this node aggregate to cover dimension space points [{"example":"source"},{"example":"spec"},{"example":"leafSpec"}] + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] And I expect the node aggregate "nodewyn-tetherton" to exist - And I expect this node aggregate to occupy dimension space points [{"example":"source"},{"example":"spec"},{"example":"leafSpec"}] - And I expect this node aggregate to cover dimension space points [{"example":"source"},{"example":"spec"},{"example":"leafSpec"}] + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] And I expect the node aggregate "nodimer-tetherton" to exist - And I expect this node aggregate to occupy dimension space points [{"example":"source"},{"example":"spec"},{"example":"leafSpec"}] - And I expect this node aggregate to cover dimension space points [{"example":"source"},{"example":"spec"},{"example":"leafSpec"}] + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] And I expect the node aggregate "invariable-mc-nodeface" to exist - And I expect this node aggregate to occupy dimension space points [{"example":"source"}] - And I expect this node aggregate to cover dimension space points [{"example":"source"},{"example":"spec"},{"example":"leafSpec"}] + And I expect this node aggregate to occupy dimension space points [{"example":"general"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] And I expect the node aggregate "younger-mc-nodeface" to exist - And I expect this node aggregate to occupy dimension space points [{"example":"source"}] - And I expect this node aggregate to cover dimension space points [{"example":"source"},{"example":"spec"},{"example":"leafSpec"}] + And I expect this node aggregate to occupy dimension space points [{"example":"general"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] And I expect the node aggregate "youngest-mc-nodeface" to exist - And I expect this node aggregate to occupy dimension space points [{"example":"source"}] - And I expect this node aggregate to cover dimension space points [{"example":"source"},{"example":"spec"},{"example":"leafSpec"}] + And I expect this node aggregate to occupy dimension space points [{"example":"general"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] - When I am in workspace "live" and dimension space point {"example":"spec"} + When I am in workspace "live" and dimension space point {"example":"source"} Then I expect the subgraph projection to consist of exactly 9 nodes Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | - | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"source"} | - | elder-document | cs-identifier;elder-mc-nodeface;{"example":"source"} | - | document | cs-identifier;nody-mc-nodeface;{"example":"spec"} | - | younger-document | cs-identifier;younger-mc-nodeface;{"example":"source"} | - | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"source"} | - And I expect node aggregate identifier "eldest-mc-nodeface" and node path "eldest-document" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"source"} + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" and node path "eldest-document" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: | NodeDiscriminator | - | cs-identifier;elder-mc-nodeface;{"example":"source"} | - | cs-identifier;nody-mc-nodeface;{"example":"spec"} | - | cs-identifier;younger-mc-nodeface;{"example":"source"} | - | cs-identifier;youngest-mc-nodeface;{"example":"source"} | - And I expect node aggregate identifier "elder-mc-nodeface" and node path "elder-document" to lead to node cs-identifier;elder-mc-nodeface;{"example":"source"} + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" and node path "elder-document" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | - | cs-identifier;eldest-mc-nodeface;{"example":"source"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have the following succeeding siblings: | NodeDiscriminator | - | cs-identifier;nody-mc-nodeface;{"example":"spec"} | - | cs-identifier;younger-mc-nodeface;{"example":"source"} | - | cs-identifier;youngest-mc-nodeface;{"example":"source"} | - And I expect node aggregate identifier "nody-mc-nodeface" and node path "document" to lead to node cs-identifier;nody-mc-nodeface;{"example":"spec"} + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" and node path "document" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | - | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"spec"} | - | invariable-document | cs-identifier;invariable-mc-nodeface;{"example":"source"} | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable-document | cs-identifier;invariable-mc-nodeface;{"example":"general"} | And I expect this node to have the following preceding siblings: | NodeDiscriminator | - | cs-identifier;elder-mc-nodeface;{"example":"source"} | - | cs-identifier;eldest-mc-nodeface;{"example":"source"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have the following succeeding siblings: | NodeDiscriminator | - | cs-identifier;younger-mc-nodeface;{"example":"source"} | - | cs-identifier;youngest-mc-nodeface;{"example":"source"} | - And I expect node aggregate identifier "nodewyn-tetherton" and node path "document/tethered-node" to lead to node cs-identifier;nodewyn-tetherton;{"example":"spec"} + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nodewyn-tetherton" and node path "document/tethered-node" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | - | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"spec"} | - And I expect node aggregate identifier "nodimer-tetherton" and node path "document/tethered-node/tethered-leaf" to lead to node cs-identifier;nodimer-tetherton;{"example":"spec"} - And I expect node aggregate identifier "invariable-mc-nodeface" and node path "document/invariable-document" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"source"} - And I expect node aggregate identifier "younger-mc-nodeface" and node path "younger-document" to lead to node cs-identifier;younger-mc-nodeface;{"example":"source"} + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" and node path "document/tethered-node/tethered-leaf" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" and node path "document/invariable-document" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" and node path "younger-document" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | - | cs-identifier;nody-mc-nodeface;{"example":"spec"} | - | cs-identifier;elder-mc-nodeface;{"example":"source"} | - | cs-identifier;eldest-mc-nodeface;{"example":"source"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have the following succeeding siblings: | NodeDiscriminator | - | cs-identifier;youngest-mc-nodeface;{"example":"source"} | - And I expect node aggregate identifier "youngest-mc-nodeface" and node path "youngest-document" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"source"} + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" and node path "youngest-document" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | - | cs-identifier;younger-mc-nodeface;{"example":"source"} | - | cs-identifier;nody-mc-nodeface;{"example":"spec"} | - | cs-identifier;elder-mc-nodeface;{"example":"source"} | - | cs-identifier;eldest-mc-nodeface;{"example":"source"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have no succeeding siblings - When I am in workspace "live" and dimension space point {"example":"leafSpec"} + When I am in workspace "live" and dimension space point {"example":"spec"} Then I expect the subgraph projection to consist of exactly 9 nodes Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | - | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"source"} | - | elder-document | cs-identifier;elder-mc-nodeface;{"example":"source"} | - | document | cs-identifier;nody-mc-nodeface;{"example":"leafSpec"} | - | younger-document | cs-identifier;younger-mc-nodeface;{"example":"source"} | - | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"source"} | - And I expect node aggregate identifier "eldest-mc-nodeface" and node path "eldest-document" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"source"} + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | document | cs-identifier;nody-mc-nodeface;{"example":"spec"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" and node path "eldest-document" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: | NodeDiscriminator | - | cs-identifier;elder-mc-nodeface;{"example":"source"} | - | cs-identifier;nody-mc-nodeface;{"example":"leafSpec"} | - | cs-identifier;younger-mc-nodeface;{"example":"source"} | - | cs-identifier;youngest-mc-nodeface;{"example":"source"} | - And I expect node aggregate identifier "elder-mc-nodeface" and node path "elder-document" to lead to node cs-identifier;elder-mc-nodeface;{"example":"source"} + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"spec"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" and node path "elder-document" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | - | cs-identifier;eldest-mc-nodeface;{"example":"source"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have the following succeeding siblings: | NodeDiscriminator | - | cs-identifier;nody-mc-nodeface;{"example":"leafSpec"} | - | cs-identifier;younger-mc-nodeface;{"example":"source"} | - | cs-identifier;youngest-mc-nodeface;{"example":"source"} | - And I expect node aggregate identifier "nody-mc-nodeface" and node path "document" to lead to node cs-identifier;nody-mc-nodeface;{"example":"leafSpec"} + | cs-identifier;nody-mc-nodeface;{"example":"spec"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" and node path "document" to lead to node cs-identifier;nody-mc-nodeface;{"example":"spec"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | - | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"leafSpec"} | - | invariable-document | cs-identifier;invariable-mc-nodeface;{"example":"source"} | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"spec"} | + | invariable-document | cs-identifier;invariable-mc-nodeface;{"example":"general"} | And I expect this node to have the following preceding siblings: | NodeDiscriminator | - | cs-identifier;elder-mc-nodeface;{"example":"source"} | - | cs-identifier;eldest-mc-nodeface;{"example":"source"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have the following succeeding siblings: | NodeDiscriminator | - | cs-identifier;younger-mc-nodeface;{"example":"source"} | - | cs-identifier;youngest-mc-nodeface;{"example":"source"} | - And I expect node aggregate identifier "nodewyn-tetherton" and node path "document/tethered-node" to lead to node cs-identifier;nodewyn-tetherton;{"example":"leafSpec"} + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nodewyn-tetherton" and node path "document/tethered-node" to lead to node cs-identifier;nodewyn-tetherton;{"example":"spec"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | - | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"leafSpec"} | - And I expect node aggregate identifier "nodimer-tetherton" and node path "document/tethered-node/tethered-leaf" to lead to node cs-identifier;nodimer-tetherton;{"example":"leafSpec"} - And I expect node aggregate identifier "invariable-mc-nodeface" and node path "document/invariable-document" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"source"} - And I expect node aggregate identifier "younger-mc-nodeface" and node path "younger-document" to lead to node cs-identifier;younger-mc-nodeface;{"example":"source"} + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"spec"} | + And I expect node aggregate identifier "nodimer-tetherton" and node path "document/tethered-node/tethered-leaf" to lead to node cs-identifier;nodimer-tetherton;{"example":"spec"} + And I expect node aggregate identifier "invariable-mc-nodeface" and node path "document/invariable-document" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" and node path "younger-document" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | - | cs-identifier;nody-mc-nodeface;{"example":"leafSpec"} | - | cs-identifier;elder-mc-nodeface;{"example":"source"} | - | cs-identifier;eldest-mc-nodeface;{"example":"source"} | + | cs-identifier;nody-mc-nodeface;{"example":"spec"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have the following succeeding siblings: | NodeDiscriminator | - | cs-identifier;youngest-mc-nodeface;{"example":"source"} | - And I expect node aggregate identifier "youngest-mc-nodeface" and node path "youngest-document" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"source"} + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" and node path "youngest-document" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | - | cs-identifier;younger-mc-nodeface;{"example":"source"} | - | cs-identifier;nody-mc-nodeface;{"example":"leafSpec"} | - | cs-identifier;elder-mc-nodeface;{"example":"source"} | - | cs-identifier;eldest-mc-nodeface;{"example":"source"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"spec"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have no succeeding siblings # @todo test based on NodeSpecializationVariantWasCreated ({"market":"CH", "language":"DE"}) From d3b9707a9f3d189d88fde00aaf8ca090992c07fa Mon Sep 17 00:00:00 2001 From: Bernhard Schmitt Date: Fri, 24 Oct 2025 16:23:11 +0200 Subject: [PATCH 3/9] 5054 - WIP: add failing tests --- ...odeSpecializationVariant-NewParent.feature | 279 ++++++++++++++++++ .../NodeSpecializationVariantWasCreated.php | 29 +- 2 files changed, 296 insertions(+), 12 deletions(-) create mode 100644 Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/05-CreateNodeSpecializationVariant-NewParent.feature diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/05-CreateNodeSpecializationVariant-NewParent.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/05-CreateNodeSpecializationVariant-NewParent.feature new file mode 100644 index 00000000000..9b335e2dcad --- /dev/null +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/05-CreateNodeSpecializationVariant-NewParent.feature @@ -0,0 +1,279 @@ +@contentrepository @adapters=DoctrineDBAL,Postgres +Feature: Create node specialization + As a user of the CR I want to create a variant of a node within an aggregate + to a more specialized dimension space point + and assign that variant to a different parent + * before the first of its new siblings + * before the first of its new siblings - which does not exist in all variants + * before one of its new siblings, wich is partially the first + * before one of its new siblings, which is not the first + * before one of its new siblings, which is not the first and does not exist in all variants + * before one of its new siblings, which is the last and does not exist in all variants + * after the last of its new siblings + * after the last of its new siblings, which does not exist in all variants + * after one of its new siblings, which is partially the last + * after one of its new siblings, which is not the last + + Background: + Given using the following content dimensions: + | Identifier | Values | Generalizations | + | example | general, source, spec, peer | spec->source->general, peer->general | + And using the following node types: + """yaml + 'Neos.ContentRepository.Testing:Document': + childNodes: + tethered-node: + type: 'Neos.ContentRepository.Testing:Tethered' + 'Neos.ContentRepository.Testing:Tethered': + childNodes: + tethered-leaf: + type: 'Neos.ContentRepository.Testing:TetheredLeaf' + 'Neos.ContentRepository.Testing:TetheredLeaf': [] + 'Neos.ContentRepository.Testing:LeafDocument': [] + """ + And using identifier "default", I define a content repository + And I am in content repository "default" + And I am user identified by "initiating-user-identifier" + And the command CreateRootWorkspace is executed with payload: + | Key | Value | + | workspaceName | "live" | + | newContentStreamId | "cs-identifier" | + And I am in workspace "live" and dimension space point {"example":"general"} + And the command CreateRootNodeAggregateWithNode is executed with payload: + | Key | Value | + | nodeAggregateId | "lady-eleonode-rootford" | + | nodeTypeName | "Neos.ContentRepository:Root" | + + And the following CreateNodeAggregateWithNode commands are executed: + | nodeAggregateId | nodeName | parentNodeAggregateId | nodeTypeName | tetheredDescendantNodeAggregateIds | + | sir-david-nodenborough | parent-document | Neos.ContentRepository.Testing:Document | lady-eleonode-rootford | {} | + | eldest-mc-nodeface | eldest-document | Neos.ContentRepository.Testing:Document | sir-david-nodenborough | {"tethered-node": "eldest-nodewyn-tetherton", "tethered-node/tethered-leaf": "eldest-nodimer-tetherton"} | + | elder-mc-nodeface | elder-document | Neos.ContentRepository.Testing:Document | sir-david-nodenborough | {"tethered-node": "elder-nodewyn-tetherton", "tethered-node/tethered-leaf": "elder-nodimer-tetherton"} | + | younger-mc-nodeface | younger-document | Neos.ContentRepository.Testing:Document | sir-david-nodenborough | {"tethered-node": "younger-nodewyn-tetherton", "tethered-node/tethered-leaf": "younger-nodimer-tetherton"} | + | youngest-mc-nodeface | youngest-document | Neos.ContentRepository.Testing:Document | sir-david-nodenborough | {"tethered-node": "youngest-nodewyn-tetherton", "tethered-node/tethered-leaf": "youngest-nodimer-tetherton"} | + | sir-nodeward-nodington-iii | esquire | Neos.ContentRepository.Testing:Document | lady-eleonode-rootford | {} | + | nody-mc-nodeface | document | Neos.ContentRepository.Testing:Document | sir-nodeward-nodington-iii | {"tethered-node": "nodewyn-tetherton", "tethered-node/tethered-leaf": "nodimer-tetherton"} | + + Scenario: Create specialization variant to a new parent before the first of its new siblings + When the command CreateNodeVariant is executed with payload: + | Key | Value | + | nodeAggregateId | "nody-mc-nodeface" | + | sourceOrigin | {"example":"general"} | + | targetOrigin | {"example":"source"} | + | parentNodeAggregateId | "sir-david-nodenborough" | + | succeedingSiblingNodeAggregateId | "eldest-mc-nodeface" | + Then I expect exactly 13 events to be published on stream "ContentStream:cs-identifier" + And event at index 10 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nody-mc-nodeface" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"eldest-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"eldest-mc-nodeface"}] | + | parentNodeAggregateId | "sir-david-nodenborough" | + And event at index 11 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodewyn-tetherton" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"invariable-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"invariable-mc-nodeface"}] | + | parentNodeAggregateId | null | + And event at index 12 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodimer-tetherton" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | + | parentNodeAggregateId | null | + + Then I expect the graph projection to consist of exactly 12 nodes + And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;eldest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;elder-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;younger-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;youngest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;invariable-mc-nodeface;{"example":"general"} to exist in the content graph + + When I am in workspace "live" + Then I expect the node aggregate "lady-eleonode-rootford" to exist + And I expect this node aggregate to occupy dimension space points [{}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] + + And I expect the node aggregate "eldest-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] + + And I expect the node aggregate "elder-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] + + And I expect the node aggregate "nody-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] + + And I expect the node aggregate "nodewyn-tetherton" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] + + And I expect the node aggregate "nodimer-tetherton" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] + + And I expect the node aggregate "invariable-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] + + And I expect the node aggregate "younger-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] + + And I expect the node aggregate "youngest-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] + + When I am in workspace "live" and dimension space point {"example":"source"} + Then I expect the subgraph projection to consist of exactly 9 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 3 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | eldest-mc-nodeface | + | 1 | elder-mc-nodeface | + | 1 | nody-mc-nodeface | + | 2 | nodewyn-tetherton | + | 3 | nodimer-tetherton | + | 2 | invariable-mc-nodeface | + | 1 | younger-mc-nodeface | + | 1 | youngest-mc-nodeface | + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" and node path "eldest-document" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" and node path "elder-document" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" and node path "document" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable-document | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nodewyn-tetherton" and node path "document/tethered-node" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" and node path "document/tethered-node/tethered-leaf" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" and node path "document/invariable-document" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" and node path "younger-document" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" and node path "youngest-document" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + When I am in workspace "live" and dimension space point {"example":"spec"} + Then I expect the subgraph projection to consist of exactly 9 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" and node path "eldest-document" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" and node path "elder-document" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" and node path "document" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable-document | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nodewyn-tetherton" and node path "document/tethered-node" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" and node path "document/tethered-node/tethered-leaf" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" and node path "document/invariable-document" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" and node path "younger-document" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" and node path "youngest-document" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Event/NodeSpecializationVariantWasCreated.php b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Event/NodeSpecializationVariantWasCreated.php index 709bef915d5..4f428b8cb4f 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Event/NodeSpecializationVariantWasCreated.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Event/NodeSpecializationVariantWasCreated.php @@ -45,6 +45,7 @@ public function __construct( public OriginDimensionSpacePoint $sourceOrigin, public OriginDimensionSpacePoint $specializationOrigin, public InterdimensionalSiblings $specializationSiblings, + public ?NodeAggregateId $parentNodeAggregateId, ) { } @@ -66,28 +67,32 @@ public function getWorkspaceName(): WorkspaceName public function withWorkspaceNameAndContentStreamId(WorkspaceName $targetWorkspaceName, ContentStreamId $contentStreamId): self { return new self( - $targetWorkspaceName, - $contentStreamId, - $this->nodeAggregateId, - $this->sourceOrigin, - $this->specializationOrigin, - $this->specializationSiblings, + workspaceName: $targetWorkspaceName, + contentStreamId: $contentStreamId, + nodeAggregateId: $this->nodeAggregateId, + sourceOrigin: $this->sourceOrigin, + specializationOrigin: $this->specializationOrigin, + specializationSiblings: $this->specializationSiblings, + parentNodeAggregateId: $this->parentNodeAggregateId, ); } public static function fromArray(array $values): self { return new self( - WorkspaceName::fromString($values['workspaceName']), - ContentStreamId::fromString($values['contentStreamId']), - NodeAggregateId::fromString($values['nodeAggregateId']), - OriginDimensionSpacePoint::fromArray($values['sourceOrigin']), - OriginDimensionSpacePoint::fromArray($values['specializationOrigin']), - array_key_exists('specializationSiblings', $values) + workspaceName: WorkspaceName::fromString($values['workspaceName']), + contentStreamId: ContentStreamId::fromString($values['contentStreamId']), + nodeAggregateId: NodeAggregateId::fromString($values['nodeAggregateId']), + sourceOrigin: OriginDimensionSpacePoint::fromArray($values['sourceOrigin']), + specializationOrigin: OriginDimensionSpacePoint::fromArray($values['specializationOrigin']), + specializationSiblings: array_key_exists('specializationSiblings', $values) ? InterdimensionalSiblings::fromArray($values['specializationSiblings']) : InterdimensionalSiblings::fromDimensionSpacePointSetWithoutSucceedingSiblings( DimensionSpacePointSet::fromArray($values['specializationCoverage']) ), + parentNodeAggregateId: array_key_exists('parentNodeAggregateId', $values) + ? NodeAggregateId::fromString($values['parentNodeAggregateId']) + : null, ); } From bbae9af490058fedf4f88c17f28e748114145aa8 Mon Sep 17 00:00:00 2001 From: Bernhard Schmitt Date: Fri, 7 Nov 2025 11:44:00 +0100 Subject: [PATCH 4/9] WIP: current state --- ...odeSpecializationVariant-NewParent.feature | 10 +-- .../Feature/Common/NodeVariationInternals.php | 84 +++++++++++++++++-- .../Feature/Common/TetheredNodeInternals.php | 6 +- .../Command/CreateNodeVariant.php | 14 +++- .../Feature/NodeVariation/NodeVariation.php | 3 +- 5 files changed, 100 insertions(+), 17 deletions(-) diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/05-CreateNodeSpecializationVariant-NewParent.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/05-CreateNodeSpecializationVariant-NewParent.feature index 9b335e2dcad..09dd6e1ee4b 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/05-CreateNodeSpecializationVariant-NewParent.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/05-CreateNodeSpecializationVariant-NewParent.feature @@ -45,7 +45,7 @@ Feature: Create node specialization | nodeTypeName | "Neos.ContentRepository:Root" | And the following CreateNodeAggregateWithNode commands are executed: - | nodeAggregateId | nodeName | parentNodeAggregateId | nodeTypeName | tetheredDescendantNodeAggregateIds | + | nodeAggregateId | nodeName | nodeTypeName | parentNodeAggregateId | tetheredDescendantNodeAggregateIds | | sir-david-nodenborough | parent-document | Neos.ContentRepository.Testing:Document | lady-eleonode-rootford | {} | | eldest-mc-nodeface | eldest-document | Neos.ContentRepository.Testing:Document | sir-david-nodenborough | {"tethered-node": "eldest-nodewyn-tetherton", "tethered-node/tethered-leaf": "eldest-nodimer-tetherton"} | | elder-mc-nodeface | elder-document | Neos.ContentRepository.Testing:Document | sir-david-nodenborough | {"tethered-node": "elder-nodewyn-tetherton", "tethered-node/tethered-leaf": "elder-nodimer-tetherton"} | @@ -62,8 +62,8 @@ Feature: Create node specialization | targetOrigin | {"example":"source"} | | parentNodeAggregateId | "sir-david-nodenborough" | | succeedingSiblingNodeAggregateId | "eldest-mc-nodeface" | - Then I expect exactly 13 events to be published on stream "ContentStream:cs-identifier" - And event at index 10 is of type "NodeSpecializationVariantWasCreated" with payload: + Then I expect exactly 26 events to be published on stream "ContentStream:cs-identifier" + And event at index 23 is of type "NodeSpecializationVariantWasCreated" with payload: | Key | Expected | | contentStreamId | "cs-identifier" | | nodeAggregateId | "nody-mc-nodeface" | @@ -71,7 +71,7 @@ Feature: Create node specialization | specializationOrigin | {"example":"source"} | | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"eldest-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"eldest-mc-nodeface"}] | | parentNodeAggregateId | "sir-david-nodenborough" | - And event at index 11 is of type "NodeSpecializationVariantWasCreated" with payload: + And event at index 24 is of type "NodeSpecializationVariantWasCreated" with payload: | Key | Expected | | contentStreamId | "cs-identifier" | | nodeAggregateId | "nodewyn-tetherton" | @@ -79,7 +79,7 @@ Feature: Create node specialization | specializationOrigin | {"example":"source"} | | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"invariable-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"invariable-mc-nodeface"}] | | parentNodeAggregateId | null | - And event at index 12 is of type "NodeSpecializationVariantWasCreated" with payload: + And event at index 25 is of type "NodeSpecializationVariantWasCreated" with payload: | Key | Expected | | contentStreamId | "cs-identifier" | | nodeAggregateId | "nodimer-tetherton" | diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/NodeVariationInternals.php b/Neos.ContentRepository.Core/Classes/Feature/Common/NodeVariationInternals.php index a23556782f1..7d68cef7f23 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/Common/NodeVariationInternals.php +++ b/Neos.ContentRepository.Core/Classes/Feature/Common/NodeVariationInternals.php @@ -23,6 +23,7 @@ use Neos\ContentRepository\Core\Feature\NodeVariation\Event\NodePeerVariantWasCreated; use Neos\ContentRepository\Core\Feature\NodeVariation\Event\NodeSpecializationVariantWasCreated; use Neos\ContentRepository\Core\Projection\ContentGraph\ContentGraphInterface; +use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindPrecedingSiblingNodesFilter; use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindSucceedingSiblingNodesFilter; use Neos\ContentRepository\Core\Projection\ContentGraph\NodeAggregate; use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; @@ -39,7 +40,8 @@ protected function createEventsForVariations( ContentGraphInterface $contentGraph, OriginDimensionSpacePoint $sourceOrigin, OriginDimensionSpacePoint $targetOrigin, - NodeAggregate $nodeAggregate + NodeAggregate $nodeAggregate, + ?NodeAggregateId $parentNodeAggregateId, ): Events { return match ( $this->getInterDimensionalVariationGraph()->getVariantType( @@ -51,7 +53,8 @@ protected function createEventsForVariations( $contentGraph, $sourceOrigin, $targetOrigin, - $nodeAggregate + $nodeAggregate, + $parentNodeAggregateId, ), DimensionSpace\VariantType::TYPE_GENERALIZATION => $this->handleCreateNodeGeneralizationVariant( $contentGraph, @@ -72,7 +75,8 @@ protected function handleCreateNodeSpecializationVariant( ContentGraphInterface $contentGraph, OriginDimensionSpacePoint $sourceOrigin, OriginDimensionSpacePoint $targetOrigin, - NodeAggregate $nodeAggregate + NodeAggregate $nodeAggregate, + ?NodeAggregateId $parentNodeAggregateId, ): Events { $specializationVisibility = $this->calculateEffectiveVisibility($targetOrigin, $nodeAggregate); $events = $this->collectNodeSpecializationVariantsThatWillHaveBeenCreated( @@ -81,7 +85,8 @@ protected function handleCreateNodeSpecializationVariant( $targetOrigin, $nodeAggregate, $specializationVisibility, - [] + $parentNodeAggregateId, + [], ); return Events::fromArray($events); @@ -97,7 +102,8 @@ protected function collectNodeSpecializationVariantsThatWillHaveBeenCreated( OriginDimensionSpacePoint $targetOrigin, NodeAggregate $nodeAggregate, DimensionSpacePointSet $specializationVisibility, - array $events + ?NodeAggregateId $parentNodeAggregateId, + array $events, ): array { $events[] = new NodeSpecializationVariantWasCreated( $contentGraph->getWorkspaceName(), @@ -111,6 +117,7 @@ protected function collectNodeSpecializationVariantsThatWillHaveBeenCreated( $sourceOrigin, $specializationVisibility ), + $parentNodeAggregateId, ); foreach ( @@ -124,6 +131,7 @@ protected function collectNodeSpecializationVariantsThatWillHaveBeenCreated( $targetOrigin, $tetheredChildNodeAggregate, $specializationVisibility, + $parentNodeAggregateId, $events ); } @@ -305,6 +313,72 @@ private function resolveInterdimensionalSiblings( return new InterdimensionalSiblings(...$interdimensionalSiblings); } + /** + * Resolves the succeeding siblings for the node variant to be created using a new parent + * and all dimension space points the variant will cover. + * + * For each dimension space point in the variant coverage + * a) All the succeeding siblings of the node aggregate in the source origin are checked + * and the first one existing in this dimension space point is used + * b) As fallback no succeeding sibling is specified + * + * Developers hint: + * Similar to {@see NodeCreationInternals::resolveInterdimensionalSiblingsForCreation()} + * except this operates on the to-be-varied node itself instead of an explicitly set succeeding sibling + */ + private function resolveInterdimensionalSiblingsForNewParent( + ContentGraphInterface $contentGraph, + NodeAggregateId $varyingNodeAggregateId, + OriginDimensionSpacePoint $sourceOrigin, + DimensionSpacePointSet $variantCoverage, + NodeAggregateId $parentNodeAggregateId, + ): InterdimensionalSiblings { + $selectedSubgraph = $contentGraph->getSubgraph( + $selectedDimensionSpacePoint, + VisibilityConstraints::createEmpty() + ); + $alternativeSucceedingSiblingIds = $succeedingSiblingId + ? $selectedSubgraph->findSucceedingSiblingNodes( + $succeedingSiblingId, + FindSucceedingSiblingNodesFilter::create() + )->toNodeAggregateIds() + : null; + $alternativePrecedingSiblingIds = $precedingSiblingId + ? $selectedSubgraph->findPrecedingSiblingNodes( + $precedingSiblingId, + FindPrecedingSiblingNodesFilter::create() + )->toNodeAggregateIds() + : null; + $originSiblings = $contentGraph + ->getSubgraph($sourceOrigin->toDimensionSpacePoint(), VisibilityConstraints::createEmpty()) + ->findSucceedingSiblingNodes($varyingNodeAggregateId, FindSucceedingSiblingNodesFilter::create()); + + $interdimensionalSiblings = []; + foreach ($variantCoverage as $variantDimensionSpacePoint) { + // check the siblings succeeding in the origin dimension space point + foreach ($originSiblings as $originSibling) { + $variantSibling = $contentGraph->getSubgraph($variantDimensionSpacePoint, VisibilityConstraints::createEmpty())->findNodeById($originSibling->aggregateId); + if (!$variantSibling) { + continue; + } + // a) one of the further succeeding sibling exists in this dimension space point + $interdimensionalSiblings[] = new InterdimensionalSibling( + $variantDimensionSpacePoint, + $variantSibling->aggregateId, + ); + continue 2; + } + + // b) fallback; there is no succeeding sibling in this dimension space point + $interdimensionalSiblings[] = new InterdimensionalSibling( + $variantDimensionSpacePoint, + null, + ); + } + + return new InterdimensionalSiblings(...$interdimensionalSiblings); + } + private function calculateEffectiveVisibility( OriginDimensionSpacePoint $targetOrigin, NodeAggregate $nodeAggregate diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/TetheredNodeInternals.php b/Neos.ContentRepository.Core/Classes/Feature/Common/TetheredNodeInternals.php index 9069b04eeb4..e2253243d61 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/Common/TetheredNodeInternals.php +++ b/Neos.ContentRepository.Core/Classes/Feature/Common/TetheredNodeInternals.php @@ -55,7 +55,8 @@ abstract protected function createEventsForVariations( ContentGraphInterface $contentGraph, OriginDimensionSpacePoint $sourceOrigin, OriginDimensionSpacePoint $targetOrigin, - NodeAggregate $nodeAggregate + NodeAggregate $nodeAggregate, + ?NodeAggregateId $parentNodeAggregateId, ): Events; /** @@ -178,7 +179,8 @@ protected function createEventsForMissingTetheredNode( contentGraph: $contentGraph, sourceOrigin: $arbitraryOccupiedDimensionSpacePoint, targetOrigin: $originDimensionSpacePoint, - nodeAggregate: $childNodeAggregate + nodeAggregate: $childNodeAggregate, + parentNodeAggregateId: null, ); } diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Command/CreateNodeVariant.php b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Command/CreateNodeVariant.php index 5eb6c9f1b2d..5bdd970d9b0 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Command/CreateNodeVariant.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Command/CreateNodeVariant.php @@ -46,6 +46,7 @@ private function __construct( public OriginDimensionSpacePoint $sourceOrigin, public OriginDimensionSpacePoint $targetOrigin, public ?NodeAggregateId $parentNodeAggregateId, + public ?NodeAggregateId $precedingSiblingNodeAggregateId, public ?NodeAggregateId $succeedingSiblingNodeAggregateId, ) { } @@ -55,12 +56,13 @@ private function __construct( * @param NodeAggregateId $nodeAggregateId The identifier of the affected node aggregate * @param OriginDimensionSpacePoint $sourceOrigin Dimension Space Point from which the node is to be copied from * @param OriginDimensionSpacePoint $targetOrigin Dimension Space Point to which the node is to be copied to - * @param ?NodeAggregateId $parentNodeAggregateId The id of the node aggregate to be used as the variant's parent + * @param ?NodeAggregateId $parentNodeAggregateId The optional id of the node aggregate to be used as the variant's parent + * @param ?NodeAggregateId $precedingSiblingNodeAggregateId The optional id of the node aggregate to be used as the variant's preceding sibling * @param ?NodeAggregateId $succeedingSiblingNodeAggregateId The optional id of the node aggregate to be used as the variant's succeeding sibling */ - public static function create(WorkspaceName $workspaceName, NodeAggregateId $nodeAggregateId, OriginDimensionSpacePoint $sourceOrigin, OriginDimensionSpacePoint $targetOrigin, ?NodeAggregateId $parentNodeAggregateId = null, ?NodeAggregateId $succeedingSiblingNodeAggregateId = null): self + public static function create(WorkspaceName $workspaceName, NodeAggregateId $nodeAggregateId, OriginDimensionSpacePoint $sourceOrigin, OriginDimensionSpacePoint $targetOrigin, ?NodeAggregateId $parentNodeAggregateId = null, ?NodeAggregateId $precedingSiblingNodeAggregateId = null, ?NodeAggregateId $succeedingSiblingNodeAggregateId = null): self { - return new self($workspaceName, $nodeAggregateId, $sourceOrigin, $targetOrigin, $parentNodeAggregateId, $succeedingSiblingNodeAggregateId); + return new self($workspaceName, $nodeAggregateId, $sourceOrigin, $targetOrigin, $parentNodeAggregateId, $precedingSiblingNodeAggregateId, $succeedingSiblingNodeAggregateId); } public static function fromArray(array $array): self @@ -73,6 +75,9 @@ public static function fromArray(array $array): self array_key_exists('parentNodeAggregateId', $array) ? NodeAggregateId::fromString($array['parentNodeAggregateId']) : null, + array_key_exists('precedingSiblingNodeAggregateId', $array) + ? NodeAggregateId::fromString($array['precedingSiblingNodeAggregateId']) + : null, array_key_exists('succeedingSiblingNodeAggregateId', $array) ? NodeAggregateId::fromString($array['succeedingSiblingNodeAggregateId']) : null, @@ -96,7 +101,8 @@ public function createCopyForWorkspace( $this->sourceOrigin, $this->targetOrigin, $this->parentNodeAggregateId, - $this->succeedingSiblingNodeAggregateId, + $this->precedingSiblingNodeAggregateId, + $this->succeedingSiblingNodeAggregateId ); } } diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/NodeVariation.php b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/NodeVariation.php index 76b9bfa8757..2db5ce1a2b5 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/NodeVariation.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/NodeVariation.php @@ -123,7 +123,8 @@ private function handleCreateNodeVariant( $contentGraph, $command->sourceOrigin, $command->targetOrigin, - $nodeAggregate + $nodeAggregate, + $command->parentNodeAggregateId, ); return new EventsToPublish( From 40cba96958e0ba33b22fc18914bc1968564ca1ac Mon Sep 17 00:00:00 2001 From: Bernhard Schmitt Date: Sat, 29 Nov 2025 22:51:27 +0100 Subject: [PATCH 5/9] 5054 - WIP: Implement node variation with target parent --- .../DoctrineDbalContentGraphProjection.php | 10 +- .../Projection/Feature/NodeVariation.php | 75 +++- .../Domain/Projection/HierarchyRelation.php | 25 ++ ...CreateNodeVariant_ConstraintChecks.feature | 4 +- ...odeSpecializationVariant-NewParent.feature | 332 ++++++++++---- ...RemoveNodeAggregate_WithDimensions.feature | 37 +- ...SpecializationVariantAfterDeletion.feature | 48 ++- .../InterdimensionalSiblingsProvider.php | 408 ++++++++++++++++++ .../Feature/Common/NodeCreationInternals.php | 90 ---- .../Feature/Common/NodeVariationInternals.php | 224 ++++------ .../Feature/Common/TetheredNodeInternals.php | 6 + .../Feature/NodeCreation/NodeCreation.php | 4 +- .../Classes/Feature/NodeMove/NodeMove.php | 169 +------- .../Command/CreateNodeVariant.php | 20 +- .../NodeSpecializationVariantWasCreated.php | 4 +- .../Feature/NodeVariation/NodeVariation.php | 12 +- 16 files changed, 945 insertions(+), 523 deletions(-) create mode 100644 Neos.ContentRepository.Core/Classes/Feature/Common/InterdimensionalSiblingsProvider.php delete mode 100644 Neos.ContentRepository.Core/Classes/Feature/Common/NodeCreationInternals.php diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphProjection.php b/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphProjection.php index fc713cc4c0b..509e8a89e2b 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphProjection.php +++ b/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphProjection.php @@ -599,7 +599,15 @@ private function writeReferencesForTargetAnchorPoint(SerializedNodeReferences $n private function whenNodeSpecializationVariantWasCreated(NodeSpecializationVariantWasCreated $event, EventEnvelope $eventEnvelope): void { - $this->createNodeSpecializationVariant($event->contentStreamId, $event->nodeAggregateId, $event->sourceOrigin, $event->specializationOrigin, $event->specializationSiblings, $eventEnvelope); + $this->createNodeSpecializationVariant( + contentStreamId: $event->contentStreamId, + nodeAggregateId: $event->nodeAggregateId, + sourceOrigin: $event->sourceOrigin, + specializationOrigin: $event->specializationOrigin, + parentNodeAggregateId: $event->parentNodeAggregateId, + specializationSiblings: $event->specializationSiblings, + eventEnvelope: $eventEnvelope, + ); } private function whenRootNodeAggregateDimensionsWereUpdated(RootNodeAggregateDimensionsWereUpdated $event): void diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/Feature/NodeVariation.php b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/Feature/NodeVariation.php index 233309f003c..04edb7f8438 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/Feature/NodeVariation.php +++ b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/Feature/NodeVariation.php @@ -21,8 +21,15 @@ */ trait NodeVariation { - private function createNodeSpecializationVariant(ContentStreamId $contentStreamId, NodeAggregateId $nodeAggregateId, OriginDimensionSpacePoint $sourceOrigin, OriginDimensionSpacePoint $specializationOrigin, InterdimensionalSiblings $specializationSiblings, EventEnvelope $eventEnvelope): void - { + private function createNodeSpecializationVariant( + ContentStreamId $contentStreamId, + NodeAggregateId $nodeAggregateId, + OriginDimensionSpacePoint $sourceOrigin, + OriginDimensionSpacePoint $specializationOrigin, + ?NodeAggregateId $parentNodeAggregateId, + InterdimensionalSiblings $specializationSiblings, + EventEnvelope $eventEnvelope + ): void { // Do the actual specialization $sourceNode = $this->projectionContentGraph->findNodeInAggregate( $contentStreamId, @@ -47,30 +54,68 @@ private function createNodeSpecializationVariant(ContentStreamId $contentStreamI $specializationSiblings->toDimensionSpacePointSet() ) as $hierarchyRelation ) { - $hierarchyRelation->assignNewChildNode( - $specializedNode->relationAnchorPoint, - $this->dbal, - $this->tableNames - ); + if ($parentNodeAggregateId) { + $targetParent = $this->projectionContentGraph->findNodeInAggregate( + $contentStreamId, + $parentNodeAggregateId, + $hierarchyRelation->dimensionSpacePoint, + ); + if (is_null($targetParent)) { + throw new \RuntimeException(sprintf('Failed to create node specialization variant for node "%s" in sub graph %s@%s because the target parent node is missing', $nodeAggregateId->value, $sourceOrigin->toJson(), $contentStreamId->value), 1764434947); + } + + $succeedingSiblingNodeAnchorPoint = null; + $succeedingSiblingId = $specializationSiblings->getSucceedingSiblingIdForDimensionSpacePoint( + $hierarchyRelation->dimensionSpacePoint + ); + if ($succeedingSiblingId) { + $succeedingSiblingNodeAnchorPoint = $this->projectionContentGraph->findNodeInAggregate( + $contentStreamId, + $succeedingSiblingId, + $hierarchyRelation->dimensionSpacePoint + )?->relationAnchorPoint; + } + + $hierarchyRelation->assignNewParentAndChildNode( + $targetParent->relationAnchorPoint, + $specializedNode->relationAnchorPoint, + $this->getRelationPosition( + $targetParent->relationAnchorPoint, + $specializedNode->relationAnchorPoint, + $succeedingSiblingNodeAnchorPoint, + $contentStreamId, + $hierarchyRelation->dimensionSpacePoint, + ), + $this->dbal, + $this->tableNames, + ); + } else { + $hierarchyRelation->assignNewChildNode( + $specializedNode->relationAnchorPoint, + $this->dbal, + $this->tableNames + ); + } unset($uncoveredDimensionSpacePoints[$hierarchyRelation->dimensionSpacePointHash]); } if (!empty($uncoveredDimensionSpacePoints)) { - $sourceParent = $this->projectionContentGraph->findParentNode( - $contentStreamId, - $nodeAggregateId, - $sourceOrigin, - ); - if (is_null($sourceParent)) { + $parentNodeAggregateId = $parentNodeAggregateId + ?: $this->projectionContentGraph->findParentNode( + $contentStreamId, + $nodeAggregateId, + $sourceOrigin, + )?->nodeAggregateId; + if (is_null($parentNodeAggregateId)) { throw new \RuntimeException(sprintf('Failed to create node specialization variant for node "%s" in sub graph %s@%s because the source parent node is missing', $nodeAggregateId->value, $sourceOrigin->toJson(), $contentStreamId->value), 1716498695); } foreach ($uncoveredDimensionSpacePoints as $uncoveredDimensionSpacePoint) { $parentNode = $this->projectionContentGraph->findNodeInAggregate( $contentStreamId, - $sourceParent->nodeAggregateId, + $parentNodeAggregateId, $uncoveredDimensionSpacePoint ); if (is_null($parentNode)) { - throw new \RuntimeException(sprintf('Failed to create node specialization variant for node "%s" in sub graph %s@%s because the target parent node "%s" is missing', $nodeAggregateId->value, $sourceOrigin->toJson(), $contentStreamId->value, $sourceParent->nodeAggregateId->value), 1716498734); + throw new \RuntimeException(sprintf('Failed to create node specialization variant for node "%s" in sub graph %s@%s because the target parent node "%s" is missing', $nodeAggregateId->value, $sourceOrigin->toJson(), $contentStreamId->value, $parentNodeAggregateId->value), 1716498734); } $parentSubtreeTags = $this->subtreeTagsForHierarchyRelation($contentStreamId, $parentNode->relationAnchorPoint, $uncoveredDimensionSpacePoint); diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/HierarchyRelation.php b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/HierarchyRelation.php index 0b34a8249e1..fb9af1cfc82 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/HierarchyRelation.php +++ b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/HierarchyRelation.php @@ -114,6 +114,31 @@ public function assignNewParentNode( } } + public function assignNewParentAndChildNode( + NodeRelationAnchorPoint $parentAnchorPoint, + NodeRelationAnchorPoint $childAnchorPoint, + ?int $position, + Connection $databaseConnection, + ContentGraphTableNames $tableNames + ): void { + $data = [ + 'parentnodeanchor' => $parentAnchorPoint->value, + 'childnodeanchor' => $childAnchorPoint->value, + ]; + if (!is_null($position)) { + $data['position'] = $position; + } + try { + $databaseConnection->update( + $tableNames->hierarchyRelation(), + $data, + $this->getDatabaseId() + ); + } catch (DBALException $e) { + throw new \RuntimeException(sprintf('Failed to update hierarchy relation: %s', $e->getMessage()), 1716478609, $e); + } + } + public function assignNewPosition(int $position, Connection $databaseConnection, ContentGraphTableNames $tableNames): void { try { diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/01-CreateNodeVariant_ConstraintChecks.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/01-CreateNodeVariant_ConstraintChecks.feature index e0461a8e22c..0584b1e2afe 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/01-CreateNodeVariant_ConstraintChecks.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/01-CreateNodeVariant_ConstraintChecks.feature @@ -59,7 +59,7 @@ Feature: Create node variant # ...and we have to add yet another node for node type constraint checks And the following CreateNodeAggregateWithNode commands are executed: | nodeAggregateId | nodeName | parentNodeAggregateId | nodeTypeName | tetheredDescendantNodeAggregateIds | - | the-governode | governode | lady-eleonode-rootford | Neos.ContentRepository.Testing:RestrictiveDocument | {"tethered": "nodimer-tetherton"} | + | the-governode | governode | lady-eleonode-rootford | Neos.ContentRepository.Testing:RestrictiveDocument | {"tethered": "another-tetherton"} | Scenario: Try to create a variant in a workspace that does not exist When the command CreateNodeVariant is executed with payload and exceptions are caught: @@ -229,5 +229,5 @@ Feature: Create node variant | nodeAggregateId | "polyglot-mc-nodeface" | | sourceOrigin | {"market":"DE", "language":"de"} | | targetOrigin | {"market":"DE", "language":"gsw"} | - | parentNodeAggregateId | "nodimer-tetherton" | + | parentNodeAggregateId | "another-tetherton" | Then the last command should have thrown an exception of type "NodeConstraintException" diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/05-CreateNodeSpecializationVariant-NewParent.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/05-CreateNodeSpecializationVariant-NewParent.feature index 09dd6e1ee4b..3d523c06c81 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/05-CreateNodeSpecializationVariant-NewParent.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/05-CreateNodeSpecializationVariant-NewParent.feature @@ -45,14 +45,15 @@ Feature: Create node specialization | nodeTypeName | "Neos.ContentRepository:Root" | And the following CreateNodeAggregateWithNode commands are executed: - | nodeAggregateId | nodeName | nodeTypeName | parentNodeAggregateId | tetheredDescendantNodeAggregateIds | - | sir-david-nodenborough | parent-document | Neos.ContentRepository.Testing:Document | lady-eleonode-rootford | {} | - | eldest-mc-nodeface | eldest-document | Neos.ContentRepository.Testing:Document | sir-david-nodenborough | {"tethered-node": "eldest-nodewyn-tetherton", "tethered-node/tethered-leaf": "eldest-nodimer-tetherton"} | - | elder-mc-nodeface | elder-document | Neos.ContentRepository.Testing:Document | sir-david-nodenborough | {"tethered-node": "elder-nodewyn-tetherton", "tethered-node/tethered-leaf": "elder-nodimer-tetherton"} | - | younger-mc-nodeface | younger-document | Neos.ContentRepository.Testing:Document | sir-david-nodenborough | {"tethered-node": "younger-nodewyn-tetherton", "tethered-node/tethered-leaf": "younger-nodimer-tetherton"} | - | youngest-mc-nodeface | youngest-document | Neos.ContentRepository.Testing:Document | sir-david-nodenborough | {"tethered-node": "youngest-nodewyn-tetherton", "tethered-node/tethered-leaf": "youngest-nodimer-tetherton"} | - | sir-nodeward-nodington-iii | esquire | Neos.ContentRepository.Testing:Document | lady-eleonode-rootford | {} | - | nody-mc-nodeface | document | Neos.ContentRepository.Testing:Document | sir-nodeward-nodington-iii | {"tethered-node": "nodewyn-tetherton", "tethered-node/tethered-leaf": "nodimer-tetherton"} | + | nodeAggregateId | nodeName | nodeTypeName | parentNodeAggregateId | tetheredDescendantNodeAggregateIds | + | sir-david-nodenborough | parent-document | Neos.ContentRepository.Testing:LeafDocument | lady-eleonode-rootford | {} | + | eldest-mc-nodeface | eldest-document | Neos.ContentRepository.Testing:Document | sir-david-nodenborough | {"tethered-node": "eldest-nodewyn-tetherton", "tethered-node/tethered-leaf": "eldest-nodimer-tetherton"} | + | elder-mc-nodeface | elder-document | Neos.ContentRepository.Testing:Document | sir-david-nodenborough | {"tethered-node": "elder-nodewyn-tetherton", "tethered-node/tethered-leaf": "elder-nodimer-tetherton"} | + | younger-mc-nodeface | younger-document | Neos.ContentRepository.Testing:Document | sir-david-nodenborough | {"tethered-node": "younger-nodewyn-tetherton", "tethered-node/tethered-leaf": "younger-nodimer-tetherton"} | + | youngest-mc-nodeface | youngest-document | Neos.ContentRepository.Testing:Document | sir-david-nodenborough | {"tethered-node": "youngest-nodewyn-tetherton", "tethered-node/tethered-leaf": "youngest-nodimer-tetherton"} | + | sir-nodeward-nodington-iii | esquire | Neos.ContentRepository.Testing:LeafDocument | lady-eleonode-rootford | {} | + | nody-mc-nodeface | document | Neos.ContentRepository.Testing:Document | sir-nodeward-nodington-iii | {"tethered-node": "nodewyn-tetherton", "tethered-node/tethered-leaf": "nodimer-tetherton"} | + | invariable-mc-nodeface | invariable | Neos.ContentRepository.Testing:LeafDocument | nody-mc-nodeface | {} | Scenario: Create specialization variant to a new parent before the first of its new siblings When the command CreateNodeVariant is executed with payload: @@ -62,8 +63,8 @@ Feature: Create node specialization | targetOrigin | {"example":"source"} | | parentNodeAggregateId | "sir-david-nodenborough" | | succeedingSiblingNodeAggregateId | "eldest-mc-nodeface" | - Then I expect exactly 26 events to be published on stream "ContentStream:cs-identifier" - And event at index 23 is of type "NodeSpecializationVariantWasCreated" with payload: + Then I expect exactly 23 events to be published on stream "ContentStream:cs-identifier" + And event at index 20 is of type "NodeSpecializationVariantWasCreated" with payload: | Key | Expected | | contentStreamId | "cs-identifier" | | nodeAggregateId | "nody-mc-nodeface" | @@ -71,7 +72,7 @@ Feature: Create node specialization | specializationOrigin | {"example":"source"} | | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"eldest-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"eldest-mc-nodeface"}] | | parentNodeAggregateId | "sir-david-nodenborough" | - And event at index 24 is of type "NodeSpecializationVariantWasCreated" with payload: + And event at index 21 is of type "NodeSpecializationVariantWasCreated" with payload: | Key | Expected | | contentStreamId | "cs-identifier" | | nodeAggregateId | "nodewyn-tetherton" | @@ -79,7 +80,7 @@ Feature: Create node specialization | specializationOrigin | {"example":"source"} | | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"invariable-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"invariable-mc-nodeface"}] | | parentNodeAggregateId | null | - And event at index 25 is of type "NodeSpecializationVariantWasCreated" with payload: + And event at index 22 is of type "NodeSpecializationVariantWasCreated" with payload: | Key | Expected | | contentStreamId | "cs-identifier" | | nodeAggregateId | "nodimer-tetherton" | @@ -88,7 +89,7 @@ Feature: Create node specialization | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | | parentNodeAggregateId | null | - Then I expect the graph projection to consist of exactly 12 nodes + Then I expect the graph projection to consist of exactly 22 nodes And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"general"} to exist in the content graph And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"source"} to exist in the content graph @@ -105,175 +106,358 @@ Feature: Create node specialization When I am in workspace "live" Then I expect the node aggregate "lady-eleonode-rootford" to exist And I expect this node aggregate to occupy dimension space points [{}] - And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] And I expect the node aggregate "eldest-mc-nodeface" to exist And I expect this node aggregate to occupy dimension space points [{"example":"general"}] - And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] And I expect the node aggregate "elder-mc-nodeface" to exist And I expect this node aggregate to occupy dimension space points [{"example":"general"}] - And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] And I expect the node aggregate "nody-mc-nodeface" to exist And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] - And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] And I expect the node aggregate "nodewyn-tetherton" to exist And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] - And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] And I expect the node aggregate "nodimer-tetherton" to exist And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] - And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] - - And I expect the node aggregate "invariable-mc-nodeface" to exist - And I expect this node aggregate to occupy dimension space points [{"example":"general"}] - And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] And I expect the node aggregate "younger-mc-nodeface" to exist And I expect this node aggregate to occupy dimension space points [{"example":"general"}] - And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] And I expect the node aggregate "youngest-mc-nodeface" to exist And I expect this node aggregate to occupy dimension space points [{"example":"general"}] - And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "invariable-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] When I am in workspace "live" and dimension space point {"example":"source"} - Then I expect the subgraph projection to consist of exactly 9 nodes + Then I expect the subgraph projection to consist of exactly 19 nodes Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} - And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 3 levels deep should be: - | Level | nodeAggregateId | - | 0 | lady-eleonode-rootford | - | 1 | eldest-mc-nodeface | - | 1 | elder-mc-nodeface | - | 1 | nody-mc-nodeface | - | 2 | nodewyn-tetherton | - | 3 | nodimer-tetherton | - | 2 | invariable-mc-nodeface | - | 1 | younger-mc-nodeface | - | 1 | youngest-mc-nodeface | + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + | 2 | eldest-mc-nodeface | + | 3 | eldest-nodewyn-tetherton | + | 4 | eldest-nodimer-tetherton | + | 2 | elder-mc-nodeface | + | 3 | elder-nodewyn-tetherton | + | 4 | elder-nodimer-tetherton | + | 2 | younger-mc-nodeface | + | 3 | younger-nodewyn-tetherton | + | 4 | younger-nodimer-tetherton | + | 2 | youngest-mc-nodeface | + | 3 | youngest-nodewyn-tetherton | + | 4 | youngest-nodimer-tetherton | + | 1 | sir-nodeward-nodington-iii | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | - | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | - And I expect node aggregate identifier "eldest-mc-nodeface" and node path "eldest-document" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} - And I expect this node to have no preceding siblings + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | And I expect this node to have the following succeeding siblings: | NodeDiscriminator | | cs-identifier;elder-mc-nodeface;{"example":"general"} | - | cs-identifier;nody-mc-nodeface;{"example":"source"} | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | - And I expect node aggregate identifier "elder-mc-nodeface" and node path "elder-document" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: | NodeDiscriminator | - | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | - And I expect node aggregate identifier "nody-mc-nodeface" and node path "document" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} And I expect this node to have the following child nodes: - | Name | NodeDiscriminator | - | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | - | invariable-document | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + And I expect this node to have no succeeding siblings + + When I am in workspace "live" and dimension space point {"example":"general"} + Then I expect the subgraph projection to consist of exactly 19 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 3 | eldest-nodewyn-tetherton | + | 4 | eldest-nodimer-tetherton | + | 2 | elder-mc-nodeface | + | 3 | elder-nodewyn-tetherton | + | 4 | elder-nodimer-tetherton | + | 2 | younger-mc-nodeface | + | 3 | younger-nodewyn-tetherton | + | 4 | younger-nodimer-tetherton | + | 2 | youngest-mc-nodeface | + | 3 | youngest-nodewyn-tetherton | + | 4 | youngest-nodimer-tetherton | + | 1 | sir-nodeward-nodington-iii | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have the following succeeding siblings: | NodeDiscriminator | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | - And I expect node aggregate identifier "nodewyn-tetherton" and node path "document/tethered-node" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | - | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | - And I expect node aggregate identifier "nodimer-tetherton" and node path "document/tethered-node/tethered-leaf" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} - And I expect node aggregate identifier "invariable-mc-nodeface" and node path "document/invariable-document" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} - And I expect node aggregate identifier "younger-mc-nodeface" and node path "younger-document" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | - | cs-identifier;nody-mc-nodeface;{"example":"source"} | | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have the following succeeding siblings: | NodeDiscriminator | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | - And I expect node aggregate identifier "youngest-mc-nodeface" and node path "youngest-document" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | | cs-identifier;younger-mc-nodeface;{"example":"general"} | - | cs-identifier;nody-mc-nodeface;{"example":"source"} | | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have no succeeding siblings When I am in workspace "live" and dimension space point {"example":"spec"} - Then I expect the subgraph projection to consist of exactly 9 nodes + Then I expect the subgraph projection to consist of exactly 19 nodes Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + | 2 | eldest-mc-nodeface | + | 3 | eldest-nodewyn-tetherton | + | 4 | eldest-nodimer-tetherton | + | 2 | elder-mc-nodeface | + | 3 | elder-nodewyn-tetherton | + | 4 | elder-nodimer-tetherton | + | 2 | younger-mc-nodeface | + | 3 | younger-nodewyn-tetherton | + | 4 | younger-nodimer-tetherton | + | 2 | youngest-mc-nodeface | + | 3 | youngest-nodewyn-tetherton | + | 4 | youngest-nodimer-tetherton | + | 1 | sir-nodeward-nodington-iii | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | - | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | - And I expect node aggregate identifier "eldest-mc-nodeface" and node path "eldest-document" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} - And I expect this node to have no preceding siblings + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | And I expect this node to have the following succeeding siblings: | NodeDiscriminator | | cs-identifier;elder-mc-nodeface;{"example":"general"} | - | cs-identifier;nody-mc-nodeface;{"example":"source"} | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | - And I expect node aggregate identifier "elder-mc-nodeface" and node path "elder-document" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: | NodeDiscriminator | - | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | - And I expect node aggregate identifier "nody-mc-nodeface" and node path "document" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} And I expect this node to have the following child nodes: - | Name | NodeDiscriminator | - | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | - | invariable-document | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | And I expect this node to have the following succeeding siblings: | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + And I expect this node to have no succeeding siblings + + When I am in workspace "live" and dimension space point {"example":"peer"} + Then I expect the subgraph projection to consist of exactly 19 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 3 | eldest-nodewyn-tetherton | + | 4 | eldest-nodimer-tetherton | + | 2 | elder-mc-nodeface | + | 3 | elder-nodewyn-tetherton | + | 4 | elder-nodimer-tetherton | + | 2 | younger-mc-nodeface | + | 3 | younger-nodewyn-tetherton | + | 4 | younger-nodimer-tetherton | + | 2 | youngest-mc-nodeface | + | 3 | youngest-nodewyn-tetherton | + | 4 | youngest-nodimer-tetherton | + | 1 | sir-nodeward-nodington-iii | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | - And I expect node aggregate identifier "nodewyn-tetherton" and node path "document/tethered-node" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | - | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | - And I expect node aggregate identifier "nodimer-tetherton" and node path "document/tethered-node/tethered-leaf" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} - And I expect node aggregate identifier "invariable-mc-nodeface" and node path "document/invariable-document" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} - And I expect node aggregate identifier "younger-mc-nodeface" and node path "younger-document" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | - | cs-identifier;nody-mc-nodeface;{"example":"source"} | | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have the following succeeding siblings: | NodeDiscriminator | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | - And I expect node aggregate identifier "youngest-mc-nodeface" and node path "youngest-document" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | | cs-identifier;younger-mc-nodeface;{"example":"general"} | - | cs-identifier;nody-mc-nodeface;{"example":"source"} | | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have no succeeding siblings diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/03-RemoveNodeAggregate_WithDimensions.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/03-RemoveNodeAggregate_WithDimensions.feature index a0408c1072d..c12999eaf7b 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/03-RemoveNodeAggregate_WithDimensions.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/03-RemoveNodeAggregate_WithDimensions.feature @@ -20,9 +20,9 @@ Feature: Remove NodeAggregate And I am in content repository "default" And I am user identified by "initiating-user-identifier" And the command CreateRootWorkspace is executed with payload: - | Key | Value | - | workspaceName | "live" | - | newContentStreamId | "cs-identifier" | + | Key | Value | + | workspaceName | "live" | + | newContentStreamId | "cs-identifier" | And I am in workspace "live" and dimension space point {"language":"en"} And the command CreateRootNodeAggregateWithNode is executed with payload: | Key | Value | @@ -34,8 +34,8 @@ Feature: Remove NodeAggregate | nodingers-cat | Neos.ContentRepository.Testing:Document | lady-eleonode-rootford | pet | | nodingers-kitten | Neos.ContentRepository.Testing:Document | nodingers-cat | kitten | And the command SetNodeReferences is executed with payload: - | Key | Value | - | sourceNodeAggregateId | "nodingers-cat" | + | Key | Value | + | sourceNodeAggregateId | "nodingers-cat" | | references | [{"referenceName": "references", "references": [{"target": "sir-david-nodenborough"}]}] | Scenario: Remove a node aggregate with strategy allSpecializations @@ -46,10 +46,10 @@ Feature: Remove NodeAggregate | nodeVariantSelectionStrategy | "allSpecializations" | Then I expect exactly 7 events to be published on stream with prefix "ContentStream:cs-identifier" And event at index 6 is of type "NodeAggregateWasRemoved" with payload: - | Key | Expected | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "nodingers-cat" | - | affectedCoveredDimensionSpacePoints | [{"language":"de"},{"language":"gsw"}] | + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodingers-cat" | + | affectedCoveredDimensionSpacePoints | [{"language":"de"},{"language":"gsw"}] | Then I expect the graph projection to consist of exactly 4 nodes And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph And I expect a node identified by cs-identifier;sir-david-nodenborough;{"language":"en"} to exist in the content graph @@ -191,10 +191,10 @@ Feature: Remove NodeAggregate | nodeVariantSelectionStrategy | "allVariants" | Then I expect exactly 8 events to be published on stream with prefix "ContentStream:cs-identifier" And event at index 7 is of type "NodeAggregateWasRemoved" with payload: - | Key | Expected | - | contentStreamId | "cs-identifier" | - | nodeAggregateId | "nodingers-cat" | - | affectedCoveredDimensionSpacePoints | [{"language":"de"},{"language":"en"},{"language":"gsw"},{"language":"fr"}] | + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodingers-cat" | + | affectedCoveredDimensionSpacePoints | [{"language":"de"},{"language":"en"},{"language":"gsw"},{"language":"fr"}] | Then I expect the graph projection to consist of exactly 2 nodes And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph And I expect a node identified by cs-identifier;sir-david-nodenborough;{"language":"en"} to exist in the content graph @@ -349,7 +349,18 @@ Feature: Remove NodeAggregate | sourceOrigin | {"language":"en"} | | targetOrigin | {"language":"de"} | + Then I expect exactly 9 events to be published on stream "ContentStream:cs-identifier" + And event at index 8 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodingers-cat" | + | sourceOrigin | {"language":"en"} | + | specializationOrigin | {"language":"de"} | + | specializationSiblings | [{"dimensionSpacePoint":{"language":"de"},"nodeAggregateId":null},{"dimensionSpacePoint":{"language":"gsw"},"nodeAggregateId":null}] | + | parentNodeAggregateId | null | + Then I expect the node aggregate "nodingers-cat" to exist + And I expect this node aggregate to occupy dimension space points [{"language":"en"},{"language":"de"}] And I expect this node aggregate to disable dimension space points [{"language":"en"},{"language":"fr"}] And I expect the graph projection to consist of exactly 5 nodes And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/06-CreateNodeSpecializationVariantAfterDeletion.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/06-CreateNodeSpecializationVariantAfterDeletion.feature index cee896968c3..1a966e9442a 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/06-CreateNodeSpecializationVariantAfterDeletion.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/06-CreateNodeSpecializationVariantAfterDeletion.feature @@ -68,7 +68,33 @@ Feature: Create node specialization | sourceOrigin | {"example":"source"} | | targetOrigin | {"example":"spec"} | - Then I expect the graph projection to consist of exactly 13 nodes + Then I expect exactly 21 events to be published on stream "ContentStream:cs-identifier" + And event at index 18 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nody-mc-nodeface" | + | sourceOrigin | {"example":"source"} | + | specializationOrigin | {"example":"spec"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"younger-mc-nodeface"},{"dimensionSpacePoint":{"example":"leafSpec"},"nodeAggregateId":"younger-mc-nodeface"}] | + | parentNodeAggregateId | null | + And event at index 19 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodewyn-tetherton" | + | sourceOrigin | {"example":"source"} | + | specializationOrigin | {"example":"spec"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"leafSpec"},"nodeAggregateId":null}] | + | parentNodeAggregateId | null | + And event at index 20 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodimer-tetherton" | + | sourceOrigin | {"example":"source"} | + | specializationOrigin | {"example":"spec"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"leafSpec"},"nodeAggregateId":null}] | + | parentNodeAggregateId | null | + + And I expect the graph projection to consist of exactly 13 nodes And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph And I expect a node identified by cs-identifier;sir-david-nodenborough;{"example":"source"} to exist in the content graph And I expect a node identified by cs-identifier;eldest-mc-nodeface;{"example":"source"} to exist in the content graph @@ -128,16 +154,16 @@ Feature: Create node specialization Then I expect the subgraph projection to consist of exactly 9 nodes Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And I expect this node to have the following child nodes: - | Name | NodeDiscriminator | - | document | cs-identifier;sir-david-nodenborough;{"example":"source"} | + | Name | NodeDiscriminator | + | document | cs-identifier;sir-david-nodenborough;{"example":"source"} | And I expect node aggregate identifier "sir-david-nodenborough" and node path "document" to lead to node cs-identifier;sir-david-nodenborough;{"example":"source"} And I expect this node to have the following child nodes: - | Name | NodeDiscriminator | - | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"source"} | - | elder-document | cs-identifier;elder-mc-nodeface;{"example":"source"} | - | child-document | cs-identifier;nody-mc-nodeface;{"example":"spec"} | - | younger-document | cs-identifier;younger-mc-nodeface;{"example":"source"} | - | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"source"} | + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"source"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"source"} | + | child-document | cs-identifier;nody-mc-nodeface;{"example":"spec"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"source"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"source"} | And I expect node aggregate identifier "eldest-mc-nodeface" and node path "document/eldest-document" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"source"} And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: @@ -197,8 +223,8 @@ Feature: Create node specialization Then I expect the subgraph projection to consist of exactly 9 nodes Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And I expect this node to have the following child nodes: - | Name | NodeDiscriminator | - | document | cs-identifier;sir-david-nodenborough;{"example":"source"} | + | Name | NodeDiscriminator | + | document | cs-identifier;sir-david-nodenborough;{"example":"source"} | Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"source"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/InterdimensionalSiblingsProvider.php b/Neos.ContentRepository.Core/Classes/Feature/Common/InterdimensionalSiblingsProvider.php new file mode 100644 index 00000000000..6762c677c03 --- /dev/null +++ b/Neos.ContentRepository.Core/Classes/Feature/Common/InterdimensionalSiblingsProvider.php @@ -0,0 +1,408 @@ +getSubgraph( + $selectedDimensionSpacePoint, + VisibilityConstraints::createEmpty() + ); + $alternativeSucceedingSiblingIds = $succeedingSiblingNodeAggregateId + ? $selectedSubgraph->findSucceedingSiblingNodes( + $succeedingSiblingNodeAggregateId, + FindSucceedingSiblingNodesFilter::create() + )->toNodeAggregateIds() + : null; + + $alternativePrecedingSiblingIds = $precedingSiblingNodeAggregateId + ? $selectedSubgraph->findPrecedingSiblingNodes( + $precedingSiblingNodeAggregateId, + FindPrecedingSiblingNodesFilter::create() + )->toNodeAggregateIds() + : null; + + $interdimensionalSiblings = []; + foreach ($affectedDimensionSpacePoints as $dimensionSpacePoint) { + $variantSubgraph = $contentGraph->getSubgraph( + $dimensionSpacePoint, + VisibilityConstraints::createEmpty() + ); + if ($succeedingSiblingNodeAggregateId) { + $variantSucceedingSibling = $variantSubgraph->findNodeById($succeedingSiblingNodeAggregateId); + $variantParentId = $parentNodeAggregateId ?: $variantSubgraph->findParentNode($nodeAggregateId)?->aggregateId; + $siblingParent = $variantSubgraph->findParentNode($succeedingSiblingNodeAggregateId); + \Neos\Flow\var_dump($variantSucceedingSibling?->aggregateId, 'succeeding variant'); + \Neos\Flow\var_dump($siblingParent?->aggregateId, 'sibling parent'); + \Neos\Flow\var_dump($variantParentId, 'variant parent'); + if ($variantSucceedingSibling && $siblingParent && (!$variantParentId || $variantParentId->equals($siblingParent->aggregateId))) { + // a) happy path, the explicitly requested succeeding sibling also exists in this dimension space point + $interdimensionalSiblings[] = new InterdimensionalSibling( + $dimensionSpacePoint, + $variantSucceedingSibling->aggregateId, + ); + continue; + } + + // check the other siblings succeeding in the selected dimension space point + foreach ($alternativeSucceedingSiblingIds ?: [] as $alternativeSucceedingSiblingId) { + // the node itself is no valid succeeding sibling + if ($alternativeSucceedingSiblingId->equals($nodeAggregateId)) { + continue; + } + $alternativeVariantSucceedingSibling = $variantSubgraph->findNodeById($alternativeSucceedingSiblingId); + if (!$alternativeVariantSucceedingSibling) { + continue; + } + $siblingParent = $variantSubgraph->findParentNode($alternativeSucceedingSiblingId); + if (!$siblingParent || !$variantParentId?->equals($siblingParent->aggregateId)) { + continue; + } + // b) one of the further succeeding sibling exists in this dimension space point + $interdimensionalSiblings[] = new InterdimensionalSibling( + $dimensionSpacePoint, + $alternativeVariantSucceedingSibling->aggregateId, + ); + continue 2; + } + } + + if ($precedingSiblingNodeAggregateId) { + $variantPrecedingSiblingId = null; + $variantPrecedingSibling = $variantSubgraph->findNodeById($precedingSiblingNodeAggregateId); + $variantParentId = $parentNodeAggregateId ?: $variantSubgraph->findParentNode($nodeAggregateId)?->aggregateId; + $siblingParent = $variantSubgraph->findParentNode($precedingSiblingNodeAggregateId); + if ($variantPrecedingSibling && $siblingParent && $variantParentId?->equals($siblingParent->aggregateId)) { + // c) happy path, the explicitly requested preceding sibling also exists in this dimension space point + $variantPrecedingSiblingId = $precedingSiblingNodeAggregateId; + } elseif ($alternativePrecedingSiblingIds) { + // check the other siblings preceding in the selected dimension space point + foreach ($alternativePrecedingSiblingIds as $alternativePrecedingSiblingId) { + // the node itself is no valid preceding sibling + if ($alternativePrecedingSiblingId->equals($nodeAggregateId)) { + continue; + } + $siblingParent = $variantSubgraph->findParentNode($alternativePrecedingSiblingId); + if (!$siblingParent || !$variantParentId?->equals($siblingParent->aggregateId)) { + continue; + } + $alternativeVariantSucceedingSibling = $variantSubgraph->findNodeById($alternativePrecedingSiblingId); + if ($alternativeVariantSucceedingSibling) { + // d) one of the further preceding siblings exists in this dimension space point + $variantPrecedingSiblingId = $alternativePrecedingSiblingId; + break; + } + } + } + + if ($variantPrecedingSiblingId) { + // we fetch two siblings because the first might be the to-be-moved node itself + $variantSucceedingSiblingIds = $variantSubgraph->findSucceedingSiblingNodes( + $variantPrecedingSiblingId, + FindSucceedingSiblingNodesFilter::create(pagination: Pagination::fromLimitAndOffset(2, 0)) + )->toNodeAggregateIds(); + $relevantVariantSucceedingSiblingId = null; + foreach ($variantSucceedingSiblingIds as $variantSucceedingSiblingId) { + if (!$variantSucceedingSiblingId->equals($nodeAggregateId)) { + $relevantVariantSucceedingSiblingId = $variantSucceedingSiblingId; + break; + } + } + $interdimensionalSiblings[] = new InterdimensionalSibling( + $dimensionSpacePoint, + $relevantVariantSucceedingSiblingId, + ); + continue; + } + } + + // e) fallback: if the set is to be completed, we add an empty sibling, otherwise we just don't + if ($completeSet) { + $interdimensionalSiblings[] = new InterdimensionalSibling( + $dimensionSpacePoint, + null, + ); + } + } + + return new InterdimensionalSiblings(...$interdimensionalSiblings); + } + + + /** + * Resolves the succeeding siblings for the node to be created in each dimension space points it will cover. + * + * For each covered dimension space point + * a) The requested succeeding sibling is selected it if also covers this dimension space point + * b) If the requested succeeding sibling does not exist, all the other succeeding siblings of the node in the origin + * will be checked and the first one covering this dimension space point is used + * c) As fallback no succeeding sibling is specified + * + * Developers hint: + * Similar to {@see self::resolveInterdimensionalSiblingsForVariation()} except this + * operates on the explicitly set succeeding sibling instead of the node itself. + */ + protected function resolveInterdimensionalSiblingsForCreation( + ContentGraphInterface $contentGraph, + NodeAggregateId $requestedSucceedingSiblingNodeAggregateId, + OriginDimensionSpacePoint $sourceOrigin, + DimensionSpacePointSet $coveredDimensionSpacePoints, + ): InterdimensionalSiblings { + $subGraph = $contentGraph->getSubgraph($sourceOrigin->toDimensionSpacePoint(), VisibilityConstraints::createEmpty()); + $originAlternativeSucceedingSiblings = $subGraph->findSucceedingSiblingNodes( + $requestedSucceedingSiblingNodeAggregateId, + FindSucceedingSiblingNodesFilter::create() + ); + + $interdimensionalSiblings = []; + foreach ($coveredDimensionSpacePoints as $coveredDimensionSpacePoint) { + $subGraph = $contentGraph->getSubgraph($coveredDimensionSpacePoint, VisibilityConstraints::createEmpty()); + $variantSucceedingSibling = $subGraph->findNodeById($requestedSucceedingSiblingNodeAggregateId); + if ($variantSucceedingSibling) { + // a) happy path, the explicitly requested succeeding sibling also exists in this dimension space point + $interdimensionalSiblings[] = new InterdimensionalSibling( + $coveredDimensionSpacePoint, + $variantSucceedingSibling->aggregateId, + ); + continue; + } + + // check the other siblings succeeding in the origin dimension space point + foreach ($originAlternativeSucceedingSiblings as $originSibling) { + $alternativeVariantSucceedingSibling = $subGraph->findNodeById($originSibling->aggregateId); + if (!$alternativeVariantSucceedingSibling) { + continue; + } + // b) one of the further succeeding sibling exists in this dimension space point + $interdimensionalSiblings[] = new InterdimensionalSibling( + $coveredDimensionSpacePoint, + $alternativeVariantSucceedingSibling->aggregateId, + ); + continue 2; + } + + // c) fallback; there is no succeeding sibling in this dimension space point + $interdimensionalSiblings[] = new InterdimensionalSibling( + $coveredDimensionSpacePoint, + null, + ); + } + + return new InterdimensionalSiblings(...$interdimensionalSiblings); + } + + /** + * Resolves the succeeding siblings for the node variant to be created and all dimension space points the variant will cover. + * + * For each dimension space point in the variant coverage + * a) All the succeeding siblings of the node aggregate in the source origin are checked + * and the first one existing in this dimension space point is used + * b) As fallback no succeeding sibling is specified + * + * Developers hint: + * Similar to {@see self::resolveInterdimensionalSiblingsForCreation()} + * except this operates on the to-be-varied node itself instead of an explicitly set succeeding sibling + */ + protected function resolveInterdimensionalSiblingsForVariation( + ContentGraphInterface $contentGraph, + NodeAggregateId $varyingNodeAggregateId, + OriginDimensionSpacePoint $sourceOrigin, + DimensionSpacePointSet $variantCoverage, + ): InterdimensionalSiblings { + $originSiblings = $contentGraph + ->getSubgraph($sourceOrigin->toDimensionSpacePoint(), VisibilityConstraints::createEmpty()) + ->findSucceedingSiblingNodes($varyingNodeAggregateId, FindSucceedingSiblingNodesFilter::create()); + + $interdimensionalSiblings = []; + foreach ($variantCoverage as $variantDimensionSpacePoint) { + // check the siblings succeeding in the origin dimension space point + foreach ($originSiblings as $originSibling) { + $variantSibling = $contentGraph->getSubgraph($variantDimensionSpacePoint, VisibilityConstraints::createEmpty())->findNodeById($originSibling->aggregateId); + if (!$variantSibling) { + continue; + } + // a) one of the further succeeding sibling exists in this dimension space point + $interdimensionalSiblings[] = new InterdimensionalSibling( + $variantDimensionSpacePoint, + $variantSibling->aggregateId, + ); + continue 2; + } + + // b) fallback; there is no succeeding sibling in this dimension space point + $interdimensionalSiblings[] = new InterdimensionalSibling( + $variantDimensionSpacePoint, + null, + ); + } + + return new InterdimensionalSiblings(...$interdimensionalSiblings); + } + + /** + * @param ?NodeAggregateId $parentNodeAggregateId the parent node aggregate ID to validate variant siblings against. + * If no new parent is given, the siblings are validated against the parent of the to-be-moved node in the respective dimension space point. + * @param bool $completeSet Whether unresolvable siblings should be added as null or not at all + * True when a new parent is set, which will result of the node being added at the end + * True when no preceding sibling is given and the succeeding sibling is explicitly set to null, which will result of the node being added at the end + * False when no new parent is set, which will result in the node not being moved + */ + protected function resolveInterdimensionalSiblingsForMove( + ContentGraphInterface $contentGraph, + DimensionSpacePoint $selectedDimensionSpacePoint, + DimensionSpacePointSet $affectedDimensionSpacePoints, + NodeAggregateId $nodeAggregateId, + ?NodeAggregateId $parentNodeAggregateId, + ?NodeAggregateId $succeedingSiblingId, + ?NodeAggregateId $precedingSiblingId, + bool $completeSet, + ): InterdimensionalSiblings { + $selectedSubgraph = $contentGraph->getSubgraph( + $selectedDimensionSpacePoint, + VisibilityConstraints::createEmpty() + ); + $alternativeSucceedingSiblingIds = $succeedingSiblingId + ? $selectedSubgraph->findSucceedingSiblingNodes( + $succeedingSiblingId, + FindSucceedingSiblingNodesFilter::create() + )->toNodeAggregateIds() + : null; + $alternativePrecedingSiblingIds = $precedingSiblingId + ? $selectedSubgraph->findPrecedingSiblingNodes( + $precedingSiblingId, + FindPrecedingSiblingNodesFilter::create() + )->toNodeAggregateIds() + : null; + + $interdimensionalSiblings = []; + foreach ($affectedDimensionSpacePoints as $dimensionSpacePoint) { + $variantSubgraph = $contentGraph->getSubgraph( + $dimensionSpacePoint, + VisibilityConstraints::createEmpty() + ); + if ($succeedingSiblingId) { + $variantSucceedingSibling = $variantSubgraph->findNodeById($succeedingSiblingId); + $variantParentId = $parentNodeAggregateId ?: $variantSubgraph->findParentNode($nodeAggregateId)?->aggregateId; + $siblingParent = $variantSubgraph->findParentNode($succeedingSiblingId); + if ($variantSucceedingSibling && $siblingParent && $variantParentId?->equals($siblingParent->aggregateId)) { + // a) happy path, the explicitly requested succeeding sibling also exists in this dimension space point + $interdimensionalSiblings[] = new InterdimensionalSibling( + $dimensionSpacePoint, + $variantSucceedingSibling->aggregateId, + ); + continue; + } + + // check the other siblings succeeding in the selected dimension space point + foreach ($alternativeSucceedingSiblingIds ?: [] as $alternativeSucceedingSiblingId) { + // the node itself is no valid succeeding sibling + if ($alternativeSucceedingSiblingId->equals($nodeAggregateId)) { + continue; + } + $alternativeVariantSucceedingSibling = $variantSubgraph->findNodeById($alternativeSucceedingSiblingId); + if (!$alternativeVariantSucceedingSibling) { + continue; + } + $siblingParent = $variantSubgraph->findParentNode($alternativeSucceedingSiblingId); + if (!$siblingParent || !$variantParentId?->equals($siblingParent->aggregateId)) { + continue; + } + // b) one of the further succeeding sibling exists in this dimension space point + $interdimensionalSiblings[] = new InterdimensionalSibling( + $dimensionSpacePoint, + $alternativeVariantSucceedingSibling->aggregateId, + ); + continue 2; + } + } + + if ($precedingSiblingId) { + $variantPrecedingSiblingId = null; + $variantPrecedingSibling = $variantSubgraph->findNodeById($precedingSiblingId); + $variantParentId = $parentNodeAggregateId ?: $variantSubgraph->findParentNode($nodeAggregateId)?->aggregateId; + $siblingParent = $variantSubgraph->findParentNode($precedingSiblingId); + if ($variantPrecedingSibling && $siblingParent && $variantParentId?->equals($siblingParent->aggregateId)) { + // c) happy path, the explicitly requested preceding sibling also exists in this dimension space point + $variantPrecedingSiblingId = $precedingSiblingId; + } elseif ($alternativePrecedingSiblingIds) { + // check the other siblings preceding in the selected dimension space point + foreach ($alternativePrecedingSiblingIds as $alternativePrecedingSiblingId) { + // the node itself is no valid preceding sibling + if ($alternativePrecedingSiblingId->equals($nodeAggregateId)) { + continue; + } + $siblingParent = $variantSubgraph->findParentNode($alternativePrecedingSiblingId); + if (!$siblingParent || !$variantParentId?->equals($siblingParent->aggregateId)) { + continue; + } + $alternativeVariantSucceedingSibling = $variantSubgraph->findNodeById($alternativePrecedingSiblingId); + if ($alternativeVariantSucceedingSibling) { + // d) one of the further preceding siblings exists in this dimension space point + $variantPrecedingSiblingId = $alternativePrecedingSiblingId; + break; + } + } + } + + if ($variantPrecedingSiblingId) { + // we fetch two siblings because the first might be the to-be-moved node itself + $variantSucceedingSiblingIds = $variantSubgraph->findSucceedingSiblingNodes( + $variantPrecedingSiblingId, + FindSucceedingSiblingNodesFilter::create(pagination: Pagination::fromLimitAndOffset(2, 0)) + )->toNodeAggregateIds(); + $relevantVariantSucceedingSiblingId = null; + foreach ($variantSucceedingSiblingIds as $variantSucceedingSiblingId) { + if (!$variantSucceedingSiblingId->equals($nodeAggregateId)) { + $relevantVariantSucceedingSiblingId = $variantSucceedingSiblingId; + break; + } + } + $interdimensionalSiblings[] = new InterdimensionalSibling( + $dimensionSpacePoint, + $relevantVariantSucceedingSiblingId, + ); + continue; + } + } + + // e) fallback: if the set is to be completed, we add an empty sibling, otherwise we just don't + if ($completeSet) { + $interdimensionalSiblings[] = new InterdimensionalSibling( + $dimensionSpacePoint, + null, + ); + } + } + + return new InterdimensionalSiblings(...$interdimensionalSiblings); + } +} diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/NodeCreationInternals.php b/Neos.ContentRepository.Core/Classes/Feature/Common/NodeCreationInternals.php deleted file mode 100644 index d99baaf6b29..00000000000 --- a/Neos.ContentRepository.Core/Classes/Feature/Common/NodeCreationInternals.php +++ /dev/null @@ -1,90 +0,0 @@ -getSubgraph($sourceOrigin->toDimensionSpacePoint(), VisibilityConstraints::createEmpty()); - $originAlternativeSucceedingSiblings = $subGraph->findSucceedingSiblingNodes( - $requestedSucceedingSiblingNodeAggregateId, - FindSucceedingSiblingNodesFilter::create() - ); - - $interdimensionalSiblings = []; - foreach ($coveredDimensionSpacePoints as $coveredDimensionSpacePoint) { - $subGraph = $contentGraph->getSubgraph($coveredDimensionSpacePoint, VisibilityConstraints::createEmpty()); - $variantSucceedingSibling = $subGraph->findNodeById($requestedSucceedingSiblingNodeAggregateId); - if ($variantSucceedingSibling) { - // a) happy path, the explicitly requested succeeding sibling also exists in this dimension space point - $interdimensionalSiblings[] = new InterdimensionalSibling( - $coveredDimensionSpacePoint, - $variantSucceedingSibling->aggregateId, - ); - continue; - } - - // check the other siblings succeeding in the origin dimension space point - foreach ($originAlternativeSucceedingSiblings as $originSibling) { - $alternativeVariantSucceedingSibling = $subGraph->findNodeById($originSibling->aggregateId); - if (!$alternativeVariantSucceedingSibling) { - continue; - } - // b) one of the further succeeding sibling exists in this dimension space point - $interdimensionalSiblings[] = new InterdimensionalSibling( - $coveredDimensionSpacePoint, - $alternativeVariantSucceedingSibling->aggregateId, - ); - continue 2; - } - - // c) fallback; there is no succeeding sibling in this dimension space point - $interdimensionalSiblings[] = new InterdimensionalSibling( - $coveredDimensionSpacePoint, - null, - ); - } - - return new InterdimensionalSiblings(...$interdimensionalSiblings); - } -} diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/NodeVariationInternals.php b/Neos.ContentRepository.Core/Classes/Feature/Common/NodeVariationInternals.php index 7d68cef7f23..3645fbfb039 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/Common/NodeVariationInternals.php +++ b/Neos.ContentRepository.Core/Classes/Feature/Common/NodeVariationInternals.php @@ -25,6 +25,7 @@ use Neos\ContentRepository\Core\Projection\ContentGraph\ContentGraphInterface; use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindPrecedingSiblingNodesFilter; use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindSucceedingSiblingNodesFilter; +use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\Pagination\Pagination; use Neos\ContentRepository\Core\Projection\ContentGraph\NodeAggregate; use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; @@ -34,6 +35,8 @@ */ trait NodeVariationInternals { + use InterdimensionalSiblingsProvider; + abstract protected function getInterDimensionalVariationGraph(): DimensionSpace\InterDimensionalVariationGraph; protected function createEventsForVariations( @@ -42,6 +45,8 @@ protected function createEventsForVariations( OriginDimensionSpacePoint $targetOrigin, NodeAggregate $nodeAggregate, ?NodeAggregateId $parentNodeAggregateId, + ?NodeAggregateId $precedingSiblingNodeAggregateId, + ?NodeAggregateId $succeedingSiblingNodeAggregateId, ): Events { return match ( $this->getInterDimensionalVariationGraph()->getVariantType( @@ -50,11 +55,13 @@ protected function createEventsForVariations( ) ) { DimensionSpace\VariantType::TYPE_SPECIALIZATION => $this->handleCreateNodeSpecializationVariant( - $contentGraph, - $sourceOrigin, - $targetOrigin, - $nodeAggregate, - $parentNodeAggregateId, + contentGraph: $contentGraph, + sourceOrigin: $sourceOrigin, + targetOrigin: $targetOrigin, + nodeAggregate: $nodeAggregate, + parentNodeAggregateId: $parentNodeAggregateId, + precedingSiblingNodeAggregateId: $precedingSiblingNodeAggregateId, + succeedingSiblingNodeAggregateId: $succeedingSiblingNodeAggregateId, ), DimensionSpace\VariantType::TYPE_GENERALIZATION => $this->handleCreateNodeGeneralizationVariant( $contentGraph, @@ -77,16 +84,20 @@ protected function handleCreateNodeSpecializationVariant( OriginDimensionSpacePoint $targetOrigin, NodeAggregate $nodeAggregate, ?NodeAggregateId $parentNodeAggregateId, + ?NodeAggregateId $precedingSiblingNodeAggregateId, + ?NodeAggregateId $succeedingSiblingNodeAggregateId, ): Events { $specializationVisibility = $this->calculateEffectiveVisibility($targetOrigin, $nodeAggregate); $events = $this->collectNodeSpecializationVariantsThatWillHaveBeenCreated( - $contentGraph, - $sourceOrigin, - $targetOrigin, - $nodeAggregate, - $specializationVisibility, - $parentNodeAggregateId, - [], + contentGraph: $contentGraph, + sourceOrigin: $sourceOrigin, + targetOrigin: $targetOrigin, + nodeAggregate: $nodeAggregate, + specializationVisibility: $specializationVisibility, + parentNodeAggregateId: $parentNodeAggregateId, + precedingSiblingNodeAggregateId: $precedingSiblingNodeAggregateId, + succeedingSiblingNodeAggregateId: $succeedingSiblingNodeAggregateId, + events: [], ); return Events::fromArray($events); @@ -103,21 +114,42 @@ protected function collectNodeSpecializationVariantsThatWillHaveBeenCreated( NodeAggregate $nodeAggregate, DimensionSpacePointSet $specializationVisibility, ?NodeAggregateId $parentNodeAggregateId, + ?NodeAggregateId $precedingSiblingNodeAggregateId, + ?NodeAggregateId $succeedingSiblingNodeAggregateId, array $events, ): array { + if (!$parentNodeAggregateId && !$succeedingSiblingNodeAggregateId && !$precedingSiblingNodeAggregateId) { + // preserve legacy behavior: this means the variant is to be created in place + $originSubgraph = $contentGraph->getSubgraph( + $sourceOrigin->toDimensionSpacePoint(), + VisibilityConstraints::createEmpty() + ); + $precedingSiblingNodeAggregateId = $originSubgraph->findPrecedingSiblingNodes( + siblingNodeAggregateId: $nodeAggregate->nodeAggregateId, + filter: FindPrecedingSiblingNodesFilter::create(pagination: Pagination::fromLimitAndOffset(1, 0)) + )->first()?->aggregateId; + $succeedingSiblingNodeAggregateId = $originSubgraph->findSucceedingSiblingNodes( + siblingNodeAggregateId: $nodeAggregate->nodeAggregateId, + filter: FindSucceedingSiblingNodesFilter::create(pagination: Pagination::fromLimitAndOffset(1, 0)) + )->first()?->aggregateId; + } $events[] = new NodeSpecializationVariantWasCreated( - $contentGraph->getWorkspaceName(), - $contentGraph->getContentStreamId(), - $nodeAggregate->nodeAggregateId, - $sourceOrigin, - $targetOrigin, - $this->resolveInterdimensionalSiblings( - $contentGraph, - $nodeAggregate->nodeAggregateId, - $sourceOrigin, - $specializationVisibility + workspaceName: $contentGraph->getWorkspaceName(), + contentStreamId: $contentGraph->getContentStreamId(), + nodeAggregateId: $nodeAggregate->nodeAggregateId, + sourceOrigin: $sourceOrigin, + specializationOrigin: $targetOrigin, + specializationSiblings: $this->resolveInterdimensionalSiblings( + contentGraph: $contentGraph, + selectedDimensionSpacePoint: $sourceOrigin->toDimensionSpacePoint(), + affectedDimensionSpacePoints: $specializationVisibility, + nodeAggregateId: $nodeAggregate->nodeAggregateId, + parentNodeAggregateId: $parentNodeAggregateId, + precedingSiblingNodeAggregateId: $precedingSiblingNodeAggregateId, + succeedingSiblingNodeAggregateId: $succeedingSiblingNodeAggregateId, + completeSet: true, ), - $parentNodeAggregateId, + parentNodeAggregateId: $parentNodeAggregateId, ); foreach ( @@ -125,14 +157,28 @@ protected function collectNodeSpecializationVariantsThatWillHaveBeenCreated( $nodeAggregate->nodeAggregateId ) as $tetheredChildNodeAggregate ) { + $originSubgraph = $contentGraph->getSubgraph( + $sourceOrigin->toDimensionSpacePoint(), + VisibilityConstraints::createEmpty() + ); + $originChildPrecedingSibling = $originSubgraph->findPrecedingSiblingNodes( + siblingNodeAggregateId: $tetheredChildNodeAggregate->nodeAggregateId, + filter: FindPrecedingSiblingNodesFilter::create(pagination: Pagination::fromLimitAndOffset(1, 0)) + )->first(); + $originChildSucceedingSibling = $originSubgraph->findSucceedingSiblingNodes( + siblingNodeAggregateId: $tetheredChildNodeAggregate->nodeAggregateId, + filter: FindSucceedingSiblingNodesFilter::create(pagination: Pagination::fromLimitAndOffset(1, 0)) + )->first(); $events = $this->collectNodeSpecializationVariantsThatWillHaveBeenCreated( - $contentGraph, - $sourceOrigin, - $targetOrigin, - $tetheredChildNodeAggregate, - $specializationVisibility, - $parentNodeAggregateId, - $events + contentGraph: $contentGraph, + sourceOrigin: $sourceOrigin, + targetOrigin: $targetOrigin, + nodeAggregate: $tetheredChildNodeAggregate, + specializationVisibility: $specializationVisibility, + parentNodeAggregateId: null, + precedingSiblingNodeAggregateId: $originChildPrecedingSibling?->aggregateId, + succeedingSiblingNodeAggregateId: $originChildSucceedingSibling?->aggregateId, + events: $events ); } @@ -176,7 +222,7 @@ protected function collectNodeGeneralizationVariantsThatWillHaveBeenCreated( $nodeAggregate->nodeAggregateId, $sourceOrigin, $targetOrigin, - $this->resolveInterdimensionalSiblings( + $this->resolveInterdimensionalSiblingsForVariation( $contentGraph, $nodeAggregate->nodeAggregateId, $sourceOrigin, @@ -239,7 +285,7 @@ protected function collectNodePeerVariantsThatWillHaveBeenCreated( $nodeAggregate->nodeAggregateId, $sourceOrigin, $targetOrigin, - $this->resolveInterdimensionalSiblings( + $this->resolveInterdimensionalSiblingsForVariation( $contentGraph, $nodeAggregate->nodeAggregateId, $sourceOrigin, @@ -265,120 +311,6 @@ protected function collectNodePeerVariantsThatWillHaveBeenCreated( return $events; } - /** - * Resolves the succeeding siblings for the node variant to be created and all dimension space points the variant will cover. - * - * For each dimension space point in the variant coverage - * a) All the succeeding siblings of the node aggregate in the source origin are checked - * and the first one existing in this dimension space point is used - * b) As fallback no succeeding sibling is specified - * - * Developers hint: - * Similar to {@see NodeCreationInternals::resolveInterdimensionalSiblingsForCreation()} - * except this operates on the to-be-varied node itself instead of an explicitly set succeeding sibling - */ - private function resolveInterdimensionalSiblings( - ContentGraphInterface $contentGraph, - NodeAggregateId $varyingNodeAggregateId, - OriginDimensionSpacePoint $sourceOrigin, - DimensionSpacePointSet $variantCoverage, - ): InterdimensionalSiblings { - $originSiblings = $contentGraph - ->getSubgraph($sourceOrigin->toDimensionSpacePoint(), VisibilityConstraints::createEmpty()) - ->findSucceedingSiblingNodes($varyingNodeAggregateId, FindSucceedingSiblingNodesFilter::create()); - - $interdimensionalSiblings = []; - foreach ($variantCoverage as $variantDimensionSpacePoint) { - // check the siblings succeeding in the origin dimension space point - foreach ($originSiblings as $originSibling) { - $variantSibling = $contentGraph->getSubgraph($variantDimensionSpacePoint, VisibilityConstraints::createEmpty())->findNodeById($originSibling->aggregateId); - if (!$variantSibling) { - continue; - } - // a) one of the further succeeding sibling exists in this dimension space point - $interdimensionalSiblings[] = new InterdimensionalSibling( - $variantDimensionSpacePoint, - $variantSibling->aggregateId, - ); - continue 2; - } - - // b) fallback; there is no succeeding sibling in this dimension space point - $interdimensionalSiblings[] = new InterdimensionalSibling( - $variantDimensionSpacePoint, - null, - ); - } - - return new InterdimensionalSiblings(...$interdimensionalSiblings); - } - - /** - * Resolves the succeeding siblings for the node variant to be created using a new parent - * and all dimension space points the variant will cover. - * - * For each dimension space point in the variant coverage - * a) All the succeeding siblings of the node aggregate in the source origin are checked - * and the first one existing in this dimension space point is used - * b) As fallback no succeeding sibling is specified - * - * Developers hint: - * Similar to {@see NodeCreationInternals::resolveInterdimensionalSiblingsForCreation()} - * except this operates on the to-be-varied node itself instead of an explicitly set succeeding sibling - */ - private function resolveInterdimensionalSiblingsForNewParent( - ContentGraphInterface $contentGraph, - NodeAggregateId $varyingNodeAggregateId, - OriginDimensionSpacePoint $sourceOrigin, - DimensionSpacePointSet $variantCoverage, - NodeAggregateId $parentNodeAggregateId, - ): InterdimensionalSiblings { - $selectedSubgraph = $contentGraph->getSubgraph( - $selectedDimensionSpacePoint, - VisibilityConstraints::createEmpty() - ); - $alternativeSucceedingSiblingIds = $succeedingSiblingId - ? $selectedSubgraph->findSucceedingSiblingNodes( - $succeedingSiblingId, - FindSucceedingSiblingNodesFilter::create() - )->toNodeAggregateIds() - : null; - $alternativePrecedingSiblingIds = $precedingSiblingId - ? $selectedSubgraph->findPrecedingSiblingNodes( - $precedingSiblingId, - FindPrecedingSiblingNodesFilter::create() - )->toNodeAggregateIds() - : null; - $originSiblings = $contentGraph - ->getSubgraph($sourceOrigin->toDimensionSpacePoint(), VisibilityConstraints::createEmpty()) - ->findSucceedingSiblingNodes($varyingNodeAggregateId, FindSucceedingSiblingNodesFilter::create()); - - $interdimensionalSiblings = []; - foreach ($variantCoverage as $variantDimensionSpacePoint) { - // check the siblings succeeding in the origin dimension space point - foreach ($originSiblings as $originSibling) { - $variantSibling = $contentGraph->getSubgraph($variantDimensionSpacePoint, VisibilityConstraints::createEmpty())->findNodeById($originSibling->aggregateId); - if (!$variantSibling) { - continue; - } - // a) one of the further succeeding sibling exists in this dimension space point - $interdimensionalSiblings[] = new InterdimensionalSibling( - $variantDimensionSpacePoint, - $variantSibling->aggregateId, - ); - continue 2; - } - - // b) fallback; there is no succeeding sibling in this dimension space point - $interdimensionalSiblings[] = new InterdimensionalSibling( - $variantDimensionSpacePoint, - null, - ); - } - - return new InterdimensionalSiblings(...$interdimensionalSiblings); - } - private function calculateEffectiveVisibility( OriginDimensionSpacePoint $targetOrigin, NodeAggregate $nodeAggregate diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/TetheredNodeInternals.php b/Neos.ContentRepository.Core/Classes/Feature/Common/TetheredNodeInternals.php index e2253243d61..42e747d5464 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/Common/TetheredNodeInternals.php +++ b/Neos.ContentRepository.Core/Classes/Feature/Common/TetheredNodeInternals.php @@ -57,6 +57,8 @@ abstract protected function createEventsForVariations( OriginDimensionSpacePoint $targetOrigin, NodeAggregate $nodeAggregate, ?NodeAggregateId $parentNodeAggregateId, + ?NodeAggregateId $precedingSiblingNodeAggregateId, + ?NodeAggregateId $succeedingSiblingNodeAggregateId, ): Events; /** @@ -181,6 +183,9 @@ protected function createEventsForMissingTetheredNode( targetOrigin: $originDimensionSpacePoint, nodeAggregate: $childNodeAggregate, parentNodeAggregateId: null, + // @todo this was and still is wrong + precedingSiblingNodeAggregateId: null, + succeedingSiblingNodeAggregateId: null, ); } @@ -228,6 +233,7 @@ protected function createEventsForMissingTetheredNodeAggregate( $creationOrigin, $originDimensionSpacePoint, $interdimensionalSiblings, + null, ), VariantType::TYPE_GENERALIZATION => new NodeGeneralizationVariantWasCreated( $contentGraph->getWorkspaceName(), diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeCreation/NodeCreation.php b/Neos.ContentRepository.Core/Classes/Feature/NodeCreation/NodeCreation.php index df4415e6dfb..c5d32658254 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeCreation/NodeCreation.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeCreation/NodeCreation.php @@ -21,8 +21,8 @@ use Neos\ContentRepository\Core\EventStore\Events; use Neos\ContentRepository\Core\EventStore\EventsToPublish; use Neos\ContentRepository\Core\Feature\Common\InterdimensionalSiblings; +use Neos\ContentRepository\Core\Feature\Common\InterdimensionalSiblingsProvider; use Neos\ContentRepository\Core\Feature\RebaseableCommand; -use Neos\ContentRepository\Core\Feature\Common\NodeCreationInternals; use Neos\ContentRepository\Core\Feature\Common\NodeReferencingInternals; use Neos\ContentRepository\Core\Feature\ContentStreamEventStreamName; use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNode; @@ -52,7 +52,7 @@ */ trait NodeCreation { - use NodeCreationInternals; + use InterdimensionalSiblingsProvider; use NodeReferencingInternals; abstract protected function getInterDimensionalVariationGraph(): DimensionSpace\InterDimensionalVariationGraph; diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeMove/NodeMove.php b/Neos.ContentRepository.Core/Classes/Feature/NodeMove/NodeMove.php index aa2e919de0e..74d09139b7d 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeMove/NodeMove.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeMove/NodeMove.php @@ -23,6 +23,7 @@ use Neos\ContentRepository\Core\EventStore\EventsToPublish; use Neos\ContentRepository\Core\Feature\Common\InterdimensionalSibling; use Neos\ContentRepository\Core\Feature\Common\InterdimensionalSiblings; +use Neos\ContentRepository\Core\Feature\Common\InterdimensionalSiblingsProvider; use Neos\ContentRepository\Core\Feature\RebaseableCommand; use Neos\ContentRepository\Core\Feature\ContentStreamEventStreamName; use Neos\ContentRepository\Core\Feature\NodeMove\Command\MoveNodeAggregate; @@ -48,6 +49,8 @@ */ trait NodeMove { + use InterdimensionalSiblingsProvider; + abstract protected function getInterDimensionalVariationGraph(): DimensionSpace\InterDimensionalVariationGraph; abstract protected function areAncestorNodeTypeConstraintChecksEnabled(): bool; @@ -182,20 +185,20 @@ private function handleMoveNodeAggregate( $events = Events::with( new NodeAggregateWasMoved( - $command->workspaceName, - $contentStreamId, - $command->nodeAggregateId, - $command->newParentNodeAggregateId, - $this->resolveInterdimensionalSiblingsForMove( - $contentGraph, - $command->dimensionSpacePoint, - $affectedDimensionSpacePoints, - $command->nodeAggregateId, - $command->newParentNodeAggregateId, - $command->newSucceedingSiblingNodeAggregateId, - $command->newPrecedingSiblingNodeAggregateId, - ($command->newParentNodeAggregateId !== null) - || (($command->newSucceedingSiblingNodeAggregateId === null) && ($command->newPrecedingSiblingNodeAggregateId === null)), + workspaceName: $command->workspaceName, + contentStreamId: $contentStreamId, + nodeAggregateId: $command->nodeAggregateId, + newParentNodeAggregateId: $command->newParentNodeAggregateId, + succeedingSiblingsForCoverage: $this->resolveInterdimensionalSiblings( + contentGraph: $contentGraph, + selectedDimensionSpacePoint: $command->dimensionSpacePoint, + affectedDimensionSpacePoints: $affectedDimensionSpacePoints, + nodeAggregateId: $command->nodeAggregateId, + parentNodeAggregateId: $command->newParentNodeAggregateId, + precedingSiblingNodeAggregateId: $command->newPrecedingSiblingNodeAggregateId, + succeedingSiblingNodeAggregateId: $command->newSucceedingSiblingNodeAggregateId, + completeSet: $command->newParentNodeAggregateId !== null + || ($command->newPrecedingSiblingNodeAggregateId === null && $command->newSucceedingSiblingNodeAggregateId === null) ) ) ); @@ -229,142 +232,4 @@ private function resolveAffectedDimensionSpacePointSet( default => $nodeAggregate->coveredDimensionSpacePoints, }; } - - /** - * @param ?NodeAggregateId $parentNodeAggregateId the parent node aggregate ID to validate variant siblings against. - * If no new parent is given, the siblings are validated against the parent of the to-be-moved node in the respective dimension space point. - * @param bool $completeSet Whether unresolvable siblings should be added as null or not at all - * True when a new parent is set, which will result of the node being added at the end - * True when no preceding sibling is given and the succeeding sibling is explicitly set to null, which will result of the node being added at the end - * False when no new parent is set, which will result in the node not being moved - */ - private function resolveInterdimensionalSiblingsForMove( - ContentGraphInterface $contentGraph, - DimensionSpacePoint $selectedDimensionSpacePoint, - DimensionSpacePointSet $affectedDimensionSpacePoints, - NodeAggregateId $nodeAggregateId, - ?NodeAggregateId $parentNodeAggregateId, - ?NodeAggregateId $succeedingSiblingId, - ?NodeAggregateId $precedingSiblingId, - bool $completeSet, - ): InterdimensionalSiblings { - $selectedSubgraph = $contentGraph->getSubgraph( - $selectedDimensionSpacePoint, - VisibilityConstraints::createEmpty() - ); - $alternativeSucceedingSiblingIds = $succeedingSiblingId - ? $selectedSubgraph->findSucceedingSiblingNodes( - $succeedingSiblingId, - FindSucceedingSiblingNodesFilter::create() - )->toNodeAggregateIds() - : null; - $alternativePrecedingSiblingIds = $precedingSiblingId - ? $selectedSubgraph->findPrecedingSiblingNodes( - $precedingSiblingId, - FindPrecedingSiblingNodesFilter::create() - )->toNodeAggregateIds() - : null; - - $interdimensionalSiblings = []; - foreach ($affectedDimensionSpacePoints as $dimensionSpacePoint) { - $variantSubgraph = $contentGraph->getSubgraph( - $dimensionSpacePoint, - VisibilityConstraints::createEmpty() - ); - if ($succeedingSiblingId) { - $variantSucceedingSibling = $variantSubgraph->findNodeById($succeedingSiblingId); - $variantParentId = $parentNodeAggregateId ?: $variantSubgraph->findParentNode($nodeAggregateId)?->aggregateId; - $siblingParent = $variantSubgraph->findParentNode($succeedingSiblingId); - if ($variantSucceedingSibling && $siblingParent && $variantParentId?->equals($siblingParent->aggregateId)) { - // a) happy path, the explicitly requested succeeding sibling also exists in this dimension space point - $interdimensionalSiblings[] = new InterdimensionalSibling( - $dimensionSpacePoint, - $variantSucceedingSibling->aggregateId, - ); - continue; - } - - // check the other siblings succeeding in the selected dimension space point - foreach ($alternativeSucceedingSiblingIds ?: [] as $alternativeSucceedingSiblingId) { - // the node itself is no valid succeeding sibling - if ($alternativeSucceedingSiblingId->equals($nodeAggregateId)) { - continue; - } - $alternativeVariantSucceedingSibling = $variantSubgraph->findNodeById($alternativeSucceedingSiblingId); - if (!$alternativeVariantSucceedingSibling) { - continue; - } - $siblingParent = $variantSubgraph->findParentNode($alternativeSucceedingSiblingId); - if (!$siblingParent || !$variantParentId?->equals($siblingParent->aggregateId)) { - continue; - } - // b) one of the further succeeding sibling exists in this dimension space point - $interdimensionalSiblings[] = new InterdimensionalSibling( - $dimensionSpacePoint, - $alternativeVariantSucceedingSibling->aggregateId, - ); - continue 2; - } - } - - if ($precedingSiblingId) { - $variantPrecedingSiblingId = null; - $variantPrecedingSibling = $variantSubgraph->findNodeById($precedingSiblingId); - $variantParentId = $parentNodeAggregateId ?: $variantSubgraph->findParentNode($nodeAggregateId)?->aggregateId; - $siblingParent = $variantSubgraph->findParentNode($precedingSiblingId); - if ($variantPrecedingSibling && $siblingParent && $variantParentId?->equals($siblingParent->aggregateId)) { - // c) happy path, the explicitly requested preceding sibling also exists in this dimension space point - $variantPrecedingSiblingId = $precedingSiblingId; - } elseif ($alternativePrecedingSiblingIds) { - // check the other siblings preceding in the selected dimension space point - foreach ($alternativePrecedingSiblingIds as $alternativePrecedingSiblingId) { - // the node itself is no valid preceding sibling - if ($alternativePrecedingSiblingId->equals($nodeAggregateId)) { - continue; - } - $siblingParent = $variantSubgraph->findParentNode($alternativePrecedingSiblingId); - if (!$siblingParent || !$variantParentId?->equals($siblingParent->aggregateId)) { - continue; - } - $alternativeVariantSucceedingSibling = $variantSubgraph->findNodeById($alternativePrecedingSiblingId); - if ($alternativeVariantSucceedingSibling) { - // d) one of the further preceding siblings exists in this dimension space point - $variantPrecedingSiblingId = $alternativePrecedingSiblingId; - break; - } - } - } - - if ($variantPrecedingSiblingId) { - // we fetch two siblings because the first might be the to-be-moved node itself - $variantSucceedingSiblingIds = $variantSubgraph->findSucceedingSiblingNodes( - $variantPrecedingSiblingId, - FindSucceedingSiblingNodesFilter::create(pagination: Pagination::fromLimitAndOffset(2, 0)) - )->toNodeAggregateIds(); - $relevantVariantSucceedingSiblingId = null; - foreach ($variantSucceedingSiblingIds as $variantSucceedingSiblingId) { - if (!$variantSucceedingSiblingId->equals($nodeAggregateId)) { - $relevantVariantSucceedingSiblingId = $variantSucceedingSiblingId; - break; - } - } - $interdimensionalSiblings[] = new InterdimensionalSibling( - $dimensionSpacePoint, - $relevantVariantSucceedingSiblingId, - ); - continue; - } - } - - // e) fallback: if the set is to be completed, we add an empty sibling, otherwise we just don't - if ($completeSet) { - $interdimensionalSiblings[] = new InterdimensionalSibling( - $dimensionSpacePoint, - null, - ); - } - } - - return new InterdimensionalSiblings(...$interdimensionalSiblings); - } } diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Command/CreateNodeVariant.php b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Command/CreateNodeVariant.php index 5bdd970d9b0..e75b1dea624 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Command/CreateNodeVariant.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Command/CreateNodeVariant.php @@ -68,18 +68,18 @@ public static function create(WorkspaceName $workspaceName, NodeAggregateId $nod public static function fromArray(array $array): self { return new self( - WorkspaceName::fromString($array['workspaceName']), - NodeAggregateId::fromString($array['nodeAggregateId']), - OriginDimensionSpacePoint::fromArray($array['sourceOrigin']), - OriginDimensionSpacePoint::fromArray($array['targetOrigin']), - array_key_exists('parentNodeAggregateId', $array) - ? NodeAggregateId::fromString($array['parentNodeAggregateId']) + workspaceName: WorkspaceName::fromString($array['workspaceName']), + nodeAggregateId: NodeAggregateId::fromString($array['nodeAggregateId']), + sourceOrigin: OriginDimensionSpacePoint::fromArray($array['sourceOrigin']), + targetOrigin: OriginDimensionSpacePoint::fromArray($array['targetOrigin']), + parentNodeAggregateId: (is_string($parentNodeAggregateId = $array['parentNodeAggregateId'] ?? null)) + ? NodeAggregateId::fromString($parentNodeAggregateId) : null, - array_key_exists('precedingSiblingNodeAggregateId', $array) - ? NodeAggregateId::fromString($array['precedingSiblingNodeAggregateId']) + precedingSiblingNodeAggregateId: (is_string($precedingSiblingNodeAggregateId = $array['precedingSiblingNodeAggregateId'] ?? null)) + ? NodeAggregateId::fromString($precedingSiblingNodeAggregateId) : null, - array_key_exists('succeedingSiblingNodeAggregateId', $array) - ? NodeAggregateId::fromString($array['succeedingSiblingNodeAggregateId']) + succeedingSiblingNodeAggregateId: (is_string($succeedingSiblingNodeAggregateId = $array['succeedingSiblingNodeAggregateId'] ?? null)) + ? NodeAggregateId::fromString($succeedingSiblingNodeAggregateId) : null, ); } diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Event/NodeSpecializationVariantWasCreated.php b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Event/NodeSpecializationVariantWasCreated.php index 4f428b8cb4f..1228db2c5d7 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Event/NodeSpecializationVariantWasCreated.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Event/NodeSpecializationVariantWasCreated.php @@ -90,8 +90,8 @@ public static function fromArray(array $values): self : InterdimensionalSiblings::fromDimensionSpacePointSetWithoutSucceedingSiblings( DimensionSpacePointSet::fromArray($values['specializationCoverage']) ), - parentNodeAggregateId: array_key_exists('parentNodeAggregateId', $values) - ? NodeAggregateId::fromString($values['parentNodeAggregateId']) + parentNodeAggregateId: is_string($parentNodeAggregateId = ($values['parentNodeAggregateId'] ?? null)) + ? NodeAggregateId::fromString($parentNodeAggregateId) : null, ); } diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/NodeVariation.php b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/NodeVariation.php index 2db5ce1a2b5..299fdc6fc98 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/NodeVariation.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/NodeVariation.php @@ -120,11 +120,13 @@ private function handleCreateNodeVariant( ); $events = $this->createEventsForVariations( - $contentGraph, - $command->sourceOrigin, - $command->targetOrigin, - $nodeAggregate, - $command->parentNodeAggregateId, + contentGraph: $contentGraph, + sourceOrigin: $command->sourceOrigin, + targetOrigin: $command->targetOrigin, + nodeAggregate: $nodeAggregate, + parentNodeAggregateId: $command->parentNodeAggregateId, + precedingSiblingNodeAggregateId: $command->precedingSiblingNodeAggregateId, + succeedingSiblingNodeAggregateId: $command->succeedingSiblingNodeAggregateId, ); return new EventsToPublish( From 663b6067114dacfd0bc545fd83a857f7cd4277bf Mon Sep 17 00:00:00 2001 From: Bernhard Schmitt Date: Sun, 30 Nov 2025 14:03:11 +0100 Subject: [PATCH 6/9] 5054 - Extend test suite for node specialization variation with new parent --- ...odeSpecializationVariant-NewParent.feature | 833 +++++++++- ...tionVariant-NewParent-AfterRemoval.feature | 1462 +++++++++++++++++ .../InterdimensionalSiblingsProvider.php | 10 +- .../Feature/Common/NodeVariationInternals.php | 2 +- .../Classes/Feature/NodeMove/NodeMove.php | 2 +- 5 files changed, 2226 insertions(+), 83 deletions(-) create mode 100644 Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/07-CreateNodeSpecializationVariant-NewParent-AfterRemoval.feature diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/05-CreateNodeSpecializationVariant-NewParent.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/05-CreateNodeSpecializationVariant-NewParent.feature index 3d523c06c81..46e3b4f4971 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/05-CreateNodeSpecializationVariant-NewParent.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/05-CreateNodeSpecializationVariant-NewParent.feature @@ -5,14 +5,14 @@ Feature: Create node specialization and assign that variant to a different parent * before the first of its new siblings * before the first of its new siblings - which does not exist in all variants - * before one of its new siblings, wich is partially the first + * before one of its new siblings, which is partially the first * before one of its new siblings, which is not the first * before one of its new siblings, which is not the first and does not exist in all variants * before one of its new siblings, which is the last and does not exist in all variants + * after one of its new siblings, which is not the last + * after one of its new siblings, which is partially the last * after the last of its new siblings * after the last of its new siblings, which does not exist in all variants - * after one of its new siblings, which is partially the last - * after one of its new siblings, which is not the last Background: Given using the following content dimensions: @@ -43,17 +43,16 @@ Feature: Create node specialization | Key | Value | | nodeAggregateId | "lady-eleonode-rootford" | | nodeTypeName | "Neos.ContentRepository:Root" | - And the following CreateNodeAggregateWithNode commands are executed: - | nodeAggregateId | nodeName | nodeTypeName | parentNodeAggregateId | tetheredDescendantNodeAggregateIds | - | sir-david-nodenborough | parent-document | Neos.ContentRepository.Testing:LeafDocument | lady-eleonode-rootford | {} | - | eldest-mc-nodeface | eldest-document | Neos.ContentRepository.Testing:Document | sir-david-nodenborough | {"tethered-node": "eldest-nodewyn-tetherton", "tethered-node/tethered-leaf": "eldest-nodimer-tetherton"} | - | elder-mc-nodeface | elder-document | Neos.ContentRepository.Testing:Document | sir-david-nodenborough | {"tethered-node": "elder-nodewyn-tetherton", "tethered-node/tethered-leaf": "elder-nodimer-tetherton"} | - | younger-mc-nodeface | younger-document | Neos.ContentRepository.Testing:Document | sir-david-nodenborough | {"tethered-node": "younger-nodewyn-tetherton", "tethered-node/tethered-leaf": "younger-nodimer-tetherton"} | - | youngest-mc-nodeface | youngest-document | Neos.ContentRepository.Testing:Document | sir-david-nodenborough | {"tethered-node": "youngest-nodewyn-tetherton", "tethered-node/tethered-leaf": "youngest-nodimer-tetherton"} | - | sir-nodeward-nodington-iii | esquire | Neos.ContentRepository.Testing:LeafDocument | lady-eleonode-rootford | {} | - | nody-mc-nodeface | document | Neos.ContentRepository.Testing:Document | sir-nodeward-nodington-iii | {"tethered-node": "nodewyn-tetherton", "tethered-node/tethered-leaf": "nodimer-tetherton"} | - | invariable-mc-nodeface | invariable | Neos.ContentRepository.Testing:LeafDocument | nody-mc-nodeface | {} | + | nodeAggregateId | nodeName | nodeTypeName | parentNodeAggregateId | tetheredDescendantNodeAggregateIds | + | sir-david-nodenborough | parent-document | Neos.ContentRepository.Testing:LeafDocument | lady-eleonode-rootford | {} | + | eldest-mc-nodeface | eldest-document | Neos.ContentRepository.Testing:LeafDocument | sir-david-nodenborough | {} | + | elder-mc-nodeface | elder-document | Neos.ContentRepository.Testing:LeafDocument | sir-david-nodenborough | {} | + | younger-mc-nodeface | younger-document | Neos.ContentRepository.Testing:LeafDocument | sir-david-nodenborough | {} | + | youngest-mc-nodeface | youngest-document | Neos.ContentRepository.Testing:LeafDocument | sir-david-nodenborough | {} | + | sir-nodeward-nodington-iii | esquire | Neos.ContentRepository.Testing:LeafDocument | lady-eleonode-rootford | {} | + | nody-mc-nodeface | document | Neos.ContentRepository.Testing:Document | sir-nodeward-nodington-iii | {"tethered-node": "nodewyn-tetherton", "tethered-node/tethered-leaf": "nodimer-tetherton"} | + | invariable-mc-nodeface | invariable | Neos.ContentRepository.Testing:LeafDocument | nody-mc-nodeface | {} | Scenario: Create specialization variant to a new parent before the first of its new siblings When the command CreateNodeVariant is executed with payload: @@ -63,8 +62,8 @@ Feature: Create node specialization | targetOrigin | {"example":"source"} | | parentNodeAggregateId | "sir-david-nodenborough" | | succeedingSiblingNodeAggregateId | "eldest-mc-nodeface" | - Then I expect exactly 23 events to be published on stream "ContentStream:cs-identifier" - And event at index 20 is of type "NodeSpecializationVariantWasCreated" with payload: + Then I expect exactly 15 events to be published on stream "ContentStream:cs-identifier" + And event at index 12 is of type "NodeSpecializationVariantWasCreated" with payload: | Key | Expected | | contentStreamId | "cs-identifier" | | nodeAggregateId | "nody-mc-nodeface" | @@ -72,7 +71,7 @@ Feature: Create node specialization | specializationOrigin | {"example":"source"} | | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"eldest-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"eldest-mc-nodeface"}] | | parentNodeAggregateId | "sir-david-nodenborough" | - And event at index 21 is of type "NodeSpecializationVariantWasCreated" with payload: + And event at index 13 is of type "NodeSpecializationVariantWasCreated" with payload: | Key | Expected | | contentStreamId | "cs-identifier" | | nodeAggregateId | "nodewyn-tetherton" | @@ -80,7 +79,7 @@ Feature: Create node specialization | specializationOrigin | {"example":"source"} | | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"invariable-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"invariable-mc-nodeface"}] | | parentNodeAggregateId | null | - And event at index 22 is of type "NodeSpecializationVariantWasCreated" with payload: + And event at index 14 is of type "NodeSpecializationVariantWasCreated" with payload: | Key | Expected | | contentStreamId | "cs-identifier" | | nodeAggregateId | "nodimer-tetherton" | @@ -89,7 +88,7 @@ Feature: Create node specialization | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | | parentNodeAggregateId | null | - Then I expect the graph projection to consist of exactly 22 nodes + Then I expect the graph projection to consist of exactly 14 nodes And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"general"} to exist in the content graph And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"source"} to exist in the content graph @@ -104,17 +103,6 @@ Feature: Create node specialization And I expect a node identified by cs-identifier;invariable-mc-nodeface;{"example":"general"} to exist in the content graph When I am in workspace "live" - Then I expect the node aggregate "lady-eleonode-rootford" to exist - And I expect this node aggregate to occupy dimension space points [{}] - And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] - - And I expect the node aggregate "eldest-mc-nodeface" to exist - And I expect this node aggregate to occupy dimension space points [{"example":"general"}] - And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] - - And I expect the node aggregate "elder-mc-nodeface" to exist - And I expect this node aggregate to occupy dimension space points [{"example":"general"}] - And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] And I expect the node aggregate "nody-mc-nodeface" to exist And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] @@ -128,20 +116,12 @@ Feature: Create node specialization And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] - And I expect the node aggregate "younger-mc-nodeface" to exist - And I expect this node aggregate to occupy dimension space points [{"example":"general"}] - And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] - - And I expect the node aggregate "youngest-mc-nodeface" to exist - And I expect this node aggregate to occupy dimension space points [{"example":"general"}] - And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] - And I expect the node aggregate "invariable-mc-nodeface" to exist And I expect this node aggregate to occupy dimension space points [{"example":"general"}] And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] When I am in workspace "live" and dimension space point {"example":"source"} - Then I expect the subgraph projection to consist of exactly 19 nodes + Then I expect the subgraph projection to consist of exactly 11 nodes Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | @@ -152,17 +132,9 @@ Feature: Create node specialization | 4 | nodimer-tetherton | | 3 | invariable-mc-nodeface | | 2 | eldest-mc-nodeface | - | 3 | eldest-nodewyn-tetherton | - | 4 | eldest-nodimer-tetherton | | 2 | elder-mc-nodeface | - | 3 | elder-nodewyn-tetherton | - | 4 | elder-nodimer-tetherton | | 2 | younger-mc-nodeface | - | 3 | younger-nodewyn-tetherton | - | 4 | younger-nodimer-tetherton | | 2 | youngest-mc-nodeface | - | 3 | youngest-nodewyn-tetherton | - | 4 | youngest-nodimer-tetherton | | 1 | sir-nodeward-nodington-iii | Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: @@ -227,24 +199,16 @@ Feature: Create node specialization And I expect this node to have no succeeding siblings When I am in workspace "live" and dimension space point {"example":"general"} - Then I expect the subgraph projection to consist of exactly 19 nodes + Then I expect the subgraph projection to consist of exactly 11 nodes Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | | 1 | sir-david-nodenborough | | 2 | eldest-mc-nodeface | - | 3 | eldest-nodewyn-tetherton | - | 4 | eldest-nodimer-tetherton | | 2 | elder-mc-nodeface | - | 3 | elder-nodewyn-tetherton | - | 4 | elder-nodimer-tetherton | | 2 | younger-mc-nodeface | - | 3 | younger-nodewyn-tetherton | - | 4 | younger-nodimer-tetherton | | 2 | youngest-mc-nodeface | - | 3 | youngest-nodewyn-tetherton | - | 4 | youngest-nodimer-tetherton | | 1 | sir-nodeward-nodington-iii | | 2 | nody-mc-nodeface | | 3 | nodewyn-tetherton | @@ -275,13 +239,13 @@ Feature: Create node specialization And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | - | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | And I expect this node to have no preceding siblings And I expect this node to have no succeeding siblings And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} And I expect this node to have the following child nodes: - | Name | NodeDiscriminator | + | Name | NodeDiscriminator | | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} @@ -302,7 +266,7 @@ Feature: Create node specialization And I expect this node to have no succeeding siblings When I am in workspace "live" and dimension space point {"example":"spec"} - Then I expect the subgraph projection to consist of exactly 19 nodes + Then I expect the subgraph projection to consist of exactly 11 nodes Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | @@ -313,17 +277,9 @@ Feature: Create node specialization | 4 | nodimer-tetherton | | 3 | invariable-mc-nodeface | | 2 | eldest-mc-nodeface | - | 3 | eldest-nodewyn-tetherton | - | 4 | eldest-nodimer-tetherton | | 2 | elder-mc-nodeface | - | 3 | elder-nodewyn-tetherton | - | 4 | elder-nodimer-tetherton | | 2 | younger-mc-nodeface | - | 3 | younger-nodewyn-tetherton | - | 4 | younger-nodimer-tetherton | | 2 | youngest-mc-nodeface | - | 3 | youngest-nodewyn-tetherton | - | 4 | youngest-nodimer-tetherton | | 1 | sir-nodeward-nodington-iii | Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: @@ -388,24 +344,16 @@ Feature: Create node specialization And I expect this node to have no succeeding siblings When I am in workspace "live" and dimension space point {"example":"peer"} - Then I expect the subgraph projection to consist of exactly 19 nodes + Then I expect the subgraph projection to consist of exactly 11 nodes Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | | 1 | sir-david-nodenborough | | 2 | eldest-mc-nodeface | - | 3 | eldest-nodewyn-tetherton | - | 4 | eldest-nodimer-tetherton | | 2 | elder-mc-nodeface | - | 3 | elder-nodewyn-tetherton | - | 4 | elder-nodimer-tetherton | | 2 | younger-mc-nodeface | - | 3 | younger-nodewyn-tetherton | - | 4 | younger-nodimer-tetherton | | 2 | youngest-mc-nodeface | - | 3 | youngest-nodewyn-tetherton | - | 4 | youngest-nodimer-tetherton | | 1 | sir-nodeward-nodington-iii | | 2 | nody-mc-nodeface | | 3 | nodewyn-tetherton | @@ -436,13 +384,746 @@ Feature: Create node specialization And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | - | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | And I expect this node to have no preceding siblings And I expect this node to have no succeeding siblings And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + # Scenario: Create specialization variant to a new parent before the first of its new siblings - which does not exist in all variants + # As this requires deletion of a specialization: @see ../07-NodeRemoval/07-CreateNodeSpecializationVariant-NewParent-AfterDeletion.feature + + # Scenario: Create specialization variant to a new parent before one of its new siblings, which is partially the first + # As this requires deletion of a specialization: @see ../07-NodeRemoval/07-CreateNodeSpecializationVariant-NewParent-AfterDeletion.feature + + Scenario: Create specialization variant to a new parent before one of its new siblings, which is not the first + When the command CreateNodeVariant is executed with payload: + | Key | Value | + | nodeAggregateId | "nody-mc-nodeface" | + | sourceOrigin | {"example":"general"} | + | targetOrigin | {"example":"source"} | + | parentNodeAggregateId | "sir-david-nodenborough" | + | succeedingSiblingNodeAggregateId | "elder-mc-nodeface" | + Then I expect exactly 15 events to be published on stream "ContentStream:cs-identifier" + And event at index 12 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nody-mc-nodeface" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"elder-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"elder-mc-nodeface"}] | + | parentNodeAggregateId | "sir-david-nodenborough" | + And event at index 13 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodewyn-tetherton" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"invariable-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"invariable-mc-nodeface"}] | + | parentNodeAggregateId | null | + And event at index 14 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodimer-tetherton" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | + | parentNodeAggregateId | null | + + Then I expect the graph projection to consist of exactly 14 nodes + And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;eldest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;elder-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;younger-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;youngest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;invariable-mc-nodeface;{"example":"general"} to exist in the content graph + + When I am in workspace "live" + + And I expect the node aggregate "nody-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "nodewyn-tetherton" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "nodimer-tetherton" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "invariable-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + When I am in workspace "live" and dimension space point {"example":"source"} + Then I expect the subgraph projection to consist of exactly 11 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + When I am in workspace "live" and dimension space point {"example":"general"} + Then I expect the subgraph projection to consist of exactly 11 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + When I am in workspace "live" and dimension space point {"example":"spec"} + Then I expect the subgraph projection to consist of exactly 11 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + When I am in workspace "live" and dimension space point {"example":"peer"} + Then I expect the subgraph projection to consist of exactly 11 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + # Scenario: Create specialization variant to a new parent before one of its new siblings, which is not the first and does not exist in all variants + # As this requires deletion of a specialization: @see ../07-NodeRemoval/07-CreateNodeSpecializationVariant-NewParent-AfterDeletion.feature + + # Scenario: Create specialization variant to a new parent before one of its new siblings, which is the last and does not exist in all variants + # As this requires deletion of a specialization: @see ../07-NodeRemoval/07-CreateNodeSpecializationVariant-NewParent-AfterDeletion.feature + + Scenario: Create specialization variant to a new parent after one of its new siblings, which is not the last + When the command CreateNodeVariant is executed with payload: + | Key | Value | + | nodeAggregateId | "nody-mc-nodeface" | + | sourceOrigin | {"example":"general"} | + | targetOrigin | {"example":"source"} | + | parentNodeAggregateId | "sir-david-nodenborough" | + | precedingSiblingNodeAggregateId | "younger-mc-nodeface" | + Then I expect exactly 15 events to be published on stream "ContentStream:cs-identifier" + And event at index 12 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nody-mc-nodeface" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"youngest-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"youngest-mc-nodeface"}] | + | parentNodeAggregateId | "sir-david-nodenborough" | + And event at index 13 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodewyn-tetherton" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"invariable-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"invariable-mc-nodeface"}] | + | parentNodeAggregateId | null | + And event at index 14 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodimer-tetherton" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | + | parentNodeAggregateId | null | + + Then I expect the graph projection to consist of exactly 14 nodes + And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;eldest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;elder-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;younger-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;youngest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;invariable-mc-nodeface;{"example":"general"} to exist in the content graph + + When I am in workspace "live" + + And I expect the node aggregate "nody-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "nodewyn-tetherton" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "nodimer-tetherton" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "invariable-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + When I am in workspace "live" and dimension space point {"example":"source"} + Then I expect the subgraph projection to consist of exactly 11 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + When I am in workspace "live" and dimension space point {"example":"general"} + Then I expect the subgraph projection to consist of exactly 11 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + When I am in workspace "live" and dimension space point {"example":"spec"} + Then I expect the subgraph projection to consist of exactly 11 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + When I am in workspace "live" and dimension space point {"example":"peer"} + Then I expect the subgraph projection to consist of exactly 11 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/07-CreateNodeSpecializationVariant-NewParent-AfterRemoval.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/07-CreateNodeSpecializationVariant-NewParent-AfterRemoval.feature new file mode 100644 index 00000000000..50033db568f --- /dev/null +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/07-CreateNodeSpecializationVariant-NewParent-AfterRemoval.feature @@ -0,0 +1,1462 @@ +@contentrepository @adapters=DoctrineDBAL,Postgres +Feature: Create node specialization + As a user of the CR I want to create a variant of a node within an aggregate + to a more specialized dimension space point + and assign that variant to a different parent + * before the first of its new siblings + * before the first of its new siblings - which does not exist in all variants + * before one of its new siblings, wich is partially the first + * before one of its new siblings, which is not the first + * before one of its new siblings, which is not the first and does not exist in all variants + * before one of its new siblings, which is the last and does not exist in all variants + * after the last of its new siblings + * after the last of its new siblings, which does not exist in all variants + * after one of its new siblings, which is partially the last + * after one of its new siblings, which is not the last + + Background: + Given using the following content dimensions: + | Identifier | Values | Generalizations | + | example | general, source, spec, peer | spec->source->general, peer->general | + And using the following node types: + """yaml + 'Neos.ContentRepository.Testing:Document': + childNodes: + tethered-node: + type: 'Neos.ContentRepository.Testing:Tethered' + 'Neos.ContentRepository.Testing:Tethered': + childNodes: + tethered-leaf: + type: 'Neos.ContentRepository.Testing:TetheredLeaf' + 'Neos.ContentRepository.Testing:TetheredLeaf': [] + 'Neos.ContentRepository.Testing:LeafDocument': [] + """ + And using identifier "default", I define a content repository + And I am in content repository "default" + And I am user identified by "initiating-user-identifier" + And the command CreateRootWorkspace is executed with payload: + | Key | Value | + | workspaceName | "live" | + | newContentStreamId | "cs-identifier" | + And I am in workspace "live" and dimension space point {"example":"general"} + And the command CreateRootNodeAggregateWithNode is executed with payload: + | Key | Value | + | nodeAggregateId | "lady-eleonode-rootford" | + | nodeTypeName | "Neos.ContentRepository:Root" | + Given the following CreateNodeAggregateWithNode commands are executed: + | nodeAggregateId | nodeName | originDimensionSpacePoint | nodeTypeName | parentNodeAggregateId | tetheredDescendantNodeAggregateIds | + | sir-david-nodenborough | parent-document | {"example": "general"} | Neos.ContentRepository.Testing:LeafDocument | lady-eleonode-rootford | {} | + | eldest-mc-nodeface | eldest-document | {"example": "general"} | Neos.ContentRepository.Testing:LeafDocument | sir-david-nodenborough | {} | + | elder-mc-nodeface | elder-document | {"example": "general"} | Neos.ContentRepository.Testing:LeafDocument | sir-david-nodenborough | {} | + | younger-mc-nodeface | younger-document | {"example": "general"} | Neos.ContentRepository.Testing:LeafDocument | sir-david-nodenborough | {} | + | youngest-mc-nodeface | youngest-document | {"example": "general"} | Neos.ContentRepository.Testing:LeafDocument | sir-david-nodenborough | {} | + | sir-nodeward-nodington-iii | esquire | {"example": "general"} | Neos.ContentRepository.Testing:LeafDocument | lady-eleonode-rootford | {} | + | nody-mc-nodeface | document | {"example": "general"} | Neos.ContentRepository.Testing:Document | sir-nodeward-nodington-iii | {"tethered-node": "nodewyn-tetherton", "tethered-node/tethered-leaf": "nodimer-tetherton"} | + | invariable-mc-nodeface | invariable | {"example": "general"} | Neos.ContentRepository.Testing:LeafDocument | nody-mc-nodeface | {} | + + Scenario: Create specialization variant to a new parent before the first of its new siblings - which does not exist in all variants + Given the command RemoveNodeAggregate is executed with payload: + | Key | Value | + | nodeAggregateId | "eldest-mc-nodeface" | + | coveredDimensionSpacePoint | {"example":"spec"} | + | nodeVariantSelectionStrategy | "allSpecializations" | + + When the command CreateNodeVariant is executed with payload: + | Key | Value | + | nodeAggregateId | "nody-mc-nodeface" | + | sourceOrigin | {"example":"general"} | + | targetOrigin | {"example":"source"} | + | parentNodeAggregateId | "sir-david-nodenborough" | + | succeedingSiblingNodeAggregateId | "eldest-mc-nodeface" | + Then I expect exactly 16 events to be published on stream "ContentStream:cs-identifier" + And event at index 13 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nody-mc-nodeface" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"eldest-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"elder-mc-nodeface"}] | + | parentNodeAggregateId | "sir-david-nodenborough" | + And event at index 14 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodewyn-tetherton" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"invariable-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"invariable-mc-nodeface"}] | + | parentNodeAggregateId | null | + And event at index 15 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodimer-tetherton" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | + | parentNodeAggregateId | null | + + Then I expect the graph projection to consist of exactly 14 nodes + And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;eldest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;elder-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;younger-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;youngest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;invariable-mc-nodeface;{"example":"general"} to exist in the content graph + + When I am in workspace "live" + + And I expect the node aggregate "nody-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "nodewyn-tetherton" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "nodimer-tetherton" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "invariable-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + When I am in workspace "live" and dimension space point {"example":"source"} + Then I expect the subgraph projection to consist of exactly 11 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + And I expect this node to have no succeeding siblings + + When I am in workspace "live" and dimension space point {"example":"general"} + Then I expect the subgraph projection to consist of exactly 11 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + When I am in workspace "live" and dimension space point {"example":"spec"} + Then I expect the subgraph projection to consist of exactly 10 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to no node + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + And I expect this node to have no succeeding siblings + + When I am in workspace "live" and dimension space point {"example":"peer"} + Then I expect the subgraph projection to consist of exactly 11 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + Scenario: Create specialization variant to a new parent before one of its new siblings, wich is partially the first + And the command RemoveNodeAggregate is executed with payload: + | Key | Value | + | nodeAggregateId | "eldest-mc-nodeface" | + | coveredDimensionSpacePoint | {"example":"spec"} | + | nodeVariantSelectionStrategy | "allSpecializations" | + + When the command CreateNodeVariant is executed with payload: + | Key | Value | + | nodeAggregateId | "nody-mc-nodeface" | + | sourceOrigin | {"example":"general"} | + | targetOrigin | {"example":"source"} | + | parentNodeAggregateId | "sir-david-nodenborough" | + | succeedingSiblingNodeAggregateId | "elder-mc-nodeface" | + Then I expect exactly 16 events to be published on stream "ContentStream:cs-identifier" + And event at index 13 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nody-mc-nodeface" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"elder-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"elder-mc-nodeface"}] | + | parentNodeAggregateId | "sir-david-nodenborough" | + And event at index 14 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodewyn-tetherton" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"invariable-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"invariable-mc-nodeface"}] | + | parentNodeAggregateId | null | + And event at index 15 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodimer-tetherton" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | + | parentNodeAggregateId | null | + + Then I expect the graph projection to consist of exactly 14 nodes + And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;eldest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;elder-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;younger-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;youngest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;invariable-mc-nodeface;{"example":"general"} to exist in the content graph + + When I am in workspace "live" + + And I expect the node aggregate "nody-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "nodewyn-tetherton" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "nodimer-tetherton" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "invariable-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + When I am in workspace "live" and dimension space point {"example":"source"} + Then I expect the subgraph projection to consist of exactly 11 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + When I am in workspace "live" and dimension space point {"example":"general"} + Then I expect the subgraph projection to consist of exactly 11 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + When I am in workspace "live" and dimension space point {"example":"spec"} + Then I expect the subgraph projection to consist of exactly 10 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to no node + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + And I expect this node to have no succeeding siblings + + When I am in workspace "live" and dimension space point {"example":"peer"} + Then I expect the subgraph projection to consist of exactly 11 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + Scenario: Create specialization variant to a new parent before one of its new siblings, which is not the first and does not exist in all variants + Given the command RemoveNodeAggregate is executed with payload: + | Key | Value | + | nodeAggregateId | "elder-mc-nodeface" | + | coveredDimensionSpacePoint | {"example":"spec"} | + | nodeVariantSelectionStrategy | "allSpecializations" | + + When the command CreateNodeVariant is executed with payload: + | Key | Value | + | nodeAggregateId | "nody-mc-nodeface" | + | sourceOrigin | {"example":"general"} | + | targetOrigin | {"example":"source"} | + | parentNodeAggregateId | "sir-david-nodenborough" | + | succeedingSiblingNodeAggregateId | "elder-mc-nodeface" | + Then I expect exactly 16 events to be published on stream "ContentStream:cs-identifier" + And event at index 13 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nody-mc-nodeface" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"elder-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"younger-mc-nodeface"}] | + | parentNodeAggregateId | "sir-david-nodenborough" | + And event at index 14 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodewyn-tetherton" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"invariable-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"invariable-mc-nodeface"}] | + | parentNodeAggregateId | null | + And event at index 15 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodimer-tetherton" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | + | parentNodeAggregateId | null | + + Then I expect the graph projection to consist of exactly 14 nodes + And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;eldest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;elder-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;younger-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;youngest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;invariable-mc-nodeface;{"example":"general"} to exist in the content graph + + When I am in workspace "live" + And I expect the node aggregate "nody-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "nodewyn-tetherton" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "nodimer-tetherton" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "invariable-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + When I am in workspace "live" and dimension space point {"example":"source"} + Then I expect the subgraph projection to consist of exactly 11 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + When I am in workspace "live" and dimension space point {"example":"general"} + Then I expect the subgraph projection to consist of exactly 11 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + When I am in workspace "live" and dimension space point {"example":"spec"} + Then I expect the subgraph projection to consist of exactly 10 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to no node + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + When I am in workspace "live" and dimension space point {"example":"peer"} + Then I expect the subgraph projection to consist of exactly 11 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + Scenario: Create specialization variant to a new parent before one of its new siblings, which is the last and does not exist in all variants + Given the command RemoveNodeAggregate is executed with payload: + | Key | Value | + | nodeAggregateId | "youngest-mc-nodeface" | + | coveredDimensionSpacePoint | {"example":"spec"} | + | nodeVariantSelectionStrategy | "allSpecializations" | + + When the command CreateNodeVariant is executed with payload: + | Key | Value | + | nodeAggregateId | "nody-mc-nodeface" | + | sourceOrigin | {"example":"general"} | + | targetOrigin | {"example":"source"} | + | parentNodeAggregateId | "sir-david-nodenborough" | + | succeedingSiblingNodeAggregateId | "youngest-mc-nodeface" | + Then I expect exactly 16 events to be published on stream "ContentStream:cs-identifier" + And event at index 13 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nody-mc-nodeface" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"youngest-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | + | parentNodeAggregateId | "sir-david-nodenborough" | + And event at index 14 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodewyn-tetherton" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"invariable-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"invariable-mc-nodeface"}] | + | parentNodeAggregateId | null | + And event at index 15 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodimer-tetherton" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | + | parentNodeAggregateId | null | + + Then I expect the graph projection to consist of exactly 14 nodes + And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;eldest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;elder-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;younger-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;youngest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;invariable-mc-nodeface;{"example":"general"} to exist in the content graph + + When I am in workspace "live" + And I expect the node aggregate "nody-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "nodewyn-tetherton" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "nodimer-tetherton" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "invariable-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + When I am in workspace "live" and dimension space point {"example":"source"} + Then I expect the subgraph projection to consist of exactly 11 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + When I am in workspace "live" and dimension space point {"example":"general"} + Then I expect the subgraph projection to consist of exactly 11 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + When I am in workspace "live" and dimension space point {"example":"spec"} + Then I expect the subgraph projection to consist of exactly 10 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to no node + + When I am in workspace "live" and dimension space point {"example":"peer"} + Then I expect the subgraph projection to consist of exactly 11 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/InterdimensionalSiblingsProvider.php b/Neos.ContentRepository.Core/Classes/Feature/Common/InterdimensionalSiblingsProvider.php index 6762c677c03..b491b64fab8 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/Common/InterdimensionalSiblingsProvider.php +++ b/Neos.ContentRepository.Core/Classes/Feature/Common/InterdimensionalSiblingsProvider.php @@ -22,10 +22,13 @@ trait InterdimensionalSiblingsProvider { /** * Resolves the succeeding siblings for the given node at the given target in the given dimension space points + * + * @param DimensionSpacePoint $referenceDimensionSpacePoint + * The reference dimension space point for alternative sibling resolution */ protected function resolveInterdimensionalSiblings( ContentGraphInterface $contentGraph, - DimensionSpacePoint $selectedDimensionSpacePoint, + DimensionSpacePoint $referenceDimensionSpacePoint, DimensionSpacePointSet $affectedDimensionSpacePoints, NodeAggregateId $nodeAggregateId, ?NodeAggregateId $parentNodeAggregateId, @@ -34,7 +37,7 @@ protected function resolveInterdimensionalSiblings( bool $completeSet, ): InterdimensionalSiblings { $selectedSubgraph = $contentGraph->getSubgraph( - $selectedDimensionSpacePoint, + $referenceDimensionSpacePoint, VisibilityConstraints::createEmpty() ); $alternativeSucceedingSiblingIds = $succeedingSiblingNodeAggregateId @@ -61,9 +64,6 @@ protected function resolveInterdimensionalSiblings( $variantSucceedingSibling = $variantSubgraph->findNodeById($succeedingSiblingNodeAggregateId); $variantParentId = $parentNodeAggregateId ?: $variantSubgraph->findParentNode($nodeAggregateId)?->aggregateId; $siblingParent = $variantSubgraph->findParentNode($succeedingSiblingNodeAggregateId); - \Neos\Flow\var_dump($variantSucceedingSibling?->aggregateId, 'succeeding variant'); - \Neos\Flow\var_dump($siblingParent?->aggregateId, 'sibling parent'); - \Neos\Flow\var_dump($variantParentId, 'variant parent'); if ($variantSucceedingSibling && $siblingParent && (!$variantParentId || $variantParentId->equals($siblingParent->aggregateId))) { // a) happy path, the explicitly requested succeeding sibling also exists in this dimension space point $interdimensionalSiblings[] = new InterdimensionalSibling( diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/NodeVariationInternals.php b/Neos.ContentRepository.Core/Classes/Feature/Common/NodeVariationInternals.php index 3645fbfb039..f08ede109a7 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/Common/NodeVariationInternals.php +++ b/Neos.ContentRepository.Core/Classes/Feature/Common/NodeVariationInternals.php @@ -141,7 +141,7 @@ protected function collectNodeSpecializationVariantsThatWillHaveBeenCreated( specializationOrigin: $targetOrigin, specializationSiblings: $this->resolveInterdimensionalSiblings( contentGraph: $contentGraph, - selectedDimensionSpacePoint: $sourceOrigin->toDimensionSpacePoint(), + referenceDimensionSpacePoint: $targetOrigin->toDimensionSpacePoint(), affectedDimensionSpacePoints: $specializationVisibility, nodeAggregateId: $nodeAggregate->nodeAggregateId, parentNodeAggregateId: $parentNodeAggregateId, diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeMove/NodeMove.php b/Neos.ContentRepository.Core/Classes/Feature/NodeMove/NodeMove.php index 74d09139b7d..b36db8058a1 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeMove/NodeMove.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeMove/NodeMove.php @@ -191,7 +191,7 @@ private function handleMoveNodeAggregate( newParentNodeAggregateId: $command->newParentNodeAggregateId, succeedingSiblingsForCoverage: $this->resolveInterdimensionalSiblings( contentGraph: $contentGraph, - selectedDimensionSpacePoint: $command->dimensionSpacePoint, + referenceDimensionSpacePoint: $command->dimensionSpacePoint, affectedDimensionSpacePoints: $affectedDimensionSpacePoints, nodeAggregateId: $command->nodeAggregateId, parentNodeAggregateId: $command->newParentNodeAggregateId, From 25764320bc029e3d90ebd0c60ba832e01abd82cf Mon Sep 17 00:00:00 2001 From: Bernhard Schmitt Date: Sun, 30 Nov 2025 14:19:10 +0100 Subject: [PATCH 7/9] 5054 - further extend test suite --- ...odeSpecializationVariant-NewParent.feature | 370 ++++++++++++++++++ ...tionVariant-NewParent-AfterRemoval.feature | 357 +++++++++++++++++ 2 files changed, 727 insertions(+) diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/05-CreateNodeSpecializationVariant-NewParent.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/05-CreateNodeSpecializationVariant-NewParent.feature index 46e3b4f4971..f0946e113da 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/05-CreateNodeSpecializationVariant-NewParent.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/05-CreateNodeSpecializationVariant-NewParent.feature @@ -1142,3 +1142,373 @@ Feature: Create node specialization | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have no succeeding siblings + + # Scenario: Create specialization variant to a new parent after one of its new siblings, which is partially the last + # As this is implemented via deletion of a specialization: @see ../07-NodeRemoval/07-CreateNodeSpecializationVariant-NewParent-AfterDeletion.feature + + Scenario: Create specialization variant to a new parent after the last of its new siblings + When the command CreateNodeVariant is executed with payload: + | Key | Value | + | nodeAggregateId | "nody-mc-nodeface" | + | sourceOrigin | {"example":"general"} | + | targetOrigin | {"example":"source"} | + | parentNodeAggregateId | "sir-david-nodenborough" | + | precedingSiblingNodeAggregateId | "youngest-mc-nodeface" | + Then I expect exactly 15 events to be published on stream "ContentStream:cs-identifier" + And event at index 12 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nody-mc-nodeface" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | + | parentNodeAggregateId | "sir-david-nodenborough" | + And event at index 13 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodewyn-tetherton" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"invariable-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"invariable-mc-nodeface"}] | + | parentNodeAggregateId | null | + And event at index 14 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodimer-tetherton" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | + | parentNodeAggregateId | null | + + Then I expect the graph projection to consist of exactly 14 nodes + And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;eldest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;elder-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;younger-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;youngest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;invariable-mc-nodeface;{"example":"general"} to exist in the content graph + + When I am in workspace "live" + + And I expect the node aggregate "nody-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "nodewyn-tetherton" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "nodimer-tetherton" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "invariable-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + When I am in workspace "live" and dimension space point {"example":"source"} + Then I expect the subgraph projection to consist of exactly 11 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + When I am in workspace "live" and dimension space point {"example":"general"} + Then I expect the subgraph projection to consist of exactly 11 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + + When I am in workspace "live" and dimension space point {"example":"spec"} + Then I expect the subgraph projection to consist of exactly 11 nodes + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + When I am in workspace "live" and dimension space point {"example":"peer"} + Then I expect the subgraph projection to consist of exactly 11 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/07-CreateNodeSpecializationVariant-NewParent-AfterRemoval.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/07-CreateNodeSpecializationVariant-NewParent-AfterRemoval.feature index 50033db568f..e30cd89dbc0 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/07-CreateNodeSpecializationVariant-NewParent-AfterRemoval.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/07-CreateNodeSpecializationVariant-NewParent-AfterRemoval.feature @@ -1460,3 +1460,360 @@ Feature: Create node specialization | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have no succeeding siblings + + Scenario: Create specialization variant to a new parent after one of its new siblings, which is partially the last + Given the command RemoveNodeAggregate is executed with payload: + | Key | Value | + | nodeAggregateId | "youngest-mc-nodeface" | + | coveredDimensionSpacePoint | {"example":"spec"} | + | nodeVariantSelectionStrategy | "allSpecializations" | + + When the command CreateNodeVariant is executed with payload: + | Key | Value | + | nodeAggregateId | "nody-mc-nodeface" | + | sourceOrigin | {"example":"general"} | + | targetOrigin | {"example":"source"} | + | parentNodeAggregateId | "sir-david-nodenborough" | + | precedingSiblingNodeAggregateId | "younger-mc-nodeface" | + Then I expect exactly 16 events to be published on stream "ContentStream:cs-identifier" + And event at index 13 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nody-mc-nodeface" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"youngest-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | + | parentNodeAggregateId | "sir-david-nodenborough" | + And event at index 14 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodewyn-tetherton" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"invariable-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"invariable-mc-nodeface"}] | + | parentNodeAggregateId | null | + And event at index 15 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodimer-tetherton" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | + | parentNodeAggregateId | null | + + Then I expect the graph projection to consist of exactly 14 nodes + And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;eldest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;elder-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;younger-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;youngest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;invariable-mc-nodeface;{"example":"general"} to exist in the content graph + + When I am in workspace "live" + + And I expect the node aggregate "nody-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "nodewyn-tetherton" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "nodimer-tetherton" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "invariable-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + When I am in workspace "live" and dimension space point {"example":"source"} + Then I expect the subgraph projection to consist of exactly 11 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + When I am in workspace "live" and dimension space point {"example":"general"} + Then I expect the subgraph projection to consist of exactly 11 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + When I am in workspace "live" and dimension space point {"example":"spec"} + Then I expect the subgraph projection to consist of exactly 10 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to no node + + When I am in workspace "live" and dimension space point {"example":"peer"} + Then I expect the subgraph projection to consist of exactly 11 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings From 17ebf1d469d5c6ccbe0f9605bddc10ac1a2a88ff Mon Sep 17 00:00:00 2001 From: Bernhard Schmitt Date: Sun, 30 Nov 2025 19:41:21 +0100 Subject: [PATCH 8/9] 5054 - Complete test suite for node specialization with new parent --- ...odeSpecializationVariant-NewParent.feature | 157 +++-- ...tionVariant-NewParent-AfterRemoval.feature | 553 ++++++++++++++++-- 2 files changed, 634 insertions(+), 76 deletions(-) diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/05-CreateNodeSpecializationVariant-NewParent.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/05-CreateNodeSpecializationVariant-NewParent.feature index f0946e113da..06b3e8ae15b 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/05-CreateNodeSpecializationVariant-NewParent.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/05-CreateNodeSpecializationVariant-NewParent.feature @@ -88,7 +88,7 @@ Feature: Create node specialization | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | | parentNodeAggregateId | null | - Then I expect the graph projection to consist of exactly 14 nodes + And I expect the graph projection to consist of exactly 14 nodes And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"general"} to exist in the content graph And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"source"} to exist in the content graph @@ -120,9 +120,10 @@ Feature: Create node specialization And I expect this node aggregate to occupy dimension space points [{"example":"general"}] And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + When I am in workspace "live" and dimension space point {"example":"source"} Then I expect the subgraph projection to consist of exactly 11 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -136,7 +137,8 @@ Feature: Create node specialization | 2 | younger-mc-nodeface | | 2 | youngest-mc-nodeface | | 1 | sir-nodeward-nodington-iii | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | @@ -144,6 +146,7 @@ Feature: Create node specialization | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -153,6 +156,7 @@ Feature: Create node specialization | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -162,6 +166,7 @@ Feature: Create node specialization | NodeDiscriminator | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | @@ -180,6 +185,7 @@ Feature: Create node specialization | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -189,6 +195,7 @@ Feature: Create node specialization And I expect this node to have the following succeeding siblings: | NodeDiscriminator | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -198,9 +205,10 @@ Feature: Create node specialization | cs-identifier;nody-mc-nodeface;{"example":"source"} | And I expect this node to have no succeeding siblings + When I am in workspace "live" and dimension space point {"example":"general"} Then I expect the subgraph projection to consist of exactly 11 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -214,13 +222,15 @@ Feature: Create node specialization | 3 | nodewyn-tetherton | | 4 | nodimer-tetherton | | 3 | invariable-mc-nodeface | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: @@ -228,6 +238,7 @@ Feature: Create node specialization | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -236,6 +247,7 @@ Feature: Create node specialization | NodeDiscriminator | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | @@ -249,6 +261,7 @@ Feature: Create node specialization | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -257,6 +270,7 @@ Feature: Create node specialization And I expect this node to have the following succeeding siblings: | NodeDiscriminator | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -265,9 +279,10 @@ Feature: Create node specialization | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have no succeeding siblings + When I am in workspace "live" and dimension space point {"example":"spec"} Then I expect the subgraph projection to consist of exactly 11 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -281,7 +296,8 @@ Feature: Create node specialization | 2 | younger-mc-nodeface | | 2 | youngest-mc-nodeface | | 1 | sir-nodeward-nodington-iii | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | @@ -289,6 +305,7 @@ Feature: Create node specialization | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -298,6 +315,7 @@ Feature: Create node specialization | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -307,6 +325,7 @@ Feature: Create node specialization | NodeDiscriminator | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | @@ -325,6 +344,7 @@ Feature: Create node specialization | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -334,6 +354,7 @@ Feature: Create node specialization And I expect this node to have the following succeeding siblings: | NodeDiscriminator | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -343,9 +364,10 @@ Feature: Create node specialization | cs-identifier;nody-mc-nodeface;{"example":"source"} | And I expect this node to have no succeeding siblings + When I am in workspace "live" and dimension space point {"example":"peer"} Then I expect the subgraph projection to consist of exactly 11 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -359,13 +381,15 @@ Feature: Create node specialization | 3 | nodewyn-tetherton | | 4 | nodimer-tetherton | | 3 | invariable-mc-nodeface | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: @@ -373,6 +397,7 @@ Feature: Create node specialization | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -381,6 +406,7 @@ Feature: Create node specialization | NodeDiscriminator | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | @@ -394,6 +420,7 @@ Feature: Create node specialization | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -402,6 +429,7 @@ Feature: Create node specialization And I expect this node to have the following succeeding siblings: | NodeDiscriminator | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -450,7 +478,7 @@ Feature: Create node specialization | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | | parentNodeAggregateId | null | - Then I expect the graph projection to consist of exactly 14 nodes + And I expect the graph projection to consist of exactly 14 nodes And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"general"} to exist in the content graph And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"source"} to exist in the content graph @@ -484,7 +512,7 @@ Feature: Create node specialization When I am in workspace "live" and dimension space point {"example":"source"} Then I expect the subgraph projection to consist of exactly 11 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -498,7 +526,8 @@ Feature: Create node specialization | 2 | younger-mc-nodeface | | 2 | youngest-mc-nodeface | | 1 | sir-nodeward-nodington-iii | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | @@ -506,6 +535,7 @@ Feature: Create node specialization | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: @@ -514,6 +544,7 @@ Feature: Create node specialization | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -523,6 +554,7 @@ Feature: Create node specialization | NodeDiscriminator | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | @@ -542,6 +574,7 @@ Feature: Create node specialization | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -551,6 +584,7 @@ Feature: Create node specialization And I expect this node to have the following succeeding siblings: | NodeDiscriminator | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -560,9 +594,10 @@ Feature: Create node specialization | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have no succeeding siblings + When I am in workspace "live" and dimension space point {"example":"general"} Then I expect the subgraph projection to consist of exactly 11 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -576,13 +611,15 @@ Feature: Create node specialization | 3 | nodewyn-tetherton | | 4 | nodimer-tetherton | | 3 | invariable-mc-nodeface | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: @@ -590,6 +627,7 @@ Feature: Create node specialization | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -598,6 +636,7 @@ Feature: Create node specialization | NodeDiscriminator | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | @@ -611,6 +650,7 @@ Feature: Create node specialization | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -619,6 +659,7 @@ Feature: Create node specialization And I expect this node to have the following succeeding siblings: | NodeDiscriminator | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -627,9 +668,10 @@ Feature: Create node specialization | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have no succeeding siblings + When I am in workspace "live" and dimension space point {"example":"spec"} Then I expect the subgraph projection to consist of exactly 11 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -643,7 +685,8 @@ Feature: Create node specialization | 2 | younger-mc-nodeface | | 2 | youngest-mc-nodeface | | 1 | sir-nodeward-nodington-iii | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | @@ -705,9 +748,10 @@ Feature: Create node specialization | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have no succeeding siblings + When I am in workspace "live" and dimension space point {"example":"peer"} Then I expect the subgraph projection to consist of exactly 11 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -721,13 +765,15 @@ Feature: Create node specialization | 3 | nodewyn-tetherton | | 4 | nodimer-tetherton | | 3 | invariable-mc-nodeface | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: @@ -735,6 +781,7 @@ Feature: Create node specialization | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -743,6 +790,7 @@ Feature: Create node specialization | NodeDiscriminator | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | @@ -756,6 +804,7 @@ Feature: Create node specialization | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -764,6 +813,7 @@ Feature: Create node specialization And I expect this node to have the following succeeding siblings: | NodeDiscriminator | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -812,7 +862,7 @@ Feature: Create node specialization | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | | parentNodeAggregateId | null | - Then I expect the graph projection to consist of exactly 14 nodes + And I expect the graph projection to consist of exactly 14 nodes And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"general"} to exist in the content graph And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"source"} to exist in the content graph @@ -844,9 +894,10 @@ Feature: Create node specialization And I expect this node aggregate to occupy dimension space points [{"example":"general"}] And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + When I am in workspace "live" and dimension space point {"example":"source"} Then I expect the subgraph projection to consist of exactly 11 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -860,7 +911,8 @@ Feature: Create node specialization | 3 | invariable-mc-nodeface | | 2 | youngest-mc-nodeface | | 1 | sir-nodeward-nodington-iii | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | @@ -868,6 +920,7 @@ Feature: Create node specialization | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: @@ -926,9 +979,10 @@ Feature: Create node specialization | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have no succeeding siblings + When I am in workspace "live" and dimension space point {"example":"general"} Then I expect the subgraph projection to consist of exactly 11 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -942,13 +996,15 @@ Feature: Create node specialization | 3 | nodewyn-tetherton | | 4 | nodimer-tetherton | | 3 | invariable-mc-nodeface | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: @@ -956,6 +1012,7 @@ Feature: Create node specialization | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -964,6 +1021,7 @@ Feature: Create node specialization | NodeDiscriminator | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | @@ -977,6 +1035,7 @@ Feature: Create node specialization | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -985,6 +1044,7 @@ Feature: Create node specialization And I expect this node to have the following succeeding siblings: | NodeDiscriminator | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -993,9 +1053,10 @@ Feature: Create node specialization | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have no succeeding siblings + When I am in workspace "live" and dimension space point {"example":"spec"} Then I expect the subgraph projection to consist of exactly 11 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -1009,7 +1070,8 @@ Feature: Create node specialization | 3 | invariable-mc-nodeface | | 2 | youngest-mc-nodeface | | 1 | sir-nodeward-nodington-iii | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | @@ -1076,9 +1138,10 @@ Feature: Create node specialization | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have no succeeding siblings + When I am in workspace "live" and dimension space point {"example":"peer"} Then I expect the subgraph projection to consist of exactly 11 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -1092,13 +1155,15 @@ Feature: Create node specialization | 3 | nodewyn-tetherton | | 4 | nodimer-tetherton | | 3 | invariable-mc-nodeface | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: @@ -1106,6 +1171,7 @@ Feature: Create node specialization | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -1114,6 +1180,7 @@ Feature: Create node specialization | NodeDiscriminator | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | @@ -1127,6 +1194,7 @@ Feature: Create node specialization | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -1135,6 +1203,7 @@ Feature: Create node specialization And I expect this node to have the following succeeding siblings: | NodeDiscriminator | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -1180,7 +1249,7 @@ Feature: Create node specialization | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | | parentNodeAggregateId | null | - Then I expect the graph projection to consist of exactly 14 nodes + And I expect the graph projection to consist of exactly 14 nodes And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"general"} to exist in the content graph And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"source"} to exist in the content graph @@ -1212,9 +1281,10 @@ Feature: Create node specialization And I expect this node aggregate to occupy dimension space points [{"example":"general"}] And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + When I am in workspace "live" and dimension space point {"example":"source"} Then I expect the subgraph projection to consist of exactly 11 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -1228,7 +1298,8 @@ Feature: Create node specialization | 4 | nodimer-tetherton | | 3 | invariable-mc-nodeface | | 1 | sir-nodeward-nodington-iii | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | @@ -1236,6 +1307,7 @@ Feature: Create node specialization | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: @@ -1294,9 +1366,10 @@ Feature: Create node specialization | NodeDiscriminator | | cs-identifier;nody-mc-nodeface;{"example":"source"} | + When I am in workspace "live" and dimension space point {"example":"general"} Then I expect the subgraph projection to consist of exactly 11 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -1310,13 +1383,15 @@ Feature: Create node specialization | 3 | nodewyn-tetherton | | 4 | nodimer-tetherton | | 3 | invariable-mc-nodeface | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: @@ -1324,6 +1399,7 @@ Feature: Create node specialization | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -1332,6 +1408,7 @@ Feature: Create node specialization | NodeDiscriminator | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | @@ -1345,6 +1422,7 @@ Feature: Create node specialization | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -1353,6 +1431,7 @@ Feature: Create node specialization And I expect this node to have the following succeeding siblings: | NodeDiscriminator | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -1446,9 +1525,10 @@ Feature: Create node specialization | NodeDiscriminator | | cs-identifier;nody-mc-nodeface;{"example":"source"} | + When I am in workspace "live" and dimension space point {"example":"peer"} Then I expect the subgraph projection to consist of exactly 11 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -1462,13 +1542,15 @@ Feature: Create node specialization | 3 | nodewyn-tetherton | | 4 | nodimer-tetherton | | 3 | invariable-mc-nodeface | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: @@ -1476,6 +1558,7 @@ Feature: Create node specialization | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -1484,6 +1567,7 @@ Feature: Create node specialization | NodeDiscriminator | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | @@ -1497,6 +1581,7 @@ Feature: Create node specialization | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -1505,6 +1590,7 @@ Feature: Create node specialization And I expect this node to have the following succeeding siblings: | NodeDiscriminator | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -1512,3 +1598,6 @@ Feature: Create node specialization | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have no succeeding siblings + + # Scenario: Create specialization variant to a new parent after the last of its new siblings, which does not exist in all variants + # As this is implemented via deletion of a specialization: @see ../07-NodeRemoval/07-CreateNodeSpecializationVariant-NewParent-AfterDeletion.feature diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/07-CreateNodeSpecializationVariant-NewParent-AfterRemoval.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/07-CreateNodeSpecializationVariant-NewParent-AfterRemoval.feature index e30cd89dbc0..24463ef385e 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/07-CreateNodeSpecializationVariant-NewParent-AfterRemoval.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/07-NodeRemoval/07-CreateNodeSpecializationVariant-NewParent-AfterRemoval.feature @@ -94,7 +94,7 @@ Feature: Create node specialization | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | | parentNodeAggregateId | null | - Then I expect the graph projection to consist of exactly 14 nodes + And I expect the graph projection to consist of exactly 14 nodes And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"general"} to exist in the content graph And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"source"} to exist in the content graph @@ -110,7 +110,7 @@ Feature: Create node specialization When I am in workspace "live" - And I expect the node aggregate "nody-mc-nodeface" to exist + Then I expect the node aggregate "nody-mc-nodeface" to exist And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] @@ -126,9 +126,10 @@ Feature: Create node specialization And I expect this node aggregate to occupy dimension space points [{"example":"general"}] And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + When I am in workspace "live" and dimension space point {"example":"source"} Then I expect the subgraph projection to consist of exactly 11 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -142,7 +143,8 @@ Feature: Create node specialization | 2 | younger-mc-nodeface | | 2 | youngest-mc-nodeface | | 1 | sir-nodeward-nodington-iii | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | @@ -189,6 +191,7 @@ Feature: Create node specialization | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -198,6 +201,7 @@ Feature: Create node specialization And I expect this node to have the following succeeding siblings: | NodeDiscriminator | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -207,9 +211,10 @@ Feature: Create node specialization | cs-identifier;nody-mc-nodeface;{"example":"source"} | And I expect this node to have no succeeding siblings + When I am in workspace "live" and dimension space point {"example":"general"} Then I expect the subgraph projection to consist of exactly 11 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -223,13 +228,15 @@ Feature: Create node specialization | 3 | nodewyn-tetherton | | 4 | nodimer-tetherton | | 3 | invariable-mc-nodeface | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: @@ -237,6 +244,7 @@ Feature: Create node specialization | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -245,6 +253,7 @@ Feature: Create node specialization | NodeDiscriminator | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | @@ -258,6 +267,7 @@ Feature: Create node specialization | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -266,6 +276,7 @@ Feature: Create node specialization And I expect this node to have the following succeeding siblings: | NodeDiscriminator | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -274,9 +285,10 @@ Feature: Create node specialization | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have no succeeding siblings + When I am in workspace "live" and dimension space point {"example":"spec"} Then I expect the subgraph projection to consist of exactly 10 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -289,14 +301,17 @@ Feature: Create node specialization | 2 | younger-mc-nodeface | | 2 | youngest-mc-nodeface | | 1 | sir-nodeward-nodington-iii | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to no node + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -305,6 +320,7 @@ Feature: Create node specialization | NodeDiscriminator | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | @@ -322,6 +338,7 @@ Feature: Create node specialization | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -330,6 +347,7 @@ Feature: Create node specialization And I expect this node to have the following succeeding siblings: | NodeDiscriminator | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -338,9 +356,10 @@ Feature: Create node specialization | cs-identifier;nody-mc-nodeface;{"example":"source"} | And I expect this node to have no succeeding siblings + When I am in workspace "live" and dimension space point {"example":"peer"} Then I expect the subgraph projection to consist of exactly 11 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -354,13 +373,15 @@ Feature: Create node specialization | 3 | nodewyn-tetherton | | 4 | nodimer-tetherton | | 3 | invariable-mc-nodeface | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: @@ -368,6 +389,7 @@ Feature: Create node specialization | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -376,6 +398,7 @@ Feature: Create node specialization | NodeDiscriminator | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | @@ -389,6 +412,7 @@ Feature: Create node specialization | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -397,6 +421,7 @@ Feature: Create node specialization And I expect this node to have the following succeeding siblings: | NodeDiscriminator | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -406,7 +431,7 @@ Feature: Create node specialization And I expect this node to have no succeeding siblings Scenario: Create specialization variant to a new parent before one of its new siblings, wich is partially the first - And the command RemoveNodeAggregate is executed with payload: + Given the command RemoveNodeAggregate is executed with payload: | Key | Value | | nodeAggregateId | "eldest-mc-nodeface" | | coveredDimensionSpacePoint | {"example":"spec"} | @@ -445,7 +470,7 @@ Feature: Create node specialization | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | | parentNodeAggregateId | null | - Then I expect the graph projection to consist of exactly 14 nodes + And I expect the graph projection to consist of exactly 14 nodes And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"general"} to exist in the content graph And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"source"} to exist in the content graph @@ -477,9 +502,10 @@ Feature: Create node specialization And I expect this node aggregate to occupy dimension space points [{"example":"general"}] And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + When I am in workspace "live" and dimension space point {"example":"source"} Then I expect the subgraph projection to consist of exactly 11 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -493,7 +519,8 @@ Feature: Create node specialization | 2 | younger-mc-nodeface | | 2 | youngest-mc-nodeface | | 1 | sir-nodeward-nodington-iii | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | @@ -540,6 +567,7 @@ Feature: Create node specialization | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -549,6 +577,7 @@ Feature: Create node specialization And I expect this node to have the following succeeding siblings: | NodeDiscriminator | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -558,9 +587,10 @@ Feature: Create node specialization | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have no succeeding siblings + When I am in workspace "live" and dimension space point {"example":"general"} Then I expect the subgraph projection to consist of exactly 11 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -574,13 +604,15 @@ Feature: Create node specialization | 3 | nodewyn-tetherton | | 4 | nodimer-tetherton | | 3 | invariable-mc-nodeface | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: @@ -588,6 +620,7 @@ Feature: Create node specialization | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -596,6 +629,7 @@ Feature: Create node specialization | NodeDiscriminator | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | @@ -609,6 +643,7 @@ Feature: Create node specialization | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -617,6 +652,7 @@ Feature: Create node specialization And I expect this node to have the following succeeding siblings: | NodeDiscriminator | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -625,9 +661,10 @@ Feature: Create node specialization | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have no succeeding siblings + When I am in workspace "live" and dimension space point {"example":"spec"} Then I expect the subgraph projection to consist of exactly 10 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -640,14 +677,17 @@ Feature: Create node specialization | 2 | younger-mc-nodeface | | 2 | youngest-mc-nodeface | | 1 | sir-nodeward-nodington-iii | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to no node + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -656,6 +696,7 @@ Feature: Create node specialization | NodeDiscriminator | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | @@ -673,6 +714,7 @@ Feature: Create node specialization | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -681,6 +723,7 @@ Feature: Create node specialization And I expect this node to have the following succeeding siblings: | NodeDiscriminator | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -689,9 +732,10 @@ Feature: Create node specialization | cs-identifier;nody-mc-nodeface;{"example":"source"} | And I expect this node to have no succeeding siblings + When I am in workspace "live" and dimension space point {"example":"peer"} Then I expect the subgraph projection to consist of exactly 11 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -705,13 +749,15 @@ Feature: Create node specialization | 3 | nodewyn-tetherton | | 4 | nodimer-tetherton | | 3 | invariable-mc-nodeface | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: @@ -719,6 +765,7 @@ Feature: Create node specialization | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -727,6 +774,7 @@ Feature: Create node specialization | NodeDiscriminator | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | @@ -740,6 +788,7 @@ Feature: Create node specialization | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -748,6 +797,7 @@ Feature: Create node specialization And I expect this node to have the following succeeding siblings: | NodeDiscriminator | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -796,7 +846,7 @@ Feature: Create node specialization | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | | parentNodeAggregateId | null | - Then I expect the graph projection to consist of exactly 14 nodes + And I expect the graph projection to consist of exactly 14 nodes And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"general"} to exist in the content graph And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"source"} to exist in the content graph @@ -829,7 +879,7 @@ Feature: Create node specialization When I am in workspace "live" and dimension space point {"example":"source"} Then I expect the subgraph projection to consist of exactly 11 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -843,7 +893,8 @@ Feature: Create node specialization | 2 | younger-mc-nodeface | | 2 | youngest-mc-nodeface | | 1 | sir-nodeward-nodington-iii | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | @@ -890,6 +941,7 @@ Feature: Create node specialization | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -899,6 +951,7 @@ Feature: Create node specialization And I expect this node to have the following succeeding siblings: | NodeDiscriminator | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -908,9 +961,10 @@ Feature: Create node specialization | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have no succeeding siblings + When I am in workspace "live" and dimension space point {"example":"general"} Then I expect the subgraph projection to consist of exactly 11 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -924,13 +978,15 @@ Feature: Create node specialization | 3 | nodewyn-tetherton | | 4 | nodimer-tetherton | | 3 | invariable-mc-nodeface | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: @@ -938,6 +994,7 @@ Feature: Create node specialization | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -946,6 +1003,7 @@ Feature: Create node specialization | NodeDiscriminator | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | @@ -959,6 +1017,7 @@ Feature: Create node specialization | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -975,9 +1034,10 @@ Feature: Create node specialization | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have no succeeding siblings + When I am in workspace "live" and dimension space point {"example":"spec"} Then I expect the subgraph projection to consist of exactly 10 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -990,13 +1050,15 @@ Feature: Create node specialization | 2 | younger-mc-nodeface | | 2 | youngest-mc-nodeface | | 1 | sir-nodeward-nodington-iii | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: @@ -1004,7 +1066,9 @@ Feature: Create node specialization | cs-identifier;nody-mc-nodeface;{"example":"source"} | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to no node + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | @@ -1023,6 +1087,7 @@ Feature: Create node specialization | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -1031,6 +1096,7 @@ Feature: Create node specialization And I expect this node to have the following succeeding siblings: | NodeDiscriminator | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -1039,9 +1105,10 @@ Feature: Create node specialization | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have no succeeding siblings + When I am in workspace "live" and dimension space point {"example":"peer"} Then I expect the subgraph projection to consist of exactly 11 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -1055,13 +1122,15 @@ Feature: Create node specialization | 3 | nodewyn-tetherton | | 4 | nodimer-tetherton | | 3 | invariable-mc-nodeface | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: @@ -1069,6 +1138,7 @@ Feature: Create node specialization | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -1077,6 +1147,7 @@ Feature: Create node specialization | NodeDiscriminator | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | @@ -1090,6 +1161,7 @@ Feature: Create node specialization | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -1098,6 +1170,7 @@ Feature: Create node specialization And I expect this node to have the following succeeding siblings: | NodeDiscriminator | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -1146,7 +1219,7 @@ Feature: Create node specialization | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | | parentNodeAggregateId | null | - Then I expect the graph projection to consist of exactly 14 nodes + And I expect the graph projection to consist of exactly 14 nodes And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"general"} to exist in the content graph And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"source"} to exist in the content graph @@ -1177,9 +1250,10 @@ Feature: Create node specialization And I expect this node aggregate to occupy dimension space points [{"example":"general"}] And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + When I am in workspace "live" and dimension space point {"example":"source"} Then I expect the subgraph projection to consist of exactly 11 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -1193,7 +1267,7 @@ Feature: Create node specialization | 3 | invariable-mc-nodeface | | 2 | youngest-mc-nodeface | | 1 | sir-nodeward-nodington-iii | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | @@ -1202,6 +1276,7 @@ Feature: Create node specialization | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: @@ -1240,6 +1315,7 @@ Feature: Create node specialization | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -1249,6 +1325,7 @@ Feature: Create node specialization | NodeDiscriminator | | cs-identifier;nody-mc-nodeface;{"example":"source"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -1258,9 +1335,10 @@ Feature: Create node specialization | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have no succeeding siblings + When I am in workspace "live" and dimension space point {"example":"general"} Then I expect the subgraph projection to consist of exactly 11 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -1274,13 +1352,15 @@ Feature: Create node specialization | 3 | nodewyn-tetherton | | 4 | nodimer-tetherton | | 3 | invariable-mc-nodeface | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: @@ -1288,6 +1368,7 @@ Feature: Create node specialization | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -1296,6 +1377,7 @@ Feature: Create node specialization | NodeDiscriminator | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | @@ -1309,6 +1391,7 @@ Feature: Create node specialization | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -1317,6 +1400,7 @@ Feature: Create node specialization And I expect this node to have the following succeeding siblings: | NodeDiscriminator | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -1325,9 +1409,10 @@ Feature: Create node specialization | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have no succeeding siblings + When I am in workspace "live" and dimension space point {"example":"spec"} Then I expect the subgraph projection to consist of exactly 10 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -1340,7 +1425,8 @@ Feature: Create node specialization | 4 | nodimer-tetherton | | 3 | invariable-mc-nodeface | | 1 | sir-nodeward-nodington-iii | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | @@ -1394,9 +1480,10 @@ Feature: Create node specialization And I expect node aggregate identifier "youngest-mc-nodeface" to lead to no node + When I am in workspace "live" and dimension space point {"example":"peer"} Then I expect the subgraph projection to consist of exactly 11 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -1410,13 +1497,15 @@ Feature: Create node specialization | 3 | nodewyn-tetherton | | 4 | nodimer-tetherton | | 3 | invariable-mc-nodeface | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: @@ -1533,9 +1622,10 @@ Feature: Create node specialization And I expect this node aggregate to occupy dimension space points [{"example":"general"}] And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + When I am in workspace "live" and dimension space point {"example":"source"} Then I expect the subgraph projection to consist of exactly 11 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -1549,7 +1639,8 @@ Feature: Create node specialization | 3 | invariable-mc-nodeface | | 2 | youngest-mc-nodeface | | 1 | sir-nodeward-nodington-iii | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | @@ -1557,6 +1648,7 @@ Feature: Create node specialization | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: @@ -1615,9 +1707,10 @@ Feature: Create node specialization | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have no succeeding siblings + When I am in workspace "live" and dimension space point {"example":"general"} Then I expect the subgraph projection to consist of exactly 11 nodes - Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: | Level | nodeAggregateId | | 0 | lady-eleonode-rootford | @@ -1631,13 +1724,15 @@ Feature: Create node specialization | 3 | nodewyn-tetherton | | 4 | nodimer-tetherton | | 3 | invariable-mc-nodeface | - Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} And I expect this node to have no preceding siblings And I expect this node to have the following succeeding siblings: @@ -1645,6 +1740,7 @@ Feature: Create node specialization | cs-identifier;elder-mc-nodeface;{"example":"general"} | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -1653,6 +1749,7 @@ Feature: Create node specialization | NodeDiscriminator | | cs-identifier;younger-mc-nodeface;{"example":"general"} | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} And I expect this node to have the following child nodes: | Name | NodeDiscriminator | @@ -1666,6 +1763,7 @@ Feature: Create node specialization | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -1674,6 +1772,7 @@ Feature: Create node specialization And I expect this node to have the following succeeding siblings: | NodeDiscriminator | | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} And I expect this node to have the following preceding siblings: | NodeDiscriminator | @@ -1682,6 +1781,7 @@ Feature: Create node specialization | cs-identifier;eldest-mc-nodeface;{"example":"general"} | And I expect this node to have no succeeding siblings + When I am in workspace "live" and dimension space point {"example":"spec"} Then I expect the subgraph projection to consist of exactly 10 nodes Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} @@ -1751,6 +1851,375 @@ Feature: Create node specialization And I expect node aggregate identifier "youngest-mc-nodeface" to lead to no node + + When I am in workspace "live" and dimension space point {"example":"peer"} + Then I expect the subgraph projection to consist of exactly 11 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + Scenario: Create specialization variant to a new parent after the last of its new siblings, which does not exist in all variants + Given the command RemoveNodeAggregate is executed with payload: + | Key | Value | + | nodeAggregateId | "youngest-mc-nodeface" | + | coveredDimensionSpacePoint | {"example":"spec"} | + | nodeVariantSelectionStrategy | "allSpecializations" | + + When the command CreateNodeVariant is executed with payload: + | Key | Value | + | nodeAggregateId | "nody-mc-nodeface" | + | sourceOrigin | {"example":"general"} | + | targetOrigin | {"example":"source"} | + | parentNodeAggregateId | "sir-david-nodenborough" | + | precedingSiblingNodeAggregateId | "youngest-mc-nodeface" | + Then I expect exactly 16 events to be published on stream "ContentStream:cs-identifier" + And event at index 13 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nody-mc-nodeface" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | + | parentNodeAggregateId | "sir-david-nodenborough" | + And event at index 14 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodewyn-tetherton" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"invariable-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"invariable-mc-nodeface"}] | + | parentNodeAggregateId | null | + And event at index 15 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodimer-tetherton" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | + | parentNodeAggregateId | null | + + Then I expect the graph projection to consist of exactly 14 nodes + And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;eldest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;elder-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;younger-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;youngest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;invariable-mc-nodeface;{"example":"general"} to exist in the content graph + + When I am in workspace "live" + + And I expect the node aggregate "nody-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "nodewyn-tetherton" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "nodimer-tetherton" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "invariable-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + + When I am in workspace "live" and dimension space point {"example":"source"} + Then I expect the subgraph projection to consist of exactly 11 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + + When I am in workspace "live" and dimension space point {"example":"general"} + Then I expect the subgraph projection to consist of exactly 11 nodes + Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + Then I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + + When I am in workspace "live" and dimension space point {"example":"spec"} + Then I expect the subgraph projection to consist of exactly 10 nodes + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to no node + + When I am in workspace "live" and dimension space point {"example":"peer"} Then I expect the subgraph projection to consist of exactly 11 nodes Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} From 7340d532ad37a4a39f555ec49132a61780904caf Mon Sep 17 00:00:00 2001 From: Bernhard Schmitt Date: Sun, 7 Dec 2025 10:01:10 +0100 Subject: [PATCH 9/9] 5054 - WIP adjust node generalization --- .../DoctrineDbalContentGraphProjection.php | 10 +- .../Projection/Feature/NodeVariation.php | 10 +- ...odeGeneralizationVariant-NewParent.feature | 1603 +++++++++++++++++ .../Feature/Common/NodeVariationInternals.php | 102 +- .../NodeGeneralizationVariantWasCreated.php | 31 +- 5 files changed, 1713 insertions(+), 43 deletions(-) create mode 100644 Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/06-CreateNodeGeneralizationVariant-NewParent.feature diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphProjection.php b/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphProjection.php index 509e8a89e2b..87f9e699595 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphProjection.php +++ b/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphProjection.php @@ -464,7 +464,15 @@ private function whenNodeAggregateWithNodeWasCreated(NodeAggregateWithNodeWasCre private function whenNodeGeneralizationVariantWasCreated(NodeGeneralizationVariantWasCreated $event, EventEnvelope $eventEnvelope): void { - $this->createNodeGeneralizationVariant($event->contentStreamId, $event->nodeAggregateId, $event->sourceOrigin, $event->generalizationOrigin, $event->variantSucceedingSiblings, $eventEnvelope); + $this->createNodeGeneralizationVariant( + contentStreamId: $event->contentStreamId, + nodeAggregateId: $event->nodeAggregateId, + sourceOrigin: $event->sourceOrigin, + generalizationOrigin: $event->generalizationOrigin, + parentNodeAggregateId: $event->parentNodeAggregateId, + generalizationSiblings: $event->variantSucceedingSiblings, + eventEnvelope: $eventEnvelope, + ); } private function whenNodePeerVariantWasCreated(NodePeerVariantWasCreated $event, EventEnvelope $eventEnvelope): void diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/Feature/NodeVariation.php b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/Feature/NodeVariation.php index 04edb7f8438..d951a7f4d23 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/Feature/NodeVariation.php +++ b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/Feature/NodeVariation.php @@ -170,7 +170,15 @@ private function createNodeSpecializationVariant( ); } - public function createNodeGeneralizationVariant(ContentStreamId $contentStreamId, NodeAggregateId $nodeAggregateId, OriginDimensionSpacePoint $sourceOrigin, OriginDimensionSpacePoint $generalizationOrigin, InterdimensionalSiblings $variantSucceedingSiblings, EventEnvelope $eventEnvelope): void + public function createNodeGeneralizationVariant( + ContentStreamId $contentStreamId, + NodeAggregateId $nodeAggregateId, + OriginDimensionSpacePoint $sourceOrigin, + OriginDimensionSpacePoint $generalizationOrigin, + ?NodeAggregateId $parentNodeAggregateId, + InterdimensionalSiblings $variantSucceedingSiblings, + EventEnvelope $eventEnvelope + ): void { // do the generalization $sourceNode = $this->projectionContentGraph->findNodeInAggregate( diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/06-CreateNodeGeneralizationVariant-NewParent.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/06-CreateNodeGeneralizationVariant-NewParent.feature new file mode 100644 index 00000000000..d8c95556384 --- /dev/null +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/03-NodeVariation/06-CreateNodeGeneralizationVariant-NewParent.feature @@ -0,0 +1,1603 @@ +@contentrepository @adapters=DoctrineDBAL,Postgres +Feature: Create node specialization + As a user of the CR I want to create a variant of a node within an aggregate + to a more general dimension space point + and assign that variant to a different parent + * before the first of its new siblings + * before the first of its new siblings - which does not exist in all variants + * before one of its new siblings, which is partially the first + * before one of its new siblings, which is not the first + * before one of its new siblings, which is not the first and does not exist in all variants + * before one of its new siblings, which is the last and does not exist in all variants + * after one of its new siblings, which is not the last + * after one of its new siblings, which is partially the last + * after the last of its new siblings + * after the last of its new siblings, which does not exist in all variants + + Background: + Given using the following content dimensions: + | Identifier | Values | Generalizations | + | example | general, source, spec, peer | spec->source->general, peer->general | + And using the following node types: + """yaml + 'Neos.ContentRepository.Testing:Document': + childNodes: + tethered-node: + type: 'Neos.ContentRepository.Testing:Tethered' + 'Neos.ContentRepository.Testing:Tethered': + childNodes: + tethered-leaf: + type: 'Neos.ContentRepository.Testing:TetheredLeaf' + 'Neos.ContentRepository.Testing:TetheredLeaf': [] + 'Neos.ContentRepository.Testing:LeafDocument': [] + """ + And using identifier "default", I define a content repository + And I am in content repository "default" + And I am user identified by "initiating-user-identifier" + And the command CreateRootWorkspace is executed with payload: + | Key | Value | + | workspaceName | "live" | + | newContentStreamId | "cs-identifier" | + And I am in workspace "live" and dimension space point {"example":"source"} + And the command CreateRootNodeAggregateWithNode is executed with payload: + | Key | Value | + | nodeAggregateId | "lady-eleonode-rootford" | + | nodeTypeName | "Neos.ContentRepository:Root" | + And the following CreateNodeAggregateWithNode commands are executed: + | nodeAggregateId | originDimensionSpacePoint | nodeName | nodeTypeName | parentNodeAggregateId | tetheredDescendantNodeAggregateIds | + | sir-david-nodenborough | {"example":"general"} | parent-document | Neos.ContentRepository.Testing:LeafDocument | lady-eleonode-rootford | {} | + | eldest-mc-nodeface | {"example":"general"} | eldest-document | Neos.ContentRepository.Testing:LeafDocument | sir-david-nodenborough | {} | + | elder-mc-nodeface | {"example":"general"} | elder-document | Neos.ContentRepository.Testing:LeafDocument | sir-david-nodenborough | {} | + | younger-mc-nodeface | {"example":"general"} | younger-document | Neos.ContentRepository.Testing:LeafDocument | sir-david-nodenborough | {} | + | youngest-mc-nodeface | {"example":"general"} | youngest-document | Neos.ContentRepository.Testing:LeafDocument | sir-david-nodenborough | {} | + | sir-nodeward-nodington-iii | {"example":"source"} | esquire | Neos.ContentRepository.Testing:LeafDocument | lady-eleonode-rootford | {} | + | nody-mc-nodeface | {"example":"source"} | document | Neos.ContentRepository.Testing:Document | sir-nodeward-nodington-iii | {"tethered-node": "nodewyn-tetherton", "tethered-node/tethered-leaf": "nodimer-tetherton"} | + | invariable-mc-nodeface | {"example":"source"} | invariable | Neos.ContentRepository.Testing:LeafDocument | nody-mc-nodeface | {} | + + Scenario: Create generalization variant to a new parent before the first of its new siblings + When the command CreateNodeVariant is executed with payload: + | Key | Value | + | nodeAggregateId | "nody-mc-nodeface" | + | sourceOrigin | {"example":"source"} | + | targetOrigin | {"example":"general"} | + | parentNodeAggregateId | "sir-david-nodenborough" | + | succeedingSiblingNodeAggregateId | "eldest-mc-nodeface" | + Then I expect exactly 15 events to be published on stream "ContentStream:cs-identifier" + And event at index 12 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nody-mc-nodeface" | + | sourceOrigin | {"example":"source"} | + | specializationOrigin | {"example":"general"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"general"},"nodeAggregateId":"eldest-mc-nodeface"},{"dimensionSpacePoint":{"example":"peer"},"nodeAggregateId":"eldest-mc-nodeface"}] | + | parentNodeAggregateId | "sir-david-nodenborough" | + And event at index 13 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodewyn-tetherton" | + | sourceOrigin | {"example":"source"} | + | specializationOrigin | {"example":"general"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"general"},"nodeAggregateId":"invariable-mc-nodeface"},{"dimensionSpacePoint":{"example":"peer"},"nodeAggregateId":"invariable-mc-nodeface"}] | + | parentNodeAggregateId | null | + And event at index 14 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodimer-tetherton" | + | sourceOrigin | {"example":"source"} | + | specializationOrigin | {"example":"general"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | + | parentNodeAggregateId | null | + + And I expect the graph projection to consist of exactly 14 nodes + And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;eldest-mc-nodeface;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;elder-mc-nodeface;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;younger-mc-nodeface;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;youngest-mc-nodeface;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;invariable-mc-nodeface;{"example":"source"} to exist in the content graph + + When I am in workspace "live" + + And I expect the node aggregate "nody-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "nodewyn-tetherton" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "nodimer-tetherton" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "invariable-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + + When I am in workspace "live" and dimension space point {"example":"source"} + Then I expect the subgraph projection to consist of exactly 11 nodes + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + And I expect this node to have no succeeding siblings + + + When I am in workspace "live" and dimension space point {"example":"general"} + Then I expect the subgraph projection to consist of exactly 11 nodes + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + + When I am in workspace "live" and dimension space point {"example":"spec"} + Then I expect the subgraph projection to consist of exactly 11 nodes + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + And I expect this node to have no succeeding siblings + + + When I am in workspace "live" and dimension space point {"example":"peer"} + Then I expect the subgraph projection to consist of exactly 11 nodes + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + # Scenario: Create specialization variant to a new parent before the first of its new siblings - which does not exist in all variants + # As this requires deletion of a specialization: @see ../07-NodeRemoval/07-CreateNodeSpecializationVariant-NewParent-AfterDeletion.feature + + # Scenario: Create specialization variant to a new parent before one of its new siblings, which is partially the first + # As this requires deletion of a specialization: @see ../07-NodeRemoval/07-CreateNodeSpecializationVariant-NewParent-AfterDeletion.feature + + Scenario: Create specialization variant to a new parent before one of its new siblings, which is not the first + When the command CreateNodeVariant is executed with payload: + | Key | Value | + | nodeAggregateId | "nody-mc-nodeface" | + | sourceOrigin | {"example":"general"} | + | targetOrigin | {"example":"source"} | + | parentNodeAggregateId | "sir-david-nodenborough" | + | succeedingSiblingNodeAggregateId | "elder-mc-nodeface" | + Then I expect exactly 15 events to be published on stream "ContentStream:cs-identifier" + And event at index 12 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nody-mc-nodeface" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"elder-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"elder-mc-nodeface"}] | + | parentNodeAggregateId | "sir-david-nodenborough" | + And event at index 13 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodewyn-tetherton" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"invariable-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"invariable-mc-nodeface"}] | + | parentNodeAggregateId | null | + And event at index 14 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodimer-tetherton" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | + | parentNodeAggregateId | null | + + And I expect the graph projection to consist of exactly 14 nodes + And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;eldest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;elder-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;younger-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;youngest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;invariable-mc-nodeface;{"example":"general"} to exist in the content graph + + When I am in workspace "live" + + And I expect the node aggregate "nody-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "nodewyn-tetherton" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "nodimer-tetherton" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "invariable-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + When I am in workspace "live" and dimension space point {"example":"source"} + Then I expect the subgraph projection to consist of exactly 11 nodes + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + + When I am in workspace "live" and dimension space point {"example":"general"} + Then I expect the subgraph projection to consist of exactly 11 nodes + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + + When I am in workspace "live" and dimension space point {"example":"spec"} + Then I expect the subgraph projection to consist of exactly 11 nodes + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + + When I am in workspace "live" and dimension space point {"example":"peer"} + Then I expect the subgraph projection to consist of exactly 11 nodes + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + # Scenario: Create specialization variant to a new parent before one of its new siblings, which is not the first and does not exist in all variants + # As this requires deletion of a specialization: @see ../07-NodeRemoval/07-CreateNodeSpecializationVariant-NewParent-AfterDeletion.feature + + # Scenario: Create specialization variant to a new parent before one of its new siblings, which is the last and does not exist in all variants + # As this requires deletion of a specialization: @see ../07-NodeRemoval/07-CreateNodeSpecializationVariant-NewParent-AfterDeletion.feature + + Scenario: Create specialization variant to a new parent after one of its new siblings, which is not the last + When the command CreateNodeVariant is executed with payload: + | Key | Value | + | nodeAggregateId | "nody-mc-nodeface" | + | sourceOrigin | {"example":"general"} | + | targetOrigin | {"example":"source"} | + | parentNodeAggregateId | "sir-david-nodenborough" | + | precedingSiblingNodeAggregateId | "younger-mc-nodeface" | + Then I expect exactly 15 events to be published on stream "ContentStream:cs-identifier" + And event at index 12 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nody-mc-nodeface" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"youngest-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"youngest-mc-nodeface"}] | + | parentNodeAggregateId | "sir-david-nodenborough" | + And event at index 13 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodewyn-tetherton" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"invariable-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"invariable-mc-nodeface"}] | + | parentNodeAggregateId | null | + And event at index 14 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodimer-tetherton" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | + | parentNodeAggregateId | null | + + And I expect the graph projection to consist of exactly 14 nodes + And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;eldest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;elder-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;younger-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;youngest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;invariable-mc-nodeface;{"example":"general"} to exist in the content graph + + When I am in workspace "live" + + And I expect the node aggregate "nody-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "nodewyn-tetherton" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "nodimer-tetherton" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "invariable-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + + When I am in workspace "live" and dimension space point {"example":"source"} + Then I expect the subgraph projection to consist of exactly 11 nodes + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + + When I am in workspace "live" and dimension space point {"example":"general"} + Then I expect the subgraph projection to consist of exactly 11 nodes + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + + When I am in workspace "live" and dimension space point {"example":"spec"} + Then I expect the subgraph projection to consist of exactly 11 nodes + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + + When I am in workspace "live" and dimension space point {"example":"peer"} + Then I expect the subgraph projection to consist of exactly 11 nodes + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + # Scenario: Create specialization variant to a new parent after one of its new siblings, which is partially the last + # As this is implemented via deletion of a specialization: @see ../07-NodeRemoval/07-CreateNodeSpecializationVariant-NewParent-AfterDeletion.feature + + Scenario: Create specialization variant to a new parent after the last of its new siblings + When the command CreateNodeVariant is executed with payload: + | Key | Value | + | nodeAggregateId | "nody-mc-nodeface" | + | sourceOrigin | {"example":"general"} | + | targetOrigin | {"example":"source"} | + | parentNodeAggregateId | "sir-david-nodenborough" | + | precedingSiblingNodeAggregateId | "youngest-mc-nodeface" | + Then I expect exactly 15 events to be published on stream "ContentStream:cs-identifier" + And event at index 12 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nody-mc-nodeface" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | + | parentNodeAggregateId | "sir-david-nodenborough" | + And event at index 13 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodewyn-tetherton" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":"invariable-mc-nodeface"},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":"invariable-mc-nodeface"}] | + | parentNodeAggregateId | null | + And event at index 14 is of type "NodeSpecializationVariantWasCreated" with payload: + | Key | Expected | + | contentStreamId | "cs-identifier" | + | nodeAggregateId | "nodimer-tetherton" | + | sourceOrigin | {"example":"general"} | + | specializationOrigin | {"example":"source"} | + | specializationSiblings | [{"dimensionSpacePoint":{"example":"source"},"nodeAggregateId":null},{"dimensionSpacePoint":{"example":"spec"},"nodeAggregateId":null}] | + | parentNodeAggregateId | null | + + And I expect the graph projection to consist of exactly 14 nodes + And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nody-mc-nodeface;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nodewyn-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;nodimer-tetherton;{"example":"source"} to exist in the content graph + And I expect a node identified by cs-identifier;eldest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;elder-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;younger-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;youngest-mc-nodeface;{"example":"general"} to exist in the content graph + And I expect a node identified by cs-identifier;invariable-mc-nodeface;{"example":"general"} to exist in the content graph + + When I am in workspace "live" + + And I expect the node aggregate "nody-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "nodewyn-tetherton" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "nodimer-tetherton" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"},{"example":"source"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + And I expect the node aggregate "invariable-mc-nodeface" to exist + And I expect this node aggregate to occupy dimension space points [{"example":"general"}] + And I expect this node aggregate to cover dimension space points [{"example":"general"},{"example":"source"},{"example":"spec"},{"example":"peer"}] + + + When I am in workspace "live" and dimension space point {"example":"source"} + Then I expect the subgraph projection to consist of exactly 11 nodes + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + + When I am in workspace "live" and dimension space point {"example":"general"} + Then I expect the subgraph projection to consist of exactly 11 nodes + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + + When I am in workspace "live" and dimension space point {"example":"spec"} + Then I expect the subgraph projection to consist of exactly 11 nodes + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + | document | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"source"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"source"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"source"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"source"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;nody-mc-nodeface;{"example":"source"} | + + + When I am in workspace "live" and dimension space point {"example":"peer"} + Then I expect the subgraph projection to consist of exactly 11 nodes + And I expect node aggregate identifier "lady-eleonode-rootford" to lead to node cs-identifier;lady-eleonode-rootford;{} + And the subtree for node aggregate "lady-eleonode-rootford" with node types "" and 4 levels deep should be: + | Level | nodeAggregateId | + | 0 | lady-eleonode-rootford | + | 1 | sir-david-nodenborough | + | 2 | eldest-mc-nodeface | + | 2 | elder-mc-nodeface | + | 2 | younger-mc-nodeface | + | 2 | youngest-mc-nodeface | + | 1 | sir-nodeward-nodington-iii | + | 2 | nody-mc-nodeface | + | 3 | nodewyn-tetherton | + | 4 | nodimer-tetherton | + | 3 | invariable-mc-nodeface | + + And I expect node aggregate identifier "sir-david-nodenborough" to lead to node cs-identifier;sir-david-nodenborough;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | eldest-document | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + | elder-document | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | younger-document | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | youngest-document | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "eldest-mc-nodeface" to lead to node cs-identifier;eldest-mc-nodeface;{"example":"general"} + And I expect this node to have no preceding siblings + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "elder-mc-nodeface" to lead to node cs-identifier;elder-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "nody-mc-nodeface" to lead to node cs-identifier;nody-mc-nodeface;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-node | cs-identifier;nodewyn-tetherton;{"example":"general"} | + | invariable | cs-identifier;invariable-mc-nodeface;{"example":"general"} | + And I expect this node to have no preceding siblings + And I expect this node to have no succeeding siblings + And I expect node aggregate identifier "nodewyn-tetherton" to lead to node cs-identifier;nodewyn-tetherton;{"example":"general"} + And I expect this node to have the following child nodes: + | Name | NodeDiscriminator | + | tethered-leaf | cs-identifier;nodimer-tetherton;{"example":"general"} | + And I expect node aggregate identifier "nodimer-tetherton" to lead to node cs-identifier;nodimer-tetherton;{"example":"general"} + And I expect node aggregate identifier "invariable-mc-nodeface" to lead to node cs-identifier;invariable-mc-nodeface;{"example":"general"} + + And I expect node aggregate identifier "younger-mc-nodeface" to lead to node cs-identifier;younger-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have the following succeeding siblings: + | NodeDiscriminator | + | cs-identifier;youngest-mc-nodeface;{"example":"general"} | + + And I expect node aggregate identifier "youngest-mc-nodeface" to lead to node cs-identifier;youngest-mc-nodeface;{"example":"general"} + And I expect this node to have the following preceding siblings: + | NodeDiscriminator | + | cs-identifier;younger-mc-nodeface;{"example":"general"} | + | cs-identifier;elder-mc-nodeface;{"example":"general"} | + | cs-identifier;eldest-mc-nodeface;{"example":"general"} | + And I expect this node to have no succeeding siblings + + # Scenario: Create specialization variant to a new parent after the last of its new siblings, which does not exist in all variants + # As this is implemented via deletion of a specialization: @see ../07-NodeRemoval/07-CreateNodeSpecializationVariant-NewParent-AfterDeletion.feature diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/NodeVariationInternals.php b/Neos.ContentRepository.Core/Classes/Feature/Common/NodeVariationInternals.php index f08ede109a7..605d9db1380 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/Common/NodeVariationInternals.php +++ b/Neos.ContentRepository.Core/Classes/Feature/Common/NodeVariationInternals.php @@ -64,10 +64,13 @@ protected function createEventsForVariations( succeedingSiblingNodeAggregateId: $succeedingSiblingNodeAggregateId, ), DimensionSpace\VariantType::TYPE_GENERALIZATION => $this->handleCreateNodeGeneralizationVariant( - $contentGraph, - $sourceOrigin, - $targetOrigin, - $nodeAggregate + contentGraph: $contentGraph, + sourceOrigin: $sourceOrigin, + targetOrigin: $targetOrigin, + nodeAggregate: $nodeAggregate, + parentNodeAggregateId: $parentNodeAggregateId, + precedingSiblingNodeAggregateId: $precedingSiblingNodeAggregateId, + succeedingSiblingNodeAggregateId: $succeedingSiblingNodeAggregateId, ), default => $this->handleCreateNodePeerVariant( $contentGraph, @@ -189,16 +192,22 @@ protected function handleCreateNodeGeneralizationVariant( ContentGraphInterface $contentGraph, OriginDimensionSpacePoint $sourceOrigin, OriginDimensionSpacePoint $targetOrigin, - NodeAggregate $nodeAggregate + NodeAggregate $nodeAggregate, + ?NodeAggregateId $parentNodeAggregateId, + ?NodeAggregateId $precedingSiblingNodeAggregateId, + ?NodeAggregateId $succeedingSiblingNodeAggregateId, ): Events { $generalizationVisibility = $this->calculateEffectiveVisibility($targetOrigin, $nodeAggregate); $events = $this->collectNodeGeneralizationVariantsThatWillHaveBeenCreated( - $contentGraph, - $sourceOrigin, - $targetOrigin, - $nodeAggregate, - $generalizationVisibility, - [] + contentGraph: $contentGraph, + sourceOrigin: $sourceOrigin, + targetOrigin: $targetOrigin, + nodeAggregate: $nodeAggregate, + generalizationVisibility: $generalizationVisibility, + parentNodeAggregateId: $parentNodeAggregateId, + precedingSiblingNodeAggregateId: $precedingSiblingNodeAggregateId, + succeedingSiblingNodeAggregateId: $succeedingSiblingNodeAggregateId, + events: [], ); return Events::fromArray($events); @@ -214,19 +223,41 @@ protected function collectNodeGeneralizationVariantsThatWillHaveBeenCreated( OriginDimensionSpacePoint $targetOrigin, NodeAggregate $nodeAggregate, DimensionSpacePointSet $generalizationVisibility, - array $events + ?NodeAggregateId $parentNodeAggregateId, + ?NodeAggregateId $precedingSiblingNodeAggregateId, + ?NodeAggregateId $succeedingSiblingNodeAggregateId, + array $events, ): array { + if (!$parentNodeAggregateId && !$succeedingSiblingNodeAggregateId && !$precedingSiblingNodeAggregateId) { + // preserve legacy behavior: this means the variant is to be created in place + $originSubgraph = $contentGraph->getSubgraph( + $sourceOrigin->toDimensionSpacePoint(), + VisibilityConstraints::createEmpty() + ); + $precedingSiblingNodeAggregateId = $originSubgraph->findPrecedingSiblingNodes( + siblingNodeAggregateId: $nodeAggregate->nodeAggregateId, + filter: FindPrecedingSiblingNodesFilter::create(pagination: Pagination::fromLimitAndOffset(1, 0)) + )->first()?->aggregateId; + $succeedingSiblingNodeAggregateId = $originSubgraph->findSucceedingSiblingNodes( + siblingNodeAggregateId: $nodeAggregate->nodeAggregateId, + filter: FindSucceedingSiblingNodesFilter::create(pagination: Pagination::fromLimitAndOffset(1, 0)) + )->first()?->aggregateId; + } $events[] = new NodeGeneralizationVariantWasCreated( - $contentGraph->getWorkspaceName(), - $contentGraph->getContentStreamId(), - $nodeAggregate->nodeAggregateId, - $sourceOrigin, - $targetOrigin, - $this->resolveInterdimensionalSiblingsForVariation( - $contentGraph, - $nodeAggregate->nodeAggregateId, - $sourceOrigin, - $generalizationVisibility + workspaceName: $contentGraph->getWorkspaceName(), + contentStreamId: $contentGraph->getContentStreamId(), + nodeAggregateId: $nodeAggregate->nodeAggregateId, + sourceOrigin: $sourceOrigin, + generalizationOrigin: $targetOrigin, + variantSucceedingSiblings: $this->resolveInterdimensionalSiblings( + contentGraph: $contentGraph, + referenceDimensionSpacePoint: $targetOrigin->toDimensionSpacePoint(), + affectedDimensionSpacePoints: $generalizationVisibility, + nodeAggregateId: $nodeAggregate->nodeAggregateId, + parentNodeAggregateId: $parentNodeAggregateId, + precedingSiblingNodeAggregateId: $precedingSiblingNodeAggregateId, + succeedingSiblingNodeAggregateId: $succeedingSiblingNodeAggregateId, + completeSet: true, ) ); @@ -235,13 +266,28 @@ protected function collectNodeGeneralizationVariantsThatWillHaveBeenCreated( $nodeAggregate->nodeAggregateId ) as $tetheredChildNodeAggregate ) { + $originSubgraph = $contentGraph->getSubgraph( + $sourceOrigin->toDimensionSpacePoint(), + VisibilityConstraints::createEmpty() + ); + $originChildPrecedingSibling = $originSubgraph->findPrecedingSiblingNodes( + siblingNodeAggregateId: $tetheredChildNodeAggregate->nodeAggregateId, + filter: FindPrecedingSiblingNodesFilter::create(pagination: Pagination::fromLimitAndOffset(1, 0)) + )->first(); + $originChildSucceedingSibling = $originSubgraph->findSucceedingSiblingNodes( + siblingNodeAggregateId: $tetheredChildNodeAggregate->nodeAggregateId, + filter: FindSucceedingSiblingNodesFilter::create(pagination: Pagination::fromLimitAndOffset(1, 0)) + )->first(); $events = $this->collectNodeGeneralizationVariantsThatWillHaveBeenCreated( - $contentGraph, - $sourceOrigin, - $targetOrigin, - $tetheredChildNodeAggregate, - $generalizationVisibility, - $events + contentGraph: $contentGraph, + sourceOrigin: $sourceOrigin, + targetOrigin: $targetOrigin, + nodeAggregate: $tetheredChildNodeAggregate, + generalizationVisibility: $generalizationVisibility, + parentNodeAggregateId: null, + precedingSiblingNodeAggregateId: $originChildPrecedingSibling?->aggregateId, + succeedingSiblingNodeAggregateId: $originChildSucceedingSibling?->aggregateId, + events: $events, ); } diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Event/NodeGeneralizationVariantWasCreated.php b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Event/NodeGeneralizationVariantWasCreated.php index dbbea290d28..41f7e15abfe 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Event/NodeGeneralizationVariantWasCreated.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Event/NodeGeneralizationVariantWasCreated.php @@ -45,6 +45,7 @@ public function __construct( public OriginDimensionSpacePoint $sourceOrigin, public OriginDimensionSpacePoint $generalizationOrigin, public InterdimensionalSiblings $variantSucceedingSiblings, + public ?NodeAggregateId $parentNodeAggregateId, ) { } @@ -65,29 +66,33 @@ public function getWorkspaceName(): WorkspaceName public function withWorkspaceNameAndContentStreamId(WorkspaceName $targetWorkspaceName, ContentStreamId $contentStreamId): self { - return new NodeGeneralizationVariantWasCreated( - $targetWorkspaceName, - $contentStreamId, - $this->nodeAggregateId, - $this->sourceOrigin, - $this->generalizationOrigin, - $this->variantSucceedingSiblings, + return new self( + workspaceName: $targetWorkspaceName, + contentStreamId: $contentStreamId, + nodeAggregateId: $this->nodeAggregateId, + sourceOrigin: $this->sourceOrigin, + generalizationOrigin: $this->generalizationOrigin, + variantSucceedingSiblings: $this->variantSucceedingSiblings, + parentNodeAggregateId: $this->parentNodeAggregateId, ); } public static function fromArray(array $values): self { return new self( - WorkspaceName::fromString($values['workspaceName']), - ContentStreamId::fromString($values['contentStreamId']), - NodeAggregateId::fromString($values['nodeAggregateId']), - OriginDimensionSpacePoint::fromArray($values['sourceOrigin']), - OriginDimensionSpacePoint::fromArray($values['generalizationOrigin']), - array_key_exists('variantSucceedingSiblings', $values) + workspaceName: WorkspaceName::fromString($values['workspaceName']), + contentStreamId: ContentStreamId::fromString($values['contentStreamId']), + nodeAggregateId: NodeAggregateId::fromString($values['nodeAggregateId']), + sourceOrigin: OriginDimensionSpacePoint::fromArray($values['sourceOrigin']), + generalizationOrigin: OriginDimensionSpacePoint::fromArray($values['generalizationOrigin']), + variantSucceedingSiblings: array_key_exists('variantSucceedingSiblings', $values) ? InterdimensionalSiblings::fromArray($values['variantSucceedingSiblings']) : InterdimensionalSiblings::fromDimensionSpacePointSetWithoutSucceedingSiblings( DimensionSpacePointSet::fromArray($values['generalizationCoverage']), ), + parentNodeAggregateId: is_string($parentNodeAggregateId = ($values['parentNodeAggregateId'] ?? null)) + ? NodeAggregateId::fromString($parentNodeAggregateId) + : null, ); }