Doubles
3 weeks ago
Providers
3 weeks ago
BulkRearmOnGrowthTest.php
3 weeks ago
BulkReviewsUpdateStuckStateTest.php
3 weeks ago
ClearCacheRelayResetTest.php
3 weeks ago
DeleteSourceRelayFailureTest.php
3 weeks ago
ErrorHandlerFalsyOptionTest.php
3 weeks ago
FeedCacheUpdateServiceTest.php
3 weeks ago
FeedMalformedPayloadTest.php
3 weeks ago
ForceKeylessRefetchTest.php
3 weeks ago
LicenseDeactivateStaleStateTest.php
3 weeks ago
MediaFinderMemoTest.php
3 weeks ago
MultiSourceAggregationTest.php
3 weeks ago
ReconcileMigratedLicenseRoutineTest.php
3 weeks ago
ReconcileRemovalTest.php
3 weeks ago
RegisterWebsiteRoutineTest.php
3 weeks ago
RemoteRequestMemoTest.php
3 weeks ago
ReviewAlertHeaderTotalsTest.php
3 weeks ago
ReviewAlertPageTargetingTest.php
3 weeks ago
ReviewAlertStarFillTest.php
3 weeks ago
ShortcodeNeutralizationTest.php
3 weeks ago
SiteMigrationRecoveryTest.php
3 weeks ago
Smash1583HeaderParityTest.php
3 weeks ago
Smash1631MultiLanguageBulkTest.php
3 weeks ago
Smash1631UpdateSingleLangScopeTest.php
3 weeks ago
Smash1706TripAdvisorPlaceIdTest.php
3 weeks ago
Smash1756SchemaServiceTest.php
3 weeks ago
Smash1785AvatarLocalUrlGuardTest.php
3 weeks ago
Smash1785AvatarReHealTest.php
3 weeks ago
Smash1795ReviewTextXssTest.php
3 weeks ago
Smash782BookingHeaderRatingTest.php
3 weeks ago
Smash782CountryFlagEmojiTest.php
3 weeks ago
Smash782ExternalRefreshCronTest.php
3 weeks ago
Smash782ExtrasTemplateTest.php
3 weeks ago
Smash782ReviewAlertProviderDataTest.php
3 weeks ago
SourceIdLookupTest.php
3 weeks ago
WpmlGetCurrentLanguageTest.php
3 weeks ago
WpmlLanguageMappingTest.php
3 weeks ago
BulkRearmOnGrowthTest.php
141 lines
| 1 | <?php |
| 2 | |
| 3 | namespace SmashBalloon\Reviews\Tests\Unit; |
| 4 | |
| 5 | use PHPUnit\Framework\TestCase; |
| 6 | use SmashBalloon\Reviews\Pro\Services\BulkUpdate\Bulk_Reviews_Update; |
| 7 | |
| 8 | /** |
| 9 | * SMASH-1634 — change-driven re-arm of the one-shot paginated backfill. |
| 10 | * |
| 11 | * The bug: after a source's bulk history completes (`is_done`), it never re-runs, |
| 12 | * so review batches larger than the hourly incremental cap (Google/Yelp keyed |
| 13 | * API returns only the 5 newest bodies) never load without a manual "reset bulk |
| 14 | * history". `maybe_rearm_source()` re-opens ONE source's backfill when its |
| 15 | * upstream review count grows. |
| 16 | * |
| 17 | * These pin the exact contract, including the cost guards (seed-on-first-sight, |
| 18 | * key/provider gating) so the fix can't silently start a mass re-backfill. |
| 19 | * |
| 20 | * @group bulk-history |
| 21 | * @group smash-1634 |
| 22 | */ |
| 23 | class BulkRearmOnGrowthTest extends TestCase |
| 24 | { |
| 25 | protected function setUp(): void |
| 26 | { |
| 27 | parent::setUp(); |
| 28 | global $wp_options_mock; |
| 29 | $wp_options_mock = []; |
| 30 | } |
| 31 | |
| 32 | /** Keyed google/yelp needs an API key for the bulk (RapidAPI) path. */ |
| 33 | private function withApiKey(string $provider = 'google'): void |
| 34 | { |
| 35 | global $wp_options_mock; |
| 36 | $wp_options_mock['sbr_apikeys'] = [$provider => 'TEST_KEY']; |
| 37 | } |
| 38 | |
| 39 | private function seedBulk(array $state): void |
| 40 | { |
| 41 | global $wp_options_mock; |
| 42 | $wp_options_mock['sbr_bulk_sources'] = $state; |
| 43 | } |
| 44 | |
| 45 | private function bulkState(string $account_id): array |
| 46 | { |
| 47 | global $wp_options_mock; |
| 48 | return $wp_options_mock['sbr_bulk_sources'][$account_id] ?? []; |
| 49 | } |
| 50 | |
| 51 | /** |
| 52 | * First time we observe a completed source, we only SEED the baseline — |
| 53 | * we must NOT re-arm. This is the cost guard that stops deploying the fix |
| 54 | * from re-backfilling every existing source at once. |
| 55 | */ |
| 56 | public function test_first_observation_seeds_baseline_without_rearm(): void |
| 57 | { |
| 58 | $this->withApiKey('google'); |
| 59 | $id = 'ChIJ_SEED'; |
| 60 | $this->seedBulk([$id => ['account_id' => $id, 'provider' => 'google', 'is_done' => true, 'page' => 3]]); |
| 61 | |
| 62 | $rearmed = Bulk_Reviews_Update::maybe_rearm_source('google', $id, 233); |
| 63 | |
| 64 | $this->assertFalse($rearmed, 'First sight of a done source must not re-arm.'); |
| 65 | $state = $this->bulkState($id); |
| 66 | $this->assertTrue($state['is_done'], 'is_done must stay true on the seed pass.'); |
| 67 | $this->assertSame(233, $state['last_total'], 'Baseline last_total must be seeded.'); |
| 68 | } |
| 69 | |
| 70 | /** No growth since the last backfill → no re-arm. */ |
| 71 | public function test_no_growth_does_not_rearm(): void |
| 72 | { |
| 73 | $this->withApiKey('google'); |
| 74 | $id = 'ChIJ_FLAT'; |
| 75 | $this->seedBulk([$id => ['account_id' => $id, 'provider' => 'google', 'is_done' => true, 'page' => 3, 'last_total' => 233]]); |
| 76 | |
| 77 | $this->assertFalse(Bulk_Reviews_Update::maybe_rearm_source('google', $id, 233)); |
| 78 | $this->assertTrue($this->bulkState($id)['is_done'], 'Equal count must leave is_done untouched.'); |
| 79 | } |
| 80 | |
| 81 | /** THE FIX: upstream count grew → re-open the backfill for that source. */ |
| 82 | public function test_growth_rearms_source(): void |
| 83 | { |
| 84 | $this->withApiKey('google'); |
| 85 | $id = 'ChIJ_GROW'; |
| 86 | $this->seedBulk([$id => ['account_id' => $id, 'provider' => 'google', 'is_done' => true, 'page' => 3, 'last_total' => 233]]); |
| 87 | |
| 88 | $rearmed = Bulk_Reviews_Update::maybe_rearm_source('google', $id, 246); |
| 89 | |
| 90 | $this->assertTrue($rearmed, 'Count growth must re-arm the source.'); |
| 91 | $state = $this->bulkState($id); |
| 92 | $this->assertFalse($state['is_done'], 'Re-arm must clear is_done so the backfill re-runs.'); |
| 93 | $this->assertSame(1, $state['page'], 'Re-arm must reset pagination to page 1 (matches the proven 1-indexed fresh init).'); |
| 94 | $this->assertSame(246, $state['last_total'], 'Re-arm must record the new baseline (prevents re-loop).'); |
| 95 | } |
| 96 | |
| 97 | /** BC: a source still mid-backfill (is_done false) is left to the normal flow. */ |
| 98 | public function test_not_done_source_is_untouched(): void |
| 99 | { |
| 100 | $this->withApiKey('google'); |
| 101 | $id = 'ChIJ_RUNNING'; |
| 102 | $this->seedBulk([$id => ['account_id' => $id, 'provider' => 'google', 'is_done' => false, 'page' => 1]]); |
| 103 | |
| 104 | $this->assertFalse(Bulk_Reviews_Update::maybe_rearm_source('google', $id, 999)); |
| 105 | $state = $this->bulkState($id); |
| 106 | $this->assertFalse($state['is_done']); |
| 107 | $this->assertArrayNotHasKey('last_total', $state, 'Running source must not be mutated.'); |
| 108 | } |
| 109 | |
| 110 | /** Cost guard: keyless (no API key) is not handled by this bulk service → never re-arm. */ |
| 111 | public function test_no_api_key_never_rearms(): void |
| 112 | { |
| 113 | // no withApiKey() |
| 114 | $id = 'ChIJ_KEYLESS'; |
| 115 | $this->seedBulk([$id => ['account_id' => $id, 'provider' => 'google', 'is_done' => true, 'page' => 3, 'last_total' => 100]]); |
| 116 | |
| 117 | $this->assertFalse(Bulk_Reviews_Update::maybe_rearm_source('google', $id, 500)); |
| 118 | $this->assertTrue($this->bulkState($id)['is_done'], 'Keyless source must be untouched.'); |
| 119 | } |
| 120 | |
| 121 | /** Guard: providers this bulk service doesn't own are ignored. */ |
| 122 | public function test_non_google_yelp_provider_ignored(): void |
| 123 | { |
| 124 | $this->withApiKey('google'); |
| 125 | $id = 'qa_booking'; |
| 126 | $this->seedBulk([$id => ['account_id' => $id, 'provider' => 'booking', 'is_done' => true, 'page' => 3, 'last_total' => 10]]); |
| 127 | |
| 128 | $this->assertFalse(Bulk_Reviews_Update::maybe_rearm_source('booking', $id, 50)); |
| 129 | } |
| 130 | |
| 131 | /** Guard: a zero/unknown current count is a no-op (don't act on missing data). */ |
| 132 | public function test_zero_current_total_is_noop(): void |
| 133 | { |
| 134 | $this->withApiKey('google'); |
| 135 | $id = 'ChIJ_ZERO'; |
| 136 | $this->seedBulk([$id => ['account_id' => $id, 'provider' => 'google', 'is_done' => true, 'page' => 3, 'last_total' => 5]]); |
| 137 | |
| 138 | $this->assertFalse(Bulk_Reviews_Update::maybe_rearm_source('google', $id, 0)); |
| 139 | } |
| 140 | } |
| 141 |