- Adds Character model with survival needs (hunger, thirst, energy) and state machine - Implements city:tick world ticker for offline progression every minute - Adds city territory expansion and building construction system - Introduces job economy, inventory, and transaction audit trail - Adds social graph (friends, marriage, children) and character chat - Implements admin approval workflow for new registrations - Adds hospital admission system for traffic accidents - Includes comprehensive feature tests and world seeders
168 lines
5.0 KiB
PHP
Executable File
168 lines
5.0 KiB
PHP
Executable File
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\Character;
|
|
use App\Models\City;
|
|
use App\Models\LegalDocument;
|
|
use App\Models\User;
|
|
use App\Models\UserConsent;
|
|
use Database\Seeders\CityWorldSeeder;
|
|
use Database\Seeders\LegalDocumentSeeder;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Tests\TestCase;
|
|
|
|
/**
|
|
* Registration approval workflow for citizen accounts.
|
|
*/
|
|
class CitizenApprovalTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
/**
|
|
* Boot legal docs + world for each test.
|
|
*/
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
$this->seed(LegalDocumentSeeder::class);
|
|
$this->seed(CityWorldSeeder::class);
|
|
}
|
|
|
|
/**
|
|
* Create an admin that already accepted all current legal documents.
|
|
*/
|
|
private function makeAdmin(): User
|
|
{
|
|
$admin = User::factory()->admin()->create();
|
|
foreach (LegalDocument::where('is_current', true)->pluck('id') as $docId) {
|
|
UserConsent::create([
|
|
'user_id' => $admin->id,
|
|
'legal_document_id' => $docId,
|
|
'accepted_at' => now(),
|
|
'ip_address' => '127.0.0.1',
|
|
'user_agent' => 'phpunit',
|
|
'source' => 'web_form',
|
|
]);
|
|
}
|
|
|
|
return $admin;
|
|
}
|
|
|
|
/**
|
|
* New registrations must start as pending, not active.
|
|
*/
|
|
public function test_register_creates_pending_user(): void
|
|
{
|
|
$response = $this->post('/register', [
|
|
'name' => 'Nova Pessoa',
|
|
'email' => 'nova@example.com',
|
|
'password' => 'Secret123!',
|
|
'password_confirmation' => 'Secret123!',
|
|
'locale' => 'pt_BR',
|
|
'captcha' => $this->captchaForRegister(),
|
|
]);
|
|
|
|
$user = User::where('email', 'nova@example.com')->first();
|
|
$this->assertNotNull($user);
|
|
$this->assertSame(User::STATUS_PENDING, $user->status);
|
|
$this->assertFalse($user->is_active);
|
|
$response->assertRedirect(route('terms.accept'));
|
|
}
|
|
|
|
/**
|
|
* Pending users are sent to the approval wait screen from the dashboard.
|
|
*/
|
|
public function test_pending_user_sees_wait_screen(): void
|
|
{
|
|
$user = User::factory()->pending()->create();
|
|
|
|
$this->actingAs($user)
|
|
->get('/dashboard')
|
|
->assertRedirect(route('account.pending'));
|
|
|
|
$this->actingAs($user)
|
|
->get(route('account.pending'))
|
|
->assertOk()
|
|
->assertSee(__('auth.pending_title'), false);
|
|
}
|
|
|
|
/**
|
|
* Login attempt by a pending account shows the approval message.
|
|
*/
|
|
public function test_login_rejects_pending_with_message(): void
|
|
{
|
|
$user = User::factory()->pending()->create(['password' => 'Secret123!']);
|
|
|
|
$response = $this->post('/login', [
|
|
'email' => $user->email,
|
|
'password' => 'Secret123!',
|
|
'captcha' => $this->captchaForLogin(),
|
|
]);
|
|
|
|
$response->assertSessionHasErrors('email');
|
|
$this->assertSame(__('auth.pending'), session('errors')->first('email'));
|
|
}
|
|
|
|
/**
|
|
* Admin approval activates the account and creates the character.
|
|
*/
|
|
public function test_admin_approval_creates_character(): void
|
|
{
|
|
$admin = $this->makeAdmin();
|
|
$pending = User::factory()->pending()->create(['name' => 'Ana Silva']);
|
|
|
|
$this->actingAs($admin)
|
|
->post(route('admin.citizens.approve', $pending))
|
|
->assertRedirect(route('admin.citizens.index'));
|
|
|
|
$pending->refresh();
|
|
$this->assertSame(User::STATUS_ACTIVE, $pending->status);
|
|
$this->assertTrue($pending->is_active);
|
|
|
|
$character = Character::query()->where('user_id', $pending->id)->first();
|
|
$this->assertNotNull($character);
|
|
$this->assertSame('Ana', $character->name);
|
|
$this->assertNotNull(City::active());
|
|
$this->assertSame(City::active()->id, $character->city_id);
|
|
}
|
|
|
|
/**
|
|
* Banned accounts cannot authenticate.
|
|
*/
|
|
public function test_banned_user_cannot_login(): void
|
|
{
|
|
$user = User::factory()->inactive()->create(['password' => 'Secret123!']);
|
|
|
|
$response = $this->post('/login', [
|
|
'email' => $user->email,
|
|
'password' => 'Secret123!',
|
|
'captcha' => $this->captchaForLogin(),
|
|
]);
|
|
|
|
$response->assertSessionHasErrors('email');
|
|
}
|
|
|
|
/**
|
|
* Solve the session math CAPTCHA for a register request.
|
|
*/
|
|
private function captchaForRegister(): int
|
|
{
|
|
// Prime the session by loading the form first in real usage; in tests
|
|
// we set the session keys directly to avoid an extra round-trip.
|
|
$a = random_int(1, 9);
|
|
$b = random_int(1, 9);
|
|
session(['captcha_result' => $a + $b, 'captcha_expires_at' => now()->addMinutes(10)->getTimestamp()]);
|
|
|
|
return $a + $b;
|
|
}
|
|
|
|
/**
|
|
* Solve the session math CAPTCHA for a login request.
|
|
*/
|
|
private function captchaForLogin(): int
|
|
{
|
|
return $this->captchaForRegister();
|
|
}
|
|
}
|