seed(CityWorldSeeder::class); $user = User::factory()->create(['status' => User::STATUS_ACTIVE, 'is_active' => true]); Character::ensureForUser($user); $this->actingAs($user); } /** * The payload carries the five worker robots; their AI personas * (model/prompt) live only in the database — never in the public * world payload. */ public function test_payload_contains_worker_robots(): void { $payload = $this->getJson('/city/data')->assertOk()->json(); $robots = array_values(array_filter( $payload['agents'], static fn (array $a): bool => $a['type'] === 'worker_robot' )); $this->assertCount(5, $robots); $this->assertSame( ['Robô TR-01', 'Robô TR-02', 'Robô TR-03', 'Robô TR-04', 'Robô TR-05'], array_column($robots, 'name') ); foreach ($robots as $robot) { $this->assertSame('active', $robot['status']); } // Personas are stored server-side only. $this->assertArrayNotHasKey('model', $robots[0]); $this->assertArrayNotHasKey('system_prompt', $robots[0]); foreach (AiAgent::where('type', 'worker_robot')->get() as $agent) { $this->assertContains($agent->model, config('ai.allowed_models')); $this->assertNotEmpty($agent->system_prompt); $this->assertStringContainsString('robô', $agent->system_prompt); } } /** * Robot spawn points sit inside the city bounds (they seed the DB * row; the viewer re-places them on walking routes). */ public function test_robot_positions_stay_inside_city_bounds(): void { $payload = $this->getJson('/city/data')->assertOk()->json(); $bounds = $payload['city']['bounds']; $robots = array_values(array_filter( $payload['agents'], static fn (array $a): bool => $a['type'] === 'worker_robot' )); $this->assertNotEmpty($robots); foreach ($robots as $robot) { $this->assertGreaterThanOrEqual($bounds['min_x'], $robot['position'][0]); $this->assertLessThanOrEqual($bounds['max_x'], $robot['position'][0]); $this->assertGreaterThanOrEqual($bounds['min_z'], $robot['position'][2]); $this->assertLessThanOrEqual($bounds['max_z'], $robot['position'][2]); } } }