dla pozycji * feedu Google. permutation_hash w bazie ma format "attr-val|attr-val". * W URL feedu separator między parami to "_" (nie "/"), żeby URL był * jednym segmentem dopasowywalnym przez routing pp_routes. * * Test wywołuje prywatną metodę przez ReflectionMethod z minimalnymi * danymi produktu i sprawdza zawartość wynikowego DOMDocument. */ class ProductFeedLinkTest extends TestCase { private function buildRepoWithMocks(): ProductRepository { $mockDb = $this->createMock(\medoo::class); $mockDb->method('select')->willReturn([]); $mockDb->method('get')->willReturn(null); $repo = new ProductRepository($mockDb); // appendShippingToXml wywołuje $this->transportRepoForXml->lowestTransportPrice(). // Inicjalizacja w generateGoogleXmlFeed(); dla unit testu wstrzykujemy mock dynamicznie. $transportMock = $this->getMockBuilder(\Domain\Transport\TransportRepository::class) ->disableOriginalConstructor() ->getMock(); $transportMock->method('lowestTransportPrice')->willReturn(0.0); $repo->transportRepoForXml = $transportMock; return $repo; } private function invokeAppendCombination(ProductRepository $repo, array $product, array $combination): string { $doc = new \DOMDocument('1.0', 'UTF-8'); $channelNode = $doc->appendChild($doc->createElement('channel')); $method = new \ReflectionMethod(ProductRepository::class, 'appendCombinationToXml'); $method->setAccessible(true); $method->invoke($repo, $doc, $channelNode, $product, $combination, 'https', 'shop.example.com'); return $doc->saveXML(); } private function baseProduct(array $overrides = []): array { return array_merge([ 'id' => 123, 'ean' => '5901234567890', 'language' => [ 'name' => 'Produkt testowy', 'xml_name' => '', 'short_description' => 'Opis', 'meta_title' => '', 'seo_link' => 'sukienka-czerwona', ], 'price_brutto' => 100, 'price_brutto_promo' => 0, 'quantity' => 10, 'stock_0_buy' => 0, 'wp' => 1, 'images' => [], ], $overrides); } public function testCombinationLinkUsesUnderscoreInSeoLinkBranch() { $repo = $this->buildRepoWithMocks(); $product = $this->baseProduct(); $combination = [ 'id' => 555, 'permutation_hash' => '20-170|21-175', 'price_brutto' => 120, 'price_brutto_promo' => 0, 'quantity' => 5, 'stock_0_buy' => 0, ]; $xml = $this->invokeAppendCombination($repo, $product, $combination); $this->assertStringContainsString( 'https://shop.example.com/sukienka-czerwona/20-170_21-175', $xml, 'Link feedu z seo_link musi używać "_" jako separatora par attr-val' ); $this->assertStringNotContainsString( '20-170/21-175', $xml, 'Link feedu nie może zawierać starego separatora "/" między parami atrybutów' ); } public function testCombinationLinkUsesUnderscoreInFallbackBranch() { $repo = $this->buildRepoWithMocks(); $product = $this->baseProduct([ 'language' => [ 'name' => 'Sukienka czerwona', 'xml_name' => '', 'short_description' => 'Opis', 'meta_title' => '', 'seo_link' => '', ], ]); $combination = [ 'id' => 555, 'permutation_hash' => '20-170|21-175', 'price_brutto' => 120, 'price_brutto_promo' => 0, 'quantity' => 5, 'stock_0_buy' => 0, ]; $xml = $this->invokeAppendCombination($repo, $product, $combination); // Fallback uses "p-{id}-{seo(name)}/...". Helpers::seo stub returns input unchanged. $this->assertStringContainsString( 'https://shop.example.com/p-123-Sukienka czerwona/20-170_21-175', $xml, 'Link fallback (bez seo_link) musi używać "_" jako separatora par attr-val' ); } public function testCombinationLinkWithSinglePair() { $repo = $this->buildRepoWithMocks(); $product = $this->baseProduct(); $combination = [ 'id' => 555, 'permutation_hash' => '20-170', 'price_brutto' => 120, 'price_brutto_promo' => 0, 'quantity' => 5, 'stock_0_buy' => 0, ]; $xml = $this->invokeAppendCombination($repo, $product, $combination); $this->assertStringContainsString( 'https://shop.example.com/sukienka-czerwona/20-170', $xml, 'Pojedyncza para attr-val pozostaje bez zmian (str_replace nie ma co podmieniać)' ); } }