vmangos
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\GameAccountUser;
|
||||
use App\Services\AzerothCoreAccountService;
|
||||
use App\Services\VMangosAccountService;
|
||||
use App\Services\InviteCodeService;
|
||||
use Illuminate\Support\Collection;
|
||||
use Inertia\Testing\AssertableInertia as Assert;
|
||||
@@ -28,7 +28,7 @@ class AdminAccountsFeatureTest extends TestCase
|
||||
|
||||
public function test_admin_can_open_accounts_page(): void
|
||||
{
|
||||
$this->mock(AzerothCoreAccountService::class, function (MockInterface $mock): void {
|
||||
$this->mock(VMangosAccountService::class, function (MockInterface $mock): void {
|
||||
$mock->shouldReceive('accountTypeStats')
|
||||
->once()
|
||||
->with(null)
|
||||
@@ -87,7 +87,7 @@ class AdminAccountsFeatureTest extends TestCase
|
||||
|
||||
public function test_admin_can_open_bots_tab(): void
|
||||
{
|
||||
$this->mock(AzerothCoreAccountService::class, function (MockInterface $mock): void {
|
||||
$this->mock(VMangosAccountService::class, function (MockInterface $mock): void {
|
||||
$mock->shouldReceive('accountTypeStats')
|
||||
->once()
|
||||
->with(null)
|
||||
@@ -137,7 +137,7 @@ class AdminAccountsFeatureTest extends TestCase
|
||||
|
||||
public function test_admin_can_update_access_level(): void
|
||||
{
|
||||
$this->mock(AzerothCoreAccountService::class, function (MockInterface $mock): void {
|
||||
$this->mock(VMangosAccountService::class, function (MockInterface $mock): void {
|
||||
$mock->shouldReceive('findUserById')
|
||||
->once()
|
||||
->with(12)
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Tests\Feature;
|
||||
|
||||
use App\Models\BalanceTransaction;
|
||||
use App\Models\GameAccountUser;
|
||||
use App\Services\AzerothCoreAccountService;
|
||||
use App\Services\VMangosAccountService;
|
||||
use App\Services\BalanceService;
|
||||
use Illuminate\Support\Collection;
|
||||
use Inertia\Testing\AssertableInertia as Assert;
|
||||
@@ -40,7 +40,7 @@ class AdminBalancesFeatureTest extends TestCase
|
||||
$admin = $this->makeUser(3);
|
||||
$player = $this->makeUser(0, 7, 'PLAYER');
|
||||
|
||||
$this->mock(AzerothCoreAccountService::class, function (MockInterface $mock) use ($player): void {
|
||||
$this->mock(VMangosAccountService::class, function (MockInterface $mock) use ($player): void {
|
||||
$mock->shouldReceive('findUserByUsername')->once()->with('PLAYER')->andReturn($player);
|
||||
});
|
||||
$this->mock(BalanceService::class, function (MockInterface $mock) use ($admin): void {
|
||||
|
||||
@@ -15,8 +15,8 @@ class BalancePaymentsFeatureTest extends TestCase
|
||||
parent::setUp();
|
||||
|
||||
config([
|
||||
'database.default' => 'azerothcore_auth',
|
||||
'database.connections.azerothcore_auth' => [
|
||||
'database.default' => 'vmangos_auth',
|
||||
'database.connections.vmangos_auth' => [
|
||||
'driver' => 'sqlite',
|
||||
'database' => ':memory:',
|
||||
'prefix' => '',
|
||||
@@ -38,7 +38,7 @@ class BalancePaymentsFeatureTest extends TestCase
|
||||
'services.robokassa.tears_per_ruble' => 10,
|
||||
]);
|
||||
|
||||
DB::purge('azerothcore_auth');
|
||||
DB::purge('vmangos_auth');
|
||||
$migration = require database_path('migrations/2026_07_19_000001_create_moonwell_balance_tables.php');
|
||||
$migration->up();
|
||||
}
|
||||
@@ -60,12 +60,12 @@ class BalancePaymentsFeatureTest extends TestCase
|
||||
|
||||
$this->assertSame('1', $parameters['IsTest']);
|
||||
$this->assertSame('1000.00', app(BalanceService::class)->balance(7));
|
||||
$this->assertDatabaseCount('balance_transactions', 1, 'azerothcore_auth');
|
||||
$this->assertDatabaseCount('balance_transactions', 1, 'vmangos_auth');
|
||||
}
|
||||
|
||||
public function test_game_spending_is_atomic_and_idempotent(): void
|
||||
{
|
||||
DB::connection('azerothcore_auth')->table('account_balances')->insert([
|
||||
DB::connection('vmangos_auth')->table('account_balances')->insert([
|
||||
'account_id' => 7,
|
||||
'balance' => '100.00',
|
||||
'created_at' => now(),
|
||||
@@ -78,7 +78,7 @@ class BalancePaymentsFeatureTest extends TestCase
|
||||
|
||||
$this->assertSame($first->id, $second->id);
|
||||
$this->assertSame('74.50', $balances->balance(7));
|
||||
$this->assertDatabaseCount('balance_transactions', 1, 'azerothcore_auth');
|
||||
$this->assertDatabaseCount('balance_transactions', 1, 'vmangos_auth');
|
||||
}
|
||||
|
||||
public function test_production_mode_uses_production_credentials_and_disables_test_flag(): void
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\GameAccountUser;
|
||||
use App\Services\AzerothCoreAccountService;
|
||||
use App\Services\VMangosAccountService;
|
||||
use App\Services\BalanceService;
|
||||
use App\Services\GameClientDownloadService;
|
||||
use App\Services\StoreAvailabilityService;
|
||||
@@ -34,7 +34,7 @@ class CabinetFeatureTest extends TestCase
|
||||
{
|
||||
$user = $this->makeUser();
|
||||
|
||||
$this->mock(AzerothCoreAccountService::class, function (MockInterface $mock) use ($user): void {
|
||||
$this->mock(VMangosAccountService::class, function (MockInterface $mock) use ($user): void {
|
||||
$mock->shouldReceive('charactersForAccount')
|
||||
->once()
|
||||
->with($user->id)
|
||||
@@ -90,7 +90,7 @@ class CabinetFeatureTest extends TestCase
|
||||
{
|
||||
$user = $this->makeUser();
|
||||
|
||||
$this->mock(AzerothCoreAccountService::class, function (MockInterface $mock) use ($user): void {
|
||||
$this->mock(VMangosAccountService::class, function (MockInterface $mock) use ($user): void {
|
||||
$mock->shouldReceive('validatePassword')
|
||||
->once()
|
||||
->with($user, 'OldPass123')
|
||||
@@ -115,7 +115,7 @@ class CabinetFeatureTest extends TestCase
|
||||
{
|
||||
$user = $this->makeUser();
|
||||
|
||||
$this->mock(AzerothCoreAccountService::class, function (MockInterface $mock) use ($user): void {
|
||||
$this->mock(VMangosAccountService::class, function (MockInterface $mock) use ($user): void {
|
||||
$mock->shouldReceive('validatePassword')
|
||||
->once()
|
||||
->with($user, 'WrongPass123')
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Tests\Feature;
|
||||
|
||||
use App\Exceptions\DuplicateGameAccountException;
|
||||
use App\Exceptions\InviteCodeException;
|
||||
use App\Services\AzerothCoreAccountRegistrar;
|
||||
use App\Services\VMangosAccountRegistrar;
|
||||
use App\Services\InviteCodeService;
|
||||
use Closure;
|
||||
use Mockery\MockInterface;
|
||||
@@ -12,6 +12,13 @@ use Tests\TestCase;
|
||||
|
||||
class GameAccountRegistrationTest extends TestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
config(['moonwell.registration.require_invite_code' => true]);
|
||||
}
|
||||
|
||||
public function test_registration_form_submits_successfully(): void
|
||||
{
|
||||
$this->mock(InviteCodeService::class, function (MockInterface $mock): void {
|
||||
@@ -21,7 +28,7 @@ class GameAccountRegistrationTest extends TestCase
|
||||
->andReturnUsing(fn (string $code, string $username, Closure $callback): array => $callback());
|
||||
});
|
||||
|
||||
$this->mock(AzerothCoreAccountRegistrar::class, function (MockInterface $mock): void {
|
||||
$this->mock(VMangosAccountRegistrar::class, function (MockInterface $mock): void {
|
||||
$mock->shouldReceive('register')
|
||||
->once()
|
||||
->with('playerone', 'player@example.com', 'Pass1234')
|
||||
@@ -63,7 +70,7 @@ class GameAccountRegistrationTest extends TestCase
|
||||
$this->mock(InviteCodeService::class, function (MockInterface $mock): void {
|
||||
$mock->shouldNotReceive('redeemForRegistration');
|
||||
});
|
||||
$this->mock(AzerothCoreAccountRegistrar::class, function (MockInterface $mock): void {
|
||||
$this->mock(VMangosAccountRegistrar::class, function (MockInterface $mock): void {
|
||||
$mock->shouldReceive('register')
|
||||
->once()
|
||||
->with('playerone', 'player@example.com', 'Pass1234')
|
||||
@@ -87,7 +94,7 @@ class GameAccountRegistrationTest extends TestCase
|
||||
->andReturnUsing(fn (string $code, string $username, Closure $callback): array => $callback());
|
||||
});
|
||||
|
||||
$this->mock(AzerothCoreAccountRegistrar::class, function (MockInterface $mock): void {
|
||||
$this->mock(VMangosAccountRegistrar::class, function (MockInterface $mock): void {
|
||||
$mock->shouldReceive('register')
|
||||
->once()
|
||||
->andThrow(DuplicateGameAccountException::forUsername());
|
||||
|
||||
@@ -13,7 +13,7 @@ class LandingPageTest extends TestCase
|
||||
|
||||
$response->assertOk();
|
||||
$response->assertSee('<title>MoonWell — Лунный колодец</title>', false);
|
||||
$response->assertSee('<meta name="description" content="Пройди историю Азерота — от Классики до Короля-лича — в своём темпе, в одиночку или с небольшим отрядом.">', false);
|
||||
$response->assertSee('<meta name="description" content="Вернись в классический Азерот — в одиночку или с небольшим отрядом.">', false);
|
||||
$response->assertInertia(fn (Assert $page) => $page
|
||||
->component('Landing/Main', false)
|
||||
->where('realm.server_name', 'MoonWell')
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\GameAccount;
|
||||
use App\Services\AzerothCoreSrpService;
|
||||
use App\Services\VMangosSrpService;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
@@ -11,115 +11,63 @@ use Tests\TestCase;
|
||||
|
||||
class LauncherGameTicketFeatureTest extends TestCase
|
||||
{
|
||||
private const string SESSION_TOKEN = 'launcher-session-access-token';
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
config(['auth.guards.api' => [
|
||||
'driver' => 'session',
|
||||
'provider' => 'game_accounts_eloquent',
|
||||
]]);
|
||||
config([
|
||||
'app.key' => 'base64:'.base64_encode(str_repeat('m', 32)),
|
||||
'auth.guards.api' => [
|
||||
'driver' => 'session',
|
||||
'provider' => 'game_accounts_eloquent',
|
||||
],
|
||||
'moonwell.auth_connection' => 'azerothcore_auth',
|
||||
'moonwell.launcher.game_ticket_ttl' => 60,
|
||||
'moonwell.launcher.client_build' => 12340,
|
||||
'database.connections.azerothcore_auth' => [
|
||||
'driver' => 'sqlite',
|
||||
'database' => ':memory:',
|
||||
'prefix' => '',
|
||||
],
|
||||
'moonwell.auth_connection' => 'vmangos_auth',
|
||||
'database.connections.vmangos_auth' => ['driver' => 'sqlite', 'database' => ':memory:', 'prefix' => ''],
|
||||
]);
|
||||
DB::purge('azerothcore_auth');
|
||||
|
||||
Schema::connection('azerothcore_auth')->create('launcher_ticket', function (Blueprint $table): void {
|
||||
DB::purge('vmangos_auth');
|
||||
Schema::connection('vmangos_auth')->create('launcher_ticket', function (Blueprint $table): void {
|
||||
$table->unsignedInteger('account_id')->primary();
|
||||
$table->binary('generation_id')->unique();
|
||||
$table->binary('generation_id');
|
||||
$table->binary('srp_salt');
|
||||
$table->binary('srp_verifier');
|
||||
$table->unsignedSmallInteger('client_build');
|
||||
$table->binary('launcher_session_hash')->nullable();
|
||||
$table->timestamp('issued_at');
|
||||
$table->timestamp('expires_at');
|
||||
$table->binary('launcher_session_hash');
|
||||
$table->dateTime('issued_at');
|
||||
$table->dateTime('expires_at');
|
||||
$table->binary('claim_nonce')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
public function test_authenticated_launcher_receives_ticket_and_database_only_stores_srp_data(): void
|
||||
{
|
||||
$account = $this->authenticate(7, 'PlayerOne');
|
||||
|
||||
$response = $this->postJson('/api/launcher/game-ticket', ['client_build' => 12340], [
|
||||
'Authorization' => 'Bearer '.self::SESSION_TOKEN,
|
||||
])->assertOk()->assertHeader('Cache-Control', 'no-store, private');
|
||||
$ticket = $response->json('ticket');
|
||||
|
||||
$response->assertJsonPath('account', 'PLAYERONE');
|
||||
$this->assertMatchesRegularExpression('/^[A-Z0-9]{16}$/', $ticket);
|
||||
$this->assertNotNull($response->json('expires_at'));
|
||||
|
||||
$row = DB::connection('azerothcore_auth')->table('launcher_ticket')->where('account_id', $account->id)->first();
|
||||
$this->assertNotNull($row);
|
||||
$this->assertSame(16, strlen($row->generation_id));
|
||||
$this->assertSame(32, strlen($row->srp_salt));
|
||||
$this->assertSame(32, strlen($row->srp_verifier));
|
||||
$this->assertSame(12340, $row->client_build);
|
||||
$this->assertSame(hash('sha256', self::SESSION_TOKEN, true), $row->launcher_session_hash);
|
||||
$this->assertTrue(app(AzerothCoreSrpService::class)->credentialsMatch(
|
||||
'PLAYERONE',
|
||||
$ticket,
|
||||
bin2hex($row->srp_salt),
|
||||
bin2hex($row->srp_verifier),
|
||||
));
|
||||
$this->assertStringNotContainsString($ticket, serialize($row));
|
||||
}
|
||||
|
||||
public function test_new_generation_revokes_previous_ticket(): void
|
||||
{
|
||||
$account = $this->authenticate(9, 'DevPlayer');
|
||||
$headers = ['Authorization' => 'Bearer '.self::SESSION_TOKEN];
|
||||
|
||||
$firstTicket = $this->postJson('/api/launcher/game-ticket', ['client_build' => 12340], $headers)
|
||||
->assertOk()->json('ticket');
|
||||
$firstGeneration = DB::connection('azerothcore_auth')->table('launcher_ticket')
|
||||
->where('account_id', $account->id)->value('generation_id');
|
||||
|
||||
$secondTicket = $this->postJson('/api/launcher/game-ticket', ['client_build' => 12340], $headers)
|
||||
->assertOk()->json('ticket');
|
||||
$row = DB::connection('azerothcore_auth')->table('launcher_ticket')
|
||||
->where('account_id', $account->id)->first();
|
||||
$srp = app(AzerothCoreSrpService::class);
|
||||
|
||||
$this->assertNotSame($firstTicket, $secondTicket);
|
||||
$this->assertNotSame($firstGeneration, $row->generation_id);
|
||||
$this->assertFalse($srp->credentialsMatch('DEVPLAYER', $firstTicket, bin2hex($row->srp_salt), bin2hex($row->srp_verifier)));
|
||||
$this->assertTrue($srp->credentialsMatch('DEVPLAYER', $secondTicket, bin2hex($row->srp_salt), bin2hex($row->srp_verifier)));
|
||||
}
|
||||
|
||||
public function test_ticket_endpoint_requires_launcher_authentication(): void
|
||||
{
|
||||
$this->postJson('/api/launcher/game-ticket', ['client_build' => 12340])->assertUnauthorized();
|
||||
}
|
||||
|
||||
public function test_ticket_endpoint_rejects_wrong_client_build(): void
|
||||
{
|
||||
$this->authenticate(10, 'PlayerTwo');
|
||||
|
||||
$this->postJson('/api/launcher/game-ticket', ['client_build' => 99999], [
|
||||
'Authorization' => 'Bearer '.self::SESSION_TOKEN,
|
||||
])->assertUnprocessable();
|
||||
|
||||
$this->assertSame(0, DB::connection('azerothcore_auth')->table('launcher_ticket')->count());
|
||||
}
|
||||
|
||||
private function authenticate(int $id, string $username): GameAccount
|
||||
public function test_game_ticket_is_issued_and_replaces_previous_generation(): void
|
||||
{
|
||||
$account = new GameAccount;
|
||||
$account->id = $id;
|
||||
$account->username = $username;
|
||||
$account->id = 7;
|
||||
$account->username = 'PLAYERONE';
|
||||
$this->actingAs($account, 'api');
|
||||
|
||||
return $account;
|
||||
$response = $this->withToken('launcher-token')->postJson('/api/launcher/game-ticket', ['client_build' => 5875])->assertOk();
|
||||
$first = $response->json('ticket');
|
||||
$this->assertMatchesRegularExpression('/^[A-Z0-9]{16}$/', $first);
|
||||
$this->assertSame('PLAYERONE', $response->json('account'));
|
||||
$row = DB::connection('vmangos_auth')->table('launcher_ticket')->where('account_id', 7)->first();
|
||||
$this->assertTrue(app(VMangosSrpService::class)->credentialsMatch('PLAYERONE', $first, strtoupper(bin2hex($row->srp_salt)), strtoupper(bin2hex($row->srp_verifier))));
|
||||
$generation = $row->generation_id;
|
||||
|
||||
$this->withToken('launcher-token')->postJson('/api/launcher/game-ticket', ['client_build' => 5875])->assertOk();
|
||||
$this->assertSame(1, DB::connection('vmangos_auth')->table('launcher_ticket')->count());
|
||||
$this->assertNotSame($generation, DB::connection('vmangos_auth')->table('launcher_ticket')->value('generation_id'));
|
||||
}
|
||||
|
||||
public function test_ticket_rejects_wrong_build(): void
|
||||
{
|
||||
$account = new GameAccount;
|
||||
$account->id = 7;
|
||||
$account->username = 'PLAYERONE';
|
||||
$this->actingAs($account, 'api');
|
||||
$this->withToken('launcher-token')->postJson('/api/launcher/game-ticket', ['client_build' => 12340])->assertUnprocessable();
|
||||
}
|
||||
|
||||
public function test_game_ticket_still_requires_launcher_authentication(): void
|
||||
{
|
||||
$this->postJson('/api/launcher/game-ticket', ['client_build' => 5875])->assertUnauthorized();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,18 +16,18 @@ class LauncherRealmsFeatureTest extends TestCase
|
||||
parent::setUp();
|
||||
|
||||
config([
|
||||
'moonwell.auth_connection' => 'azerothcore_auth',
|
||||
'moonwell.auth_connection' => 'vmangos_auth',
|
||||
'moonwell.launcher.realm_status_cache' => 0,
|
||||
'moonwell.launcher.realm_status_host' => null,
|
||||
'database.connections.azerothcore_auth' => [
|
||||
'database.connections.vmangos_auth' => [
|
||||
'driver' => 'sqlite',
|
||||
'database' => ':memory:',
|
||||
'prefix' => '',
|
||||
],
|
||||
]);
|
||||
DB::purge('azerothcore_auth');
|
||||
DB::purge('vmangos_auth');
|
||||
|
||||
Schema::connection('azerothcore_auth')->create('realmlist', function (Blueprint $table): void {
|
||||
Schema::connection('vmangos_auth')->create('realmlist', function (Blueprint $table): void {
|
||||
$table->unsignedInteger('id')->primary();
|
||||
$table->string('name');
|
||||
$table->string('address');
|
||||
@@ -36,13 +36,14 @@ class LauncherRealmsFeatureTest extends TestCase
|
||||
$table->unsignedInteger('flag')->default(0);
|
||||
$table->unsignedInteger('timezone')->default(0);
|
||||
$table->float('population')->default(0);
|
||||
$table->unsignedInteger('gamebuild')->default(12340);
|
||||
$table->unsignedInteger('gamebuild_min')->default(5875);
|
||||
$table->unsignedInteger('gamebuild_max')->default(5875);
|
||||
});
|
||||
}
|
||||
|
||||
public function test_launcher_can_get_realms_and_their_status_without_authentication(): void
|
||||
{
|
||||
DB::connection('azerothcore_auth')->table('realmlist')->insert([
|
||||
DB::connection('vmangos_auth')->table('realmlist')->insert([
|
||||
[
|
||||
'id' => 1,
|
||||
'name' => 'MoonWell',
|
||||
@@ -52,7 +53,8 @@ class LauncherRealmsFeatureTest extends TestCase
|
||||
'flag' => 0,
|
||||
'timezone' => 4,
|
||||
'population' => 0.5,
|
||||
'gamebuild' => 12340,
|
||||
'gamebuild_min' => 5875,
|
||||
'gamebuild_max' => 5875,
|
||||
],
|
||||
[
|
||||
'id' => 2,
|
||||
@@ -63,7 +65,8 @@ class LauncherRealmsFeatureTest extends TestCase
|
||||
'flag' => 0,
|
||||
'timezone' => 4,
|
||||
'population' => 0,
|
||||
'gamebuild' => 12340,
|
||||
'gamebuild_min' => 5875,
|
||||
'gamebuild_max' => 5875,
|
||||
],
|
||||
]);
|
||||
$this->mock(RealmStatusProbe::class, function (MockInterface $mock): void {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Exceptions\DuplicateGameAccountException;
|
||||
use App\Services\AzerothCoreAccountRegistrar;
|
||||
use App\Services\VMangosAccountRegistrar;
|
||||
use App\Services\InviteCodeService;
|
||||
use Closure;
|
||||
use Mockery\MockInterface;
|
||||
@@ -26,7 +26,7 @@ class LauncherRegistrationFeatureTest extends TestCase
|
||||
->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 {
|
||||
$this->mock(VMangosAccountRegistrar::class, function (MockInterface $mock): void {
|
||||
$mock->shouldReceive('register')
|
||||
->once()
|
||||
->with('playerone', 'player@example.com', 'Pass1234')
|
||||
@@ -77,7 +77,7 @@ class LauncherRegistrationFeatureTest extends TestCase
|
||||
->once()
|
||||
->andReturnUsing(fn (string $code, string $username, Closure $callback): array => $callback());
|
||||
});
|
||||
$this->mock(AzerothCoreAccountRegistrar::class, function (MockInterface $mock): void {
|
||||
$this->mock(VMangosAccountRegistrar::class, function (MockInterface $mock): void {
|
||||
$mock->shouldReceive('register')
|
||||
->once()
|
||||
->andThrow(DuplicateGameAccountException::forUsername());
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Services\VMangosAccountRegistrar;
|
||||
use App\Services\VMangosAccountService;
|
||||
use App\Services\VMangosSrpService;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Tests\TestCase;
|
||||
|
||||
class VMangosAccountIntegrationTest extends TestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
config([
|
||||
'moonwell.auth_connection' => 'vmangos_auth',
|
||||
'moonwell.characters_connection' => 'vmangos_characters',
|
||||
'moonwell.registration.realm_id' => -1,
|
||||
'moonwell.registration.gmlevel' => 0,
|
||||
'database.connections.vmangos_auth' => ['driver' => 'sqlite', 'database' => ':memory:', 'prefix' => ''],
|
||||
'database.connections.vmangos_characters' => ['driver' => 'sqlite', 'database' => ':memory:', 'prefix' => ''],
|
||||
]);
|
||||
DB::purge('vmangos_auth');
|
||||
DB::purge('vmangos_characters');
|
||||
|
||||
Schema::connection('vmangos_auth')->create('account', function (Blueprint $table): void {
|
||||
$table->increments('id');
|
||||
$table->string('username', 32)->unique();
|
||||
$table->text('s');
|
||||
$table->text('v');
|
||||
$table->unsignedTinyInteger('expansion')->default(0);
|
||||
$table->string('email')->nullable();
|
||||
$table->dateTime('joindate');
|
||||
$table->dateTime('last_login')->nullable();
|
||||
$table->boolean('locked')->default(false);
|
||||
$table->boolean('online')->default(false);
|
||||
$table->string('sessionkey')->nullable();
|
||||
});
|
||||
Schema::connection('vmangos_auth')->create('realmlist', function (Blueprint $table): void {
|
||||
$table->increments('id');
|
||||
});
|
||||
Schema::connection('vmangos_auth')->create('realmcharacters', function (Blueprint $table): void {
|
||||
$table->unsignedInteger('realmid');
|
||||
$table->unsignedInteger('acctid');
|
||||
$table->unsignedTinyInteger('numchars');
|
||||
$table->primary(['realmid', 'acctid']);
|
||||
});
|
||||
Schema::connection('vmangos_auth')->create('account_access', function (Blueprint $table): void {
|
||||
$table->unsignedInteger('id');
|
||||
$table->integer('RealmID');
|
||||
$table->unsignedTinyInteger('gmlevel');
|
||||
$table->primary(['id', 'RealmID']);
|
||||
});
|
||||
Schema::connection('vmangos_auth')->create('site_credentials', function (Blueprint $table): void {
|
||||
$table->unsignedInteger('account_id')->primary();
|
||||
$table->unsignedInteger('legacy_account_id')->nullable()->unique();
|
||||
$table->char('salt_hex', 64);
|
||||
$table->char('verifier_hex', 64);
|
||||
$table->timestamps();
|
||||
});
|
||||
Schema::connection('vmangos_characters')->create('characters', function (Blueprint $table): void {
|
||||
$table->unsignedInteger('guid')->primary();
|
||||
$table->unsignedInteger('account');
|
||||
$table->string('name');
|
||||
$table->unsignedTinyInteger('race');
|
||||
$table->unsignedTinyInteger('class');
|
||||
$table->unsignedTinyInteger('gender');
|
||||
$table->unsignedTinyInteger('level');
|
||||
$table->boolean('online');
|
||||
$table->unsignedInteger('money');
|
||||
$table->unsignedInteger('played_time_total');
|
||||
});
|
||||
}
|
||||
|
||||
public function test_srp_matches_vmangos_core_control_vector(): void
|
||||
{
|
||||
$srp = app(VMangosSrpService::class);
|
||||
$salt = 'D04E6342FE6CB5FAE54C6182F885778D0AEFE4BFCDDC6B5C7DF7DC25FF6E3C2D';
|
||||
$verifier = '46FD48476D925C4422DD1761C299C79FD6C92B5243E4F3C6C62576C0DABD8260';
|
||||
|
||||
$this->assertSame($verifier, $srp->makeVerifierHex('VMANGOS', '4EVER', $salt));
|
||||
$this->assertTrue($srp->credentialsMatch('vmangos', '4ever', $salt, $verifier));
|
||||
$this->assertFalse($srp->credentialsMatch('vmangos', 'wrong', $salt, $verifier));
|
||||
}
|
||||
|
||||
public function test_registration_login_password_access_and_characters_use_vmangos_schema(): void
|
||||
{
|
||||
DB::connection('vmangos_auth')->table('realmlist')->insert([['id' => 1], ['id' => 2]]);
|
||||
$registered = app(VMangosAccountRegistrar::class)->register('PlayerOne', 'player@example.com', 'Pass1234');
|
||||
$id = $registered['id'];
|
||||
$this->assertSame('PLAYERONE', $registered['username']);
|
||||
$this->assertDatabaseHas('account', ['id' => $id, 'username' => 'PLAYERONE', 'expansion' => 0], 'vmangos_auth');
|
||||
$this->assertSame(2, DB::connection('vmangos_auth')->table('realmcharacters')->where('acctid', $id)->count());
|
||||
$this->assertDatabaseHas('account_access', ['id' => $id, 'RealmID' => -1, 'gmlevel' => 0], 'vmangos_auth');
|
||||
$gameCredential = DB::connection('vmangos_auth')->table('account')->where('id', $id)->first();
|
||||
$this->assertFalse(app(VMangosSrpService::class)->credentialsMatch('PLAYERONE', 'Pass1234', $gameCredential->s, $gameCredential->v));
|
||||
|
||||
$accounts = app(VMangosAccountService::class);
|
||||
$user = $accounts->findUserByUsername('playerone');
|
||||
$this->assertNotNull($user);
|
||||
$this->assertTrue($accounts->validatePassword($user, 'Pass1234'));
|
||||
$this->assertFalse($accounts->validatePassword($user, 'Wrong1234'));
|
||||
|
||||
DB::connection('vmangos_auth')->table('account')->where('id', $id)->update(['last_login' => '0000-00-00 00:00:00']);
|
||||
$this->assertSame('—', $accounts->findUserById($id)->lastLoginLabel());
|
||||
|
||||
$accounts->updateAccessLevel($id, 3);
|
||||
$this->assertSame(3, $accounts->findUserById($id)->gmLevel);
|
||||
|
||||
DB::connection('vmangos_characters')->table('characters')->insert([
|
||||
'guid' => 11, 'account' => $id, 'name' => 'Hero', 'race' => 1, 'class' => 1,
|
||||
'gender' => 0, 'level' => 60, 'online' => 1, 'money' => 12345, 'played_time_total' => 7200,
|
||||
]);
|
||||
$character = $accounts->charactersForAccount($id)->first();
|
||||
$this->assertSame('Hero', $character['name']);
|
||||
$this->assertSame('2ч 0м', $character['played']);
|
||||
|
||||
$accounts->updatePassword($user, 'NewPass123');
|
||||
$fresh = $accounts->findUserById($id);
|
||||
$this->assertFalse($accounts->validatePassword($fresh, 'Pass1234'));
|
||||
$this->assertTrue($accounts->validatePassword($fresh, 'NewPass123'));
|
||||
$this->assertSame($gameCredential->v, DB::connection('vmangos_auth')->table('account')->where('id', $id)->value('v'));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user