DuplicatePreventionTest.php
1 month ago
EddItemTemplateBcTest.php
1 month ago
EddProviderGateTest.php
1 month ago
EddTitleRenderingBcTest.php
1 month ago
Smash782AirbnbUrlListingIdTest.php
1 month ago
Smash782BookingUrlHotelIdTest.php
1 month ago
DuplicatePreventionTest.php
226 lines
| 1 | <?php |
| 2 | |
| 3 | namespace SmashBalloon\Reviews\Tests\Unit\Providers; |
| 4 | |
| 5 | use PHPUnit\Framework\TestCase; |
| 6 | |
| 7 | /** |
| 8 | * Unit tests for duplicate review prevention. |
| 9 | * |
| 10 | * Tests the unique constraint logic that prevents duplicate reviews |
| 11 | * from being inserted due to race conditions. |
| 12 | */ |
| 13 | class DuplicatePreventionTest extends TestCase |
| 14 | { |
| 15 | /* |
| 16 | |-------------------------------------------------------------------------- |
| 17 | | Unique Key Generation Tests |
| 18 | |-------------------------------------------------------------------------- |
| 19 | */ |
| 20 | |
| 21 | /** |
| 22 | * Test that unique key is built from post_id, provider_id, and lang. |
| 23 | */ |
| 24 | public function test_unique_key_components(): void |
| 25 | { |
| 26 | $review1 = [ |
| 27 | 'post_id' => '5034', |
| 28 | 'provider_id' => 'wc_multi_abc123', |
| 29 | 'lang' => '', |
| 30 | ]; |
| 31 | |
| 32 | $review2 = [ |
| 33 | 'post_id' => '5034', |
| 34 | 'provider_id' => 'wc_multi_abc123', |
| 35 | 'lang' => '', |
| 36 | ]; |
| 37 | |
| 38 | // Same key = duplicate |
| 39 | $key1 = $this->buildUniqueKey($review1); |
| 40 | $key2 = $this->buildUniqueKey($review2); |
| 41 | |
| 42 | $this->assertEquals($key1, $key2); |
| 43 | } |
| 44 | |
| 45 | /** |
| 46 | * Test that different provider_ids produce different keys. |
| 47 | */ |
| 48 | public function test_different_provider_ids_are_unique(): void |
| 49 | { |
| 50 | $review1 = [ |
| 51 | 'post_id' => '5034', |
| 52 | 'provider_id' => 'wc_multi_abc123', |
| 53 | 'lang' => '', |
| 54 | ]; |
| 55 | |
| 56 | $review2 = [ |
| 57 | 'post_id' => '5034', |
| 58 | 'provider_id' => 'wc_multi_xyz789', |
| 59 | 'lang' => '', |
| 60 | ]; |
| 61 | |
| 62 | $key1 = $this->buildUniqueKey($review1); |
| 63 | $key2 = $this->buildUniqueKey($review2); |
| 64 | |
| 65 | $this->assertNotEquals($key1, $key2); |
| 66 | } |
| 67 | |
| 68 | /** |
| 69 | * Test that same review with different languages are unique. |
| 70 | */ |
| 71 | public function test_different_languages_are_unique(): void |
| 72 | { |
| 73 | $review1 = [ |
| 74 | 'post_id' => '5034', |
| 75 | 'provider_id' => 'ChIJabc123', |
| 76 | 'lang' => 'en', |
| 77 | ]; |
| 78 | |
| 79 | $review2 = [ |
| 80 | 'post_id' => '5034', |
| 81 | 'provider_id' => 'ChIJabc123', |
| 82 | 'lang' => 'de', |
| 83 | ]; |
| 84 | |
| 85 | $key1 = $this->buildUniqueKey($review1); |
| 86 | $key2 = $this->buildUniqueKey($review2); |
| 87 | |
| 88 | $this->assertNotEquals($key1, $key2); |
| 89 | } |
| 90 | |
| 91 | /* |
| 92 | |-------------------------------------------------------------------------- |
| 93 | | WooCommerce Specific Tests |
| 94 | |-------------------------------------------------------------------------- |
| 95 | */ |
| 96 | |
| 97 | /** |
| 98 | * Test that WooCommerce comment IDs are used as post_id. |
| 99 | */ |
| 100 | public function test_woocommerce_uses_comment_id_as_post_id(): void |
| 101 | { |
| 102 | $commentId = 5034; |
| 103 | $normalizedReview = $this->normalizeWooCommerceReview($commentId, 'Test Review'); |
| 104 | |
| 105 | $this->assertEquals((string) $commentId, $normalizedReview['review_id']); |
| 106 | } |
| 107 | |
| 108 | /** |
| 109 | * Test that multi-product source ID is preserved. |
| 110 | */ |
| 111 | public function test_multi_product_source_id_format(): void |
| 112 | { |
| 113 | $productIds = [1, 2, 3]; |
| 114 | $sourceId = $this->generateMultiProductSourceId($productIds); |
| 115 | |
| 116 | $this->assertStringStartsWith('wc_multi_', $sourceId); |
| 117 | $this->assertEquals(41, strlen($sourceId)); // wc_multi_ (8) + md5 hash (32) + time suffix |
| 118 | } |
| 119 | |
| 120 | /* |
| 121 | |-------------------------------------------------------------------------- |
| 122 | | Migration Routine Tests |
| 123 | |-------------------------------------------------------------------------- |
| 124 | */ |
| 125 | |
| 126 | /** |
| 127 | * Test that duplicate removal identifies correct duplicates. |
| 128 | */ |
| 129 | public function test_duplicate_identification(): void |
| 130 | { |
| 131 | $posts = [ |
| 132 | ['id' => 1, 'post_id' => '5034', 'provider_id' => 'wc_multi_abc', 'lang' => ''], |
| 133 | ['id' => 2, 'post_id' => '5034', 'provider_id' => 'wc_multi_abc', 'lang' => ''], // Duplicate |
| 134 | ['id' => 3, 'post_id' => '5035', 'provider_id' => 'wc_multi_abc', 'lang' => ''], // Different post_id |
| 135 | ['id' => 4, 'post_id' => '5034', 'provider_id' => 'wc_multi_xyz', 'lang' => ''], // Different provider_id |
| 136 | ]; |
| 137 | |
| 138 | $duplicateIds = $this->findDuplicateIds($posts); |
| 139 | |
| 140 | $this->assertCount(1, $duplicateIds); |
| 141 | $this->assertContains(2, $duplicateIds); // Only id=2 is a duplicate |
| 142 | } |
| 143 | |
| 144 | /** |
| 145 | * Test that first occurrence is kept, duplicates are removed. |
| 146 | */ |
| 147 | public function test_keeps_first_occurrence(): void |
| 148 | { |
| 149 | $posts = [ |
| 150 | ['id' => 10, 'post_id' => '5034', 'provider_id' => 'wc_multi_abc', 'lang' => ''], |
| 151 | ['id' => 20, 'post_id' => '5034', 'provider_id' => 'wc_multi_abc', 'lang' => ''], |
| 152 | ['id' => 30, 'post_id' => '5034', 'provider_id' => 'wc_multi_abc', 'lang' => ''], |
| 153 | ]; |
| 154 | |
| 155 | $duplicateIds = $this->findDuplicateIds($posts); |
| 156 | |
| 157 | // Should identify 2 duplicates (id 20 and 30), keeping id 10 |
| 158 | $this->assertCount(2, $duplicateIds); |
| 159 | $this->assertNotContains(10, $duplicateIds); |
| 160 | $this->assertContains(20, $duplicateIds); |
| 161 | $this->assertContains(30, $duplicateIds); |
| 162 | } |
| 163 | |
| 164 | /* |
| 165 | |-------------------------------------------------------------------------- |
| 166 | | Helper Methods |
| 167 | |-------------------------------------------------------------------------- |
| 168 | */ |
| 169 | |
| 170 | /** |
| 171 | * Build unique key from review data. |
| 172 | */ |
| 173 | private function buildUniqueKey(array $review): string |
| 174 | { |
| 175 | return $review['post_id'] . '|' . $review['provider_id'] . '|' . $review['lang']; |
| 176 | } |
| 177 | |
| 178 | /** |
| 179 | * Simulate WooCommerce review normalization. |
| 180 | */ |
| 181 | private function normalizeWooCommerceReview(int $commentId, string $content): array |
| 182 | { |
| 183 | return [ |
| 184 | 'review_id' => (string) $commentId, |
| 185 | 'text' => $content, |
| 186 | 'rating' => 5, |
| 187 | 'time' => time(), |
| 188 | 'reviewer' => [ |
| 189 | 'name' => 'Test User', |
| 190 | ], |
| 191 | 'provider' => [ |
| 192 | 'name' => 'woocommerce', |
| 193 | ], |
| 194 | ]; |
| 195 | } |
| 196 | |
| 197 | /** |
| 198 | * Generate multi-product source ID. |
| 199 | */ |
| 200 | private function generateMultiProductSourceId(array $productIds): string |
| 201 | { |
| 202 | return 'wc_multi_' . md5(implode('_', $productIds) . '_' . time()); |
| 203 | } |
| 204 | |
| 205 | /** |
| 206 | * Find duplicate IDs in posts array. |
| 207 | */ |
| 208 | private function findDuplicateIds(array $posts): array |
| 209 | { |
| 210 | $seen = []; |
| 211 | $duplicates = []; |
| 212 | |
| 213 | foreach ($posts as $post) { |
| 214 | $key = $this->buildUniqueKey($post); |
| 215 | |
| 216 | if (isset($seen[$key])) { |
| 217 | $duplicates[] = $post['id']; |
| 218 | } else { |
| 219 | $seen[$key] = $post['id']; |
| 220 | } |
| 221 | } |
| 222 | |
| 223 | return $duplicates; |
| 224 | } |
| 225 | } |
| 226 |