253 lines
8.2 KiB
PHP
253 lines
8.2 KiB
PHP
<?php
|
|
|
|
/**
|
|
* 文件功能:聊天室座驾接口功能测试。
|
|
*
|
|
* 覆盖座驾列表、购买、续期、替换、余额不足和后台配置保存。
|
|
*/
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Models\Room;
|
|
use App\Models\ShopItem;
|
|
use App\Models\User;
|
|
use App\Models\UserPurchase;
|
|
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();
|
|
Redis::flushall();
|
|
}
|
|
|
|
/**
|
|
* 测试座驾列表返回上架座驾、当前座驾和购买记录。
|
|
*/
|
|
public function test_items_returns_ride_items_and_purchase_state(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$ride = $this->createRide(['name' => '歼-35测试座驾', 'slug' => 'ride_j35']);
|
|
|
|
UserPurchase::create([
|
|
'user_id' => $user->id,
|
|
'shop_item_id' => $ride->id,
|
|
'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);
|
|
$this->assertDatabaseMissing('user_purchases', [
|
|
'user_id' => $user->id,
|
|
'shop_item_id' => $ride->id,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 测试购买座驾会扣金币并生成当前激活记录。
|
|
*/
|
|
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);
|
|
|
|
$this->assertDatabaseHas('user_purchases', [
|
|
'user_id' => $user->id,
|
|
'shop_item_id' => $ride->id,
|
|
'status' => 'active',
|
|
'price_paid' => 18888,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 测试同款座驾续购会叠加有效期且保持一条 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]);
|
|
|
|
$purchase = UserPurchase::create([
|
|
'user_id' => $user->id,
|
|
'shop_item_id' => $ride->id,
|
|
'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);
|
|
$activePurchase = UserPurchase::query()
|
|
->where('user_id', $user->id)
|
|
->where('shop_item_id', $ride->id)
|
|
->where('status', 'active')
|
|
->firstOrFail();
|
|
$this->assertSame(1000, (int) $activePurchase->price_paid);
|
|
$this->assertTrue($activePurchase->expires_at->greaterThan(now()->addDays(8)));
|
|
$this->assertSame(1, UserPurchase::query()->where('user_id', $user->id)->where('status', 'active')->count());
|
|
}
|
|
|
|
/**
|
|
* 测试购买不同座驾会取消旧座驾并激活新座驾。
|
|
*/
|
|
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]);
|
|
|
|
$oldPurchase = UserPurchase::create([
|
|
'user_id' => $user->id,
|
|
'shop_item_id' => $oldRide->id,
|
|
'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);
|
|
$this->assertDatabaseHas('user_purchases', [
|
|
'user_id' => $user->id,
|
|
'shop_item_id' => $newRide->id,
|
|
'status' => 'active',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 测试后台商店可以保存座驾类型和欢迎语字段。
|
|
*/
|
|
public function test_admin_can_store_ride_with_welcome_message(): void
|
|
{
|
|
$admin = User::factory()->create(['id' => 1, 'user_level' => 100]);
|
|
|
|
$response = $this->actingAs($admin)->post(route('admin.shop.store'), [
|
|
'name' => '测试座驾',
|
|
'slug' => 'ride_test',
|
|
'icon' => '🚘',
|
|
'description' => '测试座驾说明',
|
|
'price' => 12345,
|
|
'type' => 'ride',
|
|
'duration_days' => 7,
|
|
'duration_minutes' => 0,
|
|
'intimacy_bonus' => 0,
|
|
'charm_bonus' => 0,
|
|
'welcome_message' => '【{name}】驾驶【{ride}】入场',
|
|
'sort_order' => 99,
|
|
'is_active' => 1,
|
|
]);
|
|
|
|
$response->assertRedirect(route('admin.shop.index'));
|
|
$this->assertDatabaseHas('shop_items', [
|
|
'slug' => 'ride_test',
|
|
'type' => 'ride',
|
|
'welcome_message' => '【{name}】驾驶【{ride}】入场',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* 创建测试用座驾商品。
|
|
*
|
|
* @param array<string, mixed> $attributes 覆盖字段
|
|
*/
|
|
private function createRide(array $attributes = []): ShopItem
|
|
{
|
|
$data = array_merge([
|
|
'name' => '测试座驾',
|
|
'slug' => 'ride_test_'.str()->random(8),
|
|
'description' => '测试座驾说明',
|
|
'icon' => '🚘',
|
|
'price' => 1000,
|
|
'type' => 'ride',
|
|
'duration_days' => 7,
|
|
'duration_minutes' => 0,
|
|
'sort_order' => 80,
|
|
'is_active' => true,
|
|
'welcome_message' => '【{name}】驾驶【{ride}】入场',
|
|
], $attributes);
|
|
|
|
return ShopItem::query()->updateOrCreate(
|
|
['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));
|
|
}
|
|
}
|