77use RuntimeException ;
88use SimpleXMLElement ;
99use Yii ;
10- use yii \base \InvalidArgumentException ;
10+ use yii \base \{ InvalidArgumentException , InvalidConfigException } ;
1111use yii \console \Application ;
12- use yii \db \{ActiveQuery , ActiveRecord , Connection , Query , SchemaBuilderTrait };
12+ use yii \db \{ActiveQuery , ActiveRecord , Connection , Exception , SchemaBuilderTrait };
1313use yii2 \extensions \nestedsets \tests \support \model \{MultipleTree , Tree };
1414use yii2 \extensions \nestedsets \tests \support \stub \EchoMigrateController ;
1515
1616use function array_merge ;
1717use function array_values ;
18+ use function dirname ;
1819use function dom_import_simplexml ;
1920use function file_get_contents ;
21+ use function is_int ;
22+ use function is_string ;
23+ use function ob_get_clean ;
24+ use function ob_implicit_flush ;
25+ use function ob_start ;
2026use function preg_replace ;
2127use function simplexml_load_string ;
2228use function str_replace ;
2329
2430/**
31+ * Base test case for nested sets behavior test suites.
32+ *
33+ * Provides common setup, database management, fixture loading, and assertion utilities for all nested sets tests.
34+ *
35+ * This class centralizes logic for initializing the test environment, managing database state, and verifying tree
36+ * structures, ensuring consistency and reducing duplication across test cases for different database drivers and
37+ * scenarios.
38+ *
39+ * Key features.
40+ * - Assertion helpers for validating node order and query structure.
41+ * - Integration with custom migration and schema management.
42+ * - Shared database connection and fixture directory configuration.
43+ * - Support for both single-tree and multi-tree models.
44+ * - Utilities for creating, resetting, and populating test databases.
45+ * - XML fixture generation and loading for reproducible test data.
46+ *
47+ * @see EchoMigrateController for migration handling.
48+ * @see MultipleTree for multi-tree model.
49+ * @see Tree for single-tree model.
50+ *
2551 * @phpstan-type DataSetType = list<
2652 * array{
2753 * id: int,
3662 * @phpstan-type NodeChildren array<string|array{name: string, children?: array<mixed>}>
3763 * @phpstan-type TreeStructure array<array<mixed>>
3864 * @phpstan-type UpdateData array<array{name: string, lft?: int, rgt?: int, depth?: int}>
65+ *
66+ * @copyright Copyright (C) 2023 Terabytesoftw.
67+ * @license https://opensource.org/license/bsd-3-clause BSD 3-Clause License.
3968 */
40- class TestCase extends \PHPUnit \Framework \TestCase
69+ abstract class TestCase extends \PHPUnit \Framework \TestCase
4170{
4271 use SchemaBuilderTrait;
4372
4473 /**
74+ * Database connection configuration.
75+ *
4576 * @phpstan-var string[]
4677 */
4778 protected array $ connection = [];
79+
80+ /**
81+ * Directory where fixture XML files are stored.
82+ */
4883 protected string $ fixtureDirectory = __DIR__ . '/support/data/ ' ;
4984
85+ /**
86+ * @throws InvalidConfigException if the configuration is invalid or incomplete.
87+ */
5088 protected function setUp (): void
5189 {
5290 parent ::setUp ();
5391
5492 $ this ->mockConsoleApplication ();
5593 }
5694
57- public function getDb (): Connection
58- {
59- return Yii::$ app ->getDb ();
60- }
61-
6295 /**
6396 * Asserts that a list of tree nodes matches the expected order.
6497 *
65- * @param array $nodesList List of tree nodes to validate
66- * @param array $expectedOrder Expected order of node names
67- * @param string $nodeType Type of nodes being tested (for error messages)
98+ * @param array $nodesList List of tree nodes to validate.
99+ * @param array $expectedOrder Expected order of node names.
100+ * @param string $nodeType Type of nodes being tested (for error messages).
68101 *
69102 * @phpstan-param array<ActiveRecord> $nodesList
70103 * @phpstan-param array<string> $expectedOrder
@@ -97,8 +130,8 @@ protected function assertNodesInCorrectOrder(array $nodesList, array $expectedOr
97130 /**
98131 * Asserts that a query contains ORDER BY clause with 'lft' column.
99132 *
100- * @param ActiveQuery $query The query to check
101- * @param string $methodName Name of the method being tested
133+ * @param ActiveQuery $query Query to check.
134+ * @param string $methodName Name of the method being tested.
102135 *
103136 * @phpstan-param ActiveQuery<ActiveRecord> $query
104137 */
@@ -120,6 +153,10 @@ protected function assertQueryHasOrderBy(ActiveQuery $query, string $methodName)
120153 }
121154
122155 /**
156+ * Builds a flat XML dataset from a given data set array.
157+ *
158+ * @return string Formatted XML string.
159+ *
123160 * @phpstan-import-type DataSetType from TestCase
124161 *
125162 * @phpstan-param DataSetType $dataSet
@@ -155,7 +192,8 @@ protected function buildFlatXMLDataSet(array $dataSet): string
155192 throw new RuntimeException ('Failed to save XML from DOM. ' );
156193 }
157194
158- // Replace the tags with 4 spaces
195+ // Manually indent child elements with 4 spaces for consistent fixture formatting.
196+ // DOM's formatOutput doesn't provide control over indentation depth.
159197 return str_replace (
160198 [
161199 '<tree ' , '<multiple_tree ' ],
@@ -167,6 +205,14 @@ protected function buildFlatXMLDataSet(array $dataSet): string
167205 );
168206 }
169207
208+ /**
209+ * Creates the database schema and resets the tables for testing.
210+ *
211+ * This method drops existing tables and runs migrations to ensure a clean state.
212+ *
213+ * @throws Exception if an unexpected error occurs during execution.
214+ * @throws RuntimeException if a runtime error prevents the operation from completing successfully.
215+ */
170216 protected function createDatabase (): void
171217 {
172218 $ command = $ this ->getDb ()->createCommand ();
@@ -179,6 +225,7 @@ protected function createDatabase(): void
179225 try {
180226 $ this ->runMigrate ('down ' , ['all ' ]);
181227 } catch (RuntimeException ) {
228+ // Ignore errors when rolling back migrations on a potentially fresh database
182229 }
183230
184231 foreach ($ dropTables as $ table ) {
@@ -193,13 +240,14 @@ protected function createDatabase(): void
193240 /**
194241 * Creates a tree structure based on a hierarchical definition.
195242 *
196- * @param array $structure Hierarchical tree structure definition
197- * @param array $updates Database updates to apply after creation
198- * @param string $modelClass The model class to use (Tree::class or MultipleTree::class)
243+ * @param array $structure Hierarchical tree structure definition.
244+ * @param array $updates Database updates to apply after creation.
245+ * @param string $modelClass Model class to use ({@see Tree::class} or {@see MultipleTree::class}.
199246 *
200- * @throws InvalidArgumentException if the structure array is empty.
247+ * @throws Exception if an unexpected error occurs during execution.
248+ * @throws InvalidArgumentException if one or more arguments are invalid, of incorrect type or format.
201249 *
202- * @return MultipleTree|Tree The root node
250+ * @return MultipleTree|Tree Root node.
203251 *
204252 * @phpstan-param TreeStructure $structure
205253 * @phpstan-param UpdateData $updates
@@ -240,14 +288,24 @@ protected function createTreeStructure(
240288 return $ rootNode ;
241289 }
242290
291+ /**
292+ * Generates fixture data for testing tree structures.
293+ *
294+ * This method creates a database schema and populates it with predefined XML fixture data.
295+ *
296+ * It is used to set up the initial state of the database for tests that require specific tree structures.
297+ *
298+ * @throws Exception if an unexpected error occurs during execution.
299+ * @throws RuntimeException if a runtime error prevents the operation from completing successfully.
300+ */
243301 protected function generateFixtureTree (): void
244302 {
245303 $ this ->createDatabase ();
246304
247305 $ command = $ this ->getDb ()->createCommand ();
248306
249307 // Load XML fixture data into database tables
250- $ xml = new SimpleXMLElement ( "{ $ this ->fixtureDirectory } / test.xml" , 0 , true );
308+ $ xml = $ this ->loadFixtureXML ( ' test.xml ' );
251309
252310 $ children = $ xml ->children () ?? [];
253311
@@ -277,6 +335,10 @@ protected function generateFixtureTree(): void
277335 }
278336
279337 /**
338+ * Returns a dataset containing all tree nodes from both {@see Tree} and {@see MultipleTree} models.
339+ *
340+ * @return array Dataset containing all tree nodes.
341+ *
280342 * @phpstan-import-type DataSetType from TestCase
281343 *
282344 * @phpstan-return DataSetType
@@ -300,6 +362,10 @@ protected function getDataSet(): array
300362 }
301363
302364 /**
365+ * Returns a dataset containing only {@see MultipleTree} nodes.
366+ *
367+ * @return array Dataset containing only {@see MultipleTree} nodes.
368+ *
303369 * @phpstan-import-type DataSetType from TestCase
304370 *
305371 * @phpstan-return DataSetType
@@ -315,6 +381,21 @@ protected function getDataSetMultipleTree(): array
315381 return array_values ($ dataSetMultipleTree );
316382 }
317383
384+ /**
385+ * Returns the database connection used for tests.
386+ */
387+ protected function getDb (): Connection
388+ {
389+ return Yii::$ app ->getDb ();
390+ }
391+
392+ /**
393+ * Returns a dataset containing only {@see Tree} nodes.
394+ *
395+ * @throws RuntimeException if a runtime error prevents the operation from completing successfully.
396+ *
397+ * @return SimpleXMLElement Dataset containing only {@see Tree} nodes.
398+ */
318399 protected function loadFixtureXML (string $ fileName ): SimpleXMLElement
319400 {
320401 $ filePath = "{$ this ->fixtureDirectory }/ {$ fileName }" ;
@@ -334,6 +415,15 @@ protected function loadFixtureXML(string $fileName): SimpleXMLElement
334415 return $ simpleXML ;
335416 }
336417
418+ /**
419+ * Mocks the console application for testing purposes.
420+ *
421+ * This method initializes a new console application instance with a database connection.
422+ *
423+ * It is used to set up the environment for running console commands in tests.
424+ *
425+ * @throws InvalidConfigException if the configuration is invalid or incomplete.
426+ */
337427 protected function mockConsoleApplication (): void
338428 {
339429 new Application (
@@ -382,6 +472,10 @@ protected function replaceQuotes(string $sql): string
382472 }
383473
384474 /**
475+ * Runs a migration action with the specified parameters.
476+ *
477+ * @return mixed Result of the migration action.
478+ *
385479 * @phpstan-param array<array-key, mixed> $params
386480 */
387481 protected function runMigrate (string $ action , array $ params = []): mixed
@@ -415,6 +509,8 @@ protected function runMigrate(string $action, array $params = []): mixed
415509 * @param array $updates Array of updates to apply.
416510 * @param string $tableName Name of the table to apply updates to.
417511 *
512+ * @throws Exception if an unexpected error occurs during execution.
513+ *
418514 * @phpstan-param UpdateData $updates
419515 */
420516 private function applyUpdates (array $ updates , string $ tableName ): void
@@ -437,8 +533,8 @@ private function applyUpdates(array $updates, string $tableName): void
437533 /**
438534 * Recursively creates children for a given parent node.
439535 *
440- * @param MultipleTree|Tree $parent The parent node
441- * @param array $nodes Children definition (can be strings or arrays)
536+ * @param MultipleTree|Tree $parent Parent node.
537+ * @param array $nodes Children definition (can be strings or arrays).
442538 *
443539 * @phpstan-param NodeChildren $nodes
444540 */
0 commit comments