Add dedicated show/edit pages for cloud provider tokens and cloud-init scripts, including descriptions and UUID routes. Generate private keys directly from the index and surface cloud provider API loading errors in server creation flows.
197 lines
8 KiB
PHP
197 lines
8 KiB
PHP
<?php
|
|
|
|
use App\Livewire\Server\CreatePage;
|
|
use App\Livewire\Server\New\ByDigitalOcean;
|
|
use App\Livewire\Server\New\ByHetzner;
|
|
use App\Livewire\Server\New\ByVultr;
|
|
use App\Models\CloudProviderToken;
|
|
use App\Models\InstanceSettings;
|
|
use App\Models\Team;
|
|
use App\Models\User;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Livewire\Livewire;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
beforeEach(function () {
|
|
InstanceSettings::unguarded(fn () => InstanceSettings::updateOrCreate(['id' => 0], ['id' => 0]));
|
|
|
|
$this->team = Team::factory()->create();
|
|
$this->admin = User::factory()->create();
|
|
$this->admin->teams()->attach($this->team, ['role' => 'admin']);
|
|
|
|
$this->actingAs($this->admin);
|
|
session(['currentTeam' => $this->team]);
|
|
});
|
|
|
|
test('provider token selection redirects to a token specific server creation url', function (string $component, string $provider, string $type) {
|
|
$token = CloudProviderToken::factory()->create([
|
|
'team_id' => $this->team->id,
|
|
'provider' => $provider,
|
|
]);
|
|
|
|
Livewire::test($component)
|
|
->call('selectToken', $token->id)
|
|
->assertRedirect(route('server.create.token', [
|
|
'type' => $type,
|
|
'token_uuid' => $token->uuid,
|
|
]));
|
|
})->with([
|
|
'hetzner' => [ByHetzner::class, 'hetzner', 'hetzner'],
|
|
'vultr' => [ByVultr::class, 'vultr', 'vultr'],
|
|
'digital-ocean' => [ByDigitalOcean::class, 'digitalocean', 'digital-ocean'],
|
|
]);
|
|
|
|
test('provider token selection is rendered as clickable token boxes', function (string $component, string $provider, string $type, string $tokenName) {
|
|
$token = CloudProviderToken::factory()->create([
|
|
'team_id' => $this->team->id,
|
|
'provider' => $provider,
|
|
'name' => $tokenName,
|
|
]);
|
|
|
|
Livewire::test($component)
|
|
->assertSee($tokenName)
|
|
->assertSee(route('server.create.token', ['type' => $type, 'token_uuid' => $token->uuid]))
|
|
->assertDontSee('loadingProviderTokenId', false)
|
|
->assertDontSee('coolbox-loading', false)
|
|
->assertSee('coolbox', false)
|
|
->assertDontSee('wire:click="selectToken(', false)
|
|
->assertDontSee('loading-spinner', false)
|
|
->assertDontSee('Loading Hetzner details...')
|
|
->assertDontSee('Loading Vultr details...')
|
|
->assertDontSee('Loading DigitalOcean details...')
|
|
->assertDontSee('Select a saved token')
|
|
->assertDontSee('You need a saved')
|
|
->assertDontSee('Continue');
|
|
})->with([
|
|
'hetzner' => [ByHetzner::class, 'hetzner', 'hetzner', 'Production Hetzner'],
|
|
'vultr' => [ByVultr::class, 'vultr', 'vultr', 'Production Vultr'],
|
|
'digital-ocean' => [ByDigitalOcean::class, 'digitalocean', 'digital-ocean', 'Production DigitalOcean'],
|
|
]);
|
|
|
|
test('provider token selection shows an add token box when no tokens exist', function (string $component, string $title, string $description) {
|
|
Livewire::test($component)
|
|
->assertSee($title)
|
|
->assertSee($description)
|
|
->assertSee('max-w-2xl', false)
|
|
->assertSee('coolbox', false)
|
|
->assertSee('M12 4.5v15m7.5-7.5h-15', false)
|
|
->assertDontSee('wire:click="selectToken(', false);
|
|
})->with([
|
|
'hetzner' => [ByHetzner::class, 'Add a new token', 'Add a Hetzner API token to create servers from your account.'],
|
|
'vultr' => [ByVultr::class, 'Add a new token', 'Add a Vultr API token to create servers from your account.'],
|
|
'digital-ocean' => [ByDigitalOcean::class, 'Add a new token', 'Add a DigitalOcean API token to create Droplets from your account.'],
|
|
]);
|
|
|
|
test('provider token urls pass the token uuid into the server creation page', function () {
|
|
$token = CloudProviderToken::factory()->create([
|
|
'team_id' => $this->team->id,
|
|
'provider' => 'hetzner',
|
|
]);
|
|
|
|
expect(route('server.create.token', [
|
|
'type' => 'hetzner',
|
|
'token_uuid' => $token->uuid,
|
|
]))->toEndWith('/servers/new/hetzner/'.$token->uuid);
|
|
|
|
Livewire::test(CreatePage::class, [
|
|
'type' => 'hetzner',
|
|
'token_uuid' => $token->uuid,
|
|
])
|
|
->assertSet('type', 'hetzner')
|
|
->assertSet('token_uuid', $token->uuid)
|
|
->assertSet('title', 'Hetzner');
|
|
});
|
|
|
|
test('provider token pages defer provider api data loading until wire init', function (string $component, string $provider, string $type, string $loadingText, string $wireInitCall) {
|
|
$token = CloudProviderToken::factory()->create([
|
|
'team_id' => $this->team->id,
|
|
'provider' => $provider,
|
|
]);
|
|
|
|
Http::fake([
|
|
'*' => Http::response([], 200),
|
|
]);
|
|
|
|
Livewire::test($component, ['selectedTokenUuid' => $token->uuid])
|
|
->assertSet('current_step', 2)
|
|
->assertSet('loading_data', true)
|
|
->assertSee($loadingText)
|
|
->assertSee($wireInitCall, false)
|
|
->assertSee('text-coollabs dark:text-warning animate-spin', false)
|
|
->assertDontSee('border-b-2 border-primary', false);
|
|
|
|
Http::assertNothingSent();
|
|
})->with([
|
|
'hetzner' => [ByHetzner::class, 'hetzner', 'hetzner', 'Loading Hetzner data...', 'wire:init="loadHetznerData"'],
|
|
'vultr' => [ByVultr::class, 'vultr', 'vultr', 'Loading Vultr data...', 'wire:init="loadVultrData"'],
|
|
'digital-ocean' => [ByDigitalOcean::class, 'digitalocean', 'digital-ocean', 'Loading DigitalOcean data...', 'wire:init="loadDigitalOceanData"'],
|
|
]);
|
|
|
|
test('provider token pages render api error details when a selected token is invalid', function (string $component, string $provider, string $type, string $loadMethod, string $providerName, string $apiMessage) {
|
|
$token = CloudProviderToken::factory()->create([
|
|
'team_id' => $this->team->id,
|
|
'provider' => $provider,
|
|
'name' => $providerName.' Token',
|
|
]);
|
|
|
|
Http::fake([
|
|
'*' => Http::response($apiMessage, 401),
|
|
]);
|
|
|
|
Livewire::test($component, ['selectedTokenUuid' => $token->uuid])
|
|
->call($loadMethod)
|
|
->assertSet('loading_data', false)
|
|
->assertSet('provider_data_error', "{$providerName} API error: {$apiMessage}")
|
|
->assertDispatched('error')
|
|
->assertSee("Unable to load {$providerName} details")
|
|
->assertSee($apiMessage)
|
|
->assertSee('href="'.route('server.create.type', ['type' => $type]).'"', false)
|
|
->assertSee('wire:navigate', false)
|
|
->assertDontSee('wire:click="previousStep"', false);
|
|
})->with([
|
|
'hetzner' => [ByHetzner::class, 'hetzner', 'hetzner', 'loadHetznerData', 'Hetzner', 'the token you have provided is invalid'],
|
|
'vultr' => [ByVultr::class, 'vultr', 'vultr', 'loadVultrData', 'Vultr', 'Invalid API key'],
|
|
'digital-ocean' => [ByDigitalOcean::class, 'digitalocean', 'digital-ocean', 'loadDigitalOceanData', 'DigitalOcean', 'Unable to authenticate you'],
|
|
]);
|
|
|
|
test('back button on token specific provider creation pages returns to provider token selection', function (string $provider, string $type) {
|
|
$token = CloudProviderToken::factory()->create([
|
|
'team_id' => $this->team->id,
|
|
'provider' => $provider,
|
|
]);
|
|
|
|
Livewire::test(CreatePage::class, [
|
|
'type' => $type,
|
|
'token_uuid' => $token->uuid,
|
|
])
|
|
->assertSee(route('server.create.type', ['type' => $type]), false)
|
|
->assertDontSee('href="'.route('server.create').'"', false);
|
|
})->with([
|
|
'hetzner' => ['hetzner', 'hetzner'],
|
|
'vultr' => ['vultr', 'vultr'],
|
|
'digital-ocean' => ['digitalocean', 'digital-ocean'],
|
|
]);
|
|
|
|
test('new token header button is hidden on token specific provider creation pages', function (string $provider, string $type) {
|
|
$token = CloudProviderToken::factory()->create([
|
|
'team_id' => $this->team->id,
|
|
'provider' => $provider,
|
|
]);
|
|
|
|
Livewire::test(CreatePage::class, [
|
|
'type' => $type,
|
|
])
|
|
->assertSee('+ New Token');
|
|
|
|
Livewire::test(CreatePage::class, [
|
|
'type' => $type,
|
|
'token_uuid' => $token->uuid,
|
|
])
|
|
->assertDontSee('+ New Token');
|
|
})->with([
|
|
'hetzner' => ['hetzner', 'hetzner'],
|
|
'vultr' => ['vultr', 'vultr'],
|
|
'digital-ocean' => ['digitalocean', 'digital-ocean'],
|
|
]);
|