Skip to content

Commit 6aa81ec

Browse files
author
Lexx
committed
feat: added registration + tests. Fine-tuned Auth tests
1 parent bd9d009 commit 6aa81ec

File tree

24 files changed

+1034
-179
lines changed

24 files changed

+1034
-179
lines changed

.env.testing

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
APP_NAME=Laravelte
2-
APP_ENV=local
2+
APP_ENV=production
33
APP_KEY=base64:GKVp3UUkWnXCqvI2DOMLw84D0/ysWhcYaGYioAkAYNU=
44
APP_DEBUG=true
55
APP_URL=http://laravelte.test
@@ -10,6 +10,7 @@ LOG_CHANNEL=stack
1010
LOG_DEPRECATIONS_CHANNEL=null
1111
LOG_LEVEL=debug
1212
DEBUGBAR_ENABLED=false
13+
DD_ON_ERROR=false
1314

1415
DB_CONNECTION=sqlite
1516
DB_HOST=127.0.0.1
@@ -20,7 +21,7 @@ DB_PASSWORD=
2021

2122
BROADCAST_DRIVER=log
2223
CACHE_DRIVER=redis
23-
CACHE_PREFIX="laravelte_"
24+
CACHE_PREFIX="laravelte_testing_"
2425
FILESYSTEM_DISK=local
2526
QUEUE_CONNECTION=sync
2627
SESSION_DRIVER=redis

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ It features:
2929
- [Laravel Actions](https://laravelactions.com/): Instead of creating controllers, jobs, listeners and so on, it allows you to create a PHP class that handles a specific task and run that class as anything you want.
3030
- [Redis support](https://laravel.com/docs/queues) and Laravel Queues.
3131
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
32+
- [i18N (Localization) - Multiple languages support](https://laravel.com/docs/10.x/localization#main-content).
3233

3334
Laravel is accessible, powerful, and provides tools required for large, robust applications.
3435

@@ -58,6 +59,13 @@ php artisan test --group=api
5859

5960
# Setup tests
6061
php artisan test --group=setup
62+
63+
# test in parallel
64+
php artisan test --parallel
65+
php artisan test --parallel --processes=4
66+
67+
# list of your ten slowest tests
68+
php artisan test --profile
6169
```
6270

6371
## Contributing

app/Actions/Auth/Register.php

Lines changed: 11 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
namespace App\Actions\Auth;
44

55
use App\Events\CRUDErrorOccurred;
6+
use App\Events\UserWasRegistered;
67
use App\Http\Requests\RegisterRequest;
78
use App\Http\Resources\UserResource;
89
use App\Models\User;
10+
use App\Repositories\UserRepository;
911
use App\Traits\CustomControllerResponsesTrait;
1012
use App\Traits\ThemesTrait;
1113
use Exception;
@@ -21,7 +23,14 @@ class Register
2123
use ThemesTrait;
2224
use CustomControllerResponsesTrait;
2325

24-
public function asController(RegisterRequest $request)
26+
/**
27+
* Provide functions for formatting & detecting phone numbers
28+
*
29+
* @var UsersRepository
30+
*/
31+
protected $usersRepo;
32+
33+
public function asController(RegisterRequest $request, UserRepository $usersRepo)
2534
{
2635
if (!setting('allow_user_registrations')) {
2736
return $this->respError(trans('auth.registration_closed'));
@@ -40,44 +49,7 @@ public function asController(RegisterRequest $request)
4049
'last_name',
4150
]);
4251

43-
$data = collect($data)->filter(function ($value) {
44-
return !empty($value);
45-
})->toArray();
46-
47-
try {
48-
DB::beginTransaction();
49-
50-
$role = User::ROLE_DEFAULT;
51-
$theRole = 'ROLE_' . strtoupper($role);
52-
$data['role'] = constant("App\Models\User::$theRole");
53-
54-
isset($data['date_of_birth']) ? $data['date_of_birth'] = Carbon::parse(request('date_of_birth')) : '';
55-
$data['password'] = bcrypt($data['password']);
56-
57-
// set an appropriate (user)name if not provided
58-
if (!isset($data['name']) || empty($data['name'])) {
59-
if (isset($data['first_name'])) {
60-
$data['name'] = Str::slug($data['first_name'] . '-' . Str::random(5));
61-
} else if (isset($data['email'])) {
62-
$data['name'] = Str::slug(explode('@', $data['email'])[0] . '-' . Str::random(5));
63-
} else {
64-
$data['name'] = Str::slug(Str::random(10));
65-
}
66-
}
67-
68-
$user = User::create($data);
69-
} catch (Exception $e) {
70-
DB::rollBack();
71-
72-
debugOn() ? dde($e->getMessage()) : '';
73-
74-
event(new CRUDErrorOccurred($e->getMessage()));
75-
76-
return false;
77-
}
78-
79-
// TODO: event
80-
// event(new UserWasCreated($user->fresh(), doe()));
52+
$user = $usersRepo->create($data, User::ROLE_DEFAULT);
8153

8254
if ($user) {
8355
if ($request->wantsJson())

app/Events/UserWasCreated.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
namespace App\Events;
4+
5+
use App\Models\User;
6+
use Illuminate\Broadcasting\Channel;
7+
use Illuminate\Broadcasting\InteractsWithSockets;
8+
use Illuminate\Broadcasting\PresenceChannel;
9+
use Illuminate\Broadcasting\PrivateChannel;
10+
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
11+
use Illuminate\Foundation\Events\Dispatchable;
12+
use Illuminate\Queue\SerializesModels;
13+
14+
class UserWasCreated
15+
{
16+
use Dispatchable, InteractsWithSockets, SerializesModels;
17+
18+
public $user;
19+
public $ipAddress;
20+
21+
/**
22+
* User|null admin. If missing, the user was created by the system/registration)
23+
*/
24+
public $admin;
25+
26+
/**
27+
* Create a new event instance.
28+
*
29+
* @return void
30+
*/
31+
public function __construct(User $user, $ipAddress, $admin = null)
32+
{
33+
$this->user = $user;
34+
$this->ipAddress = $ipAddress;
35+
$this->admin = $admin;
36+
}
37+
38+
/**
39+
* Get the channels the event should broadcast on.
40+
*
41+
* @return \Illuminate\Broadcasting\Channel|array
42+
*/
43+
public function broadcastOn()
44+
{
45+
return new PrivateChannel('channel-name');
46+
}
47+
}

app/Http/Requests/RegisterRequest.php

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public function rules()
2525
{
2626
return [
2727
'name' => 'nullable|string|max:255|unique:users,name',
28-
'email' => 'nullable|email|string|min:1|max:255|unique:users,email',
28+
'email' => 'required|email|string|min:1|max:255|unique:users,email',
2929
'phone' => 'nullable|string|max:255|min:10|unique:users,phone|starts_with:+',
3030
'password' => 'required|string|confirmed|max:255',
3131
'first_name' => 'required|string|max:255',
@@ -35,25 +35,4 @@ public function rules()
3535
];
3636
}
3737

38-
/**
39-
* extra validation
40-
*
41-
* @param $validator
42-
*/
43-
public function withValidator($validator)
44-
{
45-
// validate at least user has provided email, or username
46-
$validator->after(function ($validator)
47-
{
48-
$email = $validator->attributes()['email'] ?? null;
49-
$username = $validator->attributes()['name'] ?? null;
50-
51-
if(! $email && ! $username)
52-
{
53-
$message = trans('auth.email_name_required');
54-
$validator->errors()->add('email', $message);
55-
$validator->errors()->add('name', $message);
56-
}
57-
});
58-
}
5938
}

app/Library/events.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
use App\Events\CRUDErrorOccurred;
44
use App\Events\UserLoggedIn;
5+
use App\Events\UserWasCreated;
56
use App\Listeners\CRUDErrorHasOccurred;
7+
use App\Listeners\UserHasBeenCreated;
68
use App\Listeners\UserHasLoggedIn;
79

810
$events = [
@@ -12,5 +14,11 @@
1214

1315
UserLoggedIn::class => [
1416
UserHasLoggedIn::class,
17+
],
18+
19+
UserWasCreated::class => [
20+
UserHasBeenCreated::class,
1521
]
22+
23+
1624
];

app/Library/helpers.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<?php
22

33
use App\Models\Configuration;
4+
use App\Models\User;
45
use App\Repositories\RedisRepository;
56
use App\Repositories\SystemInfoRepository;
67
use Illuminate\Http\Resources\Json\JsonResource;
@@ -35,6 +36,18 @@ function debugOn() : bool
3536
}
3637
}
3738

39+
if (! function_exists('ddOnError'))
40+
{
41+
/**
42+
* dd on error
43+
*/
44+
function ddOnError(Exception $e) : void
45+
{
46+
if(debugOn() && env('DD_ON_ERROR', true))
47+
dde($e->getMessage());
48+
}
49+
}
50+
3851

3952
if(! function_exists('doe'))
4053
{
@@ -317,3 +330,14 @@ function is_serialized($data)
317330
}
318331
}
319332

333+
if (! function_exists('adminUsers'))
334+
{
335+
/**
336+
* Get the admin users in the system
337+
* Userfule when one requires to alert admins of an activity
338+
*/
339+
function adminUsers()
340+
{
341+
return User::admins()->get();
342+
}
343+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
namespace App\Listeners;
4+
5+
use App\Notifications\NotifyAdmin;
6+
use Illuminate\Contracts\Queue\ShouldQueue;
7+
use Illuminate\Queue\InteractsWithQueue;
8+
9+
class UserHasBeenCreated implements ShouldQueue
10+
{
11+
public $tries = 3;
12+
public $timeout = 120;
13+
14+
/**
15+
* Create the event listener.
16+
*
17+
* @return void
18+
*/
19+
public function __construct()
20+
{
21+
//
22+
}
23+
24+
/**
25+
* Handle the event.
26+
*
27+
* @param object $event
28+
* @return void
29+
*/
30+
public function handle($event)
31+
{
32+
$user = $event->user;
33+
$ipAddress = $event->ipAddress;
34+
$admin = $event->admin;
35+
36+
// notify admins
37+
foreach(adminUsers() as $admin)
38+
{
39+
$message = $admin
40+
? $admin->aka . " added a new user in the system."
41+
: "Email: " . $user->email;
42+
43+
$admin->notify(new NotifyAdmin($user->aka . "'s account has just been created!", $message, '#', $ipAddress));
44+
}
45+
46+
}
47+
}

app/Notifications/NotifyAdmin.php

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
3+
namespace App\Notifications;
4+
5+
use Illuminate\Bus\Queueable;
6+
use Illuminate\Contracts\Queue\ShouldQueue;
7+
use Illuminate\Notifications\Messages\MailMessage;
8+
use Illuminate\Notifications\Notification;
9+
10+
class NotifyAdmin extends Notification
11+
{
12+
use Queueable;
13+
14+
public $tries = 3;
15+
public $timeout = 120;
16+
17+
public $title, $message, $ipAddress, $link;
18+
19+
/**
20+
* Create a new notification instance.
21+
*/
22+
public function __construct(
23+
string $title,
24+
string $message,
25+
$link = '#',
26+
$ipAddress = null
27+
)
28+
{
29+
$this->title = $title;
30+
$this->message = $message;
31+
$this->link = $link;
32+
$this->ipAddress = $ipAddress;
33+
}
34+
35+
/**
36+
* Get the notification's delivery channels.
37+
*
38+
* @return array<int, string>
39+
*/
40+
public function via(object $notifiable): array
41+
{
42+
// return $notifiable->prefers_sms ? ['nexmo'] : ['mail', 'database'];
43+
return ['database'];
44+
}
45+
46+
/**
47+
* Get the mail representation of the notification.
48+
*/
49+
public function toMail(object $notifiable): MailMessage
50+
{
51+
return (new MailMessage)
52+
->line('The introduction to the notification.')
53+
->action('Notification Action', url('/'))
54+
->line('Thank you for using our application!');
55+
}
56+
57+
/**
58+
* Get the array representation of the notification.
59+
*
60+
* @return array<string, mixed>
61+
*/
62+
public function toArray(object $notifiable): array
63+
{
64+
return [
65+
'title' => $this->title,
66+
'message' => $this->message,
67+
'link' => $this->link,
68+
'ip_address' => $this->ipAddress,
69+
];
70+
}
71+
}

0 commit comments

Comments
 (0)