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
DeleteSourceRelayFailureTest.php
83 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 | * Pins SBR_Feed_Saver_Manager::relay_source_removed() — the relay/remove verdict |
| 10 | * used by delete_souce() (PR #482 Copilot review, C5). |
| 11 | * |
| 12 | * SBRelay::call() returns the full body on success (`success: true`) and the |
| 13 | * UNWRAPPED error envelope on failure (`{ id, code, success: false }`, NO `error` |
| 14 | * key). The original check gated on `$relay_response['error']`, which never |
| 15 | * matched a real failure, so a failed `source/remove` read as success and the |
| 16 | * source was deleted locally anyway — orphaning the relay-side source, which then |
| 17 | * keeps counting against the per-license source cap. |
| 18 | * |
| 19 | * These exercise the verdict directly across every envelope shape, so a relay |
| 20 | * rename (e.g. `sourceNotFound`) or a change to how `call()` unwraps errors can't |
| 21 | * silently re-orphan sources or start blocking legitimate deletes. |
| 22 | */ |
| 23 | final class DeleteSourceRelayFailureTest extends TestCase |
| 24 | { |
| 25 | private static function removed($relay_response): bool |
| 26 | { |
| 27 | $m = new \ReflectionMethod(SBR_Feed_Saver_Manager::class, 'relay_source_removed'); |
| 28 | $m->setAccessible(true); |
| 29 | |
| 30 | return (bool) $m->invoke(null, $relay_response); |
| 31 | } |
| 32 | |
| 33 | public function test_success_response_allows_local_delete(): void |
| 34 | { |
| 35 | // What `respondWithSuccess([], 'Source removed.')` looks like after call(). |
| 36 | $this->assertTrue(self::removed(['success' => true, 'message' => 'Source removed.'])); |
| 37 | } |
| 38 | |
| 39 | public function test_generic_failure_blocks_local_delete(): void |
| 40 | { |
| 41 | // Unwrapped error envelope — the case the old `['error']` check missed. |
| 42 | $this->assertFalse(self::removed(['id' => 'unknownError', 'code' => 400, 'success' => false])); |
| 43 | } |
| 44 | |
| 45 | public function test_auth_failure_blocks_local_delete(): void |
| 46 | { |
| 47 | $this->assertFalse(self::removed(['id' => 'invalidToken', 'code' => 401, 'success' => false])); |
| 48 | } |
| 49 | |
| 50 | public function test_source_not_found_counts_as_removed(): void |
| 51 | { |
| 52 | $this->assertTrue(self::removed(['id' => 'sourceNotFound', 'code' => 404, 'success' => false])); |
| 53 | } |
| 54 | |
| 55 | public function test_http_404_without_id_counts_as_removed(): void |
| 56 | { |
| 57 | $this->assertTrue(self::removed(['code' => 404, 'success' => false])); |
| 58 | } |
| 59 | |
| 60 | public function test_unreachable_relay_fails_open(): void |
| 61 | { |
| 62 | // WP_Error → empty decoded body → proceed (prior, documented behaviour). |
| 63 | $this->assertTrue(self::removed([])); |
| 64 | } |
| 65 | |
| 66 | public function test_malformed_id_or_code_does_not_fatal(): void |
| 67 | { |
| 68 | // Pathological shapes must not throw — just fall through to "not removed". |
| 69 | $this->assertFalse(self::removed(['id' => ['x'], 'code' => ['y'], 'success' => false])); |
| 70 | } |
| 71 | |
| 72 | public function test_source_info_json_decode_is_array_guarded(): void |
| 73 | { |
| 74 | // C4: delete_souce() must coerce a malformed json_decode() result to an |
| 75 | // array before reading relay_source_id (PHP 8 offset-on-null guard). |
| 76 | $path = __DIR__ . '/../../class/Common/Builder/SBR_Feed_Saver_Manager.php'; |
| 77 | $this->assertStringContainsString( |
| 78 | 'if (!is_array($source_info))', |
| 79 | (string) file_get_contents($path) |
| 80 | ); |
| 81 | } |
| 82 | } |
| 83 |