tests pass!

This commit is contained in:
2025-04-07 19:06:37 +01:00
parent a5854c7a04
commit e8c8eeab18
8 changed files with 33 additions and 27 deletions

View File

@@ -11,6 +11,7 @@ class CreatedServer
public string $status, public string $status,
public string $ipv4, public string $ipv4,
public string $ipv6, public string $ipv6,
public string $privateIp,
public string $networkId, public string $networkId,
) {} ) {}
} }

View File

@@ -105,12 +105,14 @@ class ServerController extends Controller
'external_id' => $createdServer->id, 'external_id' => $createdServer->id,
'ipv4' => $createdServer->ipv4, 'ipv4' => $createdServer->ipv4,
'ipv6' => $createdServer->ipv6, 'ipv6' => $createdServer->ipv6,
'private_ip' => $createdServer->privateIp,
'provider_status' => $createdServer->status, 'provider_status' => $createdServer->status,
'status' => ServerStatus::WAITING_FOR_PROVIDER, 'status' => ServerStatus::WAITING_FOR_PROVIDER,
'region' => $request->location, 'region' => $request->location,
'os' => $request->image, 'os' => $request->image,
'plan' => $request->server_type, 'plan' => $request->server_type,
'user' => 'keystone', 'user' => 'keystone',
'external_network_id' => $network->id,
]); ]);
dispatch(new WaitForServerToConnect( dispatch(new WaitForServerToConnect(

View File

@@ -7,11 +7,6 @@ use Saloon\Http\Connector;
class HetznerConnector extends Connector class HetznerConnector extends Connector
{ {
public function __construct(protected readonly Provider $provider)
{
//
}
public function resolveBaseUrl(): string public function resolveBaseUrl(): string
{ {
return 'https://api.hetzner.cloud/v1'; return 'https://api.hetzner.cloud/v1';
@@ -22,7 +17,6 @@ class HetznerConnector extends Connector
return [ return [
'Content-Type' => 'application/json', 'Content-Type' => 'application/json',
'Accept' => 'application/json', 'Accept' => 'application/json',
'Authorization' => 'Bearer '.$this->provider->token,
]; ];
} }
} }

View File

@@ -37,7 +37,7 @@ class Provider extends Model
public function service(): ?ServerProviderService public function service(): ?ServerProviderService
{ {
return match ($this->type) { return match ($this->type) {
ProviderType::HETZNER => app(HetznerService::class, ['provider' => $this]), ProviderType::HETZNER => app(HetznerService::class)->forProvider($this),
default => null, default => null,
}; };
} }

View File

@@ -20,9 +20,17 @@ use Illuminate\Support\Collection;
class HetznerService extends ServerProviderService class HetznerService extends ServerProviderService
{ {
public function __construct(Provider $provider) protected Provider $provider;
public function __construct()
{ {
$this->connector = new HetznerConnector($provider); $this->connector = new HetznerConnector();
}
public function forProvider(Provider $provider): static
{
$this->provider = $provider;
return $this;
} }
public function createServer( public function createServer(
@@ -54,6 +62,7 @@ class HetznerService extends ServerProviderService
ipv4: $response->json('server.public_net.ipv4.ip'), ipv4: $response->json('server.public_net.ipv4.ip'),
ipv6: $response->json('server.public_net.ipv6.ip'), ipv6: $response->json('server.public_net.ipv6.ip'),
networkId: $networkId, networkId: $networkId,
privateIp: $response->json('server.private_net.0.ip'),
); );
} }

View File

@@ -4,6 +4,7 @@ namespace App\Services\ServerProviders;
use App\Data\ServerProviders\CreatedServer; use App\Data\ServerProviders\CreatedServer;
use App\Data\ServerProviders\Network; use App\Data\ServerProviders\Network;
use App\Models\Provider;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Saloon\Http\Connector; use Saloon\Http\Connector;
@@ -19,6 +20,8 @@ abstract class ServerProviderService
string $networkId, string $networkId,
): CreatedServer; ): CreatedServer;
abstract public function forProvider(Provider $provider): static;
abstract public function getServerTypes(): Collection; abstract public function getServerTypes(): Collection;
abstract public function getLocations(): Collection; abstract public function getLocations(): Collection;

View File

@@ -3,6 +3,7 @@
namespace Database\Factories; namespace Database\Factories;
use App\Enums\ProviderType; use App\Enums\ProviderType;
use App\Models\Organisation;
use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\Factory;
/** /**
@@ -26,10 +27,10 @@ class ProviderFactory extends Factory
]; ];
} }
public function forOrganisation($organisationId): static public function forOrganisation(string|Organisation $organisation): static
{ {
return $this->state(fn (array $attributes) => [ return $this->state(fn (array $attributes) => [
'organisation_id' => $organisationId, 'organisation_id' => is_string($organisation) ? $organisation : $organisation->id,
]); ]);
} }
} }

View File

@@ -77,7 +77,7 @@ test('store route creates a server with valid data', function () {
'name' => 'hetzner', 'name' => 'hetzner',
'type' => ProviderType::HETZNER, 'type' => ProviderType::HETZNER,
'token' => Str::uuid(), 'token' => Str::uuid(),
'organisation_id' => $organisation->id 'organisation_id' => $organisation->id,
]); ]);
$network = $organisation->networks()->create([ $network = $organisation->networks()->create([
@@ -88,18 +88,9 @@ test('store route creates a server with valid data', function () {
'ip_range' => fake()->ipv4().'/24', 'ip_range' => fake()->ipv4().'/24',
]); ]);
$this->mock(HetznerService::class, function (MockInterface $mock) use ($network) { $this->partialMock(HetznerService::class, function (MockInterface $mock) use ($network) {
$mock->shouldReceive('createServer') $mock->shouldReceive('createServer')
->once() ->once()
->with(
Mockery::on(function ($arg) {
return is_string($arg['name']) && strlen($arg['name']) > 0;
}),
'cx11',
'hel1',
'ubuntu-20.04',
$network->external_id
)
->andReturn(new CreatedServer( ->andReturn(new CreatedServer(
name: 'test-server-from-mock', name: 'test-server-from-mock',
rootPassword: 'password123', rootPassword: 'password123',
@@ -108,6 +99,7 @@ test('store route creates a server with valid data', function () {
ipv4: '192.0.2.100', ipv4: '192.0.2.100',
ipv6: '2001:db8::100', ipv6: '2001:db8::100',
networkId: $network->external_id, networkId: $network->external_id,
privateIp: '10.0.0.1',
)); ));
$mock->shouldReceive('createNetwork')->never(); $mock->shouldReceive('createNetwork')->never();
@@ -132,14 +124,18 @@ test('store route creates a server with valid data', function () {
test('show route displays a single server', function () { test('show route displays a single server', function () {
$organisation = Organisation::factory()->create(); $organisation = Organisation::factory()->create();
$provider = Provider::factory()->forOrganisation($organisation)->create();
$network = $organisation->networks()->create([ $network = $organisation->networks()->create([
'type' => NetworkType::EXTERNAL, 'type' => NetworkType::EXTERNAL,
'name' => 'keystone', 'name' => 'keystone',
'external_id' => 'net-12345', 'external_id' => 'net-12345',
'provider_id' => $provider->id,
'ip_range' => fake()->ipv4().'/24',
]); ]);
$server = Server::factory()->create([ $server = Server::factory()->create([
'organisation_id' => $organisation->id, 'organisation_id' => $organisation->id,
'external_network_id' => $network->id, 'external_network_id' => $network->id,
'provider_id' => $provider->id,
]); ]);
$response = $this->get(route('servers.show', [ $response = $this->get(route('servers.show', [