Doubles
1 week ago
Providers
1 week ago
BulkRearmOnGrowthTest.php
1 week ago
BulkReviewsUpdateStuckStateTest.php
1 week ago
ClearCacheRelayResetTest.php
1 week ago
DeleteSourceRelayFailureTest.php
1 week ago
ErrorHandlerFalsyOptionTest.php
1 week ago
FeedCacheUpdateServiceTest.php
1 week ago
FeedMalformedPayloadTest.php
1 week ago
ForceKeylessRefetchTest.php
1 week ago
LicenseDeactivateStaleStateTest.php
1 week ago
MediaFinderMemoTest.php
1 week ago
MultiSourceAggregationTest.php
1 week ago
ReconcileMigratedLicenseRoutineTest.php
1 week ago
ReconcileRemovalTest.php
1 week ago
RegisterWebsiteRoutineTest.php
1 week ago
RelaySlowEndpointsTest.php
1 week ago
RemoteRequestMemoTest.php
1 week ago
ReviewAlertHeaderTotalsTest.php
1 week ago
ReviewAlertPageTargetingTest.php
1 week ago
ReviewAlertStarFillTest.php
1 week ago
ShortcodeNeutralizationTest.php
1 week ago
SiteMigrationRecoveryTest.php
1 week ago
Smash1130UsageTrackingHardeningTest.php
1 week ago
Smash1130UsageTrackingHooksTest.php
1 week ago
Smash1583HeaderParityTest.php
1 week ago
Smash1631MultiLanguageBulkTest.php
1 week ago
Smash1631UpdateSingleLangScopeTest.php
1 week ago
Smash1706TripAdvisorPlaceIdTest.php
1 week ago
Smash1756SchemaServiceTest.php
1 week ago
Smash1785AvatarLocalUrlGuardTest.php
1 week ago
Smash1785AvatarReHealTest.php
1 week ago
Smash1795ReviewTextXssTest.php
1 week ago
Smash1835TripAdvisorKeyShapeTest.php
1 week ago
Smash1973WordpressOrgPlaceIdNullTest.php
1 week ago
Smash1987NestedSourceErrorShapeTest.php
1 week ago
Smash782BookingHeaderRatingTest.php
1 week ago
Smash782CountryFlagEmojiTest.php
1 week ago
Smash782ExternalRefreshCronTest.php
1 week ago
Smash782ExtrasTemplateTest.php
1 week ago
Smash782ReviewAlertProviderDataTest.php
1 week ago
SourceIdLookupTest.php
1 week ago
WpmlGetCurrentLanguageTest.php
1 week ago
WpmlLanguageMappingTest.php
1 week ago
Smash1973WordpressOrgPlaceIdNullTest.php
156 lines
| 1 | <?php |
| 2 | |
| 3 | namespace SmashBalloon\Reviews\Tests\Unit; |
| 4 | |
| 5 | use PHPUnit\Framework\TestCase; |
| 6 | use SmashBalloon\Reviews\Common\Builder\SBR_Feed_Saver_Manager; |
| 7 | |
| 8 | /** |
| 9 | * SMASH-1973 — get_place_id_wordpressorg() raised two PHP 8 deprecations per |
| 10 | * call when handed anything that was not a string. |
| 11 | * |
| 12 | * Both callers can do that: |
| 13 | * |
| 14 | * - RemoteRequest.php:190 reads a stored `info['url']`, and `info` is an empty |
| 15 | * string on a source row that never completed a fetch, so the key is absent. |
| 16 | * - SBR_Feed_Saver_Manager::process_source_apikey() reads `$data['providerIdUrl']`, |
| 17 | * which update_api_key() never sets — it builds $data with provider + apiKey only. |
| 18 | * |
| 19 | * Measured before the fix on PHP 8.2: `trim(): Passing null` and |
| 20 | * `strpos(): Passing null`, three diagnostics per refresh including the |
| 21 | * "Undefined array key" from the read itself. |
| 22 | * |
| 23 | * The return SHAPE is deliberately unchanged and an unusable url still yields an |
| 24 | * empty slug, so callers behave exactly as they did. That is what the BC cases |
| 25 | * below pin: this removes diagnostics, not behaviour. |
| 26 | */ |
| 27 | final class Smash1973WordpressOrgPlaceIdNullTest extends TestCase |
| 28 | { |
| 29 | /** |
| 30 | * The regression: a non-string must not raise a diagnostic. |
| 31 | * |
| 32 | * Deprecations are not exceptions, so asserting the return value alone would |
| 33 | * pass on the old code too. The error handler is what makes this bite. |
| 34 | * |
| 35 | * @dataProvider nonStringProvider |
| 36 | * @param mixed $input |
| 37 | */ |
| 38 | public function test_a_non_string_url_raises_no_php_diagnostic($input, string $label): void |
| 39 | { |
| 40 | $raised = []; |
| 41 | set_error_handler(static function ($errno, $errstr) use (&$raised) { |
| 42 | $raised[] = $errstr; |
| 43 | |
| 44 | return true; |
| 45 | }); |
| 46 | |
| 47 | try { |
| 48 | $result = SBR_Feed_Saver_Manager::get_place_id_wordpressorg($input); |
| 49 | } finally { |
| 50 | restore_error_handler(); |
| 51 | } |
| 52 | |
| 53 | $this->assertSame([], $raised, "{$label} raised: " . implode(' | ', $raised)); |
| 54 | $this->assertIsArray($result, "{$label} must still return the array shape"); |
| 55 | $this->assertArrayHasKey('type', $result); |
| 56 | $this->assertArrayHasKey('slug', $result); |
| 57 | } |
| 58 | |
| 59 | /** |
| 60 | * @return array<string,array{0:mixed,1:string}> |
| 61 | */ |
| 62 | public static function nonStringProvider(): array |
| 63 | { |
| 64 | return [ |
| 65 | // The two shapes the real callers actually produce. |
| 66 | 'null (absent info.url / providerIdUrl)' => [null, 'null'], |
| 67 | 'empty string (info decoded to [])' => ['', 'empty string'], |
| 68 | // Defensive: a malformed row could hold either of these. |
| 69 | 'int' => [0, 'int'], |
| 70 | 'array' => [[], 'array'], |
| 71 | 'false' => [false, 'false'], |
| 72 | ]; |
| 73 | } |
| 74 | |
| 75 | /** |
| 76 | * An unusable url yields an empty slug, exactly as before the fix, so the |
| 77 | * relay request the callers build is byte-identical. |
| 78 | * |
| 79 | * @dataProvider unusableProvider |
| 80 | * @param mixed $input |
| 81 | */ |
| 82 | public function test_an_unusable_url_still_yields_an_empty_slug($input): void |
| 83 | { |
| 84 | $result = SBR_Feed_Saver_Manager::get_place_id_wordpressorg($input); |
| 85 | |
| 86 | $this->assertSame('', $result['slug']); |
| 87 | $this->assertSame('plugin', $result['type'], 'type defaults to plugin when "theme" is absent'); |
| 88 | } |
| 89 | |
| 90 | /** |
| 91 | * @return array<string,array{0:mixed}> |
| 92 | */ |
| 93 | public static function unusableProvider(): array |
| 94 | { |
| 95 | return [ |
| 96 | 'null' => [null], |
| 97 | 'empty string' => [''], |
| 98 | ]; |
| 99 | } |
| 100 | |
| 101 | /** |
| 102 | * A url with no path at all: `parse_url()` returns null for the PHP_URL_PATH |
| 103 | * component, which the old explode() received directly. Same class of bug, |
| 104 | * different door, and reachable from a stored url that is just a host. |
| 105 | */ |
| 106 | public function test_a_url_with_no_path_raises_no_diagnostic(): void |
| 107 | { |
| 108 | $raised = []; |
| 109 | set_error_handler(static function ($errno, $errstr) use (&$raised) { |
| 110 | $raised[] = $errstr; |
| 111 | |
| 112 | return true; |
| 113 | }); |
| 114 | |
| 115 | try { |
| 116 | $result = SBR_Feed_Saver_Manager::get_place_id_wordpressorg('https://wordpress.org'); |
| 117 | } finally { |
| 118 | restore_error_handler(); |
| 119 | } |
| 120 | |
| 121 | $this->assertSame([], $raised, 'path-less url raised: ' . implode(' | ', $raised)); |
| 122 | $this->assertSame('', $result['slug']); |
| 123 | } |
| 124 | |
| 125 | /** |
| 126 | * BACKWARDS COMPATIBILITY (Rule 6) — the shapes that have always worked must |
| 127 | * still resolve to the same slug and type. If this suite ever fails, the fix |
| 128 | * changed behaviour rather than only silencing diagnostics. |
| 129 | * |
| 130 | * @dataProvider realUrlProvider |
| 131 | */ |
| 132 | public function test_real_urls_are_unchanged(string $url, string $type, string $slug): void |
| 133 | { |
| 134 | $this->assertSame( |
| 135 | ['type' => $type, 'slug' => $slug], |
| 136 | SBR_Feed_Saver_Manager::get_place_id_wordpressorg($url), |
| 137 | "Regressed on: {$url}" |
| 138 | ); |
| 139 | } |
| 140 | |
| 141 | /** |
| 142 | * @return array<string,array{0:string,1:string,2:string}> |
| 143 | */ |
| 144 | public static function realUrlProvider(): array |
| 145 | { |
| 146 | return [ |
| 147 | 'plugin, trailing slash' => ['https://wordpress.org/plugins/instagram-feed/', 'plugin', 'instagram-feed'], |
| 148 | 'plugin, no trailing slash' => ['https://wordpress.org/plugins/instagram-feed', 'plugin', 'instagram-feed'], |
| 149 | 'theme, trailing slash' => ['https://wordpress.org/themes/twentytwentythree/', 'theme', 'twentytwentythree'], |
| 150 | 'theme, no trailing slash' => ['https://wordpress.org/themes/twentytwentythree', 'theme', 'twentytwentythree'], |
| 151 | 'reviews sub-path' => ['https://wordpress.org/plugins/reviews-feed/reviews/', 'plugin', 'reviews'], |
| 152 | 'scheme-less plugin' => ['wordpress.org/plugins/instagram-feed/', 'plugin', 'instagram-feed'], |
| 153 | ]; |
| 154 | } |
| 155 | } |
| 156 |