🎉 First commit

This commit is contained in:
2026-02-20 08:40:00 +01:00
commit 07a3b3a874
206 changed files with 22834 additions and 0 deletions

View File

@@ -0,0 +1,67 @@
<?php
use App\Models\User;
use Laravel\Fortify\Features;
test('login screen can be rendered', function () {
$response = $this->get(route('login'));
$response->assertOk();
});
test('users can authenticate using the login screen', function () {
$user = User::factory()->create();
$response = $this->post(route('login.store'), [
'email' => $user->email,
'password' => 'password',
]);
$response
->assertSessionHasNoErrors()
->assertRedirect(route('dashboard', absolute: false));
$this->assertAuthenticated();
});
test('users can not authenticate with invalid password', function () {
$user = User::factory()->create();
$response = $this->post(route('login.store'), [
'email' => $user->email,
'password' => 'wrong-password',
]);
$response->assertSessionHasErrorsIn('email');
$this->assertGuest();
});
test('users with two factor enabled are redirected to two factor challenge', function () {
if (! Features::canManageTwoFactorAuthentication()) {
$this->markTestSkipped('Two-factor authentication is not enabled.');
}
Features::twoFactorAuthentication([
'confirm' => true,
'confirmPassword' => true,
]);
$user = User::factory()->withTwoFactor()->create();
$response = $this->post(route('login.store'), [
'email' => $user->email,
'password' => 'password',
]);
$response->assertRedirect(route('two-factor.login'));
$this->assertGuest();
});
test('users can logout', function () {
$user = User::factory()->create();
$response = $this->actingAs($user)->post(route('logout'));
$response->assertRedirect(route('home'));
$this->assertGuest();
});

View File

@@ -0,0 +1,67 @@
<?php
use App\Models\User;
use Illuminate\Auth\Events\Verified;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\URL;
test('email verification screen can be rendered', function () {
$user = User::factory()->unverified()->create();
$response = $this->actingAs($user)->get(route('verification.notice'));
$response->assertOk();
});
test('email can be verified', function () {
$user = User::factory()->unverified()->create();
Event::fake();
$verificationUrl = URL::temporarySignedRoute(
'verification.verify',
now()->addMinutes(60),
['id' => $user->id, 'hash' => sha1($user->email)]
);
$response = $this->actingAs($user)->get($verificationUrl);
Event::assertDispatched(Verified::class);
expect($user->fresh()->hasVerifiedEmail())->toBeTrue();
$response->assertRedirect(route('dashboard', absolute: false).'?verified=1');
});
test('email is not verified with invalid hash', function () {
$user = User::factory()->unverified()->create();
$verificationUrl = URL::temporarySignedRoute(
'verification.verify',
now()->addMinutes(60),
['id' => $user->id, 'hash' => sha1('wrong-email')]
);
$this->actingAs($user)->get($verificationUrl);
expect($user->fresh()->hasVerifiedEmail())->toBeFalse();
});
test('already verified user visiting verification link is redirected without firing event again', function () {
$user = User::factory()->create([
'email_verified_at' => now(),
]);
Event::fake();
$verificationUrl = URL::temporarySignedRoute(
'verification.verify',
now()->addMinutes(60),
['id' => $user->id, 'hash' => sha1($user->email)]
);
$this->actingAs($user)->get($verificationUrl)
->assertRedirect(route('dashboard', absolute: false).'?verified=1');
expect($user->fresh()->hasVerifiedEmail())->toBeTrue();
Event::assertNotDispatched(Verified::class);
});

View File

@@ -0,0 +1,11 @@
<?php
use App\Models\User;
test('confirm password screen can be rendered', function () {
$user = User::factory()->create();
$response = $this->actingAs($user)->get(route('password.confirm'));
$response->assertOk();
});

View File

@@ -0,0 +1,59 @@
<?php
use App\Models\User;
use Illuminate\Auth\Notifications\ResetPassword;
use Illuminate\Support\Facades\Notification;
test('reset password link screen can be rendered', function () {
$response = $this->get(route('password.request'));
$response->assertOk();
});
test('reset password link can be requested', function () {
Notification::fake();
$user = User::factory()->create();
$this->post(route('password.request'), ['email' => $user->email]);
Notification::assertSentTo($user, ResetPassword::class);
});
test('reset password screen can be rendered', function () {
Notification::fake();
$user = User::factory()->create();
$this->post(route('password.request'), ['email' => $user->email]);
Notification::assertSentTo($user, ResetPassword::class, function ($notification) {
$response = $this->get(route('password.reset', $notification->token));
$response->assertOk();
return true;
});
});
test('password can be reset with valid token', function () {
Notification::fake();
$user = User::factory()->create();
$this->post(route('password.request'), ['email' => $user->email]);
Notification::assertSentTo($user, ResetPassword::class, function ($notification) use ($user) {
$response = $this->post(route('password.update'), [
'token' => $notification->token,
'email' => $user->email,
'password' => 'password',
'password_confirmation' => 'password',
]);
$response
->assertSessionHasNoErrors()
->assertRedirect(route('login', absolute: false));
return true;
});
});

View File

@@ -0,0 +1,21 @@
<?php
test('registration screen can be rendered', function () {
$response = $this->get(route('register'));
$response->assertOk();
});
test('new users can register', function () {
$response = $this->post(route('register.store'), [
'name' => 'John Doe',
'email' => 'test@example.com',
'password' => 'password',
'password_confirmation' => 'password',
]);
$response->assertSessionHasNoErrors()
->assertRedirect(route('dashboard', absolute: false));
$this->assertAuthenticated();
});

View File

@@ -0,0 +1,32 @@
<?php
use App\Models\User;
use Laravel\Fortify\Features;
test('two factor challenge redirects to login when not authenticated', function () {
if (! Features::canManageTwoFactorAuthentication()) {
$this->markTestSkipped('Two-factor authentication is not enabled.');
}
$response = $this->get(route('two-factor.login'));
$response->assertRedirect(route('login'));
});
test('two factor challenge can be rendered', function () {
if (! Features::canManageTwoFactorAuthentication()) {
$this->markTestSkipped('Two-factor authentication is not enabled.');
}
Features::twoFactorAuthentication([
'confirm' => true,
'confirmPassword' => true,
]);
$user = User::factory()->withTwoFactor()->create();
$this->post(route('login.store'), [
'email' => $user->email,
'password' => 'password',
])->assertRedirect(route('two-factor.login'));
});

View File

@@ -0,0 +1,16 @@
<?php
use App\Models\User;
test('guests are redirected to the login page', function () {
$response = $this->get(route('dashboard'));
$response->assertRedirect(route('login'));
});
test('authenticated users can visit the dashboard', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = $this->get(route('dashboard'));
$response->assertOk();
});

View File

@@ -0,0 +1,7 @@
<?php
test('returns a successful response', function () {
$response = $this->get(route('home'));
$response->assertOk();
});

View File

@@ -0,0 +1,125 @@
<?php
use App\Services\LogisticsService;
use Illuminate\Support\Facades\Http;
beforeEach(function () {
config([
'logistics.base_url' => 'http://test-server.local',
'logistics.api_key' => 'test-api-key',
'logistics.folder' => 'testfolder',
]);
});
it('sends the X-API-KEY header on every request', function () {
Http::fake([
'*' => Http::response(['data' => [], 'metadata' => ['rowcount' => 0, 'issuccess' => true], 'error' => null]),
]);
$service = app(LogisticsService::class);
$service->tablesList();
Http::assertSent(function ($request) {
return $request->hasHeader('X-API-KEY', 'test-api-key');
});
});
it('builds the correct URL for tables_list', function () {
Http::fake([
'*' => Http::response(['data' => ['art', 'cust'], 'metadata' => ['rowcount' => 2, 'issuccess' => true], 'error' => null]),
]);
$service = app(LogisticsService::class);
$result = $service->tablesList();
Http::assertSent(function ($request) {
return str_contains($request->url(), 'http://test-server.local/testfolder/tables_list');
});
expect($result['data'])->toBe(['art', 'cust']);
});
it('builds the correct URL for column_list with table name', function () {
Http::fake([
'*' => Http::response(['data' => [['name' => 'artid']], 'metadata' => ['rowcount' => 1, 'issuccess' => true], 'error' => null]),
]);
$service = app(LogisticsService::class);
$service->columnList('art');
Http::assertSent(function ($request) {
return str_contains($request->url(), 'testfolder/column_list/art');
});
});
it('sends correct parameters for art_list', function () {
Http::fake([
'*' => Http::response(['data' => [], 'metadata' => ['rowcount' => 0, 'issuccess' => true], 'error' => null]),
]);
$service = app(LogisticsService::class);
$service->artList(['select' => 'artid,artname', 'search' => 'test']);
Http::assertSent(function ($request) {
$body = $request->data();
return $body['select'] === 'artid,artname' && $body['search'] === 'test';
});
});
it('sends ARTID for art_getstk', function () {
Http::fake([
'*' => Http::response(['data' => ['stock' => 42], 'metadata' => ['rowcount' => 1, 'issuccess' => true], 'error' => null]),
]);
$service = app(LogisticsService::class);
$result = $service->artGetStock('ART001');
Http::assertSent(function ($request) {
return $request->data()['ARTID'] === 'ART001';
});
expect($result['data']['stock'])->toBe(42);
});
it('sends correct parameters for document_detail', function () {
Http::fake([
'*' => Http::response(['data' => ['jnl' => 'VEN', 'number' => '1'], 'metadata' => ['rowcount' => 1, 'issuccess' => true], 'error' => null]),
]);
$service = app(LogisticsService::class);
$service->documentDetail('VEN', '1');
Http::assertSent(function ($request) {
$body = $request->data();
return $body['jnl'] === 'VEN' && $body['number'] === '1';
});
});
it('sends correct parameters for third_list', function () {
Http::fake([
'*' => Http::response(['data' => [], 'metadata' => ['rowcount' => 0, 'issuccess' => true], 'error' => null]),
]);
$service = app(LogisticsService::class);
$service->thirdList(['select' => 'custid', 'search' => 'test']);
Http::assertSent(function ($request) {
$body = $request->data();
return $body['select'] === 'custid' && $body['search'] === 'test';
});
});
it('returns fallback data when API returns empty response', function () {
Http::fake([
'*' => Http::response(null),
]);
$service = app(LogisticsService::class);
$result = $service->tablesList();
expect($result)->toHaveKey('error')
->and($result['metadata']['issuccess'])->toBeFalse();
});

View File

@@ -0,0 +1,40 @@
<?php
use App\Livewire\Settings\Password;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Livewire\Livewire;
test('password can be updated', function () {
$user = User::factory()->create([
'password' => Hash::make('password'),
]);
$this->actingAs($user);
$response = Livewire::test(Password::class)
->set('current_password', 'password')
->set('password', 'new-password')
->set('password_confirmation', 'new-password')
->call('updatePassword');
$response->assertHasNoErrors();
expect(Hash::check('new-password', $user->refresh()->password))->toBeTrue();
});
test('correct password must be provided to update password', function () {
$user = User::factory()->create([
'password' => Hash::make('password'),
]);
$this->actingAs($user);
$response = Livewire::test(Password::class)
->set('current_password', 'wrong-password')
->set('password', 'new-password')
->set('password_confirmation', 'new-password')
->call('updatePassword');
$response->assertHasErrors(['current_password']);
});

View File

@@ -0,0 +1,76 @@
<?php
use App\Livewire\Settings\Profile;
use App\Models\User;
use Livewire\Livewire;
test('profile page is displayed', function () {
$this->actingAs($user = User::factory()->create());
$this->get('/settings/profile')->assertOk();
});
test('profile information can be updated', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = Livewire::test(Profile::class)
->set('name', 'Test User')
->set('email', 'test@example.com')
->call('updateProfileInformation');
$response->assertHasNoErrors();
$user->refresh();
expect($user->name)->toEqual('Test User');
expect($user->email)->toEqual('test@example.com');
expect($user->email_verified_at)->toBeNull();
});
test('email verification status is unchanged when email address is unchanged', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = Livewire::test(Profile::class)
->set('name', 'Test User')
->set('email', $user->email)
->call('updateProfileInformation');
$response->assertHasNoErrors();
expect($user->refresh()->email_verified_at)->not->toBeNull();
});
test('user can delete their account', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = Livewire::test('settings.delete-user-form')
->set('password', 'password')
->call('deleteUser');
$response
->assertHasNoErrors()
->assertRedirect('/');
expect($user->fresh())->toBeNull();
expect(auth()->check())->toBeFalse();
});
test('correct password must be provided to delete account', function () {
$user = User::factory()->create();
$this->actingAs($user);
$response = Livewire::test('settings.delete-user-form')
->set('password', 'wrong-password')
->call('deleteUser');
$response->assertHasErrors(['password']);
expect($user->fresh())->not->toBeNull();
});

View File

@@ -0,0 +1,70 @@
<?php
use App\Models\User;
use Laravel\Fortify\Features;
use Livewire\Livewire;
beforeEach(function () {
if (! Features::canManageTwoFactorAuthentication()) {
$this->markTestSkipped('Two-factor authentication is not enabled.');
}
Features::twoFactorAuthentication([
'confirm' => true,
'confirmPassword' => true,
]);
});
test('two factor settings page can be rendered', function () {
$user = User::factory()->create();
$this->actingAs($user)
->withSession(['auth.password_confirmed_at' => time()])
->get(route('two-factor.show'))
->assertOk()
->assertSee('Two Factor Authentication')
->assertSee('Disabled');
});
test('two factor settings page requires password confirmation when enabled', function () {
$user = User::factory()->create();
$response = $this->actingAs($user)
->get(route('two-factor.show'));
$response->assertRedirect(route('password.confirm'));
});
test('two factor settings page returns forbidden response when two factor is disabled', function () {
config(['fortify.features' => []]);
$user = User::factory()->create();
$response = $this->actingAs($user)
->withSession(['auth.password_confirmed_at' => time()])
->get(route('two-factor.show'));
$response->assertForbidden();
});
test('two factor authentication disabled when confirmation abandoned between requests', function () {
$user = User::factory()->create();
$user->forceFill([
'two_factor_secret' => encrypt('test-secret'),
'two_factor_recovery_codes' => encrypt(json_encode(['code1', 'code2'])),
'two_factor_confirmed_at' => null,
])->save();
$this->actingAs($user);
$component = Livewire::test('settings.two-factor');
$component->assertSet('twoFactorEnabled', false);
$this->assertDatabaseHas('users', [
'id' => $user->id,
'two_factor_secret' => null,
'two_factor_recovery_codes' => null,
]);
});