Separator URL miedzy parami attr-val zmieniony z "/" na "_" w generatorze feedu (ProductRepository::appendCombinationToXml). Wzorzec routingu pp_routes rozszerzony do [0-9_-]+ w Helpers::htacces (oba warianty: seo_link i fallback p-id-name). LayoutEngine konwertuje "_" -> "|" przed wywolaniem ProductRepository::findCached — format DB pozostaje "|". Partial product-attribute.php preselectuje wartosc z permutation_hash URL (forced_value_id), co poprawia UX wejscia z linka feedu. Suita: 834 -> 841 testow (+7), 2330 assertions. Wymagane akcje na produkcji po deployu: regeneracja pp_routes (Helpers::htacces), wyczyszczenie klucza pp_routes:all w Redis, regeneracja google-feed.xml, resubmit feedu w GMC. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
152 lines
5.5 KiB
PHP
152 lines
5.5 KiB
PHP
<?php
|
|
namespace Tests\Unit\Domain\Product;
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
use Domain\Product\ProductRepository;
|
|
|
|
/**
|
|
* Phase 18 — testy generatora linku do feedu Google.
|
|
*
|
|
* ProductRepository::appendCombinationToXml buduje <link> 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(
|
|
'<link>https://shop.example.com/sukienka-czerwona/20-170_21-175</link>',
|
|
$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(
|
|
'<link>https://shop.example.com/p-123-Sukienka czerwona/20-170_21-175</link>',
|
|
$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(
|
|
'<link>https://shop.example.com/sukienka-czerwona/20-170</link>',
|
|
$xml,
|
|
'Pojedyncza para attr-val pozostaje bez zmian (str_replace nie ma co podmieniać)'
|
|
);
|
|
}
|
|
}
|