админка
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\GameAccountUser;
|
||||
use App\Services\AzerothCoreAccountService;
|
||||
use App\Services\InviteCodeService;
|
||||
use Illuminate\Support\Collection;
|
||||
use Mockery\MockInterface;
|
||||
use Tests\TestCase;
|
||||
|
||||
class AdminAccountsFeatureTest extends TestCase
|
||||
{
|
||||
public function test_guest_is_redirected_to_login_for_admin(): void
|
||||
{
|
||||
$response = $this->get(route('admin.accounts.index'));
|
||||
|
||||
$response->assertRedirect(route('login'));
|
||||
}
|
||||
|
||||
public function test_regular_player_cannot_open_admin_accounts_page(): void
|
||||
{
|
||||
$response = $this->actingAs($this->makeUser(0))->get(route('admin.accounts.index'));
|
||||
|
||||
$response->assertForbidden();
|
||||
}
|
||||
|
||||
public function test_admin_can_open_accounts_page(): void
|
||||
{
|
||||
$this->mock(AzerothCoreAccountService::class, function (MockInterface $mock): void {
|
||||
$mock->shouldReceive('listAccounts')
|
||||
->once()
|
||||
->with(null)
|
||||
->andReturn(new Collection([
|
||||
[
|
||||
'id' => 1,
|
||||
'username' => 'ADMIN',
|
||||
'email' => 'ADMIN@EXAMPLE.COM',
|
||||
'reg_mail' => 'ADMIN@EXAMPLE.COM',
|
||||
'gm_level' => 3,
|
||||
'access_label' => 'Администратор',
|
||||
'locked' => false,
|
||||
'online' => false,
|
||||
'joined_at' => '12.03.2026 12:00',
|
||||
'last_login_at' => '13.03.2026 18:00',
|
||||
'characters' => new Collection([
|
||||
[
|
||||
'name' => 'Stormmage',
|
||||
'race' => 'Человек',
|
||||
'class' => 'Маг',
|
||||
'level' => 80,
|
||||
'online' => false,
|
||||
],
|
||||
]),
|
||||
],
|
||||
]));
|
||||
});
|
||||
|
||||
$this->mock(InviteCodeService::class, function (MockInterface $mock): void {
|
||||
$mock->shouldReceive('latest')
|
||||
->once()
|
||||
->andReturn(new Collection());
|
||||
|
||||
$mock->shouldReceive('stats')
|
||||
->once()
|
||||
->andReturn(['total' => 0, 'available' => 0, 'redeemed' => 0]);
|
||||
});
|
||||
|
||||
$response = $this->actingAs($this->makeUser(3))->get(route('admin.accounts.index'));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertSee('Stormmage');
|
||||
$response->assertSee('Аккаунты и персонажи');
|
||||
$response->assertSee('Управление инвайт-кодами');
|
||||
}
|
||||
|
||||
public function test_admin_can_update_access_level(): void
|
||||
{
|
||||
$this->mock(AzerothCoreAccountService::class, function (MockInterface $mock): void {
|
||||
$mock->shouldReceive('findUserById')
|
||||
->once()
|
||||
->with(12)
|
||||
->andReturn($this->makeUser(0));
|
||||
|
||||
$mock->shouldReceive('updateAccessLevel')
|
||||
->once()
|
||||
->with(12, 2);
|
||||
});
|
||||
|
||||
$response = $this->from(route('admin.accounts.index'))
|
||||
->actingAs($this->makeUser(3))
|
||||
->patch(route('admin.accounts.access.update', 12), [
|
||||
'gmlevel' => 2,
|
||||
]);
|
||||
|
||||
$response->assertRedirect(route('admin.accounts.index'));
|
||||
$response->assertSessionHas('status', 'Уровень доступа обновлён.');
|
||||
}
|
||||
|
||||
public function test_admin_can_create_invite_codes(): void
|
||||
{
|
||||
$admin = $this->makeUser(3);
|
||||
|
||||
$this->mock(InviteCodeService::class, function (MockInterface $mock) use ($admin): void {
|
||||
$mock->shouldReceive('createCodes')
|
||||
->once()
|
||||
->with(3, $admin)
|
||||
->andReturn(new Collection([1, 2, 3]));
|
||||
});
|
||||
|
||||
$response = $this->from(route('admin.accounts.index'))
|
||||
->actingAs($admin)
|
||||
->post(route('admin.invite-codes.store'), [
|
||||
'quantity' => 3,
|
||||
]);
|
||||
|
||||
$response->assertRedirect(route('admin.accounts.index'));
|
||||
$response->assertSessionHas('status', 'Создано инвайт-кодов: 3.');
|
||||
}
|
||||
|
||||
private function makeUser(int $gmLevel): GameAccountUser
|
||||
{
|
||||
return new GameAccountUser(
|
||||
id: 1,
|
||||
username: 'ADMIN',
|
||||
email: 'ADMIN@EXAMPLE.COM',
|
||||
regMail: 'ADMIN@EXAMPLE.COM',
|
||||
gmLevel: $gmLevel,
|
||||
locked: false,
|
||||
lastLoginAt: '2026-03-13 18:00:00',
|
||||
joinedAt: '2026-03-12 12:00:00',
|
||||
saltHex: str_repeat('AA', 32),
|
||||
verifierHex: str_repeat('BB', 32),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\GameAccountUser;
|
||||
use App\Services\AzerothCoreAccountService;
|
||||
use App\Services\GameClientDownloadService;
|
||||
use Illuminate\Support\Collection;
|
||||
use Mockery\MockInterface;
|
||||
use Tests\TestCase;
|
||||
|
||||
class CabinetFeatureTest extends TestCase
|
||||
{
|
||||
public function test_guest_is_redirected_to_login_for_cabinet(): void
|
||||
{
|
||||
$response = $this->get(route('cabinet.index'));
|
||||
|
||||
$response->assertRedirect(route('login'));
|
||||
}
|
||||
|
||||
public function test_authenticated_user_can_open_cabinet(): void
|
||||
{
|
||||
$user = $this->makeUser();
|
||||
|
||||
$this->mock(AzerothCoreAccountService::class, function (MockInterface $mock) use ($user): void {
|
||||
$mock->shouldReceive('charactersForAccount')
|
||||
->once()
|
||||
->with($user->id)
|
||||
->andReturn(new Collection([
|
||||
[
|
||||
'name' => 'Arthion',
|
||||
'race' => 'Человек',
|
||||
'class' => 'Паладин',
|
||||
'gender' => 'Мужской',
|
||||
'level' => 80,
|
||||
'money' => '125з 0с 0м',
|
||||
'played' => '4д 6ч',
|
||||
'online' => false,
|
||||
],
|
||||
]));
|
||||
});
|
||||
|
||||
$response = $this->actingAs($user)->get(route('cabinet.index'));
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertSee('Arthion');
|
||||
$response->assertSee('Получить ссылку на клиент');
|
||||
}
|
||||
|
||||
public function test_authenticated_user_can_change_password(): void
|
||||
{
|
||||
$user = $this->makeUser();
|
||||
|
||||
$this->mock(AzerothCoreAccountService::class, function (MockInterface $mock) use ($user): void {
|
||||
$mock->shouldReceive('validatePassword')
|
||||
->once()
|
||||
->with($user, 'OldPass123')
|
||||
->andReturn(true);
|
||||
|
||||
$mock->shouldReceive('updatePassword')
|
||||
->once()
|
||||
->with($user, 'NewPass123');
|
||||
});
|
||||
|
||||
$response = $this->actingAs($user)->post(route('cabinet.password.update'), [
|
||||
'current_password' => 'OldPass123',
|
||||
'password' => 'NewPass123',
|
||||
'password_confirmation' => 'NewPass123',
|
||||
]);
|
||||
|
||||
$response->assertRedirect();
|
||||
$response->assertSessionHas('status', 'Пароль успешно обновлён.');
|
||||
}
|
||||
|
||||
public function test_password_change_rejects_invalid_current_password(): void
|
||||
{
|
||||
$user = $this->makeUser();
|
||||
|
||||
$this->mock(AzerothCoreAccountService::class, function (MockInterface $mock) use ($user): void {
|
||||
$mock->shouldReceive('validatePassword')
|
||||
->once()
|
||||
->with($user, 'WrongPass123')
|
||||
->andReturn(false);
|
||||
|
||||
$mock->shouldNotReceive('updatePassword');
|
||||
});
|
||||
|
||||
$response = $this->from(route('cabinet.index'))->actingAs($user)->post(route('cabinet.password.update'), [
|
||||
'current_password' => 'WrongPass123',
|
||||
'password' => 'NewPass123',
|
||||
'password_confirmation' => 'NewPass123',
|
||||
]);
|
||||
|
||||
$response->assertRedirect(route('cabinet.index'));
|
||||
$response->assertSessionHasErrors(['current_password']);
|
||||
}
|
||||
|
||||
public function test_authenticated_user_can_request_client_link(): void
|
||||
{
|
||||
$user = $this->makeUser();
|
||||
|
||||
$this->mock(GameClientDownloadService::class, function (MockInterface $mock): void {
|
||||
$mock->shouldReceive('temporaryUrl')
|
||||
->once()
|
||||
->andReturn('https://example.com/client.zip');
|
||||
});
|
||||
|
||||
$response = $this->actingAs($user)->get(route('cabinet.client'));
|
||||
|
||||
$response->assertRedirect('https://example.com/client.zip');
|
||||
}
|
||||
|
||||
private function makeUser(int $gmLevel = 0): GameAccountUser
|
||||
{
|
||||
return new GameAccountUser(
|
||||
id: 7,
|
||||
username: 'PLAYERONE',
|
||||
email: 'PLAYER@EXAMPLE.COM',
|
||||
regMail: 'PLAYER@EXAMPLE.COM',
|
||||
gmLevel: $gmLevel,
|
||||
locked: false,
|
||||
lastLoginAt: '2026-03-13 18:00:00',
|
||||
joinedAt: '2026-03-12 12:00:00',
|
||||
saltHex: str_repeat('AA', 32),
|
||||
verifierHex: str_repeat('BB', 32),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,10 @@
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Exceptions\DuplicateGameAccountException;
|
||||
use App\Exceptions\InviteCodeException;
|
||||
use App\Services\AzerothCoreAccountRegistrar;
|
||||
use App\Services\InviteCodeService;
|
||||
use Closure;
|
||||
use Mockery\MockInterface;
|
||||
use Tests\TestCase;
|
||||
|
||||
@@ -11,6 +14,13 @@ class GameAccountRegistrationTest extends TestCase
|
||||
{
|
||||
public function test_registration_form_submits_successfully(): void
|
||||
{
|
||||
$this->mock(InviteCodeService::class, function (MockInterface $mock): void {
|
||||
$mock->shouldReceive('redeemForRegistration')
|
||||
->once()
|
||||
->with('INVITE-1234', 'playerone', \Mockery::type(Closure::class))
|
||||
->andReturnUsing(fn (string $code, string $username, Closure $callback): array => $callback());
|
||||
});
|
||||
|
||||
$this->mock(AzerothCoreAccountRegistrar::class, function (MockInterface $mock): void {
|
||||
$mock->shouldReceive('register')
|
||||
->once()
|
||||
@@ -21,6 +31,7 @@ class GameAccountRegistrationTest extends TestCase
|
||||
$response = $this->from(route('landing.index'))->post(route('game-account.store'), [
|
||||
'username' => 'playerone',
|
||||
'email' => 'player@example.com',
|
||||
'invite_code' => 'INVITE-1234',
|
||||
'password' => 'Pass1234',
|
||||
'password_confirmation' => 'Pass1234',
|
||||
'terms' => '1',
|
||||
@@ -35,17 +46,24 @@ class GameAccountRegistrationTest extends TestCase
|
||||
$response = $this->from(route('landing.index'))->post(route('game-account.store'), [
|
||||
'username' => 'игрок!',
|
||||
'email' => 'not-an-email',
|
||||
'invite_code' => '',
|
||||
'password' => '123',
|
||||
'password_confirmation' => '456',
|
||||
'terms' => '',
|
||||
]);
|
||||
|
||||
$response->assertRedirect(route('landing.index'));
|
||||
$response->assertSessionHasErrors(['username', 'email', 'password', 'terms']);
|
||||
$response->assertSessionHasErrors(['username', 'email', 'invite_code', 'password', 'terms']);
|
||||
}
|
||||
|
||||
public function test_duplicate_username_is_reported_back_to_the_form(): void
|
||||
{
|
||||
$this->mock(InviteCodeService::class, function (MockInterface $mock): void {
|
||||
$mock->shouldReceive('redeemForRegistration')
|
||||
->once()
|
||||
->andReturnUsing(fn (string $code, string $username, Closure $callback): array => $callback());
|
||||
});
|
||||
|
||||
$this->mock(AzerothCoreAccountRegistrar::class, function (MockInterface $mock): void {
|
||||
$mock->shouldReceive('register')
|
||||
->once()
|
||||
@@ -55,6 +73,7 @@ class GameAccountRegistrationTest extends TestCase
|
||||
$response = $this->from(route('landing.index'))->post(route('game-account.store'), [
|
||||
'username' => 'playerone',
|
||||
'email' => 'player@example.com',
|
||||
'invite_code' => 'INVITE-1234',
|
||||
'password' => 'Pass1234',
|
||||
'password_confirmation' => 'Pass1234',
|
||||
'terms' => '1',
|
||||
@@ -63,4 +82,25 @@ class GameAccountRegistrationTest extends TestCase
|
||||
$response->assertRedirect(route('landing.index'));
|
||||
$response->assertSessionHasErrors(['username']);
|
||||
}
|
||||
|
||||
public function test_invalid_invite_code_is_reported_back_to_the_form(): void
|
||||
{
|
||||
$this->mock(InviteCodeService::class, function (MockInterface $mock): void {
|
||||
$mock->shouldReceive('redeemForRegistration')
|
||||
->once()
|
||||
->andThrow(InviteCodeException::invalid());
|
||||
});
|
||||
|
||||
$response = $this->from(route('landing.index'))->post(route('game-account.store'), [
|
||||
'username' => 'playerone',
|
||||
'email' => 'player@example.com',
|
||||
'invite_code' => 'WRONG-CODE',
|
||||
'password' => 'Pass1234',
|
||||
'password_confirmation' => 'Pass1234',
|
||||
'terms' => '1',
|
||||
]);
|
||||
|
||||
$response->assertRedirect(route('landing.index'));
|
||||
$response->assertSessionHasErrors(['invite_code']);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user