merge user-search-and-admin-promote
This commit is contained in:
commit
c55852ec12
12 changed files with 531 additions and 0 deletions
88
backend/app/Controllers/UserController.php
Normal file
88
backend/app/Controllers/UserController.php
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controllers;
|
||||||
|
|
||||||
|
use App\Exceptions\BadRequestException;
|
||||||
|
use App\Exceptions\ForbiddenException;
|
||||||
|
use App\User\UseCases\PromoteUserToAdmin\PromoteUserToAdmin;
|
||||||
|
use App\User\UseCases\PromoteUserToAdmin\PromoteUserToAdminRequest;
|
||||||
|
use App\User\UseCases\SearchUsers\SearchUsers;
|
||||||
|
use App\User\UseCases\SearchUsers\SearchUsersRequest;
|
||||||
|
use App\User\User;
|
||||||
|
use DomainException;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class UserController
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private SearchUsers $searchUsers,
|
||||||
|
private PromoteUserToAdmin $promoteUserToAdmin,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public function search(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
$query = $request->query('q');
|
||||||
|
if (! is_string($query) || trim($query) === '') {
|
||||||
|
return new JsonResponse(['users' => []], 200);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$results = $this->searchUsers->execute(
|
||||||
|
new SearchUsersRequest(query: $query),
|
||||||
|
);
|
||||||
|
} catch (BadRequestException $exception) {
|
||||||
|
return new JsonResponse(
|
||||||
|
['error' => $exception->getMessage()], 400,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonResponse([
|
||||||
|
'users' => array_map(
|
||||||
|
function (User $user) {
|
||||||
|
return [
|
||||||
|
'id' => $user->getId(),
|
||||||
|
'email' => $user->getEmail()->value(),
|
||||||
|
'displayName' => $user->getDisplayName(),
|
||||||
|
'isAdmin' => $user->isAdmin(),
|
||||||
|
];
|
||||||
|
},
|
||||||
|
$results,
|
||||||
|
),
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function promote(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
/** @var User $requester */
|
||||||
|
$requester = $request->attributes->get('user');
|
||||||
|
try {
|
||||||
|
$promoted = $this->promoteUserToAdmin->execute(
|
||||||
|
new PromoteUserToAdminRequest(
|
||||||
|
targetUserId: (int) $request->input('userId'),
|
||||||
|
requesterIsAdmin: $requester->isAdmin(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} catch (BadRequestException $exception) {
|
||||||
|
return new JsonResponse(
|
||||||
|
['error' => $exception->getMessage()], 400,
|
||||||
|
);
|
||||||
|
} catch (ForbiddenException $exception) {
|
||||||
|
return new JsonResponse(
|
||||||
|
['error' => $exception->getMessage()], 403,
|
||||||
|
);
|
||||||
|
} catch (DomainException $exception) {
|
||||||
|
return new JsonResponse(
|
||||||
|
['error' => $exception->getMessage()], 404,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonResponse([
|
||||||
|
'user' => [
|
||||||
|
'id' => $promoted->getId(),
|
||||||
|
'email' => $promoted->getEmail()->value(),
|
||||||
|
'displayName' => $promoted->getDisplayName(),
|
||||||
|
'isAdmin' => $promoted->isAdmin(),
|
||||||
|
],
|
||||||
|
], 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -43,6 +43,25 @@ class EloquentUserRepository implements UserRepository
|
||||||
return $model === null ? null : $this->toDomain($model);
|
return $model === null ? null : $this->toDomain($model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return User[]
|
||||||
|
*/
|
||||||
|
public function search(string $query): array
|
||||||
|
{
|
||||||
|
$like = strtolower($query).'%';
|
||||||
|
$models = UserModel::query()
|
||||||
|
->whereRaw('LOWER(display_name) LIKE ?', [$like])
|
||||||
|
->orWhereRaw('LOWER(email) LIKE ?', [$like])
|
||||||
|
->orderBy('display_name')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
return $models->map(
|
||||||
|
function (UserModel $model) {
|
||||||
|
return $this->toDomain($model);
|
||||||
|
},
|
||||||
|
)->all();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws RuntimeException
|
* @throws RuntimeException
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\User\UseCases\PromoteUserToAdmin;
|
||||||
|
|
||||||
|
use App\Exceptions\BadRequestException;
|
||||||
|
use App\Exceptions\ForbiddenException;
|
||||||
|
use App\User\User;
|
||||||
|
use App\User\UserRepository;
|
||||||
|
use DomainException;
|
||||||
|
|
||||||
|
class PromoteUserToAdmin
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private UserRepository $userRepo,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws BadRequestException
|
||||||
|
* @throws ForbiddenException
|
||||||
|
* @throws DomainException
|
||||||
|
*/
|
||||||
|
public function execute(PromoteUserToAdminRequest $request): User
|
||||||
|
{
|
||||||
|
if (! $request->requesterIsAdmin) {
|
||||||
|
throw new ForbiddenException(
|
||||||
|
'only admins can promote users'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ($request->targetUserId <= 0) {
|
||||||
|
throw new BadRequestException(
|
||||||
|
'targetUserId must be positive'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$target = $this->userRepo->find($request->targetUserId);
|
||||||
|
if ($target === null) {
|
||||||
|
throw new DomainException('user not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($target->isAdmin()) {
|
||||||
|
return $target;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->userRepo->update(new User(
|
||||||
|
id: $target->getId(),
|
||||||
|
email: $target->getEmail(),
|
||||||
|
displayName: $target->getDisplayName(),
|
||||||
|
passwordHash: $target->getPasswordHash(),
|
||||||
|
isAdmin: true,
|
||||||
|
emailConfirmedAt: $target->getEmailConfirmedAt(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\User\UseCases\PromoteUserToAdmin;
|
||||||
|
|
||||||
|
class PromoteUserToAdminRequest
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public int $targetUserId,
|
||||||
|
public bool $requesterIsAdmin,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
29
backend/app/User/UseCases/SearchUsers/SearchUsers.php
Normal file
29
backend/app/User/UseCases/SearchUsers/SearchUsers.php
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\User\UseCases\SearchUsers;
|
||||||
|
|
||||||
|
use App\Exceptions\BadRequestException;
|
||||||
|
use App\User\User;
|
||||||
|
use App\User\UserRepository;
|
||||||
|
|
||||||
|
class SearchUsers
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private UserRepository $userRepo,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return User[]
|
||||||
|
*
|
||||||
|
* @throws BadRequestException
|
||||||
|
*/
|
||||||
|
public function execute(SearchUsersRequest $request): array
|
||||||
|
{
|
||||||
|
$query = trim($request->query);
|
||||||
|
if ($query === '') {
|
||||||
|
throw new BadRequestException('query is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->userRepo->search($query);
|
||||||
|
}
|
||||||
|
}
|
||||||
10
backend/app/User/UseCases/SearchUsers/SearchUsersRequest.php
Normal file
10
backend/app/User/UseCases/SearchUsers/SearchUsersRequest.php
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\User\UseCases\SearchUsers;
|
||||||
|
|
||||||
|
class SearchUsersRequest
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public string $query,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
@ -15,4 +15,9 @@ interface UserRepository
|
||||||
public function findByDisplayName(string $displayName): ?User;
|
public function findByDisplayName(string $displayName): ?User;
|
||||||
|
|
||||||
public function update(User $user): User;
|
public function update(User $user): User;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return User[]
|
||||||
|
*/
|
||||||
|
public function search(string $query): array;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
use App\Controllers\AuthController;
|
use App\Controllers\AuthController;
|
||||||
use App\Controllers\CommentController;
|
use App\Controllers\CommentController;
|
||||||
use App\Controllers\PostController;
|
use App\Controllers\PostController;
|
||||||
|
use App\Controllers\UserController;
|
||||||
use App\Http\Middleware\AuthMiddleware;
|
use App\Http\Middleware\AuthMiddleware;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
|
|
@ -32,6 +33,10 @@ Route::post('/admin/posts/feature', [PostController::class, 'feature'])
|
||||||
Route::post('/admin/posts/unfeature', [PostController::class, 'unfeature'])
|
Route::post('/admin/posts/unfeature', [PostController::class, 'unfeature'])
|
||||||
->middleware(AuthMiddleware::class);
|
->middleware(AuthMiddleware::class);
|
||||||
|
|
||||||
|
Route::get('/users', [UserController::class, 'search']);
|
||||||
|
Route::post('/admin/users/promote', [UserController::class, 'promote'])
|
||||||
|
->middleware(AuthMiddleware::class);
|
||||||
|
|
||||||
Route::get(
|
Route::get(
|
||||||
'/users/{displayName}/posts',
|
'/users/{displayName}/posts',
|
||||||
[PostController::class, 'listByUser'],
|
[PostController::class, 'listByUser'],
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,36 @@ class FakeUserRepository implements UserRepository
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return User[]
|
||||||
|
*/
|
||||||
|
public function search(string $query): array
|
||||||
|
{
|
||||||
|
$needle = strtolower($query);
|
||||||
|
$results = [];
|
||||||
|
foreach ($this->existingUsers as $user) {
|
||||||
|
$displayName = strtolower($user->getDisplayName());
|
||||||
|
$email = strtolower($user->getEmail()->value());
|
||||||
|
if (
|
||||||
|
str_starts_with($displayName, $needle)
|
||||||
|
|| str_starts_with($email, $needle)
|
||||||
|
) {
|
||||||
|
$results[] = $this->copy($user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
usort(
|
||||||
|
$results,
|
||||||
|
function (User $left, User $right) {
|
||||||
|
return strcmp(
|
||||||
|
$left->getDisplayName(),
|
||||||
|
$right->getDisplayName(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws RuntimeException
|
* @throws RuntimeException
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
92
backend/tests/Feature/User/UserSearchAndPromoteTest.php
Normal file
92
backend/tests/Feature/User/UserSearchAndPromoteTest.php
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature\User;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Tests\Feature\AuthenticatesUsers;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class UserSearchAndPromoteTest extends TestCase
|
||||||
|
{
|
||||||
|
use AuthenticatesUsers;
|
||||||
|
use RefreshDatabase;
|
||||||
|
|
||||||
|
public function test_search_is_public(): void
|
||||||
|
{
|
||||||
|
$this->signupAndLogin(
|
||||||
|
email: 'alice@example.com',
|
||||||
|
displayName: 'alice',
|
||||||
|
password: 'longenoughpassword',
|
||||||
|
);
|
||||||
|
$this->signupAndLogin(
|
||||||
|
email: 'alex@example.com',
|
||||||
|
displayName: 'alex',
|
||||||
|
password: 'longenoughpassword',
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->resetClientState();
|
||||||
|
$response = $this->getJson('/api/users?q=al');
|
||||||
|
$response->assertStatus(200);
|
||||||
|
$response->assertJsonCount(2, 'users');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_search_with_no_query_returns_empty(): void
|
||||||
|
{
|
||||||
|
$response = $this->getJson('/api/users');
|
||||||
|
$response->assertStatus(200);
|
||||||
|
$response->assertJsonPath('users', []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_non_admin_cannot_promote(): void
|
||||||
|
{
|
||||||
|
$alice = $this->signupAndLogin(
|
||||||
|
email: 'alice@example.com',
|
||||||
|
displayName: 'alice',
|
||||||
|
password: 'longenoughpassword',
|
||||||
|
);
|
||||||
|
$bob = $this->signupAndLogin(
|
||||||
|
email: 'bob@example.com',
|
||||||
|
displayName: 'bob',
|
||||||
|
password: 'longenoughpassword',
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->resetClientState();
|
||||||
|
$this->withCredentials()
|
||||||
|
->withUnencryptedCookie('auth_token', $alice['cookie'])
|
||||||
|
->postJson('/api/admin/users/promote', [
|
||||||
|
'userId' => $bob['user']->getId(),
|
||||||
|
])
|
||||||
|
->assertStatus(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_admin_promotes_user(): void
|
||||||
|
{
|
||||||
|
$alice = $this->signupAndLogin(
|
||||||
|
email: 'alice@example.com',
|
||||||
|
displayName: 'alice',
|
||||||
|
password: 'longenoughpassword',
|
||||||
|
);
|
||||||
|
$bob = $this->signupAndLogin(
|
||||||
|
email: 'bob@example.com',
|
||||||
|
displayName: 'bob',
|
||||||
|
password: 'longenoughpassword',
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->promoteToAdmin($alice['user']->getId());
|
||||||
|
$loginResponse = $this->postJson('/api/login', [
|
||||||
|
'email' => 'alice@example.com',
|
||||||
|
'password' => 'longenoughpassword',
|
||||||
|
]);
|
||||||
|
$aliceCookie = $loginResponse->getCookie('auth_token', false)
|
||||||
|
->getValue();
|
||||||
|
|
||||||
|
$this->resetClientState();
|
||||||
|
$this->withCredentials()
|
||||||
|
->withUnencryptedCookie('auth_token', $aliceCookie)
|
||||||
|
->postJson('/api/admin/users/promote', [
|
||||||
|
'userId' => $bob['user']->getId(),
|
||||||
|
])
|
||||||
|
->assertStatus(200)
|
||||||
|
->assertJsonPath('user.isAdmin', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
94
backend/tests/Unit/User/UseCases/PromoteUserToAdminTest.php
Normal file
94
backend/tests/Unit/User/UseCases/PromoteUserToAdminTest.php
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Unit\User\UseCases;
|
||||||
|
|
||||||
|
use App\Exceptions\BadRequestException;
|
||||||
|
use App\Exceptions\ForbiddenException;
|
||||||
|
use App\Shared\ValueObject\EmailAddress;
|
||||||
|
use App\User\CreateUserDto;
|
||||||
|
use App\User\UseCases\PromoteUserToAdmin\PromoteUserToAdmin;
|
||||||
|
use App\User\UseCases\PromoteUserToAdmin\PromoteUserToAdminRequest;
|
||||||
|
use DomainException;
|
||||||
|
use Tests\Fakes\FakeUserRepository;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class PromoteUserToAdminTest extends TestCase
|
||||||
|
{
|
||||||
|
private FakeUserRepository $userRepo;
|
||||||
|
|
||||||
|
private PromoteUserToAdmin $useCase;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
$this->userRepo = new FakeUserRepository;
|
||||||
|
$this->useCase = new PromoteUserToAdmin($this->userRepo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function seedUser(bool $isAdmin): int
|
||||||
|
{
|
||||||
|
$user = $this->userRepo->create(new CreateUserDto(
|
||||||
|
email: new EmailAddress('user@example.com'),
|
||||||
|
displayName: 'user',
|
||||||
|
passwordHash: 'h',
|
||||||
|
isAdmin: $isAdmin,
|
||||||
|
emailConfirmedAt: null,
|
||||||
|
));
|
||||||
|
|
||||||
|
return $user->getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_non_admin_requester_throws_forbidden(): void
|
||||||
|
{
|
||||||
|
$userId = $this->seedUser(false);
|
||||||
|
|
||||||
|
$this->expectException(ForbiddenException::class);
|
||||||
|
$this->useCase->execute(new PromoteUserToAdminRequest(
|
||||||
|
targetUserId: $userId,
|
||||||
|
requesterIsAdmin: false,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_zero_target_id_throws_bad_request(): void
|
||||||
|
{
|
||||||
|
$this->expectException(BadRequestException::class);
|
||||||
|
$this->useCase->execute(new PromoteUserToAdminRequest(
|
||||||
|
targetUserId: 0,
|
||||||
|
requesterIsAdmin: true,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_unknown_user_throws_domain_exception(): void
|
||||||
|
{
|
||||||
|
$this->expectException(DomainException::class);
|
||||||
|
$this->useCase->execute(new PromoteUserToAdminRequest(
|
||||||
|
targetUserId: 999,
|
||||||
|
requesterIsAdmin: true,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_admin_promotes_target(): void
|
||||||
|
{
|
||||||
|
$userId = $this->seedUser(false);
|
||||||
|
|
||||||
|
$this->useCase->execute(new PromoteUserToAdminRequest(
|
||||||
|
targetUserId: $userId,
|
||||||
|
requesterIsAdmin: true,
|
||||||
|
));
|
||||||
|
|
||||||
|
$reloaded = $this->userRepo->find($userId);
|
||||||
|
$this->assertTrue($reloaded->isAdmin());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_already_admin_is_idempotent(): void
|
||||||
|
{
|
||||||
|
$userId = $this->seedUser(true);
|
||||||
|
|
||||||
|
$this->useCase->execute(new PromoteUserToAdminRequest(
|
||||||
|
targetUserId: $userId,
|
||||||
|
requesterIsAdmin: true,
|
||||||
|
));
|
||||||
|
|
||||||
|
$reloaded = $this->userRepo->find($userId);
|
||||||
|
$this->assertTrue($reloaded->isAdmin());
|
||||||
|
}
|
||||||
|
}
|
||||||
95
backend/tests/Unit/User/UseCases/SearchUsersTest.php
Normal file
95
backend/tests/Unit/User/UseCases/SearchUsersTest.php
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Unit\User\UseCases;
|
||||||
|
|
||||||
|
use App\Exceptions\BadRequestException;
|
||||||
|
use App\Shared\ValueObject\EmailAddress;
|
||||||
|
use App\User\CreateUserDto;
|
||||||
|
use App\User\UseCases\SearchUsers\SearchUsers;
|
||||||
|
use App\User\UseCases\SearchUsers\SearchUsersRequest;
|
||||||
|
use Tests\Fakes\FakeUserRepository;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class SearchUsersTest extends TestCase
|
||||||
|
{
|
||||||
|
private FakeUserRepository $userRepo;
|
||||||
|
|
||||||
|
private SearchUsers $useCase;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
$this->userRepo = new FakeUserRepository;
|
||||||
|
$this->useCase = new SearchUsers($this->userRepo);
|
||||||
|
|
||||||
|
$this->userRepo->create(new CreateUserDto(
|
||||||
|
email: new EmailAddress('alice@example.com'),
|
||||||
|
displayName: 'alice',
|
||||||
|
passwordHash: '',
|
||||||
|
isAdmin: false,
|
||||||
|
emailConfirmedAt: null,
|
||||||
|
));
|
||||||
|
$this->userRepo->create(new CreateUserDto(
|
||||||
|
email: new EmailAddress('alex@example.com'),
|
||||||
|
displayName: 'alex',
|
||||||
|
passwordHash: '',
|
||||||
|
isAdmin: false,
|
||||||
|
emailConfirmedAt: null,
|
||||||
|
));
|
||||||
|
$this->userRepo->create(new CreateUserDto(
|
||||||
|
email: new EmailAddress('bob@other.com'),
|
||||||
|
displayName: 'bob',
|
||||||
|
passwordHash: '',
|
||||||
|
isAdmin: false,
|
||||||
|
emailConfirmedAt: null,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_blank_query_throws_bad_request(): void
|
||||||
|
{
|
||||||
|
$this->expectException(BadRequestException::class);
|
||||||
|
$this->useCase->execute(new SearchUsersRequest(query: ' '));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_finds_users_by_display_name_prefix(): void
|
||||||
|
{
|
||||||
|
$results = $this->useCase->execute(new SearchUsersRequest(
|
||||||
|
query: 'al',
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->assertCount(2, $results);
|
||||||
|
$names = array_map(
|
||||||
|
function ($user) {
|
||||||
|
return $user->getDisplayName();
|
||||||
|
},
|
||||||
|
$results,
|
||||||
|
);
|
||||||
|
sort($names);
|
||||||
|
$this->assertSame(['alex', 'alice'], $names);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_search_is_case_insensitive(): void
|
||||||
|
{
|
||||||
|
$results = $this->useCase->execute(new SearchUsersRequest(
|
||||||
|
query: 'ALI',
|
||||||
|
));
|
||||||
|
$this->assertCount(1, $results);
|
||||||
|
$this->assertSame('alice', $results[0]->getDisplayName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_finds_users_by_email_prefix(): void
|
||||||
|
{
|
||||||
|
$results = $this->useCase->execute(new SearchUsersRequest(
|
||||||
|
query: 'bob@',
|
||||||
|
));
|
||||||
|
$this->assertCount(1, $results);
|
||||||
|
$this->assertSame('bob', $results[0]->getDisplayName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_no_results_when_no_match(): void
|
||||||
|
{
|
||||||
|
$results = $this->useCase->execute(new SearchUsersRequest(
|
||||||
|
query: 'zzz',
|
||||||
|
));
|
||||||
|
$this->assertSame([], $results);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue