Files
portal-api/tests/Feature/Api/DomainOrder/ClientDomainOrderTest.php
2026-09-23 15:17:18 +07:00

174 lines
6.0 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Feature\Api\DomainOrder;
use App\Enums\Domain\DomainProvisioningStatus;
use App\Enums\Order\OrderStatus;
use App\Models\Customer;
use App\Models\Product;
use App\Models\Setting;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class ClientDomainOrderTest extends TestCase
{
use RefreshDatabase;
private Customer $customer;
private Product $product;
protected function setUp(): void
{
parent::setUp();
$this->withHeaders(['x-access-token' => $this->buildClientAccessToken()]);
$this->customer = Customer::factory()->create([
'total' => 1_000_000,
// DomainOrderService bắt buộc đã có ảnh CCCD 2 mặt
'front_identity_number' => 'cccd/front.jpg',
'back_identity_number' => 'cccd/back.jpg',
]);
// Minimal domain product — product_type is set automatically from category
// but we force it directly since we have no category seeder in tests
$this->product = Product::factory()->create([
'product_type' => 'domain',
'is_active' => true,
]);
Setting::create([
'key' => 'domain_pricing',
'value' => json_encode([
'domains' => [
['tld' => '.com.vn', 'firstYear' => 300_000, 'renewal' => 300_000],
['tld' => '.vn', 'firstYear' => 400_000, 'renewal' => 400_000],
['tld' => '.com', 'firstYear' => 320_000, 'renewal' => 340_000],
],
]),
]);
}
private function actingAsCustomer(): static
{
return $this->actingAs($this->customer, 'client');
}
// =========================================================================
public function test_authenticated_customer_can_place_domain_order(): void
{
$response = $this->actingAsCustomer()->postJson('/v1/client/domain-orders', [
'product_uuid' => $this->product->uuid,
'domain_name' => 'example.com',
'years' => 1,
]);
$response->assertStatus(201)
->assertJsonPath('success', true)
->assertJsonPath('data.domain_name', 'example.com')
->assertJsonPath('data.status', 'pending');
$this->assertDatabaseHas('orders', ['status' => OrderStatus::PROCESSING->value]);
$this->assertDatabaseHas('order_items', ['domain_name' => 'example.com']);
$this->assertDatabaseHas('services', ['status' => DomainProvisioningStatus::PENDING->value]);
$this->assertEquals(1_000_000 - 320_000, $this->customer->fresh()->total);
}
public function test_insufficient_balance_returns_422(): void
{
$this->customer->update(['total' => 1_000]);
$response = $this->actingAsCustomer()->postJson('/v1/client/domain-orders', [
'product_uuid' => $this->product->uuid,
'domain_name' => 'test.com',
'years' => 1,
]);
$response->assertStatus(422)->assertJsonPath('success', false);
$this->assertEquals(1_000, $this->customer->fresh()->total);
}
public function test_duplicate_pending_domain_is_rejected(): void
{
// Place the first order
$this->actingAsCustomer()->postJson('/v1/client/domain-orders', [
'product_uuid' => $this->product->uuid,
'domain_name' => 'taken.com',
'years' => 1,
])->assertStatus(201);
// Replenish balance for second attempt
$this->customer->update(['total' => 1_000_000]);
$response = $this->actingAsCustomer()->postJson('/v1/client/domain-orders', [
'product_uuid' => $this->product->uuid,
'domain_name' => 'taken.com',
'years' => 1,
]);
$response->assertStatus(422)->assertJsonPath('success', false);
}
public function test_unauthenticated_cannot_place_order(): void
{
$this->postJson('/v1/client/domain-orders', [
'product_uuid' => $this->product->uuid,
'domain_name' => 'test.com',
])->assertStatus(401);
}
public function test_invalid_product_type_rejected(): void
{
$nonDomainProduct = Product::factory()->create([
'product_type' => 'vps',
'is_active' => true,
]);
$response = $this->actingAsCustomer()->postJson('/v1/client/domain-orders', [
'product_uuid' => $nonDomainProduct->uuid,
'domain_name' => 'test.com',
'years' => 1,
]);
// Product::firstOrFail() will throw ModelNotFoundException → 500, or service rejects it
$response->assertStatus(500);
}
public function test_domain_normalized_to_lowercase(): void
{
$response = $this->actingAsCustomer()->postJson('/v1/client/domain-orders', [
'product_uuid' => $this->product->uuid,
'domain_name' => 'HTTPS://MY-SITE.COM/path',
'years' => 1,
]);
$response->assertStatus(201);
$this->assertDatabaseHas('order_items', ['domain_name' => 'my-site.com']);
}
public function test_unknown_tld_returns_422(): void
{
$response = $this->actingAsCustomer()->postJson('/v1/client/domain-orders', [
'product_uuid' => $this->product->uuid,
'domain_name' => 'test.xyz',
'years' => 1,
]);
$response->assertStatus(422)->assertJsonPath('success', false);
}
public function test_years_defaults_to_1_when_not_provided(): void
{
$response = $this->actingAsCustomer()->postJson('/v1/client/domain-orders', [
'product_uuid' => $this->product->uuid,
'domain_name' => 'default-years.com',
]);
$response->assertStatus(201);
$this->assertDatabaseHas('order_items', ['domain_name' => 'default-years.com', 'months' => 12]);
}
}