diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 0000000..a12b3fa
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,42 @@
+name: Tests PHP Versions
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ branches: [main]
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - php: 8.1
+ laravel: 10.*
+ - php: 8.2
+ laravel: 11.*
+ - php: 8.3
+ laravel: 11.*
+
+ name: P${{ matrix.php }} - L${{ matrix.laravel }}
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php }}
+ extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo
+ coverage: none
+
+ - name: Install dependencies
+ run: |
+ composer require "laravel/framework:${{ matrix.laravel }}" --no-interaction --no-update
+ composer update --prefer-dist --no-interaction --no-progress
+
+ - name: Execute tests
+ run: composer test
diff --git a/composer.json b/composer.json
index cd97a38..9df4160 100644
--- a/composer.json
+++ b/composer.json
@@ -25,6 +25,12 @@
"Waad\\ScrambleSwagger\\": "src/"
}
},
+ "autoload-dev": {
+ "psr-4": {
+ "Tests\\": "tests/",
+ "Workbench\\App\\": "workbench/app/"
+ }
+ },
"extra": {
"laravel": {
"providers": [
@@ -42,6 +48,18 @@
"prefer-stable": true,
"scripts": {
"lint": "vendor/bin/pint",
- "test": "vendor/bin/pest"
+ "test": "vendor/bin/pest",
+ "post-autoload-dump": [
+ "@clear",
+ "@prepare"
+ ],
+ "clear": "@php vendor/bin/testbench package:purge-skeleton --ansi",
+ "prepare": "@php vendor/bin/testbench package:discover --ansi",
+ "build": "@php vendor/bin/testbench workbench:build --ansi",
+ "serve": [
+ "Composer\\Config::disableProcessTimeout",
+ "@build",
+ "@php vendor/bin/testbench serve --ansi"
+ ]
}
}
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100644
index 0000000..9ae6fc7
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ ./tests/WithoutVersions
+ ./tests/WithVersions
+
+
+
+
+ ./src
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/Pest.php b/tests/Pest.php
new file mode 100644
index 0000000..3d54c25
--- /dev/null
+++ b/tests/Pest.php
@@ -0,0 +1,4 @@
+in('WithoutVersions');
+uses(Tests\TestCaseWithVersions::class)->in('WithVersions');
diff --git a/tests/TestCaseWithVersions.php b/tests/TestCaseWithVersions.php
new file mode 100644
index 0000000..e862d6f
--- /dev/null
+++ b/tests/TestCaseWithVersions.php
@@ -0,0 +1,34 @@
+set('scramble-swagger.versions', ['all', 'v1', 'v2']);
+ $app['config']->set('scramble-swagger.default_version', 'v2');
+ }
+
+ public function defineRoutes($router): void
+ {
+ $router->get('api/v1/test', [TestController::class, 'index']);
+ $router->get('api/v1/test/test', [TestController::class, 'index']);
+ $router->get('api/v2/test', [TestController::class, 'index']);
+ $router->get('api/v2/test/test', [TestController::class, 'index']);
+ }
+}
diff --git a/tests/TestCaseWithoutVersions.php b/tests/TestCaseWithoutVersions.php
new file mode 100644
index 0000000..65cc4c8
--- /dev/null
+++ b/tests/TestCaseWithoutVersions.php
@@ -0,0 +1,37 @@
+set('scramble-swagger', [
+ // 'enable' => true,
+ // 'url' => 'docs/swagger',
+ // 'versions' => [
+ // 'all',
+ // ],
+ // 'default_version' => 'all',
+ // ]);
+ }
+
+ public function defineRoutes($router): void
+ {
+ $router->get('api/test', [TestController::class, 'index']);
+ }
+}
diff --git a/tests/WithVersions/ApiDocumentationTest.php b/tests/WithVersions/ApiDocumentationTest.php
new file mode 100644
index 0000000..37ba6a5
--- /dev/null
+++ b/tests/WithVersions/ApiDocumentationTest.php
@@ -0,0 +1,64 @@
+status())->toBe(200);
+ $json = $response->getContent();
+ expect($json)->toBeJson();
+
+ // main
+ $array = json_decode($json, true);
+ expect($array)->toHaveKey('openapi');
+ expect($array)->toHaveKey('info');
+ expect($array)->toHaveKey('servers');
+ expect($array)->toHaveKey('paths');
+ expect($array)->toHaveKey('components');
+
+ // sub
+ expect($array)->toHaveKey('servers.0.url');
+ expect($array)->toHaveKey('info.title');
+ expect($array)->toHaveKey('info.version');
+ expect($array)->toHaveKey('paths./v1/test');
+ expect($array)->toHaveKey('paths./v1/test/test');
+ expect($array)->toHaveKey('paths./v2/test');
+ expect($array)->toHaveKey('paths./v2/test/test');
+ expect($array)->toHaveKey('components.responses');
+ expect($array)->toHaveKey('components.paths');
+});
+
+it('test controller endpoint returns correct data', function () {
+ $response = get('api/v2/test?per_page=2&page=1');
+
+ $response->assertStatus(200)
+ ->assertJson([
+ ['id' => 1, 'name' => 'John Doe'],
+ ['id' => 2, 'name' => 'Jane Ronaldo'],
+ ]);
+});
+
+it('test controller search returns filtered results', function () {
+ $response = get('api/v2/test?per_page=5&search=John');
+
+ $response->assertStatus(200)
+ ->assertJsonCount(2)
+ ->assertJsonFragment(['name' => 'John Doe'])
+ ->assertJsonFragment(['name' => 'John Smith']);
+});
+
+it('test controller pagination works correctly', function () {
+ $response = get('api/v2/test?per_page=3&page=2');
+
+ $response->assertStatus(200)
+ ->assertJsonCount(3)
+ ->assertJsonFragment(['id' => 4])
+ ->assertJsonFragment(['id' => 5])
+ ->assertJsonFragment(['id' => 6]);
+});
+
+it('test controller validates required parameters', function () {
+ $response = get('api/v2/test');
+ expect($response->status())->toBe(422);
+});
diff --git a/tests/WithVersions/SwaggerTest.php b/tests/WithVersions/SwaggerTest.php
new file mode 100644
index 0000000..272e448
--- /dev/null
+++ b/tests/WithVersions/SwaggerTest.php
@@ -0,0 +1,32 @@
+status())->toBe(200)
+ ->and($response->getContent())->toContain('
');
+});
+
+it('swagger ui endpoint is accessible and contains all versions', function () {
+ $response = get('docs/swagger');
+ expect($response->status())->toBe(200);
+
+ expect($response->getContent())
+ ->toContain('')
+ ->toContain("url: 'http://localhost/docs/swagger/json?version=all'")
+ ->toContain("url: 'http://localhost/docs/swagger/json?version=v1'")
+ ->toContain("url: 'http://localhost/docs/swagger/json?version=v2'");
+});
+
+it('swagger ui is disabled when config is false', function () {
+ config(['scramble-swagger.enable' => false]);
+
+ $response = get('docs/swagger');
+ expect($response->status())->toBe(404);
+});
+
+it('swagger ui url can be configured', function () {
+ $response = get('doc/doc');
+ expect($response->status())->toBe(404);
+});
diff --git a/tests/WithoutVersions/ApiDocumentationTest.php b/tests/WithoutVersions/ApiDocumentationTest.php
new file mode 100644
index 0000000..a0364b9
--- /dev/null
+++ b/tests/WithoutVersions/ApiDocumentationTest.php
@@ -0,0 +1,61 @@
+status())->toBe(200);
+ $json = $response->getContent();
+ expect($json)->toBeJson();
+
+ // main
+ $array = json_decode($json, true);
+ expect($array)->toHaveKey('openapi');
+ expect($array)->toHaveKey('info');
+ expect($array)->toHaveKey('servers');
+ expect($array)->toHaveKey('paths');
+ expect($array)->toHaveKey('components');
+
+ // sub
+ expect($array)->toHaveKey('servers.0.url');
+ expect($array)->toHaveKey('info.title');
+ expect($array)->toHaveKey('info.version');
+ expect($array)->toHaveKey('paths./test');
+ expect($array)->toHaveKey('components.responses');
+ expect($array)->toHaveKey('components.paths');
+});
+
+it('test controller endpoint returns correct data', function () {
+ $response = get('api/test?per_page=2&page=1');
+
+ $response->assertStatus(200)
+ ->assertJson([
+ ['id' => 1, 'name' => 'John Doe'],
+ ['id' => 2, 'name' => 'Jane Ronaldo'],
+ ]);
+});
+
+it('test controller search returns filtered results', function () {
+ $response = get('api/test?per_page=5&search=John');
+
+ $response->assertStatus(200)
+ ->assertJsonCount(2)
+ ->assertJsonFragment(['name' => 'John Doe'])
+ ->assertJsonFragment(['name' => 'John Smith']);
+});
+
+it('test controller pagination works correctly', function () {
+ $response = get('api/test?per_page=3&page=2');
+
+ $response->assertStatus(200)
+ ->assertJsonCount(3)
+ ->assertJsonFragment(['id' => 4])
+ ->assertJsonFragment(['id' => 5])
+ ->assertJsonFragment(['id' => 6]);
+});
+
+it('test controller validates required parameters', function () {
+ $response = get('api/test');
+ expect($response->status())->toBe(422);
+});
diff --git a/tests/WithoutVersions/SwaggerTest.php b/tests/WithoutVersions/SwaggerTest.php
new file mode 100644
index 0000000..b195c47
--- /dev/null
+++ b/tests/WithoutVersions/SwaggerTest.php
@@ -0,0 +1,23 @@
+status())->toBe(200)
+ ->and($response->getContent())
+ ->toContain('')
+ ->toContain("url: 'http://localhost/docs/swagger/json?version=all'");
+});
+
+it('swagger ui is disabled when config is false', function () {
+ config(['scramble-swagger.enable' => false]);
+
+ $response = get('docs/swagger');
+ expect($response->status())->toBe(404);
+});
+
+it('swagger ui url can be configured', function () {
+ $response = get('doc/doc');
+ expect($response->status())->toBe(404);
+});
diff --git a/workbench/app/Http/Controllers/TestController.php b/workbench/app/Http/Controllers/TestController.php
new file mode 100644
index 0000000..f8c10a8
--- /dev/null
+++ b/workbench/app/Http/Controllers/TestController.php
@@ -0,0 +1,51 @@
+ 1, 'name' => 'John Doe'],
+ ['id' => 2, 'name' => 'Jane Ronaldo'],
+ ['id' => 3, 'name' => 'John Smith'],
+ ['id' => 4, 'name' => 'Jane Messi'],
+ ['id' => 5, 'name' => 'Mario Khabib'],
+ ['id' => 6, 'name' => 'Luigi Jordin'],
+ ['id' => 7, 'name' => 'Mario Sami'],
+ ['id' => 8, 'name' => 'Roni Maikel'],
+ ['id' => 9, 'name' => 'Sarah Connor'],
+ ['id' => 10, 'name' => 'Tony Stark'],
+ ['id' => 11, 'name' => 'Bruce Wayne'],
+ ['id' => 12, 'name' => 'Peter Parker'],
+ ['id' => 13, 'name' => 'Clark Kent'],
+ ];
+
+ public function index(Request $request)
+ {
+ try {
+ $request->validate([
+ 'per_page' => 'required|integer',
+ 'page' => 'nullable|integer',
+ 'search' => 'nullable|string',
+ ]);
+ } catch (\Illuminate\Validation\ValidationException $e) {
+ return response()->json(['error' => $e->errors()], 422);
+ }
+
+ $page = $request->page ?? 1;
+ $perPage = $request->per_page ?? 5;
+ $search = $request->search ?? null;
+
+ $data = collect(self::DATA);
+
+ if ($search) {
+ $data = $data->filter(function ($item) use ($search) {
+ return str_contains($item['name'], $search);
+ });
+ }
+
+ return $data->skip(($page - 1) * $perPage)->take($perPage)->toArray();
+ }
+}
diff --git a/workbench/app/Models/.gitkeep b/workbench/app/Models/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/workbench/app/Providers/WorkbenchServiceProvider.php b/workbench/app/Providers/WorkbenchServiceProvider.php
new file mode 100644
index 0000000..de910c2
--- /dev/null
+++ b/workbench/app/Providers/WorkbenchServiceProvider.php
@@ -0,0 +1,24 @@
+usePublicPath(__DIR__.'/../public');
+ }
+
+ /**
+ * Bootstrap services.
+ */
+ public function boot(): void
+ {
+ //
+ }
+}
diff --git a/workbench/app/public/.gitkeep b/workbench/app/public/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/workbench/app/public/scramble-swagger/.gitignore b/workbench/app/public/scramble-swagger/.gitignore
new file mode 100644
index 0000000..ac23051
--- /dev/null
+++ b/workbench/app/public/scramble-swagger/.gitignore
@@ -0,0 +1,2 @@
+*.*
+!.gitignore
\ No newline at end of file