测试: 完成游戏娱乐模块 (Gomoku, HorseRace, Lottery 等) 功能全量联调测试与代码格式化
This commit is contained in:
@@ -39,7 +39,15 @@ class AuthController extends Controller
|
||||
|
||||
if ($user) {
|
||||
// 用户存在,验证密码
|
||||
if (Hash::check($password, $user->password)) {
|
||||
$passwordMatches = false;
|
||||
try {
|
||||
$passwordMatches = Hash::check($password, $user->password);
|
||||
} catch (\RuntimeException $e) {
|
||||
// Hash::check() in Laravel 11/12 throws if the hash isn't a valid bcrypt string
|
||||
$passwordMatches = false;
|
||||
}
|
||||
|
||||
if ($passwordMatches) {
|
||||
// Bcrypt 验证通过
|
||||
|
||||
// 检测是否被封禁 (后台管理员级别获得豁免权,防止误把自己关在门外)
|
||||
|
||||
@@ -33,7 +33,7 @@ class StoreRoomRequest extends FormRequest
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => ['required', 'string', 'max:50', 'unique:rooms,name'],
|
||||
'name' => ['required', 'string', 'max:50', 'unique:rooms,room_name'],
|
||||
'description' => ['nullable', 'string', 'max:255'],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ class UpdateRoomRequest extends FormRequest
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => ['required', 'string', 'max:50', 'unique:rooms,name,'.$this->route('id')],
|
||||
'name' => ['required', 'string', 'max:50', 'unique:rooms,room_name,'.$this->route('id')],
|
||||
'description' => ['nullable', 'string', 'max:255'],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -33,16 +33,32 @@ class KafkaConsumerService
|
||||
protected string $groupId = '';
|
||||
|
||||
/**
|
||||
* 构造函数 — 从 SysParam 获取配置
|
||||
* 构造函数
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$param = SysParam::where('alias', 'wechat_bot_config')->first();
|
||||
if ($param && ! empty($param->body)) {
|
||||
$config = json_decode($param->body, true);
|
||||
$this->brokers = $config['kafka']['brokers'] ?? '';
|
||||
$this->topic = $config['kafka']['topic'] ?? '';
|
||||
$this->groupId = $config['kafka']['group_id'] ?? 'chatroom_wechat_bot';
|
||||
// 延迟加载配置
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载 Kafka 配置
|
||||
*/
|
||||
protected function loadConfig(): void
|
||||
{
|
||||
if (! empty($this->brokers)) {
|
||||
return; // 已经加载过
|
||||
}
|
||||
|
||||
try {
|
||||
$param = SysParam::where('alias', 'wechat_bot_config')->first();
|
||||
if ($param && ! empty($param->body)) {
|
||||
$config = json_decode($param->body, true);
|
||||
$this->brokers = $config['kafka']['brokers'] ?? '';
|
||||
$this->topic = $config['kafka']['topic'] ?? '';
|
||||
$this->groupId = $config['kafka']['group_id'] ?? 'chatroom_wechat_bot';
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning('加载 Kafka 配置失败', ['error' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +67,8 @@ class KafkaConsumerService
|
||||
*/
|
||||
public function createConsumer(): ?Consumer
|
||||
{
|
||||
$this->loadConfig();
|
||||
|
||||
if (empty($this->brokers) || empty($this->topic)) {
|
||||
Log::warning('WechatBot Kafka: brokers or topic is empty. Consumer not started.');
|
||||
|
||||
|
||||
@@ -24,21 +24,12 @@ class UserFactory extends Factory
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'name' => fake()->name(),
|
||||
'username' => fake()->unique()->name(),
|
||||
'email' => fake()->unique()->safeEmail(),
|
||||
'email_verified_at' => now(),
|
||||
'password' => static::$password ??= Hash::make('password'),
|
||||
'remember_token' => Str::random(10),
|
||||
'sex' => 1,
|
||||
'user_level' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the model's email address should be unverified.
|
||||
*/
|
||||
public function unverified(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'email_verified_at' => null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ return new class extends Migration
|
||||
['name' => '🌧️ 下雨周卡', 'slug' => 'week_rain', 'description' => '7天内进房间自动下雨', 'icon' => '🌧️', 'price' => 8888, 'type' => 'duration', 'duration_days' => 7, 'sort_order' => 6, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()],
|
||||
['name' => '⚡ 雷电周卡', 'slug' => 'week_lightning', 'description' => '7天内进房间自动雷电', 'icon' => '⚡', 'price' => 8888, 'type' => 'duration', 'duration_days' => 7, 'sort_order' => 7, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()],
|
||||
['name' => '❄️ 下雪周卡', 'slug' => 'week_snow', 'description' => '7天内进房间自动下雪', 'icon' => '❄️', 'price' => 8888, 'type' => 'duration', 'duration_days' => 7, 'sort_order' => 8, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()],
|
||||
['name' => '✏️ 改名卡', 'slug' => 'rename_card', 'description' => '修改一次用户名(受黑名单限制)', 'icon' => '✏️', 'price' => 5000, 'type' => 'rename', 'duration_days' => null, 'sort_order' => 9, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()],
|
||||
['name' => '✏️ 改名卡', 'slug' => 'rename_card', 'description' => '修改一次用户名(受黑名单限制)', 'icon' => '✏️', 'price' => 5000, 'type' => 'one_time', 'duration_days' => null, 'sort_order' => 9, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()],
|
||||
]);
|
||||
|
||||
// ── 购买记录表 ────────────────────────────────────────────────
|
||||
|
||||
@@ -67,7 +67,7 @@ return new class extends Migration
|
||||
|
||||
// 联合唯一索引:每个用户每条反馈只能赞同一次
|
||||
$table->unique(['feedback_id', 'user_id'], 'uq_vote');
|
||||
$table->index('feedback_id', 'idx_feedback_id');
|
||||
$table->index('feedback_id');
|
||||
});
|
||||
|
||||
// ——— 表3:补充评论 ———
|
||||
@@ -83,7 +83,7 @@ return new class extends Migration
|
||||
$table->boolean('is_admin')->default(false)->comment('是否为 id=1 管理员的官方回复');
|
||||
$table->timestamps();
|
||||
|
||||
$table->index('feedback_id', 'idx_feedback_id');
|
||||
$table->index('feedback_id');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
<env name="BCRYPT_ROUNDS" value="4"/>
|
||||
<env name="BROADCAST_CONNECTION" value="null"/>
|
||||
<env name="CACHE_STORE" value="array"/>
|
||||
<env name="DB_CONNECTION" value="sqlite"/>
|
||||
<env name="DB_DATABASE" value=":memory:"/>
|
||||
<env name="DB_CONNECTION" value="mysql"/>
|
||||
<env name="DB_DATABASE" value="chatroom_testing"/>
|
||||
<env name="MAIL_MAILER" value="array"/>
|
||||
<env name="QUEUE_CONNECTION" value="sync"/>
|
||||
<env name="SESSION_DRIVER" value="array"/>
|
||||
|
||||
184
tests/Feature/AuthControllerTest.php
Normal file
184
tests/Feature/AuthControllerTest.php
Normal file
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\Sysparam;
|
||||
use App\Models\User;
|
||||
use App\Models\UsernameBlacklist;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Tests\TestCase;
|
||||
|
||||
class AuthControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
// Mock captcha validation to always pass
|
||||
Validator::extend('captcha', function () {
|
||||
return true;
|
||||
});
|
||||
|
||||
// Mock Sysparam to return a default superlevel
|
||||
Sysparam::updateOrCreate(
|
||||
['alias' => 'superlevel'],
|
||||
['body' => '100']
|
||||
);
|
||||
}
|
||||
|
||||
public function test_can_register_new_user()
|
||||
{
|
||||
Redis::shouldReceive('sismember')->with('banned_ips', '127.0.0.1')->andReturn(false);
|
||||
|
||||
$response = $this->postJson('/login', [
|
||||
'username' => 'newuser',
|
||||
'password' => 'secret123',
|
||||
'captcha' => '1234',
|
||||
'bSex' => '1',
|
||||
]);
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonPath('status', 'success');
|
||||
|
||||
$this->assertDatabaseHas('users', [
|
||||
'username' => 'newuser',
|
||||
'user_level' => 1,
|
||||
'sex' => 1,
|
||||
]);
|
||||
|
||||
$this->assertAuthenticated();
|
||||
}
|
||||
|
||||
public function test_cannot_register_with_blacklisted_username()
|
||||
{
|
||||
Redis::shouldReceive('sismember')->with('banned_ips', '127.0.0.1')->andReturn(false);
|
||||
|
||||
UsernameBlacklist::create([
|
||||
'username' => 'admin',
|
||||
'type' => 'permanent',
|
||||
]);
|
||||
|
||||
$response = $this->postJson('/login', [
|
||||
'username' => 'admin',
|
||||
'password' => 'secret123',
|
||||
'captcha' => '1234',
|
||||
]);
|
||||
|
||||
$response->assertStatus(422)
|
||||
->assertJsonPath('status', 'error');
|
||||
|
||||
$this->assertDatabaseMissing('users', [
|
||||
'username' => 'admin',
|
||||
]);
|
||||
|
||||
$this->assertGuest();
|
||||
}
|
||||
|
||||
public function test_can_login_existing_user()
|
||||
{
|
||||
Redis::shouldReceive('sismember')->with('banned_ips', '127.0.0.1')->andReturn(false);
|
||||
|
||||
$user = User::factory()->create([
|
||||
'username' => 'testuser',
|
||||
'password' => Hash::make('password123'),
|
||||
]);
|
||||
|
||||
$response = $this->postJson('/login', [
|
||||
'username' => 'testuser',
|
||||
'password' => 'password123',
|
||||
'captcha' => '1234',
|
||||
]);
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonPath('status', 'success');
|
||||
|
||||
$this->assertAuthenticatedAs($user);
|
||||
}
|
||||
|
||||
public function test_login_md5_user_upgrades_to_bcrypt()
|
||||
{
|
||||
Redis::shouldReceive('sismember')->with('banned_ips', '127.0.0.1')->andReturn(false);
|
||||
|
||||
$password = 'oldsecret';
|
||||
$user = User::factory()->create([
|
||||
'username' => 'olduser',
|
||||
'password' => 'temp',
|
||||
]);
|
||||
|
||||
\Illuminate\Support\Facades\DB::table('users')
|
||||
->where('id', $user->id)
|
||||
->update(['password' => md5($password)]);
|
||||
|
||||
$response = $this->postJson('/login', [
|
||||
'username' => 'olduser',
|
||||
'password' => $password,
|
||||
'captcha' => '1234',
|
||||
]);
|
||||
|
||||
if ($response->status() !== 200) {
|
||||
dd($response->json());
|
||||
}
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonPath('status', 'success');
|
||||
|
||||
$user->refresh();
|
||||
$this->assertTrue(Hash::check($password, $user->password));
|
||||
$this->assertAuthenticatedAs($user);
|
||||
}
|
||||
|
||||
public function test_banned_user_cannot_login()
|
||||
{
|
||||
$user = User::factory()->create([
|
||||
'username' => 'banneduser',
|
||||
'password' => Hash::make('secret123'),
|
||||
'user_level' => -1, // banned
|
||||
]);
|
||||
|
||||
$response = $this->postJson('/login', [
|
||||
'username' => 'banneduser',
|
||||
'password' => 'secret123',
|
||||
'captcha' => '1234',
|
||||
]);
|
||||
|
||||
$response->assertStatus(403)
|
||||
->assertJsonPath('status', 'error');
|
||||
|
||||
$this->assertGuest();
|
||||
}
|
||||
|
||||
public function test_banned_ip_cannot_login()
|
||||
{
|
||||
Redis::shouldReceive('sismember')->with('banned_ips', '127.0.0.1')->andReturn(true);
|
||||
|
||||
$user = User::factory()->create([
|
||||
'username' => 'normaluser',
|
||||
'password' => Hash::make('secret'),
|
||||
]);
|
||||
|
||||
$response = $this->postJson('/login', [
|
||||
'username' => 'normaluser',
|
||||
'password' => 'secret',
|
||||
'captcha' => '1234',
|
||||
]);
|
||||
|
||||
$response->assertStatus(403);
|
||||
$this->assertGuest();
|
||||
}
|
||||
|
||||
public function test_can_logout()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
|
||||
$response = $this->actingAs($user)->post('/logout');
|
||||
|
||||
$response->assertRedirect('/');
|
||||
$this->assertGuest();
|
||||
}
|
||||
}
|
||||
200
tests/Feature/BaccaratControllerTest.php
Normal file
200
tests/Feature/BaccaratControllerTest.php
Normal file
@@ -0,0 +1,200 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\BaccaratBet;
|
||||
use App\Models\BaccaratRound;
|
||||
use App\Models\GameConfig;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Tests\TestCase;
|
||||
|
||||
class BaccaratControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
GameConfig::updateOrCreate(
|
||||
['game_key' => 'baccarat'],
|
||||
[
|
||||
'name' => 'Baccarat',
|
||||
'icon' => 'baccarat',
|
||||
'description' => 'Baccarat Game',
|
||||
'enabled' => true,
|
||||
'params' => [
|
||||
'min_bet' => 100,
|
||||
'max_bet' => 50000,
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function test_can_get_current_round()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
|
||||
$round = BaccaratRound::forceCreate([
|
||||
'status' => 'betting',
|
||||
'bet_opens_at' => now(),
|
||||
'bet_closes_at' => now()->addMinutes(1),
|
||||
'total_bet_big' => 0,
|
||||
'total_bet_small' => 0,
|
||||
'total_bet_triple' => 0,
|
||||
'bet_count' => 0,
|
||||
'bet_count_big' => 0,
|
||||
'bet_count_small' => 0,
|
||||
'bet_count_triple' => 0,
|
||||
'total_payout' => 0,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->getJson(route('baccarat.current'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonStructure(['round' => ['id', 'status', 'bet_closes_at']]);
|
||||
$this->assertEquals($round->id, $response->json('round.id'));
|
||||
}
|
||||
|
||||
public function test_can_bet()
|
||||
{
|
||||
Event::fake();
|
||||
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create(['jjb' => 200]);
|
||||
|
||||
$round = BaccaratRound::forceCreate([
|
||||
'status' => 'betting',
|
||||
'bet_opens_at' => now(),
|
||||
'bet_closes_at' => now()->addMinutes(1),
|
||||
'total_bet_big' => 0,
|
||||
'total_bet_small' => 0,
|
||||
'total_bet_triple' => 0,
|
||||
'bet_count' => 0,
|
||||
'bet_count_big' => 0,
|
||||
'bet_count_small' => 0,
|
||||
'bet_count_triple' => 0,
|
||||
'total_payout' => 0,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('baccarat.bet'), [
|
||||
'round_id' => $round->id,
|
||||
'bet_type' => 'big',
|
||||
'amount' => 100,
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['ok' => true]);
|
||||
|
||||
$this->assertEquals(100, $user->fresh()->jjb);
|
||||
$this->assertDatabaseHas('baccarat_bets', [
|
||||
'round_id' => $round->id,
|
||||
'user_id' => $user->id,
|
||||
'bet_type' => 'big',
|
||||
'amount' => 100,
|
||||
]);
|
||||
|
||||
Event::assertDispatched(\App\Events\BaccaratPoolUpdated::class);
|
||||
}
|
||||
|
||||
public function test_cannot_bet_out_of_range()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create(['jjb' => 200]);
|
||||
|
||||
$round = BaccaratRound::forceCreate([
|
||||
'status' => 'betting',
|
||||
'bet_opens_at' => now(),
|
||||
'bet_closes_at' => now()->addMinutes(1),
|
||||
'total_bet_big' => 0,
|
||||
'total_bet_small' => 0,
|
||||
'total_bet_triple' => 0,
|
||||
'bet_count' => 0,
|
||||
'bet_count_big' => 0,
|
||||
'bet_count_small' => 0,
|
||||
'bet_count_triple' => 0,
|
||||
'total_payout' => 0,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('baccarat.bet'), [
|
||||
'round_id' => $round->id,
|
||||
'bet_type' => 'big',
|
||||
'amount' => 50, // Less than min_bet
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['ok' => false]);
|
||||
}
|
||||
|
||||
public function test_cannot_bet_twice_in_same_round()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create(['jjb' => 200]);
|
||||
|
||||
$round = BaccaratRound::forceCreate([
|
||||
'status' => 'betting',
|
||||
'bet_opens_at' => now(),
|
||||
'bet_closes_at' => now()->addMinutes(1),
|
||||
'total_bet_big' => 0,
|
||||
'total_bet_small' => 0,
|
||||
'total_bet_triple' => 0,
|
||||
'bet_count' => 0,
|
||||
'bet_count_big' => 0,
|
||||
'bet_count_small' => 0,
|
||||
'bet_count_triple' => 0,
|
||||
'total_payout' => 0,
|
||||
]);
|
||||
|
||||
BaccaratBet::forceCreate([
|
||||
'round_id' => $round->id,
|
||||
'user_id' => $user->id,
|
||||
'bet_type' => 'big',
|
||||
'amount' => 100,
|
||||
'status' => 'pending',
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('baccarat.bet'), [
|
||||
'round_id' => $round->id,
|
||||
'bet_type' => 'small',
|
||||
'amount' => 100,
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['ok' => false]);
|
||||
}
|
||||
|
||||
public function test_can_get_history()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
|
||||
BaccaratRound::forceCreate([
|
||||
'status' => 'settled',
|
||||
'bet_opens_at' => now()->subMinutes(2),
|
||||
'bet_closes_at' => now()->subMinutes(1),
|
||||
'settled_at' => now(),
|
||||
'dice1' => 1,
|
||||
'dice2' => 2,
|
||||
'dice3' => 3,
|
||||
'total_points' => 6,
|
||||
'result' => 'small',
|
||||
'total_bet_big' => 0,
|
||||
'total_bet_small' => 0,
|
||||
'total_bet_triple' => 0,
|
||||
'bet_count' => 0,
|
||||
'bet_count_big' => 0,
|
||||
'bet_count_small' => 0,
|
||||
'bet_count_triple' => 0,
|
||||
'total_payout' => 0,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->getJson(route('baccarat.history'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonStructure(['history']);
|
||||
$this->assertCount(1, $response->json('history'));
|
||||
}
|
||||
}
|
||||
154
tests/Feature/BankControllerTest.php
Normal file
154
tests/Feature/BankControllerTest.php
Normal file
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\BankLog;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class BankControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_info_returns_balances_and_logs()
|
||||
{
|
||||
$user = User::factory()->create([
|
||||
'jjb' => 1000,
|
||||
'bank_jjb' => 5000,
|
||||
]);
|
||||
|
||||
BankLog::create([
|
||||
'user_id' => $user->id,
|
||||
'type' => 'deposit',
|
||||
'amount' => 500,
|
||||
'balance_after' => 5000,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->getJson(route('bank.info'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson([
|
||||
'status' => 'success',
|
||||
'jjb' => 1000,
|
||||
'bank_jjb' => 5000,
|
||||
]);
|
||||
$response->assertJsonCount(1, 'logs');
|
||||
}
|
||||
|
||||
public function test_deposit_transfers_jjb_to_bank()
|
||||
{
|
||||
$user = User::factory()->create([
|
||||
'jjb' => 1000,
|
||||
'bank_jjb' => 0,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('bank.deposit'), [
|
||||
'amount' => 500,
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson([
|
||||
'status' => 'success',
|
||||
'jjb' => 500,
|
||||
'bank_jjb' => 500,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('users', [
|
||||
'id' => $user->id,
|
||||
'jjb' => 500,
|
||||
'bank_jjb' => 500,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('bank_logs', [
|
||||
'user_id' => $user->id,
|
||||
'type' => 'deposit',
|
||||
'amount' => 500,
|
||||
'balance_after' => 500,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_deposit_fails_if_insufficient_funds()
|
||||
{
|
||||
$user = User::factory()->create([
|
||||
'jjb' => 100,
|
||||
'bank_jjb' => 0,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('bank.deposit'), [
|
||||
'amount' => 500,
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson([
|
||||
'status' => 'error',
|
||||
]);
|
||||
|
||||
$this->assertDatabaseMissing('bank_logs', [
|
||||
'user_id' => $user->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_withdraw_transfers_bank_to_jjb()
|
||||
{
|
||||
$user = User::factory()->create([
|
||||
'jjb' => 0,
|
||||
'bank_jjb' => 1000,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('bank.withdraw'), [
|
||||
'amount' => 500,
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson([
|
||||
'status' => 'success',
|
||||
'jjb' => 500,
|
||||
'bank_jjb' => 500,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('bank_logs', [
|
||||
'user_id' => $user->id,
|
||||
'type' => 'withdraw',
|
||||
'amount' => 500,
|
||||
'balance_after' => 500,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_withdraw_fails_if_insufficient_funds()
|
||||
{
|
||||
$user = User::factory()->create([
|
||||
'jjb' => 0,
|
||||
'bank_jjb' => 100,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('bank.withdraw'), [
|
||||
'amount' => 500,
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson([
|
||||
'status' => 'error',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_ranking_returns_paginated_users_ordered_by_bank_jjb()
|
||||
{
|
||||
$user = User::factory()->create(); // To act as
|
||||
|
||||
User::factory()->create(['bank_jjb' => 1000, 'username' => 'Rich']);
|
||||
User::factory()->create(['bank_jjb' => 500, 'username' => 'Poorer']);
|
||||
|
||||
$response = $this->actingAs($user)->getJson(route('bank.ranking'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson([
|
||||
'status' => 'success',
|
||||
]);
|
||||
|
||||
$ranking = $response->json('ranking');
|
||||
$this->assertCount(2, $ranking);
|
||||
$this->assertEquals('Rich', $ranking[0]['username']);
|
||||
$this->assertEquals('Poorer', $ranking[1]['username']);
|
||||
}
|
||||
}
|
||||
125
tests/Feature/ChatBotControllerTest.php
Normal file
125
tests/Feature/ChatBotControllerTest.php
Normal file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\Sysparam;
|
||||
use App\Models\User;
|
||||
use App\Services\AiChatService;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use Mockery\MockInterface;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ChatBotControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
Redis::flushall();
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function test_chatbot_disabled_by_default()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('chatbot.chat'), [
|
||||
'message' => 'Hello',
|
||||
'room_id' => 1,
|
||||
]);
|
||||
|
||||
$response->assertStatus(403);
|
||||
$response->assertJson(['status' => 'error']);
|
||||
}
|
||||
|
||||
public function test_chatbot_can_reply()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
|
||||
Sysparam::updateOrCreate(['alias' => 'chatbot_enabled'], ['body' => '1']);
|
||||
|
||||
User::factory()->create([
|
||||
'username' => 'AI小班长',
|
||||
'exp_num' => 0,
|
||||
'jjb' => 0,
|
||||
]);
|
||||
|
||||
// Mock the AiChatService
|
||||
$this->mock(AiChatService::class, function (MockInterface $mock) {
|
||||
$mock->shouldReceive('chat')
|
||||
->once()
|
||||
->andReturn([
|
||||
'reply' => 'Hello from AI',
|
||||
'provider' => 'test_provider',
|
||||
'model' => 'test_model',
|
||||
]);
|
||||
});
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('chatbot.chat'), [
|
||||
'message' => 'Hello',
|
||||
'room_id' => 1,
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson([
|
||||
'status' => 'success',
|
||||
'reply' => 'Hello from AI',
|
||||
'provider' => 'test_provider',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_chatbot_can_give_gold()
|
||||
{
|
||||
$user = User::factory()->create(['jjb' => 0]);
|
||||
|
||||
Sysparam::updateOrCreate(['alias' => 'chatbot_enabled'], ['body' => '1']);
|
||||
Sysparam::updateOrCreate(['alias' => 'chatbot_max_daily_rewards'], ['body' => '1']);
|
||||
Sysparam::updateOrCreate(['alias' => 'chatbot_max_gold'], ['body' => '500']);
|
||||
|
||||
User::factory()->create([
|
||||
'username' => 'AI小班长',
|
||||
'exp_num' => 0,
|
||||
'jjb' => 1000, // Ensure AI bot has enough gold
|
||||
]);
|
||||
|
||||
// Mock the AiChatService
|
||||
$this->mock(AiChatService::class, function (MockInterface $mock) {
|
||||
$mock->shouldReceive('chat')
|
||||
->once()
|
||||
->andReturn([
|
||||
'reply' => 'Here is some gold! [ACTION:GIVE_GOLD]',
|
||||
'provider' => 'test_provider',
|
||||
'model' => 'test_model',
|
||||
]);
|
||||
});
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('chatbot.chat'), [
|
||||
'message' => 'Give me gold',
|
||||
'room_id' => 1,
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
// User should have received gold
|
||||
$user->refresh();
|
||||
$this->assertGreaterThan(0, $user->jjb);
|
||||
$this->assertLessThanOrEqual(500, $user->jjb);
|
||||
}
|
||||
|
||||
public function test_clear_context()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
|
||||
$this->mock(AiChatService::class, function (MockInterface $mock) use ($user) {
|
||||
$mock->shouldReceive('clearContext')
|
||||
->once()
|
||||
->with($user->id);
|
||||
});
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('chatbot.clear'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['status' => 'success']);
|
||||
}
|
||||
}
|
||||
150
tests/Feature/ChatControllerTest.php
Normal file
150
tests/Feature/ChatControllerTest.php
Normal file
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\Room;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ChatControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Redis::flushall();
|
||||
}
|
||||
|
||||
public function test_can_view_room()
|
||||
{
|
||||
$room = Room::create(['room_name' => 'testroom']);
|
||||
$user = User::factory()->create();
|
||||
|
||||
$response = $this->actingAs($user)->get(route('chat.room', $room->id));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertViewIs('chat.frame');
|
||||
|
||||
// Assert user was added to room in redis
|
||||
$this->assertEquals(1, Redis::hexists("room:{$room->id}:users", $user->username));
|
||||
}
|
||||
|
||||
public function test_can_send_message()
|
||||
{
|
||||
$room = Room::create(['room_name' => 'test_send']);
|
||||
$user = User::factory()->create();
|
||||
|
||||
// 进房
|
||||
$this->actingAs($user)->get(route('chat.room', $room->id));
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('chat.send', $room->id), [
|
||||
'to_user' => '大家',
|
||||
'content' => '测试消息',
|
||||
'is_secret' => false,
|
||||
'font_color' => '#000000',
|
||||
'action' => 'say',
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['status' => 'success']);
|
||||
|
||||
// 查看 Redis 里的消息记录
|
||||
$messages = Redis::lrange("room:{$room->id}:messages", 0, -1);
|
||||
$this->assertNotEmpty($messages);
|
||||
|
||||
$found = false;
|
||||
foreach ($messages as $msgJson) {
|
||||
$msg = json_decode($msgJson, true);
|
||||
if ($msg['from_user'] === $user->username && $msg['content'] === '测试消息') {
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$this->assertTrue($found, 'Message not found in Redis');
|
||||
}
|
||||
|
||||
public function test_can_trigger_heartbeat()
|
||||
{
|
||||
$room = Room::create(['room_name' => 'test_hb']);
|
||||
$user = User::factory()->create(['exp_num' => 0]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('chat.heartbeat', $room->id));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonFragment(['status' => 'success']);
|
||||
|
||||
$user->refresh();
|
||||
$this->assertGreaterThanOrEqual(0, $user->exp_num); // Might be 1 depending on sysparam
|
||||
}
|
||||
|
||||
public function test_can_leave_room()
|
||||
{
|
||||
$room = Room::create(['room_name' => 'test_leave']);
|
||||
$user = User::factory()->create();
|
||||
|
||||
// 进房
|
||||
$this->actingAs($user)->get(route('chat.room', $room->id));
|
||||
$this->assertEquals(1, Redis::hexists("room:{$room->id}:users", $user->username));
|
||||
|
||||
// 显式退房
|
||||
$response = $this->actingAs($user)->postJson(route('chat.leave', $room->id).'?explicit=1');
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
// 缓存中被移除
|
||||
$this->assertEquals(0, Redis::hexists("room:{$room->id}:users", $user->username));
|
||||
}
|
||||
|
||||
public function test_can_get_rooms_online_status()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
$room1 = Room::create(['room_name' => 'room1']);
|
||||
$room2 = Room::create(['room_name' => 'room2']);
|
||||
|
||||
$this->actingAs($user)->get(route('chat.room', $room1->id));
|
||||
|
||||
$response = $this->actingAs($user)->getJson(route('chat.rooms-online-status'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
// Assert room1 has 1 online, room2 has 0
|
||||
$response->assertJsonFragment([
|
||||
'id' => $room1->id,
|
||||
'online' => 1,
|
||||
]);
|
||||
$response->assertJsonFragment([
|
||||
'id' => $room2->id,
|
||||
'online' => 0,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_can_set_announcement()
|
||||
{
|
||||
$user = User::factory()->create(['user_level' => 100]); // superadmin
|
||||
$room = Room::create(['room_name' => 'test_ann', 'room_owner' => 'someone']);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('chat.announcement', $room->id), [
|
||||
'announcement' => 'This is a new test announcement',
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
$room->refresh();
|
||||
$this->assertStringContainsString('This is a new test announcement', $room->announcement);
|
||||
}
|
||||
|
||||
public function test_cannot_set_announcement_without_permission()
|
||||
{
|
||||
$user = User::factory()->create(['user_level' => 0]);
|
||||
$room = Room::create(['room_name' => 'test_ann2', 'room_owner' => 'someone']);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('chat.announcement', $room->id), [
|
||||
'announcement' => 'This is a new test announcement',
|
||||
]);
|
||||
|
||||
$response->assertStatus(403);
|
||||
}
|
||||
}
|
||||
84
tests/Feature/DutyHallControllerTest.php
Normal file
84
tests/Feature/DutyHallControllerTest.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\Department;
|
||||
use App\Models\Position;
|
||||
use App\Models\PositionAuthorityLog;
|
||||
use App\Models\PositionDutyLog;
|
||||
use App\Models\User;
|
||||
use App\Models\UserPosition;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class DutyHallControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_can_view_duty_hall_roster_tab()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
|
||||
$department = Department::create(['name' => 'Support', 'rank' => 1]);
|
||||
$position = Position::create(['name' => 'Helper', 'department_id' => $department->id, 'rank' => 1, 'type' => 'temp']);
|
||||
|
||||
UserPosition::create([
|
||||
'user_id' => $user->id,
|
||||
'position_id' => $position->id,
|
||||
'status' => 'active',
|
||||
'appointed_at' => now(),
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->get(route('duty-hall.index', ['tab' => 'roster']));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertViewIs('duty-hall.index');
|
||||
$response->assertViewHas('tab', 'roster');
|
||||
|
||||
$currentStaff = $response->viewData('currentStaff');
|
||||
$this->assertNotNull($currentStaff);
|
||||
$this->assertEquals(1, $currentStaff->count());
|
||||
$this->assertEquals('Support', $currentStaff->first()->name);
|
||||
}
|
||||
|
||||
public function test_can_view_duty_hall_leaderboard_tabs()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
|
||||
PositionDutyLog::create([
|
||||
'user_id' => $user->id,
|
||||
'position_id' => 1,
|
||||
'login_at' => now()->subMinutes(30),
|
||||
'logout_at' => now(),
|
||||
'duration_seconds' => 1800,
|
||||
]);
|
||||
|
||||
PositionAuthorityLog::create([
|
||||
'user_id' => $user->id, // operator
|
||||
'target_user_id' => $user->id, // target
|
||||
'action_type' => 'reward',
|
||||
'amount' => 500,
|
||||
]);
|
||||
|
||||
$tabs = ['day', 'week', 'month', 'all'];
|
||||
|
||||
foreach ($tabs as $tab) {
|
||||
$response = $this->actingAs($user)->get(route('duty-hall.index', ['tab' => $tab]));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertViewIs('duty-hall.index');
|
||||
$response->assertViewHas('tab', $tab);
|
||||
|
||||
$leaderboard = $response->viewData('leaderboard');
|
||||
$this->assertNotNull($leaderboard);
|
||||
|
||||
if ($leaderboard->count() > 0) {
|
||||
$row = $leaderboard->first();
|
||||
$this->assertEquals($user->id, $row->user_id);
|
||||
$this->assertEquals(1800, $row->total_seconds);
|
||||
$this->assertEquals(1, $row->reward_count);
|
||||
$this->assertEquals(500, $row->reward_total);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
78
tests/Feature/EarnControllerTest.php
Normal file
78
tests/Feature/EarnControllerTest.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use Tests\TestCase;
|
||||
|
||||
class EarnControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
Redis::flushall(); // Clear redis keys so they don't leak between tests
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function test_can_claim_video_reward()
|
||||
{
|
||||
$user = User::factory()->create([
|
||||
'jjb' => 0,
|
||||
'exp_num' => 0,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('earn.video_reward'), [
|
||||
'room_id' => 1,
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson([
|
||||
'success' => true,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('users', [
|
||||
'id' => $user->id,
|
||||
'jjb' => 5000, // Reward is hardcoded to 5000
|
||||
'exp_num' => 500, // Exp is hardcoded to 500
|
||||
]);
|
||||
|
||||
// Cooldown should be set
|
||||
$this->assertTrue((bool) Redis::exists("earn_video:cooldown:{$user->id}"));
|
||||
}
|
||||
|
||||
public function test_cannot_claim_during_cooldown()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
|
||||
// First claim
|
||||
$this->actingAs($user)->postJson(route('earn.video_reward'));
|
||||
|
||||
// Second claim immediately should fail due to cooldown
|
||||
$response = $this->actingAs($user)->postJson(route('earn.video_reward'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson([
|
||||
'success' => false,
|
||||
'message' => '操作过快,请稍后再试。',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_cannot_exceed_daily_limit()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
$dateKey = now()->format('Y-m-d');
|
||||
|
||||
// Manually set daily count to max limit (3)
|
||||
Redis::set("earn_video:count:{$user->id}:{$dateKey}", 3);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('earn.video_reward'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonFragment([
|
||||
'success' => false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,13 @@
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
// use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ExampleTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
/**
|
||||
* A basic test example.
|
||||
*/
|
||||
|
||||
125
tests/Feature/FishingControllerTest.php
Normal file
125
tests/Feature/FishingControllerTest.php
Normal file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\GameConfig;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use Tests\TestCase;
|
||||
|
||||
class FishingControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Redis::flushall();
|
||||
|
||||
GameConfig::updateOrCreate(
|
||||
['game_key' => 'fishing'],
|
||||
[
|
||||
'name' => 'Fishing',
|
||||
'icon' => 'fish',
|
||||
'description' => 'Fishing Game',
|
||||
'enabled' => true,
|
||||
'params' => [
|
||||
'fishing_cost' => 5,
|
||||
'fishing_cooldown' => 300,
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function test_can_cast_rod()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create(['jjb' => 10]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('fishing.cast', ['id' => 1]));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson([
|
||||
'status' => 'success',
|
||||
'cost' => 5,
|
||||
]);
|
||||
|
||||
$this->assertEquals(5, $user->fresh()->jjb);
|
||||
}
|
||||
|
||||
public function test_cannot_cast_when_on_cooldown()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create(['jjb' => 10]);
|
||||
Redis::setex("fishing:cd:{$user->id}", 100, time());
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('fishing.cast', ['id' => 1]));
|
||||
|
||||
$response->assertStatus(429);
|
||||
$response->assertJson([
|
||||
'status' => 'error',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_cannot_cast_without_enough_gold()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create(['jjb' => 2]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('fishing.cast', ['id' => 1]));
|
||||
|
||||
$response->assertStatus(422);
|
||||
$response->assertJson([
|
||||
'status' => 'error',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_can_reel_after_waiting()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create(['jjb' => 10]);
|
||||
|
||||
$token = 'test-token';
|
||||
$waitTime = 0; // Set to 0 so we can test immediately
|
||||
|
||||
Redis::set("fishing:token:{$user->id}", json_encode([
|
||||
'token' => $token,
|
||||
'cast_at' => time() - 1, // Simulate past
|
||||
'wait_time' => 0,
|
||||
]));
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('fishing.reel', ['id' => 1]), [
|
||||
'token' => $token,
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson([
|
||||
'status' => 'success',
|
||||
]);
|
||||
|
||||
// Cooldown should be set
|
||||
$this->assertTrue((bool) Redis::exists("fishing:cd:{$user->id}"));
|
||||
}
|
||||
|
||||
public function test_cannot_reel_with_invalid_token()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create(['jjb' => 10]);
|
||||
|
||||
Redis::set("fishing:token:{$user->id}", json_encode([
|
||||
'token' => 'valid-token',
|
||||
'cast_at' => time(),
|
||||
'wait_time' => 0,
|
||||
]));
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('fishing.reel', ['id' => 1]), [
|
||||
'token' => 'invalid-token',
|
||||
]);
|
||||
|
||||
$response->assertStatus(422);
|
||||
$response->assertJson([
|
||||
'status' => 'error',
|
||||
]);
|
||||
}
|
||||
}
|
||||
142
tests/Feature/FortuneTellingControllerTest.php
Normal file
142
tests/Feature/FortuneTellingControllerTest.php
Normal file
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\FortuneLog;
|
||||
use App\Models\GameConfig;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class FortuneTellingControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
GameConfig::updateOrCreate(
|
||||
['game_key' => 'fortune_telling'],
|
||||
[
|
||||
'name' => 'Fortune Telling',
|
||||
'icon' => 'fortune',
|
||||
'description' => 'Fortune Telling Game',
|
||||
'enabled' => true,
|
||||
'params' => [
|
||||
'free_count_per_day' => 1,
|
||||
'extra_cost' => 500,
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function test_can_get_today_status()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
|
||||
$response = $this->actingAs($user)->getJson(route('fortune.today'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson([
|
||||
'enabled' => true,
|
||||
'today_count' => 0,
|
||||
'free_count' => 1,
|
||||
'free_used' => 0,
|
||||
'has_free_left' => true,
|
||||
'extra_cost' => 500,
|
||||
]);
|
||||
$response->assertJsonStructure(['latest']);
|
||||
}
|
||||
|
||||
public function test_can_tell_free()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create(['jjb' => 0]); // Note: 0 jjb needed for free
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('fortune.tell'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['ok' => true, 'is_free' => true]);
|
||||
|
||||
$this->assertDatabaseHas('fortune_logs', [
|
||||
'user_id' => $user->id,
|
||||
'is_free' => 1,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_cannot_tell_paid_without_enough_gold()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create(['jjb' => 0]);
|
||||
|
||||
// Consume free tell
|
||||
FortuneLog::create([
|
||||
'user_id' => $user->id,
|
||||
'grade' => 'great_luck',
|
||||
'text' => 'Test',
|
||||
'buff_desc' => 'Test',
|
||||
'is_free' => true,
|
||||
'cost' => 0,
|
||||
'fortune_date' => today(),
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('fortune.tell'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['ok' => false]);
|
||||
}
|
||||
|
||||
public function test_can_tell_paid_with_enough_gold()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create(['jjb' => 500]);
|
||||
|
||||
// Consume free tell
|
||||
FortuneLog::create([
|
||||
'user_id' => $user->id,
|
||||
'grade' => 'great_luck',
|
||||
'text' => 'Test',
|
||||
'buff_desc' => 'Test',
|
||||
'is_free' => true,
|
||||
'cost' => 0,
|
||||
'fortune_date' => today(),
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('fortune.tell'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['ok' => true, 'is_free' => false]);
|
||||
|
||||
$this->assertDatabaseHas('fortune_logs', [
|
||||
'user_id' => $user->id,
|
||||
'is_free' => 0,
|
||||
'cost' => 500,
|
||||
]);
|
||||
|
||||
$this->assertEquals(0, $user->fresh()->jjb);
|
||||
}
|
||||
|
||||
public function test_can_get_history()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
|
||||
FortuneLog::create([
|
||||
'user_id' => $user->id,
|
||||
'grade' => 'great_luck',
|
||||
'text' => 'Test',
|
||||
'buff_desc' => 'Test',
|
||||
'is_free' => true,
|
||||
'cost' => 0,
|
||||
'fortune_date' => today(),
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->getJson(route('fortune.history'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonStructure(['history']);
|
||||
$this->assertCount(1, $response->json('history'));
|
||||
}
|
||||
}
|
||||
169
tests/Feature/FriendControllerTest.php
Normal file
169
tests/Feature/FriendControllerTest.php
Normal file
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\FriendRequest;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class FriendControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_can_check_friend_status()
|
||||
{
|
||||
$me = User::factory()->create();
|
||||
$target = User::factory()->create();
|
||||
|
||||
// 此时不是好友
|
||||
$response = $this->actingAs($me)->getJson(route('friend.status', $target->username));
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson([
|
||||
'is_friend' => false,
|
||||
'mutual' => false,
|
||||
]);
|
||||
|
||||
// 我加了对方
|
||||
FriendRequest::create(['who' => $me->username, 'towho' => $target->username, 'sub_time' => now()]);
|
||||
|
||||
$response = $this->actingAs($me)->getJson(route('friend.status', $target->username));
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson([
|
||||
'is_friend' => true,
|
||||
'mutual' => false,
|
||||
]);
|
||||
|
||||
// 对方也加了我
|
||||
FriendRequest::create(['who' => $target->username, 'towho' => $me->username, 'sub_time' => now()]);
|
||||
|
||||
$response = $this->actingAs($me)->getJson(route('friend.status', $target->username));
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson([
|
||||
'is_friend' => true,
|
||||
'mutual' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_cannot_add_self_as_friend()
|
||||
{
|
||||
$me = User::factory()->create();
|
||||
|
||||
$response = $this->actingAs($me)->postJson(route('friend.add', $me->username));
|
||||
|
||||
$response->assertStatus(422);
|
||||
$response->assertJsonFragment(['message' => '不能将自己加为好友']);
|
||||
}
|
||||
|
||||
public function test_cannot_add_nonexistent_user()
|
||||
{
|
||||
$me = User::factory()->create();
|
||||
|
||||
$response = $this->actingAs($me)->postJson(route('friend.add', 'nonexistent_foo'));
|
||||
|
||||
$response->assertStatus(404);
|
||||
$response->assertJsonFragment(['message' => '用户不存在']);
|
||||
}
|
||||
|
||||
public function test_can_add_friend()
|
||||
{
|
||||
$me = User::factory()->create();
|
||||
$target = User::factory()->create();
|
||||
|
||||
$response = $this->actingAs($me)->postJson(route('friend.add', $target->username));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonFragment(['status' => 'success']);
|
||||
|
||||
$this->assertDatabaseHas('friend_requests', [
|
||||
'who' => $me->username,
|
||||
'towho' => $target->username,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_cannot_add_same_friend_twice()
|
||||
{
|
||||
$me = User::factory()->create();
|
||||
$target = User::factory()->create();
|
||||
|
||||
FriendRequest::create(['who' => $me->username, 'towho' => $target->username, 'sub_time' => now()]);
|
||||
|
||||
$response = $this->actingAs($me)->postJson(route('friend.add', $target->username));
|
||||
|
||||
$response->assertStatus(422);
|
||||
$response->assertJsonFragment(['message' => '已是好友,无需重复添加']);
|
||||
|
||||
// 确保只有1条记录
|
||||
$this->assertEquals(1, FriendRequest::where('who', $me->username)->where('towho', $target->username)->count());
|
||||
}
|
||||
|
||||
public function test_can_remove_friend()
|
||||
{
|
||||
$me = User::factory()->create();
|
||||
$target = User::factory()->create();
|
||||
|
||||
FriendRequest::create(['who' => $me->username, 'towho' => $target->username, 'sub_time' => now()]);
|
||||
|
||||
$response = $this->actingAs($me)->deleteJson(route('friend.remove', $target->username));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonFragment(['status' => 'success']);
|
||||
|
||||
$this->assertDatabaseMissing('friend_requests', [
|
||||
'who' => $me->username,
|
||||
'towho' => $target->username,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_cannot_remove_non_friend()
|
||||
{
|
||||
$me = User::factory()->create();
|
||||
$target = User::factory()->create();
|
||||
|
||||
$response = $this->actingAs($me)->deleteJson(route('friend.remove', $target->username));
|
||||
|
||||
$response->assertStatus(404);
|
||||
$response->assertJsonFragment(['message' => '好友关系不存在']);
|
||||
}
|
||||
|
||||
public function test_can_view_friend_list()
|
||||
{
|
||||
$me = User::factory()->create();
|
||||
|
||||
$myFriend = User::factory()->create();
|
||||
$someoneWhoAddedMe = User::factory()->create();
|
||||
$mutualFriend = User::factory()->create();
|
||||
|
||||
// 我加了他
|
||||
FriendRequest::create(['who' => $me->username, 'towho' => $myFriend->username, 'sub_time' => now()]);
|
||||
|
||||
// 他加了我 (Pending)
|
||||
FriendRequest::create(['who' => $someoneWhoAddedMe->username, 'towho' => $me->username, 'sub_time' => now()]);
|
||||
|
||||
// 互相好友
|
||||
FriendRequest::create(['who' => $me->username, 'towho' => $mutualFriend->username, 'sub_time' => now()]);
|
||||
FriendRequest::create(['who' => $mutualFriend->username, 'towho' => $me->username, 'sub_time' => now()]);
|
||||
|
||||
$response = $this->actingAs($me)->getJson(route('friend.index'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
$response->assertJsonStructure([
|
||||
'status',
|
||||
'friends' => [
|
||||
'*' => ['username', 'headface', 'user_level', 'sex', 'mutual', 'sub_time', 'is_online'],
|
||||
],
|
||||
'pending' => [
|
||||
'*' => ['username', 'headface', 'user_level', 'sex', 'added_at', 'is_online'],
|
||||
],
|
||||
]);
|
||||
|
||||
$responseData = $response->json();
|
||||
|
||||
$this->assertCount(2, $responseData['friends']);
|
||||
$this->assertCount(1, $responseData['pending']);
|
||||
|
||||
// 验证 pending 列表
|
||||
$this->assertEquals($someoneWhoAddedMe->username, $responseData['pending'][0]['username']);
|
||||
}
|
||||
}
|
||||
176
tests/Feature/GomokuControllerTest.php
Normal file
176
tests/Feature/GomokuControllerTest.php
Normal file
@@ -0,0 +1,176 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\GameConfig;
|
||||
use App\Models\GomokuGame;
|
||||
use App\Models\Room;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Tests\TestCase;
|
||||
|
||||
class GomokuControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
GameConfig::updateOrCreate(
|
||||
['game_key' => 'gomoku'],
|
||||
[
|
||||
'name' => 'Gomoku',
|
||||
'icon' => 'gomoku',
|
||||
'description' => 'Gomoku Game',
|
||||
'enabled' => true,
|
||||
'params' => [
|
||||
'invite_timeout' => 60,
|
||||
'pvp_reward' => 80,
|
||||
'pve_easy_fee' => 0,
|
||||
'pve_normal_fee' => 10,
|
||||
'pve_hard_fee' => 30,
|
||||
'pve_expert_fee' => 80,
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
private function createRoom(int $ownerId)
|
||||
{
|
||||
return Room::forceCreate([
|
||||
'id' => 100, // Safe room id
|
||||
'room_name' => 'test',
|
||||
'room_auto' => 'open',
|
||||
'room_owner' => 'owner',
|
||||
'room_des' => 'test description',
|
||||
'announcement' => 'welcome',
|
||||
'room_top' => '',
|
||||
'room_title' => 'test title',
|
||||
'room_keep' => 0,
|
||||
'room_time' => now(),
|
||||
'room_tt' => 0,
|
||||
'room_html' => 0,
|
||||
'room_exp' => 0,
|
||||
'build_time' => now(),
|
||||
'permit_level' => 0,
|
||||
'door_open' => 1,
|
||||
'ooooo' => 0,
|
||||
'visit_num' => 0,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_can_create_pve_game()
|
||||
{
|
||||
/** @var \App\Models\User $owner */
|
||||
$owner = User::factory()->create();
|
||||
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
|
||||
$room = $this->createRoom($owner->id);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('gomoku.create'), [
|
||||
'mode' => 'pve',
|
||||
'room_id' => $room->id,
|
||||
'ai_level' => 1,
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['ok' => true]);
|
||||
$this->assertDatabaseHas('gomoku_games', [
|
||||
'mode' => 'pve',
|
||||
'player_black_id' => $user->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_can_create_pvp_game()
|
||||
{
|
||||
Event::fake();
|
||||
|
||||
/** @var \App\Models\User $owner */
|
||||
$owner = User::factory()->create();
|
||||
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
|
||||
$room = $this->createRoom($owner->id);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('gomoku.create'), [
|
||||
'mode' => 'pvp',
|
||||
'room_id' => $room->id,
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['ok' => true]);
|
||||
$this->assertDatabaseHas('gomoku_games', [
|
||||
'mode' => 'pvp',
|
||||
'status' => 'waiting',
|
||||
'player_black_id' => $user->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_user_can_join_pvp()
|
||||
{
|
||||
/** @var \App\Models\User $owner */
|
||||
$owner = User::factory()->create();
|
||||
|
||||
/** @var \App\Models\User $user1 */
|
||||
$user1 = User::factory()->create();
|
||||
|
||||
/** @var \App\Models\User $user2 */
|
||||
$user2 = User::factory()->create();
|
||||
|
||||
$room = $this->createRoom($owner->id);
|
||||
|
||||
$game = GomokuGame::create([
|
||||
'mode' => 'pvp',
|
||||
'room_id' => $room->id,
|
||||
'player_black_id' => $user1->id,
|
||||
'status' => 'waiting',
|
||||
'board' => [],
|
||||
'current_turn' => 1,
|
||||
'entry_fee' => 0,
|
||||
'invite_expires_at' => now()->addMinutes(1),
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user2)->postJson(route('gomoku.join', $game));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['ok' => true]);
|
||||
$this->assertEquals($user2->id, $game->fresh()->player_white_id);
|
||||
$this->assertEquals('playing', $game->fresh()->status);
|
||||
}
|
||||
|
||||
public function test_user_can_get_active_game()
|
||||
{
|
||||
/** @var \App\Models\User $owner */
|
||||
$owner = User::factory()->create();
|
||||
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
|
||||
$room = $this->createRoom($owner->id);
|
||||
|
||||
$game = GomokuGame::create([
|
||||
'mode' => 'pve',
|
||||
'room_id' => $room->id,
|
||||
'player_black_id' => $user->id,
|
||||
'ai_level' => 1,
|
||||
'status' => 'playing',
|
||||
'board' => [],
|
||||
'current_turn' => 1,
|
||||
'entry_fee' => 0,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->getJson(route('gomoku.active'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson([
|
||||
'ok' => true,
|
||||
'has_active' => true,
|
||||
'game_id' => $game->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
173
tests/Feature/GuestbookControllerTest.php
Normal file
173
tests/Feature/GuestbookControllerTest.php
Normal file
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\Guestbook;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class GuestbookControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_index_displays_public_messages()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
$otherUser = User::factory()->create();
|
||||
|
||||
// Public message
|
||||
Guestbook::create([
|
||||
'who' => $otherUser->username,
|
||||
'towho' => null,
|
||||
'secret' => 0,
|
||||
'text_title' => 'Public Title',
|
||||
'text_body' => 'Public message body',
|
||||
'ip' => '127.0.0.1',
|
||||
'post_time' => now(),
|
||||
]);
|
||||
|
||||
// Secret message to someone else
|
||||
Guestbook::create([
|
||||
'who' => $otherUser->username,
|
||||
'towho' => 'anotheruser',
|
||||
'secret' => 1,
|
||||
'text_title' => 'Secret Title',
|
||||
'text_body' => 'Secret message body',
|
||||
'ip' => '127.0.0.1',
|
||||
'post_time' => now(),
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->get(route('guestbook.index', ['tab' => 'public']));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertViewIs('guestbook.index');
|
||||
$response->assertSee('Public message body');
|
||||
$response->assertDontSee('Secret message body');
|
||||
}
|
||||
|
||||
public function test_can_post_public_message()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
|
||||
$response = $this->actingAs($user)->post(route('guestbook.store'), [
|
||||
'text_title' => 'Hello',
|
||||
'text_body' => 'World',
|
||||
]);
|
||||
|
||||
$response->assertRedirect();
|
||||
|
||||
$this->assertDatabaseHas('guestbooks', [
|
||||
'who' => $user->username,
|
||||
'towho' => null,
|
||||
'secret' => 0,
|
||||
'text_body' => 'World',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_can_post_secret_message_to_user()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
$targetUser = User::factory()->create(['username' => 'target']);
|
||||
|
||||
$response = $this->actingAs($user)->post(route('guestbook.store'), [
|
||||
'text_title' => 'Secret',
|
||||
'text_body' => 'Top secret',
|
||||
'towho' => 'target',
|
||||
'secret' => 1,
|
||||
]);
|
||||
|
||||
$response->assertRedirect();
|
||||
|
||||
$this->assertDatabaseHas('guestbooks', [
|
||||
'who' => $user->username,
|
||||
'towho' => 'target',
|
||||
'secret' => 1,
|
||||
'text_body' => 'Top secret',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_cannot_post_message_to_non_existent_user()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
|
||||
$response = $this->actingAs($user)->post(route('guestbook.store'), [
|
||||
'text_title' => 'Secret',
|
||||
'text_body' => 'Top secret',
|
||||
'towho' => 'nonexistent',
|
||||
'secret' => 1,
|
||||
]);
|
||||
|
||||
$response->assertRedirect();
|
||||
$response->assertSessionHas('error');
|
||||
|
||||
$this->assertDatabaseMissing('guestbooks', [
|
||||
'who' => $user->username,
|
||||
'towho' => 'nonexistent',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_user_can_delete_own_message()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
|
||||
$message = Guestbook::create([
|
||||
'who' => $user->username,
|
||||
'towho' => null,
|
||||
'secret' => 0,
|
||||
'text_title' => 'My Body',
|
||||
'text_body' => 'Delete me',
|
||||
'ip' => '127.0.0.1',
|
||||
'post_time' => now(),
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->delete(route('guestbook.destroy', $message->id));
|
||||
|
||||
$response->assertRedirect();
|
||||
$response->assertSessionHas('success');
|
||||
$this->assertDatabaseMissing('guestbooks', ['id' => $message->id]);
|
||||
}
|
||||
|
||||
public function test_user_cannot_delete_others_message()
|
||||
{
|
||||
$owner = User::factory()->create();
|
||||
$otherUser = User::factory()->create(['user_level' => 1]); // regular user
|
||||
|
||||
$message = Guestbook::create([
|
||||
'who' => $owner->username,
|
||||
'towho' => null,
|
||||
'secret' => 0,
|
||||
'text_title' => 'Their Body',
|
||||
'text_body' => 'Cant touch this',
|
||||
'ip' => '127.0.0.1',
|
||||
'post_time' => now(),
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($otherUser)->delete(route('guestbook.destroy', $message->id));
|
||||
|
||||
$response->assertStatus(403);
|
||||
$this->assertDatabaseHas('guestbooks', ['id' => $message->id]);
|
||||
}
|
||||
|
||||
public function test_admin_can_delete_others_message()
|
||||
{
|
||||
$owner = User::factory()->create();
|
||||
$admin = User::factory()->create(['user_level' => 15]);
|
||||
|
||||
$message = Guestbook::create([
|
||||
'who' => $owner->username,
|
||||
'towho' => null,
|
||||
'secret' => 0,
|
||||
'text_title' => 'Their Body',
|
||||
'text_body' => 'Delete by admin',
|
||||
'ip' => '127.0.0.1',
|
||||
'post_time' => now(),
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($admin)->delete(route('guestbook.destroy', $message->id));
|
||||
|
||||
$response->assertRedirect();
|
||||
$response->assertSessionHas('success');
|
||||
$this->assertDatabaseMissing('guestbooks', ['id' => $message->id]);
|
||||
}
|
||||
}
|
||||
139
tests/Feature/HolidayControllerTest.php
Normal file
139
tests/Feature/HolidayControllerTest.php
Normal file
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\HolidayClaim;
|
||||
use App\Models\HolidayEvent;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class HolidayControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_can_check_holiday_status()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
$event = HolidayEvent::create([
|
||||
'name' => 'Test Holiday',
|
||||
'status' => 'active',
|
||||
'expires_at' => now()->addDays(1),
|
||||
'max_claimants' => 10,
|
||||
'claimed_amount' => 0,
|
||||
'total_amount' => 5000,
|
||||
'distribute_type' => 'fixed',
|
||||
'send_at' => now(),
|
||||
'repeat_type' => 'once',
|
||||
'target_type' => 'all',
|
||||
]);
|
||||
|
||||
HolidayClaim::create([
|
||||
'event_id' => $event->id,
|
||||
'user_id' => $user->id,
|
||||
'amount' => 500,
|
||||
'claimed_at' => now(),
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->getJson(route('holiday.status', ['event' => $event->id]));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson([
|
||||
'claimable' => true,
|
||||
'amount' => 500,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_can_claim_holiday_bonus()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create(['jjb' => 100]);
|
||||
$event = HolidayEvent::create([
|
||||
'name' => 'Test Holiday',
|
||||
'status' => 'active',
|
||||
'expires_at' => now()->addDays(1),
|
||||
'max_claimants' => 10,
|
||||
'claimed_amount' => 0,
|
||||
'total_amount' => 5000,
|
||||
'distribute_type' => 'fixed',
|
||||
'send_at' => now(),
|
||||
'repeat_type' => 'once',
|
||||
'target_type' => 'all',
|
||||
]);
|
||||
|
||||
HolidayClaim::create([
|
||||
'event_id' => $event->id,
|
||||
'user_id' => $user->id,
|
||||
'amount' => 500,
|
||||
'claimed_at' => now(),
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('holiday.claim', ['event' => $event->id]));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['ok' => true]);
|
||||
|
||||
// Verify currency incremented
|
||||
$this->assertEquals(600, $user->fresh()->jjb);
|
||||
|
||||
// Verify claim is deleted
|
||||
$this->assertDatabaseMissing('holiday_claims', [
|
||||
'event_id' => $event->id,
|
||||
'user_id' => $user->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_cannot_claim_if_not_in_list()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
$event = HolidayEvent::create([
|
||||
'name' => 'Test Holiday',
|
||||
'status' => 'active',
|
||||
'expires_at' => now()->addDays(1),
|
||||
'max_claimants' => 10,
|
||||
'claimed_amount' => 0,
|
||||
'total_amount' => 5000,
|
||||
'distribute_type' => 'fixed',
|
||||
'send_at' => now(),
|
||||
'repeat_type' => 'once',
|
||||
'target_type' => 'all',
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('holiday.claim', ['event' => $event->id]));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['ok' => false, 'message' => '您不在本次福利名单内,或活动已结束。']);
|
||||
}
|
||||
|
||||
public function test_cannot_claim_if_expired()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
$event = HolidayEvent::create([
|
||||
'name' => 'Test Holiday',
|
||||
'status' => 'completed',
|
||||
'expires_at' => now()->subDays(1),
|
||||
'max_claimants' => 10,
|
||||
'claimed_amount' => 0,
|
||||
'total_amount' => 5000,
|
||||
'distribute_type' => 'fixed',
|
||||
'send_at' => now(),
|
||||
'repeat_type' => 'once',
|
||||
'target_type' => 'all',
|
||||
]);
|
||||
|
||||
HolidayClaim::create([
|
||||
'event_id' => $event->id,
|
||||
'user_id' => $user->id,
|
||||
'amount' => 500,
|
||||
'claimed_at' => now(),
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('holiday.claim', ['event' => $event->id]));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['ok' => false, 'message' => '活动已结束或已过期。']);
|
||||
}
|
||||
}
|
||||
185
tests/Feature/HorseRaceControllerTest.php
Normal file
185
tests/Feature/HorseRaceControllerTest.php
Normal file
@@ -0,0 +1,185 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\GameConfig;
|
||||
use App\Models\HorseBet;
|
||||
use App\Models\HorseRace;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Tests\TestCase;
|
||||
|
||||
class HorseRaceControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
GameConfig::updateOrCreate(
|
||||
['game_key' => 'horse_racing'],
|
||||
[
|
||||
'name' => 'Horse Racing',
|
||||
'icon' => 'horse',
|
||||
'description' => 'Horse Racing Game',
|
||||
'enabled' => true,
|
||||
'params' => [
|
||||
'min_bet' => 100,
|
||||
'max_bet' => 100000,
|
||||
'house_take_percent' => 5,
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function test_can_get_current_race()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
|
||||
$race = HorseRace::create([
|
||||
'status' => 'betting',
|
||||
'bet_opens_at' => now(),
|
||||
'bet_closes_at' => now()->addMinutes(1),
|
||||
'horses' => [
|
||||
['id' => 1, 'name' => 'Horse A', 'emoji' => '🐎'],
|
||||
['id' => 2, 'name' => 'Horse B', 'emoji' => '🏇'],
|
||||
],
|
||||
'total_bets' => 0,
|
||||
'total_pool' => 0,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->getJson(route('horse-race.current'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonStructure(['race' => ['id', 'status', 'bet_closes_at', 'horses']]);
|
||||
$this->assertEquals($race->id, $response->json('race.id'));
|
||||
}
|
||||
|
||||
public function test_can_bet()
|
||||
{
|
||||
Event::fake();
|
||||
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create(['jjb' => 500]);
|
||||
|
||||
$race = HorseRace::create([
|
||||
'status' => 'betting',
|
||||
'bet_opens_at' => now(),
|
||||
'bet_closes_at' => now()->addMinutes(1),
|
||||
'horses' => [
|
||||
['id' => 1, 'name' => 'Horse A', 'emoji' => '🐎'],
|
||||
['id' => 2, 'name' => 'Horse B', 'emoji' => '🏇'],
|
||||
],
|
||||
'total_bets' => 0,
|
||||
'total_pool' => 0,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('horse-race.bet'), [
|
||||
'race_id' => $race->id,
|
||||
'horse_id' => 1,
|
||||
'amount' => 100,
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['ok' => true]);
|
||||
|
||||
$this->assertEquals(400, $user->fresh()->jjb);
|
||||
$this->assertDatabaseHas('horse_bets', [
|
||||
'race_id' => $race->id,
|
||||
'user_id' => $user->id,
|
||||
'horse_id' => 1,
|
||||
'amount' => 100,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_cannot_bet_out_of_range()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create(['jjb' => 500]);
|
||||
|
||||
$race = HorseRace::create([
|
||||
'status' => 'betting',
|
||||
'bet_opens_at' => now(),
|
||||
'bet_closes_at' => now()->addMinutes(1),
|
||||
'horses' => [
|
||||
['id' => 1, 'name' => 'Horse A', 'emoji' => '🐎'],
|
||||
['id' => 2, 'name' => 'Horse B', 'emoji' => '🏇'],
|
||||
],
|
||||
'total_bets' => 0,
|
||||
'total_pool' => 0,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('horse-race.bet'), [
|
||||
'race_id' => $race->id,
|
||||
'horse_id' => 1,
|
||||
'amount' => 50, // Less than min_bet
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['ok' => false]);
|
||||
}
|
||||
|
||||
public function test_cannot_bet_twice_in_same_race()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create(['jjb' => 500]);
|
||||
|
||||
$race = HorseRace::create([
|
||||
'status' => 'betting',
|
||||
'bet_opens_at' => now(),
|
||||
'bet_closes_at' => now()->addMinutes(1),
|
||||
'horses' => [
|
||||
['id' => 1, 'name' => 'Horse A', 'emoji' => '🐎'],
|
||||
['id' => 2, 'name' => 'Horse B', 'emoji' => '🏇'],
|
||||
],
|
||||
'total_bets' => 0,
|
||||
'total_pool' => 0,
|
||||
]);
|
||||
|
||||
HorseBet::forceCreate([
|
||||
'race_id' => $race->id,
|
||||
'user_id' => $user->id,
|
||||
'horse_id' => 1,
|
||||
'amount' => 100,
|
||||
'status' => 'pending',
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('horse-race.bet'), [
|
||||
'race_id' => $race->id,
|
||||
'horse_id' => 2,
|
||||
'amount' => 100,
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['ok' => false]);
|
||||
}
|
||||
|
||||
public function test_can_get_history()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
|
||||
HorseRace::create([
|
||||
'status' => 'settled',
|
||||
'bet_opens_at' => now()->subMinutes(2),
|
||||
'bet_closes_at' => now()->subMinutes(1),
|
||||
'settled_at' => now(),
|
||||
'winner_horse_id' => 1,
|
||||
'horses' => [
|
||||
['id' => 1, 'name' => 'Horse A', 'emoji' => '🐎'],
|
||||
['id' => 2, 'name' => 'Horse B', 'emoji' => '🏇'],
|
||||
],
|
||||
'total_bets' => 1,
|
||||
'total_pool' => 100,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->getJson(route('horse-race.history'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonStructure(['history']);
|
||||
$this->assertCount(1, $response->json('history'));
|
||||
}
|
||||
}
|
||||
43
tests/Feature/InviteControllerTest.php
Normal file
43
tests/Feature/InviteControllerTest.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class InviteControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_handle_sets_cookie()
|
||||
{
|
||||
$inviter = User::factory()->create();
|
||||
|
||||
$response = $this->get(route('invite.link', $inviter->id));
|
||||
|
||||
$response->assertRedirect(route('home'));
|
||||
$response->assertCookie('inviter_id', $inviter->id);
|
||||
}
|
||||
|
||||
public function test_handle_ignores_invalid_inviter()
|
||||
{
|
||||
$response = $this->get(route('invite.link', 99999));
|
||||
|
||||
$response->assertRedirect(route('home'));
|
||||
$response->assertCookieMissing('inviter_id');
|
||||
}
|
||||
|
||||
public function test_leaderboard_displays_inviters()
|
||||
{
|
||||
$inviter = User::factory()->create();
|
||||
$user = User::factory()->create();
|
||||
|
||||
User::factory()->count(2)->create(['inviter_id' => $inviter->id]);
|
||||
|
||||
$response = $this->actingAs($user)->get(route('invite.leaderboard'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertViewIs('invite.leaderboard');
|
||||
}
|
||||
}
|
||||
73
tests/Feature/LeaderboardControllerTest.php
Normal file
73
tests/Feature/LeaderboardControllerTest.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\Sysparam;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class LeaderboardControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_can_view_leaderboard_index()
|
||||
{
|
||||
$user = User::factory()->create(['exp_num' => 10, 'jjb' => 100, 'meili' => 5]);
|
||||
|
||||
Sysparam::updateOrCreate(['alias' => 'superlevel'], ['body' => '100']);
|
||||
Sysparam::updateOrCreate(['alias' => 'leaderboard_limit'], ['body' => '20']);
|
||||
|
||||
// Create users for leaderboard
|
||||
User::factory()->create(['user_level' => 10, 'exp_num' => 100, 'jjb' => 1000, 'meili' => 50]);
|
||||
User::factory()->create(['user_level' => 5, 'exp_num' => 50, 'jjb' => 500, 'meili' => 20]);
|
||||
|
||||
// Super admin should be hidden
|
||||
User::factory()->create(['user_level' => 100, 'exp_num' => 10000, 'jjb' => 100000, 'meili' => 5000]);
|
||||
|
||||
$response = $this->actingAs($user)->get(route('leaderboard.index'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertViewIs('leaderboard.index');
|
||||
|
||||
$topLevels = $response->viewData('topLevels');
|
||||
$this->assertNotNull($topLevels);
|
||||
$this->assertEquals(3, $topLevels->count()); // Excludes super admin
|
||||
$this->assertEquals(10, $topLevels->first()->user_level);
|
||||
|
||||
$topExp = $response->viewData('topExp');
|
||||
$this->assertNotNull($topExp);
|
||||
$this->assertEquals(3, $topExp->count());
|
||||
$this->assertEquals(100, $topExp->first()->exp_num);
|
||||
|
||||
$topWealth = $response->viewData('topWealth');
|
||||
$this->assertNotNull($topWealth);
|
||||
$this->assertEquals(3, $topWealth->count());
|
||||
$this->assertEquals(1000, $topWealth->first()->jjb);
|
||||
|
||||
$topCharm = $response->viewData('topCharm');
|
||||
$this->assertNotNull($topCharm);
|
||||
$this->assertEquals(3, $topCharm->count());
|
||||
$this->assertEquals(50, $topCharm->first()->meili);
|
||||
}
|
||||
|
||||
public function test_can_view_today_leaderboard()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
|
||||
$response = $this->actingAs($user)->get(route('leaderboard.today'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertViewIs('leaderboard.today');
|
||||
}
|
||||
|
||||
public function test_can_view_my_currency_logs()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
|
||||
$response = $this->actingAs($user)->get(route('currency.my-logs'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertViewIs('leaderboard.my-logs');
|
||||
}
|
||||
}
|
||||
214
tests/Feature/LotteryControllerTest.php
Normal file
214
tests/Feature/LotteryControllerTest.php
Normal file
@@ -0,0 +1,214 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\GameConfig;
|
||||
use App\Models\LotteryIssue;
|
||||
use App\Models\LotteryTicket;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class LotteryControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
GameConfig::updateOrCreate(
|
||||
['game_key' => 'lottery'],
|
||||
[
|
||||
'name' => 'Lottery',
|
||||
'icon' => 'lottery',
|
||||
'description' => 'Lottery Game',
|
||||
'enabled' => true,
|
||||
'params' => [
|
||||
'ticket_price' => 100,
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function test_can_get_current()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
|
||||
$issue = LotteryIssue::create([
|
||||
'issue_no' => '2026101',
|
||||
'status' => 'open',
|
||||
'red1' => 0,
|
||||
'red2' => 0,
|
||||
'red3' => 0,
|
||||
'blue' => 0,
|
||||
'pool_amount' => 5000,
|
||||
'carry_amount' => 0,
|
||||
'is_super_issue' => false,
|
||||
'no_winner_streak' => 0,
|
||||
'total_tickets' => 0,
|
||||
'payout_amount' => 0,
|
||||
'sell_closes_at' => now()->addMinutes(1),
|
||||
'draw_at' => now()->addMinutes(2),
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->getJson(route('lottery.current'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonStructure(['issue' => ['id', 'issue_no', 'status', 'pool_amount']]);
|
||||
$this->assertEquals($issue->id, $response->json('issue.id'));
|
||||
}
|
||||
|
||||
public function test_can_quick_pick()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
|
||||
$response = $this->actingAs($user)->getJson(route('lottery.quick-pick', ['count' => 2]));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonStructure(['numbers']);
|
||||
$this->assertCount(2, $response->json('numbers'));
|
||||
}
|
||||
|
||||
public function test_cannot_buy_without_enough_gold()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create(['jjb' => 50]);
|
||||
|
||||
LotteryIssue::create([
|
||||
'issue_no' => '2026101',
|
||||
'status' => 'open',
|
||||
'red1' => 0,
|
||||
'red2' => 0,
|
||||
'red3' => 0,
|
||||
'blue' => 0,
|
||||
'pool_amount' => 5000,
|
||||
'carry_amount' => 0,
|
||||
'is_super_issue' => false,
|
||||
'no_winner_streak' => 0,
|
||||
'total_tickets' => 0,
|
||||
'payout_amount' => 0,
|
||||
'sell_closes_at' => now()->addMinutes(1),
|
||||
'draw_at' => now()->addMinutes(2),
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('lottery.buy'), [
|
||||
'numbers' => [
|
||||
[
|
||||
'reds' => [1, 2, 3],
|
||||
'blue' => 4,
|
||||
],
|
||||
],
|
||||
'quick_pick' => false,
|
||||
]);
|
||||
|
||||
$response->assertStatus(422); // Exception thrown with 422
|
||||
}
|
||||
|
||||
public function test_can_buy_with_enough_gold()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create(['jjb' => 200]);
|
||||
|
||||
LotteryIssue::create([
|
||||
'issue_no' => '2026101',
|
||||
'status' => 'open',
|
||||
'red1' => 0,
|
||||
'red2' => 0,
|
||||
'red3' => 0,
|
||||
'blue' => 0,
|
||||
'pool_amount' => 5000,
|
||||
'carry_amount' => 0,
|
||||
'is_super_issue' => false,
|
||||
'no_winner_streak' => 0,
|
||||
'total_tickets' => 0,
|
||||
'payout_amount' => 0,
|
||||
'sell_closes_at' => now()->addMinutes(1),
|
||||
'draw_at' => now()->addMinutes(2),
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('lottery.buy'), [
|
||||
'numbers' => [
|
||||
[
|
||||
'reds' => [1, 2, 3],
|
||||
'blue' => 4,
|
||||
],
|
||||
],
|
||||
'quick_pick' => false,
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['status' => 'success']);
|
||||
|
||||
$this->assertEquals(100, $user->fresh()->jjb); // cost is 100 per ticket
|
||||
}
|
||||
|
||||
public function test_can_get_history()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
|
||||
LotteryIssue::create([
|
||||
'issue_no' => '2026100',
|
||||
'status' => 'settled',
|
||||
'red1' => 1,
|
||||
'red2' => 2,
|
||||
'red3' => 3,
|
||||
'blue' => 4,
|
||||
'pool_amount' => 5000,
|
||||
'carry_amount' => 0,
|
||||
'is_super_issue' => false,
|
||||
'no_winner_streak' => 0,
|
||||
'total_tickets' => 10,
|
||||
'payout_amount' => 1000,
|
||||
'draw_at' => now(),
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->getJson(route('lottery.history'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonStructure(['issues']);
|
||||
$this->assertCount(1, $response->json('issues'));
|
||||
}
|
||||
|
||||
public function test_can_get_my_tickets()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
|
||||
$issue = LotteryIssue::create([
|
||||
'issue_no' => '2026100',
|
||||
'status' => 'settled',
|
||||
'red1' => 1,
|
||||
'red2' => 2,
|
||||
'red3' => 3,
|
||||
'blue' => 4,
|
||||
'pool_amount' => 5000,
|
||||
'carry_amount' => 0,
|
||||
'is_super_issue' => false,
|
||||
'no_winner_streak' => 0,
|
||||
'total_tickets' => 10,
|
||||
'payout_amount' => 1000,
|
||||
'draw_at' => now(),
|
||||
]);
|
||||
|
||||
LotteryTicket::create([
|
||||
'issue_id' => $issue->id,
|
||||
'user_id' => $user->id,
|
||||
'red1' => 1,
|
||||
'red2' => 2,
|
||||
'red3' => 3,
|
||||
'blue' => 4,
|
||||
'amount' => 100,
|
||||
'is_quick_pick' => 0,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->getJson(route('lottery.my'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonStructure(['tickets']);
|
||||
$this->assertCount(1, $response->json('tickets'));
|
||||
}
|
||||
}
|
||||
269
tests/Feature/MarriageControllerTest.php
Normal file
269
tests/Feature/MarriageControllerTest.php
Normal file
@@ -0,0 +1,269 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\Marriage;
|
||||
use App\Models\ShopItem;
|
||||
use App\Models\User;
|
||||
use App\Models\UserPurchase;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Tests\TestCase;
|
||||
|
||||
class MarriageControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
private function createLegacyMarriageData()
|
||||
{
|
||||
return [
|
||||
'hyname' => 'test',
|
||||
'hyname1' => 'test1',
|
||||
'hytime' => now(),
|
||||
'hygb' => 'test',
|
||||
'hyjb' => 'test',
|
||||
'i' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
public function test_can_get_divorce_config()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
|
||||
$response = $this->actingAs($user)->getJson(route('marriage.divorce-config'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonStructure([
|
||||
'mutual_charm_penalty',
|
||||
'forced_charm_penalty',
|
||||
'mutual_cooldown_days',
|
||||
'forced_cooldown_days',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_can_get_status_unmarried()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
|
||||
$response = $this->actingAs($user)->getJson(route('marriage.status'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['married' => false]);
|
||||
}
|
||||
|
||||
public function test_can_get_status_married()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
/** @var \App\Models\User $partner */
|
||||
$partner = User::factory()->create();
|
||||
|
||||
$marriage = Marriage::create(array_merge([
|
||||
'user_id' => $user->id,
|
||||
'partner_id' => $partner->id,
|
||||
'status' => 'married',
|
||||
'married_at' => now(),
|
||||
'intimacy' => 100,
|
||||
], $this->createLegacyMarriageData()));
|
||||
|
||||
$response = $this->actingAs($user)->getJson(route('marriage.status'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson([
|
||||
'married' => true,
|
||||
'status' => 'married',
|
||||
]);
|
||||
$response->assertJsonPath('marriage.partner.id', $partner->id);
|
||||
}
|
||||
|
||||
public function test_can_get_target_status()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
/** @var \App\Models\User $target */
|
||||
$target = User::factory()->create();
|
||||
/** @var \App\Models\User $partner */
|
||||
$partner = User::factory()->create();
|
||||
|
||||
Marriage::create(array_merge([
|
||||
'user_id' => $target->id,
|
||||
'partner_id' => $partner->id,
|
||||
'status' => 'married',
|
||||
'married_at' => now(),
|
||||
], $this->createLegacyMarriageData()));
|
||||
|
||||
$response = $this->actingAs($user)->getJson(route('marriage.target-status', [
|
||||
'username' => $target->username,
|
||||
]));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson([
|
||||
'married' => true,
|
||||
]);
|
||||
$response->assertJsonPath('marriage.partner_name', $partner->username);
|
||||
}
|
||||
|
||||
public function test_can_get_my_rings()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
|
||||
$ring = ShopItem::create([
|
||||
'name' => 'Diamond Ring',
|
||||
'slug' => 'diamond_ring',
|
||||
'type' => 'ring',
|
||||
'price' => 1000,
|
||||
'currency' => 'gold',
|
||||
'use_type' => 'permanent',
|
||||
]);
|
||||
|
||||
$purchase = UserPurchase::create([
|
||||
'user_id' => $user->id,
|
||||
'shop_item_id' => $ring->id,
|
||||
'status' => 'active',
|
||||
'number' => 1,
|
||||
'amount' => 1000,
|
||||
'currency' => 'gold',
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->getJson(route('marriage.rings'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonStructure(['status', 'rings']);
|
||||
$this->assertCount(1, $response->json('rings'));
|
||||
$this->assertEquals($purchase->id, $response->json('rings.0.purchase_id'));
|
||||
}
|
||||
|
||||
public function test_can_propose()
|
||||
{
|
||||
Event::fake();
|
||||
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create(['jjb' => 10000, 'sex' => 1]);
|
||||
/** @var \App\Models\User $target */
|
||||
$target = User::factory()->create(['sex' => 2]);
|
||||
|
||||
$ring = ShopItem::create([
|
||||
'name' => 'Diamond Ring',
|
||||
'slug' => 'diamond_ring',
|
||||
'type' => 'ring',
|
||||
'price' => 1000,
|
||||
'currency' => 'gold',
|
||||
'use_type' => 'permanent',
|
||||
]);
|
||||
|
||||
$purchase = UserPurchase::create([
|
||||
'user_id' => $user->id,
|
||||
'shop_item_id' => $ring->id,
|
||||
'status' => 'active',
|
||||
'number' => 1,
|
||||
'amount' => 1000,
|
||||
'currency' => 'gold',
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('marriage.propose'), [
|
||||
'target_username' => $target->username,
|
||||
'ring_purchase_id' => $purchase->id,
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['ok' => true]);
|
||||
|
||||
Event::assertDispatched(\App\Events\MarriageProposed::class);
|
||||
}
|
||||
|
||||
public function test_can_accept_proposal()
|
||||
{
|
||||
Event::fake();
|
||||
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
/** @var \App\Models\User $partner */
|
||||
$partner = User::factory()->create();
|
||||
|
||||
$ring = ShopItem::create([
|
||||
'name' => 'Diamond',
|
||||
'slug' => 'diamond',
|
||||
'type' => 'ring',
|
||||
'price' => 1000,
|
||||
'currency' => 'gold',
|
||||
'use_type' => 'permanent',
|
||||
]);
|
||||
|
||||
$purchase = UserPurchase::create([
|
||||
'user_id' => $partner->id,
|
||||
'shop_item_id' => $ring->id,
|
||||
'status' => 'active',
|
||||
'number' => 1,
|
||||
'amount' => 1000,
|
||||
'currency' => 'gold',
|
||||
]);
|
||||
|
||||
$marriage = Marriage::create(array_merge([
|
||||
'user_id' => $partner->id,
|
||||
'partner_id' => $user->id,
|
||||
'status' => 'pending',
|
||||
'ring_purchase_id' => $purchase->id,
|
||||
'proposed_at' => now(),
|
||||
], $this->createLegacyMarriageData()));
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('marriage.accept', ['marriage' => $marriage->id]));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['ok' => true]);
|
||||
|
||||
Event::assertDispatched(\App\Events\MarriageAccepted::class);
|
||||
}
|
||||
|
||||
public function test_can_reject_proposal()
|
||||
{
|
||||
Event::fake();
|
||||
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
/** @var \App\Models\User $partner */
|
||||
$partner = User::factory()->create();
|
||||
|
||||
$marriage = Marriage::create(array_merge([
|
||||
'user_id' => $partner->id,
|
||||
'partner_id' => $user->id,
|
||||
'status' => 'pending',
|
||||
'proposed_at' => now(),
|
||||
], $this->createLegacyMarriageData()));
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('marriage.reject', ['marriage' => $marriage->id]));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['ok' => true]);
|
||||
|
||||
Event::assertDispatched(\App\Events\MarriageRejected::class);
|
||||
}
|
||||
|
||||
public function test_can_divorce_mutual()
|
||||
{
|
||||
Event::fake();
|
||||
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
/** @var \App\Models\User $partner */
|
||||
$partner = User::factory()->create();
|
||||
|
||||
$marriage = Marriage::create(array_merge([
|
||||
'user_id' => $user->id,
|
||||
'partner_id' => $partner->id,
|
||||
'status' => 'married',
|
||||
'married_at' => now()->subDays(10), // Needs to have marriage age
|
||||
], $this->createLegacyMarriageData()));
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('marriage.divorce', ['marriage' => $marriage->id]), [
|
||||
'type' => 'mutual',
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['ok' => true]);
|
||||
|
||||
Event::assertDispatched(\App\Events\MarriageDivorceRequested::class);
|
||||
}
|
||||
}
|
||||
165
tests/Feature/RedPacketControllerTest.php
Normal file
165
tests/Feature/RedPacketControllerTest.php
Normal file
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\RedPacketEnvelope;
|
||||
use App\Models\Sysparam;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use Tests\TestCase;
|
||||
|
||||
class RedPacketControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Redis::flushall();
|
||||
Sysparam::updateOrCreate(['alias' => 'superlevel'], ['body' => '100']);
|
||||
}
|
||||
|
||||
public function test_normal_user_cannot_send_red_packet()
|
||||
{
|
||||
$user = User::factory()->create(['user_level' => 10]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('command.red_packet.send'), [
|
||||
'room_id' => 1,
|
||||
'type' => 'gold',
|
||||
]);
|
||||
|
||||
$response->assertStatus(403);
|
||||
$response->assertJson(['status' => 'error']);
|
||||
}
|
||||
|
||||
public function test_superadmin_can_send_red_packet()
|
||||
{
|
||||
$admin = User::factory()->create(['user_level' => 100]);
|
||||
|
||||
$response = $this->actingAs($admin)->postJson(route('command.red_packet.send'), [
|
||||
'room_id' => 1,
|
||||
'type' => 'gold',
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['status' => 'success']);
|
||||
|
||||
$this->assertDatabaseHas('red_packet_envelopes', [
|
||||
'sender_id' => $admin->id,
|
||||
'room_id' => 1,
|
||||
'type' => 'gold',
|
||||
'status' => 'active',
|
||||
]);
|
||||
|
||||
$envelope = RedPacketEnvelope::first();
|
||||
// Check Redis for parts
|
||||
$this->assertEquals(10, Redis::llen("red_packet:{$envelope->id}:amounts"));
|
||||
}
|
||||
|
||||
public function test_cannot_send_multiple_active_packets_in_same_room()
|
||||
{
|
||||
$admin = User::factory()->create(['user_level' => 100]);
|
||||
|
||||
$this->actingAs($admin)->postJson(route('command.red_packet.send'), [
|
||||
'room_id' => 1,
|
||||
'type' => 'gold',
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($admin)->postJson(route('command.red_packet.send'), [
|
||||
'room_id' => 1,
|
||||
'type' => 'gold',
|
||||
]);
|
||||
|
||||
$response->assertStatus(422);
|
||||
}
|
||||
|
||||
public function test_user_can_claim_red_packet()
|
||||
{
|
||||
$admin = User::factory()->create(['user_level' => 100]);
|
||||
$user = User::factory()->create(['jjb' => 100]);
|
||||
|
||||
// Send packet
|
||||
$this->actingAs($admin)->postJson(route('command.red_packet.send'), [
|
||||
'room_id' => 1,
|
||||
'type' => 'gold',
|
||||
]);
|
||||
|
||||
$envelope = RedPacketEnvelope::first();
|
||||
|
||||
// Claim packet
|
||||
$response = $this->actingAs($user)->postJson(route('red_packet.claim', ['envelopeId' => $envelope->id]), [
|
||||
'room_id' => 1,
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['status' => 'success']);
|
||||
|
||||
$this->assertDatabaseHas('red_packet_claims', [
|
||||
'envelope_id' => $envelope->id,
|
||||
'user_id' => $user->id,
|
||||
]);
|
||||
|
||||
// Verify currency incremented
|
||||
$this->assertGreaterThan(100, $user->fresh()->jjb);
|
||||
}
|
||||
|
||||
public function test_user_cannot_claim_same_packet_twice()
|
||||
{
|
||||
$admin = User::factory()->create(['user_level' => 100]);
|
||||
$user = User::factory()->create();
|
||||
|
||||
$this->actingAs($admin)->postJson(route('command.red_packet.send'), [
|
||||
'room_id' => 1,
|
||||
'type' => 'gold',
|
||||
]);
|
||||
|
||||
$envelope = RedPacketEnvelope::first();
|
||||
|
||||
// First claim
|
||||
$this->actingAs($user)->postJson(route('red_packet.claim', ['envelopeId' => $envelope->id]), [
|
||||
'room_id' => 1,
|
||||
]);
|
||||
|
||||
// Second claim
|
||||
$response = $this->actingAs($user)->postJson(route('red_packet.claim', ['envelopeId' => $envelope->id]), [
|
||||
'room_id' => 1,
|
||||
]);
|
||||
|
||||
$response->assertStatus(422);
|
||||
$response->assertJson(['message' => '您已经领过这个礼包了']);
|
||||
}
|
||||
|
||||
public function test_can_check_packet_status()
|
||||
{
|
||||
$admin = User::factory()->create(['user_level' => 100]);
|
||||
$user = User::factory()->create();
|
||||
|
||||
$this->actingAs($admin)->postJson(route('command.red_packet.send'), [
|
||||
'room_id' => 1,
|
||||
'type' => 'gold',
|
||||
]);
|
||||
|
||||
$envelope = RedPacketEnvelope::first();
|
||||
|
||||
$response = $this->actingAs($user)->getJson(route('red_packet.status', ['envelopeId' => $envelope->id]));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson([
|
||||
'status' => 'success',
|
||||
'has_claimed' => false,
|
||||
'is_expired' => false,
|
||||
]);
|
||||
|
||||
// Claim it
|
||||
$this->actingAs($user)->postJson(route('red_packet.claim', ['envelopeId' => $envelope->id]), [
|
||||
'room_id' => 1,
|
||||
]);
|
||||
|
||||
$response2 = $this->actingAs($user)->getJson(route('red_packet.status', ['envelopeId' => $envelope->id]));
|
||||
$response2->assertJson([
|
||||
'status' => 'success',
|
||||
'has_claimed' => true,
|
||||
]);
|
||||
}
|
||||
}
|
||||
201
tests/Feature/RoomControllerTest.php
Normal file
201
tests/Feature/RoomControllerTest.php
Normal file
@@ -0,0 +1,201 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\Room;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class RoomControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_can_view_rooms_index()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
$room = Room::create([
|
||||
'room_name' => 'TestRoom',
|
||||
'room_owner' => $user->username,
|
||||
'room_keep' => false,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->get(route('rooms.index'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertSee('TestRoom');
|
||||
}
|
||||
|
||||
public function test_can_create_room_if_level_is_high_enough()
|
||||
{
|
||||
// Require level 10
|
||||
$user = User::factory()->create(['user_level' => 10]);
|
||||
|
||||
$response = $this->actingAs($user)->post(route('rooms.store'), [
|
||||
'name' => 'NewRoom',
|
||||
'description' => 'Test Description',
|
||||
]);
|
||||
|
||||
$response->assertRedirect(route('rooms.index'));
|
||||
$this->assertDatabaseHas('rooms', [
|
||||
'room_name' => 'NewRoom',
|
||||
'room_owner' => $user->username,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_cannot_create_room_if_level_too_low()
|
||||
{
|
||||
$user = User::factory()->create(['user_level' => 9]);
|
||||
|
||||
$response = $this->actingAs($user)->post(route('rooms.store'), [
|
||||
'name' => 'NewRoom',
|
||||
]);
|
||||
|
||||
$response->assertStatus(403);
|
||||
$this->assertDatabaseMissing('rooms', [
|
||||
'room_name' => 'NewRoom',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_room_owner_can_update_room()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
$room = Room::create([
|
||||
'room_name' => 'OldName',
|
||||
'room_owner' => $user->username,
|
||||
'room_keep' => false,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->put(route('rooms.update', $room->id), [
|
||||
'name' => 'NewName',
|
||||
]);
|
||||
|
||||
$response->assertRedirect();
|
||||
$response->assertSessionHas('success');
|
||||
$this->assertDatabaseHas('rooms', [
|
||||
'id' => $room->id,
|
||||
'room_name' => 'NewName',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_non_owner_cannot_update_room()
|
||||
{
|
||||
$owner = User::factory()->create(['user_level' => 1]);
|
||||
$attacker = User::factory()->create(['user_level' => 1]);
|
||||
$room = Room::create([
|
||||
'room_name' => 'OldName',
|
||||
'room_owner' => $owner->username,
|
||||
'room_keep' => false,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($attacker)->put(route('rooms.update', $room->id), [
|
||||
'name' => 'HackName',
|
||||
]);
|
||||
|
||||
$response->assertStatus(403);
|
||||
$this->assertDatabaseHas('rooms', [
|
||||
'id' => $room->id,
|
||||
'room_name' => 'OldName',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_admin_can_update_any_room()
|
||||
{
|
||||
$owner = User::factory()->create(['user_level' => 1]);
|
||||
$admin = User::factory()->create(['user_level' => 15]);
|
||||
$room = Room::create([
|
||||
'room_name' => 'OldName',
|
||||
'room_owner' => $owner->username,
|
||||
'room_keep' => false,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($admin)->put(route('rooms.update', $room->id), [
|
||||
'name' => 'AdminRoom',
|
||||
]);
|
||||
|
||||
$response->assertRedirect();
|
||||
$this->assertDatabaseHas('rooms', [
|
||||
'id' => $room->id,
|
||||
'room_name' => 'AdminRoom',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_room_owner_can_destroy_non_system_room()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
$room = Room::create([
|
||||
'room_name' => 'ToDelete',
|
||||
'room_owner' => $user->username,
|
||||
'room_keep' => false,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->delete(route('rooms.destroy', $room->id));
|
||||
|
||||
$response->assertRedirect(route('rooms.index'));
|
||||
$this->assertDatabaseMissing('rooms', [
|
||||
'id' => $room->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_cannot_destroy_system_room()
|
||||
{
|
||||
$user = User::factory()->create(['user_level' => 20]);
|
||||
$room = Room::create([
|
||||
'room_name' => 'SysRoom',
|
||||
'room_owner' => $user->username,
|
||||
'room_keep' => true,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->delete(route('rooms.destroy', $room->id));
|
||||
|
||||
$response->assertStatus(403);
|
||||
$this->assertDatabaseHas('rooms', [
|
||||
'id' => $room->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_room_owner_can_transfer_room()
|
||||
{
|
||||
$owner = User::factory()->create();
|
||||
$target = User::factory()->create();
|
||||
|
||||
$room = Room::create([
|
||||
'room_name' => 'TransferMe',
|
||||
'room_owner' => $owner->username,
|
||||
'room_keep' => false,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($owner)->post(route('rooms.transfer', $room->id), [
|
||||
'target_username' => $target->username,
|
||||
]);
|
||||
|
||||
$response->assertRedirect();
|
||||
$response->assertSessionHas('success');
|
||||
$this->assertDatabaseHas('rooms', [
|
||||
'id' => $room->id,
|
||||
'room_owner' => $target->username,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_cannot_transfer_to_invalid_user()
|
||||
{
|
||||
$owner = User::factory()->create();
|
||||
|
||||
$room = Room::create([
|
||||
'room_name' => 'TransferMe',
|
||||
'room_owner' => $owner->username,
|
||||
'room_keep' => false,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($owner)->post(route('rooms.transfer', $room->id), [
|
||||
'target_username' => 'ghost_user_999',
|
||||
]);
|
||||
|
||||
$response->assertRedirect();
|
||||
$response->assertSessionHas('error');
|
||||
$this->assertDatabaseHas('rooms', [
|
||||
'id' => $room->id,
|
||||
'room_owner' => $owner->username,
|
||||
]);
|
||||
}
|
||||
}
|
||||
169
tests/Feature/ShopControllerTest.php
Normal file
169
tests/Feature/ShopControllerTest.php
Normal file
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\ShopItem;
|
||||
use App\Models\User;
|
||||
use App\Models\UserPurchase;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ShopControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_items_returns_active_shop_items()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
|
||||
$activeItem = ShopItem::create([
|
||||
'name' => 'Active',
|
||||
'slug' => 'active_item',
|
||||
'type' => 'one_time',
|
||||
'price' => 100,
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$inactiveItem = ShopItem::create([
|
||||
'name' => 'Inactive',
|
||||
'slug' => 'inactive_item',
|
||||
'type' => 'one_time',
|
||||
'price' => 100,
|
||||
'is_active' => false,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->getJson(route('shop.items'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
$responseItems = collect($response->json('items'));
|
||||
$this->assertTrue($responseItems->contains('id', $activeItem->id));
|
||||
$this->assertFalse($responseItems->contains('id', $inactiveItem->id));
|
||||
}
|
||||
|
||||
public function test_can_buy_one_time_item()
|
||||
{
|
||||
$user = User::factory()->create(['jjb' => 500]);
|
||||
|
||||
$item = ShopItem::firstOrCreate(
|
||||
['slug' => 'rename_card_test'],
|
||||
[
|
||||
'name' => 'Rename Card Test',
|
||||
'type' => 'one_time',
|
||||
'price' => 100,
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('shop.buy'), [
|
||||
'item_id' => $item->id,
|
||||
'room_id' => 1,
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['status' => 'success']);
|
||||
|
||||
$this->assertDatabaseHas('user_purchases', [
|
||||
'user_id' => $user->id,
|
||||
'shop_item_id' => $item->id,
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('users', [
|
||||
'id' => $user->id,
|
||||
'jjb' => 400,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_cannot_buy_if_insufficient_funds()
|
||||
{
|
||||
$user = User::factory()->create(['jjb' => 50]);
|
||||
|
||||
$item = ShopItem::firstOrCreate(
|
||||
['slug' => 'rename_card_test'],
|
||||
[
|
||||
'name' => 'Rename Card Test',
|
||||
'type' => 'one_time',
|
||||
'price' => 100,
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('shop.buy'), [
|
||||
'item_id' => $item->id,
|
||||
]);
|
||||
|
||||
$response->assertStatus(400);
|
||||
$response->assertJson(['status' => 'error']);
|
||||
|
||||
$this->assertDatabaseMissing('user_purchases', [
|
||||
'user_id' => $user->id,
|
||||
'shop_item_id' => $item->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_cannot_buy_inactive_item()
|
||||
{
|
||||
$user = User::factory()->create(['jjb' => 500]);
|
||||
|
||||
$item = ShopItem::create([
|
||||
'name' => 'Old Card',
|
||||
'slug' => 'old_card',
|
||||
'type' => 'one_time',
|
||||
'price' => 100,
|
||||
'is_active' => false,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('shop.buy'), [
|
||||
'item_id' => $item->id,
|
||||
]);
|
||||
|
||||
$response->assertStatus(400);
|
||||
|
||||
$this->assertDatabaseMissing('user_purchases', [
|
||||
'user_id' => $user->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_can_use_rename_card()
|
||||
{
|
||||
$user = User::factory()->create(['username' => 'OldName']);
|
||||
|
||||
// Actually the service hardcodes 'rename_card' slug check: $item->slug === 'rename_card'
|
||||
// So we MUST use 'rename_card'
|
||||
$item = ShopItem::firstOrCreate(
|
||||
['slug' => 'rename_card'],
|
||||
[
|
||||
'name' => 'Rename Card',
|
||||
'type' => 'one_time',
|
||||
'price' => 100,
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
UserPurchase::create([
|
||||
'user_id' => $user->id,
|
||||
'shop_item_id' => $item->id,
|
||||
'status' => 'active',
|
||||
'used_at' => null,
|
||||
'cost_amount' => 100,
|
||||
'currency_type' => 'gold',
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('shop.rename'), [
|
||||
'new_name' => 'NewName',
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['status' => 'success']);
|
||||
|
||||
// Assert user's name is updated
|
||||
$this->assertDatabaseHas('users', [
|
||||
'id' => $user->id,
|
||||
'username' => 'NewName',
|
||||
]);
|
||||
|
||||
// Assert card is used
|
||||
$this->assertDatabaseHas('user_purchases', [
|
||||
'user_id' => $user->id,
|
||||
'shop_item_id' => $item->id,
|
||||
'status' => 'used',
|
||||
]);
|
||||
}
|
||||
}
|
||||
143
tests/Feature/SlotMachineControllerTest.php
Normal file
143
tests/Feature/SlotMachineControllerTest.php
Normal file
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\GameConfig;
|
||||
use App\Models\SlotMachineLog;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Tests\TestCase;
|
||||
|
||||
class SlotMachineControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
GameConfig::updateOrCreate(
|
||||
['game_key' => 'slot_machine'],
|
||||
[
|
||||
'name' => 'Slot Machine',
|
||||
'icon' => 'slot',
|
||||
'description' => 'Slot Machine Game',
|
||||
'enabled' => true,
|
||||
'params' => [
|
||||
'cost_per_spin' => 100,
|
||||
'daily_limit' => 100,
|
||||
'jackpot_payout' => 100,
|
||||
'triple_payout' => 50,
|
||||
'same_payout' => 10,
|
||||
'pair_payout' => 2,
|
||||
'curse_enabled' => true,
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function test_can_get_info()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
|
||||
$response = $this->actingAs($user)->getJson(route('slot.info'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson([
|
||||
'enabled' => true,
|
||||
'cost_per_spin' => 100,
|
||||
'daily_limit' => 100,
|
||||
'used_today' => 0,
|
||||
]);
|
||||
$response->assertJsonStructure(['symbols']);
|
||||
}
|
||||
|
||||
public function test_can_spin_enough_gold()
|
||||
{
|
||||
Event::fake();
|
||||
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create(['jjb' => 200]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('slot.spin'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['ok' => true]);
|
||||
|
||||
$this->assertDatabaseHas('slot_machine_logs', [
|
||||
'user_id' => $user->id,
|
||||
'cost' => 100,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_cannot_spin_without_enough_gold()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create(['jjb' => 50]); // Need 100
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('slot.spin'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['ok' => false]);
|
||||
}
|
||||
|
||||
public function test_cannot_spin_exceed_daily_limit()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create(['jjb' => 2000]);
|
||||
|
||||
// Mock daily limit 1
|
||||
GameConfig::updateOrCreate(
|
||||
['game_key' => 'slot_machine'],
|
||||
[
|
||||
'name' => 'Slot Machine',
|
||||
'icon' => 'slot',
|
||||
'description' => 'Slot Mac',
|
||||
'enabled' => true,
|
||||
'params' => [
|
||||
'cost_per_spin' => 100,
|
||||
'daily_limit' => 1,
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
SlotMachineLog::create([
|
||||
'user_id' => $user->id,
|
||||
'reel1' => 'cherry',
|
||||
'reel2' => 'lemon',
|
||||
'reel3' => 'orange',
|
||||
'result_type' => 'miss',
|
||||
'cost' => 100,
|
||||
'payout' => 0,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('slot.spin'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['ok' => false]);
|
||||
}
|
||||
|
||||
public function test_can_get_history()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
|
||||
SlotMachineLog::create([
|
||||
'user_id' => $user->id,
|
||||
'reel1' => 'cherry',
|
||||
'reel2' => 'cherry',
|
||||
'reel3' => 'cherry',
|
||||
'result_type' => 'triple',
|
||||
'cost' => 100,
|
||||
'payout' => 1000,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->getJson(route('slot.history'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonStructure(['history']);
|
||||
$this->assertCount(1, $response->json('history'));
|
||||
}
|
||||
}
|
||||
255
tests/Feature/UserControllerTest.php
Normal file
255
tests/Feature/UserControllerTest.php
Normal file
@@ -0,0 +1,255 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\Room;
|
||||
use App\Models\Sysparam;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use Tests\TestCase;
|
||||
|
||||
class UserControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
Sysparam::updateOrCreate(['alias' => 'superlevel'], ['body' => '100']);
|
||||
Sysparam::updateOrCreate(['alias' => 'level_kick'], ['body' => '15']);
|
||||
Sysparam::updateOrCreate(['alias' => 'level_mute'], ['body' => '15']);
|
||||
Sysparam::updateOrCreate(['alias' => 'level_ban'], ['body' => '15']);
|
||||
Sysparam::updateOrCreate(['alias' => 'level_banip'], ['body' => '15']);
|
||||
Sysparam::updateOrCreate(['alias' => 'smtp_enabled'], ['body' => '1']); // Allow email changing in tests
|
||||
}
|
||||
|
||||
public function test_can_view_user_profile()
|
||||
{
|
||||
$user = User::factory()->create([
|
||||
'username' => 'testuser',
|
||||
'user_level' => 10,
|
||||
]);
|
||||
|
||||
$this->actingAs($user);
|
||||
|
||||
$response = $this->getJson("/user/{$user->username}");
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonPath('data.username', 'testuser')
|
||||
->assertJsonPath('data.user_level', 10);
|
||||
}
|
||||
|
||||
public function test_can_update_profile_without_email_change()
|
||||
{
|
||||
$user = User::factory()->create([
|
||||
'username' => 'testuser',
|
||||
'email' => 'old@example.com',
|
||||
'sign' => 'old sign',
|
||||
]);
|
||||
|
||||
$this->actingAs($user);
|
||||
|
||||
$response = $this->putJson('/user/profile', [
|
||||
'email' => 'old@example.com',
|
||||
'sign' => 'new sign',
|
||||
'sex' => 1,
|
||||
'headface' => 'avatar1.png',
|
||||
]);
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonPath('status', 'success');
|
||||
|
||||
$user->refresh();
|
||||
$this->assertEquals('new sign', $user->sign);
|
||||
}
|
||||
|
||||
public function test_cannot_update_email_without_verification_code()
|
||||
{
|
||||
$user = User::factory()->create([
|
||||
'username' => 'testuser',
|
||||
'email' => 'old@example.com',
|
||||
]);
|
||||
|
||||
$this->actingAs($user);
|
||||
|
||||
$response = $this->putJson('/user/profile', [
|
||||
'email' => 'new@example.com',
|
||||
'sex' => 1,
|
||||
'headface' => 'avatar1.png',
|
||||
]);
|
||||
|
||||
$response->assertStatus(422)
|
||||
->assertJsonPath('status', 'error')
|
||||
->assertJsonPath('message', '新邮箱需要验证码,请先获取并填写验证码。');
|
||||
}
|
||||
|
||||
public function test_can_update_email_with_valid_code()
|
||||
{
|
||||
$user = User::factory()->create([
|
||||
'username' => 'testuser',
|
||||
'email' => 'old@example.com',
|
||||
]);
|
||||
|
||||
Cache::put("email_verify_code_{$user->id}_new@example.com", '123456', 5);
|
||||
|
||||
$this->actingAs($user);
|
||||
|
||||
$response = $this->putJson('/user/profile', [
|
||||
'email' => 'new@example.com',
|
||||
'email_code' => '123456',
|
||||
'sex' => 1,
|
||||
'headface' => 'avatar1.png',
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
$user->refresh();
|
||||
$this->assertEquals('new@example.com', $user->email);
|
||||
}
|
||||
|
||||
public function test_can_change_password()
|
||||
{
|
||||
$user = User::factory()->create([
|
||||
'username' => 'testuser',
|
||||
'password' => Hash::make('oldpassword'),
|
||||
]);
|
||||
|
||||
$this->actingAs($user);
|
||||
|
||||
$response = $this->putJson('/user/password', [
|
||||
'old_password' => 'oldpassword',
|
||||
'new_password' => 'newpassword123',
|
||||
'new_password_confirmation' => 'newpassword123',
|
||||
]);
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonPath('status', 'success');
|
||||
|
||||
$user->refresh();
|
||||
$this->assertTrue(Hash::check('newpassword123', $user->password));
|
||||
}
|
||||
|
||||
public function test_admin_can_kick_user()
|
||||
{
|
||||
$admin = User::factory()->create(['username' => 'admin', 'user_level' => 20]);
|
||||
$target = User::factory()->create(['username' => 'target', 'user_level' => 1]);
|
||||
$room = Room::create(['id' => 1, 'room_name' => 'Test Room', 'room_owner' => 'someone']);
|
||||
|
||||
$this->actingAs($admin);
|
||||
|
||||
$response = $this->postJson("/user/{$target->username}/kick", [
|
||||
'room_id' => $room->id,
|
||||
]);
|
||||
|
||||
$response->assertStatus(200)
|
||||
->assertJsonPath('status', 'success');
|
||||
}
|
||||
|
||||
public function test_low_level_user_cannot_kick()
|
||||
{
|
||||
$user = User::factory()->create(['username' => 'user', 'user_level' => 1]);
|
||||
$target = User::factory()->create(['username' => 'target', 'user_level' => 1]);
|
||||
$room = Room::create(['id' => 1, 'room_name' => 'Test Room', 'room_owner' => 'someone']);
|
||||
|
||||
$this->actingAs($user);
|
||||
|
||||
$response = $this->postJson("/user/{$target->username}/kick", [
|
||||
'room_id' => $room->id,
|
||||
]);
|
||||
|
||||
$response->assertStatus(403);
|
||||
}
|
||||
|
||||
public function test_room_master_can_kick()
|
||||
{
|
||||
$user = User::factory()->create(['username' => 'user', 'user_level' => 2]);
|
||||
$target = User::factory()->create(['username' => 'target', 'user_level' => 1]);
|
||||
$room = Room::create(['id' => 1, 'room_name' => 'Test Room', 'room_owner' => 'user']); // Master is 'user'
|
||||
|
||||
$this->actingAs($user);
|
||||
|
||||
$response = $this->postJson("/user/{$target->username}/kick", [
|
||||
'room_id' => $room->id,
|
||||
]);
|
||||
|
||||
if ($response->status() !== 200) {
|
||||
dump($response->json());
|
||||
}
|
||||
$response->assertStatus(200);
|
||||
}
|
||||
|
||||
public function test_cannot_kick_higher_level()
|
||||
{
|
||||
$admin = User::factory()->create(['username' => 'admin', 'user_level' => 20]);
|
||||
$superadmin = User::factory()->create(['username' => 'superadmin', 'user_level' => 100]);
|
||||
$room = Room::create(['id' => 1, 'room_name' => 'Test Room', 'room_owner' => 'someone']);
|
||||
|
||||
$this->actingAs($admin);
|
||||
|
||||
$response = $this->postJson("/user/{$superadmin->username}/kick", [
|
||||
'room_id' => $room->id,
|
||||
]);
|
||||
|
||||
$response->assertStatus(403);
|
||||
}
|
||||
|
||||
public function test_admin_can_mute_user()
|
||||
{
|
||||
$admin = User::factory()->create(['username' => 'admin', 'user_level' => 20]);
|
||||
$target = User::factory()->create(['username' => 'target', 'user_level' => 1]);
|
||||
$room = Room::create(['id' => 1, 'room_name' => 'Test Room', 'room_owner' => 'someone']);
|
||||
|
||||
Redis::shouldReceive('setex')->once();
|
||||
|
||||
$this->actingAs($admin);
|
||||
|
||||
$response = $this->postJson("/user/{$target->username}/mute", [
|
||||
'room_id' => $room->id,
|
||||
'duration' => 10,
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
}
|
||||
|
||||
public function test_admin_can_ban_user()
|
||||
{
|
||||
$admin = User::factory()->create(['username' => 'admin', 'user_level' => 20]);
|
||||
$target = User::factory()->create(['username' => 'target', 'user_level' => 1]);
|
||||
$room = Room::create(['id' => 1, 'room_name' => 'Test Room', 'room_owner' => 'someone']);
|
||||
|
||||
$this->actingAs($admin);
|
||||
|
||||
$response = $this->postJson("/user/{$target->username}/ban", [
|
||||
'room_id' => $room->id,
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
$target->refresh();
|
||||
$this->assertEquals(-1, $target->user_level);
|
||||
}
|
||||
|
||||
public function test_admin_can_ban_ip()
|
||||
{
|
||||
$admin = User::factory()->create(['username' => 'admin', 'user_level' => 20]);
|
||||
$target = User::factory()->create(['username' => 'target', 'user_level' => 1, 'last_ip' => '192.168.1.100']);
|
||||
$room = Room::create(['id' => 1, 'room_name' => 'Test Room', 'room_owner' => 'someone']);
|
||||
|
||||
Redis::shouldReceive('sadd')->with('banned_ips', '192.168.1.100')->once();
|
||||
|
||||
$this->actingAs($admin);
|
||||
|
||||
$response = $this->postJson("/user/{$target->username}/banip", [
|
||||
'room_id' => $room->id,
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
$target->refresh();
|
||||
$this->assertEquals(-1, $target->user_level);
|
||||
}
|
||||
}
|
||||
244
tests/Feature/WeddingControllerTest.php
Normal file
244
tests/Feature/WeddingControllerTest.php
Normal file
@@ -0,0 +1,244 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\Marriage;
|
||||
use App\Models\User;
|
||||
use App\Models\WeddingCeremony;
|
||||
use App\Models\WeddingEnvelopeClaim;
|
||||
use App\Models\WeddingTier;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Tests\TestCase;
|
||||
|
||||
class WeddingControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
private function createLegacyMarriageData()
|
||||
{
|
||||
return [
|
||||
'hyname' => 'test',
|
||||
'hyname1' => 'test1',
|
||||
'hytime' => now(),
|
||||
'hygb' => 'test',
|
||||
'hyjb' => 'test',
|
||||
'i' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
public function test_can_get_tiers()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
|
||||
WeddingTier::forceCreate([
|
||||
'tier' => 1,
|
||||
'name' => 'Basic Wedding',
|
||||
'icon' => 'basic',
|
||||
'amount' => 1000,
|
||||
'description' => 'A basic wedding',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->getJson(route('wedding.tiers'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonStructure(['tiers']);
|
||||
$this->assertCount(1, $response->json('tiers'));
|
||||
}
|
||||
|
||||
public function test_can_setup_wedding()
|
||||
{
|
||||
Event::fake();
|
||||
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create(['jjb' => 10000]);
|
||||
/** @var \App\Models\User $partner */
|
||||
$partner = User::factory()->create();
|
||||
|
||||
$marriage = Marriage::create(array_merge([
|
||||
'user_id' => $user->id,
|
||||
'partner_id' => $partner->id,
|
||||
'status' => 'married',
|
||||
'married_at' => now(),
|
||||
], $this->createLegacyMarriageData()));
|
||||
|
||||
$tier = WeddingTier::forceCreate([
|
||||
'tier' => 1,
|
||||
'name' => 'Basic Wedding',
|
||||
'icon' => 'basic',
|
||||
'amount' => 1000,
|
||||
'description' => 'A basic wedding',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('wedding.setup', ['marriage' => $marriage->id]), [
|
||||
'tier_id' => $tier->id,
|
||||
'payer_type' => 'groom',
|
||||
]);
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['ok' => true]);
|
||||
|
||||
Event::assertDispatched(\App\Events\WeddingCelebration::class);
|
||||
}
|
||||
|
||||
public function test_can_claim_envelope()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create(['jjb' => 0]);
|
||||
/** @var \App\Models\User $groom */
|
||||
$groom = User::factory()->create();
|
||||
/** @var \App\Models\User $bride */
|
||||
$bride = User::factory()->create();
|
||||
|
||||
$marriage = Marriage::create(array_merge([
|
||||
'user_id' => $groom->id,
|
||||
'partner_id' => $bride->id,
|
||||
'status' => 'married',
|
||||
'married_at' => now(),
|
||||
], $this->createLegacyMarriageData()));
|
||||
|
||||
$tier = WeddingTier::forceCreate([
|
||||
'tier' => 1,
|
||||
'name' => 'Basic Wedding',
|
||||
'icon' => 'basic',
|
||||
'amount' => 1000,
|
||||
'description' => 'A basic wedding',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$ceremony = WeddingCeremony::forceCreate([
|
||||
'marriage_id' => $marriage->id,
|
||||
'tier_id' => $tier->id,
|
||||
'total_amount' => 1000,
|
||||
'payer_type' => 'groom',
|
||||
'groom_amount' => 1000,
|
||||
'partner_amount' => 0,
|
||||
'ceremony_type' => 'immediate',
|
||||
'status' => 'active',
|
||||
'expires_at' => now()->addHour(),
|
||||
]);
|
||||
|
||||
// Mock existing envelope claim waiting to be claimed
|
||||
WeddingEnvelopeClaim::forceCreate([
|
||||
'ceremony_id' => $ceremony->id,
|
||||
'user_id' => $user->id,
|
||||
'amount' => 100,
|
||||
'claimed' => false,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('wedding.claim', ['ceremony' => $ceremony->id]));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson(['ok' => true]);
|
||||
|
||||
$this->assertEquals(100, $user->fresh()->jjb);
|
||||
}
|
||||
|
||||
public function test_can_get_envelope_status()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
/** @var \App\Models\User $groom */
|
||||
$groom = User::factory()->create();
|
||||
/** @var \App\Models\User $bride */
|
||||
$bride = User::factory()->create();
|
||||
|
||||
$marriage = Marriage::create(array_merge([
|
||||
'user_id' => $groom->id,
|
||||
'partner_id' => $bride->id,
|
||||
'status' => 'married',
|
||||
'married_at' => now(),
|
||||
], $this->createLegacyMarriageData()));
|
||||
|
||||
$tier = WeddingTier::forceCreate([
|
||||
'tier' => 1,
|
||||
'name' => 'Basic',
|
||||
'icon' => 'basic',
|
||||
'amount' => 1000,
|
||||
'description' => 'basic',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$ceremony = WeddingCeremony::forceCreate([
|
||||
'marriage_id' => $marriage->id,
|
||||
'tier_id' => $tier->id,
|
||||
'total_amount' => 1000,
|
||||
'payer_type' => 'groom',
|
||||
'groom_amount' => 1000,
|
||||
'partner_amount' => 0,
|
||||
'ceremony_type' => 'immediate',
|
||||
'status' => 'active',
|
||||
'expires_at' => now()->addHour(),
|
||||
]);
|
||||
|
||||
WeddingEnvelopeClaim::forceCreate([
|
||||
'ceremony_id' => $ceremony->id,
|
||||
'user_id' => $user->id,
|
||||
'amount' => 100,
|
||||
'claimed' => false,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->getJson(route('wedding.envelope-status', ['ceremony' => $ceremony->id]));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJson([
|
||||
'has_envelope' => true,
|
||||
'amount' => 100,
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_can_get_pending_envelopes()
|
||||
{
|
||||
/** @var \App\Models\User $user */
|
||||
$user = User::factory()->create();
|
||||
/** @var \App\Models\User $groom */
|
||||
$groom = User::factory()->create();
|
||||
/** @var \App\Models\User $bride */
|
||||
$bride = User::factory()->create();
|
||||
|
||||
$marriage = Marriage::create(array_merge([
|
||||
'user_id' => $groom->id,
|
||||
'partner_id' => $bride->id,
|
||||
'status' => 'married',
|
||||
'married_at' => now(),
|
||||
], $this->createLegacyMarriageData()));
|
||||
|
||||
$tier = WeddingTier::forceCreate([
|
||||
'tier' => 1,
|
||||
'name' => 'Basic',
|
||||
'icon' => 'basic',
|
||||
'amount' => 1000,
|
||||
'description' => 'basic',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$ceremony = WeddingCeremony::forceCreate([
|
||||
'marriage_id' => $marriage->id,
|
||||
'tier_id' => $tier->id,
|
||||
'total_amount' => 1000,
|
||||
'payer_type' => 'groom',
|
||||
'groom_amount' => 1000,
|
||||
'partner_amount' => 0,
|
||||
'ceremony_type' => 'immediate',
|
||||
'status' => 'active',
|
||||
'expires_at' => now()->addHour(),
|
||||
]);
|
||||
|
||||
WeddingEnvelopeClaim::forceCreate([
|
||||
'ceremony_id' => $ceremony->id,
|
||||
'user_id' => $user->id,
|
||||
'amount' => 100,
|
||||
'claimed' => false,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->getJson(route('wedding.pending-envelopes'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertJsonStructure(['envelopes']);
|
||||
$this->assertCount(1, $response->json('envelopes'));
|
||||
$this->assertEquals($ceremony->id, $response->json('envelopes.0.ceremony_id'));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user