Completed
Pull Request — master (#125)
by
unknown
26:03
created

TestQueuedJob::process()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 18
Code Lines 9

Duplication

Lines 17
Ratio 94.44 %

Importance

Changes 0
Metric Value
dl 17
loc 18
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 9
nc 2
nop 0
1
<?php
2
3
use SilverStripe\Dev\SapphireTest;
4
use SilverStripe\Core\Config\Config;
5
use SilverStripe\ORM\DataObject;
6
use SilverStripe\QueuedJobs\DataObjects\QueuedJobDescriptor;
7
use SilverStripe\QueuedJobs\Services\QueuedJob;
8
use SilverStripe\QueuedJobs\Tests\TestQueuedJob;
9
use SilverStripe\QueuedJobs\Tests\TestQJService;
10
11
/**
12
 *
13
 *
14
 * @author Marcus Nyeholt <[email protected]>
15
 */
16
class QueuedJobsTest extends SapphireTest
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
17
{
18
    /**
19
     * We need the DB for this test
20
     *
21
     * @var bool
22
     */
23
    protected $usesDatabase = true;
24
25
    /**
26
     * {@inheritDoc}
27
     */
28
    public function setUp()
29
    {
30
        parent::setUp();
31
32
        Config::nest();
33
        // Two restarts are allowed per job
34
        Config::inst()->update('SilverStripe\\QueuedJobs\\Services\\QueuedJobService', 'stall_threshold', 2);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface SilverStripe\Config\Coll...nfigCollectionInterface as the method update() does only exist in the following implementations of said interface: SilverStripe\Config\Coll...\MemoryConfigCollection.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
35
    }
36
37
    /**
38
     * {@inheritDoc}
39
     */
40
    public function tearDown()
41
    {
42
        Config::unnest();
43
        parent::tearDown();
44
    }
45
46
    /**
47
     * @return QueuedJobService
48
     */
49
    protected function getService()
50
    {
51
        return singleton(TestQJService::class);
52
    }
53
54
    public function testQueueJob()
55
    {
56
        $svc = $this->getService();
57
58
        // lets create a new job and add it tio the queue
59
        $job = new TestQueuedJob();
60
        $jobId = $svc->queueJob($job);
61
        $list = $svc->getJobList();
62
63
        $this->assertEquals(1, $list->count());
64
65
        $myJob = null;
66
        foreach ($list as $job) {
67
            if ($job->Implementation == TestQueuedJob::class) {
68
                $myJob = $job;
69
                break;
70
            }
71
        }
72
73
        $this->assertNotNull($myJob);
74
        $this->assertTrue($jobId > 0);
75
        $this->assertEquals(TestQueuedJob::class, $myJob->Implementation);
76
        $this->assertNotNull($myJob->SavedJobData);
77
    }
78
79
    public function testJobRunAs()
80
    {
81
        $svc = $this->getService();
82
        $list = $svc->getJobList();
83
        foreach ($list as $job) {
84
            $job->delete();
85
        }
86
87
        $this->logInWithPermission('DUMMY');
88
89
        // lets create a new job and add it tio the queue
90
        $job = new TestQueuedJob();
91
        $job->runningAs = "DUMMY";
0 ignored issues
show
Documentation introduced by
The property runningAs does not exist on object<SilverStripe\Queu...bs\Tests\TestQueuedJob>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
92
        $jobId = $svc->queueJob($job);
0 ignored issues
show
Unused Code introduced by
$jobId is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
93
        $list = $svc->getJobList();
94
95
        $myJob = $list->First();
96
97
        $this->assertEquals("[email protected]", $myJob->RunAs()->Email);
98
    }
99
100
    public function testQueueSignature()
101
    {
102
        $svc = $this->getService();
103
104
        // lets create a new job and add it tio the queue
105
        $job = new TestQueuedJob();
106
        $jobId = $svc->queueJob($job);
107
108
        $newJob = new TestQueuedJob();
109
        $newId = $svc->queueJob($newJob);
110
111
        $this->assertEquals($jobId, $newId);
112
113
        // now try another, but with different params
114
        $newJob = new TestQueuedJob();
115
        $newJob->randomParam = 'stuff';
0 ignored issues
show
Documentation introduced by
The property randomParam does not exist on object<SilverStripe\Queu...bs\Tests\TestQueuedJob>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
116
        $newId = $svc->queueJob($newJob);
117
118
        $this->assertNotEquals($jobId, $newId);
119
    }
120
121
    public function testProcessJob()
122
    {
123
        $job = new TestQueuedJob();
124
        $job->setup();
125
        $job->process();
126
        // we should now have some  data
127
        $data = $job->getJobData();
128
        $this->assertNotNull($data->messages);
129
        $this->assertFalse($data->isComplete);
130
131
        $jd = $data->jobData;
132
        $this->assertTrue(isset($jd->times));
133
        $this->assertEquals(1, count($jd->times));
134
135
        // now take the 'saved' data and try restoring the job
136
    }
137
138
    public function testResumeJob()
139
    {
140
        $job = new TestQueuedJob();
141
        $job->setup();
142
        $job->process();
143
        // we should now have some  data
144
        $data = $job->getJobData();
145
146
        // so create a new job and restore it from this data
147
148
        $job = new TestQueuedJob();
149
        $job->setup();
150
151
        $job->setJobData($data->totalSteps, $data->currentStep, $data->isComplete, $data->jobData, $data->messages);
152
        $job->process();
153
154
        $data = $job->getJobData();
155
        $this->assertFalse($data->isComplete);
156
        $jd = $data->jobData;
157
        $this->assertTrue(isset($jd->times));
158
        $this->assertEquals(2, count($jd->times));
159
    }
160
161
    public function testInitialiseJob()
162
    {
163
        // okay, lets test it out on the actual service
164
        $svc = $this->getService();
165
        // lets create a new job and add it to the queue
166
        $job = new TestQueuedJob();
167
        $id = $svc->queueJob($job);
168
169
        $descriptor = DataObject::get_by_id('SilverStripe\\QueuedJobs\\DataObjects\\QueuedJobDescriptor', $id);
170
171
        $job = $svc->testInit($descriptor);
172
        $this->assertInstanceOf(TestQueuedJob::class, $job, 'Job has been triggered');
173
174
        $descriptor = DataObject::get_by_id('SilverStripe\\QueuedJobs\\DataObjects\\QueuedJobDescriptor', $id);
175
176
        $this->assertEquals(QueuedJob::STATUS_INIT, $descriptor->JobStatus);
177
    }
178
179
    public function testStartJob()
180
    {
181
        // okay, lets test it out on the actual service
182
        $svc = $this->getService();
183
        // lets create a new job and add it to the queue
184
185
        $this->logInWithPermission('DUMMYUSER');
186
187
        $job = new TestQueuedJob();
188
        $job->testingStartJob = true;
0 ignored issues
show
Documentation introduced by
The property testingStartJob does not exist on object<SilverStripe\Queu...bs\Tests\TestQueuedJob>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
189
        $id = $svc->queueJob($job);
190
191
        $this->logInWithPermission('ADMIN');
192
193
        $result = $svc->runJob($id);
194
        $this->assertTrue($result);
195
196
        // we want to make sure that the current user is the runas user of the job
197
        $descriptor = DataObject::get_by_id('SilverStripe\\QueuedJobs\\DataObjects\\QueuedJobDescriptor', $id);
198
        $this->assertEquals('Complete', $descriptor->JobStatus);
199
    }
200
201
    public function testImmediateQueuedJob()
202
    {
203
        // okay, lets test it out on the actual service
204
        $svc = $this->getService();
205
        // lets create a new job and add it to the queue
206
207
        $job = new TestQueuedJob(QueuedJob::IMMEDIATE);
208
        $job->firstJob = true;
0 ignored issues
show
Documentation introduced by
The property firstJob does not exist on object<SilverStripe\Queu...bs\Tests\TestQueuedJob>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
209
        $id = $svc->queueJob($job);
0 ignored issues
show
Unused Code introduced by
$id is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
210
211
        $job = new TestQueuedJob(QueuedJob::IMMEDIATE);
212
        $job->secondJob = true;
0 ignored issues
show
Documentation introduced by
The property secondJob does not exist on object<SilverStripe\Queu...bs\Tests\TestQueuedJob>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
213
        $id = $svc->queueJob($job);
0 ignored issues
show
Unused Code introduced by
$id is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
214
215
        $jobs = $svc->getJobList(QueuedJob::IMMEDIATE);
216
        $this->assertEquals(2, $jobs->count());
217
218
        // now fake a shutdown
219
        $svc->onShutdown();
220
221
        $jobs = $svc->getJobList(QueuedJob::IMMEDIATE);
222
        $this->assertInstanceOf('SilverStripe\\ORM\\DataList', $jobs);
223
        $this->assertEquals(0, $jobs->count());
224
    }
225
226
    public function testNextJob()
227
    {
228
        $svc = $this->getService();
229
        $list = $svc->getJobList();
230
231
        foreach ($list as $job) {
232
            $job->delete();
233
        }
234
235
        $list = $svc->getJobList();
236
        $this->assertEquals(0, $list->count());
237
238
        $job = new TestQueuedJob();
239
        $id1 = $svc->queueJob($job);
240
241
        $job = new TestQueuedJob();
242
        // to get around the signature checks
243
        $job->randomParam = 'me';
0 ignored issues
show
Documentation introduced by
The property randomParam does not exist on object<SilverStripe\Queu...bs\Tests\TestQueuedJob>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
244
        $id2 = $svc->queueJob($job);
0 ignored issues
show
Unused Code introduced by
$id2 is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
245
246
        $job = new TestQueuedJob();
247
        // to get around the signature checks
248
        $job->randomParam = 'mo';
0 ignored issues
show
Documentation introduced by
The property randomParam does not exist on object<SilverStripe\Queu...bs\Tests\TestQueuedJob>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
249
        $id3 = $svc->queueJob($job);
250
251
        $this->assertEquals(2, $id3 - $id1);
252
253
        $list = $svc->getJobList();
254
        $this->assertEquals(3, $list->count());
255
256
        // okay, lets get the first one and initialise it, then make sure that a subsequent init attempt fails
257
        $job = $svc->getNextPendingJob();
258
259
        $this->assertEquals($id1, $job->ID);
260
        $svc->testInit($job);
261
262
        // now try and get another, it should be === false
263
        $next = $svc->getNextPendingJob();
264
265
        $this->assertFalse($next);
266
    }
267
268
    /**
269
     * Verify that broken jobs are correctly verified for health and restarted as necessary
270
     *
271
     * Order of checkJobHealth() and getNextPendingJob() is important
272
     *
273
     * Execution of this job is broken into several "loops", each of which represents one invocation
274
     * of ProcessJobQueueTask
275
     */
276
    public function testJobHealthCheck()
277
    {
278
        // Create a job and add it to the queue
279
        $svc = $this->getService();
280
        $job = new TestQueuedJob(QueuedJob::IMMEDIATE);
281
        $job->firstJob = true;
0 ignored issues
show
Documentation introduced by
The property firstJob does not exist on object<SilverStripe\Queu...bs\Tests\TestQueuedJob>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
282
        $id = $svc->queueJob($job);
283
        $descriptor = QueuedJobDescriptor::get()->byID($id);
284
285
        // Verify initial state is new and LastProcessedCount is not marked yet
286
        $this->assertEquals(QueuedJob::STATUS_NEW, $descriptor->JobStatus);
287
        $this->assertEquals(0, $descriptor->StepsProcessed);
288
        $this->assertEquals(-1, $descriptor->LastProcessedCount);
289
        $this->assertEquals(0, $descriptor->ResumeCounts);
290
291
        // Loop 1 - Pick up new job and attempt to run it
292
        // Job health should not attempt to cleanup unstarted jobs
293
        $svc->checkJobHealth(QueuedJob::IMMEDIATE);
294
        $nextJob = $svc->getNextPendingJob(QueuedJob::IMMEDIATE);
295
296
        // Ensure that this is the next job ready to go
297
        $descriptor = QueuedJobDescriptor::get()->byID($id);
298
        $this->assertEquals($nextJob->ID, $descriptor->ID);
299
        $this->assertEquals(QueuedJob::STATUS_NEW, $descriptor->JobStatus);
300
        $this->assertEquals(0, $descriptor->StepsProcessed);
301
        $this->assertEquals(-1, $descriptor->LastProcessedCount);
302
        $this->assertEquals(0, $descriptor->ResumeCounts);
303
304
        // Run 1 - Start the job (no work is done)
305
        $descriptor->JobStatus = QueuedJob::STATUS_INIT;
306
        $descriptor->write();
307
308
        // Assume that something bad happens at this point, the process dies during execution, and
309
        // the task is re-initiated somewhere down the track
310
311
        // Loop 2 - Detect broken job, and mark it for future checking.
312
        $svc->checkJobHealth(QueuedJob::IMMEDIATE);
313
        $nextJob = $svc->getNextPendingJob(QueuedJob::IMMEDIATE);
314
315
        // Note that we don't immediately try to restart it until StepsProcessed = LastProcessedCount
316
        $descriptor = QueuedJobDescriptor::get()->byID($id);
317
        $this->assertFalse($nextJob); // Don't run it this round please!
318
        $this->assertEquals(QueuedJob::STATUS_INIT, $descriptor->JobStatus);
319
        $this->assertEquals(0, $descriptor->StepsProcessed);
320
        $this->assertEquals(0, $descriptor->LastProcessedCount);
321
        $this->assertEquals(0, $descriptor->ResumeCounts);
322
323
        // Loop 3 - We've previously marked this job as broken, so restart it this round
324
        // If no more work has been done on the job at this point, assume that we are able to
325
        // restart it
326
        $svc->checkJobHealth(QueuedJob::IMMEDIATE);
327
        $nextJob = $svc->getNextPendingJob(QueuedJob::IMMEDIATE);
328
329
        // This job is resumed and exeuction is attempted this round
330
        $descriptor = QueuedJobDescriptor::get()->byID($id);
331
        $this->assertEquals($nextJob->ID, $descriptor->ID);
332
        $this->assertEquals(QueuedJob::STATUS_WAIT, $descriptor->JobStatus);
333
        $this->assertEquals(0, $descriptor->StepsProcessed);
334
        $this->assertEquals(0, $descriptor->LastProcessedCount);
335
        $this->assertEquals(1, $descriptor->ResumeCounts);
336
337
        // Run 2 - First restart (work is done)
338
        $descriptor->JobStatus = QueuedJob::STATUS_RUN;
339
        $descriptor->StepsProcessed++; // Essentially delays the next restart by 1 loop
340
        $descriptor->write();
341
342
        // Once again, at this point, assume the job fails and crashes
343
344
        // Loop 4 - Assuming a job has LastProcessedCount < StepsProcessed we are in the same
345
        // situation as step 2.
346
        // Because the last time the loop ran, StepsProcessed was incremented,
347
        // this indicates that it's likely that another task could be working on this job, so
348
        // don't run this.
349
        $svc->checkJobHealth(QueuedJob::IMMEDIATE);
350
        $nextJob = $svc->getNextPendingJob(QueuedJob::IMMEDIATE);
351
352
        $descriptor = QueuedJobDescriptor::get()->byID($id);
353
        $this->assertFalse($nextJob); // Don't run jobs we aren't sure should be restarted
354
        $this->assertEquals(QueuedJob::STATUS_RUN, $descriptor->JobStatus);
355
        $this->assertEquals(1, $descriptor->StepsProcessed);
356
        $this->assertEquals(1, $descriptor->LastProcessedCount);
357
        $this->assertEquals(1, $descriptor->ResumeCounts);
358
359
        // Loop 5 - Job is again found to not have been restarted since last iteration, so perform second
360
        // restart. The job should be attempted to run this loop
361
        $svc->checkJobHealth(QueuedJob::IMMEDIATE);
362
        $nextJob = $svc->getNextPendingJob(QueuedJob::IMMEDIATE);
363
364
        // This job is resumed and exeuction is attempted this round
365
        $descriptor = QueuedJobDescriptor::get()->byID($id);
366
        $this->assertEquals($nextJob->ID, $descriptor->ID);
367
        $this->assertEquals(QueuedJob::STATUS_WAIT, $descriptor->JobStatus);
368
        $this->assertEquals(1, $descriptor->StepsProcessed);
369
        $this->assertEquals(1, $descriptor->LastProcessedCount);
370
        $this->assertEquals(2, $descriptor->ResumeCounts);
371
372
        // Run 3 - Second and last restart (no work is done)
373
        $descriptor->JobStatus = QueuedJob::STATUS_RUN;
374
        $descriptor->write();
375
376
        // Loop 6 - As no progress has been made since loop 3, we can mark this as dead
377
        $svc->checkJobHealth(QueuedJob::IMMEDIATE);
378
        $nextJob = $svc->getNextPendingJob(QueuedJob::IMMEDIATE);
379
380
        // Since no StepsProcessed has been done, don't wait another loop to mark this as dead
381
        $descriptor = QueuedJobDescriptor::get()->byID($id);
382
        $this->assertEquals(QueuedJob::STATUS_PAUSED, $descriptor->JobStatus);
383
        $this->assertEmpty($nextJob);
384
    }
385
}
386