Tests fixed

This commit is contained in:
Dan Baker 2026-02-22 19:02:59 +00:00
parent 2418edccfd
commit 030c049ca8
13 changed files with 131 additions and 38 deletions

1
.gitignore vendored
View file

@ -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

View file

@ -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"]

View file

@ -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:
@ -53,6 +54,21 @@ services:
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

View file

@ -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
View file

@ -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",

View file

@ -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;

View file

@ -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

View file

@ -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"/>

View file

@ -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">

View file

@ -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');

View file

@ -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([

View file

@ -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;
} }

View file

@ -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');
}
}
} }