Tests fixed
This commit is contained in:
parent
2418edccfd
commit
030c049ca8
13 changed files with 131 additions and 38 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -5,6 +5,7 @@
|
||||||
.env.production
|
.env.production
|
||||||
.phpactor.json
|
.phpactor.json
|
||||||
.phpunit.result.cache
|
.phpunit.result.cache
|
||||||
|
/.composer
|
||||||
/.fleet
|
/.fleet
|
||||||
/.idea
|
/.idea
|
||||||
/.nova
|
/.nova
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,10 @@ RUN apt-get update && apt-get install -y \
|
||||||
# Clear cache
|
# Clear cache
|
||||||
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
|
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Fix nginx permissions for rootless
|
||||||
|
RUN mkdir -p /var/lib/nginx/body /var/lib/nginx/proxy /var/lib/nginx/fastcgi /var/log/nginx /var/run \
|
||||||
|
&& chmod -R 777 /var/lib/nginx /var/log/nginx /var/run
|
||||||
|
|
||||||
# Install PHP extensions
|
# Install PHP extensions
|
||||||
RUN docker-php-ext-install pdo_mysql pdo_pgsql mbstring exif pcntl bcmath gd zip
|
RUN docker-php-ext-install pdo_mysql pdo_pgsql mbstring exif pcntl bcmath gd zip
|
||||||
|
|
||||||
|
|
@ -42,7 +46,7 @@ RUN chown -R www-data:www-data /var/www/html \
|
||||||
&& chmod -R 755 /var/www/html/bootstrap/cache
|
&& chmod -R 755 /var/www/html/bootstrap/cache
|
||||||
|
|
||||||
# Expose port 80
|
# Expose port 80
|
||||||
EXPOSE 80
|
EXPOSE 8080
|
||||||
|
|
||||||
# Start supervisor
|
# Start supervisor
|
||||||
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
|
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
|
||||||
|
|
|
||||||
22
compose.yml
22
compose.yml
|
|
@ -5,11 +5,12 @@ services:
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
container_name: laravel-app
|
container_name: laravel-app
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
userns_mode: "keep-id"
|
||||||
working_dir: /var/www/html
|
working_dir: /var/www/html
|
||||||
volumes:
|
volumes:
|
||||||
- ./:/var/www/html:Z
|
- ./:/var/www/html:Z
|
||||||
ports:
|
ports:
|
||||||
- "8080:80"
|
- "8080:8080"
|
||||||
networks:
|
networks:
|
||||||
- laravel
|
- laravel
|
||||||
depends_on:
|
depends_on:
|
||||||
|
|
@ -48,11 +49,26 @@ services:
|
||||||
container_name: laravel-mailpit
|
container_name: laravel-mailpit
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- "8025:8025" # Web UI
|
- "8025:8025" # Web UI
|
||||||
- "1025:1025" # SMTP server
|
- "1025:1025" # SMTP server
|
||||||
networks:
|
networks:
|
||||||
- laravel
|
- laravel
|
||||||
|
|
||||||
|
phpmyadmin:
|
||||||
|
image: docker.io/phpmyadmin/phpmyadmin:latest
|
||||||
|
container_name: laravel-phpmyadmin
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
PMA_HOST: db
|
||||||
|
PMA_USER: root
|
||||||
|
PMA_PASSWORD: root
|
||||||
|
ports:
|
||||||
|
- "8081:80"
|
||||||
|
networks:
|
||||||
|
- laravel
|
||||||
|
depends_on:
|
||||||
|
- db
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
laravel:
|
laravel:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^8.2",
|
"php": "^8.2",
|
||||||
"laravel/framework": "^12.0",
|
"laravel/framework": "^12.0",
|
||||||
"laravel/tinker": "^2.10.1"
|
"laravel/tinker": "^2.10.1",
|
||||||
|
"predis/predis": "^3.4"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"fakerphp/faker": "^1.23",
|
"fakerphp/faker": "^1.23",
|
||||||
|
|
|
||||||
65
composer.lock
generated
65
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": "c514d8f7b9fc5970bdd94287905ef584",
|
"content-hash": "d6bd2a26512dd52b870be9ab0b7d9c72",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "brick/math",
|
"name": "brick/math",
|
||||||
|
|
@ -2603,6 +2603,69 @@
|
||||||
],
|
],
|
||||||
"time": "2025-12-27T19:41:33+00:00"
|
"time": "2025-12-27T19:41:33+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "predis/predis",
|
||||||
|
"version": "v3.4.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/predis/predis.git",
|
||||||
|
"reference": "1183f5732e6b10efd33f64984a96726eaecb59aa"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/predis/predis/zipball/1183f5732e6b10efd33f64984a96726eaecb59aa",
|
||||||
|
"reference": "1183f5732e6b10efd33f64984a96726eaecb59aa",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.2 || ^8.0",
|
||||||
|
"psr/http-message": "^1.0|^2.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"friendsofphp/php-cs-fixer": "^3.3",
|
||||||
|
"phpstan/phpstan": "^1.9",
|
||||||
|
"phpunit/phpcov": "^6.0 || ^8.0",
|
||||||
|
"phpunit/phpunit": "^8.0 || ~9.4.4"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-relay": "Faster connection with in-memory caching (>=0.6.2)"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Predis\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Till Krüss",
|
||||||
|
"homepage": "https://till.im",
|
||||||
|
"role": "Maintainer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "A flexible and feature-complete Redis/Valkey client for PHP.",
|
||||||
|
"homepage": "http://github.com/predis/predis",
|
||||||
|
"keywords": [
|
||||||
|
"nosql",
|
||||||
|
"predis",
|
||||||
|
"redis"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/predis/predis/issues",
|
||||||
|
"source": "https://github.com/predis/predis/tree/v3.4.0"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/sponsors/tillkruss",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2026-02-11T17:30:28+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "psr/clock",
|
"name": "psr/clock",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 8080;
|
||||||
listen [::]:80;
|
listen [::]:8080;
|
||||||
server_name localhost;
|
server_name localhost;
|
||||||
root /var/www/html/public;
|
root /var/www/html/public;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
[supervisord]
|
[supervisord]
|
||||||
nodaemon=true
|
nodaemon=true
|
||||||
user=root
|
logfile=/dev/stdout
|
||||||
logfile=/var/log/supervisor/supervisord.log
|
logfile_maxbytes=0
|
||||||
pidfile=/var/run/supervisord.pid
|
pidfile=/tmp/supervisord.pid
|
||||||
|
|
||||||
[program:php-fpm]
|
[program:php-fpm]
|
||||||
command=/usr/local/sbin/php-fpm -F
|
command=/usr/local/sbin/php-fpm -F
|
||||||
|
|
@ -21,3 +21,12 @@ stdout_logfile=/dev/stdout
|
||||||
stdout_logfile_maxbytes=0
|
stdout_logfile_maxbytes=0
|
||||||
stderr_logfile=/dev/stderr
|
stderr_logfile=/dev/stderr
|
||||||
stderr_logfile_maxbytes=0
|
stderr_logfile_maxbytes=0
|
||||||
|
|
||||||
|
[program:queue-worker]
|
||||||
|
command=php /var/www/html/artisan queue:work --sleep=3 --tries=3 --max-time=3600
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
stdout_logfile=/dev/stdout
|
||||||
|
stdout_logfile_maxbytes=0
|
||||||
|
stderr_logfile=/dev/stderr
|
||||||
|
stderr_logfile_maxbytes=0
|
||||||
|
|
@ -23,6 +23,8 @@
|
||||||
<env name="BCRYPT_ROUNDS" value="4"/>
|
<env name="BCRYPT_ROUNDS" value="4"/>
|
||||||
<env name="BROADCAST_CONNECTION" value="null"/>
|
<env name="BROADCAST_CONNECTION" value="null"/>
|
||||||
<env name="CACHE_STORE" value="array"/>
|
<env name="CACHE_STORE" value="array"/>
|
||||||
|
<env name="DB_CONNECTION" value="sqlite"/>
|
||||||
|
<env name="DB_DATABASE" value=":memory:"/>
|
||||||
<env name="MAIL_MAILER" value="array"/>
|
<env name="MAIL_MAILER" value="array"/>
|
||||||
<env name="QUEUE_CONNECTION" value="sync"/>
|
<env name="QUEUE_CONNECTION" value="sync"/>
|
||||||
<env name="SESSION_DRIVER" value="array"/>
|
<env name="SESSION_DRIVER" value="array"/>
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<form method="POST" action="{{ route('login') }}">
|
<form method="POST" action="{{ route('magic-link.send') }}">
|
||||||
@csrf
|
@csrf
|
||||||
|
|
||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ Route::get('/', function () {
|
||||||
// Guest routes (unauthenticated users)
|
// Guest routes (unauthenticated users)
|
||||||
Route::middleware('guest')->group(function () {
|
Route::middleware('guest')->group(function () {
|
||||||
Route::get('/login', [MagicLinkController::class, 'showLoginForm'])->name('login');
|
Route::get('/login', [MagicLinkController::class, 'showLoginForm'])->name('login');
|
||||||
Route::post('/login', [MagicLinkController::class, 'sendLink'])->name('magic-link.send');
|
Route::post('/magic-link', [MagicLinkController::class, 'sendLink'])->name('magic-link.send');
|
||||||
Route::get('/verify-code', [MagicLinkController::class, 'showCodeForm'])->name('verify-code');
|
Route::get('/verify-code', [MagicLinkController::class, 'showCodeForm'])->name('verify-code');
|
||||||
Route::post('/verify-code', [MagicLinkController::class, 'verifyCode'])->name('magic-link.verify-code');
|
Route::post('/verify-code', [MagicLinkController::class, 'verifyCode'])->name('magic-link.verify-code');
|
||||||
Route::get('/auth/magic-link', [MagicLinkController::class, 'verifyLink'])->name('magic-link.verify');
|
Route::get('/auth/magic-link', [MagicLinkController::class, 'verifyLink'])->name('magic-link.verify');
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ use App\Models\MagicLoginToken;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Hash;
|
|
||||||
use Illuminate\Support\Facades\Mail;
|
use Illuminate\Support\Facades\Mail;
|
||||||
use Illuminate\Support\Facades\RateLimiter;
|
use Illuminate\Support\Facades\RateLimiter;
|
||||||
use Illuminate\Support\Facades\URL;
|
use Illuminate\Support\Facades\URL;
|
||||||
|
|
@ -165,27 +164,6 @@ class MagicLinkAuthTest extends TestCase
|
||||||
$this->assertGuest();
|
$this->assertGuest();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_4_word_token_format_validation(): void
|
|
||||||
{
|
|
||||||
$token = MagicLoginToken::generate('test@example.com', '127.0.0.1', 'TestAgent');
|
|
||||||
|
|
||||||
$plainToken = $token->plain_token;
|
|
||||||
|
|
||||||
$this->assertMatchesRegularExpression(
|
|
||||||
'/^[a-z]+-[a-z]+-[a-z]+-[a-z]+$/',
|
|
||||||
$plainToken,
|
|
||||||
'Token should be 4 words separated by hyphens'
|
|
||||||
);
|
|
||||||
|
|
||||||
$words = explode('-', $plainToken);
|
|
||||||
$this->assertCount(4, $words, 'Token should contain exactly 4 words');
|
|
||||||
|
|
||||||
foreach ($words as $word) {
|
|
||||||
$this->assertNotEmpty($word, 'Each word should not be empty');
|
|
||||||
$this->assertMatchesRegularExpression('/^[a-z]+$/', $word, 'Each word should contain only lowercase letters');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_remember_token_always_set(): void
|
public function test_remember_token_always_set(): void
|
||||||
{
|
{
|
||||||
$user = User::create([
|
$user = User::create([
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,9 @@
|
||||||
|
|
||||||
namespace Tests;
|
namespace Tests;
|
||||||
|
|
||||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
|
||||||
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
|
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
|
||||||
|
|
||||||
abstract class TestCase extends BaseTestCase
|
abstract class TestCase extends BaseTestCase
|
||||||
{
|
{
|
||||||
use CreatesApplication;
|
use CreatesApplication;
|
||||||
use DatabaseTransactions;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -219,7 +219,7 @@ class MagicLoginTokenTest extends TestCase
|
||||||
|
|
||||||
public function test_token_expiry_is_15_minutes(): void
|
public function test_token_expiry_is_15_minutes(): void
|
||||||
{
|
{
|
||||||
$beforeCreation = now()->addMinutes(15);
|
$beforeCreation = now()->startOfSecond()->addMinutes(15);
|
||||||
|
|
||||||
$token = MagicLoginToken::generate('test@example.com', '127.0.0.1', 'TestAgent');
|
$token = MagicLoginToken::generate('test@example.com', '127.0.0.1', 'TestAgent');
|
||||||
|
|
||||||
|
|
@ -300,4 +300,25 @@ class MagicLoginTokenTest extends TestCase
|
||||||
|
|
||||||
$this->assertInstanceOf(\Illuminate\Support\Carbon::class, $token->used_at);
|
$this->assertInstanceOf(\Illuminate\Support\Carbon::class, $token->used_at);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_4_word_token_format_validation(): void
|
||||||
|
{
|
||||||
|
$token = MagicLoginToken::generate('test@example.com', '127.0.0.1', 'TestAgent');
|
||||||
|
|
||||||
|
$plainToken = $token->plain_token;
|
||||||
|
|
||||||
|
$this->assertMatchesRegularExpression(
|
||||||
|
'/^[a-z]+-[a-z]+-[a-z]+-[a-z]+$/',
|
||||||
|
$plainToken,
|
||||||
|
'Token should be 4 words separated by hyphens'
|
||||||
|
);
|
||||||
|
|
||||||
|
$words = explode('-', $plainToken);
|
||||||
|
$this->assertCount(4, $words, 'Token should contain exactly 4 words');
|
||||||
|
|
||||||
|
foreach ($words as $word) {
|
||||||
|
$this->assertNotEmpty($word, 'Each word should not be empty');
|
||||||
|
$this->assertMatchesRegularExpression('/^[a-z]+$/', $word, 'Each word should contain only lowercase letters');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue