- Dịch vụ custom: bảng custom_products + services.custom_product_id; createService nhận
custom{} thay product_id, giá = đơn giá tháng x số tháng chu kỳ (1/3/6/12/24/36),
gia hạn tính theo cùng đơn giá, ServiceClientDetailResource trả custom_product và specs.
- Giám sát VM: GET v1/system/vm-metrics/history gom lịch sử về tối đa 240 điểm;
lỗi lấy dung lượng đĩa không còn làm hỏng cả thẻ metrics.
- SealResponse: AES-256-GCM theo khoá phiên do proxy Next cấp qua header x-seal-key
(HMAC của session id). Client opt-in bằng x-seal; chi tiết dịch vụ, mật khẩu VM và
console ticket luôn được mã hoá. Body gửi lên dạng {enc} được giải mã trước validate;
khoá lệch trả 428 để client lấy khoá mới. Đây là che mắt DevTools, không phải bảo mật
chống chính chủ tài khoản.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
70 lines
2.4 KiB
PHP
70 lines
2.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Feature\Api\Admin\Customer;
|
|
|
|
use App\Models\Customer;
|
|
use App\Models\CustomProduct;
|
|
use App\Models\Invoice;
|
|
use App\Models\Service;
|
|
use App\Models\User;
|
|
use App\Support\RenewalPricing;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Spatie\Permission\Models\Role;
|
|
use Tests\TestCase;
|
|
|
|
class CreateCustomServiceTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
public function test_custom_service_stores_own_product_and_prices_by_cycle(): void
|
|
{
|
|
Role::firstOrCreate(['name' => 'Full Administrator', 'guard_name' => 'admin']);
|
|
$admin = User::factory()->create();
|
|
$admin->assignRole('Full Administrator');
|
|
$customer = Customer::factory()->create();
|
|
|
|
$response = $this->actingAs($admin, 'admin')->postJson(
|
|
"/api/v1/administrator/customers/{$customer->uuid}/services",
|
|
[
|
|
'custom' => [
|
|
'name' => 'VPS riêng',
|
|
'category' => 'vps',
|
|
'unit_price' => 200000,
|
|
'cpu' => 8,
|
|
'ram_mb' => 16384,
|
|
'disk_gb' => 200,
|
|
'specs' => [['label' => 'Vị trí', 'value' => 'HCM']],
|
|
],
|
|
'billing_cycle' => 'quarterly',
|
|
'payment_status' => 'unpaid',
|
|
],
|
|
)->assertCreated();
|
|
|
|
$service = Service::findOrFail($response->json('data.id'));
|
|
$custom = CustomProduct::findOrFail($service->custom_product_id);
|
|
|
|
$this->assertNull($service->product_id);
|
|
$this->assertSame('vps', $service->type);
|
|
$this->assertSame(16384, $custom->ram_mb);
|
|
$this->assertSame(600000, (int) Invoice::where('customer_id', $customer->id)->value('total'));
|
|
$this->assertSame(
|
|
1200000,
|
|
collect(RenewalPricing::options($custom))->firstWhere('months', 6)['price'],
|
|
);
|
|
}
|
|
|
|
public function test_requires_product_or_custom(): void
|
|
{
|
|
Role::firstOrCreate(['name' => 'Full Administrator', 'guard_name' => 'admin']);
|
|
$admin = User::factory()->create();
|
|
$admin->assignRole('Full Administrator');
|
|
$customer = Customer::factory()->create();
|
|
|
|
$this->actingAs($admin, 'admin')
|
|
->postJson("/api/v1/administrator/customers/{$customer->uuid}/services", ['payment_status' => 'unpaid'])
|
|
->assertStatus(422);
|
|
}
|
|
}
|