Completed
Push — master ( d00453...22bf77 )
by Marco
24s queued 18s
created

testOptimisticImmutableTimestampSetsDefaultValue()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 7
nc 1
nop 0
dl 0
loc 14
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\Tests\ORM\Functional\Locking;
6
7
use DateTime;
8
use DateTimeImmutable;
9
use Doctrine\DBAL\LockMode;
10
use Doctrine\ORM\Annotation as ORM;
11
use Doctrine\ORM\OptimisticLockException;
12
use Doctrine\Tests\OrmFunctionalTestCase;
13
use Exception;
14
use function date;
15
use function strtotime;
16
17
class OptimisticTest extends OrmFunctionalTestCase
18
{
19
    protected function setUp() : void
20
    {
21
        parent::setUp();
22
23
        try {
24
            $this->schemaTool->createSchema(
25
                [
26
                    $this->em->getClassMetadata(OptimisticJoinedParent::class),
27
                    $this->em->getClassMetadata(OptimisticJoinedChild::class),
28
                    $this->em->getClassMetadata(OptimisticStandard::class),
29
                    $this->em->getClassMetadata(OptimisticTimestamp::class),
30
                    $this->em->getClassMetadata(OptimisticImmutableTimestamp::class),
31
                ]
32
            );
33
        } catch (Exception $e) {
34
            // Swallow all exceptions. We do not test the schema tool here.
35
        }
36
37
        $this->conn = $this->em->getConnection();
0 ignored issues
show
Bug Best Practice introduced by
The property conn does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
38
    }
39
40
    public function testJoinedChildInsertSetsInitialVersionValue() : OptimisticJoinedChild
41
    {
42
        $test = new OptimisticJoinedChild();
43
44
        $test->name     = 'child';
45
        $test->whatever = 'whatever';
46
47
        $this->em->persist($test);
48
        $this->em->flush();
49
50
        self::assertEquals(1, $test->version);
51
52
        return $test;
53
    }
54
55
    /**
56
     * @depends testJoinedChildInsertSetsInitialVersionValue
57
     */
58
    public function testJoinedChildFailureThrowsException(OptimisticJoinedChild $child) : void
59
    {
60
        $q = $this->em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticJoinedChild t WHERE t.id = :id');
61
62
        $q->setParameter('id', $child->id);
63
64
        $test = $q->getSingleResult();
65
66
        // Manually update/increment the version so we can try and save the same
67
        // $test and make sure the exception is thrown saying the record was
68
        // changed or updated since you read it
69
        $this->conn->executeQuery('UPDATE optimistic_joined_parent SET version = ? WHERE id = ?', [2, $test->id]);
70
71
        // Now lets change a property and try and save it again
72
        $test->whatever = 'ok';
73
74
        try {
75
            $this->em->flush();
76
        } catch (OptimisticLockException $e) {
77
            self::assertSame($test, $e->getEntity());
78
        }
79
    }
80
81
    public function testJoinedParentInsertSetsInitialVersionValue() : OptimisticJoinedParent
82
    {
83
        $test = new OptimisticJoinedParent();
84
85
        $test->name = 'parent';
86
87
        $this->em->persist($test);
88
        $this->em->flush();
89
90
        self::assertEquals(1, $test->version);
91
92
        return $test;
93
    }
94
95
    /**
96
     * @depends testJoinedParentInsertSetsInitialVersionValue
97
     */
98
    public function testJoinedParentFailureThrowsException(OptimisticJoinedParent $parent) : void
99
    {
100
        $q = $this->em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticJoinedParent t WHERE t.id = :id');
101
102
        $q->setParameter('id', $parent->id);
103
104
        $test = $q->getSingleResult();
105
106
        // Manually update/increment the version so we can try and save the same
107
        // $test and make sure the exception is thrown saying the record was
108
        // changed or updated since you read it
109
        $this->conn->executeQuery('UPDATE optimistic_joined_parent SET version = ? WHERE id = ?', [2, $test->id]);
110
111
        // Now lets change a property and try and save it again
112
        $test->name = 'WHATT???';
113
114
        try {
115
            $this->em->flush();
116
        } catch (OptimisticLockException $e) {
117
            self::assertSame($test, $e->getEntity());
118
        }
119
    }
120
121
    public function testMultipleFlushesDoIncrementalUpdates() : void
122
    {
123
        $test = new OptimisticStandard();
124
125
        for ($i = 0; $i < 5; $i++) {
126
            $test->name = 'test' . $i;
127
128
            $this->em->persist($test);
129
            $this->em->flush();
130
131
            self::assertInternalType('int', $test->getVersion());
132
            self::assertEquals($i + 1, $test->getVersion());
133
        }
134
    }
135
136
    public function testStandardInsertSetsInitialVersionValue() : OptimisticStandard
137
    {
138
        $test = new OptimisticStandard();
139
140
        $test->name = 'test';
141
142
        $this->em->persist($test);
143
        $this->em->flush();
144
145
        self::assertInternalType('int', $test->getVersion());
146
        self::assertEquals(1, $test->getVersion());
147
148
        return $test;
149
    }
150
151
    /**
152
     * @depends testStandardInsertSetsInitialVersionValue
153
     */
154
    public function testStandardFailureThrowsException(OptimisticStandard $entity) : void
155
    {
156
        $q = $this->em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticStandard t WHERE t.id = :id');
157
158
        $q->setParameter('id', $entity->id);
159
160
        $test = $q->getSingleResult();
161
162
        // Manually update/increment the version so we can try and save the same
163
        // $test and make sure the exception is thrown saying the record was
164
        // changed or updated since you read it
165
        $this->conn->executeQuery('UPDATE optimistic_standard SET version = ? WHERE id = ?', [2, $test->id]);
166
167
        // Now lets change a property and try and save it again
168
        $test->name = 'WHATT???';
169
170
        try {
171
            $this->em->flush();
172
        } catch (OptimisticLockException $e) {
173
            self::assertSame($test, $e->getEntity());
174
        }
175
    }
176
177
    public function testLockWorksWithProxy() : void
178
    {
179
        $test       = new OptimisticStandard();
180
        $test->name = 'test';
181
182
        $this->em->persist($test);
183
        $this->em->flush();
184
        $this->em->clear();
185
186
        $proxy = $this->em->getReference(OptimisticStandard::class, $test->id);
187
188
        $this->em->lock($proxy, LockMode::OPTIMISTIC, 1);
189
190
        self::addToAssertionCount(1);
0 ignored issues
show
Bug Best Practice introduced by
The method PHPUnit\Framework\TestCase::addToAssertionCount() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

190
        self::/** @scrutinizer ignore-call */ 
191
              addToAssertionCount(1);
Loading history...
191
    }
192
193
    public function testOptimisticTimestampSetsDefaultValue() : OptimisticTimestamp
194
    {
195
        $test = new OptimisticTimestamp();
196
197
        $test->name = 'Testing';
198
199
        self::assertNull($test->version, 'Pre-Condition');
200
201
        $this->em->persist($test);
202
        $this->em->flush();
203
204
        self::assertInstanceOf('DateTime', $test->version);
205
206
        return $test;
207
    }
208
209
    public function testOptimisticImmutableTimestampSetsDefaultValue() : OptimisticImmutableTimestamp
210
    {
211
        $test = new OptimisticImmutableTimestamp();
212
213
        $test->name = 'Testing';
214
215
        self::assertNull($test->version, 'Pre-Condition');
216
217
        $this->em->persist($test);
218
        $this->em->flush();
219
220
        self::assertInstanceOf(DateTimeImmutable::class, $test->version);
221
222
        return $test;
223
    }
224
225
    /**
226
     * @depends testOptimisticTimestampSetsDefaultValue
227
     */
228
    public function testOptimisticTimestampFailureThrowsException(OptimisticTimestamp $entity) : void
229
    {
230
        $q = $this->em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticTimestamp t WHERE t.id = :id');
231
232
        $q->setParameter('id', $entity->id);
233
234
        $test = $q->getSingleResult();
235
236
        self::assertInstanceOf('DateTime', $test->version);
237
238
        // Manually increment the version datetime column
239
        $format = $this->em->getConnection()->getDatabasePlatform()->getDateTimeFormatString();
240
241
        $this->conn->executeQuery('UPDATE optimistic_timestamp SET version = ? WHERE id = ?', [date($format, strtotime($test->version->format($format)) + 3600), $test->id]);
242
243
        // Try and update the record and it should throw an exception
244
        $caughtException = null;
245
        $test->name      = 'Testing again';
246
247
        try {
248
            $this->em->flush();
249
        } catch (OptimisticLockException $e) {
250
            $caughtException = $e;
251
        }
252
253
        self::assertNotNull($caughtException, 'No OptimisticLockingException was thrown');
254
        self::assertSame($test, $caughtException->getEntity());
0 ignored issues
show
Bug introduced by
The method getEntity() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

254
        self::assertSame($test, $caughtException->/** @scrutinizer ignore-call */ getEntity());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
255
    }
256
257
    /**
258
     * @depends testOptimisticTimestampSetsDefaultValue
259
     */
260
    public function testOptimisticTimestampLockFailureThrowsException(OptimisticTimestamp $entity) : void
261
    {
262
        $q = $this->em->createQuery('SELECT t FROM Doctrine\Tests\ORM\Functional\Locking\OptimisticTimestamp t WHERE t.id = :id');
263
264
        $q->setParameter('id', $entity->id);
265
266
        $test = $q->getSingleResult();
267
268
        self::assertInstanceOf('DateTime', $test->version);
269
270
        // Try to lock the record with an older timestamp and it should throw an exception
271
        $caughtException = null;
272
273
        try {
274
            $expectedVersionExpired = DateTime::createFromFormat('U', (string) ($test->version->getTimestamp()-3600));
275
276
            $this->em->lock($test, LockMode::OPTIMISTIC, $expectedVersionExpired);
0 ignored issues
show
Bug introduced by
$expectedVersionExpired of type DateTime|false is incompatible with the type integer|null expected by parameter $lockVersion of Doctrine\ORM\EntityManagerInterface::lock(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

276
            $this->em->lock($test, LockMode::OPTIMISTIC, /** @scrutinizer ignore-type */ $expectedVersionExpired);
Loading history...
277
        } catch (OptimisticLockException $e) {
278
            $caughtException = $e;
279
        }
280
281
        self::assertNotNull($caughtException, 'No OptimisticLockingException was thrown');
282
        self::assertSame($test, $caughtException->getEntity());
283
    }
284
}
285
286
/**
287
 * @ORM\Entity
288
 * @ORM\Table(name="optimistic_joined_parent")
289
 * @ORM\InheritanceType("JOINED")
290
 * @ORM\DiscriminatorColumn(name="discr", type="string")
291
 * @ORM\DiscriminatorMap({"parent" = OptimisticJoinedParent::class, "child" = OptimisticJoinedChild::class})
292
 */
293
class OptimisticJoinedParent
294
{
295
    /**
296
     * @ORM\Id @ORM\Column(type="integer")
297
     * @ORM\GeneratedValue(strategy="AUTO")
298
     */
299
    public $id;
300
301
    /** @ORM\Column(type="string", length=255) */
302
    public $name;
303
304
    /** @ORM\Version @ORM\Column(type="integer") */
305
    public $version;
306
}
307
308
/**
309
 * @ORM\Entity
310
 * @ORM\Table(name="optimistic_joined_child")
311
 */
312
class OptimisticJoinedChild extends OptimisticJoinedParent
313
{
314
    /** @ORM\Column(type="string", length=255) */
315
    public $whatever;
316
}
317
318
/**
319
 * @ORM\Entity
320
 * @ORM\Table(name="optimistic_standard")
321
 */
322
class OptimisticStandard
323
{
324
    /**
325
     * @ORM\Id @ORM\Column(type="integer")
326
     * @ORM\GeneratedValue(strategy="AUTO")
327
     */
328
    public $id;
329
330
    /** @ORM\Column(type="string", length=255) */
331
    public $name;
332
333
    /** @ORM\Version @ORM\Column(type="integer") */
334
    private $version;
335
336
    public function getVersion()
337
    {
338
        return $this->version;
339
    }
340
}
341
342
/**
343
 * @ORM\Entity
344
 * @ORM\Table(name="optimistic_timestamp")
345
 */
346
class OptimisticTimestamp
347
{
348
    /**
349
     * @ORM\Id @ORM\Column(type="integer")
350
     * @ORM\GeneratedValue(strategy="AUTO")
351
     */
352
    public $id;
353
354
    /** @ORM\Column(type="string", length=255) */
355
    public $name;
356
357
    /** @ORM\Version @ORM\Column(type="datetime") */
358
    public $version;
359
}
360
361
/**
362
 * @ORM\Entity
363
 * @ORM\Table(name="optimistic_immutable_timestamp")
364
 */
365
class OptimisticImmutableTimestamp
366
{
367
    /**
368
     * @ORM\Id @ORM\Column(type="integer")
369
     * @ORM\GeneratedValue(strategy="AUTO")
370
     */
371
    public $id;
372
373
    /** @ORM\Column(type="string", length=255) */
374
    public $name;
375
376
    /** @ORM\Version @ORM\Column(type="datetime_immutable") */
377
    public $version;
378
}
379