[ 'driver' => 'session', 'provider' => 'game_accounts_eloquent', ]]); config([ 'moonwell.auth_connection' => 'vmangos_auth', 'database.connections.vmangos_auth' => ['driver' => 'sqlite', 'database' => ':memory:', 'prefix' => ''], ]); DB::purge('vmangos_auth'); Schema::connection('vmangos_auth')->create('launcher_ticket', function (Blueprint $table): void { $table->unsignedInteger('account_id')->primary(); $table->binary('generation_id'); $table->binary('srp_salt'); $table->binary('srp_verifier'); $table->unsignedSmallInteger('client_build'); $table->binary('launcher_session_hash'); $table->dateTime('issued_at'); $table->dateTime('expires_at'); $table->binary('claim_nonce')->nullable(); }); } public function test_game_ticket_is_issued_and_replaces_previous_generation(): void { config(['moonwell.timezone' => 'Europe/Samara']); $this->travelTo(Carbon::parse('2026-10-04 12:00:00', 'UTC')); $account = new GameAccount; $account->id = 7; $account->username = 'PLAYERONE'; $this->actingAs($account, 'api'); $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->assertSame('2026-10-04 12:00:00.000000', $row->issued_at); $this->assertSame('2026-10-04 12:01:00.000000', $row->expires_at); $this->assertSame('2026-10-04T12:01:00Z', $response->json('expires_at')); $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(); } }