Skip to content

Commit fc52e00

Browse files
authored
chore: Merge pull request #281 from WebFiori/dev
test: Added More Tests
2 parents 98d34b9 + 80d90e3 commit fc52e00

5 files changed

Lines changed: 693 additions & 1 deletion

File tree

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
<?php
2+
namespace WebFiori\Framework\Test;
3+
4+
use PHPUnit\Framework\TestCase;
5+
use WebFiori\Framework\Autoload\ClassLoader;
6+
use WebFiori\Framework\Autoload\ClassLoaderException;
7+
8+
class ClassLoaderTest extends TestCase {
9+
10+
protected function tearDown(): void {
11+
// Always restore to do-nothing so other tests are not affected
12+
ClassLoader::setOnFail('do-nothing');
13+
parent::tearDown();
14+
}
15+
16+
// ── isValidNamespace ────────────────────────────────────────────────────
17+
18+
/** @test */
19+
public function testIsValidNamespace_root() {
20+
$this->assertTrue(ClassLoader::isValidNamespace('\\'));
21+
}
22+
23+
/** @test */
24+
public function testIsValidNamespace_simple() {
25+
$this->assertTrue(ClassLoader::isValidNamespace('App'));
26+
}
27+
28+
/** @test */
29+
public function testIsValidNamespace_nested() {
30+
$this->assertTrue(ClassLoader::isValidNamespace('WebFiori\\Framework\\Autoload'));
31+
}
32+
33+
/** @test */
34+
public function testIsValidNamespace_startsWithDigit() {
35+
$this->assertFalse(ClassLoader::isValidNamespace('1Bad'));
36+
}
37+
38+
/** @test */
39+
public function testIsValidNamespace_invalidChar() {
40+
$this->assertFalse(ClassLoader::isValidNamespace('Bad-NS'));
41+
}
42+
43+
// ── live-instance basics ────────────────────────────────────────────────
44+
45+
/** @test */
46+
public function testRoot() {
47+
$this->assertEquals(ROOT_PATH, ClassLoader::root());
48+
}
49+
50+
/** @test */
51+
public function testGetCachePath() {
52+
$path = ClassLoader::getCachePath();
53+
$this->assertStringEndsWith(ClassLoader::CACHE_NAME, $path);
54+
}
55+
56+
/** @test */
57+
public function testGetFolders_nonEmpty() {
58+
$this->assertNotEmpty(ClassLoader::getFolders());
59+
}
60+
61+
/** @test */
62+
public function testGetLoadedClasses_nonEmpty() {
63+
$this->assertNotEmpty(ClassLoader::getLoadedClasses());
64+
}
65+
66+
/** @test */
67+
public function testGetCacheArray_isArray() {
68+
$this->assertIsArray(ClassLoader::getCacheArray());
69+
}
70+
71+
// ── isLoaded ────────────────────────────────────────────────────────────
72+
73+
/** @test */
74+
public function testIsLoaded_withoutNs() {
75+
$this->assertTrue(ClassLoader::isLoaded('ClassLoader'));
76+
}
77+
78+
/** @test */
79+
public function testIsLoaded_withNs() {
80+
$this->assertTrue(ClassLoader::isLoaded('ClassLoader', 'WebFiori\\Framework\\Autoload'));
81+
}
82+
83+
/** @test */
84+
public function testIsLoaded_wrongNs() {
85+
$this->assertFalse(ClassLoader::isLoaded('ClassLoader', 'Wrong\\NS'));
86+
}
87+
88+
/** @test */
89+
public function testIsLoaded_unknownClass() {
90+
$this->assertFalse(ClassLoader::isLoaded('TotallyNonExistentClass99'));
91+
}
92+
93+
// ── getClassPath ────────────────────────────────────────────────────────
94+
95+
/** @test */
96+
public function testGetClassPath_noNs() {
97+
$paths = ClassLoader::getClassPath('ClassLoader');
98+
$this->assertNotEmpty($paths);
99+
}
100+
101+
/** @test */
102+
public function testGetClassPath_withNs() {
103+
$paths = ClassLoader::getClassPath('ClassLoader', 'WebFiori\\Framework\\Autoload');
104+
$this->assertCount(1, $paths);
105+
}
106+
107+
/** @test */
108+
public function testGetClassPath_wrongNs_returnsEmpty() {
109+
$paths = ClassLoader::getClassPath('ClassLoader', 'Wrong\\NS');
110+
$this->assertEmpty($paths);
111+
}
112+
113+
// ── newSearchFolder ─────────────────────────────────────────────────────
114+
115+
/** @test */
116+
public function testNewSearchFolder_addsFolder() {
117+
$before = count(ClassLoader::getFolders());
118+
$tmpDir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'cl_test_' . time();
119+
mkdir($tmpDir);
120+
121+
ClassLoader::newSearchFolder($tmpDir, false);
122+
123+
$this->assertGreaterThan($before, count(ClassLoader::getFolders()));
124+
rmdir($tmpDir);
125+
}
126+
127+
// ── map / addClassMap ───────────────────────────────────────────────────
128+
129+
/** @test */
130+
public function testMap_loadsClassFromFile() {
131+
$className = 'TempMappedClass' . time();
132+
$tmpFile = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $className . '.php';
133+
134+
file_put_contents($tmpFile, "<?php\nclass $className {}");
135+
136+
ClassLoader::map($className, $className, $tmpFile);
137+
138+
$this->assertTrue(class_exists($className, false));
139+
unlink($tmpFile);
140+
}
141+
142+
/** @test */
143+
public function testAddClassMap_returnsFalse_whenFileNotExist() {
144+
$result = ClassLoader::get()->addClassMap('Ghost', 'Ghost', '/nonexistent/Ghost.php');
145+
$this->assertFalse($result);
146+
}
147+
148+
/** @test */
149+
public function testAddClassMap_returnsTrue_whenFileExists() {
150+
$className = 'TempMappedClass2' . time();
151+
$tmpFile = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $className . '.php';
152+
file_put_contents($tmpFile, "<?php\nclass $className {}");
153+
154+
$result = ClassLoader::get()->addClassMap($className, $className, $tmpFile);
155+
156+
$this->assertTrue($result);
157+
unlink($tmpFile);
158+
}
159+
160+
// ── mapAll ──────────────────────────────────────────────────────────────
161+
162+
/** @test */
163+
public function testMapAll_loadsMultipleClasses() {
164+
$tmpDir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'mapall_' . time();
165+
mkdir($tmpDir);
166+
167+
$names = ['MapAllA' . time(), 'MapAllB' . time()];
168+
foreach ($names as $name) {
169+
file_put_contents($tmpDir . DIRECTORY_SEPARATOR . $name . '.php', "<?php\nclass $name {}");
170+
}
171+
172+
ClassLoader::mapAll('', $tmpDir, $names);
173+
174+
foreach ($names as $name) {
175+
$this->assertTrue(class_exists($name, false));
176+
unlink($tmpDir . DIRECTORY_SEPARATOR . $name . '.php');
177+
}
178+
rmdir($tmpDir);
179+
}
180+
181+
// ── setOnFail ───────────────────────────────────────────────────────────
182+
183+
/** @test */
184+
public function testSetOnFail_doNothing_noException() {
185+
ClassLoader::setOnFail('do-nothing');
186+
// Trigger autoload of a non-existent class — should not throw
187+
class_exists('\\AbsolutelyNonExistent' . time());
188+
$this->assertTrue(true); // reached here = pass
189+
ClassLoader::setOnFail('throw-exception'); // restore
190+
}
191+
192+
/** @test */
193+
public function testSetOnFail_throwException() {
194+
ClassLoader::setOnFail('throw-exception');
195+
$this->expectException(ClassLoaderException::class);
196+
class_exists('\\AbsolutelyNonExistent' . time()); // triggers spl_autoload
197+
ClassLoader::setOnFail('do-nothing'); // restore (only reached if no exception, but expectException handles it)
198+
}
199+
200+
/** @test */
201+
public function testSetOnFail_callable_isCalled() {
202+
$called = false;
203+
ClassLoader::setOnFail(function() use (&$called) {
204+
$called = true;
205+
});
206+
207+
class_exists('\\AbsolutelyNonExistent' . time());
208+
209+
$this->assertTrue($called);
210+
ClassLoader::setOnFail('do-nothing'); // restore to safe default
211+
}
212+
}

tests/WebFiori/Framework/Tests/Cli/CreateMigrationCommandTest.php

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,149 @@ public function testCreateMigrationWithDefaultDescription() {
9797
$this->assertTrue(class_exists('\\App\\Database\\Migrations\\'.$className));
9898
$this->removeClass('\\App\\Database\\Migrations\\'.$className);
9999
}
100+
101+
102+
/**
103+
* @test
104+
* Covers: getEnvironments() interactive yes-branch
105+
*/
106+
public function testCreateMigrationInteractiveWithEnvironments() {
107+
$className = 'EnvMigration'.time();
108+
109+
$output = $this->executeSingleCommand(new CreateMigrationCommand(), [], [
110+
$className,
111+
'Some description',
112+
'y',
113+
'dev',
114+
'test',
115+
'',
116+
'n',
117+
]);
118+
119+
$this->assertContains("Enter environment name (leave empty to finish):\n", $output);
120+
$this->assertContains("Success: Migration class created at: ".APP_PATH."Database".DIRECTORY_SEPARATOR."Migrations".DIRECTORY_SEPARATOR.$className.".php\n", $output);
121+
$this->assertEquals(0, $this->getExitCode());
122+
$this->assertTrue(class_exists('\\App\\Database\\Migrations\\'.$className));
123+
$this->removeClass('\\App\\Database\\Migrations\\'.$className);
124+
}
125+
126+
/**
127+
* @test
128+
* Covers: getEnvironments() via --environments arg
129+
*/
130+
public function testCreateMigrationWithEnvironmentsArg() {
131+
$className = 'EnvArgMigration'.time();
132+
133+
$output = $this->executeMultiCommand([
134+
CreateMigrationCommand::class,
135+
'--class-name' => $className,
136+
'--environments' => 'dev,prod',
137+
]);
138+
139+
$this->assertEquals([
140+
"Success: Migration class created at: ".APP_PATH."Database".DIRECTORY_SEPARATOR."Migrations".DIRECTORY_SEPARATOR.$className.".php\n"
141+
], $output);
142+
$this->assertEquals(0, $this->getExitCode());
143+
$this->assertTrue(class_exists('\\App\\Database\\Migrations\\'.$className));
144+
$this->removeClass('\\App\\Database\\Migrations\\'.$className);
145+
}
146+
147+
/**
148+
* @test
149+
* Covers: selectDependenciesInteractive() - valid, invalid selection, finish
150+
*/
151+
public function testCreateMigrationInteractiveWithDependencies() {
152+
$depName = 'DepMigration'.time();
153+
$this->executeMultiCommand([
154+
CreateMigrationCommand::class,
155+
'--class-name' => $depName,
156+
]);
157+
158+
$className = 'WithDepMigration'.time();
159+
160+
$output = $this->executeSingleCommand(new CreateMigrationCommand(), [], [
161+
$className,
162+
'Some description',
163+
'n',
164+
'y',
165+
'99',
166+
'1',
167+
'',
168+
]);
169+
170+
$this->assertContains("Available database changes:\n", $output);
171+
$this->assertContains("Error: Invalid selection.\n", $output);
172+
$this->assertEquals(0, $this->getExitCode());
173+
$this->assertTrue(class_exists('\\App\\Database\\Migrations\\'.$className));
174+
175+
$this->removeClass('\\App\\Database\\Migrations\\'.$className);
176+
$this->removeClass('\\App\\Database\\Migrations\\'.$depName);
177+
}
178+
179+
/**
180+
* @test
181+
* Covers: selectDependenciesInteractive() when no existing changes found
182+
*/
183+
public function testCreateMigrationInteractiveDependenciesNoneAvailable() {
184+
$className = 'NoDepsAvailMigration'.time();
185+
186+
$migrationsPath = APP_PATH.'Database'.DS.'Migrations';
187+
$tempPath = APP_PATH.'Database'.DS.'Migrations_bak_'.time();
188+
rename($migrationsPath, $tempPath);
189+
mkdir($migrationsPath);
190+
191+
$seedersPath = APP_PATH.'Database'.DS.'Seeders';
192+
$tempSeedersPath = APP_PATH.'Database'.DS.'Seeders_bak_'.time();
193+
rename($seedersPath, $tempSeedersPath);
194+
mkdir($seedersPath);
195+
196+
try {
197+
$output = $this->executeSingleCommand(new CreateMigrationCommand(), [], [
198+
$className,
199+
'Some description',
200+
'n',
201+
'y',
202+
]);
203+
204+
$this->assertContains("Info: No existing database changes found.\n", $output);
205+
$this->assertEquals(0, $this->getExitCode());
206+
} finally {
207+
$createdFile = $migrationsPath.DS.$className.'.php';
208+
if (file_exists($createdFile)) {
209+
unlink($createdFile);
210+
}
211+
rmdir($migrationsPath);
212+
rename($tempPath, $migrationsPath);
213+
rmdir($seedersPath);
214+
rename($tempSeedersPath, $seedersPath);
215+
}
216+
}
217+
218+
/**
219+
* @test
220+
* Covers: resolveDependencies() via --depends-on - found and not-found (warning) branches
221+
*/
222+
public function testCreateMigrationWithDependsOnArg() {
223+
$depName = 'ResolvableDepMigration'.time();
224+
$this->executeMultiCommand([
225+
CreateMigrationCommand::class,
226+
'--class-name' => $depName,
227+
]);
228+
229+
$className = 'DependsOnArgMigration'.time();
230+
231+
$output = $this->executeMultiCommand([
232+
CreateMigrationCommand::class,
233+
'--class-name' => $className,
234+
'--depends-on' => $depName.',NonExistentClass',
235+
]);
236+
237+
$this->assertContains("Warning: Dependency 'NonExistentClass' not found, skipping.\n", $output);
238+
$this->assertContains("Success: Migration class created at: ".APP_PATH."Database".DIRECTORY_SEPARATOR."Migrations".DIRECTORY_SEPARATOR.$className.".php\n", $output);
239+
$this->assertEquals(0, $this->getExitCode());
240+
$this->assertTrue(class_exists('\\App\\Database\\Migrations\\'.$className));
241+
242+
$this->removeClass('\\App\\Database\\Migrations\\'.$className);
243+
$this->removeClass('\\App\\Database\\Migrations\\'.$depName);
244+
}
100245
}

0 commit comments

Comments
 (0)