diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..b08a2af --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,64 @@ +name: Tests + +on: + pull_request: + branches: + - main + types: [opened, synchronize, reopened] + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + php-version: ['8.3', '8.4'] + fail-fast: false + + services: + mysql: + image: mysql:8.0 + env: + MYSQL_ROOT_PASSWORD: secret + MYSQL_DATABASE: test_openapi_theme + ports: + - 3306:3306 + options: >- + --health-cmd="mysqladmin ping" + --health-interval=10s + --health-timeout=5s + --health-retries=3 + + steps: + - uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: intl, zip, pdo_mysql + coverage: none + + - name: Get composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache composer dependencies + uses: actions/cache@v4 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + - name: Wait for MySQL + run: | + while ! mysqladmin ping -h"127.0.0.1" -P"3306" --silent; do + sleep 1 + done + + - name: Run tests + run: vendor/bin/phpunit + env: + DATABASE_URL: mysql://root:secret@127.0.0.1/test_openapi_theme?encoding=utf8mb4&timezone=UTC&cacheMetadata=true \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2b0d4df..aa76ed4 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ /vendor/ /.idea/ .DS_Store +/tests/test_app/src/Controller diff --git a/Dockerfile.test b/Dockerfile.test new file mode 100644 index 0000000..1576836 --- /dev/null +++ b/Dockerfile.test @@ -0,0 +1,18 @@ +ARG PHP_VERSION +FROM php:${PHP_VERSION}-cli + +RUN apt-get update && apt-get install -y \ + libicu-dev \ + libzip-dev \ + unzip \ + git \ + && docker-php-ext-install \ + intl \ + zip \ + pdo_mysql + +COPY --from=composer:latest /usr/bin/composer /usr/bin/composer + +WORKDIR /app + +CMD ["vendor/bin/phpunit"] \ No newline at end of file diff --git a/composer.json b/composer.json index b1502bf..5388d17 100644 --- a/composer.json +++ b/composer.json @@ -7,10 +7,14 @@ "php": ">=8.3", "cakephp/cakephp": "^5.0 || ^5.1", "cakephp/bake": "^3.1", - "zircote/swagger-php": "^4.9" + "zircote/swagger-php": "^4.9", + "cakephp/twig-view": "^2.0.0" }, "require-dev": { - "phpunit/phpunit": "^10.1.0" + "phpunit/phpunit": "^10.1.0", + "cakephp/chronos": "^3.0", + "cakephp/migrations": "^4.0", + "cakephp/plugin-installer": "^2.0" }, "autoload": { "psr-4": { @@ -20,6 +24,7 @@ "autoload-dev": { "psr-4": { "OpenApiTheme\\Test\\": "tests/", + "TestApp\\": "tests/test_app/src/", "Cake\\Test\\": "vendor/cakephp/cakephp/tests/" } }, @@ -27,7 +32,8 @@ "bin/build-swagger-json" ], "scripts": { - "build:swagger": "build-swagger-json" + "build:swagger": "build-swagger-json", + "test": "phpunit" }, "config": { "allow-plugins": { diff --git a/docker-compose.test.yml b/docker-compose.test.yml new file mode 100644 index 0000000..e558e32 --- /dev/null +++ b/docker-compose.test.yml @@ -0,0 +1,37 @@ +version: '3.8' + +services: + test-php84: + build: + context: . + dockerfile: Dockerfile.test + args: + PHP_VERSION: "8.4" + volumes: + - .:/app + depends_on: + - db-test + environment: + - DATABASE_URL=mysql://root:secret@db-test/test_openapi_theme?encoding=utf8mb4&timezone=UTC&cacheMetadata=true + + test-php83: + build: + context: . + dockerfile: Dockerfile.test + args: + PHP_VERSION: "8.3" + volumes: + - .:/app + depends_on: + - db-test + environment: + - DATABASE_URL=mysql://root:secret@db-test/test_openapi_theme?encoding=utf8mb4&timezone=UTC&cacheMetadata=true + + db-test: + image: mysql:8.0 + environment: + MYSQL_ROOT_PASSWORD: secret + MYSQL_DATABASE: test_openapi_theme + command: --default-authentication-plugin=mysql_native_password + ports: + - "3306" \ No newline at end of file diff --git a/run-tests.sh b/run-tests.sh new file mode 100755 index 0000000..7ed096d --- /dev/null +++ b/run-tests.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# コンテナをビルド +docker-compose -f docker-compose.test.yml build + +# PHP 8.4でテスト実行 +echo "Running tests with PHP 8.4..." +docker-compose -f docker-compose.test.yml run --rm test-php84 composer install +docker-compose -f docker-compose.test.yml run --rm test-php84 + +# PHP 8.3でテスト実行 +echo "Running tests with PHP 8.3..." +docker-compose -f docker-compose.test.yml run --rm test-php83 composer install +docker-compose -f docker-compose.test.yml run --rm test-php83 + +# コンテナを停止・削除 +docker-compose -f docker-compose.test.yml down \ No newline at end of file diff --git a/tests/Fixture/TestTablesFixture.php b/tests/Fixture/TestTablesFixture.php new file mode 100644 index 0000000..8be1d3d --- /dev/null +++ b/tests/Fixture/TestTablesFixture.php @@ -0,0 +1,46 @@ + + */ + public array $fields = [ + 'id' => ['type' => 'integer', 'length' => 11, 'unsigned' => false, 'null' => false, 'default' => null, 'comment' => 'Primary key', 'autoIncrement' => true], + 'name' => ['type' => 'string', 'length' => 255, 'null' => false, 'default' => null, 'comment' => 'Name field', 'collate' => 'utf8mb4_general_ci'], + 'created' => ['type' => 'datetime', 'length' => null, 'precision' => null, 'null' => false, 'default' => null, 'comment' => ''], + 'modified' => ['type' => 'datetime', 'length' => null, 'precision' => null, 'null' => false, 'default' => null, 'comment' => ''], + '_constraints' => [ + 'primary' => ['type' => 'primary', 'columns' => ['id'], 'length' => []], + ], + '_options' => [ + 'engine' => 'InnoDB', + 'collation' => 'utf8mb4_general_ci' + ], + ]; + + /** + * Init method + * + * @return void + */ + public function init(): void + { + $this->records = [ + [ + 'id' => 1, + 'name' => 'Test Record', + 'created' => '2024-01-01 00:00:00', + 'modified' => '2024-01-01 00:00:00' + ], + ]; + parent::init(); + } +} \ No newline at end of file diff --git a/tests/TestCase/Command/OpenApiControllerCommandTest.php b/tests/TestCase/Command/OpenApiControllerCommandTest.php index 026edcb..517794d 100644 --- a/tests/TestCase/Command/OpenApiControllerCommandTest.php +++ b/tests/TestCase/Command/OpenApiControllerCommandTest.php @@ -3,9 +3,18 @@ namespace OpenApiTheme\Test\TestCase\Command; -use Cake\TestSuite\ConsoleIntegrationTestTrait; +use Cake\Console\TestSuite\ConsoleIntegrationTestTrait; use Cake\TestSuite\TestCase; -use OpenApiTheme\Command\OpenApiControllerCommand; +use Cake\Core\Configure; +use Cake\Core\Plugin; +use OpenApi\Attributes\Get; +use OpenApi\Attributes\Post; +use OpenApi\Attributes\Put; +use OpenApi\Attributes\Delete; +use OpenApi\Attributes\Tag; +use OpenApi\Attributes\Response; +use ReflectionClass; +use ReflectionMethod; /** * OpenApiTheme\Command\OpenApiControllerCommand Test Case @@ -16,6 +25,11 @@ class OpenApiControllerCommandTest extends TestCase { use ConsoleIntegrationTestTrait; + /** + * @var array + */ + private $loadedClasses = []; + /** * setUp method * @@ -24,25 +38,272 @@ class OpenApiControllerCommandTest extends TestCase public function setUp(): void { parent::setUp(); - $this->useCommandRunner(); + $this->setAppNamespace('TestApp'); + + Configure::write('App.namespace', 'TestApp'); + Plugin::getCollection()->add(new \OpenApiTheme\Plugin()); + + // TwigViewの設定 + if (!defined('Cake\TwigView\View\CACHE')) { + define('Cake\TwigView\View\CACHE', TMP . 'twig' . DS); + } + + $this->cleanupTestFiles(); + } + + public function tearDown(): void + { + parent::tearDown(); + Configure::delete('App.namespace'); + Plugin::getCollection()->remove('OpenApiTheme'); + $this->cleanupTestFiles(); + } + + /** + * テストファイルをクリーンアップ + */ + private function cleanupTestFiles(): void + { + $testFiles = glob(TEST_APP . 'Controller' . DS . '*Controller.php'); + $testAdminFiles = glob(TEST_APP . 'Controller' . DS . 'Admin' . DS . '*Controller.php'); + + foreach (array_merge($testFiles ?? [], $testAdminFiles ?? []) as $file) { + // AppController.phpは削除しない + if (basename($file) === 'AppController.php') { + continue; + } + + if (file_exists($file)) { + unlink($file); + } + } + + $adminDir = TEST_APP . 'Controller' . DS . 'Admin'; + if (is_dir($adminDir) && count(glob($adminDir . '/*')) === 0) { + rmdir($adminDir); + } + } + + /** + * クラスをアンロードする + * + * @param string $className + * @return void + */ + private function removeClass(string $className): void + { + $classExists = class_exists($className, false); + if ($classExists) { + $class = new ReflectionClass($className); + $fileName = $class->getFileName(); + + if ($fileName && is_file($fileName)) { + opcache_invalidate($fileName, true); + opcache_reset(); + } + } + } + + /** + * クラスをロードする + * + * @param string $controllerPath + * @param string $className + * @return ReflectionClass + */ + private function loadControllerClass(string $controllerPath, string $className): ReflectionClass + { + if (class_exists($className, false)) { + $this->removeClass($className); + } + + require $controllerPath; + $this->loadedClasses[] = $className; + + return new ReflectionClass($className); + } + + /** + * Test basic controller baking + * + * @return void + */ + public function testBasicBaking(): void + { + $this->exec('bake controller BasicArticles --connection default --theme OpenApiTheme --no-test'); + $this->assertExitSuccess(); + $this->assertOutputContains('Baking controller class for BasicArticles'); + $this->assertOutputContains('Wrote'); + + $controllerPath = TEST_APP . 'Controller' . DS . 'BasicArticlesController.php'; + $this->assertFileExists($controllerPath); + + $className = 'TestApp\\Controller\\BasicArticlesController'; + $reflClass = $this->loadControllerClass($controllerPath, $className); + + $indexMethod = $reflClass->getMethod('index'); + $this->assertNotEmpty( + $indexMethod->getAttributes(Get::class), + 'indexメソッドにGetアトリビュートが存在しません' + ); + + $viewMethod = $reflClass->getMethod('view'); + $this->assertNotEmpty( + $viewMethod->getAttributes(Get::class), + 'viewメソッドにGetアトリビュートが存在しません' + ); + + $addMethod = $reflClass->getMethod('add'); + $this->assertNotEmpty( + $addMethod->getAttributes(Post::class), + 'addメソッドにPostアトリビュートが存在しません' + ); + + $editMethod = $reflClass->getMethod('edit'); + $this->assertNotEmpty( + $editMethod->getAttributes(Put::class), + 'editメソッドにPutアトリビュートが存在しません' + ); + + $deleteMethod = $reflClass->getMethod('delete'); + $this->assertNotEmpty( + $deleteMethod->getAttributes(Delete::class), + 'deleteメソッドにDeleteアトリビュートが存在しません' + ); } + /** - * Test buildOptionParser method + * Test baking with prefix * * @return void */ - public function testBuildOptionParser(): void + public function testBakeWithPrefix(): void { - $this->markTestIncomplete('Not implemented yet.'); + $this->exec('bake controller PrefixArticles --prefix Admin --connection default --theme OpenApiTheme --no-test'); + $this->assertExitSuccess(); + $this->assertOutputContains('Baking controller class for PrefixArticles'); + $this->assertOutputContains('Wrote'); + + $controllerPath = TEST_APP . 'Controller' . DS . 'Admin' . DS . 'PrefixArticlesController.php'; + $this->assertFileExists($controllerPath); + + $className = 'TestApp\\Controller\\Admin\\PrefixArticlesController'; + $reflClass = $this->loadControllerClass($controllerPath, $className); + + $indexMethod = $reflClass->getMethod('index'); + $this->assertNotEmpty( + $indexMethod->getAttributes(Get::class), + 'Admin/indexメソッドにGetアトリビュートが存在しません' + ); + + $viewMethod = $reflClass->getMethod('view'); + $this->assertNotEmpty( + $viewMethod->getAttributes(Get::class), + 'Admin/viewメソッドにGetアトリビュートが存在しません' + ); + + $addMethod = $reflClass->getMethod('add'); + $this->assertNotEmpty( + $addMethod->getAttributes(Post::class), + 'Admin/addメソッドにPostアトリビュートが存在しません' + ); + + $editMethod = $reflClass->getMethod('edit'); + $this->assertNotEmpty( + $editMethod->getAttributes(Put::class), + 'Admin/editメソッドにPutアトリビュートが存在しません' + ); + + $deleteMethod = $reflClass->getMethod('delete'); + $this->assertNotEmpty( + $deleteMethod->getAttributes(Delete::class), + 'Admin/deleteメソッドにDeleteアトリビュートが存在しません' + ); + } + + /** + * Test baking with actions + * + * @return void + */ + public function testBakeWithActions(): void + { + $this->exec('bake controller ActionArticles --actions index,view,add --connection default --theme OpenApiTheme --no-test'); + $this->assertExitSuccess(); + $this->assertOutputContains('Baking controller class for ActionArticles'); + $this->assertOutputContains('Wrote'); + + $controllerPath = TEST_APP . 'Controller' . DS . 'ActionArticlesController.php'; + $this->assertFileExists($controllerPath); + + $className = 'TestApp\\Controller\\ActionArticlesController'; + $reflClass = $this->loadControllerClass($controllerPath, $className); + + $indexMethod = $reflClass->getMethod('index'); + $this->assertNotEmpty( + $indexMethod->getAttributes(Get::class), + 'indexメソッドにGetアトリビュートが存在しません' + ); + + $viewMethod = $reflClass->getMethod('view'); + $this->assertNotEmpty( + $viewMethod->getAttributes(Get::class), + 'viewメソッドにGetアトリビュートが存在しません' + ); + + $addMethod = $reflClass->getMethod('add'); + $this->assertNotEmpty( + $addMethod->getAttributes(Post::class), + 'addメソッドにPostアトリビュートが存在しません' + ); + + $this->assertFalse( + method_exists($className, 'edit'), + 'editメソッドが存在してはいけません' + ); + $this->assertFalse( + method_exists($className, 'delete'), + 'deleteメソッドが存在してはいけません' + ); } /** - * Test execute method + * Test baking with no actions * * @return void */ - public function testExecute(): void + public function testBakeWithNoActions(): void { - $this->markTestIncomplete('Not implemented yet.'); + $this->exec('bake controller NoActionArticles --no-actions --connection default --theme OpenApiTheme --no-test'); + $this->assertExitSuccess(); + $this->assertOutputContains('Baking controller class for NoActionArticles'); + $this->assertOutputContains('Wrote'); + + $controllerPath = TEST_APP . 'Controller' . DS . 'NoActionArticlesController.php'; + $this->assertFileExists($controllerPath); + + $className = 'TestApp\\Controller\\NoActionArticlesController'; + $reflClass = $this->loadControllerClass($controllerPath, $className); + + $this->assertFalse( + method_exists($className, 'index'), + 'indexメソッドが存在してはいけません' + ); + $this->assertFalse( + method_exists($className, 'view'), + 'viewメソッドが存在してはいけません' + ); + $this->assertFalse( + method_exists($className, 'add'), + 'addメソッドが存在してはいけません' + ); + $this->assertFalse( + method_exists($className, 'edit'), + 'editメソッドが存在してはいけません' + ); + $this->assertFalse( + method_exists($className, 'delete'), + 'deleteメソッドが存在してはいけません' + ); } } diff --git a/tests/TestCase/Command/OpenApiModelCommandTest.php b/tests/TestCase/Command/OpenApiModelCommandTest.php new file mode 100644 index 0000000..bd09fe0 --- /dev/null +++ b/tests/TestCase/Command/OpenApiModelCommandTest.php @@ -0,0 +1,161 @@ +command = new OpenApiModelCommand(); + } + + /** + * tearDown method + * + * @return void + */ + public function tearDown(): void + { + unset($this->command); + parent::tearDown(); + } + + /** + * Test getEntityPropertySchema method + * + * @return void + */ + public function testGetEntityPropertySchema(): void + { + /** @var Table&MockObject $table */ + $table = $this->getMockBuilder(Table::class) + ->onlyMethods(['getSchema', 'associations']) + ->getMock(); + + $schema = new TableSchema('test_table', [ + 'id' => ['type' => 'integer', 'null' => false, 'comment' => 'Primary key'], + 'name' => ['type' => 'string', 'null' => false, 'comment' => 'Name field'], + ]); + + $associationCollection = new AssociationCollection(); + + $table->expects($this->once()) + ->method('getSchema') + ->willReturn($schema); + + $table->expects($this->once()) + ->method('associations') + ->willReturn($associationCollection); + + $result = $this->command->getEntityPropertySchema($table); + + $this->assertIsArray($result); + $this->assertArrayHasKey('id', $result); + $this->assertArrayHasKey('name', $result); + + $this->assertEquals('column', $result['id']['kind']); + $this->assertEquals('integer', $result['id']['type']); + $this->assertEquals(false, $result['id']['null']); + $this->assertEquals('Primary key', $result['id']['comment']); + + $this->assertEquals('column', $result['name']['kind']); + $this->assertEquals('string', $result['name']['type']); + $this->assertEquals(false, $result['name']['null']); + $this->assertEquals('Name field', $result['name']['comment']); + } + + /** + * Test getEntityPropertySchema method with associations + * + * @return void + */ + public function testGetEntityPropertySchemaWithAssociations(): void + { + /** @var Table&MockObject $table */ + $table = $this->getMockBuilder(Table::class) + ->onlyMethods(['getSchema', 'associations']) + ->getMock(); + + $schema = new TableSchema('test_table', [ + 'id' => ['type' => 'integer', 'null' => false], + ]); + + /** @var BelongsTo&MockObject $association */ + $association = $this->getMockBuilder(BelongsTo::class) + ->disableOriginalConstructor() + ->onlyMethods(['getProperty', 'getTarget']) + ->getMock(); + + /** @var Table&MockObject $targetTable */ + $targetTable = $this->getMockBuilder(Table::class) + ->onlyMethods(['getEntityClass', 'getRegistryAlias', 'getAlias']) + ->getMock(); + + $association->expects($this->any()) + ->method('getProperty') + ->willReturn('associated'); + + $association->expects($this->any()) + ->method('getTarget') + ->willReturn($targetTable); + + $targetTable->expects($this->any()) + ->method('getEntityClass') + ->willReturn('App\Model\Entity\Associated'); + + $targetTable->expects($this->any()) + ->method('getRegistryAlias') + ->willReturn('Associated'); + + $targetTable->expects($this->any()) + ->method('getAlias') + ->willReturn('Associated'); + + $associationCollection = new AssociationCollection(); + $associationCollection->add('Associated', $association); + + $table->expects($this->once()) + ->method('getSchema') + ->willReturn($schema); + + $table->expects($this->once()) + ->method('associations') + ->willReturn($associationCollection); + + $result = $this->command->getEntityPropertySchema($table); + + $this->assertIsArray($result); + $this->assertArrayHasKey('id', $result); + $this->assertArrayHasKey('associated', $result); + + $this->assertEquals('column', $result['id']['kind']); + $this->assertEquals('integer', $result['id']['type']); + + $this->assertEquals('association', $result['associated']['kind']); + $this->assertEquals('\App\Model\Entity\Associated', $result['associated']['type']); + } +} \ No newline at end of file diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 1575f86..5cbafc2 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -24,17 +24,91 @@ chdir($root); +if (!defined('DS')) { + define('DS', DIRECTORY_SEPARATOR); +} +define('ROOT', $root); +define('APP_DIR', 'TestApp'); +define('APP', ROOT . DS . 'tests' . DS . 'test_app' . DS . 'src' . DS); +define('TMP', sys_get_temp_dir() . DS); +define('CONFIG', ROOT . DS . 'tests' . DS . 'config' . DS); +define('CAKE_CORE_INCLUDE_PATH', ROOT . DS . 'vendor' . DS . 'cakephp' . DS . 'cakephp'); +define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS); +define('CAKE', CORE_PATH . 'src' . DS); +define('TESTS', ROOT . DS . 'tests' . DS); +define('TEST_APP', TESTS . 'test_app' . DS . 'src' . DS); +define('WWW_ROOT', TEST_APP . 'webroot' . DS); + require_once $root . '/vendor/autoload.php'; +require_once CORE_PATH . 'config/bootstrap.php'; -/** - * Define fallback values for required constants and configuration. - * To customize constants and configuration remove this require - * and define the data required by your plugin here. - */ -require_once $root . '/vendor/cakephp/cakephp/tests/bootstrap.php'; +use Cake\Cache\Cache; +use Cake\Core\Configure; +use Cake\Database\Connection; +use Cake\Database\Driver\Mysql; +use Cake\Datasource\ConnectionManager; +use Cake\Log\Log; +use Cake\Utility\Security; -if (file_exists($root . '/config/bootstrap.php')) { - require $root . '/config/bootstrap.php'; +Configure::write('debug', true); +Security::setSalt('dummy-salt-for-tests'); - return; -} +// Setup test database configuration +ConnectionManager::setConfig('default', [ + 'className' => Connection::class, + 'driver' => Mysql::class, + 'database' => 'test_openapi_theme', + 'username' => 'root', + 'password' => '', + 'encoding' => 'utf8mb4', + 'timezone' => 'UTC', + 'cacheMetadata' => true, + 'quoteIdentifiers' => false, + 'log' => false, +]); + +ConnectionManager::setConfig('test', [ + 'className' => Connection::class, + 'driver' => Mysql::class, + 'database' => 'test_openapi_theme_test', + 'username' => 'root', + 'password' => '', + 'encoding' => 'utf8mb4', + 'timezone' => 'UTC', + 'cacheMetadata' => true, + 'quoteIdentifiers' => false, + 'log' => false, +]); + +Cache::setConfig([ + '_cake_core_' => [ + 'engine' => 'File', + 'prefix' => 'cake_core_', + 'serialize' => true, + 'path' => TMP, + ], + '_cake_model_' => [ + 'engine' => 'File', + 'prefix' => 'cake_model_', + 'serialize' => true, + 'path' => TMP, + ], +]); + +Log::setConfig([ + 'debug' => [ + 'engine' => 'Cake\Log\Engine\FileLog', + 'path' => TMP . 'logs/', + 'file' => 'debug', + 'levels' => ['notice', 'info', 'debug'], + ], + 'error' => [ + 'engine' => 'Cake\Log\Engine\FileLog', + 'path' => TMP . 'logs/', + 'file' => 'error', + 'levels' => ['warning', 'error', 'critical', 'alert', 'emergency'], + ], +]); + +// Load test specific plugin configuration +Configure::load('app', 'default', false); diff --git a/tests/config/app.php b/tests/config/app.php new file mode 100644 index 0000000..bdec49e --- /dev/null +++ b/tests/config/app.php @@ -0,0 +1,57 @@ + [ + 'namespace' => 'TestApp', + 'encoding' => 'UTF-8', + 'defaultLocale' => 'en_US', + 'defaultTimezone' => 'UTC', + 'base' => false, + 'dir' => 'src', + 'webroot' => 'webroot', + 'wwwRoot' => WWW_ROOT, + 'fullBaseUrl' => false, + 'imageBaseUrl' => 'img/', + 'cssBaseUrl' => 'css/', + 'jsBaseUrl' => 'js/', + 'paths' => [ + 'plugins' => [dirname(dirname(__DIR__))], + 'templates' => [APP . 'Template' . DS], + 'locales' => [APP . 'Locale' . DS], + ], + ], + 'Security' => [ + 'salt' => 'test-salt-for-testing', + ], + 'Datasources' => [ + 'default' => [ + 'className' => Connection::class, + 'driver' => Mysql::class, + 'persistent' => false, + 'host' => 'localhost', + 'username' => 'root', + 'password' => '', + 'database' => 'test_openapi_theme', + 'encoding' => 'utf8mb4', + 'timezone' => 'UTC', + 'cacheMetadata' => true, + ], + 'test' => [ + 'className' => Connection::class, + 'driver' => Mysql::class, + 'persistent' => false, + 'host' => 'localhost', + 'username' => 'root', + 'password' => '', + 'database' => 'test_openapi_theme_test', + 'encoding' => 'utf8mb4', + 'timezone' => 'UTC', + 'cacheMetadata' => true, + ], + ], + 'debug' => true, +]; \ No newline at end of file diff --git a/tests/config/bootstrap.php b/tests/config/bootstrap.php new file mode 100644 index 0000000..0519ecb --- /dev/null +++ b/tests/config/bootstrap.php @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/test_app/config/app.php b/tests/test_app/config/app.php new file mode 100644 index 0000000..bf19bb6 --- /dev/null +++ b/tests/test_app/config/app.php @@ -0,0 +1,28 @@ + [ + 'namespace' => 'TestApp', + 'encoding' => 'UTF-8', + 'defaultLocale' => 'en_US', + 'defaultTimezone' => 'UTC', + 'base' => false, + 'dir' => 'src', + 'webroot' => 'webroot', + 'wwwRoot' => WWW_ROOT, + 'fullBaseUrl' => false, + 'imageBaseUrl' => 'img/', + 'cssBaseUrl' => 'css/', + 'jsBaseUrl' => 'js/', + 'paths' => [ + 'plugins' => [dirname(dirname(dirname(__DIR__)))], + 'templates' => [APP . 'Template' . DS], + 'locales' => [APP . 'Locale' . DS], + ], + ], + 'Security' => [ + 'salt' => 'test-salt-for-testing', + ], + 'debug' => true, +]; \ No newline at end of file diff --git a/tests/test_app/config/app_local.php b/tests/test_app/config/app_local.php new file mode 100644 index 0000000..29c708e --- /dev/null +++ b/tests/test_app/config/app_local.php @@ -0,0 +1,34 @@ + [ + 'default' => [ + 'className' => Connection::class, + 'driver' => Mysql::class, + 'persistent' => false, + 'host' => 'localhost', + 'username' => 'root', + 'password' => '', + 'database' => 'test_openapi_theme', + 'encoding' => 'utf8mb4', + 'timezone' => 'UTC', + 'cacheMetadata' => true, + ], + 'test' => [ + 'className' => Connection::class, + 'driver' => Mysql::class, + 'persistent' => false, + 'host' => 'localhost', + 'username' => 'root', + 'password' => '', + 'database' => 'test_openapi_theme_test', + 'encoding' => 'utf8mb4', + 'timezone' => 'UTC', + 'cacheMetadata' => true, + ], + ], +]; \ No newline at end of file diff --git a/tests/test_app/config/bootstrap.php b/tests/test_app/config/bootstrap.php new file mode 100644 index 0000000..15745c9 --- /dev/null +++ b/tests/test_app/config/bootstrap.php @@ -0,0 +1,6 @@ +addPlugin('Bake'); + $this->addPlugin('OpenApiTheme'); + } + + public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue + { + return $middlewareQueue; + } + + public function routes(RouteBuilder $routes): void + { + $routes->scope('/', function (RouteBuilder $builder): void { + $builder->fallbacks(); + }); + parent::routes($routes); + } +} \ No newline at end of file diff --git a/tests/test_app/src/Controller/AppController.php b/tests/test_app/src/Controller/AppController.php new file mode 100644 index 0000000..bb8d7d7 --- /dev/null +++ b/tests/test_app/src/Controller/AppController.php @@ -0,0 +1,10 @@ +