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
sync-basalam / tests / JobsRunnerLeaseRegression.php

JobsRunnerLeaseRegression.php in ووسلام – همگام سازی ووکامرس و باسلام 1.10.29, at tests/JobsRunnerLeaseRegression.php

143 lines 7.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 define('ABSPATH', __DIR__);
3 $state = [];
4 function add_action(...$args) {}
5 function current_filter() { return $GLOBALS['state']['filter'] ?? ''; }
6 function wp_doing_ajax() { return $GLOBALS['state']['ajax'] ?? false; }
7 function get_transient($name) { return $GLOBALS['state']['leases'][$name] ?? false; }
8 function set_transient($name, $value, $ttl = 0) {
9 $GLOBALS['state']['leases'][$name] = $value;
10 $GLOBALS['state']['writes'][] = [$name, $value, $ttl];
11 return true;
12 }
13 function get_option($name, $default = false) { return $GLOBALS['state']['options'][$name] ?? $default; }
14 function wp_using_ext_object_cache() { return $GLOBALS['state']['external_cache'] ?? false; }
15 function wp_installing() { return false; }
16 function wp_unslash($value) { return $value; }
17 function sanitize_key($value) { return $value; }
18 function admin_url($path) { return 'https://example.test/wp-admin/' . $path; }
19 function add_query_arg($name, $value, $url) { return $url . '?' . $name . '=' . $value; }
20 function esc_url_raw($url) { return $url; }
21 function wp_create_nonce($action) { return 'test-nonce'; }
22 function apply_filters($name, $value) { return $value; }
23 function wp_remote_post($url, $args) { $GLOBALS['state']['requests'][] = [$url, $args]; return []; }
24
25 // Run in its own PHP process: php tests/JobsRunnerLeaseRegression.php
26 require_once dirname(__DIR__) . '/includes/Services/AsyncWorkerHealth.php';
27 require $argv[1] ?? dirname(__DIR__) . '/JobsRunner.php';
28
29 class QueueForLeaseTest {
30 public $checks = 0;
31 public $pending = true;
32 public $onCheck;
33 public function hasPendingOrStaleProcessingJobs($timeout) {
34 $this->checks++;
35 if ($this->onCheck) ($this->onCheck)();
36 return $this->pending;
37 }
38 }
39 class HttpForLeaseTest {
40 public function SyncBasalamHttpBlock() { return false; }
41 }
42 const DISPATCH = 'sync_basalam_jobs_runner_async_dispatch_lock';
43 const IDLE = 'sync_basalam_jobs_runner_idle_probe_lock';
44 function check($condition, $message) { if (!$condition) throw new RuntimeException($message); }
45 function resetRunner() {
46 $GLOBALS['state'] = ['leases' => [], 'options' => [], 'writes' => [], 'requests' => []];
47 $_REQUEST = [];
48 $_COOKIE = [];
49 $queue = new QueueForLeaseTest();
50 return [new SyncBasalam\JobsRunner($queue, new stdClass(), new stdClass(), new HttpForLeaseTest()), $queue];
51 }
52 $cases = [
53 'orphaned legacy dispatch lock resumes work' => function () {
54 [$runner, $queue] = resetRunner();
55 $GLOBALS['state']['leases'][DISPATCH] = 1;
56 $runner->maybeDispatchAsyncRequest();
57 check(count($GLOBALS['state']['requests']) === 1, 'legacy orphan blocked dispatch');
58 },
59 'legacy lock with a valid expiry still blocks duplicate dispatch' => function () {
60 [$runner, $queue] = resetRunner();
61 $GLOBALS['state']['leases'][DISPATCH] = 1;
62 $GLOBALS['state']['options']['_transient_timeout_' . DISPATCH] = time() + 25;
63 $runner->maybeDispatchAsyncRequest();
64 check($queue->checks === 0 && !$GLOBALS['state']['requests'], 'valid legacy lease ignored');
65 },
66 'new lock stores a bounded deadline and preserves async arguments' => function () {
67 [$runner, $queue] = resetRunner();
68 $_COOKIE = ['test_cookie' => 'value'];
69 $before = time();
70 $runner->maybeDispatchAsyncRequest();
71 [$name, $value, $ttl] = $GLOBALS['state']['writes'][0];
72 check($name === DISPATCH && $ttl === 25 && $value >= $before + 25 && $value <= time() + 25, 'lease deadline missing');
73 $args = $GLOBALS['state']['requests'][0][1];
74 check($args['blocking'] === false && $args['timeout'] === 0.01 && $args['cookies'] === $_COOKIE, 'async contract changed');
75 },
76 'active timestamp still blocks when the timeout option disappears' => function () {
77 [$runner, $queue] = resetRunner();
78 $GLOBALS['state']['leases'][DISPATCH] = time() + 25;
79 $runner->maybeDispatchAsyncRequest();
80 check($queue->checks === 0 && !$GLOBALS['state']['requests'], 'active timestamp lease ignored');
81 },
82 'expired timestamp resumes work even when its timeout option is absent' => function () {
83 [$runner, $queue] = resetRunner();
84 $GLOBALS['state']['leases'][DISPATCH] = time() - 1;
85 $runner->maybeDispatchAsyncRequest();
86 check(count($GLOBALS['state']['requests']) === 1, 'expired orphan timestamp blocked work');
87 },
88 'expired timestamp resumes despite a stale future timeout option' => function () {
89 [$runner, $queue] = resetRunner();
90 $GLOBALS['state']['leases'][DISPATCH] = time() - 1;
91 $GLOBALS['state']['options']['_transient_timeout_' . DISPATCH] = time() + 999;
92 $runner->maybeDispatchAsyncRequest();
93 check(count($GLOBALS['state']['requests']) === 1, 'value deadline was not authoritative');
94 },
95 'external object cache legacy lease keeps its cache TTL semantics' => function () {
96 [$runner, $queue] = resetRunner();
97 $GLOBALS['state']['external_cache'] = true;
98 $GLOBALS['state']['leases'][DISPATCH] = 1;
99 $runner->maybeDispatchAsyncRequest();
100 check($queue->checks === 0 && !$GLOBALS['state']['requests'], 'external cache legacy lease ignored');
101 },
102 'orphaned idle probe does not permanently block queued work' => function () {
103 [$runner, $queue] = resetRunner();
104 $GLOBALS['state']['leases'][IDLE] = 1;
105 $runner->maybeDispatchAsyncRequest();
106 check(count($GLOBALS['state']['requests']) === 1, 'orphan idle probe blocked work');
107 },
108 'empty queue stores a five second idle deadline without dispatch' => function () {
109 [$runner, $queue] = resetRunner();
110 $queue->pending = false;
111 $before = time();
112 $runner->maybeDispatchAsyncRequest();
113 [$name, $value, $ttl] = $GLOBALS['state']['writes'][0];
114 check($name === IDLE && $ttl === 5 && $value >= $before + 5 && $value <= time() + 5, 'idle deadline missing');
115 check(!$GLOBALS['state']['requests'], 'empty queue dispatched');
116 },
117 'new job bypasses an active idle probe' => function () {
118 [$runner, $queue] = resetRunner();
119 $GLOBALS['state']['leases'][IDLE] = time() + 5;
120 $GLOBALS['state']['filter'] = 'sync_basalam_job_created';
121 $runner->maybeDispatchAsyncRequest();
122 check(count($GLOBALS['state']['requests']) === 1, 'new job was blocked by idle probe');
123 },
124 'second lease check respects a lease acquired during the queue probe' => function () {
125 [$runner, $queue] = resetRunner();
126 $queue->onCheck = function () { $GLOBALS['state']['leases'][DISPATCH] = time() + 25; };
127 $runner->maybeDispatchAsyncRequest();
128 check($queue->checks === 1 && !$GLOBALS['state']['requests'], 'second lease check ignored');
129 },
130 'async worker never recursively dispatches itself' => function () {
131 [$runner, $queue] = resetRunner();
132 $GLOBALS['state']['ajax'] = true;
133 $_REQUEST['action'] = 'sync_basalam_run_jobs_async';
134 $runner->maybeDispatchAsyncRequest();
135 check($queue->checks === 0 && !$GLOBALS['state']['requests'], 'recursive async dispatch');
136 },
137 ];
138 foreach ($cases as $name => $run) {
139 $run();
140 echo "PASS: $name\n";
141 }
142 echo count($cases) . " regression cases passed.\n";
143