| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259 |
- <?php
- namespace Tests\Unit;
- use App\Models\UserCoupon;
- use App\Services\CouponService;
- use Tests\TestCase;
- use Illuminate\Support\Facades\DB;
- /**
- * 优惠券服务单元测试
- *
- * 测试 CouponService 的核心业务逻辑:
- * - 优惠券发放条件判断
- * - 优惠券验证
- * - 金币计算
- * - 优惠券使用
- */
- class CouponServiceTest extends TestCase
- {
- /** @var CouponService */
- protected $service;
- /** @var int 测试用户ID */
- protected $testUserId = 999999;
- public function setUp(): void
- {
- parent::setUp();
- $this->service = new CouponService();
- // 清理测试数据
- DB::connection('write')->table(UserCoupon::TABLE)
- ->where('user_id', $this->testUserId)
- ->delete();
- }
- public function tearDown(): void
- {
- // 清理测试数据
- DB::connection('write')->table(UserCoupon::TABLE)
- ->where('user_id', $this->testUserId)
- ->delete();
- parent::tearDown();
- }
- // ========================
- // 固定金额券 - 金币计算
- // ========================
- /**
- * 测试固定金额券:赠送金额等于券面值
- */
- public function test_calc_bonus_coins_fixed_type()
- {
- $coupon = (object) [
- 'coupon_type' => UserCoupon::TYPE_FIXED,
- 'coupon_value' => 10.00, // 10元
- 'max_bonus' => 0,
- ];
- $bonus = $this->service->calcBonusCoins($coupon, 50.00);
- $this->assertEquals(1000, $bonus); // 10元 × 100 = 1000分
- }
- /**
- * 测试固定金额券:不同充值金额,赠送不变
- */
- public function test_calc_bonus_coins_fixed_independent_of_pay_amt()
- {
- $coupon = (object) [
- 'coupon_type' => UserCoupon::TYPE_FIXED,
- 'coupon_value' => 5.00,
- 'max_bonus' => 0,
- ];
- $bonus1 = $this->service->calcBonusCoins($coupon, 10.00);
- $bonus2 = $this->service->calcBonusCoins($coupon, 100.00);
- $this->assertEquals($bonus1, $bonus2); // 固定金额与充值额无关
- $this->assertEquals(500, $bonus1);
- }
- // ========================
- // 百分比券 - 金币计算
- // ========================
- /**
- * 测试百分比券:按充值金额百分比计算
- */
- public function test_calc_bonus_coins_percent_type()
- {
- $coupon = (object) [
- 'coupon_type' => UserCoupon::TYPE_PERCENT,
- 'coupon_value' => 50, // 50%
- 'max_bonus' => 100, // 上限100元
- ];
- $bonus = $this->service->calcBonusCoins($coupon, 40.00);
- $this->assertEquals(2000, $bonus); // 40 × 50% × 100 = 2000分
- }
- /**
- * 测试百分比券:超过上限时按上限计算
- */
- public function test_calc_bonus_coins_percent_capped_at_max()
- {
- $coupon = (object) [
- 'coupon_type' => UserCoupon::TYPE_PERCENT,
- 'coupon_value' => 50,
- 'max_bonus' => 30, // 上限30元
- ];
- // 充值200元,50% = 100元,但上限30元 → 3000分
- $bonus = $this->service->calcBonusCoins($coupon, 200.00);
- $this->assertEquals(3000, $bonus); // 30 × 100 = 3000分
- }
- // ========================
- // 优惠券验证
- // ========================
- /**
- * 测试验证:优惠券不存在
- */
- public function test_validate_coupon_not_found()
- {
- $result = $this->service->validateCoupon(99999, $this->testUserId, 50.00);
- $this->assertFalse($result['valid']);
- $this->assertEquals('Coupon not found', $result['error']);
- }
- /**
- * 测试验证:充值金额不满足最低门槛
- */
- public function test_validate_coupon_below_min_recharge()
- {
- // 手动插入一张测试券
- $couponId = DB::connection('write')->table(UserCoupon::TABLE)->insertGetId([
- 'user_id' => $this->testUserId,
- 'coupon_name' => 'test_fixed',
- 'coupon_type' => UserCoupon::TYPE_FIXED,
- 'coupon_value' => 10.00,
- 'min_recharge' => 50.00, // 最低50元
- 'max_bonus' => 0,
- 'bonus_coins' => 0,
- 'order_sn' => '',
- 'status' => UserCoupon::STATUS_UNUSED,
- 'issued_at' => date('Y-m-d H:i:s'),
- 'used_at' => null,
- 'expire_at' => date('Y-m-d H:i:s', strtotime('+7 days')),
- ]);
- $result = $this->service->validateCoupon($couponId, $this->testUserId, 20.00); // 20 < 50
- $this->assertFalse($result['valid']);
- $this->assertStringContainsString('Minimum recharge', $result['error']);
- }
- /**
- * 测试验证:优惠券已过期
- */
- public function test_validate_coupon_expired()
- {
- $couponId = DB::connection('write')->table(UserCoupon::TABLE)->insertGetId([
- 'user_id' => $this->testUserId,
- 'coupon_name' => 'test_expired',
- 'coupon_type' => UserCoupon::TYPE_FIXED,
- 'coupon_value' => 10.00,
- 'min_recharge' => 0,
- 'max_bonus' => 0,
- 'bonus_coins' => 0,
- 'order_sn' => '',
- 'status' => UserCoupon::STATUS_UNUSED,
- 'issued_at' => date('Y-m-d H:i:s', strtotime('-10 days')),
- 'used_at' => null,
- 'expire_at' => date('Y-m-d H:i:s', strtotime('-1 day')), // 已过期
- ]);
- $result = $this->service->validateCoupon($couponId, $this->testUserId, 50.00);
- $this->assertFalse($result['valid']);
- $this->assertEquals('Coupon expired', $result['error']);
- }
- /**
- * 测试验证:有效优惠券
- */
- public function test_validate_coupon_valid()
- {
- $couponId = DB::connection('write')->table(UserCoupon::TABLE)->insertGetId([
- 'user_id' => $this->testUserId,
- 'coupon_name' => 'test_valid',
- 'coupon_type' => UserCoupon::TYPE_FIXED,
- 'coupon_value' => 10.00,
- 'min_recharge' => 30.00,
- 'max_bonus' => 0,
- 'bonus_coins' => 0,
- 'order_sn' => '',
- 'status' => UserCoupon::STATUS_UNUSED,
- 'issued_at' => date('Y-m-d H:i:s'),
- 'used_at' => null,
- 'expire_at' => date('Y-m-d H:i:s', strtotime('+7 days')),
- ]);
- $result = $this->service->validateCoupon($couponId, $this->testUserId, 50.00);
- $this->assertTrue($result['valid']);
- }
- // ========================
- // 防重复发放
- // ========================
- /**
- * 测试 hasSameCoupon:有效券存在时返回 true
- */
- public function test_has_same_coupon_returns_true_when_valid_exists()
- {
- $couponName = 'test_duplicate_check';
- DB::connection('write')->table(UserCoupon::TABLE)->insert([
- 'user_id' => $this->testUserId,
- 'coupon_name' => $couponName,
- 'coupon_type' => UserCoupon::TYPE_FIXED,
- 'coupon_value' => 5.00,
- 'min_recharge' => 0,
- 'max_bonus' => 0,
- 'bonus_coins' => 0,
- 'order_sn' => '',
- 'status' => UserCoupon::STATUS_UNUSED,
- 'issued_at' => date('Y-m-d H:i:s'),
- 'used_at' => null,
- 'expire_at' => date('Y-m-d H:i:s', strtotime('+7 days')),
- ]);
- $this->assertTrue(UserCoupon::hasSameCoupon($this->testUserId, $couponName));
- }
- /**
- * 测试 hasSameCoupon:同名券已过期时返回 false(允许重新发放)
- */
- public function test_has_same_coupon_returns_false_when_expired()
- {
- $couponName = 'test_expired_dup';
- DB::connection('write')->table(UserCoupon::TABLE)->insert([
- 'user_id' => $this->testUserId,
- 'coupon_name' => $couponName,
- 'coupon_type' => UserCoupon::TYPE_FIXED,
- 'coupon_value' => 5.00,
- 'min_recharge' => 0,
- 'max_bonus' => 0,
- 'bonus_coins' => 0,
- 'order_sn' => '',
- 'status' => UserCoupon::STATUS_UNUSED,
- 'issued_at' => date('Y-m-d H:i:s', strtotime('-10 days')),
- 'used_at' => null,
- 'expire_at' => date('Y-m-d H:i:s', strtotime('-1 day')), // 已过期
- ]);
- $this->assertFalse(UserCoupon::hasSameCoupon($this->testUserId, $couponName));
- }
- }
|