VPS and dedicated server ordering, managed panels, SSO, billing, and launcher integration — forked from hosting infrastructure with server-focused routes and dashboard. Co-authored-by: Cursor <cursoragent@cursor.com>
780 lines
30 KiB
PHP
780 lines
30 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Http\Controllers\Hosting\HostingProductController;
|
|
use App\Jobs\ProvisionHostingOrderJob;
|
|
use App\Models\CustomerHostingOrder;
|
|
use App\Models\HostingProduct;
|
|
use App\Models\ProvisioningQueueItem;
|
|
use App\Models\User;
|
|
use App\Services\ExchangeRateService;
|
|
use App\Services\Hosting\HostingResourcePolicyService;
|
|
use App\Services\Hosting\NodeCapacityService;
|
|
use App\Services\Hosting\Providers\ContaboProvider;
|
|
use App\Services\Hosting\Providers\SharedNodeProvider;
|
|
use App\Services\Hosting\ServerAgentBootstrapService;
|
|
use App\Services\Hosting\ServerAgentInstallerService;
|
|
use App\Services\Hosting\ServerOrderService;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Http\Request;
|
|
use Tests\TestCase;
|
|
|
|
class HostingPricingDisplayTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
private function mockContaboImages(): void
|
|
{
|
|
$provider = \Mockery::mock(ContaboProvider::class);
|
|
$provider->shouldReceive('getAvailableImages')->andReturn([
|
|
[
|
|
'id' => 'ubuntu-24-image',
|
|
'name' => 'Ubuntu 24.04',
|
|
'description' => 'Ubuntu',
|
|
'os_type' => 'linux',
|
|
'standard_image' => true,
|
|
],
|
|
[
|
|
'id' => 'ubuntu-image',
|
|
'name' => 'Ubuntu 22.04',
|
|
'description' => 'Ubuntu',
|
|
'os_type' => 'linux',
|
|
'standard_image' => true,
|
|
],
|
|
[
|
|
'id' => 'win-image',
|
|
'name' => 'Windows Server 2022',
|
|
'description' => 'Windows',
|
|
'os_type' => 'windows',
|
|
'standard_image' => true,
|
|
],
|
|
]);
|
|
$provider->shouldReceive('getAvailableProducts')->andReturn([
|
|
['id' => 'V91', 'name' => 'Cloud VPS 10 NVMe', 'cpu' => 3, 'ram_gb' => 8, 'disk_gb' => 75, 'price_monthly' => 4.99],
|
|
['id' => 'V45', 'name' => 'VPS S SSD', 'cpu' => 4, 'ram_gb' => 8, 'disk_gb' => 200, 'price_monthly' => 6.99],
|
|
]);
|
|
|
|
$this->app->instance(ContaboProvider::class, $provider);
|
|
}
|
|
|
|
public function test_vps_page_renders_dynamic_ghs_price_instead_of_stored_zero_value(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$this->mockContaboImages();
|
|
|
|
$this->mock(ExchangeRateService::class, function ($mock): void {
|
|
$mock->shouldReceive('getUsdToGhs')->andReturn(15.50);
|
|
});
|
|
|
|
HostingProduct::create([
|
|
'name' => 'VPS S',
|
|
'slug' => 'vps-s-test',
|
|
'description' => 'Dynamic VPS pricing test',
|
|
'category' => 'vps',
|
|
'type' => 'vps',
|
|
'price_monthly' => 0,
|
|
'price_quarterly' => 0,
|
|
'price_yearly' => 0,
|
|
'currency' => 'USD',
|
|
'cpu_cores' => 4,
|
|
'ram_mb' => 8192,
|
|
'disk_gb' => 200,
|
|
'contabo_product_id' => 'V45',
|
|
'is_active' => true,
|
|
'is_visible' => true,
|
|
]);
|
|
|
|
$request = Request::create('http://localhost/hosting/vps', 'GET');
|
|
$request->setUserResolver(fn (): User => $user);
|
|
|
|
$view = $this->app->make(HostingProductController::class)->vps($request);
|
|
$html = $view->render();
|
|
|
|
$this->assertStringContainsString('GHS 140.00', $html);
|
|
$this->assertStringNotContainsString('$0.00', $html);
|
|
}
|
|
|
|
public function test_vps_page_lists_install_later_and_multiple_live_images(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$this->mockContaboImages();
|
|
|
|
$this->mock(ExchangeRateService::class, function ($mock): void {
|
|
$mock->shouldReceive('getUsdToGhs')->andReturn(15.50);
|
|
});
|
|
|
|
HostingProduct::create([
|
|
'name' => 'VPS S',
|
|
'slug' => 'vps-s-install-later-test',
|
|
'description' => 'Dynamic VPS pricing test',
|
|
'category' => 'vps',
|
|
'type' => 'vps',
|
|
'price_monthly' => 0,
|
|
'price_quarterly' => 0,
|
|
'price_yearly' => 0,
|
|
'currency' => 'USD',
|
|
'cpu_cores' => 4,
|
|
'ram_mb' => 8192,
|
|
'disk_gb' => 200,
|
|
'contabo_product_id' => 'V45',
|
|
'is_active' => true,
|
|
'is_visible' => true,
|
|
]);
|
|
|
|
$request = Request::create('http://localhost/hosting/vps', 'GET');
|
|
$request->setUserResolver(fn (): User => $user);
|
|
|
|
$view = $this->app->make(HostingProductController::class)->vps($request);
|
|
$html = $view->render();
|
|
|
|
$this->assertStringContainsString('Install Later', $html);
|
|
$this->assertStringContainsString('Ubuntu 24.04', $html);
|
|
$this->assertStringContainsString('Ubuntu 22.04', $html);
|
|
}
|
|
|
|
public function test_server_orders_use_display_currency_for_dynamic_products(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$this->mockContaboImages();
|
|
|
|
$this->mock(ExchangeRateService::class, function ($mock): void {
|
|
$mock->shouldReceive('getUsdToGhs')->andReturn(15.50);
|
|
});
|
|
|
|
$product = HostingProduct::create([
|
|
'name' => 'VPS S',
|
|
'slug' => 'vps-s-order-test',
|
|
'description' => 'Dynamic VPS order test',
|
|
'category' => 'vps',
|
|
'type' => 'vps',
|
|
'price_monthly' => 0,
|
|
'price_quarterly' => 0,
|
|
'price_yearly' => 0,
|
|
'currency' => 'USD',
|
|
'cpu_cores' => 4,
|
|
'ram_mb' => 8192,
|
|
'disk_gb' => 200,
|
|
'contabo_product_id' => 'V45',
|
|
'is_active' => true,
|
|
'is_visible' => true,
|
|
]);
|
|
|
|
$response = $this->actingAs($user)->postJson(route('hosting.servers.store'), [
|
|
'product_id' => $product->id,
|
|
'server_name' => 'pricing-test-server',
|
|
'billing_cycle' => 'monthly',
|
|
'region' => 'EU',
|
|
'image' => 'ubuntu-image',
|
|
'license' => 'none',
|
|
'application' => 'none',
|
|
'default_user' => 'root',
|
|
'additional_ip' => 'none',
|
|
'private_networking' => 'disabled',
|
|
'storage_type' => 'included',
|
|
'object_storage' => 'none',
|
|
'server_password' => 'Secure123!!',
|
|
'server_password_confirmation' => 'Secure123!!',
|
|
]);
|
|
|
|
$response->assertOk();
|
|
|
|
$order = CustomerHostingOrder::query()->firstOrFail();
|
|
|
|
$this->assertSame('GHS', $order->currency);
|
|
$this->assertSame('140.00', number_format((float) $order->amount_paid, 2, '.', ''));
|
|
}
|
|
|
|
public function test_server_orders_persist_selected_options_password_and_semiannual_term_pricing(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$this->mockContaboImages();
|
|
|
|
$this->mock(ExchangeRateService::class, function ($mock): void {
|
|
$mock->shouldReceive('getUsdToGhs')->andReturn(15.50);
|
|
});
|
|
|
|
$product = HostingProduct::create([
|
|
'name' => 'VPS S',
|
|
'slug' => 'vps-s-semiannual-test',
|
|
'description' => 'Dynamic VPS order test',
|
|
'category' => 'vps',
|
|
'type' => 'vps',
|
|
'price_monthly' => 0,
|
|
'price_quarterly' => 0,
|
|
'price_yearly' => 0,
|
|
'currency' => 'USD',
|
|
'cpu_cores' => 4,
|
|
'ram_mb' => 8192,
|
|
'disk_gb' => 200,
|
|
'contabo_product_id' => 'V45',
|
|
'is_active' => true,
|
|
'is_visible' => true,
|
|
]);
|
|
|
|
$response = $this->actingAs($user)->postJson(route('hosting.servers.store'), [
|
|
'product_id' => $product->id,
|
|
'server_name' => 'semiannual-configured-server',
|
|
'billing_cycle' => 'semiannual',
|
|
'region' => 'EU',
|
|
'image' => 'win-image',
|
|
'license' => 'plesk_windows',
|
|
'application' => 'none',
|
|
'default_user' => 'administrator',
|
|
'additional_ip' => 'none',
|
|
'private_networking' => 'disabled',
|
|
'storage_type' => 'included',
|
|
'object_storage' => 'eu_250',
|
|
'server_password' => 'Abc123!!',
|
|
'server_password_confirmation' => 'Abc123!!',
|
|
]);
|
|
|
|
$response->assertOk();
|
|
|
|
$order = CustomerHostingOrder::query()->firstOrFail();
|
|
|
|
$this->assertSame('semiannual', $order->billing_cycle);
|
|
$this->assertSame('4985.36', number_format((float) $order->amount_paid, 2, '.', ''));
|
|
$this->assertSame('win-image', data_get($order->meta, 'server_order.selection.image'));
|
|
$this->assertSame('administrator', data_get($order->meta, 'server_order.selection.default_user'));
|
|
$this->assertSame(
|
|
['250 GB Object Storage (EU)'],
|
|
data_get($order->meta, 'server_order.quote.manual_follow_up')
|
|
);
|
|
$this->assertSame('Abc123!!', decrypt((string) data_get($order->meta, 'server_order.server_password_encrypted')));
|
|
$this->assertSame('Windows Server 2022', data_get($order->meta, 'server_order.selection_summary.0.value'));
|
|
}
|
|
|
|
public function test_server_orders_allow_install_later_with_remote_login_only(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$this->mockContaboImages();
|
|
|
|
$this->mock(ExchangeRateService::class, function ($mock): void {
|
|
$mock->shouldReceive('getUsdToGhs')->andReturn(15.50);
|
|
});
|
|
|
|
$product = HostingProduct::create([
|
|
'name' => 'VPS S',
|
|
'slug' => 'vps-s-install-later-order-test',
|
|
'description' => 'Dynamic VPS order test',
|
|
'category' => 'vps',
|
|
'type' => 'vps',
|
|
'price_monthly' => 0,
|
|
'price_quarterly' => 0,
|
|
'price_yearly' => 0,
|
|
'currency' => 'USD',
|
|
'cpu_cores' => 4,
|
|
'ram_mb' => 8192,
|
|
'disk_gb' => 200,
|
|
'contabo_product_id' => 'V45',
|
|
'is_active' => true,
|
|
'is_visible' => true,
|
|
]);
|
|
|
|
$response = $this->actingAs($user)->postJson(route('hosting.servers.store'), [
|
|
'product_id' => $product->id,
|
|
'server_name' => 'install-later-server',
|
|
'billing_cycle' => 'monthly',
|
|
'region' => 'EU',
|
|
'image' => '__install_later__',
|
|
'license' => 'none',
|
|
'application' => 'none',
|
|
'default_user' => 'root',
|
|
'additional_ip' => 'none',
|
|
'private_networking' => 'disabled',
|
|
'storage_type' => 'included',
|
|
'object_storage' => 'none',
|
|
'server_password' => 'Secure123!!',
|
|
'server_password_confirmation' => 'Secure123!!',
|
|
]);
|
|
|
|
$response->assertOk();
|
|
|
|
$order = CustomerHostingOrder::query()->firstOrFail();
|
|
|
|
$this->assertSame('__install_later__', data_get($order->meta, 'server_order.selection.image'));
|
|
$this->assertSame('none', data_get($order->meta, 'server_order.selection.license'));
|
|
$this->assertSame('none', data_get($order->meta, 'server_order.selection.application'));
|
|
$this->assertSame('140.00', number_format((float) $order->amount_paid, 2, '.', ''));
|
|
}
|
|
|
|
public function test_vps_10_monthly_orders_include_one_time_setup_fee_but_yearly_orders_do_not(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$this->mockContaboImages();
|
|
|
|
$this->mock(ExchangeRateService::class, function ($mock): void {
|
|
$mock->shouldReceive('getUsdToGhs')->andReturn(15.50);
|
|
});
|
|
|
|
$product = HostingProduct::create([
|
|
'name' => 'VPS 10',
|
|
'slug' => 'vps-10-setup-fee-test',
|
|
'description' => 'Dynamic VPS setup fee test',
|
|
'category' => 'vps',
|
|
'type' => 'vps',
|
|
'price_monthly' => 0,
|
|
'price_quarterly' => 0,
|
|
'price_yearly' => 0,
|
|
'currency' => 'GHS',
|
|
'cpu_cores' => 3,
|
|
'ram_mb' => 8192,
|
|
'disk_gb' => 75,
|
|
'contabo_product_id' => 'V91',
|
|
'is_active' => true,
|
|
'is_visible' => true,
|
|
]);
|
|
|
|
$this->actingAs($user)->postJson(route('hosting.servers.store'), [
|
|
'product_id' => $product->id,
|
|
'server_name' => 'monthly-setup-fee-server',
|
|
'billing_cycle' => 'monthly',
|
|
'region' => 'EU',
|
|
'image' => '__install_later__',
|
|
'license' => 'none',
|
|
'application' => 'none',
|
|
'default_user' => 'root',
|
|
'additional_ip' => 'none',
|
|
'private_networking' => 'disabled',
|
|
'storage_type' => 'included',
|
|
'object_storage' => 'none',
|
|
'server_password' => 'Secure123!!',
|
|
'server_password_confirmation' => 'Secure123!!',
|
|
])->assertOk();
|
|
|
|
$monthlyOrder = CustomerHostingOrder::query()->latest('id')->firstOrFail();
|
|
|
|
$this->assertSame('200.00', number_format((float) $monthlyOrder->amount_paid, 2, '.', ''));
|
|
$this->assertSame('100.00', number_format((float) collect(data_get($monthlyOrder->meta, 'server_order.quote.line_items', []))->firstWhere('code', 'setup_fee')['amount'], 2, '.', ''));
|
|
|
|
$this->actingAs($user)->postJson(route('hosting.servers.store'), [
|
|
'product_id' => $product->id,
|
|
'server_name' => 'yearly-no-setup-fee-server',
|
|
'billing_cycle' => 'yearly',
|
|
'region' => 'EU',
|
|
'image' => '__install_later__',
|
|
'license' => 'none',
|
|
'application' => 'none',
|
|
'default_user' => 'root',
|
|
'additional_ip' => 'none',
|
|
'private_networking' => 'disabled',
|
|
'storage_type' => 'included',
|
|
'object_storage' => 'none',
|
|
'server_password' => 'Secure123!!',
|
|
'server_password_confirmation' => 'Secure123!!',
|
|
])->assertOk();
|
|
|
|
$yearlyOrder = CustomerHostingOrder::query()->latest('id')->firstOrFail();
|
|
|
|
$this->assertSame('960.00', number_format((float) $yearlyOrder->amount_paid, 2, '.', ''));
|
|
$this->assertNull(collect(data_get($yearlyOrder->meta, 'server_order.quote.line_items', []))->firstWhere('code', 'setup_fee'));
|
|
}
|
|
|
|
public function test_provisioning_job_uses_selected_server_options_for_contabo(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
|
|
$this->mock(ExchangeRateService::class, function ($mock): void {
|
|
$mock->shouldReceive('getUsdToGhs')->andReturn(15.50);
|
|
});
|
|
|
|
$provider = \Mockery::mock(ContaboProvider::class);
|
|
$provider->shouldReceive('getAvailableImages')->once()->andReturn([
|
|
[
|
|
'id' => 'win-image',
|
|
'name' => 'Windows Server 2022',
|
|
'description' => 'Windows',
|
|
'os_type' => 'windows',
|
|
'standard_image' => true,
|
|
],
|
|
]);
|
|
$provider->shouldReceive('createInstance')
|
|
->once()
|
|
->withArgs(function (array $config): bool {
|
|
$this->assertSame('V45', $config['product_id']);
|
|
$this->assertSame('win-image', $config['image_id']);
|
|
$this->assertSame('US-west', $config['region']);
|
|
$this->assertSame('administrator', $config['default_user']);
|
|
$this->assertSame('PleskHost', $config['license']);
|
|
$this->assertSame(6, $config['period']);
|
|
$this->assertSame('Abc123!!', $config['root_password']);
|
|
$this->assertArrayHasKey('additionalIps', $config['add_ons']);
|
|
$this->assertArrayHasKey('privateNetworking', $config['add_ons']);
|
|
|
|
return true;
|
|
})
|
|
->andReturn([
|
|
'instance_id' => 'instance-123',
|
|
'ip_address' => '203.0.113.10',
|
|
]);
|
|
|
|
$this->app->instance(ContaboProvider::class, $provider);
|
|
|
|
$product = HostingProduct::create([
|
|
'name' => 'VPS S',
|
|
'slug' => 'vps-s-provisioning-test',
|
|
'description' => 'Dynamic VPS provisioning test',
|
|
'category' => 'vps',
|
|
'type' => 'vps',
|
|
'price_monthly' => 0,
|
|
'price_quarterly' => 0,
|
|
'price_yearly' => 0,
|
|
'currency' => 'USD',
|
|
'cpu_cores' => 4,
|
|
'ram_mb' => 8192,
|
|
'disk_gb' => 200,
|
|
'contabo_product_id' => 'V45',
|
|
'contabo_region' => 'EU',
|
|
'is_active' => true,
|
|
'is_visible' => true,
|
|
]);
|
|
|
|
$order = CustomerHostingOrder::create([
|
|
'user_id' => $user->id,
|
|
'hosting_product_id' => $product->id,
|
|
'domain_name' => 'job-provisioned-server',
|
|
'billing_cycle' => 'semiannual',
|
|
'amount_paid' => 0,
|
|
'currency' => 'GHS',
|
|
'status' => CustomerHostingOrder::STATUS_APPROVED,
|
|
'meta' => [
|
|
'region' => 'US-west',
|
|
'server_order' => [
|
|
'selection' => [
|
|
'image' => 'win-image',
|
|
'region' => 'US-west',
|
|
'license' => 'plesk_host',
|
|
'application' => 'none',
|
|
'default_user' => 'administrator',
|
|
'additional_ip' => 'one_extra',
|
|
'private_networking' => 'enabled',
|
|
'storage_type' => 'included',
|
|
'object_storage' => 'none',
|
|
],
|
|
'server_password_encrypted' => encrypt('Abc123!!'),
|
|
],
|
|
],
|
|
]);
|
|
|
|
$capacityService = \Mockery::mock(NodeCapacityService::class);
|
|
$sharedProvider = \Mockery::mock(SharedNodeProvider::class);
|
|
$serverOrders = $this->app->make(ServerOrderService::class);
|
|
$bootstrap = \Mockery::mock(ServerAgentBootstrapService::class);
|
|
$bootstrap->shouldReceive('ensureBootstrap')->andReturn(['token' => 'bootstrap-token']);
|
|
$installer = \Mockery::mock(ServerAgentInstallerService::class);
|
|
$installer->shouldReceive('augmentProvisioningConfig')->andReturnUsing(
|
|
fn ($freshOrder, array $config, array $bootstrapConfig): array => $config
|
|
);
|
|
|
|
(new ProvisionHostingOrderJob($order))->handle(
|
|
$capacityService,
|
|
app(HostingResourcePolicyService::class),
|
|
$sharedProvider,
|
|
$provider,
|
|
$serverOrders,
|
|
$bootstrap,
|
|
$installer,
|
|
);
|
|
|
|
$order->refresh();
|
|
|
|
$this->assertSame(CustomerHostingOrder::STATUS_ACTIVE, $order->status);
|
|
$this->assertSame('203.0.113.10', $order->server_ip);
|
|
$this->assertSame('administrator', $order->server_username);
|
|
$this->assertSame('instance-123', data_get($order->meta, 'contabo_instance_id'));
|
|
$this->assertDatabaseHas('provisioning_queue', [
|
|
'customer_hosting_order_id' => $order->id,
|
|
'status' => ProvisioningQueueItem::STATUS_COMPLETED,
|
|
]);
|
|
}
|
|
|
|
public function test_ladill_control_panel_selection_sets_internal_panel_url_after_provisioning(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
|
|
$this->mock(ExchangeRateService::class, function ($mock): void {
|
|
$mock->shouldReceive('getUsdToGhs')->andReturn(15.50);
|
|
});
|
|
|
|
$provider = \Mockery::mock(ContaboProvider::class);
|
|
$provider->shouldReceive('getAvailableImages')->once()->andReturn([
|
|
[
|
|
'id' => 'ubuntu-image',
|
|
'name' => 'Ubuntu 22.04',
|
|
'description' => 'Ubuntu',
|
|
'os_type' => 'linux',
|
|
'standard_image' => true,
|
|
],
|
|
]);
|
|
$provider->shouldReceive('createInstance')
|
|
->once()
|
|
->withArgs(function (array $config): bool {
|
|
$this->assertNull($config['license']);
|
|
$this->assertSame('ladill', $config['panel']);
|
|
|
|
return true;
|
|
})
|
|
->andReturn([
|
|
'instance_id' => 'instance-panel-123',
|
|
'ip_address' => '203.0.113.20',
|
|
'status' => 'running',
|
|
'region' => 'EU',
|
|
'datacenter' => 'MUC1',
|
|
'image' => 'ubuntu-image',
|
|
]);
|
|
|
|
$this->app->instance(ContaboProvider::class, $provider);
|
|
|
|
$product = HostingProduct::create([
|
|
'name' => 'VPS S',
|
|
'slug' => 'vps-s-ladill-panel-test',
|
|
'description' => 'Dynamic VPS provisioning test',
|
|
'category' => 'vps',
|
|
'type' => 'vps',
|
|
'price_monthly' => 0,
|
|
'price_quarterly' => 0,
|
|
'price_yearly' => 0,
|
|
'currency' => 'USD',
|
|
'cpu_cores' => 4,
|
|
'ram_mb' => 8192,
|
|
'disk_gb' => 200,
|
|
'contabo_product_id' => 'V45',
|
|
'contabo_region' => 'EU',
|
|
'is_active' => true,
|
|
'is_visible' => true,
|
|
]);
|
|
|
|
$order = CustomerHostingOrder::create([
|
|
'user_id' => $user->id,
|
|
'hosting_product_id' => $product->id,
|
|
'domain_name' => 'ladill-panel-server',
|
|
'billing_cycle' => 'monthly',
|
|
'amount_paid' => 0,
|
|
'currency' => 'GHS',
|
|
'status' => CustomerHostingOrder::STATUS_APPROVED,
|
|
'meta' => [
|
|
'region' => 'EU',
|
|
'server_order' => [
|
|
'selection' => [
|
|
'image' => 'ubuntu-image',
|
|
'region' => 'EU',
|
|
'license' => 'ladill_panel',
|
|
'application' => 'none',
|
|
'default_user' => 'root',
|
|
'additional_ip' => 'none',
|
|
'private_networking' => 'disabled',
|
|
'storage_type' => 'included',
|
|
'object_storage' => 'none',
|
|
],
|
|
'selection_summary' => [
|
|
['label' => 'License', 'value' => 'Ladill Control Panel'],
|
|
],
|
|
'server_password_encrypted' => encrypt('Root123!!'),
|
|
],
|
|
],
|
|
]);
|
|
|
|
$capacityService = \Mockery::mock(NodeCapacityService::class);
|
|
$sharedProvider = \Mockery::mock(SharedNodeProvider::class);
|
|
$serverOrders = $this->app->make(ServerOrderService::class);
|
|
$bootstrap = \Mockery::mock(ServerAgentBootstrapService::class);
|
|
$bootstrap->shouldReceive('ensureBootstrap')->andReturn(['token' => 'bootstrap-token']);
|
|
$installer = \Mockery::mock(ServerAgentInstallerService::class);
|
|
$installer->shouldReceive('augmentProvisioningConfig')->andReturnUsing(
|
|
fn ($freshOrder, array $config, array $bootstrapConfig): array => $config
|
|
);
|
|
|
|
(new ProvisionHostingOrderJob($order))->handle(
|
|
$capacityService,
|
|
app(HostingResourcePolicyService::class),
|
|
$sharedProvider,
|
|
$provider,
|
|
$serverOrders,
|
|
$bootstrap,
|
|
$installer,
|
|
);
|
|
|
|
$order->refresh();
|
|
|
|
$this->assertSame(route('hosting.server-panel.show', $order), $order->control_panel_url);
|
|
$this->assertSame('ladill', data_get($order->meta, 'server_panel.panel'));
|
|
$this->assertSame('instance-panel-123', data_get($order->meta, 'contabo_instance_id'));
|
|
}
|
|
|
|
public function test_ladill_server_panel_page_and_power_action_work_for_selected_orders(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
|
|
$product = HostingProduct::create([
|
|
'name' => 'VPS S',
|
|
'slug' => 'vps-s-panel-route-test',
|
|
'description' => 'Dynamic VPS provisioning test',
|
|
'category' => 'vps',
|
|
'type' => 'vps',
|
|
'price_monthly' => 0,
|
|
'price_quarterly' => 0,
|
|
'price_yearly' => 0,
|
|
'currency' => 'USD',
|
|
'cpu_cores' => 4,
|
|
'ram_mb' => 8192,
|
|
'disk_gb' => 200,
|
|
'contabo_product_id' => 'V45',
|
|
'is_active' => true,
|
|
'is_visible' => true,
|
|
]);
|
|
|
|
$order = CustomerHostingOrder::create([
|
|
'user_id' => $user->id,
|
|
'hosting_product_id' => $product->id,
|
|
'domain_name' => 'panel-route-server',
|
|
'billing_cycle' => 'monthly',
|
|
'amount_paid' => 0,
|
|
'currency' => 'GHS',
|
|
'status' => CustomerHostingOrder::STATUS_ACTIVE,
|
|
'server_ip' => '203.0.113.50',
|
|
'server_username' => 'root',
|
|
'control_panel_url' => 'http://localhost/hosting/orders/1/panel',
|
|
'meta' => [
|
|
'contabo_instance_id' => 'instance-route-123',
|
|
'server_order' => [
|
|
'selection' => [
|
|
'license' => 'ladill_panel',
|
|
'application' => 'none',
|
|
],
|
|
'selection_summary' => [
|
|
['label' => 'License', 'value' => 'Ladill Control Panel'],
|
|
],
|
|
],
|
|
'server_panel' => [
|
|
'panel' => 'ladill',
|
|
'power_status' => 'on',
|
|
'instance_status' => 'running',
|
|
],
|
|
],
|
|
]);
|
|
|
|
$provider = \Mockery::mock(ContaboProvider::class);
|
|
$provider->shouldReceive('startInstance')->once()->with('instance-route-123')->andReturn(true);
|
|
$this->app->instance(ContaboProvider::class, $provider);
|
|
|
|
$this->actingAs($user)
|
|
->get(route('hosting.server-panel.show', $order))
|
|
->assertOk()
|
|
->assertSee('Ladill Control Panel')
|
|
->assertSee('panel-route-server');
|
|
|
|
$this->actingAs($user)
|
|
->post(route('hosting.server-panel.power', [$order, 'start']))
|
|
->assertRedirect();
|
|
}
|
|
|
|
public function test_provisioning_uses_cloud_init_for_webmin_profile(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
|
|
$this->mock(ExchangeRateService::class, function ($mock): void {
|
|
$mock->shouldReceive('getUsdToGhs')->andReturn(15.50);
|
|
});
|
|
|
|
$provider = \Mockery::mock(ContaboProvider::class);
|
|
$provider->shouldReceive('getAvailableImages')->once()->andReturn([
|
|
[
|
|
'id' => 'ubuntu-image',
|
|
'name' => 'Ubuntu 22.04',
|
|
'description' => 'Ubuntu',
|
|
'os_type' => 'linux',
|
|
'standard_image' => true,
|
|
],
|
|
]);
|
|
$provider->shouldReceive('generateCloudInit')->once()->andReturn('#cloud-config webmin');
|
|
$provider->shouldReceive('createInstance')
|
|
->once()
|
|
->withArgs(function (array $config): bool {
|
|
$this->assertSame('ubuntu-image', $config['image_id']);
|
|
$this->assertSame('#cloud-config webmin', $config['user_data']);
|
|
$this->assertNull($config['license']);
|
|
$this->assertNull($config['application_id']);
|
|
|
|
return true;
|
|
})
|
|
->andReturn([
|
|
'instance_id' => 'instance-webmin-123',
|
|
'ip_address' => '203.0.113.30',
|
|
]);
|
|
|
|
$this->app->instance(ContaboProvider::class, $provider);
|
|
|
|
$product = HostingProduct::create([
|
|
'name' => 'VPS S',
|
|
'slug' => 'vps-s-webmin-test',
|
|
'description' => 'Dynamic VPS provisioning test',
|
|
'category' => 'vps',
|
|
'type' => 'vps',
|
|
'price_monthly' => 0,
|
|
'price_quarterly' => 0,
|
|
'price_yearly' => 0,
|
|
'currency' => 'USD',
|
|
'cpu_cores' => 4,
|
|
'ram_mb' => 8192,
|
|
'disk_gb' => 200,
|
|
'contabo_product_id' => 'V45',
|
|
'contabo_region' => 'EU',
|
|
'is_active' => true,
|
|
'is_visible' => true,
|
|
]);
|
|
|
|
$order = CustomerHostingOrder::create([
|
|
'user_id' => $user->id,
|
|
'hosting_product_id' => $product->id,
|
|
'domain_name' => 'webmin-server',
|
|
'billing_cycle' => 'monthly',
|
|
'amount_paid' => 0,
|
|
'currency' => 'GHS',
|
|
'status' => CustomerHostingOrder::STATUS_APPROVED,
|
|
'meta' => [
|
|
'region' => 'EU',
|
|
'server_order' => [
|
|
'selection' => [
|
|
'image' => 'ubuntu-image',
|
|
'region' => 'EU',
|
|
'license' => 'none',
|
|
'application' => 'webmin',
|
|
'default_user' => 'root',
|
|
'additional_ip' => 'none',
|
|
'private_networking' => 'disabled',
|
|
'storage_type' => 'included',
|
|
'object_storage' => 'none',
|
|
],
|
|
'server_password_encrypted' => encrypt('Root123!!'),
|
|
],
|
|
],
|
|
]);
|
|
|
|
$capacityService = \Mockery::mock(NodeCapacityService::class);
|
|
$sharedProvider = \Mockery::mock(SharedNodeProvider::class);
|
|
$serverOrders = $this->app->make(ServerOrderService::class);
|
|
$bootstrap = \Mockery::mock(ServerAgentBootstrapService::class);
|
|
$bootstrap->shouldReceive('ensureBootstrap')->andReturn(['token' => 'bootstrap-token']);
|
|
$installer = \Mockery::mock(ServerAgentInstallerService::class);
|
|
$installer->shouldReceive('augmentProvisioningConfig')->andReturnUsing(
|
|
fn ($freshOrder, array $config, array $bootstrapConfig): array => $config
|
|
);
|
|
|
|
(new ProvisionHostingOrderJob($order))->handle(
|
|
$capacityService,
|
|
app(HostingResourcePolicyService::class),
|
|
$sharedProvider,
|
|
$provider,
|
|
$serverOrders,
|
|
$bootstrap,
|
|
$installer,
|
|
);
|
|
|
|
$this->assertDatabaseHas('provisioning_queue', [
|
|
'customer_hosting_order_id' => $order->id,
|
|
'status' => ProvisioningQueueItem::STATUS_COMPLETED,
|
|
]);
|
|
}
|
|
}
|