- Implement AdminDomainOrderTest to cover admin functionalities for domain orders including listing, confirming registration, KYC updates, activation, rejection, and refunds. - Implement ClientDomainOrderTest to cover client functionalities for placing domain orders, including validation for balance, duplicate orders, and domain normalization. - Ensure proper handling of domain pricing and service statuses throughout the tests.
167 lines
5.6 KiB
PHP
167 lines
5.6 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->customer = Customer::factory()->create(['total' => 1_000_000]);
|
|
|
|
// 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]);
|
|
}
|
|
}
|