diff --git a/composer.json b/composer.json index 97ce9ba..3051010 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,13 @@ }, "minimum-stability": "dev", "prefer-stable": true, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/" + } + }, "require-dev": { - "phpunit/phpunit": "^11.5" + "phpunit/phpunit": "^11.5", + "orchestra/testbench": "^10.0" } } diff --git a/tests/Feature/Commands/MakeModifyMigrationTest.php b/tests/Feature/Commands/MakeModifyMigrationTest.php new file mode 100644 index 0000000..b4ea48e --- /dev/null +++ b/tests/Feature/Commands/MakeModifyMigrationTest.php @@ -0,0 +1,71 @@ +modulesPath = App::path('Modules'); + + // Create test module structure + $testModulePath = $this->modulesPath . '/TestModule/Infrastructure/Database/Migrations'; + File::makeDirectory($testModulePath, 0755, true); + } + + protected function tearDown(): void + { + if (File::exists($this->modulesPath . '/TestModule')) { + File::deleteDirectory($this->modulesPath . '/TestModule'); + } + + parent::tearDown(); + } + + /** @test */ + public function it_creates_modify_migration_file() + { + $this->artisan('make:modify-migration', [ + 'name' => 'modify_users_table', + 'module' => 'TestModule', + '--table' => 'users' + ])->assertExitCode(0); + + $migrationPath = $this->modulesPath . '/TestModule/Infrastructure/Database/Migrations'; + $files = File::files($migrationPath); + + $this->assertCount(1, $files); + + $migrationFile = $files[0]; + $content = File::get($migrationFile->getPathname()); + + $this->assertStringContainsString('modify_users_table', $content); + $this->assertStringContainsString('Schema::table', $content); + } + + /** @test */ + public function it_requires_module_parameter() + { + $this->artisan('make:modify-migration', [ + 'name' => 'modify_users_table' + ])->assertExitCode(1); + } + + /** @test */ + public function it_validates_module_exists() + { + $this->artisan('make:modify-migration', [ + 'name' => 'modify_users_table', + 'module' => 'NonExistentModule' + ])->expectsOutput('Module NonExistentModule does not exist!') + ->assertExitCode(1); + } +} \ No newline at end of file diff --git a/tests/Feature/Commands/MakeModuleTest.php b/tests/Feature/Commands/MakeModuleTest.php new file mode 100644 index 0000000..0c2a1ae --- /dev/null +++ b/tests/Feature/Commands/MakeModuleTest.php @@ -0,0 +1,93 @@ +modulesPath = App::path('Modules'); + + // Clean up any existing test modules + if (File::exists($this->modulesPath . '/TestModule')) { + File::deleteDirectory($this->modulesPath . '/TestModule'); + } + } + + protected function tearDown(): void + { + // Clean up test modules + if (File::exists($this->modulesPath . '/TestModule')) { + File::deleteDirectory($this->modulesPath . '/TestModule'); + } + + parent::tearDown(); + } + + /** @test */ + public function it_creates_basic_module_structure() + { + $this->artisan('make:module', ['name' => 'TestModule']) + ->expectsOutput('Module TestModule created successfully!') + ->assertExitCode(0); + + $modulePath = $this->modulesPath . '/TestModule'; + + // Check basic directory structure + $this->assertTrue(File::exists($modulePath)); + $this->assertTrue(File::exists($modulePath . '/Application')); + $this->assertTrue(File::exists($modulePath . '/Domain')); + $this->assertTrue(File::exists($modulePath . '/Infrastructure')); + $this->assertTrue(File::exists($modulePath . '/Interface')); + } + + /** @test */ + public function it_creates_module_with_all_options() + { + $this->artisan('make:module', [ + 'name' => 'TestModule', + '--data' => true, + '--dto' => true, + '--migration' => true, + '--model' => true, + '--repository' => true, + '--service' => true, + '--controller' => true, + '--request' => true, + '--resource' => true, + '--route' => true, + '--seeder' => true, + ])->assertExitCode(0); + + $modulePath = $this->modulesPath . '/TestModule'; + + // Check if specific files are created + $this->assertTrue(File::exists($modulePath . '/Domain/Models')); + $this->assertTrue(File::exists($modulePath . '/Infrastructure/Database/Migrations')); + $this->assertTrue(File::exists($modulePath . '/Infrastructure/Repositories')); + $this->assertTrue(File::exists($modulePath . '/Application/Services')); + $this->assertTrue(File::exists($modulePath . '/Interface/Controllers')); + $this->assertTrue(File::exists($modulePath . '/Interface/Routes')); + } + + /** @test */ + public function it_prevents_creating_duplicate_modules() + { + // Create module first time + $this->artisan('make:module', ['name' => 'TestModule']) + ->assertExitCode(0); + + // Try to create same module again + $this->artisan('make:module', ['name' => 'TestModule']) + ->expectsOutput('Module TestModule already exists!') + ->assertExitCode(1); + } +} \ No newline at end of file diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 0000000..90af4ab --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,101 @@ +artisan('list'); // This forces command registration + + // Setup test environment + $this->setUpDatabase(); + } + + protected function getPackageProviders($app) + { + return [ + DDDModularToolkitServiceProvider::class, + ]; + } + + protected function getEnvironmentSetUp($app) + { + // Setup default database to use sqlite :memory: + $app['config']->set('database.default', 'testing'); + $app['config']->set('database.connections.testing', [ + 'driver' => 'sqlite', + 'database' => ':memory:', + 'prefix' => '', + ]); + + // Set up DDD config + $app['config']->set('ddd.blade.is_active', true); + $app['config']->set('ddd.blade.path', 'Blade'); + $app['config']->set('ddd.react.is_active', false); + $app['config']->set('ddd.react.path', 'React'); + $app['config']->set('ddd.middleware.auth', true); + $app['config']->set('ddd.middleware.api', true); + } + + protected function setUpDatabase() + { + // Create basic Laravel tables if needed + } + + protected function tearDown(): void + { + // Clean up test modules + $modulesPath = App::path('Modules'); + if (File::exists($modulesPath)) { + $directories = File::directories($modulesPath); + foreach ($directories as $directory) { + if (str_contains($directory, 'Test')) { + File::deleteDirectory($directory); + } + } + } + + parent::tearDown(); + } + + /** + * Define additional package providers. + */ + protected function getPackageAliases($app) + { + return []; + } + + /** + * Resolve application Console Kernel implementation. + */ + protected function resolveApplicationConsoleKernel($app) + { + $app->singleton('Illuminate\Contracts\Console\Kernel', function ($app) { + $kernel = new \Illuminate\Foundation\Console\Kernel($app, $app['events']); + + // Force register our commands + $kernel->registerCommand(new MakeModule()); + $kernel->registerCommand(new MakeModifyMigration()); + + return $kernel; + }); + } +} \ No newline at end of file diff --git a/tests/Unit/Helpers/PathHelperTest.php b/tests/Unit/Helpers/PathHelperTest.php new file mode 100644 index 0000000..1d2c0ba --- /dev/null +++ b/tests/Unit/Helpers/PathHelperTest.php @@ -0,0 +1,66 @@ +testDir = sys_get_temp_dir() . '/ddd_test_' . uniqid(); + File::makeDirectory($this->testDir, 0755, true); + + // Create test file structure + File::makeDirectory($this->testDir . '/Module1/Routes', 0755, true); + File::makeDirectory($this->testDir . '/Module2/Routes', 0755, true); + File::put($this->testDir . '/Module1/Routes/web.php', 'testDir . '/Module2/Routes/web.php', 'testDir)) { + File::deleteDirectory($this->testDir); + } + + parent::tearDown(); + } + + /** @test */ + public function glob_recursive_finds_matching_files() + { + $foundFiles = []; + + globRecursive( + $this->testDir . '/*/Routes/web.php', + function (string $file) use (&$foundFiles) { + $foundFiles[] = $file; + } + ); + + $this->assertCount(2, $foundFiles); + $this->assertStringContainsString('Module1/Routes/web.php', $foundFiles[0]); + $this->assertStringContainsString('Module2/Routes/web.php', $foundFiles[1]); + } + + /** @test */ + public function glob_recursive_handles_no_matches() + { + $foundFiles = []; + + globRecursive( + $this->testDir . '/*/NonExistent/*.php', + function (string $file) use (&$foundFiles) { + $foundFiles[] = $file; + } + ); + + $this->assertCount(0, $foundFiles); + } +} \ No newline at end of file diff --git a/tests/Unit/Providers/DDDModularToolkitServiceProviderTest.php b/tests/Unit/Providers/DDDModularToolkitServiceProviderTest.php new file mode 100644 index 0000000..ad3b31c --- /dev/null +++ b/tests/Unit/Providers/DDDModularToolkitServiceProviderTest.php @@ -0,0 +1,129 @@ +assertTrue(File::exists($modulesPath)); + } + + /** @test */ + public function it_registers_commands_when_running_in_console() + { + // Force the application to think it's running in console + $this->app->instance('env', 'testing'); + + // Get all registered commands + $kernel = $this->app->make('Illuminate\Contracts\Console\Kernel'); + $commands = $kernel->all(); + + // Check if our commands are registered + $this->assertTrue(isset($commands['make:module']), 'make:module command not found'); + $this->assertTrue(isset($commands['make:modify-migration']), 'make:modify-migration command not found'); + } + + /** @test */ + public function it_merges_config_from_package() + { + $this->assertNotNull(config('ddd.blade.is_active')); + $this->assertNotNull(config('ddd.middleware.auth')); + $this->assertTrue(config('ddd.blade.is_active')); + $this->assertTrue(config('ddd.middleware.auth')); + } + + /** @test */ + public function it_has_publishable_config_file() + { + // Test that the config file exists in the package + $configPath = __DIR__ . '/../../../config/ddd.php'; + $this->assertTrue(File::exists($configPath)); + + // Test that config is properly loaded + $this->assertIsArray(config('ddd')); + $this->assertArrayHasKey('blade', config('ddd')); + $this->assertArrayHasKey('middleware', config('ddd')); + } + + /** @test */ + public function it_can_bind_interfaces_to_implementations_with_subdirectories() + { + // Create a test module with subdirectories + $modulesPath = App::path('Modules'); + $testModulePath = $modulesPath . '/TestModule'; + + // Clean up if exists + if (File::exists($testModulePath)) { + File::deleteDirectory($testModulePath); + } + + // Create directory structure + $directories = [ + 'TestModule/Domain/Contracts/Master', + 'TestModule/Infrastructure/Repositories/Master', + ]; + + foreach ($directories as $dir) { + $fullPath = "{$modulesPath}/{$dir}"; + if (!File::exists($fullPath)) { + File::makeDirectory($fullPath, 0755, true); + } + } + + // Create test interface + File::put("{$modulesPath}/TestModule/Domain/Contracts/Master/TestRepositoryInterface.php", <<<'PHP' +app->register(DDDModularToolkitServiceProvider::class, true); + + // Check if binding works + $interfaceClass = "App\\Modules\\TestModule\\Domain\\Contracts\\Master\\TestRepositoryInterface"; + $repositoryClass = "App\\Modules\\TestModule\\Infrastructure\\Repositories\\Master\\TestRepository"; + + $this->assertTrue($this->app->bound($interfaceClass)); + $this->assertInstanceOf($repositoryClass, $this->app->make($interfaceClass)); + + // Clean up + if (File::exists($testModulePath)) { + File::deleteDirectory($testModulePath); + } + } +} \ No newline at end of file