generate(); if (strlen($keyPair->publicKey) >= 16 && strlen($keyPair->privateKey) >= 16) { echo " ✅ VAPID keys generated\n"; echo " Public Key: " . substr($keyPair->publicKey, 0, 24) . "...\n"; echo " Private Key: " . substr($keyPair->privateKey, 0, 24) . "...\n\n"; } else { throw new \Exception("Invalid key lengths"); } } catch (\Exception $e) { echo " ❌ Failed: {$e->getMessage()}\n"; exit(1); } // Test 2: Subscription Repository echo "Test 2: Subscription Repository (Cache-based)\n"; try { $fileCache = new FileCache(); $serializer = SerializerFactory::createPhpSerializer(); $cache = new GeneralCache($fileCache, $serializer); $repository = new CacheSubscriptionRepository($cache); // Create mock subscription $subscription = new PushSubscription( endpoint: UrlFactory::parse('https://fcm.googleapis.com/fcm/send/test-endpoint-12345'), p256dhKey: 'BMockP256dhKeyBase64UrlSafeEncodedString', authToken: 'MockAuthSecretBase64UrlSafeString', userId: 'user-123', userAgent: 'Mozilla/5.0 Test Browser', createdAt: new \DateTimeImmutable() ); // Save subscription $saved = $repository->save($subscription); if ($saved) { echo " ✅ Subscription saved\n"; echo " Hash: " . $subscription->getHash() . "\n"; // Retrieve subscription $retrieved = $repository->findByHash($subscription->getHash()); if ($retrieved && $retrieved->endpoint->toString() === $subscription->endpoint->toString()) { echo " ✅ Subscription retrieved successfully\n\n"; } else { throw new \Exception("Retrieved subscription doesn't match"); } } else { throw new \Exception("Failed to save subscription"); } } catch (\Exception $e) { echo " ❌ Failed: {$e->getMessage()}\n"; echo " Stack trace:\n"; echo $e->getTraceAsString() . "\n"; exit(1); } // Test 3: Push Message Value Object echo "Test 3: Push Message Value Object\n"; try { $message = PushMessage::simple('Test Title', 'Test notification body'); if ($message->title === 'Test Title' && $message->body === 'Test notification body') { echo " ✅ PushMessage created\n"; echo " Title: {$message->title}\n"; echo " Body: {$message->body}\n"; echo " TTL: {$message->ttl} seconds\n\n"; } else { throw new \Exception("Message properties don't match"); } } catch (\Exception $e) { echo " ❌ Failed: {$e->getMessage()}\n"; exit(1); } // Test 4: WebPushService Initialization echo "Test 4: WebPushService Initialization\n"; try { $webPushService = new WebPushService( vapidKeys: $keyPair, vapidSubject: 'mailto:test@example.com' ); echo " ✅ WebPushService initialized\n\n"; } catch (\Exception $e) { echo " ❌ Failed: {$e->getMessage()}\n"; exit(1); } // Summary echo "=== Test Summary ===\n"; echo "✅ All tests passed!\n\n"; echo "Web Push System Components:\n"; echo " - VAPID Key Generation\n"; echo " - Subscription Storage (Cache)\n"; echo " - Push Message Value Objects\n"; echo " - WebPushService (RFC 8291/8292 compliant)\n\n"; echo "Next Steps:\n"; echo " 1. Generate VAPID keys: docker exec php php console.php vapid:generate\n"; echo " 2. Add keys to .env: VAPID_PUBLIC_KEY=... VAPID_PRIVATE_KEY=...\n"; echo " 3. Run migration: docker exec php php console.php db:migrate\n"; echo " 4. Test endpoint: curl -X POST https://localhost/api/push/subscribe\n";