7
7
use RuntimeException ;
8
8
use SimpleXMLElement ;
9
9
use Yii ;
10
- use yii \base \InvalidArgumentException ;
10
+ use yii \base \{ InvalidArgumentException , InvalidConfigException } ;
11
11
use yii \console \Application ;
12
- use yii \db \{ActiveQuery , ActiveRecord , Connection , Query , SchemaBuilderTrait };
12
+ use yii \db \{ActiveQuery , ActiveRecord , Connection , Exception , SchemaBuilderTrait };
13
13
use yii2 \extensions \nestedsets \tests \support \model \{MultipleTree , Tree };
14
14
use yii2 \extensions \nestedsets \tests \support \stub \EchoMigrateController ;
15
15
16
16
use function array_merge ;
17
17
use function array_values ;
18
+ use function dirname ;
18
19
use function dom_import_simplexml ;
19
20
use 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 ;
20
26
use function preg_replace ;
21
27
use function simplexml_load_string ;
22
28
use function str_replace ;
23
29
24
30
/**
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
+ *
25
51
* @phpstan-type DataSetType = list<
26
52
* array{
27
53
* id: int,
36
62
* @phpstan-type NodeChildren array<string|array{name: string, children?: array<mixed>}>
37
63
* @phpstan-type TreeStructure array<array<mixed>>
38
64
* @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.
39
68
*/
40
- class TestCase extends \PHPUnit \Framework \TestCase
69
+ abstract class TestCase extends \PHPUnit \Framework \TestCase
41
70
{
42
71
use SchemaBuilderTrait;
43
72
44
73
/**
74
+ * Database connection configuration.
75
+ *
45
76
* @phpstan-var string[]
46
77
*/
47
78
protected array $ connection = [];
79
+
80
+ /**
81
+ * Directory where fixture XML files are stored.
82
+ */
48
83
protected string $ fixtureDirectory = __DIR__ . '/support/data/ ' ;
49
84
85
+ /**
86
+ * @throws InvalidConfigException if the configuration is invalid or incomplete.
87
+ */
50
88
protected function setUp (): void
51
89
{
52
90
parent ::setUp ();
53
91
54
92
$ this ->mockConsoleApplication ();
55
93
}
56
94
57
- public function getDb (): Connection
58
- {
59
- return Yii::$ app ->getDb ();
60
- }
61
-
62
95
/**
63
96
* Asserts that a list of tree nodes matches the expected order.
64
97
*
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).
68
101
*
69
102
* @phpstan-param array<ActiveRecord> $nodesList
70
103
* @phpstan-param array<string> $expectedOrder
@@ -97,8 +130,8 @@ protected function assertNodesInCorrectOrder(array $nodesList, array $expectedOr
97
130
/**
98
131
* Asserts that a query contains ORDER BY clause with 'lft' column.
99
132
*
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.
102
135
*
103
136
* @phpstan-param ActiveQuery<ActiveRecord> $query
104
137
*/
@@ -120,6 +153,10 @@ protected function assertQueryHasOrderBy(ActiveQuery $query, string $methodName)
120
153
}
121
154
122
155
/**
156
+ * Builds a flat XML dataset from a given data set array.
157
+ *
158
+ * @return string Formatted XML string.
159
+ *
123
160
* @phpstan-import-type DataSetType from TestCase
124
161
*
125
162
* @phpstan-param DataSetType $dataSet
@@ -155,7 +192,8 @@ protected function buildFlatXMLDataSet(array $dataSet): string
155
192
throw new RuntimeException ('Failed to save XML from DOM. ' );
156
193
}
157
194
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.
159
197
return str_replace (
160
198
[
161
199
'<tree ' , '<multiple_tree ' ],
@@ -167,6 +205,14 @@ protected function buildFlatXMLDataSet(array $dataSet): string
167
205
);
168
206
}
169
207
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
+ */
170
216
protected function createDatabase (): void
171
217
{
172
218
$ command = $ this ->getDb ()->createCommand ();
@@ -179,6 +225,7 @@ protected function createDatabase(): void
179
225
try {
180
226
$ this ->runMigrate ('down ' , ['all ' ]);
181
227
} catch (RuntimeException ) {
228
+ // Ignore errors when rolling back migrations on a potentially fresh database
182
229
}
183
230
184
231
foreach ($ dropTables as $ table ) {
@@ -193,13 +240,14 @@ protected function createDatabase(): void
193
240
/**
194
241
* Creates a tree structure based on a hierarchical definition.
195
242
*
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}.
199
246
*
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.
201
249
*
202
- * @return MultipleTree|Tree The root node
250
+ * @return MultipleTree|Tree Root node.
203
251
*
204
252
* @phpstan-param TreeStructure $structure
205
253
* @phpstan-param UpdateData $updates
@@ -240,14 +288,24 @@ protected function createTreeStructure(
240
288
return $ rootNode ;
241
289
}
242
290
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
+ */
243
301
protected function generateFixtureTree (): void
244
302
{
245
303
$ this ->createDatabase ();
246
304
247
305
$ command = $ this ->getDb ()->createCommand ();
248
306
249
307
// Load XML fixture data into database tables
250
- $ xml = new SimpleXMLElement ( "{ $ this ->fixtureDirectory } / test.xml" , 0 , true );
308
+ $ xml = $ this ->loadFixtureXML ( ' test.xml ' );
251
309
252
310
$ children = $ xml ->children () ?? [];
253
311
@@ -277,6 +335,10 @@ protected function generateFixtureTree(): void
277
335
}
278
336
279
337
/**
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
+ *
280
342
* @phpstan-import-type DataSetType from TestCase
281
343
*
282
344
* @phpstan-return DataSetType
@@ -300,6 +362,10 @@ protected function getDataSet(): array
300
362
}
301
363
302
364
/**
365
+ * Returns a dataset containing only {@see MultipleTree} nodes.
366
+ *
367
+ * @return array Dataset containing only {@see MultipleTree} nodes.
368
+ *
303
369
* @phpstan-import-type DataSetType from TestCase
304
370
*
305
371
* @phpstan-return DataSetType
@@ -315,6 +381,21 @@ protected function getDataSetMultipleTree(): array
315
381
return array_values ($ dataSetMultipleTree );
316
382
}
317
383
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
+ */
318
399
protected function loadFixtureXML (string $ fileName ): SimpleXMLElement
319
400
{
320
401
$ filePath = "{$ this ->fixtureDirectory }/ {$ fileName }" ;
@@ -334,6 +415,15 @@ protected function loadFixtureXML(string $fileName): SimpleXMLElement
334
415
return $ simpleXML ;
335
416
}
336
417
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
+ */
337
427
protected function mockConsoleApplication (): void
338
428
{
339
429
new Application (
@@ -382,6 +472,10 @@ protected function replaceQuotes(string $sql): string
382
472
}
383
473
384
474
/**
475
+ * Runs a migration action with the specified parameters.
476
+ *
477
+ * @return mixed Result of the migration action.
478
+ *
385
479
* @phpstan-param array<array-key, mixed> $params
386
480
*/
387
481
protected function runMigrate (string $ action , array $ params = []): mixed
@@ -415,6 +509,8 @@ protected function runMigrate(string $action, array $params = []): mixed
415
509
* @param array $updates Array of updates to apply.
416
510
* @param string $tableName Name of the table to apply updates to.
417
511
*
512
+ * @throws Exception if an unexpected error occurs during execution.
513
+ *
418
514
* @phpstan-param UpdateData $updates
419
515
*/
420
516
private function applyUpdates (array $ updates , string $ tableName ): void
@@ -437,8 +533,8 @@ private function applyUpdates(array $updates, string $tableName): void
437
533
/**
438
534
* Recursively creates children for a given parent node.
439
535
*
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).
442
538
*
443
539
* @phpstan-param NodeChildren $nodes
444
540
*/
0 commit comments