PluginProbe
ووسلام – همگام سازی ووکامرس و باسلام / 1.10.29
ووسلام – همگام سازی ووکامرس و باسلام v1.10.29
1.10.29 1.10.28 1.1.26 1.10.27 1.10.24 1.10.25 1.10.23 1.10.22 1.10.21 1.10.19 1.10.20 1.10.18 1.10.17 1.10.15 1.10.14 1.10.13 1.10.12 1.10.10 1.10.9 1.10.8 1.10.7 1.10.6 1.10.5 1.10.4 1.10.3 All 62 releases
← All changes | tests/JobsRunnerTest.php +132 -19 1.10.27 → 1.10.29 View file →
@@ -14,8 +14,10 @@
14 14 private $originalRequest;
15 15 private $originalCookie;
16 16 private $originalWpdb;
17 17 private $hadOriginalWpdb;
18 + private $originalOptions;
19 + private $hadOriginalOptions;
18 20
19 21 protected function setUp(): void
20 22 {
21 23 $this->originalRequest = $_REQUEST;
@@ -21,11 +23,14 @@
21 23 $this->originalRequest = $_REQUEST;
22 24 $this->originalCookie = $_COOKIE;
23 25 $this->hadOriginalWpdb = array_key_exists('wpdb', $GLOBALS);
24 26 $this->originalWpdb = $GLOBALS['wpdb'] ?? null;
27 + $this->hadOriginalOptions = array_key_exists('sync_basalam_test_options', $GLOBALS);
28 + $this->originalOptions = $GLOBALS['sync_basalam_test_options'] ?? null;
25 29
26 30 $_REQUEST = [];
27 31 $_COOKIE = [];
32 + $GLOBALS['sync_basalam_test_options'] = [];
28 33
29 34 $GLOBALS['sync_basalam_jobs_runner_test_state'] = [
30 35 'actions' => [],
31 36 'did_actions' => [],
@@ -51,8 +56,13 @@
51 56 unset($GLOBALS['wpdb']);
52 57 }
53 58
54 59 unset($GLOBALS['sync_basalam_jobs_runner_test_state']);
60 + if ($this->hadOriginalOptions) {
61 + $GLOBALS['sync_basalam_test_options'] = $this->originalOptions;
62 + } else {
63 + unset($GLOBALS['sync_basalam_test_options']);
64 + }
55 65 }
56 66
57 67 public function testRegistersDispatcherOnShutdownAndNeverOnInit(): void
58 68 {
@@ -82,8 +92,9 @@
82 92 $jobManager = new FakeJobManager([true]);
83 93 $runner = $this->newRunner($jobManager);
84 94
85 95 $_COOKIE = ['wordpress_test_cookie' => 'cookie-value'];
96 + $before = time();
86 97 $runner->maybeDispatchAsyncRequest();
87 98
88 99 self::assertSame(
89 100 ['get_transient', 'get_transient', 'has_pending_jobs', 'get_transient', 'set_transient', 'remote_post'],
@@ -89,16 +100,14 @@
89 100 ['get_transient', 'get_transient', 'has_pending_jobs', 'get_transient', 'set_transient', 'remote_post'],
90 101 $this->state()['events']
91 102 );
92 103 self::assertSame([120], $jobManager->timeouts);
93 - self::assertSame(
94 - [[
95 - 'name' => self::DISPATCH_LOCK,
96 - 'value' => 1,
97 - 'expiration' => 25,
98 - ]],
99 - $this->state()['transient_writes']
100 - );
104 + $writes = $this->state()['transient_writes'];
105 + self::assertCount(1, $writes);
106 + self::assertSame(self::DISPATCH_LOCK, $writes[0]['name']);
107 + self::assertSame(25, $writes[0]['expiration']);
108 + self::assertGreaterThanOrEqual($before + 25, $writes[0]['value']);
109 + self::assertLessThanOrEqual(time() + 25, $writes[0]['value']);
101 110
102 111 $requests = $this->state()['remote_requests'];
103 112 self::assertCount(1, $requests);
104 113 self::assertSame(
@@ -111,9 +120,9 @@
111 120 self::assertSame('test-nonce-for-' . self::ASYNC_ACTION, $requests[0]['args']['body']['nonce']);
112 121 self::assertSame($_COOKIE, $requests[0]['args']['cookies']);
113 122 }
114 123
115 - public function testShutdownRepairsDatabaseBeforeUsingQueueApis(): void
124 + public function testShutdownRepairsDatabaseWithoutFinishingFastCgiResponse(): void
116 125 {
117 126 $GLOBALS['sync_basalam_jobs_runner_test_state']['current_filter'] = 'shutdown';
118 127 $GLOBALS['wpdb'] = new FakeWpdb();
119 128
@@ -123,9 +132,8 @@
123 132 $runner->maybeDispatchAsyncRequest();
124 133
125 134 self::assertSame(
126 135 [
127 - 'fastcgi_finish_request',
128 136 'db_flush',
129 137 'db_check_connection',
130 138 'get_transient',
131 139 'get_transient',
@@ -141,20 +149,19 @@
141 149 {
142 150 $jobManager = new FakeJobManager([false]);
143 151 $runner = $this->newRunner($jobManager);
144 152
153 + $before = time();
145 154 $runner->maybeDispatchAsyncRequest();
146 155
147 156 self::assertSame(['get_transient', 'get_transient', 'has_pending_jobs', 'set_transient'], $this->state()['events']);
148 157 self::assertSame([120], $jobManager->timeouts);
149 - self::assertSame(
150 - [[
151 - 'name' => self::IDLE_PROBE_LOCK,
152 - 'value' => 1,
153 - 'expiration' => 5,
154 - ]],
155 - $this->state()['transient_writes']
156 - );
158 + $writes = $this->state()['transient_writes'];
159 + self::assertCount(1, $writes);
160 + self::assertSame(self::IDLE_PROBE_LOCK, $writes[0]['name']);
161 + self::assertSame(5, $writes[0]['expiration']);
162 + self::assertGreaterThanOrEqual($before + 5, $writes[0]['value']);
163 + self::assertLessThanOrEqual(time() + 5, $writes[0]['value']);
157 164 self::assertSame([], $this->state()['remote_requests']);
158 165 }
159 166
160 167 public function testNewJobBypassesIdleProbeLeaseAndDispatchesImmediately(): void
@@ -174,9 +181,9 @@
174 181 }
175 182
176 183 public function testExistingLockSkipsQueueCheckAndDispatch(): void
177 184 {
178 - $GLOBALS['sync_basalam_jobs_runner_test_state']['transients'][self::DISPATCH_LOCK] = 1;
185 + $GLOBALS['sync_basalam_jobs_runner_test_state']['transients'][self::DISPATCH_LOCK] = time() + 25;
179 186 $jobManager = new FakeJobManager([true]);
180 187 $runner = $this->newRunner($jobManager);
181 188
182 189 $runner->maybeDispatchAsyncRequest();
@@ -186,8 +193,102 @@
186 193 self::assertSame([], $this->state()['transient_writes']);
187 194 self::assertSame([], $this->state()['remote_requests']);
188 195 }
189 196
197 + /** @dataProvider inactiveLeaseCases */
198 + public function testExpiredOrOrphanedLeaseResumesDispatch(
199 + string $name,
200 + string $format,
201 + int $leaseOffset,
202 + ?int $timeoutOffset
203 + ): void {
204 + $this->setLease($name, $format, $leaseOffset, $timeoutOffset);
205 + $jobManager = new FakeJobManager([true]);
206 + $runner = $this->newRunner($jobManager);
207 +
208 + $runner->maybeDispatchAsyncRequest();
209 +
210 + self::assertSame([120], $jobManager->timeouts);
211 + self::assertCount(1, $this->state()['remote_requests']);
212 + self::assertSame(self::DISPATCH_LOCK, $this->state()['transient_writes'][0]['name']);
213 + self::assertGreaterThan(time(), $this->state()['transient_writes'][0]['value']);
214 + }
215 +
216 + public function inactiveLeaseCases(): array
217 + {
218 + $cases = [];
219 + foreach ([self::DISPATCH_LOCK, self::IDLE_PROBE_LOCK] as $name) {
220 + $cases[$name . ': legacy without timeout'] = [$name, 'legacy', 0, null];
221 + $cases[$name . ': legacy string without timeout'] = [$name, 'legacy_string', 0, null];
222 + $cases[$name . ': legacy with expired timeout'] = [$name, 'legacy', 0, -1];
223 + $cases[$name . ': expired deadline without timeout'] = [$name, 'deadline', -1, null];
224 + $cases[$name . ': expired stored deadline without timeout'] = [$name, 'deadline_string', -1, null];
225 + $cases[$name . ': expired deadline with future timeout'] = [$name, 'deadline', -1, 999];
226 + $cases[$name . ': deadline expires at current second'] = [$name, 'deadline', 0, null];
227 + }
228 + return $cases;
229 + }
230 +
231 + /** @dataProvider activeLeaseCases */
232 + public function testActiveLeasePreservesDispatchThrottle(
233 + string $name,
234 + string $format,
235 + ?int $timeoutOffset,
236 + bool $externalCache
237 + ): void {
238 + $this->setLease($name, $format, 25, $timeoutOffset);
239 + $GLOBALS['sync_basalam_jobs_runner_test_state']['external_object_cache'] = $externalCache;
240 + $jobManager = new FakeJobManager([true]);
241 + $runner = $this->newRunner($jobManager);
242 +
243 + $runner->maybeDispatchAsyncRequest();
244 +
245 + self::assertSame([], $jobManager->timeouts);
246 + self::assertSame([], $this->state()['transient_writes']);
247 + self::assertSame([], $this->state()['remote_requests']);
248 + }
249 +
250 + public function activeLeaseCases(): array
251 + {
252 + $cases = [];
253 + foreach ([self::DISPATCH_LOCK, self::IDLE_PROBE_LOCK] as $name) {
254 + $cases[$name . ': legacy with valid timeout'] = [$name, 'legacy', 25, false];
255 + $cases[$name . ': legacy string with valid timeout'] = [$name, 'legacy_string', 25, false];
256 + $cases[$name . ': deadline without timeout'] = [$name, 'deadline', null, false];
257 + $cases[$name . ': stored deadline without timeout'] = [$name, 'deadline_string', null, false];
258 + $cases[$name . ': cached legacy without database timeout'] = [$name, 'legacy', null, true];
259 + }
260 + return $cases;
261 + }
262 +
263 + public function testLosingTimeoutDoesNotReleaseAnActiveDispatchLease(): void
264 + {
265 + $jobManager = new FakeJobManager([true, true]);
266 + $runner = $this->newRunner($jobManager);
267 + $runner->maybeDispatchAsyncRequest();
268 + unset($GLOBALS['sync_basalam_test_options']['_transient_timeout_' . self::DISPATCH_LOCK]);
269 +
270 + $runner->maybeDispatchAsyncRequest();
271 +
272 + self::assertSame([120], $jobManager->timeouts);
273 + self::assertCount(1, $this->state()['remote_requests']);
274 + }
275 +
276 + public function testDispatchRechecksLeaseAfterProbingQueue(): void
277 + {
278 + $jobManager = new FakeJobManager([true]);
279 + $jobManager->onProbe = static function (): void {
280 + $GLOBALS['sync_basalam_jobs_runner_test_state']['transients'][self::DISPATCH_LOCK] = time() + 25;
281 + };
282 + $runner = $this->newRunner($jobManager);
283 +
284 + $runner->maybeDispatchAsyncRequest();
285 +
286 + self::assertSame([120], $jobManager->timeouts);
287 + self::assertSame([], $this->state()['remote_requests']);
288 + self::assertSame([], $this->state()['transient_writes']);
289 + }
290 +
190 291 public function testSelfAsyncRequestReturnsBeforeQueueAndLockChecks(): void
191 292 {
192 293 $GLOBALS['sync_basalam_jobs_runner_test_state']['doing_ajax'] = true;
193 294 $_REQUEST['action'] = self::ASYNC_ACTION;
@@ -250,8 +351,18 @@
250 351 $httpBlockService ?? new FakeHttpBlockService(false)
251 352 );
252 353 }
253 354
355 + private function setLease(string $name, string $format, int $leaseOffset, ?int $timeoutOffset): void
356 + {
357 + $value = in_array($format, ['deadline', 'deadline_string'], true) ? time() + $leaseOffset : 1;
358 + if (in_array($format, ['legacy_string', 'deadline_string'], true)) $value = (string) $value;
359 + $GLOBALS['sync_basalam_jobs_runner_test_state']['transients'][$name] = $value;
360 + if ($timeoutOffset !== null) {
361 + $GLOBALS['sync_basalam_test_options']['_transient_timeout_' . $name] = (string) (time() + $timeoutOffset);
362 + }
363 + }
364 +
254 365 private function invokeRunAsyncBatch(JobsRunner $runner): int
255 366 {
256 367 $method = new \ReflectionMethod($runner, 'runAsyncBatch');
257 368 $method->setAccessible(true);
@@ -275,8 +386,9 @@
275 386
276 387 class FakeJobManager
277 388 {
278 389 public $timeouts = [];
390 + public $onProbe;
279 391
280 392 private $pendingResults;
281 393
282 394 public function __construct(array $pendingResults = [])
@@ -287,8 +399,9 @@
287 399 public function hasPendingOrStaleProcessingJobs(int $timeout): bool
288 400 {
289 401 $GLOBALS['sync_basalam_jobs_runner_test_state']['events'][] = 'has_pending_jobs';
290 402 $this->timeouts[] = $timeout;
403 + if ($this->onProbe) ($this->onProbe)();
291 404
292 405 return (bool) array_shift($this->pendingResults);
293 406 }
294 407 }