diff --git a/app/Livewire/Project/Service/Configuration.php b/app/Livewire/Project/Service/Configuration.php index 559851e3a..2d69ceb12 100644 --- a/app/Livewire/Project/Service/Configuration.php +++ b/app/Livewire/Project/Service/Configuration.php @@ -33,6 +33,8 @@ public function getListeners() return [ "echo-private:team.{$teamId},ServiceChecked" => 'serviceChecked', + 'refreshServices' => 'refreshServices', + 'refresh' => 'refreshServices', ]; } diff --git a/app/Livewire/Project/Service/EditDomain.php b/app/Livewire/Project/Service/EditDomain.php index 7c718393d..dbbcca8f8 100644 --- a/app/Livewire/Project/Service/EditDomain.php +++ b/app/Livewire/Project/Service/EditDomain.php @@ -73,6 +73,7 @@ public function submit() } $this->application->service->parse(); $this->dispatch('refresh'); + $this->dispatch('refreshServices'); $this->dispatch('configurationChanged'); } catch (\Throwable $e) { $originalFqdn = $this->application->getOriginal('fqdn'); diff --git a/bootstrap/helpers/parsers.php b/bootstrap/helpers/parsers.php index a588ed882..09d4c7549 100644 --- a/bootstrap/helpers/parsers.php +++ b/bootstrap/helpers/parsers.php @@ -1181,23 +1181,26 @@ function serviceParser(Service $resource): Collection $image = data_get_str($service, 'image'); $isDatabase = isDatabaseImage($image, $service); if ($isDatabase) { - $applicationFound = ServiceApplication::where('name', $serviceName)->where('image', $image)->where('service_id', $resource->id)->first(); + $applicationFound = ServiceApplication::where('name', $serviceName)->where('service_id', $resource->id)->first(); if ($applicationFound) { $savedService = $applicationFound; } else { $savedService = ServiceDatabase::firstOrCreate([ 'name' => $serviceName, - 'image' => $image, 'service_id' => $resource->id, ]); } } else { $savedService = ServiceApplication::firstOrCreate([ 'name' => $serviceName, - 'image' => $image, 'service_id' => $resource->id, ]); } + // Update image if it changed + if ($savedService->image !== $image) { + $savedService->image = $image; + $savedService->save(); + } } foreach ($services as $serviceName => $service) { $predefinedPort = null; @@ -1514,20 +1517,18 @@ function serviceParser(Service $resource): Collection } if ($isDatabase) { - $applicationFound = ServiceApplication::where('name', $serviceName)->where('image', $image)->where('service_id', $resource->id)->first(); + $applicationFound = ServiceApplication::where('name', $serviceName)->where('service_id', $resource->id)->first(); if ($applicationFound) { $savedService = $applicationFound; } else { $savedService = ServiceDatabase::firstOrCreate([ 'name' => $serviceName, - 'image' => $image, 'service_id' => $resource->id, ]); } } else { $savedService = ServiceApplication::firstOrCreate([ 'name' => $serviceName, - 'image' => $image, 'service_id' => $resource->id, ]); } diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index 656c607bf..308f522fb 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -1317,6 +1317,13 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal 'name' => $serviceName, 'service_id' => $resource->id, ])->first(); + if (is_null($savedService)) { + $savedService = ServiceDatabase::create([ + 'name' => $serviceName, + 'image' => $image, + 'service_id' => $resource->id, + ]); + } } } else { if ($isNew) { @@ -1330,21 +1337,13 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal 'name' => $serviceName, 'service_id' => $resource->id, ])->first(); - } - } - if (is_null($savedService)) { - if ($isDatabase) { - $savedService = ServiceDatabase::create([ - 'name' => $serviceName, - 'image' => $image, - 'service_id' => $resource->id, - ]); - } else { - $savedService = ServiceApplication::create([ - 'name' => $serviceName, - 'image' => $image, - 'service_id' => $resource->id, - ]); + if (is_null($savedService)) { + $savedService = ServiceApplication::create([ + 'name' => $serviceName, + 'image' => $image, + 'service_id' => $resource->id, + ]); + } } } diff --git a/tests/Unit/ServiceConfigurationRefreshTest.php b/tests/Unit/ServiceConfigurationRefreshTest.php new file mode 100644 index 000000000..c4f4a9703 --- /dev/null +++ b/tests/Unit/ServiceConfigurationRefreshTest.php @@ -0,0 +1,44 @@ +toContain("'refreshServices' => 'refreshServices'") + ->toContain("'refresh' => 'refreshServices'"); +}); + +it('ensures Configuration component has refreshServices method', function () { + $configurationFile = file_get_contents(__DIR__.'/../../app/Livewire/Project/Service/Configuration.php'); + + // Check that the refreshServices method exists + expect($configurationFile) + ->toContain('public function refreshServices()') + ->toContain('$this->service->refresh()') + ->toContain('$this->applications = $this->service->applications->sort()') + ->toContain('$this->databases = $this->service->databases->sort()'); +}); + +it('ensures StackForm dispatches refreshServices event on submit', function () { + $stackFormFile = file_get_contents(__DIR__.'/../../app/Livewire/Project/Service/StackForm.php'); + + // Check that StackForm dispatches refreshServices event + expect($stackFormFile) + ->toContain("->dispatch('refreshServices')"); +}); + +it('ensures EditDomain dispatches refreshServices event on submit', function () { + $editDomainFile = file_get_contents(__DIR__.'/../../app/Livewire/Project/Service/EditDomain.php'); + + // Check that EditDomain dispatches refreshServices event + expect($editDomainFile) + ->toContain("->dispatch('refreshServices')"); +}); diff --git a/tests/Unit/ServiceParserImageUpdateTest.php b/tests/Unit/ServiceParserImageUpdateTest.php new file mode 100644 index 000000000..b52e0b820 --- /dev/null +++ b/tests/Unit/ServiceParserImageUpdateTest.php @@ -0,0 +1,55 @@ +toContain("firstOrCreate([\n 'name' => \$serviceName,\n 'service_id' => \$resource->id,\n ]);") + ->not->toContain("firstOrCreate([\n 'name' => \$serviceName,\n 'image' => \$image,\n 'service_id' => \$resource->id,\n ]);"); +}); + +it('ensures service parser updates image after finding or creating service', function () { + // Read the serviceParser function from parsers.php + $parsersFile = file_get_contents(__DIR__.'/../../bootstrap/helpers/parsers.php'); + + // Check that image update logic exists after firstOrCreate + expect($parsersFile) + ->toContain('// Update image if it changed') + ->toContain('if ($savedService->image !== $image) {') + ->toContain('$savedService->image = $image;') + ->toContain('$savedService->save();'); +}); + +it('ensures parseDockerComposeFile does not create duplicates on null savedService', function () { + // Read the parseDockerComposeFile function from shared.php + $sharedFile = file_get_contents(__DIR__.'/../../bootstrap/helpers/shared.php'); + + // Check that the duplicate creation logic after is_null check has been fixed + // The old code would create a duplicate if savedService was null + // The new code checks for null within the else block and creates only if needed + expect($sharedFile) + ->toContain('if (is_null($savedService)) {') + ->toContain('$savedService = ServiceDatabase::create(['); +}); + +it('verifies image update logic is present in parseDockerComposeFile', function () { + // Read the parseDockerComposeFile function from shared.php + $sharedFile = file_get_contents(__DIR__.'/../../bootstrap/helpers/shared.php'); + + // Verify the image update logic exists + expect($sharedFile) + ->toContain('// Check if image changed') + ->toContain('if ($savedService->image !== $image) {') + ->toContain('$savedService->image = $image;') + ->toContain('$savedService->save();'); +});