Files
saasshop/tests/Feature/SubscriptionActivationServiceTest.php

178 lines
6.1 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace Tests\Feature;
use App\Models\Merchant;
use App\Models\Plan;
use App\Models\PlatformOrder;
use App\Models\SiteSubscription;
use App\Support\SubscriptionActivationService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class SubscriptionActivationServiceTest extends TestCase
{
use RefreshDatabase;
public function test_activate_order_creates_subscription_and_links_order(): void
{
$this->seed();
$merchant = Merchant::query()->firstOrFail();
$plan = Plan::query()->create([
'code' => 'activate_test_monthly',
'name' => '激活测试(月付)',
'billing_cycle' => 'monthly',
'price' => 99,
'list_price' => 99,
'status' => 'active',
'sort' => 10,
'published_at' => now(),
]);
$order = PlatformOrder::query()->create([
'merchant_id' => $merchant->id,
'plan_id' => $plan->id,
'order_no' => 'PO_ACTIVATE_0001',
'order_type' => 'new_purchase',
'status' => 'activated',
'payment_status' => 'paid',
'plan_name' => $plan->name,
'billing_cycle' => $plan->billing_cycle,
'period_months' => 1,
'quantity' => 1,
'list_amount' => 99,
'discount_amount' => 0,
'payable_amount' => 99,
'paid_amount' => 99,
'placed_at' => now()->subMinutes(10),
'paid_at' => now()->subMinutes(5),
'activated_at' => now()->subMinutes(1),
]);
$service = new SubscriptionActivationService();
$subscription = $service->activateOrder($order->id, 1);
// 二次执行应为幂等:不重复续期/不重复创建,直接返回同一个订阅
$again = $service->activateOrder($order->id, 1);
$this->assertInstanceOf(SiteSubscription::class, $subscription);
$this->assertSame($subscription->id, $again->id);
$this->assertSame('activated', $subscription->status);
$this->assertSame($merchant->id, $subscription->merchant_id);
$this->assertSame($plan->id, $subscription->plan_id);
$this->assertNotNull($subscription->starts_at);
$this->assertNotNull($subscription->ends_at);
$this->assertTrue($subscription->ends_at->greaterThan($subscription->starts_at));
$order->refresh();
$this->assertSame($subscription->id, $order->site_subscription_id);
}
public function test_activate_order_extends_existing_subscription(): void
{
$this->seed();
$merchant = Merchant::query()->firstOrFail();
$plan = Plan::query()->create([
'code' => 'renew_test_monthly',
'name' => '续费测试(月付)',
'billing_cycle' => 'monthly',
'price' => 120,
'list_price' => 120,
'status' => 'active',
'sort' => 10,
'published_at' => now(),
]);
$subscription = SiteSubscription::query()->create([
'merchant_id' => $merchant->id,
'plan_id' => $plan->id,
'status' => 'activated',
'source' => 'manual',
'subscription_no' => 'SUB_EXIST_0001',
'plan_name' => $plan->name,
'billing_cycle' => $plan->billing_cycle,
'period_months' => 1,
'amount' => 120,
'starts_at' => now()->subDays(10),
'ends_at' => now()->addDays(20),
'activated_at' => now()->subDays(10),
]);
$oldEndsAt = $subscription->ends_at->copy();
$order = PlatformOrder::query()->create([
'merchant_id' => $merchant->id,
'plan_id' => $plan->id,
'site_subscription_id' => $subscription->id,
'order_no' => 'PO_RENEW_0001',
'order_type' => 'renewal',
'status' => 'activated',
'payment_status' => 'paid',
'plan_name' => $plan->name,
'billing_cycle' => $plan->billing_cycle,
'period_months' => 1,
'quantity' => 1,
'list_amount' => 120,
'discount_amount' => 0,
'payable_amount' => 120,
'paid_amount' => 120,
'placed_at' => now()->subMinutes(10),
'paid_at' => now()->subMinutes(5),
'activated_at' => now()->subMinutes(1),
]);
$service = new SubscriptionActivationService();
$updated = $service->activateOrder($order->id, 1);
$updated->refresh();
$this->assertTrue($updated->ends_at->greaterThan($oldEndsAt));
$this->assertSame('activated', $updated->status);
// 再次执行应为幂等ends_at 不应继续增长
$endsAtAfterFirst = $updated->ends_at->copy();
$again = $service->activateOrder($order->id, 1);
$again->refresh();
$this->assertTrue($again->ends_at->equalTo($endsAtAfterFirst));
}
public function test_activate_order_requires_paid_and_activated(): void
{
$this->seed();
$merchant = Merchant::query()->firstOrFail();
$plan = Plan::query()->create([
'code' => 'invalid_test',
'name' => '无效测试',
'billing_cycle' => 'monthly',
'price' => 10,
'list_price' => 10,
'status' => 'active',
'sort' => 10,
'published_at' => now(),
]);
$order = PlatformOrder::query()->create([
'merchant_id' => $merchant->id,
'plan_id' => $plan->id,
'order_no' => 'PO_INVALID_0001',
'order_type' => 'new_purchase',
'status' => 'pending',
'payment_status' => 'unpaid',
'plan_name' => $plan->name,
'billing_cycle' => $plan->billing_cycle,
'period_months' => 1,
'quantity' => 1,
'payable_amount' => 10,
'paid_amount' => 0,
'placed_at' => now(),
]);
$this->expectException(\InvalidArgumentException::class);
$service = new SubscriptionActivationService();
$service->activateOrder($order->id, 1);
}
}