Files
chatroom/tests/Feature/RideControllerTest.php
T

279 lines
9.2 KiB
PHP
Raw Normal View History

2026-04-30 09:40:50 +08:00
<?php
/**
* 文件功能:聊天室座驾接口功能测试。
*
* 覆盖座驾列表、购买、续期、替换、余额不足和后台配置保存。
*/
namespace Tests\Feature;
2026-04-30 10:02:59 +08:00
use App\Enums\CurrencySource;
2026-04-30 09:55:20 +08:00
use App\Models\Ride;
2026-04-30 09:40:50 +08:00
use App\Models\Room;
use App\Models\User;
2026-04-30 09:55:20 +08:00
use App\Models\UserRidePurchase;
2026-04-30 09:40:50 +08:00
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Redis;
use Tests\TestCase;
/**
* 座驾控制器功能测试
* 验证前台座驾购买接口与后台商品配置的核心行为。
*/
class RideControllerTest extends TestCase
{
use RefreshDatabase;
/**
* 每个测试前清空 Redis,避免房间在线状态串扰。
*/
protected function setUp(): void
{
parent::setUp();
2026-04-30 09:58:18 +08:00
$this->flushChatRoomRedisState();
2026-04-30 09:40:50 +08:00
}
/**
* 测试座驾列表返回上架座驾、当前座驾和购买记录。
*/
public function test_items_returns_ride_items_and_purchase_state(): void
{
$user = User::factory()->create();
$ride = $this->createRide(['name' => '歼-35测试座驾', 'slug' => 'ride_j35']);
2026-04-30 09:55:20 +08:00
UserRidePurchase::create([
2026-04-30 09:40:50 +08:00
'user_id' => $user->id,
2026-04-30 09:55:20 +08:00
'ride_id' => $ride->id,
2026-04-30 09:40:50 +08:00
'status' => 'active',
'price_paid' => 18888,
'expires_at' => now()->addDays(3),
]);
$response = $this->actingAs($user)->getJson(route('rides.items'));
$response->assertOk()
->assertJsonFragment(['slug' => 'ride_j35'])
->assertJsonPath('current_ride.item.slug', 'ride_j35')
->assertJsonPath('purchases.0.item.slug', 'ride_j35');
}
/**
* 测试金币不足时不能购买座驾。
*/
public function test_cannot_buy_ride_when_balance_is_not_enough(): void
{
$user = User::factory()->create(['jjb' => 100]);
$room = Room::create(['room_name' => '座驾余额房']);
$this->joinRoom($user, $room);
$ride = $this->createRide(['price' => 18888]);
$response = $this->actingAs($user)->postJson(route('rides.buy'), [
'item_id' => $ride->id,
'room_id' => $room->id,
]);
$response->assertStatus(400)
->assertJsonPath('status', 'error');
$this->assertSame(100, (int) $user->fresh()->jjb);
2026-04-30 09:55:20 +08:00
$this->assertDatabaseMissing('user_ride_purchases', [
2026-04-30 09:40:50 +08:00
'user_id' => $user->id,
2026-04-30 09:55:20 +08:00
'ride_id' => $ride->id,
2026-04-30 09:40:50 +08:00
]);
2026-04-30 10:02:59 +08:00
$this->assertDatabaseMissing('user_currency_logs', [
'user_id' => $user->id,
'source' => CurrencySource::RIDE_BUY->value,
]);
2026-04-30 09:40:50 +08:00
}
/**
* 测试购买座驾会扣金币并生成当前激活记录。
*/
public function test_can_buy_ride_and_activate_it(): void
{
$user = User::factory()->create(['jjb' => 30000]);
$room = Room::create(['room_name' => '座驾购买房']);
$this->joinRoom($user, $room);
$ride = $this->createRide(['slug' => 'ride_99a', 'price' => 18888, 'duration_days' => 7]);
$response = $this->actingAs($user)->postJson(route('rides.buy'), [
'item_id' => $ride->id,
'room_id' => $room->id,
]);
$response->assertOk()
->assertJsonPath('status', 'success')
->assertJsonPath('current_ride.item.slug', 'ride_99a')
->assertJsonPath('jjb', 11112);
2026-04-30 09:55:20 +08:00
$this->assertDatabaseHas('user_ride_purchases', [
2026-04-30 09:40:50 +08:00
'user_id' => $user->id,
2026-04-30 09:55:20 +08:00
'ride_id' => $ride->id,
2026-04-30 09:40:50 +08:00
'status' => 'active',
'price_paid' => 18888,
]);
2026-04-30 10:02:59 +08:00
$this->assertDatabaseHas('user_currency_logs', [
'user_id' => $user->id,
'currency' => 'gold',
'amount' => -18888,
'balance_after' => 11112,
'source' => CurrencySource::RIDE_BUY->value,
'remark' => '购买聊天室座驾:测试座驾',
'room_id' => $room->id,
]);
2026-04-30 09:40:50 +08:00
}
/**
* 测试同款座驾续购会叠加有效期且保持一条 active 当前座驾。
*/
public function test_buying_same_ride_extends_active_purchase(): void
{
$user = User::factory()->create(['jjb' => 50000]);
$room = Room::create(['room_name' => '座驾续费房']);
$this->joinRoom($user, $room);
$ride = $this->createRide(['price' => 1000, 'duration_days' => 7]);
2026-04-30 09:55:20 +08:00
$purchase = UserRidePurchase::create([
2026-04-30 09:40:50 +08:00
'user_id' => $user->id,
2026-04-30 09:55:20 +08:00
'ride_id' => $ride->id,
2026-04-30 09:40:50 +08:00
'status' => 'active',
'price_paid' => 1000,
'expires_at' => now()->addDays(2),
]);
$response = $this->actingAs($user)->postJson(route('rides.buy'), [
'item_id' => $ride->id,
'room_id' => $room->id,
]);
$response->assertOk();
$this->assertSame('cancelled', $purchase->fresh()->status);
2026-04-30 09:55:20 +08:00
$activePurchase = UserRidePurchase::query()
2026-04-30 09:40:50 +08:00
->where('user_id', $user->id)
2026-04-30 09:55:20 +08:00
->where('ride_id', $ride->id)
2026-04-30 09:40:50 +08:00
->where('status', 'active')
->firstOrFail();
$this->assertSame(1000, (int) $activePurchase->price_paid);
$this->assertTrue($activePurchase->expires_at->greaterThan(now()->addDays(8)));
2026-04-30 09:55:20 +08:00
$this->assertSame(1, UserRidePurchase::query()->where('user_id', $user->id)->where('status', 'active')->count());
2026-04-30 09:40:50 +08:00
}
/**
* 测试购买不同座驾会取消旧座驾并激活新座驾。
*/
public function test_buying_different_ride_cancels_previous_active_ride(): void
{
$user = User::factory()->create(['jjb' => 50000]);
$room = Room::create(['room_name' => '座驾替换房']);
$this->joinRoom($user, $room);
$oldRide = $this->createRide(['slug' => 'ride_j35', 'price' => 1000]);
$newRide = $this->createRide(['slug' => 'ride_df5c', 'price' => 2000]);
2026-04-30 09:55:20 +08:00
$oldPurchase = UserRidePurchase::create([
2026-04-30 09:40:50 +08:00
'user_id' => $user->id,
2026-04-30 09:55:20 +08:00
'ride_id' => $oldRide->id,
2026-04-30 09:40:50 +08:00
'status' => 'active',
'price_paid' => 1000,
'expires_at' => now()->addDays(3),
]);
$response = $this->actingAs($user)->postJson(route('rides.buy'), [
'item_id' => $newRide->id,
'room_id' => $room->id,
]);
$response->assertOk()
->assertJsonPath('current_ride.item.slug', 'ride_df5c');
$this->assertSame('cancelled', $oldPurchase->fresh()->status);
2026-04-30 09:55:20 +08:00
$this->assertDatabaseHas('user_ride_purchases', [
2026-04-30 09:40:50 +08:00
'user_id' => $user->id,
2026-04-30 09:55:20 +08:00
'ride_id' => $newRide->id,
2026-04-30 09:40:50 +08:00
'status' => 'active',
]);
}
/**
2026-04-30 09:55:20 +08:00
* 测试后台座驾独立模块可以保存欢迎语字段。
2026-04-30 09:40:50 +08:00
*/
public function test_admin_can_store_ride_with_welcome_message(): void
{
$admin = User::factory()->create(['id' => 1, 'user_level' => 100]);
2026-04-30 09:55:20 +08:00
$response = $this->actingAs($admin)->post(route('admin.rides.store'), [
2026-04-30 09:40:50 +08:00
'name' => '测试座驾',
'slug' => 'ride_test',
2026-04-30 09:55:20 +08:00
'effect_key' => 'test',
2026-04-30 09:40:50 +08:00
'icon' => '🚘',
'description' => '测试座驾说明',
'price' => 12345,
'duration_days' => 7,
'welcome_message' => '【{name}】驾驶【{ride}】入场',
'sort_order' => 99,
'is_active' => 1,
]);
2026-04-30 09:55:20 +08:00
$response->assertRedirect(route('admin.rides.index'));
$this->assertDatabaseHas('rides', [
2026-04-30 09:40:50 +08:00
'slug' => 'ride_test',
2026-04-30 09:55:20 +08:00
'effect_key' => 'test',
2026-04-30 09:40:50 +08:00
'welcome_message' => '【{name}】驾驶【{ride}】入场',
]);
}
/**
2026-04-30 09:55:20 +08:00
* 测试后台座驾管理页面可以显示独立座驾数据。
*/
public function test_admin_can_view_ride_management_page(): void
{
$admin = User::factory()->create(['id' => 1, 'user_level' => 100]);
$ride = $this->createRide(['name' => '后台可见座驾', 'slug' => 'ride_admin_visible']);
$response = $this->actingAs($admin)->get(route('admin.rides.index'));
$response->assertOk()
->assertSee('座驾管理')
->assertSee($ride->name)
->assertSee('ride_admin_visible');
}
/**
* 创建测试用独立座驾。
2026-04-30 09:40:50 +08:00
*
* @param array<string, mixed> $attributes 覆盖字段
*/
2026-04-30 09:55:20 +08:00
private function createRide(array $attributes = []): Ride
2026-04-30 09:40:50 +08:00
{
$data = array_merge([
'name' => '测试座驾',
'slug' => 'ride_test_'.str()->random(8),
2026-04-30 09:55:20 +08:00
'effect_key' => 'test_'.str()->random(8),
2026-04-30 09:40:50 +08:00
'description' => '测试座驾说明',
'icon' => '🚘',
'price' => 1000,
'duration_days' => 7,
'sort_order' => 80,
'is_active' => true,
'welcome_message' => '【{name}】驾驶【{ride}】入场',
], $attributes);
2026-04-30 09:55:20 +08:00
return Ride::query()->updateOrCreate(
2026-04-30 09:40:50 +08:00
['slug' => $data['slug']],
$data,
);
}
/**
* 将测试用户写入指定房间在线表,模拟已经进入聊天室的状态。
*/
private function joinRoom(User $user, Room $room): void
{
Redis::hset("room:{$room->id}:users", $user->username, json_encode([
'id' => $user->id,
'username' => $user->username,
], JSON_UNESCAPED_UNICODE));
}
}