新增每日签到与补签卡功能
This commit is contained in:
@@ -0,0 +1,416 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 文件功能:每日签到前台接口测试
|
||||
*
|
||||
* 覆盖签到领取、重复拦截、连续天数、身份有效期与聊天室播报。
|
||||
*/
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Enums\CurrencySource;
|
||||
use App\Events\MessageSent;
|
||||
use App\Events\UserStatusUpdated;
|
||||
use App\Models\DailySignIn;
|
||||
use App\Models\Room;
|
||||
use App\Models\ShopItem;
|
||||
use App\Models\SignInRewardRule;
|
||||
use App\Models\User;
|
||||
use App\Models\UserPurchase;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* 类功能:验证每日签到前台接口的核心行为。
|
||||
*/
|
||||
class DailySignInControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
/**
|
||||
* 方法功能:每个用例前清理 Redis 与时间,避免跨测试污染。
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Redis::flushall();
|
||||
Carbon::setTestNow('2026-04-24 10:00:00');
|
||||
}
|
||||
|
||||
/**
|
||||
* 方法功能:每个用例后恢复系统时间。
|
||||
*/
|
||||
protected function tearDown(): void
|
||||
{
|
||||
Carbon::setTestNow();
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试首次签到会发放奖励、写入流水并生成身份。
|
||||
*/
|
||||
public function test_user_can_claim_daily_sign_in_reward(): void
|
||||
{
|
||||
$user = User::factory()->create(['jjb' => 10, 'exp_num' => 0, 'meili' => 0]);
|
||||
SignInRewardRule::query()->updateOrCreate(['streak_days' => 1], [
|
||||
'streak_days' => 1,
|
||||
'gold_reward' => 100,
|
||||
'exp_reward' => 20,
|
||||
'charm_reward' => 3,
|
||||
'identity_badge_code' => 'sign_1',
|
||||
'identity_badge_name' => '签到新星',
|
||||
'identity_badge_icon' => '⭐',
|
||||
'identity_badge_color' => '#0f766e',
|
||||
'identity_duration_days' => 7,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('daily-sign-in.claim'));
|
||||
|
||||
$response->assertOk()
|
||||
->assertJsonPath('status', 'success')
|
||||
->assertJsonPath('data.sign_in.streak_days', 1)
|
||||
->assertJsonPath('data.user.jjb', 110)
|
||||
->assertJsonPath('data.identity.label', '签到新星');
|
||||
|
||||
$this->assertDatabaseHas('daily_sign_ins', [
|
||||
'user_id' => $user->id,
|
||||
'sign_in_date' => '2026-04-24',
|
||||
'streak_days' => 1,
|
||||
'gold_reward' => 100,
|
||||
'exp_reward' => 20,
|
||||
'charm_reward' => 3,
|
||||
]);
|
||||
$this->assertDatabaseHas('user_currency_logs', [
|
||||
'user_id' => $user->id,
|
||||
'currency' => 'gold',
|
||||
'amount' => 100,
|
||||
'source' => CurrencySource::SIGN_IN->value,
|
||||
]);
|
||||
$this->assertDatabaseHas('user_identity_badges', [
|
||||
'user_id' => $user->id,
|
||||
'source' => 'sign_in',
|
||||
'badge_code' => 'sign_1',
|
||||
'is_active' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试同一天重复签到不会重复发放奖励。
|
||||
*/
|
||||
public function test_duplicate_daily_sign_in_is_rejected_without_second_reward(): void
|
||||
{
|
||||
$user = User::factory()->create(['jjb' => 0]);
|
||||
SignInRewardRule::query()->updateOrCreate(['streak_days' => 1], [
|
||||
'streak_days' => 1,
|
||||
'gold_reward' => 50,
|
||||
]);
|
||||
|
||||
$this->actingAs($user)->postJson(route('daily-sign-in.claim'))->assertOk();
|
||||
$currencyLogCount = \App\Models\UserCurrencyLog::query()
|
||||
->where('user_id', $user->id)
|
||||
->where('source', CurrencySource::SIGN_IN->value)
|
||||
->count();
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('daily-sign-in.claim'));
|
||||
|
||||
$response->assertStatus(422)
|
||||
->assertJsonPath('status', 'error')
|
||||
->assertJsonPath('message', '今日已签到,请明天再来。');
|
||||
|
||||
$this->assertSame(1, DailySignIn::query()->where('user_id', $user->id)->count());
|
||||
$this->assertSame($currencyLogCount, \App\Models\UserCurrencyLog::query()->where('user_id', $user->id)->where('source', CurrencySource::SIGN_IN->value)->count());
|
||||
$this->assertSame(50, (int) $user->fresh()->jjb);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试连续签到会递增,断签后会重新从 1 开始。
|
||||
*/
|
||||
public function test_streak_increments_and_resets_after_gap(): void
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
SignInRewardRule::query()->updateOrCreate(['streak_days' => 1], ['streak_days' => 1, 'gold_reward' => 1]);
|
||||
SignInRewardRule::query()->updateOrCreate(['streak_days' => 2], ['streak_days' => 2, 'gold_reward' => 2]);
|
||||
|
||||
$this->actingAs($user)->postJson(route('daily-sign-in.claim'))->assertJsonPath('data.sign_in.streak_days', 1);
|
||||
|
||||
Carbon::setTestNow('2026-04-25 10:00:00');
|
||||
$this->actingAs($user)->postJson(route('daily-sign-in.claim'))->assertJsonPath('data.sign_in.streak_days', 2);
|
||||
|
||||
Carbon::setTestNow('2026-04-27 10:00:00');
|
||||
$this->actingAs($user)->postJson(route('daily-sign-in.claim'))->assertJsonPath('data.sign_in.streak_days', 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试连续签到满一年后下一天会重新从 1 计算。
|
||||
*/
|
||||
public function test_streak_resets_after_yearly_cycle_is_completed(): void
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
DailySignIn::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'sign_in_date' => '2026-04-23',
|
||||
'streak_days' => 365,
|
||||
]);
|
||||
|
||||
$this->actingAs($user)
|
||||
->postJson(route('daily-sign-in.claim'))
|
||||
->assertOk()
|
||||
->assertJsonPath('data.sign_in.streak_days', 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试闰年需要满 366 天后才重新计算。
|
||||
*/
|
||||
public function test_leap_year_streak_resets_after_366_days(): void
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
Carbon::setTestNow('2024-12-31 10:00:00');
|
||||
DailySignIn::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'sign_in_date' => '2024-12-30',
|
||||
'streak_days' => 365,
|
||||
]);
|
||||
|
||||
$this->actingAs($user)
|
||||
->postJson(route('daily-sign-in.claim'))
|
||||
->assertOk()
|
||||
->assertJsonPath('data.sign_in.streak_days', 366);
|
||||
|
||||
Carbon::setTestNow('2025-01-01 10:00:00');
|
||||
$this->actingAs($user)
|
||||
->postJson(route('daily-sign-in.claim'))
|
||||
->assertOk()
|
||||
->assertJsonPath('data.sign_in.streak_days', 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试身份有效期过后不会继续出现在状态接口。
|
||||
*/
|
||||
public function test_expired_identity_is_not_returned_in_status(): void
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
SignInRewardRule::query()->updateOrCreate(['streak_days' => 1], [
|
||||
'streak_days' => 1,
|
||||
'identity_badge_code' => 'short',
|
||||
'identity_badge_name' => '短期身份',
|
||||
'identity_badge_icon' => '⏳',
|
||||
'identity_duration_days' => 1,
|
||||
]);
|
||||
|
||||
$this->actingAs($user)->postJson(route('daily-sign-in.claim'))->assertOk();
|
||||
|
||||
Carbon::setTestNow('2026-04-26 10:00:00');
|
||||
$this->actingAs($user)
|
||||
->getJson(route('daily-sign-in.status'))
|
||||
->assertOk()
|
||||
->assertJsonPath('data.identity', null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试当前房间签到会写入聊天室通知并附带快速签到按钮。
|
||||
*/
|
||||
public function test_claim_with_room_broadcasts_chat_notice_with_quick_button(): void
|
||||
{
|
||||
Event::fake([MessageSent::class, UserStatusUpdated::class]);
|
||||
|
||||
$room = Room::create(['room_name' => 'testroom', 'door_open' => true]);
|
||||
$user = User::factory()->create(['jjb' => 0]);
|
||||
SignInRewardRule::query()->updateOrCreate(['streak_days' => 1], [
|
||||
'streak_days' => 1,
|
||||
'gold_reward' => 100,
|
||||
'exp_reward' => 20,
|
||||
]);
|
||||
|
||||
Redis::hset("room:{$room->id}:users", $user->username, json_encode(['username' => $user->username], JSON_UNESCAPED_UNICODE));
|
||||
Redis::setex("room:{$room->id}:alive:{$user->username}", 90, 1);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('daily-sign-in.claim'), [
|
||||
'room_id' => $room->id,
|
||||
]);
|
||||
|
||||
$response->assertOk();
|
||||
|
||||
$rawMessage = Redis::lindex("room:{$room->id}:messages", -1);
|
||||
$message = json_decode((string) $rawMessage, true);
|
||||
$this->assertSame('签到播报', $message['from_user']);
|
||||
$this->assertStringContainsString('快速签到', $message['content']);
|
||||
$this->assertStringContainsString('quickDailySignIn', $message['content']);
|
||||
|
||||
Event::assertDispatched(MessageSent::class, function (MessageSent $event) use ($room): bool {
|
||||
return $event->roomId === $room->id
|
||||
&& ($event->message['from_user'] ?? null) === '签到播报';
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试不携带房间 ID 时不会发送聊天室通知。
|
||||
*/
|
||||
public function test_claim_without_room_does_not_write_chat_notice(): void
|
||||
{
|
||||
Event::fake([MessageSent::class]);
|
||||
|
||||
$user = User::factory()->create();
|
||||
SignInRewardRule::query()->updateOrCreate(['streak_days' => 1], ['streak_days' => 1, 'gold_reward' => 1]);
|
||||
|
||||
$this->actingAs($user)->postJson(route('daily-sign-in.claim'))->assertOk();
|
||||
|
||||
$this->assertSame([], Redis::keys('room:*:messages'));
|
||||
Event::assertNotDispatched(MessageSent::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试签到日历会展示已签、漏签和补签卡数量。
|
||||
*/
|
||||
public function test_calendar_returns_month_days_and_makeup_card_count(): void
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
$card = ShopItem::query()->where('slug', 'sign_repair_card')->firstOrFail();
|
||||
UserPurchase::query()->create([
|
||||
'user_id' => $user->id,
|
||||
'shop_item_id' => $card->id,
|
||||
'status' => 'active',
|
||||
'price_paid' => 1200,
|
||||
]);
|
||||
DailySignIn::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'sign_in_date' => '2026-04-23',
|
||||
'streak_days' => 1,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->getJson(route('daily-sign-in.calendar', ['month' => '2026-04']));
|
||||
|
||||
$response->assertOk()
|
||||
->assertJsonPath('data.month', '2026-04')
|
||||
->assertJsonPath('data.makeup_card_count', 1)
|
||||
->assertJsonPath('data.sign_repair_card_item.slug', 'sign_repair_card')
|
||||
->assertJsonPath('data.sign_repair_card_item.price', 10000)
|
||||
->assertJsonPath('data.reward_rules.0.streak_days', 1);
|
||||
|
||||
$days = collect($response->json('data.days'));
|
||||
$this->assertTrue((bool) $days->firstWhere('date', '2026-04-23')['signed']);
|
||||
$this->assertTrue((bool) $days->firstWhere('date', '2026-04-22')['can_makeup']);
|
||||
|
||||
$previousMonthResponse = $this->actingAs($user)->getJson(route('daily-sign-in.calendar', ['month' => '2026-03']));
|
||||
$previousMonthDays = collect($previousMonthResponse->json('data.days'));
|
||||
$this->assertFalse((bool) $previousMonthDays->firstWhere('date', '2026-03-31')['can_makeup']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试用户可以消耗补签卡补签历史漏签日期。
|
||||
*/
|
||||
public function test_user_can_makeup_missed_day_with_sign_repair_card(): void
|
||||
{
|
||||
$user = User::factory()->create(['jjb' => 0]);
|
||||
SignInRewardRule::query()->updateOrCreate(['streak_days' => 1], [
|
||||
'streak_days' => 1,
|
||||
'gold_reward' => 10,
|
||||
]);
|
||||
SignInRewardRule::query()->updateOrCreate(['streak_days' => 2], [
|
||||
'streak_days' => 2,
|
||||
'gold_reward' => 20,
|
||||
]);
|
||||
SignInRewardRule::query()->updateOrCreate(['streak_days' => 3], [
|
||||
'streak_days' => 3,
|
||||
'gold_reward' => 30,
|
||||
]);
|
||||
$card = ShopItem::query()->where('slug', 'sign_repair_card')->firstOrFail();
|
||||
$purchase = UserPurchase::query()->create([
|
||||
'user_id' => $user->id,
|
||||
'shop_item_id' => $card->id,
|
||||
'status' => 'active',
|
||||
'price_paid' => 1200,
|
||||
]);
|
||||
DailySignIn::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'sign_in_date' => '2026-04-22',
|
||||
'streak_days' => 1,
|
||||
]);
|
||||
DailySignIn::factory()->create([
|
||||
'user_id' => $user->id,
|
||||
'sign_in_date' => '2026-04-24',
|
||||
'streak_days' => 1,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('daily-sign-in.makeup'), [
|
||||
'target_date' => '2026-04-23',
|
||||
]);
|
||||
|
||||
$response->assertOk()
|
||||
->assertJsonPath('status', 'success')
|
||||
->assertJsonPath('data.sign_in.is_makeup', true)
|
||||
->assertJsonPath('data.sign_in.streak_days', 2)
|
||||
->assertJsonPath('data.current_streak_days', 3);
|
||||
|
||||
$this->assertStringContainsString('当前连续签到 3 天', (string) $response->json('message'));
|
||||
|
||||
$this->assertDatabaseHas('daily_sign_ins', [
|
||||
'user_id' => $user->id,
|
||||
'sign_in_date' => '2026-04-23',
|
||||
'is_makeup' => true,
|
||||
'makeup_purchase_id' => $purchase->id,
|
||||
'streak_days' => 2,
|
||||
'gold_reward' => 30,
|
||||
]);
|
||||
$this->assertDatabaseHas('daily_sign_ins', [
|
||||
'user_id' => $user->id,
|
||||
'sign_in_date' => '2026-04-24',
|
||||
'streak_days' => 3,
|
||||
]);
|
||||
$this->assertSame('used', $purchase->fresh()->status);
|
||||
$this->assertSame(30, (int) $user->fresh()->jjb);
|
||||
$this->assertDatabaseHas('user_currency_logs', [
|
||||
'user_id' => $user->id,
|
||||
'currency' => 'gold',
|
||||
'amount' => 30,
|
||||
'source' => CurrencySource::SIGN_IN->value,
|
||||
'remark' => '补签 2026-04-23:当前连续签到 3 天',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试没有补签卡时不能补签。
|
||||
*/
|
||||
public function test_makeup_requires_available_sign_repair_card(): void
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('daily-sign-in.makeup'), [
|
||||
'target_date' => '2026-04-23',
|
||||
]);
|
||||
|
||||
$response->assertStatus(422)
|
||||
->assertJsonValidationErrors('target_date');
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试补签卡不能补签本月之外的漏签日期。
|
||||
*/
|
||||
public function test_makeup_only_allows_current_month_missed_days(): void
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
$card = ShopItem::query()->where('slug', 'sign_repair_card')->firstOrFail();
|
||||
$purchase = UserPurchase::query()->create([
|
||||
'user_id' => $user->id,
|
||||
'shop_item_id' => $card->id,
|
||||
'status' => 'active',
|
||||
'price_paid' => 1200,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('daily-sign-in.makeup'), [
|
||||
'target_date' => '2026-03-31',
|
||||
]);
|
||||
|
||||
$response->assertStatus(422)
|
||||
->assertJsonValidationErrors('target_date')
|
||||
->assertJsonPath('errors.target_date.0', '补签卡只能补签本月的未签到日期。');
|
||||
|
||||
$this->assertSame('active', $purchase->fresh()->status);
|
||||
$this->assertDatabaseMissing('daily_sign_ins', [
|
||||
'user_id' => $user->id,
|
||||
'sign_in_date' => '2026-03-31',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 文件功能:后台签到奖励规则管理测试
|
||||
*
|
||||
* 覆盖后台连续签到奖励档位的创建、更新、启停与校验。
|
||||
*/
|
||||
|
||||
namespace Tests\Feature\Feature;
|
||||
|
||||
use App\Models\SignInRewardRule;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* 类功能:验证后台签到奖励规则管理页行为。
|
||||
*/
|
||||
class AdminSignInRewardRuleControllerTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
/**
|
||||
* 测试管理员可以创建签到奖励规则。
|
||||
*/
|
||||
public function test_admin_can_create_sign_in_reward_rule(): void
|
||||
{
|
||||
$admin = $this->createSuperAdmin();
|
||||
|
||||
$response = $this->actingAs($admin)->post(route('admin.sign-in-rules.store'), [
|
||||
'streak_days' => 11,
|
||||
'gold_reward' => 700,
|
||||
'exp_reward' => 70,
|
||||
'charm_reward' => 7,
|
||||
'identity_badge_code' => 'sign_11',
|
||||
'identity_badge_name' => '十一日星辉',
|
||||
'identity_badge_icon' => '🔥',
|
||||
'identity_badge_color' => '#0f766e',
|
||||
'identity_duration_days' => 30,
|
||||
'sort_order' => 11,
|
||||
'is_enabled' => '1',
|
||||
]);
|
||||
|
||||
$response->assertRedirect(route('admin.sign-in-rules.index'));
|
||||
$this->assertDatabaseHas('sign_in_reward_rules', [
|
||||
'streak_days' => 11,
|
||||
'gold_reward' => 700,
|
||||
'identity_badge_code' => 'sign_11',
|
||||
'identity_duration_days' => 30,
|
||||
'is_enabled' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试管理员可以更新并停用签到奖励规则。
|
||||
*/
|
||||
public function test_admin_can_update_and_toggle_sign_in_reward_rule(): void
|
||||
{
|
||||
$admin = $this->createSuperAdmin();
|
||||
$rule = SignInRewardRule::query()->where('streak_days', 3)->firstOrFail();
|
||||
$rule->update(['gold_reward' => 100, 'is_enabled' => true]);
|
||||
|
||||
$this->actingAs($admin)->put(route('admin.sign-in-rules.update', $rule), [
|
||||
'streak_days' => 5,
|
||||
'gold_reward' => 500,
|
||||
'exp_reward' => 50,
|
||||
'charm_reward' => 5,
|
||||
'identity_badge_code' => 'sign_5',
|
||||
'identity_badge_name' => '五日之星',
|
||||
'identity_badge_icon' => '⭐',
|
||||
'identity_badge_color' => '#4338ca',
|
||||
'identity_duration_days' => 10,
|
||||
'sort_order' => 5,
|
||||
'is_enabled' => '1',
|
||||
])->assertRedirect(route('admin.sign-in-rules.index'));
|
||||
|
||||
$this->assertDatabaseHas('sign_in_reward_rules', [
|
||||
'id' => $rule->id,
|
||||
'streak_days' => 5,
|
||||
'gold_reward' => 500,
|
||||
'identity_badge_name' => '五日之星',
|
||||
]);
|
||||
|
||||
$this->actingAs($admin)
|
||||
->postJson(route('admin.sign-in-rules.toggle', $rule))
|
||||
->assertOk()
|
||||
->assertJsonPath('is_enabled', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试重复连续天数会被校验拦截。
|
||||
*/
|
||||
public function test_duplicate_streak_days_are_rejected(): void
|
||||
{
|
||||
$admin = $this->createSuperAdmin();
|
||||
$this->actingAs($admin)->post(route('admin.sign-in-rules.store'), [
|
||||
'streak_days' => 7,
|
||||
'gold_reward' => 100,
|
||||
'exp_reward' => 10,
|
||||
'charm_reward' => 0,
|
||||
'identity_duration_days' => 0,
|
||||
'sort_order' => 0,
|
||||
])->assertSessionHasErrors('streak_days');
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建可访问后台的超级管理员账号。
|
||||
*/
|
||||
private function createSuperAdmin(): User
|
||||
{
|
||||
return User::factory()->create([
|
||||
'user_level' => 100,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -300,4 +300,62 @@ class ShopControllerTest extends TestCase
|
||||
&& $event->broadcastWith()['operator'] === $buyer->username;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试购买签到补签卡会扣金币并生成可用背包记录。
|
||||
*/
|
||||
public function test_buy_sign_repair_card_creates_active_purchase(): void
|
||||
{
|
||||
$user = User::factory()->create(['jjb' => 12000]);
|
||||
$item = ShopItem::query()->where('slug', 'sign_repair_card')->firstOrFail();
|
||||
$item->update([
|
||||
'price' => 10000,
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('shop.buy'), [
|
||||
'item_id' => $item->id,
|
||||
]);
|
||||
|
||||
$response->assertOk()
|
||||
->assertJsonPath('status', 'success')
|
||||
->assertJsonPath('jjb', 2000);
|
||||
|
||||
$this->assertDatabaseHas('user_purchases', [
|
||||
'user_id' => $user->id,
|
||||
'shop_item_id' => $item->id,
|
||||
'status' => 'active',
|
||||
'price_paid' => 10000,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试补签卡支持一次购买多张。
|
||||
*/
|
||||
public function test_buy_sign_repair_card_supports_quantity(): void
|
||||
{
|
||||
$user = User::factory()->create(['jjb' => 35000]);
|
||||
$item = ShopItem::query()->where('slug', 'sign_repair_card')->firstOrFail();
|
||||
$item->update([
|
||||
'price' => 10000,
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($user)->postJson(route('shop.buy'), [
|
||||
'item_id' => $item->id,
|
||||
'quantity' => 3,
|
||||
]);
|
||||
|
||||
$response->assertOk()
|
||||
->assertJsonPath('status', 'success')
|
||||
->assertJsonPath('quantity', 3)
|
||||
->assertJsonPath('total_price', 30000)
|
||||
->assertJsonPath('jjb', 5000);
|
||||
|
||||
$this->assertSame(3, UserPurchase::query()
|
||||
->where('user_id', $user->id)
|
||||
->where('shop_item_id', $item->id)
|
||||
->where('status', 'active')
|
||||
->count());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user