Doubles
2 months ago
Providers
2 months ago
BulkReviewsUpdateStuckStateTest.php
2 months ago
ClearCacheRelayResetTest.php
2 months ago
DeleteSourceRelayFailureTest.php
2 months ago
ErrorHandlerFalsyOptionTest.php
2 months ago
FeedCacheUpdateServiceTest.php
2 months ago
FeedMalformedPayloadTest.php
2 months ago
ForceKeylessRefetchTest.php
2 months ago
LicenseDeactivateStaleStateTest.php
2 months ago
MediaFinderMemoTest.php
2 months ago
MultiSourceAggregationTest.php
2 months ago
ReconcileMigratedLicenseRoutineTest.php
2 months ago
ReconcileRemovalTest.php
2 months ago
RegisterWebsiteRoutineTest.php
2 months ago
RemoteRequestMemoTest.php
2 months ago
ReviewAlertHeaderTotalsTest.php
2 months ago
ReviewAlertPageTargetingTest.php
2 months ago
ReviewAlertStarFillTest.php
2 months ago
ShortcodeNeutralizationTest.php
2 months ago
SiteMigrationRecoveryTest.php
2 months ago
Smash1583HeaderParityTest.php
2 months ago
SourceIdLookupTest.php
2 months ago
WpmlGetCurrentLanguageTest.php
2 months ago
WpmlLanguageMappingTest.php
2 months ago
MediaFinderMemoTest.php
123 lines
| 1 | <?php |
| 2 | |
| 3 | namespace SmashBalloon\Reviews\Tests\Unit; |
| 4 | |
| 5 | use PHPUnit\Framework\TestCase; |
| 6 | use SmashBalloon\Reviews\Pro\MediaFinder; |
| 7 | |
| 8 | /** |
| 9 | * Unit tests for the per-request memoization on MediaFinder::search() |
| 10 | * (SMASH-1360 Phase 2 round 2). |
| 11 | * |
| 12 | * Pins the memo-key contract: same `(provider, primary_url)` within ONE PHP |
| 13 | * request collapses to the cached media-array. Different reviewers / different |
| 14 | * places / different providers get distinct keys. |
| 15 | * |
| 16 | * The wp_lhr_log captured on demo-wp2 2026-05-06 showed the customizer-side |
| 17 | * direct-to-Google scraping amplifier — 25 calls / 10 unique reviewers = |
| 18 | * 2.5× duplication ratio. Without this memo, every shortcode independently |
| 19 | * fetches each reviewer's `https://www.google.com/maps/contrib/...` profile. |
| 20 | * With this memo, identical (reviewer, place_id) pairs across shortcodes |
| 21 | * collapse to one fetch. |
| 22 | * |
| 23 | * @group memo |
| 24 | * @group SMASH-1360 |
| 25 | */ |
| 26 | class MediaFinderMemoTest extends TestCase |
| 27 | { |
| 28 | protected function setUp(): void |
| 29 | { |
| 30 | parent::setUp(); |
| 31 | MediaFinder::flush_search_memo(); |
| 32 | } |
| 33 | |
| 34 | /** |
| 35 | * Helper: build a MediaFinder pre-populated with provider + primary_url. |
| 36 | * We use reflection because primary_url is private and normally set via |
| 37 | * `construct_url_from_post_and_source()`, which requires a real $post. |
| 38 | */ |
| 39 | private function makeFinder(string $provider, string $primaryUrl): MediaFinder |
| 40 | { |
| 41 | $finder = new MediaFinder(['info' => '{"id":"X"}']); |
| 42 | $finder->set_provider($provider); |
| 43 | |
| 44 | // primary_url is private; use reflection to set it. |
| 45 | $ref = new \ReflectionProperty(MediaFinder::class, 'primary_url'); |
| 46 | $ref->setAccessible(true); |
| 47 | $ref->setValue($finder, $primaryUrl); |
| 48 | |
| 49 | return $finder; |
| 50 | } |
| 51 | |
| 52 | public function test_memo_key_collapses_for_same_provider_and_url(): void |
| 53 | { |
| 54 | $a = $this->makeFinder('google', 'https://www.google.com/maps/contrib/123/place/CHIJ_X'); |
| 55 | $b = $this->makeFinder('google', 'https://www.google.com/maps/contrib/123/place/CHIJ_X'); |
| 56 | |
| 57 | $this->assertSame($a->search_memo_key(), $b->search_memo_key()); |
| 58 | } |
| 59 | |
| 60 | public function test_memo_key_differs_for_different_reviewers(): void |
| 61 | { |
| 62 | $a = $this->makeFinder('google', 'https://www.google.com/maps/contrib/AAA/place/CHIJ_X'); |
| 63 | $b = $this->makeFinder('google', 'https://www.google.com/maps/contrib/BBB/place/CHIJ_X'); |
| 64 | |
| 65 | $this->assertNotSame($a->search_memo_key(), $b->search_memo_key()); |
| 66 | } |
| 67 | |
| 68 | public function test_memo_key_differs_for_different_places(): void |
| 69 | { |
| 70 | $a = $this->makeFinder('google', 'https://www.google.com/maps/contrib/123/place/CHIJ_AFI'); |
| 71 | $b = $this->makeFinder('google', 'https://www.google.com/maps/contrib/123/place/CHIJ_GOOGLEPLEX'); |
| 72 | |
| 73 | $this->assertNotSame($a->search_memo_key(), $b->search_memo_key()); |
| 74 | } |
| 75 | |
| 76 | public function test_memo_key_differs_for_different_providers(): void |
| 77 | { |
| 78 | // Yelp + TripAdvisor have their own primary_url shapes; the memo must |
| 79 | // not collapse them with Google even if URLs were structurally similar. |
| 80 | $google = $this->makeFinder('google', 'https://www.google.com/maps/contrib/X/place/Y'); |
| 81 | $yelp = $this->makeFinder('yelp', 'https://www.google.com/maps/contrib/X/place/Y'); // synthetic |
| 82 | |
| 83 | $this->assertNotSame($google->search_memo_key(), $yelp->search_memo_key()); |
| 84 | } |
| 85 | |
| 86 | public function test_memo_key_returns_null_when_primary_url_empty(): void |
| 87 | { |
| 88 | $finder = $this->makeFinder('google', ''); |
| 89 | |
| 90 | $this->assertNull($finder->search_memo_key()); |
| 91 | } |
| 92 | |
| 93 | public function test_memo_key_is_deterministic_sha256(): void |
| 94 | { |
| 95 | $finder = $this->makeFinder('google', 'https://www.google.com/maps/contrib/X/place/Y'); |
| 96 | $expected = hash('sha256', 'google|https://www.google.com/maps/contrib/X/place/Y'); |
| 97 | |
| 98 | $this->assertSame($expected, $finder->search_memo_key()); |
| 99 | } |
| 100 | |
| 101 | public function test_memo_key_handles_long_urls(): void |
| 102 | { |
| 103 | // Defensive — Google contributor IDs can be long, place_ids can be |
| 104 | // ~30 chars, plus query params. Memo key derivation must be stable |
| 105 | // regardless of URL length. |
| 106 | $longUrl = 'https://www.google.com/maps/contrib/' . str_repeat('1', 50) |
| 107 | . '/place/' . str_repeat('a', 60) |
| 108 | . '?hl=en&extra=1'; |
| 109 | $finder = $this->makeFinder('google', $longUrl); |
| 110 | |
| 111 | $this->assertNotNull($finder->search_memo_key()); |
| 112 | // sha256 hex output is always 64 chars regardless of input length |
| 113 | $this->assertSame(64, strlen($finder->search_memo_key())); |
| 114 | } |
| 115 | |
| 116 | public function test_flush_search_memo_clears_state(): void |
| 117 | { |
| 118 | // Test isolation contract — same as RemoteRequestMemoTest's flush_memo. |
| 119 | MediaFinder::flush_search_memo(); |
| 120 | $this->assertTrue(true); |
| 121 | } |
| 122 | } |
| 123 |