Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c1f450c4b | ||
|
|
f417ec23c2 |
55
.github/workflows/tests.yml
vendored
Normal file
55
.github/workflows/tests.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
name: tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ci:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup PHP
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: 8.3
|
||||||
|
tools: composer:v2
|
||||||
|
coverage: xdebug
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: composer install --no-interaction --prefer-dist --optimize-autoloader
|
||||||
|
|
||||||
|
- name: Copy Environment File
|
||||||
|
run: cp .env.example .env
|
||||||
|
|
||||||
|
- name: Generate Application Key
|
||||||
|
run: php artisan key:generate
|
||||||
|
|
||||||
|
- name: Create Database
|
||||||
|
run: touch database/database.sqlite
|
||||||
|
|
||||||
|
- name: Rector Cache
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: /tmp/rector
|
||||||
|
key: ${{ runner.os }}-rector-${{ hashFiles('composer.lock') }}
|
||||||
|
restore-keys: ${{ runner.os }}-rector-
|
||||||
|
- run: mkdir -p /tmp/rector
|
||||||
|
|
||||||
|
- name: PHPStan Cache
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: /tmp/phpstan
|
||||||
|
key: ${{ runner.os }}-phpstan-${{ hashFiles('composer.lock') }}
|
||||||
|
restore-keys: ${{ runner.os }}-phpstan-
|
||||||
|
- run: mkdir -p /tmp/phpstan
|
||||||
|
|
||||||
|
- name: Tests
|
||||||
|
run: composer test
|
||||||
@@ -2,6 +2,11 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
## [2.1.0](https://github.com/Grazulex/laravel-api-kit/releases/tag/v2.1.0) (2026-01-20)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- integrate PHPStan, Rector and Pint code quality tools (#13) ([f417ec2](https://github.com/Grazulex/laravel-api-kit/commit/f417ec23c2eada4eaddf3ec9e4cff7ccc71a3572))
|
||||||
## [2.0.2](https://github.com/Grazulex/laravel-api-kit/releases/tag/v2.0.2) (2026-01-08)
|
## [2.0.2](https://github.com/Grazulex/laravel-api-kit/releases/tag/v2.0.2) (2026-01-08)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|||||||
58
README.md
58
README.md
@@ -15,6 +15,7 @@ A production-ready, API-only Laravel 12 starter kit following the 2024-2025 REST
|
|||||||
- **Data Objects** - Type-safe DTOs via [spatie/laravel-data](https://github.com/spatie/laravel-data)
|
- **Data Objects** - Type-safe DTOs via [spatie/laravel-data](https://github.com/spatie/laravel-data)
|
||||||
- **Auto Documentation** - Zero-annotation OpenAPI 3.1 via [dedoc/scramble](https://github.com/dedoc/scramble)
|
- **Auto Documentation** - Zero-annotation OpenAPI 3.1 via [dedoc/scramble](https://github.com/dedoc/scramble)
|
||||||
- **Modern Testing** - Pest PHP with Laravel HTTP testing
|
- **Modern Testing** - Pest PHP with Laravel HTTP testing
|
||||||
|
- **Code Quality** - PHPStan (max level), Rector, and Pint with strict rules
|
||||||
- **Rate Limiting** - Configurable per-route rate limiters
|
- **Rate Limiting** - Configurable per-route rate limiters
|
||||||
- **Standardized Responses** - Consistent JSON response format
|
- **Standardized Responses** - Consistent JSON response format
|
||||||
|
|
||||||
@@ -435,15 +436,60 @@ it('requires authentication', function () {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Code Quality
|
||||||
|
|
||||||
|
This kit includes strict code quality tools configured following [nunomaduro/laravel-starter-kit](https://github.com/nunomaduro/laravel-starter-kit) standards.
|
||||||
|
|
||||||
|
### Tools
|
||||||
|
|
||||||
|
| Tool | Purpose | Config |
|
||||||
|
|------|---------|--------|
|
||||||
|
| [PHPStan](https://phpstan.org/) + [Larastan](https://github.com/larastan/larastan) | Static analysis (level max) | `phpstan.neon` |
|
||||||
|
| [Rector](https://getrector.com/) | Automated refactoring | `rector.php` |
|
||||||
|
| [Pint](https://laravel.com/docs/pint) | Code style (strict rules) | `pint.json` |
|
||||||
|
|
||||||
|
### Composer Scripts
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Apply all fixes (Rector + Pint)
|
||||||
|
composer lint
|
||||||
|
|
||||||
|
# Check without fixing (CI mode)
|
||||||
|
composer test:lint
|
||||||
|
|
||||||
|
# Static analysis only
|
||||||
|
composer test:types
|
||||||
|
|
||||||
|
# Unit tests only
|
||||||
|
composer test:unit
|
||||||
|
|
||||||
|
# Full test suite (lint + types + unit)
|
||||||
|
composer test
|
||||||
|
```
|
||||||
|
|
||||||
|
### With Docker
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose exec app composer lint
|
||||||
|
docker compose exec app composer test
|
||||||
|
```
|
||||||
|
|
||||||
|
### Strict Rules Applied
|
||||||
|
|
||||||
|
- `declare(strict_types=1)` on all files
|
||||||
|
- `final` classes by default
|
||||||
|
- Type declarations enforced
|
||||||
|
- Dead code removal
|
||||||
|
- Early returns
|
||||||
|
- Strict comparisons
|
||||||
|
|
||||||
|
### GitHub Actions
|
||||||
|
|
||||||
|
Tests run automatically on push/PR to `main` via `.github/workflows/tests.yml`.
|
||||||
|
|
||||||
## Development Commands
|
## Development Commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Code formatting (Laravel Pint)
|
|
||||||
docker compose run --rm app ./vendor/bin/pint
|
|
||||||
|
|
||||||
# Check code style without fixing
|
|
||||||
docker compose run --rm app ./vendor/bin/pint --test
|
|
||||||
|
|
||||||
# List all routes
|
# List all routes
|
||||||
docker compose run --rm app php artisan route:list
|
docker compose run --rm app php artisan route:list
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Http\Controllers\Api;
|
namespace App\Http\Controllers\Api;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Http\Controllers\Api\V1;
|
namespace App\Http\Controllers\Api\V1;
|
||||||
|
|
||||||
use App\Http\Controllers\Api\ApiController;
|
use App\Http\Controllers\Api\ApiController;
|
||||||
@@ -11,11 +13,11 @@ use Illuminate\Http\JsonResponse;
|
|||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
|
||||||
class AuthController extends ApiController
|
final class AuthController extends ApiController
|
||||||
{
|
{
|
||||||
public function register(RegisterRequest $request): JsonResponse
|
public function register(RegisterRequest $request): JsonResponse
|
||||||
{
|
{
|
||||||
$user = User::create([
|
$user = User::query()->create([
|
||||||
'name' => $request->name,
|
'name' => $request->name,
|
||||||
'email' => $request->email,
|
'email' => $request->email,
|
||||||
'password' => Hash::make($request->password),
|
'password' => Hash::make($request->password),
|
||||||
@@ -31,7 +33,7 @@ class AuthController extends ApiController
|
|||||||
|
|
||||||
public function login(LoginRequest $request): JsonResponse
|
public function login(LoginRequest $request): JsonResponse
|
||||||
{
|
{
|
||||||
$user = User::where('email', $request->email)->first();
|
$user = User::query()->where('email', $request->email)->first();
|
||||||
|
|
||||||
if (! $user || ! Hash::check($request->password, $user->password)) {
|
if (! $user || ! Hash::check($request->password, $user->password)) {
|
||||||
return $this->unauthorized('Invalid credentials');
|
return $this->unauthorized('Invalid credentials');
|
||||||
@@ -47,7 +49,9 @@ class AuthController extends ApiController
|
|||||||
|
|
||||||
public function logout(Request $request): JsonResponse
|
public function logout(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$request->user()->currentAccessToken()->delete();
|
/** @var User $user */
|
||||||
|
$user = $request->user();
|
||||||
|
$user->currentAccessToken()->delete();
|
||||||
|
|
||||||
return $this->success(message: 'Logged out successfully');
|
return $this->success(message: 'Logged out successfully');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
abstract class Controller
|
abstract class Controller
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Http\Requests\Api\V1;
|
namespace App\Http\Requests\Api\V1;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\Validation\ValidationRule;
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
class LoginRequest extends FormRequest
|
/**
|
||||||
|
* @property string $email
|
||||||
|
* @property string $password
|
||||||
|
*/
|
||||||
|
final class LoginRequest extends FormRequest
|
||||||
{
|
{
|
||||||
public function authorize(): bool
|
public function authorize(): bool
|
||||||
{
|
{
|
||||||
@@ -12,7 +19,7 @@ class LoginRequest extends FormRequest
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
* @return array<string, ValidationRule|array<mixed>|string>
|
||||||
*/
|
*/
|
||||||
public function rules(): array
|
public function rules(): array
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,10 +1,18 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Http\Requests\Api\V1;
|
namespace App\Http\Requests\Api\V1;
|
||||||
|
|
||||||
|
use Illuminate\Contracts\Validation\ValidationRule;
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
class RegisterRequest extends FormRequest
|
/**
|
||||||
|
* @property string $name
|
||||||
|
* @property string $email
|
||||||
|
* @property string $password
|
||||||
|
*/
|
||||||
|
final class RegisterRequest extends FormRequest
|
||||||
{
|
{
|
||||||
public function authorize(): bool
|
public function authorize(): bool
|
||||||
{
|
{
|
||||||
@@ -12,7 +20,7 @@ class RegisterRequest extends FormRequest
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
* @return array<string, ValidationRule|array<mixed>|string>
|
||||||
*/
|
*/
|
||||||
public function rules(): array
|
public function rules(): array
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Http\Resources;
|
namespace App\Http\Resources;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Http\Resources\Json\JsonResource;
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @mixin \App\Models\User
|
* @mixin User
|
||||||
*/
|
*/
|
||||||
class UserResource extends JsonResource
|
final class UserResource extends JsonResource
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
|
|||||||
@@ -1,17 +1,34 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||||
|
use Database\Factories\UserFactory;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
use Laravel\Sanctum\HasApiTokens;
|
use Laravel\Sanctum\HasApiTokens;
|
||||||
|
|
||||||
class User extends Authenticatable
|
/**
|
||||||
|
* @property int $id
|
||||||
|
* @property string $name
|
||||||
|
* @property string $email
|
||||||
|
* @property Carbon|null $email_verified_at
|
||||||
|
* @property string $password
|
||||||
|
* @property Carbon|null $created_at
|
||||||
|
* @property Carbon|null $updated_at
|
||||||
|
*/
|
||||||
|
final class User extends Authenticatable
|
||||||
{
|
{
|
||||||
/** @use HasFactory<\Database\Factories\UserFactory> */
|
use HasApiTokens;
|
||||||
use HasApiTokens, HasFactory, Notifiable;
|
|
||||||
|
/** @use HasFactory<UserFactory> */
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
use Notifiable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The attributes that are mass assignable.
|
* The attributes that are mass assignable.
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
use Illuminate\Cache\RateLimiting\Limit;
|
use Illuminate\Cache\RateLimiting\Limit;
|
||||||
@@ -7,7 +9,7 @@ use Illuminate\Http\Request;
|
|||||||
use Illuminate\Support\Facades\RateLimiter;
|
use Illuminate\Support\Facades\RateLimiter;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
final class AppServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Register any application services.
|
* Register any application services.
|
||||||
@@ -28,23 +30,17 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
/**
|
/**
|
||||||
* Configure the rate limiters for the application.
|
* Configure the rate limiters for the application.
|
||||||
*/
|
*/
|
||||||
protected function configureRateLimiting(): void
|
private function configureRateLimiting(): void
|
||||||
{
|
{
|
||||||
// Default API rate limiter - 60 requests per minute
|
// Default API rate limiter - 60 requests per minute
|
||||||
RateLimiter::for('api', function (Request $request) {
|
RateLimiter::for('api', fn (Request $request) => Limit::perMinute(60)->by($request->user()?->id ?: $request->ip()));
|
||||||
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
|
|
||||||
});
|
|
||||||
|
|
||||||
// Auth endpoints - more restrictive (prevent brute force)
|
// Auth endpoints - more restrictive (prevent brute force)
|
||||||
RateLimiter::for('auth', function (Request $request) {
|
RateLimiter::for('auth', fn (Request $request) => Limit::perMinute(5)->by($request->ip()));
|
||||||
return Limit::perMinute(5)->by($request->ip());
|
|
||||||
});
|
|
||||||
|
|
||||||
// Authenticated user requests - higher limit
|
// Authenticated user requests - higher limit
|
||||||
RateLimiter::for('authenticated', function (Request $request) {
|
RateLimiter::for('authenticated', fn (Request $request) => $request->user()
|
||||||
return $request->user()
|
? Limit::perMinute(120)->by($request->user()->id)
|
||||||
? Limit::perMinute(120)->by($request->user()->id)
|
: Limit::perMinute(60)->by($request->ip()));
|
||||||
: Limit::perMinute(60)->by($request->ip());
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Traits;
|
namespace App\Traits;
|
||||||
|
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
@@ -31,6 +33,9 @@ trait ApiResponse
|
|||||||
return response()->json(null, Response::HTTP_NO_CONTENT);
|
return response()->json(null, Response::HTTP_NO_CONTENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $errors
|
||||||
|
*/
|
||||||
protected function error(
|
protected function error(
|
||||||
string $message = 'Error',
|
string $message = 'Error',
|
||||||
int $code = Response::HTTP_BAD_REQUEST,
|
int $code = Response::HTTP_BAD_REQUEST,
|
||||||
@@ -41,7 +46,7 @@ trait ApiResponse
|
|||||||
'message' => $message,
|
'message' => $message,
|
||||||
];
|
];
|
||||||
|
|
||||||
if (! empty($errors)) {
|
if ($errors !== []) {
|
||||||
$response['errors'] = $errors;
|
$response['errors'] = $errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,6 +68,9 @@ trait ApiResponse
|
|||||||
return $this->error($message, Response::HTTP_FORBIDDEN);
|
return $this->error($message, Response::HTTP_FORBIDDEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $errors
|
||||||
|
*/
|
||||||
protected function validationError(array $errors, string $message = 'Validation failed'): JsonResponse
|
protected function validationError(array $errors, string $message = 'Validation failed'): JsonResponse
|
||||||
{
|
{
|
||||||
return $this->error($message, Response::HTTP_UNPROCESSABLE_ENTITY, $errors);
|
return $this->error($message, Response::HTTP_UNPROCESSABLE_ENTITY, $errors);
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
use Illuminate\Foundation\Application;
|
use Illuminate\Foundation\Application;
|
||||||
use Illuminate\Foundation\Configuration\Exceptions;
|
use Illuminate\Foundation\Configuration\Exceptions;
|
||||||
use Illuminate\Foundation\Configuration\Middleware;
|
use Illuminate\Foundation\Configuration\Middleware;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
App\Providers\AppServiceProvider::class,
|
App\Providers\AppServiceProvider::class,
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -16,13 +16,16 @@
|
|||||||
"spatie/laravel-query-builder": "^6.0"
|
"spatie/laravel-query-builder": "^6.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
"driftingly/rector-laravel": "^2.0",
|
||||||
"fakerphp/faker": "^1.23",
|
"fakerphp/faker": "^1.23",
|
||||||
|
"larastan/larastan": "^3.7",
|
||||||
"laravel/pail": "^1.2.2",
|
"laravel/pail": "^1.2.2",
|
||||||
"laravel/pint": "^1.24",
|
"laravel/pint": "^1.24",
|
||||||
"mockery/mockery": "^1.6",
|
"mockery/mockery": "^1.6",
|
||||||
"nunomaduro/collision": "^8.6",
|
"nunomaduro/collision": "^8.6",
|
||||||
"pestphp/pest": "^4.0",
|
"pestphp/pest": "^4.0",
|
||||||
"pestphp/pest-plugin-laravel": "^4.0"
|
"pestphp/pest-plugin-laravel": "^4.0",
|
||||||
|
"rector/rector": "^2.2"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
@@ -37,10 +40,26 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": [
|
"lint": [
|
||||||
|
"rector",
|
||||||
|
"pint"
|
||||||
|
],
|
||||||
|
"test:lint": [
|
||||||
|
"pint --test",
|
||||||
|
"rector --dry-run"
|
||||||
|
],
|
||||||
|
"test:types": [
|
||||||
|
"phpstan"
|
||||||
|
],
|
||||||
|
"test:unit": [
|
||||||
"@php artisan config:clear --ansi",
|
"@php artisan config:clear --ansi",
|
||||||
"@php artisan test"
|
"@php artisan test"
|
||||||
],
|
],
|
||||||
|
"test": [
|
||||||
|
"@test:lint",
|
||||||
|
"@test:types",
|
||||||
|
"@test:unit"
|
||||||
|
],
|
||||||
"post-autoload-dump": [
|
"post-autoload-dump": [
|
||||||
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
|
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
|
||||||
"@php artisan package:discover --ansi"
|
"@php artisan package:discover --ansi"
|
||||||
|
|||||||
282
composer.lock
generated
282
composer.lock
generated
@@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "07247fcb148cb2f2dd5ce91bdef3c5e1",
|
"content-hash": "76745da02f7c3b291785aa3cd83bd614",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "brick/math",
|
"name": "brick/math",
|
||||||
@@ -7090,6 +7090,42 @@
|
|||||||
],
|
],
|
||||||
"time": "2026-01-08T07:23:06+00:00"
|
"time": "2026-01-08T07:23:06+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "driftingly/rector-laravel",
|
||||||
|
"version": "2.1.9",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/driftingly/rector-laravel.git",
|
||||||
|
"reference": "aee9d4a1d489e7ec484fc79f33137f8ee051b3f7"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/driftingly/rector-laravel/zipball/aee9d4a1d489e7ec484fc79f33137f8ee051b3f7",
|
||||||
|
"reference": "aee9d4a1d489e7ec484fc79f33137f8ee051b3f7",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.4 || ^8.0",
|
||||||
|
"rector/rector": "^2.2.7",
|
||||||
|
"webmozart/assert": "^1.11"
|
||||||
|
},
|
||||||
|
"type": "rector-extension",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"RectorLaravel\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"description": "Rector upgrades rules for Laravel Framework",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/driftingly/rector-laravel/issues",
|
||||||
|
"source": "https://github.com/driftingly/rector-laravel/tree/2.1.9"
|
||||||
|
},
|
||||||
|
"time": "2025-12-25T23:31:36+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "fakerphp/faker",
|
"name": "fakerphp/faker",
|
||||||
"version": "v1.24.1",
|
"version": "v1.24.1",
|
||||||
@@ -7336,6 +7372,47 @@
|
|||||||
},
|
},
|
||||||
"time": "2025-04-30T06:54:44+00:00"
|
"time": "2025-04-30T06:54:44+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "iamcal/sql-parser",
|
||||||
|
"version": "v0.6",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/iamcal/SQLParser.git",
|
||||||
|
"reference": "947083e2dca211a6f12fb1beb67a01e387de9b62"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/iamcal/SQLParser/zipball/947083e2dca211a6f12fb1beb67a01e387de9b62",
|
||||||
|
"reference": "947083e2dca211a6f12fb1beb67a01e387de9b62",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"php-coveralls/php-coveralls": "^1.0",
|
||||||
|
"phpunit/phpunit": "^5|^6|^7|^8|^9"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"iamcal\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Cal Henderson",
|
||||||
|
"email": "cal@iamcal.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "MySQL schema parser",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/iamcal/SQLParser/issues",
|
||||||
|
"source": "https://github.com/iamcal/SQLParser/tree/v0.6"
|
||||||
|
},
|
||||||
|
"time": "2025-03-17T16:59:46+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "jean85/pretty-package-versions",
|
"name": "jean85/pretty-package-versions",
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
@@ -7396,6 +7473,96 @@
|
|||||||
},
|
},
|
||||||
"time": "2025-03-19T14:43:43+00:00"
|
"time": "2025-03-19T14:43:43+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "larastan/larastan",
|
||||||
|
"version": "v3.9.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/larastan/larastan.git",
|
||||||
|
"reference": "82c18890d0d5b012bc39a3432531e5b6cd1b4b3a"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/larastan/larastan/zipball/82c18890d0d5b012bc39a3432531e5b6cd1b4b3a",
|
||||||
|
"reference": "82c18890d0d5b012bc39a3432531e5b6cd1b4b3a",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-json": "*",
|
||||||
|
"iamcal/sql-parser": "^0.6.0",
|
||||||
|
"illuminate/console": "^11.44.2 || ^12.4.1",
|
||||||
|
"illuminate/container": "^11.44.2 || ^12.4.1",
|
||||||
|
"illuminate/contracts": "^11.44.2 || ^12.4.1",
|
||||||
|
"illuminate/database": "^11.44.2 || ^12.4.1",
|
||||||
|
"illuminate/http": "^11.44.2 || ^12.4.1",
|
||||||
|
"illuminate/pipeline": "^11.44.2 || ^12.4.1",
|
||||||
|
"illuminate/support": "^11.44.2 || ^12.4.1",
|
||||||
|
"php": "^8.2",
|
||||||
|
"phpstan/phpstan": "^2.1.32"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"doctrine/coding-standard": "^13",
|
||||||
|
"laravel/framework": "^11.44.2 || ^12.7.2",
|
||||||
|
"mockery/mockery": "^1.6.12",
|
||||||
|
"nikic/php-parser": "^5.4",
|
||||||
|
"orchestra/canvas": "^v9.2.2 || ^10.0.1",
|
||||||
|
"orchestra/testbench-core": "^9.12.0 || ^10.1",
|
||||||
|
"phpstan/phpstan-deprecation-rules": "^2.0.1",
|
||||||
|
"phpunit/phpunit": "^10.5.35 || ^11.5.15"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"orchestra/testbench": "Using Larastan for analysing a package needs Testbench",
|
||||||
|
"phpmyadmin/sql-parser": "Install to enable Larastan's optional phpMyAdmin-based SQL parser automatically"
|
||||||
|
},
|
||||||
|
"type": "phpstan-extension",
|
||||||
|
"extra": {
|
||||||
|
"phpstan": {
|
||||||
|
"includes": [
|
||||||
|
"extension.neon"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "3.0-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Larastan\\Larastan\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Can Vural",
|
||||||
|
"email": "can9119@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel",
|
||||||
|
"keywords": [
|
||||||
|
"PHPStan",
|
||||||
|
"code analyse",
|
||||||
|
"code analysis",
|
||||||
|
"larastan",
|
||||||
|
"laravel",
|
||||||
|
"package",
|
||||||
|
"php",
|
||||||
|
"static analysis"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/larastan/larastan/issues",
|
||||||
|
"source": "https://github.com/larastan/larastan/tree/v3.9.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/canvural",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2026-01-17T23:00:37+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "laravel/pail",
|
"name": "laravel/pail",
|
||||||
"version": "v1.2.4",
|
"version": "v1.2.4",
|
||||||
@@ -8304,6 +8471,59 @@
|
|||||||
},
|
},
|
||||||
"time": "2022-02-21T01:04:05+00:00"
|
"time": "2022-02-21T01:04:05+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "phpstan/phpstan",
|
||||||
|
"version": "2.1.35",
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/72f843c7f59d3aac0b7510f5e70913a9b72a8e88",
|
||||||
|
"reference": "72f843c7f59d3aac0b7510f5e70913a9b72a8e88",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.4|^8.0"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"phpstan/phpstan-shim": "*"
|
||||||
|
},
|
||||||
|
"bin": [
|
||||||
|
"phpstan",
|
||||||
|
"phpstan.phar"
|
||||||
|
],
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"bootstrap.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"description": "PHPStan - PHP Static Analysis Tool",
|
||||||
|
"keywords": [
|
||||||
|
"dev",
|
||||||
|
"static analysis"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"docs": "https://phpstan.org/user-guide/getting-started",
|
||||||
|
"forum": "https://github.com/phpstan/phpstan/discussions",
|
||||||
|
"issues": "https://github.com/phpstan/phpstan/issues",
|
||||||
|
"security": "https://github.com/phpstan/phpstan/security/policy",
|
||||||
|
"source": "https://github.com/phpstan/phpstan-src"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/ondrejmirtes",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/phpstan",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2026-01-20T17:33:48+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/php-code-coverage",
|
"name": "phpunit/php-code-coverage",
|
||||||
"version": "12.5.2",
|
"version": "12.5.2",
|
||||||
@@ -8743,6 +8963,66 @@
|
|||||||
],
|
],
|
||||||
"time": "2025-12-15T06:05:34+00:00"
|
"time": "2025-12-15T06:05:34+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "rector/rector",
|
||||||
|
"version": "2.3.2",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/rectorphp/rector.git",
|
||||||
|
"reference": "07cbbe28bd60251b96b18d42e514779b0e2faa83"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/rectorphp/rector/zipball/07cbbe28bd60251b96b18d42e514779b0e2faa83",
|
||||||
|
"reference": "07cbbe28bd60251b96b18d42e514779b0e2faa83",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.4|^8.0",
|
||||||
|
"phpstan/phpstan": "^2.1.34"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"rector/rector-doctrine": "*",
|
||||||
|
"rector/rector-downgrade-php": "*",
|
||||||
|
"rector/rector-phpunit": "*",
|
||||||
|
"rector/rector-symfony": "*"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-dom": "To manipulate phpunit.xml via the custom-rule command"
|
||||||
|
},
|
||||||
|
"bin": [
|
||||||
|
"bin/rector"
|
||||||
|
],
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"bootstrap.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"description": "Instant Upgrade and Automated Refactoring of any PHP code",
|
||||||
|
"homepage": "https://getrector.com/",
|
||||||
|
"keywords": [
|
||||||
|
"automation",
|
||||||
|
"dev",
|
||||||
|
"migration",
|
||||||
|
"refactoring"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/rectorphp/rector/issues",
|
||||||
|
"source": "https://github.com/rectorphp/rector/tree/2.3.2"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/tomasvotruba",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2026-01-20T01:11:51+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "sebastian/cli-parser",
|
"name": "sebastian/cli-parser",
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -62,7 +66,7 @@ return [
|
|||||||
'providers' => [
|
'providers' => [
|
||||||
'users' => [
|
'users' => [
|
||||||
'driver' => 'eloquent',
|
'driver' => 'eloquent',
|
||||||
'model' => env('AUTH_MODEL', App\Models\User::class),
|
'model' => env('AUTH_MODEL', User::class),
|
||||||
],
|
],
|
||||||
|
|
||||||
// 'users' => [
|
// 'users' => [
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@@ -59,7 +61,7 @@ return [
|
|||||||
'strict' => true,
|
'strict' => true,
|
||||||
'engine' => null,
|
'engine' => null,
|
||||||
'options' => extension_loaded('pdo_mysql') ? array_filter([
|
'options' => extension_loaded('pdo_mysql') ? array_filter([
|
||||||
(PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),
|
(PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),
|
||||||
]) : [],
|
]) : [],
|
||||||
],
|
],
|
||||||
|
|
||||||
@@ -79,7 +81,7 @@ return [
|
|||||||
'strict' => true,
|
'strict' => true,
|
||||||
'engine' => null,
|
'engine' => null,
|
||||||
'options' => extension_loaded('pdo_mysql') ? array_filter([
|
'options' => extension_loaded('pdo_mysql') ? array_filter([
|
||||||
(PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : \PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),
|
(PHP_VERSION_ID >= 80500 ? \Pdo\Mysql::ATTR_SSL_CA : PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'),
|
||||||
]) : [],
|
]) : [],
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
use Monolog\Handler\NullHandler;
|
use Monolog\Handler\NullHandler;
|
||||||
use Monolog\Handler\StreamHandler;
|
use Monolog\Handler\StreamHandler;
|
||||||
use Monolog\Handler\SyslogUdpHandler;
|
use Monolog\Handler\SyslogUdpHandler;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Illuminate\Cookie\Middleware\EncryptCookies;
|
||||||
|
use Illuminate\Foundation\Http\Middleware\ValidateCsrfToken;
|
||||||
|
use Laravel\Sanctum\Http\Middleware\AuthenticateSession;
|
||||||
use Laravel\Sanctum\Sanctum;
|
use Laravel\Sanctum\Sanctum;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@@ -15,7 +20,7 @@ return [
|
|||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
|
'stateful' => explode(',', (string) env('SANCTUM_STATEFUL_DOMAINS', sprintf(
|
||||||
'%s%s',
|
'%s%s',
|
||||||
'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
|
'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
|
||||||
Sanctum::currentApplicationUrlWithPort(),
|
Sanctum::currentApplicationUrlWithPort(),
|
||||||
@@ -76,9 +81,9 @@ return [
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
'middleware' => [
|
'middleware' => [
|
||||||
'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class,
|
'authenticate_session' => AuthenticateSession::class,
|
||||||
'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class,
|
'encrypt_cookies' => EncryptCookies::class,
|
||||||
'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
|
'validate_csrf_token' => ValidateCsrfToken::class,
|
||||||
],
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
use Dedoc\Scramble\Http\Middleware\RestrictedDocsAccess;
|
use Dedoc\Scramble\Http\Middleware\RestrictedDocsAccess;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|||||||
@@ -1,20 +1,23 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Database\Factories;
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
|
* @extends Factory<User>
|
||||||
*/
|
*/
|
||||||
class UserFactory extends Factory
|
final class UserFactory extends Factory
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The current password being used by the factory.
|
* The current password being used by the factory.
|
||||||
*/
|
*/
|
||||||
protected static ?string $password;
|
private static ?string $password = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define the model's default state.
|
* Define the model's default state.
|
||||||
@@ -27,7 +30,7 @@ class UserFactory extends Factory
|
|||||||
'name' => fake()->name(),
|
'name' => fake()->name(),
|
||||||
'email' => fake()->unique()->safeEmail(),
|
'email' => fake()->unique()->safeEmail(),
|
||||||
'email_verified_at' => now(),
|
'email_verified_at' => now(),
|
||||||
'password' => static::$password ??= Hash::make('password'),
|
'password' => self::$password ??= Hash::make('password'),
|
||||||
'remember_token' => Str::random(10),
|
'remember_token' => Str::random(10),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -37,7 +40,7 @@ class UserFactory extends Factory
|
|||||||
*/
|
*/
|
||||||
public function unverified(): static
|
public function unverified(): static
|
||||||
{
|
{
|
||||||
return $this->state(fn (array $attributes) => [
|
return $this->state(fn (array $attributes): array => [
|
||||||
'email_verified_at' => null,
|
'email_verified_at' => null,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
use Illuminate\Database\Migrations\Migration;
|
use Illuminate\Database\Migrations\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
@@ -11,7 +13,7 @@ return new class extends Migration
|
|||||||
*/
|
*/
|
||||||
public function up(): void
|
public function up(): void
|
||||||
{
|
{
|
||||||
Schema::create('users', function (Blueprint $table) {
|
Schema::create('users', function (Blueprint $table): void {
|
||||||
$table->id();
|
$table->id();
|
||||||
$table->string('name');
|
$table->string('name');
|
||||||
$table->string('email')->unique();
|
$table->string('email')->unique();
|
||||||
@@ -21,13 +23,13 @@ return new class extends Migration
|
|||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
});
|
});
|
||||||
|
|
||||||
Schema::create('password_reset_tokens', function (Blueprint $table) {
|
Schema::create('password_reset_tokens', function (Blueprint $table): void {
|
||||||
$table->string('email')->primary();
|
$table->string('email')->primary();
|
||||||
$table->string('token');
|
$table->string('token');
|
||||||
$table->timestamp('created_at')->nullable();
|
$table->timestamp('created_at')->nullable();
|
||||||
});
|
});
|
||||||
|
|
||||||
Schema::create('sessions', function (Blueprint $table) {
|
Schema::create('sessions', function (Blueprint $table): void {
|
||||||
$table->string('id')->primary();
|
$table->string('id')->primary();
|
||||||
$table->foreignId('user_id')->nullable()->index();
|
$table->foreignId('user_id')->nullable()->index();
|
||||||
$table->string('ip_address', 45)->nullable();
|
$table->string('ip_address', 45)->nullable();
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
use Illuminate\Database\Migrations\Migration;
|
use Illuminate\Database\Migrations\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
@@ -11,13 +13,13 @@ return new class extends Migration
|
|||||||
*/
|
*/
|
||||||
public function up(): void
|
public function up(): void
|
||||||
{
|
{
|
||||||
Schema::create('cache', function (Blueprint $table) {
|
Schema::create('cache', function (Blueprint $table): void {
|
||||||
$table->string('key')->primary();
|
$table->string('key')->primary();
|
||||||
$table->mediumText('value');
|
$table->mediumText('value');
|
||||||
$table->integer('expiration');
|
$table->integer('expiration');
|
||||||
});
|
});
|
||||||
|
|
||||||
Schema::create('cache_locks', function (Blueprint $table) {
|
Schema::create('cache_locks', function (Blueprint $table): void {
|
||||||
$table->string('key')->primary();
|
$table->string('key')->primary();
|
||||||
$table->string('owner');
|
$table->string('owner');
|
||||||
$table->integer('expiration');
|
$table->integer('expiration');
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
use Illuminate\Database\Migrations\Migration;
|
use Illuminate\Database\Migrations\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
@@ -11,7 +13,7 @@ return new class extends Migration
|
|||||||
*/
|
*/
|
||||||
public function up(): void
|
public function up(): void
|
||||||
{
|
{
|
||||||
Schema::create('jobs', function (Blueprint $table) {
|
Schema::create('jobs', function (Blueprint $table): void {
|
||||||
$table->id();
|
$table->id();
|
||||||
$table->string('queue')->index();
|
$table->string('queue')->index();
|
||||||
$table->longText('payload');
|
$table->longText('payload');
|
||||||
@@ -21,7 +23,7 @@ return new class extends Migration
|
|||||||
$table->unsignedInteger('created_at');
|
$table->unsignedInteger('created_at');
|
||||||
});
|
});
|
||||||
|
|
||||||
Schema::create('job_batches', function (Blueprint $table) {
|
Schema::create('job_batches', function (Blueprint $table): void {
|
||||||
$table->string('id')->primary();
|
$table->string('id')->primary();
|
||||||
$table->string('name');
|
$table->string('name');
|
||||||
$table->integer('total_jobs');
|
$table->integer('total_jobs');
|
||||||
@@ -34,7 +36,7 @@ return new class extends Migration
|
|||||||
$table->integer('finished_at')->nullable();
|
$table->integer('finished_at')->nullable();
|
||||||
});
|
});
|
||||||
|
|
||||||
Schema::create('failed_jobs', function (Blueprint $table) {
|
Schema::create('failed_jobs', function (Blueprint $table): void {
|
||||||
$table->id();
|
$table->id();
|
||||||
$table->string('uuid')->unique();
|
$table->string('uuid')->unique();
|
||||||
$table->text('connection');
|
$table->text('connection');
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
use Illuminate\Database\Migrations\Migration;
|
use Illuminate\Database\Migrations\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
@@ -11,7 +13,7 @@ return new class extends Migration
|
|||||||
*/
|
*/
|
||||||
public function up(): void
|
public function up(): void
|
||||||
{
|
{
|
||||||
Schema::create('personal_access_tokens', function (Blueprint $table) {
|
Schema::create('personal_access_tokens', function (Blueprint $table): void {
|
||||||
$table->id();
|
$table->id();
|
||||||
$table->morphs('tokenable');
|
$table->morphs('tokenable');
|
||||||
$table->text('name');
|
$table->text('name');
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Database\Seeders;
|
namespace Database\Seeders;
|
||||||
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||||
use Illuminate\Database\Seeder;
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
class DatabaseSeeder extends Seeder
|
final class DatabaseSeeder extends Seeder
|
||||||
{
|
{
|
||||||
use WithoutModelEvents;
|
use WithoutModelEvents;
|
||||||
|
|
||||||
|
|||||||
21
phpstan.neon
Normal file
21
phpstan.neon
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
includes:
|
||||||
|
- vendor/larastan/larastan/extension.neon
|
||||||
|
- vendor/nesbot/carbon/extension.neon
|
||||||
|
- phar://phpstan.phar/conf/bleedingEdge.neon
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
|
||||||
|
paths:
|
||||||
|
- app
|
||||||
|
- bootstrap/app.php
|
||||||
|
- config
|
||||||
|
- database
|
||||||
|
- public
|
||||||
|
- routes
|
||||||
|
|
||||||
|
excludePaths:
|
||||||
|
- config/database.php
|
||||||
|
|
||||||
|
level: max
|
||||||
|
|
||||||
|
tmpDir: /tmp/phpstan
|
||||||
63
pint.json
Normal file
63
pint.json
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"preset": "laravel",
|
||||||
|
"notPath": [
|
||||||
|
"tests/TestCase.php",
|
||||||
|
"tmp",
|
||||||
|
"config/database.php"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"array_push": true,
|
||||||
|
"backtick_to_shell_exec": true,
|
||||||
|
"date_time_immutable": true,
|
||||||
|
"declare_strict_types": true,
|
||||||
|
"lowercase_keywords": true,
|
||||||
|
"lowercase_static_reference": true,
|
||||||
|
"final_class": true,
|
||||||
|
"final_internal_class": true,
|
||||||
|
"final_public_method_for_abstract_class": true,
|
||||||
|
"fully_qualified_strict_types": true,
|
||||||
|
"global_namespace_import": {
|
||||||
|
"import_classes": true,
|
||||||
|
"import_constants": true,
|
||||||
|
"import_functions": true
|
||||||
|
},
|
||||||
|
"mb_str_functions": true,
|
||||||
|
"modernize_types_casting": true,
|
||||||
|
"new_with_parentheses": false,
|
||||||
|
"no_superfluous_elseif": true,
|
||||||
|
"no_useless_else": true,
|
||||||
|
"no_multiple_statements_per_line": true,
|
||||||
|
"ordered_class_elements": {
|
||||||
|
"order": [
|
||||||
|
"use_trait",
|
||||||
|
"case",
|
||||||
|
"constant",
|
||||||
|
"constant_public",
|
||||||
|
"constant_protected",
|
||||||
|
"constant_private",
|
||||||
|
"property_public",
|
||||||
|
"property_protected",
|
||||||
|
"property_private",
|
||||||
|
"construct",
|
||||||
|
"destruct",
|
||||||
|
"magic",
|
||||||
|
"phpunit",
|
||||||
|
"method_abstract",
|
||||||
|
"method_public_static",
|
||||||
|
"method_public",
|
||||||
|
"method_protected_static",
|
||||||
|
"method_protected",
|
||||||
|
"method_private_static",
|
||||||
|
"method_private"
|
||||||
|
],
|
||||||
|
"sort_algorithm": "none"
|
||||||
|
},
|
||||||
|
"ordered_interfaces": true,
|
||||||
|
"ordered_traits": true,
|
||||||
|
"protected_to_private": true,
|
||||||
|
"self_accessor": true,
|
||||||
|
"self_static_accessor": true,
|
||||||
|
"strict_comparison": true,
|
||||||
|
"visibility_required": true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
use Illuminate\Foundation\Application;
|
use Illuminate\Foundation\Application;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
|||||||
54
rector.php
Normal file
54
rector.php
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Rector\Caching\ValueObject\Storage\FileCacheStorage;
|
||||||
|
use Rector\Config\RectorConfig;
|
||||||
|
use Rector\Php83\Rector\ClassMethod\AddOverrideAttributeToOverriddenMethodsRector;
|
||||||
|
use RectorLaravel\Set\LaravelSetList;
|
||||||
|
use RectorLaravel\Set\LaravelSetProvider;
|
||||||
|
|
||||||
|
return RectorConfig::configure()
|
||||||
|
->withSetProviders(LaravelSetProvider::class)
|
||||||
|
->withSets([
|
||||||
|
LaravelSetList::LARAVEL_ARRAYACCESS_TO_METHOD_CALL,
|
||||||
|
LaravelSetList::LARAVEL_ARRAY_STR_FUNCTION_TO_STATIC_CALL,
|
||||||
|
LaravelSetList::LARAVEL_CODE_QUALITY,
|
||||||
|
LaravelSetList::LARAVEL_COLLECTION,
|
||||||
|
LaravelSetList::LARAVEL_CONTAINER_STRING_TO_FULLY_QUALIFIED_NAME,
|
||||||
|
LaravelSetList::LARAVEL_ELOQUENT_MAGIC_METHOD_TO_QUERY_BUILDER,
|
||||||
|
LaravelSetList::LARAVEL_FACADE_ALIASES_TO_FULL_NAMES,
|
||||||
|
LaravelSetList::LARAVEL_FACTORIES,
|
||||||
|
LaravelSetList::LARAVEL_IF_HELPERS,
|
||||||
|
LaravelSetList::LARAVEL_LEGACY_FACTORIES_TO_CLASSES,
|
||||||
|
])
|
||||||
|
->withImportNames(
|
||||||
|
removeUnusedImports: true,
|
||||||
|
)
|
||||||
|
->withComposerBased(laravel: true)
|
||||||
|
->withCache(
|
||||||
|
cacheDirectory: '/tmp/rector',
|
||||||
|
cacheClass: FileCacheStorage::class,
|
||||||
|
)
|
||||||
|
->withPaths([
|
||||||
|
__DIR__.'/app',
|
||||||
|
__DIR__.'/bootstrap/app.php',
|
||||||
|
__DIR__.'/config',
|
||||||
|
__DIR__.'/database',
|
||||||
|
__DIR__.'/public',
|
||||||
|
__DIR__.'/routes',
|
||||||
|
__DIR__.'/tests',
|
||||||
|
])
|
||||||
|
->withSkip([
|
||||||
|
AddOverrideAttributeToOverriddenMethodsRector::class,
|
||||||
|
__DIR__.'/config/database.php',
|
||||||
|
])
|
||||||
|
->withPreparedSets(
|
||||||
|
deadCode: true,
|
||||||
|
codeQuality: true,
|
||||||
|
typeDeclarations: true,
|
||||||
|
privatization: true,
|
||||||
|
earlyReturn: true,
|
||||||
|
codingStyle: true,
|
||||||
|
)
|
||||||
|
->withPhpSets();
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| API Routes
|
| API Routes
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
use App\Http\Controllers\Api\V1\AuthController;
|
use App\Http\Controllers\Api\V1\AuthController;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
@@ -13,13 +15,13 @@ use Illuminate\Support\Facades\Route;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Public routes with auth rate limiter (5/min - brute force protection)
|
// Public routes with auth rate limiter (5/min - brute force protection)
|
||||||
Route::middleware('throttle:auth')->group(function () {
|
Route::middleware('throttle:auth')->group(function (): void {
|
||||||
Route::post('register', [AuthController::class, 'register'])->name('api.v1.register');
|
Route::post('register', [AuthController::class, 'register'])->name('api.v1.register');
|
||||||
Route::post('login', [AuthController::class, 'login'])->name('api.v1.login');
|
Route::post('login', [AuthController::class, 'login'])->name('api.v1.login');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Protected routes with authenticated rate limiter (120/min)
|
// Protected routes with authenticated rate limiter (120/min)
|
||||||
Route::middleware(['auth:sanctum', 'throttle:authenticated'])->group(function () {
|
Route::middleware(['auth:sanctum', 'throttle:authenticated'])->group(function (): void {
|
||||||
Route::post('logout', [AuthController::class, 'logout'])->name('api.v1.logout');
|
Route::post('logout', [AuthController::class, 'logout'])->name('api.v1.logout');
|
||||||
Route::get('me', [AuthController::class, 'me'])->name('api.v1.me');
|
Route::get('me', [AuthController::class, 'me'])->name('api.v1.me');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
use Illuminate\Foundation\Inspiring;
|
use Illuminate\Foundation\Inspiring;
|
||||||
use Illuminate\Support\Facades\Artisan;
|
use Illuminate\Support\Facades\Artisan;
|
||||||
|
|
||||||
Artisan::command('inspire', function () {
|
Artisan::command('inspire', function (): void {
|
||||||
$this->comment(Inspiring::quote());
|
$this->comment(Inspiring::quote());
|
||||||
})->purpose('Display an inspiring quote');
|
})->purpose('Display an inspiring quote');
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
// Web routes disabled - API only application
|
// Web routes disabled - API only application
|
||||||
// Scramble documentation available at /docs/api
|
// Scramble documentation available at /docs/api
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
|
||||||
uses(RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
describe('Registration', function () {
|
describe('Registration', function (): void {
|
||||||
it('registers a new user successfully', function () {
|
it('registers a new user successfully', function (): void {
|
||||||
$response = $this->postJson('/api/v1/register', [
|
$response = $this->postJson('/api/v1/register', [
|
||||||
'name' => 'Test User',
|
'name' => 'Test User',
|
||||||
'email' => 'test@example.com',
|
'email' => 'test@example.com',
|
||||||
@@ -33,7 +35,7 @@ describe('Registration', function () {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fails registration with invalid data', function () {
|
it('fails registration with invalid data', function (): void {
|
||||||
$response = $this->postJson('/api/v1/register', [
|
$response = $this->postJson('/api/v1/register', [
|
||||||
'name' => '',
|
'name' => '',
|
||||||
'email' => 'invalid-email',
|
'email' => 'invalid-email',
|
||||||
@@ -43,7 +45,7 @@ describe('Registration', function () {
|
|||||||
$response->assertStatus(422);
|
$response->assertStatus(422);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fails registration with duplicate email', function () {
|
it('fails registration with duplicate email', function (): void {
|
||||||
User::factory()->create(['email' => 'existing@example.com']);
|
User::factory()->create(['email' => 'existing@example.com']);
|
||||||
|
|
||||||
$response = $this->postJson('/api/v1/register', [
|
$response = $this->postJson('/api/v1/register', [
|
||||||
@@ -57,8 +59,8 @@ describe('Registration', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Login', function () {
|
describe('Login', function (): void {
|
||||||
it('logs in with valid credentials', function () {
|
it('logs in with valid credentials', function (): void {
|
||||||
$user = User::factory()->create([
|
$user = User::factory()->create([
|
||||||
'password' => bcrypt('password123'),
|
'password' => bcrypt('password123'),
|
||||||
]);
|
]);
|
||||||
@@ -83,7 +85,7 @@ describe('Login', function () {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fails login with invalid credentials', function () {
|
it('fails login with invalid credentials', function (): void {
|
||||||
$user = User::factory()->create([
|
$user = User::factory()->create([
|
||||||
'password' => bcrypt('password123'),
|
'password' => bcrypt('password123'),
|
||||||
]);
|
]);
|
||||||
@@ -100,7 +102,7 @@ describe('Login', function () {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fails login with non-existent user', function () {
|
it('fails login with non-existent user', function (): void {
|
||||||
$response = $this->postJson('/api/v1/login', [
|
$response = $this->postJson('/api/v1/login', [
|
||||||
'email' => 'nonexistent@example.com',
|
'email' => 'nonexistent@example.com',
|
||||||
'password' => 'password123',
|
'password' => 'password123',
|
||||||
@@ -110,12 +112,12 @@ describe('Login', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Logout', function () {
|
describe('Logout', function (): void {
|
||||||
it('logs out authenticated user', function () {
|
it('logs out authenticated user', function (): void {
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$token = $user->createToken('test-token')->plainTextToken;
|
$token = $user->createToken('test-token')->plainTextToken;
|
||||||
|
|
||||||
$response = $this->withHeader('Authorization', "Bearer {$token}")
|
$response = $this->withHeader('Authorization', 'Bearer '.$token)
|
||||||
->postJson('/api/v1/logout');
|
->postJson('/api/v1/logout');
|
||||||
|
|
||||||
$response->assertStatus(200)
|
$response->assertStatus(200)
|
||||||
@@ -125,19 +127,19 @@ describe('Logout', function () {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fails logout without authentication', function () {
|
it('fails logout without authentication', function (): void {
|
||||||
$response = $this->postJson('/api/v1/logout');
|
$response = $this->postJson('/api/v1/logout');
|
||||||
|
|
||||||
$response->assertStatus(401);
|
$response->assertStatus(401);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Me', function () {
|
describe('Me', function (): void {
|
||||||
it('returns authenticated user data', function () {
|
it('returns authenticated user data', function (): void {
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$token = $user->createToken('test-token')->plainTextToken;
|
$token = $user->createToken('test-token')->plainTextToken;
|
||||||
|
|
||||||
$response = $this->withHeader('Authorization', "Bearer {$token}")
|
$response = $this->withHeader('Authorization', 'Bearer '.$token)
|
||||||
->getJson('/api/v1/me');
|
->getJson('/api/v1/me');
|
||||||
|
|
||||||
$response->assertStatus(200)
|
$response->assertStatus(200)
|
||||||
@@ -155,7 +157,7 @@ describe('Me', function () {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fails without authentication', function () {
|
it('fails without authentication', function (): void {
|
||||||
$response = $this->getJson('/api/v1/me');
|
$response = $this->getJson('/api/v1/me');
|
||||||
|
|
||||||
$response->assertStatus(401);
|
$response->assertStatus(401);
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
| Test Case
|
| Test Case
|
||||||
@@ -11,7 +15,7 @@
|
|||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pest()->extend(Tests\TestCase::class)
|
pest()->extend(TestCase::class)
|
||||||
// ->use(Illuminate\Foundation\Testing\RefreshDatabase::class)
|
// ->use(Illuminate\Foundation\Testing\RefreshDatabase::class)
|
||||||
->in('Feature');
|
->in('Feature');
|
||||||
|
|
||||||
@@ -26,9 +30,7 @@ pest()->extend(Tests\TestCase::class)
|
|||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
expect()->extend('toBeOne', function () {
|
expect()->extend('toBeOne', fn () => $this->toBe(1));
|
||||||
return $this->toBe(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
@@ -41,7 +43,7 @@ expect()->extend('toBeOne', function () {
|
|||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function something()
|
function something(): void
|
||||||
{
|
{
|
||||||
// ..
|
// ..
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Tests\Unit;
|
namespace Tests\Unit;
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class ExampleTest extends TestCase
|
final class ExampleTest extends TestCase
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* A basic test example.
|
* A basic test example.
|
||||||
|
|||||||
Reference in New Issue
Block a user