diff --git a/psalm.xml b/psalm.xml
index 01ea359..1ec8788 100644
--- a/psalm.xml
+++ b/psalm.xml
@@ -64,9 +64,6 @@
-
-
-
@@ -74,12 +71,6 @@
-
-
-
-
-
-
diff --git a/src/Core/Cdn/Services/BunnyStorageService.php b/src/Core/Cdn/Services/BunnyStorageService.php
index 9447228..03741ef 100644
--- a/src/Core/Cdn/Services/BunnyStorageService.php
+++ b/src/Core/Cdn/Services/BunnyStorageService.php
@@ -14,8 +14,6 @@
use Bunny\Storage\Client;
use Core\Config\ConfigService;
use Core\Crypt\LthnHash;
-use Core\Service\Contracts\HealthCheckable;
-use Core\Service\HealthCheckResult;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
@@ -27,9 +25,11 @@
* - Private zone: DRM/gated content
*
* Supports vBucket scoping for workspace-isolated CDN paths.
- * Implements HealthCheckable for monitoring CDN connectivity.
+ * Zone reachability is exposed through checkZoneHealth() and isReachable();
+ * turning that into a service health report belongs to dappcore/service,
+ * which is the package that depends on this one.
*/
-class BunnyStorageService implements HealthCheckable
+class BunnyStorageService
{
protected ?Client $publicClient = null;
@@ -555,84 +555,19 @@ public function vBucketList(string $domain, string $path = '', string $zone = 'p
return $this->list($scopedPath, $zone);
}
- // ─────────────────────────────────────────────────────────────────────────────
- // Health Check (implements HealthCheckable)
- // ─────────────────────────────────────────────────────────────────────────────
-
- /**
- * Perform a health check on the CDN storage zones.
- *
- * Tests connectivity by listing the root directory of configured storage zones.
- * Returns a HealthCheckResult with status, latency, and zone information.
- */
- public function healthCheck(): HealthCheckResult
- {
- $publicConfigured = $this->isConfigured('public');
- $privateConfigured = $this->isConfigured('private');
-
- if (! $publicConfigured && ! $privateConfigured) {
- return HealthCheckResult::unknown('No CDN storage zones configured');
- }
-
- $results = [];
- $startTime = microtime(true);
- $hasError = false;
- $isDegraded = false;
-
- // Check public zone
- if ($publicConfigured) {
- $publicResult = $this->checkZoneHealth('public');
- $results['public'] = $publicResult;
- if (! $publicResult['success']) {
- $hasError = true;
- } elseif ($publicResult['latency_ms'] > 1000) {
- $isDegraded = true;
- }
- }
-
- // Check private zone
- if ($privateConfigured) {
- $privateResult = $this->checkZoneHealth('private');
- $results['private'] = $privateResult;
- if (! $privateResult['success']) {
- $hasError = true;
- } elseif ($privateResult['latency_ms'] > 1000) {
- $isDegraded = true;
- }
- }
-
- $totalLatency = (microtime(true) - $startTime) * 1000;
-
- if ($hasError) {
- return HealthCheckResult::unhealthy(
- 'One or more CDN storage zones are unreachable',
- ['zones' => $results],
- $totalLatency
- );
- }
-
- if ($isDegraded) {
- return HealthCheckResult::degraded(
- 'CDN storage zones responding slowly',
- ['zones' => $results],
- $totalLatency
- );
- }
-
- return HealthCheckResult::healthy(
- 'All configured CDN storage zones operational',
- ['zones' => $results],
- $totalLatency
- );
- }
-
/**
* Check health of a specific storage zone.
*
+ * Public because it is the seam the health-reporting decorator in
+ * dappcore/service consumes. That decorator composes this service rather
+ * than extending it, so a protected probe would be unreachable and the
+ * relocation would cost the detail — latency and the error string — that
+ * makes a health report worth reading.
+ *
* @param string $zone 'public' or 'private'
* @return array{success: bool, latency_ms: float, error?: string}
*/
- protected function checkZoneHealth(string $zone): array
+ public function checkZoneHealth(string $zone): array
{
$startTime = microtime(true);
@@ -677,7 +612,8 @@ protected function checkZoneHealth(string $zone): array
/**
* Perform a quick connectivity check.
*
- * Simpler than healthCheck() - just returns true/false.
+ * A boolean answer, for callers that only need reachability. The detailed
+ * per-zone probe is checkZoneHealth().
*
* @param string $zone 'public', 'private', or 'any' (default)
*/
diff --git a/src/Core/Tests/Feature/CdnIntegrationTest.php b/src/Core/Tests/Feature/CdnIntegrationTest.php
index c07e3d6..b4abd07 100644
--- a/src/Core/Tests/Feature/CdnIntegrationTest.php
+++ b/src/Core/Tests/Feature/CdnIntegrationTest.php
@@ -12,6 +12,7 @@
namespace Core\Tests\Feature;
use Core\Cdn\Services\AssetPipeline;
+use Core\Cdn\Services\BunnyStorageService;
use Core\Cdn\Services\CdnUrlBuilder;
use Core\Cdn\Services\StorageUrlResolver;
use Core\Tests\TestCase;
@@ -67,9 +68,24 @@ protected function setUp(): void
'visibility' => 'private',
]);
- // Initialize services
+ // Initialize services.
+ //
+ // StorageUrlResolver takes the storage service first and the URL builder
+ // second, and the builder is the optional one. This passed the builder
+ // into the first parameter and omitted the required argument entirely,
+ // so every test in this file died in setUp on a TypeError — 30 of them,
+ // reported as 30 failures rather than as the one line they are.
+ //
+ // BunnyStorageService comes from the container, which is how production
+ // gets it: Core\Cdn\Boot registers StorageUrlResolver as a singleton and
+ // lets Laravel autowire the chain. Resolving it here rather than
+ // hand-building BunnyStorageService(ConfigService) keeps the test on the
+ // same path the application uses.
$this->urlBuilder = new CdnUrlBuilder();
- $this->urlResolver = new StorageUrlResolver($this->urlBuilder);
+ $this->urlResolver = new StorageUrlResolver(
+ app(BunnyStorageService::class),
+ $this->urlBuilder,
+ );
$this->assetPipeline = new AssetPipeline($this->urlResolver);
}