From 3df38ae2a711fcb583d9d4e294465ba402dd8a94 Mon Sep 17 00:00:00 2001
From: Oskar Stark
Date: Fri, 12 Sep 2025 18:26:16 +0200
Subject: [PATCH] [Demo] Add tests for `FeedLoader`
---
demo/tests/Blog/FeedLoaderTest.php | 159 ++++++++++++++++++++
demo/tests/Blog/fixtures/symfony-feed.xml | 175 ++++++++++++++++++++++
2 files changed, 334 insertions(+)
create mode 100644 demo/tests/Blog/FeedLoaderTest.php
create mode 100644 demo/tests/Blog/fixtures/symfony-feed.xml
diff --git a/demo/tests/Blog/FeedLoaderTest.php b/demo/tests/Blog/FeedLoaderTest.php
new file mode 100644
index 000000000..120dc5e69
--- /dev/null
+++ b/demo/tests/Blog/FeedLoaderTest.php
@@ -0,0 +1,159 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace App\Tests\Blog;
+
+use App\Blog\FeedLoader;
+use App\Blog\Post;
+use PHPUnit\Framework\Attributes\CoversClass;
+use PHPUnit\Framework\Attributes\UsesClass;
+use PHPUnit\Framework\TestCase;
+use Symfony\AI\Store\Document\TextDocument;
+use Symfony\AI\Store\Exception\InvalidArgumentException;
+use Symfony\Component\HttpClient\MockHttpClient;
+use Symfony\Component\HttpClient\Response\MockResponse;
+use Symfony\Component\Uid\Uuid;
+
+#[CoversClass(FeedLoader::class)]
+#[UsesClass(Post::class)]
+final class FeedLoaderTest extends TestCase
+{
+ public function testLoadWithValidFeedUrl()
+ {
+ $loader = new FeedLoader(new MockHttpClient(MockResponse::fromFile(__DIR__.'/fixtures/symfony-feed.xml')));
+ $documents = iterator_to_array($loader->load('https://feeds.feedburner.com/symfony/blog'));
+
+ $this->assertCount(2, $documents);
+
+ // Test first document
+ $firstDocument = $documents[0];
+ $this->assertInstanceOf(TextDocument::class, $firstDocument);
+
+ $expectedFirstUuid = Uuid::v5(Uuid::fromString('6ba7b810-9dad-11d1-80b4-00c04fd430c8'), 'Save the date, SymfonyDay Montreal 2026!');
+ $this->assertEquals($expectedFirstUuid, $firstDocument->id);
+
+ $this->assertStringContainsString('Title: Save the date, SymfonyDay Montreal 2026!', $firstDocument->text);
+ $this->assertStringContainsString('From: Paola Suárez on 2025-09-11', $firstDocument->text);
+ $this->assertStringContainsString("We're thrilled to announce that SymfonyDay Montreal is happening on June 4, 2026!", $firstDocument->text);
+ $this->assertStringContainsString('Mark your calendars, tell your friends', $firstDocument->text);
+
+ $firstMetadata = $firstDocument->metadata->toArray();
+ $this->assertSame($expectedFirstUuid->toRfc4122(), $firstMetadata['id']);
+ $this->assertSame('Save the date, SymfonyDay Montreal 2026!', $firstMetadata['title']);
+ $this->assertSame('https://symfony.com/blog/save-the-date-symfonyday-montreal-2026?utm_source=Symfony%20Blog%20Feed&utm_medium=feed', $firstMetadata['link']);
+ $this->assertStringContainsString("We're thrilled to announce that SymfonyDay Montreal is happening on June 4, 2026!", $firstMetadata['description']);
+ $this->assertStringContainsString('Mark your calendars, tell your friends', $firstMetadata['content']);
+ $this->assertSame('Paola Suárez', $firstMetadata['author']);
+ $this->assertSame('2025-09-11', $firstMetadata['date']);
+
+ // Test second document
+ $secondDocument = $documents[1];
+ $this->assertInstanceOf(TextDocument::class, $secondDocument);
+
+ $expectedSecondUuid = Uuid::v5(Uuid::fromString('6ba7b810-9dad-11d1-80b4-00c04fd430c8'), 'SymfonyCon Amsterdam 2025: Call for IT student volunteers: Volunteer, Learn & Connect!');
+ $this->assertEquals($expectedSecondUuid, $secondDocument->id);
+
+ $this->assertStringContainsString('Title: SymfonyCon Amsterdam 2025: Call for IT student volunteers: Volunteer, Learn & Connect!', $secondDocument->text);
+ $this->assertStringContainsString('From: Paola Suárez on 2025-09-10', $secondDocument->text);
+ $this->assertStringContainsString('🎓SymfonyCon Amsterdam 2025: Call for IT Student Volunteers!', $secondDocument->text);
+
+ $secondMetadata = $secondDocument->metadata->toArray();
+ $this->assertSame($expectedSecondUuid->toRfc4122(), $secondMetadata['id']);
+ $this->assertSame('SymfonyCon Amsterdam 2025: Call for IT student volunteers: Volunteer, Learn & Connect!', $secondMetadata['title']);
+ $this->assertSame('https://symfony.com/blog/symfonycon-amsterdam-2025-call-for-it-student-volunteers-volunteer-learn-and-connect?utm_source=Symfony%20Blog%20Feed&utm_medium=feed', $secondMetadata['link']);
+ $this->assertStringContainsString('🎓SymfonyCon Amsterdam 2025: Call for IT Student Volunteers!', $secondMetadata['description']);
+ $this->assertStringContainsString('🎓SymfonyCon Amsterdam 2025: Call for IT Student Volunteers!', $secondMetadata['content']);
+ $this->assertSame('Paola Suárez', $secondMetadata['author']);
+ $this->assertSame('2025-09-10', $secondMetadata['date']);
+ }
+
+ public function testLoadWithNullSource()
+ {
+ $loader = new FeedLoader(new MockHttpClient([]));
+
+ $this->expectException(InvalidArgumentException::class);
+ $this->expectExceptionMessage('FeedLoader requires a RSS feed URL as source, null given.');
+
+ iterator_to_array($loader->load(null));
+ }
+
+ public function testLoadWithEmptyFeed()
+ {
+ $emptyFeedXml = <<
+
+
+ Empty Feed
+ https://example.com/
+ An empty RSS feed
+
+
+XML;
+
+ $loader = new FeedLoader(new MockHttpClient(new MockResponse($emptyFeedXml)));
+ $documents = iterator_to_array($loader->load('https://example.com/feed.xml'));
+
+ $this->assertCount(0, $documents);
+ }
+
+ public function testLoadWithHttpError()
+ {
+ $loader = new FeedLoader(new MockHttpClient(new MockResponse('', ['http_code' => 404])));
+
+ $this->expectException(\Symfony\Contracts\HttpClient\Exception\ClientException::class);
+
+ iterator_to_array($loader->load('https://example.com/non-existent-feed.xml'));
+ }
+
+ public function testLoadWithMalformedXml()
+ {
+ $malformedXml = 'Test';
+
+ $loader = new FeedLoader(new MockHttpClient(new MockResponse($malformedXml)));
+
+ $this->expectException(\Exception::class);
+
+ iterator_to_array($loader->load('https://example.com/malformed-feed.xml'));
+ }
+
+ public function testLoadReturnsIterableOfTextDocuments()
+ {
+ $loader = new FeedLoader(new MockHttpClient(MockResponse::fromFile(__DIR__.'/fixtures/symfony-feed.xml')));
+ $result = $loader->load('https://feeds.feedburner.com/symfony/blog');
+
+ $this->assertIsIterable($result);
+
+ foreach ($result as $document) {
+ $this->assertInstanceOf(TextDocument::class, $document);
+ $this->assertInstanceOf(Uuid::class, $document->id);
+ $this->assertIsString($document->text);
+ $this->assertNotEmpty($document->text);
+ $this->assertIsArray($document->metadata->toArray());
+ }
+ }
+
+ public function testLoadGeneratesConsistentUuids()
+ {
+ $loader = new FeedLoader(new MockHttpClient(MockResponse::fromFile(__DIR__.'/fixtures/symfony-feed.xml')));
+ $documents1 = iterator_to_array($loader->load('https://feeds.feedburner.com/symfony/blog'));
+
+ // Load same feed again
+ $loader2 = new FeedLoader(new MockHttpClient(MockResponse::fromFile(__DIR__.'/fixtures/symfony-feed.xml')));
+ $documents2 = iterator_to_array($loader2->load('https://feeds.feedburner.com/symfony/blog'));
+
+ $this->assertCount(2, $documents1);
+ $this->assertCount(2, $documents2);
+
+ // UUIDs should be identical for same content
+ $this->assertEquals($documents1[0]->id, $documents2[0]->id);
+ $this->assertEquals($documents1[1]->id, $documents2[1]->id);
+ }
+}
diff --git a/demo/tests/Blog/fixtures/symfony-feed.xml b/demo/tests/Blog/fixtures/symfony-feed.xml
new file mode 100644
index 000000000..d20a27f00
--- /dev/null
+++ b/demo/tests/Blog/fixtures/symfony-feed.xml
@@ -0,0 +1,175 @@
+
+
+
+ Symfony Blog
+
+ https://symfony.com/blog/
+ Most recent posts published on the Symfony project blog
+ Fri, 12 Sep 2025 14:25:38 +0200
+ Thu, 11 Sep 2025 14:30:00 +0200
+ en
+
+
+ https://symfony.com/blog/save-the-date-symfonyday-montreal-2026?utm_source=Symfony%20Blog%20Feed&utm_medium=feed
+
+
+
+
+We're thrilled to announce that SymfonyDay Montreal is happening on June 4, 2026! 🎉
+
+Mark your calendars, tell your friends, and get ready for a day full of inspiring talks, networking opportunities, and the vibrant energy of the Symfony community…
+
+
+
+
+
We're thrilled to announce that SymfonyDay Montreal is happening on June 4, 2026! 🎉
+
+
Mark your calendars, tell your friends, and get ready for a day full of inspiring talks, networking opportunities, and the vibrant energy of the Symfony community in Canada! 🍁
Submit your talk for SymfonyDay Montreal 2026! Every selected speaker will receive a complimentary conference ticket and a speaker gift! Speakers who do not live in the conference city will also have their travel and accommodation expenses covered.
+
+
If you have never spoken at a conference before, you can take advantage of our speaker mentoring program! Experienced speakers will gladly assist you in preparing for your conference, whether it involves creating your slides or rehearsing your talk. Feel free to seek advice in the #diversity or #speaker-mentoring channels on the Symfony Devs Slack or include comments requesting guidance when submitting your talk proposal.
+
+
+
+
🤝 Want to support the event?
+
+
Become a sponsor and showcase your brand to an engaged audience of developers.
+
+
⚡Contact Hadrien Cren by email to learn more about the options.
+ ]]>
+ https://symfony.com/blog/save-the-date-symfonyday-montreal-2026?utm_source=Symfony%20Blog%20Feed&utm_medium=feed
+
+ Thu, 11 Sep 2025 14:30:00 +0200
+ https://symfony.com/blog/save-the-date-symfonyday-montreal-2026?utm_source=Symfony%20Blog%20Feed&utm_medium=feed#comments-list
+
+
+
+ https://symfony.com/blog/symfonycon-amsterdam-2025-call-for-it-student-volunteers-volunteer-learn-and-connect?utm_source=Symfony%20Blog%20Feed&utm_medium=feed
+
+
+
+
+🎓SymfonyCon Amsterdam 2025: Call for IT Student Volunteers!
+
+Are you an IT or computer science student looking to gain hands-on experience, grow your network, and take part in an international tech event? 🎤✨
+Symfony is looking for motivated student…
+
+
+
+
+
🎓SymfonyCon Amsterdam 2025: Call for IT Student Volunteers!
+
+
Are you an IT or computer science student looking to gain hands-on experience, grow your network, and take part in an international tech event? 🎤✨
+Symfony is looking for motivated student volunteers to join the adventure at SymfonyCon Amsterdam 2025!
+
+
📍Where: Amsterdam, The Netherlands
+
+
🗓️When: November 28–29, 2025
+
+
Volunteering with us means helping out for one day and getting a free ticket to attend the conference the other day, so you don't miss out on the action!
+
+
+
+
💼 Volunteer Tasks
+
+
As a volunteer, you'll be assigned to:
+
+
✔️Welcome attendees
+
+
✔️Provide directions and information
+
+
✔️Help ensure everything runs smoothly!
+
+
+
+
✅ Requirements
+
+
To apply, you must:
+
+
✔️Be a student in computer science / IT (all backgrounds welcome!)
+
+
✔️Be curious about careers in development or tech
+
+
✔️Be available for at least one full day during the event
+
+
✔️Speak English (conference language)
+
+
+
+
🎁 What You Get
+
+
Free access to the full 2-day SymfonyCon Amsterdam 2025 conference
+
+
✔️Opportunities to network with developers, speakers, and community leaders
+
+
✔️Delicious lunches included
+
+
✔️An unforgettable experience in an inclusive and friendly environment!
+
+
+
+
🚫 Please note:
+
+
We are unable to cover:
+
+
✔️Travel or accommodation costs ✔️Dinners or evening expenses
+
+
+
+
💌 How to Apply:
+
+
Interested? Send us an email at events@symfony.com with:
+
+
✔️Your name, school, and area of study
+
+
✔️The reason you'd like to volunteer
+
+
✔️Confirmation of your availability on November 28 or 29, 2025
+
+
+
+
Be part of the Symfony community, gain valuable experience, and help make this event amazing!
+We can't wait to meet you! 🧑💻💬 #SymfonyCon