unique-id-123 https://example.com/article '; $parser = new FeedParser($xml); $parser->init(); $items = $parser->get_items(); $this->assertCount(1, $items); $this->assertEquals('unique-id-123', $items[0]->get_id()); } public function testRssGetIdFallsBackToLink(): void { $xml = ' https://example.com/article '; $parser = new FeedParser($xml); $parser->init(); $items = $parser->get_items(); $this->assertCount(1, $items); $this->assertEquals('https://example.com/article', $items[0]->get_id()); } public function testRssGetDateFromPubDate(): void { $xml = ' Mon, 01 Jan 2024 12:00:00 GMT '; $parser = new FeedParser($xml); $parser->init(); $items = $parser->get_items(); $this->assertCount(1, $items); $timestamp = $items[0]->get_date(); $this->assertEquals(1704110400, $timestamp); // 2024-01-01 12:00:00 GMT } public function testRssGetDateFromDcDate(): void { $xml = ' 2024-01-01T12:00:00Z '; $parser = new FeedParser($xml); $parser->init(); $items = $parser->get_items(); $this->assertCount(1, $items); $timestamp = $items[0]->get_date(); $this->assertEquals(1704110400, $timestamp); // 2024-01-01 12:00:00 GMT } public function testRssGetDateReturnsFalseForMissingDate(): void { $xml = ' No date '; $parser = new FeedParser($xml); $parser->init(); $items = $parser->get_items(); $this->assertCount(1, $items); $this->assertFalse($items[0]->get_date()); } public function testRssGetLinkFromAtomLink(): void { $xml = ' https://example.com/old-link '; $parser = new FeedParser($xml); $parser->init(); $items = $parser->get_items(); $this->assertCount(1, $items); $this->assertEquals('https://example.com/article', $items[0]->get_link()); } public function testRssGetLinkFromPermaLinkGuid(): void { $xml = ' https://example.com/permalink https://example.com/link '; $parser = new FeedParser($xml); $parser->init(); $items = $parser->get_items(); $this->assertCount(1, $items); // Permalink guid is checked AFTER atom:link but BEFORE // Since there's no atom:link, permalink guid is used $this->assertEquals('https://example.com/permalink', $items[0]->get_link()); } public function testRssGetLinkFromLinkElement(): void { $xml = ' https://example.com/article '; $parser = new FeedParser($xml); $parser->init(); $items = $parser->get_items(); $this->assertCount(1, $items); $this->assertEquals('https://example.com/article', $items[0]->get_link()); } public function testRssGetTitle(): void { $xml = ' Test Article Title '; $parser = new FeedParser($xml); $parser->init(); $items = $parser->get_items(); $this->assertCount(1, $items); $this->assertEquals('Test Article Title', $items[0]->get_title()); } public function testRssGetContentFromEncoded(): void { $xml = ' Full article content

]]>
Short description
'; $parser = new FeedParser($xml); $parser->init(); $items = $parser->get_items(); $this->assertCount(1, $items); $content = $items[0]->get_content(); $this->assertStringContainsString('Full article content', $content); } public function testRssGetContentPrefersLonger(): void { $xml = ' Short This is a much longer description with more text '; $parser = new FeedParser($xml); $parser->init(); $items = $parser->get_items(); $this->assertCount(1, $items); $content = $items[0]->get_content(); $this->assertStringContainsString('longer description', $content); } // ===== FeedItem_Atom Tests ===== public function testAtomGetIdFromIdElement(): void { $xml = ' tag:example.com,2024:entry-123 '; $parser = new FeedParser($xml); $parser->init(); $items = $parser->get_items(); $this->assertCount(1, $items); $this->assertEquals('tag:example.com,2024:entry-123', $items[0]->get_id()); } public function testAtomGetIdFallsBackToLink(): void { $xml = ' '; $parser = new FeedParser($xml); $parser->init(); $items = $parser->get_items(); $this->assertCount(1, $items); $this->assertEquals('https://example.com/article', $items[0]->get_id()); } public function testAtomGetDateFromUpdated(): void { $xml = ' 2024-01-01T12:00:00Z 2024-01-01T10:00:00Z '; $parser = new FeedParser($xml); $parser->init(); $items = $parser->get_items(); $this->assertCount(1, $items); $timestamp = $items[0]->get_date(); $this->assertEquals(1704110400, $timestamp); // updated time } public function testAtomGetDateFromPublished(): void { $xml = ' 2024-01-01T12:00:00Z '; $parser = new FeedParser($xml); $parser->init(); $items = $parser->get_items(); $this->assertCount(1, $items); $timestamp = $items[0]->get_date(); $this->assertEquals(1704110400, $timestamp); } public function testAtomGetLink(): void { $xml = ' '; $parser = new FeedParser($xml); $parser->init(); $items = $parser->get_items(); $this->assertCount(1, $items); $this->assertEquals('https://example.com/article', $items[0]->get_link()); } public function testAtomGetLinkWithoutRel(): void { $xml = ' '; $parser = new FeedParser($xml); $parser->init(); $items = $parser->get_items(); $this->assertCount(1, $items); $this->assertEquals('https://example.com/article', $items[0]->get_link()); } public function testAtomGetTitle(): void { $xml = ' Atom Entry Title '; $parser = new FeedParser($xml); $parser->init(); $items = $parser->get_items(); $this->assertCount(1, $items); $this->assertEquals('Atom Entry Title', $items[0]->get_title()); } public function testAtomGetContentFromContent(): void { $xml = ' Full content

]]>
Short summary
'; $parser = new FeedParser($xml); $parser->init(); $items = $parser->get_items(); $this->assertCount(1, $items); $content = $items[0]->get_content(); $this->assertStringContainsString('Full content', $content); } public function testAtomGetContentReturnsContentOnly(): void { $xml = ' Short This is a much longer summary with more details '; $parser = new FeedParser($xml); $parser->init(); $items = $parser->get_items(); $this->assertCount(1, $items); // Atom get_content() returns only , not $this->assertEquals('Short', $items[0]->get_content()); } // ===== Edge Cases ===== public function testRssEmptyItemReturnsEmptyStrings(): void { $xml = ' '; $parser = new FeedParser($xml); $parser->init(); $items = $parser->get_items(); $this->assertCount(1, $items); $this->assertEquals('', $items[0]->get_link()); $this->assertEquals('', $items[0]->get_title()); } public function testAtomEmptyEntryReturnsEmptyStrings(): void { $xml = ' '; $parser = new FeedParser($xml); $parser->init(); $items = $parser->get_items(); $this->assertCount(1, $items); $this->assertEquals('', $items[0]->get_link()); $this->assertEquals('', $items[0]->get_title()); } // ===== FeedItem_Common::normalize_categories() Tests ===== public function testNormalizeCategoriesHandlesSingleCategory(): void { $cats = ['Technology']; $result = FeedItem_Common::normalize_categories($cats); $this->assertCount(1, $result); $this->assertContains('technology', $result); } public function testNormalizeCategoriesHandlesCommaSeparatedCategories(): void { $cats = ['Tech,News,Programming']; $result = FeedItem_Common::normalize_categories($cats); $this->assertCount(3, $result); $this->assertContains('tech', $result); $this->assertContains('news', $result); $this->assertContains('programming', $result); } public function testNormalizeCategoriesConvertsToLowercase(): void { $cats = ['TECHNOLOGY', 'NeWs', 'PrOgRaMmInG']; $result = FeedItem_Common::normalize_categories($cats); $this->assertContains('technology', $result); $this->assertContains('news', $result); $this->assertContains('programming', $result); $this->assertNotContains('TECHNOLOGY', $result); } public function testNormalizeCategoriesTrimsWhitespace(): void { $cats = [' technology ', ' news ', 'programming']; $result = FeedItem_Common::normalize_categories($cats); $this->assertContains('technology', $result); $this->assertContains('news', $result); $this->assertNotContains(' technology ', $result); } public function testNormalizeCategoriesRemovesDuplicates(): void { $cats = ['tech', 'Tech', 'TECH', 'technology', 'tech']; $result = FeedItem_Common::normalize_categories($cats); // After lowercase normalization, 'tech' appears 4 times, 'technology' once $this->assertContains('tech', $result); $this->assertContains('technology', $result); // Count occurrences - 'tech' should appear only once $tech_count = count(array_filter($result, fn($c) => $c === 'tech')); $this->assertEquals(1, $tech_count); } public function testNormalizeCategoriesRemovesEmptyValues(): void { $cats = ['tech', '', ' ', 'news', '']; $result = FeedItem_Common::normalize_categories($cats); $this->assertCount(2, $result); $this->assertContains('tech', $result); $this->assertContains('news', $result); $this->assertNotContains('', $result); } public function testNormalizeCategoriesHandlesNumericTags(): void { $cats = ['123', '456']; $result = FeedItem_Common::normalize_categories($cats); // Numeric tags are prefixed with 't:' $this->assertContains('t:123', $result); $this->assertContains('t:456', $result); $this->assertNotContains('123', $result); } public function testNormalizeCategoriesRemovesSpecialCharacters(): void { $cats = ["tech,news", "tech'news", 'tech"news']; $result = FeedItem_Common::normalize_categories($cats); // Commas split categories, quotes are removed $this->assertContains('tech', $result); $this->assertContains('news', $result); $this->assertContains('technews', $result); } public function testNormalizeCategoriesTruncatesLongCategories(): void { $long_cat = str_repeat('a', 300); $cats = [$long_cat]; $result = FeedItem_Common::normalize_categories($cats); $this->assertCount(1, $result); $this->assertEquals(250, mb_strlen($result[0])); $this->assertEquals(str_repeat('a', 250), $result[0]); } public function testNormalizeCategoriesHandlesMultibyteCharacters(): void { $cats = ['技術', 'ニュース', '日本']; $result = FeedItem_Common::normalize_categories($cats); $this->assertCount(3, $result); $this->assertContains('技術', $result); $this->assertContains('ニュース', $result); $this->assertContains('日本', $result); } public function testNormalizeCategoriesSortsResults(): void { $cats = ['zebra', 'apple', 'mango', 'banana']; $result = FeedItem_Common::normalize_categories($cats); // Results should be sorted alphabetically $expected = ['apple', 'banana', 'mango', 'zebra']; $this->assertEquals($expected, array_values($result)); } public function testNormalizeCategoriesHandlesEmptyArray(): void { $cats = []; $result = FeedItem_Common::normalize_categories($cats); $this->assertIsArray($result); $this->assertEmpty($result); } public function testNormalizeCategoriesHandlesComplexInput(): void { $cats = [ 'Tech,News', ' PROGRAMMING ', '123', 'Tech', // duplicate "tech'news", // special chars '', // empty str_repeat('x', 300), // too long ]; $result = FeedItem_Common::normalize_categories($cats); // Should contain: tech, news, programming, t:123, technews, xxx... (250 chars) $this->assertContains('tech', $result); $this->assertContains('news', $result); $this->assertContains('programming', $result); $this->assertContains('t:123', $result); $this->assertContains('technews', $result); $this->assertContains(str_repeat('x', 250), $result); // Count occurrences - 'tech' should appear only once despite duplicates $tech_count = count(array_filter($result, fn($c) => $c === 'tech')); $this->assertEquals(1, $tech_count); } }