From a401c2a2d696b62d73f0bcccc7fe5b28180ccf99 Mon Sep 17 00:00:00 2001 From: Romain Canon Date: Fri, 5 Aug 2022 11:57:25 +0200 Subject: [PATCH] fix: handle invalid nodes recursively --- src/Mapper/Tree/Builder/TreeNode.php | 3 +- src/Mapper/Tree/Node.php | 4 +++ .../Unit/Mapper/Tree/Builder/TreeNodeTest.php | 27 ++++++---------- tests/Unit/Mapper/Tree/NodeTest.php | 31 +++++++++++++++++++ 4 files changed, 46 insertions(+), 19 deletions(-) diff --git a/src/Mapper/Tree/Builder/TreeNode.php b/src/Mapper/Tree/Builder/TreeNode.php index f32f7df..5d81761 100644 --- a/src/Mapper/Tree/Builder/TreeNode.php +++ b/src/Mapper/Tree/Builder/TreeNode.php @@ -145,7 +145,8 @@ final class TreeNode } if ($this->valid && ! $this->shell->type()->accepts($this->value)) { - throw new InvalidNodeValue($this->value, $this->shell->type()); + $this->valid = false; + $this->messages[] = new InvalidNodeValue($this->value, $this->shell->type()); } } diff --git a/src/Mapper/Tree/Node.php b/src/Mapper/Tree/Node.php index 5ba90c2..c73d799 100644 --- a/src/Mapper/Tree/Node.php +++ b/src/Mapper/Tree/Node.php @@ -68,6 +68,10 @@ final class Node $this->messages[] = $message; $this->isValid = $this->isValid && ! $message->isError(); } + + foreach ($this->children as $child) { + $this->isValid = $this->isValid && $child->isValid(); + } } public function isRoot(): bool diff --git a/tests/Unit/Mapper/Tree/Builder/TreeNodeTest.php b/tests/Unit/Mapper/Tree/Builder/TreeNodeTest.php index a5fce42..76f72ba 100644 --- a/tests/Unit/Mapper/Tree/Builder/TreeNodeTest.php +++ b/tests/Unit/Mapper/Tree/Builder/TreeNodeTest.php @@ -7,7 +7,6 @@ namespace CuyZ\Valinor\Tests\Unit\Mapper\Tree\Builder; use AssertionError; use CuyZ\Valinor\Mapper\Tree\Builder\TreeNode; use CuyZ\Valinor\Mapper\Tree\Exception\DuplicatedNodeChild; -use CuyZ\Valinor\Mapper\Tree\Exception\InvalidNodeValue; use CuyZ\Valinor\Mapper\Tree\Shell; use CuyZ\Valinor\Tests\Fake\Definition\FakeAttributes; use CuyZ\Valinor\Tests\Fake\Mapper\Tree\Builder\FakeTreeNode; @@ -37,15 +36,13 @@ final class TreeNodeTest extends TestCase self::assertSame('some value', $node->mappedValue()); } - public function test_node_leaf_with_incorrect_value_throws_exception(): void + public function test_node_leaf_with_incorrect_value_is_invalid(): void { $type = new FakeType(); - $this->expectException(InvalidNodeValue::class); - $this->expectExceptionCode(1630678334); - $this->expectExceptionMessage("Value 'foo' does not match type `{$type->toString()}`."); + $node = FakeTreeNode::leaf($type, 'foo'); - FakeTreeNode::leaf($type, 'foo'); + self::assertFalse($node->isValid()); } public function test_node_branch_values_can_be_retrieved(): void @@ -84,11 +81,9 @@ final class TreeNodeTest extends TestCase { $type = new FakeType(); - $this->expectException(InvalidNodeValue::class); - $this->expectExceptionCode(1630678334); - $this->expectExceptionMessage("Value 'foo' does not match type `{$type->toString()}`."); + $node = FakeTreeNode::branch([], $type, 'foo'); - FakeTreeNode::branch([], $type, 'foo'); + self::assertFalse($node->isValid()); } public function test_node_error_values_can_be_retrieved(): void @@ -133,11 +128,9 @@ final class TreeNodeTest extends TestCase { $type = FakeType::accepting('foo'); - $this->expectException(InvalidNodeValue::class); - $this->expectExceptionCode(1630678334); - $this->expectExceptionMessage("Value 1337 does not match type `{$type->toString()}`."); + $node = FakeTreeNode::leaf($type, 'foo')->withValue(1337); - FakeTreeNode::leaf($type, 'foo')->withValue(1337); + self::assertFalse($node->isValid()); } public function test_node_with_invalid_value_for_object_type_returns_invalid_node(): void @@ -145,11 +138,9 @@ final class TreeNodeTest extends TestCase $object = new stdClass(); $type = FakeObjectType::accepting($object); - $this->expectException(InvalidNodeValue::class); - $this->expectExceptionCode(1630678334); - $this->expectExceptionMessage('Invalid value 1337.'); + $node = FakeTreeNode::leaf($type, $object)->withValue(1337); - FakeTreeNode::leaf($type, $object)->withValue(1337); + self::assertFalse($node->isValid()); } public function test_node_with_messages_returns_node_with_messages(): void diff --git a/tests/Unit/Mapper/Tree/NodeTest.php b/tests/Unit/Mapper/Tree/NodeTest.php index 9f46fd1..85d72da 100644 --- a/tests/Unit/Mapper/Tree/NodeTest.php +++ b/tests/Unit/Mapper/Tree/NodeTest.php @@ -62,6 +62,37 @@ final class NodeTest extends TestCase self::assertSame(false, $node->isValid()); } + public function test_invalid_child_makes_root_node_not_valid(): void + { + $message = new FakeErrorMessage(); + + $node = new Node( + true, + '', + '*root*', + 'string', + true, + 'some source value', + 'some value', + [], + [ + new Node( + false, + 'nodeName', + 'some.node.path', + 'string', + true, + 'some source value', + 'some value', + [$message], + [] + ) + ] + ); + + self::assertSame(false, $node->isValid()); + } + public function test_get_source_value_not_filled_throws_exception(): void { $this->expectException(SourceValueWasNotFilled::class);