- Add two airports (north/south) with full geometry: runways, taxiways,
aprons, terminals, hangars, stands, and helipads; walkable airport
grounds extend beyond city bounds.
- Introduce ATC AI agent ("Torre de Controle City10") using AI_ATC_MODEL
to generate takeoff clearances; includes dispatch endpoint with
30-second cooldown and deterministic fallback on failure.
- Implement AI model fallback chain: preferred model → AI_FALLBACK_MODEL
before failing, applied in ChatController and PersonTalkController.
- Add luminous sign labels (letreiros) for plazas, beach, food, hospital,
school, city hall, and airports with day/night glow.
- Fix city presence cache: compute is_me per viewer instead of baking
into shared per-second cache to prevent cross-user flag leakage.
- Add tests: AirportTest, CityPresenceTest, SignLabelsTest, and
PersonTalkTest fallback model retry scenario.
77 lines
2.1 KiB
PHP
77 lines
2.1 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\User;
|
|
use Database\Seeders\CityWorldSeeder;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Tests\TestCase;
|
|
|
|
/**
|
|
* Luminous-sign labels must exist in every locale (RULES.md 1.4:
|
|
* every user-visible string ships in pt_BR, en and es).
|
|
*/
|
|
class SignLabelsTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
/**
|
|
* Locales covered by the platform.
|
|
*
|
|
* @var list<string>
|
|
*/
|
|
private const LOCALES = ['pt_BR', 'en', 'es'];
|
|
|
|
/**
|
|
* Sign label keys rendered by the 3D letreiros.
|
|
*
|
|
* @var list<string>
|
|
*/
|
|
private const KEYS = [
|
|
'city.sign_plaza',
|
|
'city.sign_beach',
|
|
'city.sign_food',
|
|
'city.sign_hospital',
|
|
'city.sign_school',
|
|
'city.sign_cityhall',
|
|
];
|
|
|
|
/**
|
|
* Every sign label exists (non-empty) in all three languages.
|
|
*/
|
|
public function test_sign_labels_exist_in_all_locales(): void
|
|
{
|
|
foreach (self::LOCALES as $locale) {
|
|
$path = base_path("lang/{$locale}.json");
|
|
$this->assertFileExists($path);
|
|
|
|
$labels = json_decode((string) file_get_contents($path), true);
|
|
$this->assertIsArray($labels);
|
|
|
|
foreach (self::KEYS as $key) {
|
|
$this->assertArrayHasKey($key, $labels, "Missing {$key} in {$locale}");
|
|
$this->assertNotSame('', trim((string) $labels[$key]), "Empty {$key} in {$locale}");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The labels reach the viewer page for the active locale.
|
|
*/
|
|
public function test_viewer_page_carries_sign_labels(): void
|
|
{
|
|
$this->seed(CityWorldSeeder::class);
|
|
$user = User::factory()->create([
|
|
'status' => User::STATUS_ACTIVE,
|
|
'is_active' => true,
|
|
]);
|
|
$this->actingAs($user);
|
|
|
|
$html = $this->get('/city')->assertOk()->getContent();
|
|
|
|
foreach (['signPlaza', 'signBeach', 'signFood', 'signHospital', 'signSchool', 'signCityhall'] as $jsKey) {
|
|
$this->assertStringContainsString($jsKey.':', $html, "Missing i18n entry {$jsKey}");
|
|
}
|
|
}
|
|
}
|