PluginProbe
ووسلام – همگام سازی ووکامرس و باسلام / 1.10.19
ووسلام – همگام سازی ووکامرس و باسلام v1.10.19
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 1.10.2 1.10.1 1.10.0 1.9.2 1.9.1 1.9.0 1.8.8 1.8.5 1.8.6 All 53 releases
← All changes | tests/JobsRunnerTest.php +81 -28 1.10.141.10.19 View file →
@@ -41,18 +41,18 @@
41 41
42 42 unset($GLOBALS['sync_basalam_jobs_runner_test_state']);
43 43 }
44 44
45 - public function testRegistersDispatcherOnInitAndNeverOnShutdown(): void
45 + public function testRegistersDispatcherOnShutdownAndNeverOnInit(): void
46 46 {
47 47 $runner = $this->newRunner();
48 48 $actions = $this->state()['actions'];
49 49
50 - self::assertArrayHasKey('init', $actions);
51 - self::assertSame([$runner, 'maybeDispatchAsyncRequest'], $actions['init'][0]['callback']);
52 - self::assertSame(10, $actions['init'][0]['priority']);
53 - self::assertSame(0, $actions['init'][0]['accepted_args']);
54 - self::assertArrayNotHasKey('shutdown', $actions);
50 + self::assertArrayHasKey('shutdown', $actions);
51 + self::assertSame([$runner, 'maybeDispatchAsyncRequest'], $actions['shutdown'][0]['callback']);
52 + self::assertSame(PHP_INT_MAX, $actions['shutdown'][0]['priority']);
53 + self::assertSame(1, $actions['shutdown'][0]['accepted_args']);
54 + self::assertArrayNotHasKey('init', $actions);
55 55
56 56 self::assertSame(
57 57 [$runner, 'maybeDispatchAsyncRequest'],
58 58 $actions['sync_basalam_job_created'][0]['callback']
@@ -64,9 +64,9 @@
64 64 $actions['wp_ajax_nopriv_' . self::ASYNC_ACTION][0]['callback']
65 65 );
66 66 }
67 67
68 - public function testEligibleQueueIsConfirmedBeforeWritingLockAndDispatching(): void
68 + public function testDispatchLeaseIsWrittenBeforeQueueProbeAndDispatching(): void
69 69 {
70 70 $jobManager = new FakeJobManager([true]);
71 71 $runner = $this->newRunner($jobManager);
72 72
@@ -73,9 +73,9 @@
73 73 $_COOKIE = ['wordpress_test_cookie' => 'cookie-value'];
74 74 $runner->maybeDispatchAsyncRequest();
75 75
76 76 self::assertSame(
77 - ['get_transient', 'has_pending_jobs', 'set_transient', 'remote_post'],
77 + ['get_transient', 'set_transient', 'has_pending_jobs', 'remote_post'],
78 78 $this->state()['events']
79 79 );
80 80 self::assertSame([120], $jobManager->timeouts);
81 81 self::assertSame(
@@ -81,9 +81,9 @@
81 81 self::assertSame(
82 82 [[
83 83 'name' => self::DISPATCH_LOCK,
84 84 'value' => 1,
85 - 'expiration' => 1,
85 + 'expiration' => 25,
86 86 ]],
87 87 $this->state()['transient_writes']
88 88 );
89 89
@@ -99,9 +99,9 @@
99 99 self::assertSame('test-nonce-for-' . self::ASYNC_ACTION, $requests[0]['args']['body']['nonce']);
100 100 self::assertSame($_COOKIE, $requests[0]['args']['cookies']);
101 101 }
102 102
103 - public function testEmptyQueueDoesNotWriteLockOrDispatch(): void
103 + public function testEmptyQueueStillReservesDispatchLease(): void
104 104 {
105 105 $jobManager = new FakeJobManager([false]);
106 106 $runner = $this->newRunner($jobManager);
107 107
@@ -106,29 +106,36 @@
106 106 $runner = $this->newRunner($jobManager);
107 107
108 108 $runner->maybeDispatchAsyncRequest();
109 109
110 - self::assertSame(['get_transient', 'has_pending_jobs'], $this->state()['events']);
110 + self::assertSame(['get_transient', 'set_transient', 'has_pending_jobs'], $this->state()['events']);
111 111 self::assertSame([120], $jobManager->timeouts);
112 - self::assertSame([], $this->state()['transient_writes']);
112 + self::assertSame(
113 + [[
114 + 'name' => self::DISPATCH_LOCK,
115 + 'value' => 1,
116 + 'expiration' => 25,
117 + ]],
118 + $this->state()['transient_writes']
119 + );
113 120 self::assertSame([], $this->state()['remote_requests']);
114 121 }
115 122
116 - public function testJobCreatedAfterAnEmptyInitCanStillDispatch(): void
123 + public function testEmptyQueueLeasePreventsASecondProbeInTheSameRequest(): void
117 124 {
118 125 $jobManager = new FakeJobManager([false, true]);
119 126 $runner = $this->newRunner($jobManager);
120 127
121 - // The init probe sees no work and must not reserve the dispatch lock.
128 + // The shutdown probe reserves the lease even when no work is found.
122 129 $runner->maybeDispatchAsyncRequest();
123 - self::assertSame([], $this->state()['transient_writes']);
130 + self::assertCount(1, $this->state()['transient_writes']);
124 131
125 - // A job created later in the same request must still wake the runner.
132 + // A second callback in the same request must observe that lease.
126 133 $runner->maybeDispatchAsyncRequest();
127 134
128 - self::assertSame([120, 120], $jobManager->timeouts);
135 + self::assertSame([120], $jobManager->timeouts);
129 136 self::assertCount(1, $this->state()['transient_writes']);
130 - self::assertCount(1, $this->state()['remote_requests']);
137 + self::assertCount(0, $this->state()['remote_requests']);
131 138 }
132 139
133 140 public function testExistingLockSkipsQueueCheckAndDispatch(): void
134 141 {
@@ -170,34 +177,53 @@
170 177 self::assertSame(1, $httpBlockService->calls);
171 178 $this->assertNoQueueLockOrDispatchActivity($jobManager);
172 179 }
173 180
174 - public function testShutdownGuardPreventsEveryDispatchSideEffect(): void
181 + public function testAsyncBatchExitsImmediatelyWhenAnotherRunnerOwnsTheGlobalLock(): void
175 182 {
176 - $GLOBALS['sync_basalam_jobs_runner_test_state']['did_actions']['shutdown'] = 1;
183 + $jobManager = new FakeJobManager([true]);
184 + $jobExecutor = new FakeJobExecutor(false);
185 + $runner = $this->newRunner($jobManager, null, $jobExecutor);
177 186
178 - $jobManager = new FakeJobManager([true]);
179 - $httpBlockService = new FakeHttpBlockService(false);
180 - $runner = $this->newRunner($jobManager, $httpBlockService);
187 + self::assertSame(0, $this->invokeRunAsyncBatch($runner));
188 + self::assertSame(1, $jobExecutor->acquireCalls);
189 + self::assertSame(0, $jobExecutor->releaseCalls);
190 + self::assertSame([], $jobManager->timeouts);
191 + }
181 192
182 - $runner->maybeDispatchAsyncRequest();
193 + public function testAsyncBatchReleasesTheGlobalLockWhenTheQueueIsEmpty(): void
194 + {
195 + $jobManager = new FakeJobManager([false]);
196 + $jobExecutor = new FakeJobExecutor(true);
197 + $runner = $this->newRunner($jobManager, null, $jobExecutor);
183 198
184 - self::assertSame(0, $httpBlockService->calls);
185 - $this->assertNoQueueLockOrDispatchActivity($jobManager);
199 + self::assertSame(0, $this->invokeRunAsyncBatch($runner));
200 + self::assertSame(1, $jobExecutor->acquireCalls);
201 + self::assertSame(1, $jobExecutor->releaseCalls);
202 + self::assertSame([120], $jobManager->timeouts);
186 203 }
187 204
188 205 private function newRunner(
189 206 ?FakeJobManager $jobManager = null,
190 - ?FakeHttpBlockService $httpBlockService = null
207 + ?FakeHttpBlockService $httpBlockService = null,
208 + $jobExecutor = null
191 209 ): JobsRunner {
192 210 return new JobsRunner(
193 211 $jobManager ?? new FakeJobManager(),
212 + $jobExecutor ?? new FakeJobExecutor(),
194 213 new \stdClass(),
195 - new \stdClass(),
196 214 $httpBlockService ?? new FakeHttpBlockService(false)
197 215 );
198 216 }
199 217
218 + private function invokeRunAsyncBatch(JobsRunner $runner): int
219 + {
220 + $method = new \ReflectionMethod($runner, 'runAsyncBatch');
221 + $method->setAccessible(true);
222 +
223 + return $method->invoke($runner);
224 + }
225 +
200 226 private function assertNoQueueLockOrDispatchActivity(FakeJobManager $jobManager): void
201 227 {
202 228 self::assertSame([], $jobManager->timeouts);
203 229 self::assertSame([], $this->state()['transient_reads']);
@@ -246,6 +272,33 @@
246 272 {
247 273 $this->calls++;
248 274
249 275 return $this->blocked;
276 + }
277 +}
278 +
279 +class FakeJobExecutor
280 +{
281 + public $acquireCalls = 0;
282 + public $releaseCalls = 0;
283 +
284 + private $canAcquire;
285 +
286 + public function __construct(bool $canAcquire = true)
287 + {
288 + $this->canAcquire = $canAcquire;
289 + }
290 +
291 + public function acquireGlobalJobsLock(int $timeout = 0): bool
292 + {
293 + $this->acquireCalls++;
294 +
295 + return $this->canAcquire;
296 + }
297 +
298 + public function releaseGlobalJobsLock(): bool
299 + {
300 + $this->releaseCalls++;
301 +
302 + return true;
250 303 }
251 304 }