DirtyTest.php 38.6 KB
Newer Older
1
2
3
4
5
6
7
8
<?php
/**
 * Created by PhpStorm.
 * User: crose
 * Date: 2/2/16
 * Time: 8:47 AM
 */

9
namespace qfq;
10

11
require_once(__DIR__ . '/../database/AbstractDatabaseTest.php');
12

13
14
15
16
17
/*
 * Open to check
 * - FORM_DELETE
 */

18
19
20
/*
 * 1. DirtyMode: none
 * ===============
21
22
23
 * r=0, Alice lock, Alice releasefirst simple unit tests for recdord locking.-k
 * r>0, Alice lock, Alice release
 * Bob lock, Alice lock
24
25
26
 *
 * 2. DirtyMode: advisory
 * ===================
27
28
29
30
31
32
33
 * r=0, Alice lock, Alice release
 * r>0, Alice lock, Alice release
 * Alice lock, Alice timeout, Alice release
 * Alice release
 * Alice lock, Record modified by someone else, Alice release
 * Bob lock, Alice lock
 * Bob lock, Bob timeout, Alice lock
34
 * Alice lock 1, Alice lock 2, Bob lock 3, Bob release 3, Alice release 2, Alice release 1
35
 * Alice lock 1 Form A, Bob lock 1 Form B
36
37
38
 *
 * 3. DirtyMode: exclusive
 * ====================
39
40
41
42
43
44
45
 * r=0, Alice lock, Alice release
 * r>0, Alice lock, Alice release
 * Alice lock, Alice timeout, Alice release
 * Alice release
 * Alice lock, Record modified by someone else, Alice release
 * Bob lock, Alice lock
 * Bob lock, Bob timeout, Alice lock
46
 * Alice lock 1, Alice lock 2, Bob lock 3, Bob release 3, Alice release 2, Alice release 1
47
 * Alice lock 1 Form A, Bob lock 1 Form B
48
49
50
51
 *
 * 4. Mix Mode
 * ===========
 *
52
53
54
55
56
57
58
 * For all: Alice lock 1 Form A, Bob lock 1 Form B
 *   Form A: none, Form B: exclusive
 *   Form A: none, Form B: advisory
 *   Form A: advisory, Form B: none
 *   Form A: advisory, Form B: exclusive
 *   Form A: exclusive, Form B: none
 *   Form A: exclusive, Form B: advisory
59
60
 *
 *
61
62
 * 5. Special
 * ==========
63
 *
64
65
 * Compute expire time
 * Lock non existing record
66
67
 */

68
69
70
/**
 * Class DirtyTest
 */
71
class DirtyTest extends AbstractDatabaseTest {
72

73
74
    /**
     * @var Sip Instance of class SIP
75
76
77
78
     */
    protected $sip = null;

    /**
Carsten  Rose's avatar
Carsten Rose committed
79
     * @var Database instantiated class
80
     */
81
    protected $dbArray = null;
82

83
84
85
86
87
    /**********************************************************************
     * 1. DirtyMode: none
     */


88
    /**
89
     * r=0, Alice lock, Alice release
Carsten  Rose's avatar
Carsten Rose committed
90
91
92
93
94
     *
     * @throws CodeException
     * @throws DbException
     * @throws UserFormException
     * @throws UserReportException
95
96
97
     */
    public function testNoneR0AliceLockRelease() {

98
        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=0&form=lockNone", RETURN_SIP);
99
100
101

        // Alice lock
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_LOCK;
102
        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => ''];
Carsten  Rose's avatar
Carsten Rose committed
103
        $dirty = new Dirty();
104
105
106
107

        $result = $dirty->process();
        $this->assertEquals($expected, $result);

108
        $this->dbArray[DB_INDEX_DEFAULT]->sql("SELECT id FROM Dirty", ROW_EXPECT_0);
109
110
111

        // Alice release
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_RELEASE;
Carsten  Rose's avatar
Carsten Rose committed
112
        $dirty = new Dirty();
113
114
115
116

        $result = $dirty->process();
        $this->assertEquals($expected, $result);

117
        $this->dbArray[DB_INDEX_DEFAULT]->sql("SELECT id FROM Dirty", ROW_EXPECT_0);
118
119
    }

120
    /**
121
     * r=1, Alice lock, Alice release
Carsten  Rose's avatar
Carsten Rose committed
122
123
124
125
126
     *
     * @throws CodeException
     * @throws DbException
     * @throws UserFormException
     * @throws UserReportException
127
128
129
     */
    public function testNoneR1AliceLockRelease() {

130
131
132
133
        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=1&form=lockNone", RETURN_SIP);

        // Alice lock
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_LOCK;
134
        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => ''];
Carsten  Rose's avatar
Carsten Rose committed
135
        $dirty = new Dirty();
136
137
138
139

        $result = $dirty->process();
        $this->assertEquals($expected, $result);

140
        $this->dbArray[DB_INDEX_DEFAULT]->sql("SELECT id FROM Dirty", ROW_EXPECT_0);
141
142
143

        // Alice release
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_RELEASE;
144
        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => ''];
Carsten  Rose's avatar
Carsten Rose committed
145
        $dirty = new Dirty();
146
147
148
149

        $result = $dirty->process();
        $this->assertEquals($expected, $result);

150
        $this->dbArray[DB_INDEX_DEFAULT]->sql("SELECT id FROM Dirty", ROW_EXPECT_0);
151
152
    }

153
154
    /**
     *  Alice lock, Record modified by someone else, Alice release
Carsten  Rose's avatar
Carsten Rose committed
155
156
157
158
159
     *
     * @throws CodeException
     * @throws DbException
     * @throws UserFormException
     * @throws UserReportException
160
161
162
163
164
165
166
     */
    public function testNoneAliceLockBobLock() {

        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=1&form=lockNone", RETURN_SIP);

        // Alice lock - but change cookie later to Bob
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_LOCK;
Carsten  Rose's avatar
Carsten Rose committed
167
        $dirty = new Dirty();
168
169
170
171

        $result = $dirty->process();

        // move lock to another owner (Alice fake becomes Bob) - but there is no lockrecord!
172
        $this->dbArray[DB_INDEX_DEFAULT]->sql("UPDATE Dirty SET qfqUserSessionCookie='SessionCookieBob' WHERE id=1", ROW_REGULAR);
173
174
175
176
177
178
179
180
181

        // Alice lock again
        $result = $dirty->process();

        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => ''];

        $this->assertEquals($expected, $result);
    }

182
183
184
185
    /**********************************************************************
     * 1. DirtyMode: advisory
     */

186
    /**
187
     * r=0, Alice lock, Alice release
Carsten  Rose's avatar
Carsten Rose committed
188
189
190
191
192
     *
     * @throws CodeException
     * @throws DbException
     * @throws UserFormException
     * @throws UserReportException
193
194
195
196
197
198
199
     */
    public function testAdvisoryR0AliceLockRelease() {

        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=0&form=lockAdvisory", RETURN_SIP);

        // Alice lock
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_LOCK;
200
        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => ''];
Carsten  Rose's avatar
Carsten Rose committed
201
        $dirty = new Dirty();
202
203
204
205

        $result = $dirty->process();
        $this->assertEquals($expected, $result);

206
        $this->dbArray[DB_INDEX_DEFAULT]->sql("SELECT id FROM Dirty", ROW_EXPECT_0);
207
208
209

        // Alice release
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_RELEASE;
Carsten  Rose's avatar
Carsten Rose committed
210
        $dirty = new Dirty();
211
212
213
214

        $result = $dirty->process();
        $this->assertEquals($expected, $result);

215
        $this->dbArray[DB_INDEX_DEFAULT]->sql("SELECT id FROM Dirty", ROW_EXPECT_0);
216
217
    }

218
    /**
219
     * r=1, Alice lock, Alice release
Carsten  Rose's avatar
Carsten Rose committed
220
221
222
223
224
     *
     * @throws CodeException
     * @throws DbException
     * @throws UserFormException
     * @throws UserReportException
225
226
227
228
229
230
231
     */
    public function testAdvisoryR1AliceLockRelease() {

        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=1&form=lockAdvisory", RETURN_SIP);

        // Alice lock
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_LOCK;
232
        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => '', API_LOCK_TIMEOUT => 900];
Carsten  Rose's avatar
Carsten Rose committed
233
        $dirty = new Dirty();
234
235
236
237

        $result = $dirty->process();
        $this->assertEquals($expected, $result);

238
        $this->dbArray[DB_INDEX_DEFAULT]->sql("SELECT id FROM Dirty", ROW_EXPECT_1);
239
240
241

        // Alice release
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_RELEASE;
242
        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => ''];
Carsten  Rose's avatar
Carsten Rose committed
243
        $dirty = new Dirty();
244
245
246
247

        $result = $dirty->process();
        $this->assertEquals($expected, $result);

248
        $this->dbArray[DB_INDEX_DEFAULT]->sql("SELECT id FROM Dirty", ROW_EXPECT_0);
249
250
    }

251
    /**
Carsten  Rose's avatar
Carsten Rose committed
252
253
254
255
256
257
     * Alice lock, Alice timeout, Alice release
     *
     * @throws CodeException
     * @throws DbException
     * @throws UserFormException
     * @throws UserReportException
258
259
260
261
262
263
264
     */
    public function testAdvisoryAliceLockTimeoutRelease() {

        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=1&form=lockAdvisory", RETURN_SIP);

        // Alice lock
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_LOCK;
265
        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => '', API_LOCK_TIMEOUT => 900];
Carsten  Rose's avatar
Carsten Rose committed
266
        $dirty = new Dirty();
267
268
269
270
271

        $result = $dirty->process();
        $this->assertEquals($expected, $result);

        // Timeout
272
        $rc = $this->dbArray[DB_INDEX_DEFAULT]->sql("UPDATE Dirty SET expire='2001-01-01 00:00:00'", ROW_REGULAR);
273
274
275
276
        $this->assertEquals(1, $rc, 'Expect that exactly one record has been updated');

        // Alice release
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_RELEASE;
277
        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => ''];
Carsten  Rose's avatar
Carsten Rose committed
278
        $dirty = new Dirty();
279
280
281
282

        $result = $dirty->process();
        $this->assertEquals($expected, $result);

283
        $this->dbArray[DB_INDEX_DEFAULT]->sql("SELECT id FROM Dirty", ROW_EXPECT_0);
284
285
    }

286
287
    /**
     *  Alice lock, Record modified by someone else, Alice release
Carsten  Rose's avatar
Carsten Rose committed
288
289
290
291
292
     *
     * @throws CodeException
     * @throws DbException
     * @throws UserFormException
     * @throws UserReportException
293
294
295
296
297
298
299
300
     */
    public function testAdvisoryAliceLockModifiedRelease() {

        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=1&form=lockAdvisory", RETURN_SIP);

        // Alice lock
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_LOCK;
        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => '', API_LOCK_TIMEOUT => 900];
Carsten  Rose's avatar
Carsten Rose committed
301
        $dirty = new Dirty();
302

303
        $this->dbArray[DB_INDEX_DEFAULT]->sql("UPDATE Person SET modified='2001-01-01 13:14:15' WHERE id=1", ROW_REGULAR);
304
305
306
307
        $result = $dirty->process();
        $this->assertEquals($expected, $result);

        // change modified
308
        $this->dbArray[DB_INDEX_DEFAULT]->sql("UPDATE Person SET modified='2002-02-02 16:17:18' WHERE id=1", ROW_REGULAR);
309
310
311

        // Alice release
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_RELEASE;
Carsten  Rose's avatar
Carsten Rose committed
312
        $dirty = new Dirty();
313
314
315

        $result = $dirty->process();

Carsten  Rose's avatar
Carsten Rose committed
316
317
318
        //TODO: this should be enabled again.
//        $expected = [API_STATUS => API_ANSWER_STATUS_CONFLICT, API_MESSAGE => 'The record has been modified in the meantime. Please reload the form, edit and save again.'];
//        $this->assertEquals($expected, $result);
319
320
321
322
    }

    /**
     *  Bob lock, Alice lock
Carsten  Rose's avatar
Carsten Rose committed
323
324
325
326
327
     *
     * @throws CodeException
     * @throws DbException
     * @throws UserFormException
     * @throws UserReportException
328
329
330
331
332
333
334
     */
    public function testAdvisoryBobLockAliceLock() {

        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=1&form=lockAdvisory", RETURN_SIP);

        // Alice lock - but change cookie later to Bob
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_LOCK;
Carsten  Rose's avatar
Carsten Rose committed
335
        $dirty = new Dirty();
336
337
338
339

        $result = $dirty->process();

        // move lock to another owner (Alice fake becomes Bob)
340
        $this->dbArray[DB_INDEX_DEFAULT]->sql("UPDATE Dirty SET qfqUserSessionCookie='SessionCookieBob' WHERE id=1", ROW_REGULAR);
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355

        // Alice lock again
        $result = $dirty->process();

        $msg = 'The record has already';
        $expected = [API_STATUS => API_ANSWER_STATUS_CONFLICT_ALLOW_FORCE, API_MESSAGE => $msg];

        // cut IP, User and Timestamp
        $result[API_MESSAGE] = substr($result[API_MESSAGE], 0, strlen($msg));

        $this->assertEquals($expected, $result);
    }


    /**
Carsten  Rose's avatar
Carsten Rose committed
356
357
358
359
360
361
     * Bob lock, Bob timeout, Alice lock
     *
     * @throws CodeException
     * @throws DbException
     * @throws UserFormException
     * @throws UserReportException
362
363
364
365
366
367
368
369
     */
    public function testAdvisoryBobLockTimeoutAliceLock() {

        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=1&form=lockAdvisory", RETURN_SIP);

        // Alice fake (becomes Bob) lock
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_LOCK;
        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => '', API_LOCK_TIMEOUT => 900];
Carsten  Rose's avatar
Carsten Rose committed
370
        $dirty = new Dirty();
371
372
373
374
375

        $result = $dirty->process();
        $this->assertEquals($expected, $result);

        // Set timeout to expired, Change Alice to Bob
376
        $rc = $this->dbArray[DB_INDEX_DEFAULT]->sql("UPDATE Dirty SET expire='2001-01-01 00:00:00', qfqUserSessionCookie='SessionCookieBob'", ROW_REGULAR);
377
378
379
380
        $this->assertEquals(1, $rc, 'Expect that exactly one record has been updated');

        // Alice lock
        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => '', API_LOCK_TIMEOUT => 900];
Carsten  Rose's avatar
Carsten Rose committed
381
        $dirty = new Dirty();
382
383
384
385
386

        $result = $dirty->process();
        $this->assertEquals($expected, $result);
    }

387
    /**
Carsten  Rose's avatar
Carsten Rose committed
388
389
390
391
392
393
     * Alice lock 1, Alice lock 2, Bob lock 3, Bob release 3, Alice release 2, Alice release 1
     *
     * @throws CodeException
     * @throws DbException
     * @throws UserFormException
     * @throws UserReportException
394
395
396
397
398
399
400
401
     */
    public function testAdvisoryAliceLock1AliceLock2BobLock3BobRelease3AliceRelease2AliceRelease1() {


        // Alice lock 1
        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=1&form=lockAdvisory", RETURN_SIP);
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_LOCK;
        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => '', API_LOCK_TIMEOUT => 900];
Carsten  Rose's avatar
Carsten Rose committed
402
        $dirty = new Dirty();
403
404
405
406
407
408

        $result = $dirty->process();
        $this->assertEquals($expected, $result);

        // Alice lock 2
        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=2&form=lockAdvisory", RETURN_SIP);
Carsten  Rose's avatar
Carsten Rose committed
409
        $dirty = new Dirty();
410
411
412
413
414
415
416
417

        $result = $dirty->process();
        $this->assertEquals($expected, $result);

        // Bob becomes logged in user
        $_COOKIE[SESSION_NAME] = 'SessionCookieBob';

        // Create third Person record
418
        $rc = $this->dbArray[DB_INDEX_DEFAULT]->sql("INSERT INTO Person (`name`, `firstName`) VALUES ('no', 'name')", ROW_REGULAR);
419
420
421

        // Bob lock 3
        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=3&form=lockAdvisory", RETURN_SIP);
Carsten  Rose's avatar
Carsten Rose committed
422
        $dirty = new Dirty();
423
424
425
426
427
428
429
430

        $result = $dirty->process();
        $this->assertEquals($expected, $result);

        // RELEASE

        // Bob Release 3
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_RELEASE;
Carsten  Rose's avatar
Carsten Rose committed
431
        $dirty = new Dirty();
432
433
434
435
436
437

        $result = $dirty->process();

        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => ''];
        $this->assertEquals($expected, $result);

438
        $session = Session::getInstance(true);
439
440
441
442
443
444
445
446
        $session::clearAll();

        // Alice Release 2
        $_COOKIE[SESSION_NAME] = 'SessionCookieAlice';
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_RELEASE;

        // with phpunit, we only have one sip at a time
        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=2&form=lockExclusive", RETURN_SIP);
Carsten  Rose's avatar
Carsten Rose committed
447
        $dirty = new Dirty();
448
449
450
451
452
453
454
455
456
457

        $result = $dirty->process();

        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => ''];
        $this->assertEquals($expected, $result);

        // Alice Release 1
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_RELEASE;
        // with phpunit, we only have one sip at a time
        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=1&form=lockExclusive", RETURN_SIP);
Carsten  Rose's avatar
Carsten Rose committed
458
        $dirty = new Dirty();
459
460
461
462
463
464
465
466
467

        $result = $dirty->process();

        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => ''];
        $this->assertEquals($expected, $result);
    }

    /**
     *  Alice lock 1 Form A, Bob lock 1 Form B
Carsten  Rose's avatar
Carsten Rose committed
468
469
470
471
472
     *
     * @throws CodeException
     * @throws DbException
     * @throws UserFormException
     * @throws UserReportException
473
474
475
476
477
478
479
480
481
     */
    public function testAdvisoryAliceFormALock1BobFormBLock1() {

        // Create clean environment
        $this->clean('SessionCookieAlice');

        // Alice lock 1 Form A
        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=1&form=lockAdvisory", RETURN_SIP);
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_LOCK;
Carsten  Rose's avatar
Carsten Rose committed
482
        $dirty = new Dirty();
483
484
485

        $result = $dirty->process();

Carsten  Rose's avatar
Carsten Rose committed
486
        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => '', API_LOCK_TIMEOUT => 900];
487
488
489
490
491
492
493
494
        $this->assertEquals($expected, $result);

        // Create clean environment
        $this->clean('SessionCookieBob');

        // Bob lock 1 Form B
        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=1&form=lockAdvisory2", RETURN_SIP);
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_LOCK;
Carsten  Rose's avatar
Carsten Rose committed
495
        $dirty = new Dirty();
496
497
498
499
500
501
502
503
504
505
506
507

        $result = $dirty->process();

        $msg = 'The record has already';
        $expected = [API_STATUS => API_ANSWER_STATUS_CONFLICT_ALLOW_FORCE, API_MESSAGE => $msg];

        // cut IP, User and Timestamp
        $result[API_MESSAGE] = substr($result[API_MESSAGE], 0, strlen($msg));

        $this->assertEquals($expected, $result);
    }

508
509
510
511
    /**********************************************************************
     * 3. DirtyMode: exclusive
     */

512
    /**
513
     * r=0, Alice lock, Alice release
Carsten  Rose's avatar
Carsten Rose committed
514
515
516
517
518
     *
     * @throws CodeException
     * @throws DbException
     * @throws UserFormException
     * @throws UserReportException
519
520
521
522
523
524
525
     */
    public function testExclusiveR0AliceLockRelease() {

        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=0&form=lockExclusive", RETURN_SIP);

        // Alice lock
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_LOCK;
526
        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => ''];
Carsten  Rose's avatar
Carsten Rose committed
527
        $dirty = new Dirty();
528
529
530
531

        $result = $dirty->process();
        $this->assertEquals($expected, $result);

532
        $this->dbArray[DB_INDEX_DEFAULT]->sql("SELECT id FROM Dirty", ROW_EXPECT_0);
533
534
535

        // Alice release
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_RELEASE;
Carsten  Rose's avatar
Carsten Rose committed
536
        $dirty = new Dirty();
537
538
539
540

        $result = $dirty->process();
        $this->assertEquals($expected, $result);

541
        $this->dbArray[DB_INDEX_DEFAULT]->sql("SELECT id FROM Dirty", ROW_EXPECT_0);
542
543
    }

544
    /**
545
     * r=1, Alice lock, Alice release
Carsten  Rose's avatar
Carsten Rose committed
546
547
548
549
550
     *
     * @throws CodeException
     * @throws DbException
     * @throws UserFormException
     * @throws UserReportException
551
552
553
     */
    public function testExclusiveR1AliceLockRelease() {

554
555
556
        // Create clean environment
        $this->clean('SessionCookieAlice');

557
558
559
560
        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=1&form=lockExclusive", RETURN_SIP);

        // Alice lock
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_LOCK;
561
        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => '', API_LOCK_TIMEOUT => 900];
Carsten  Rose's avatar
Carsten Rose committed
562
        $dirty = new Dirty();
563
564
565
566

        $result = $dirty->process();
        $this->assertEquals($expected, $result);

567
        $this->dbArray[DB_INDEX_DEFAULT]->sql("SELECT id FROM Dirty", ROW_EXPECT_1);
568
569
570

        // Alice release
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_RELEASE;
571
        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => ''];
Carsten  Rose's avatar
Carsten Rose committed
572
        $dirty = new Dirty();
573
574
575
576

        $result = $dirty->process();
        $this->assertEquals($expected, $result);

577
        $this->dbArray[DB_INDEX_DEFAULT]->sql("SELECT id FROM Dirty", ROW_EXPECT_0);
578
579
    }

580
    /**
Carsten  Rose's avatar
Carsten Rose committed
581
582
583
584
585
586
     * Alice lock, Alice timeout, Alice release
     *
     * @throws CodeException
     * @throws DbException
     * @throws UserFormException
     * @throws UserReportException
587
588
     */
    public function testExclusiveAliceLockTimeoutRelease() {
589

590
        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=1&form=lockExclusive", RETURN_SIP);
591

592
593
        // Alice lock
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_LOCK;
594
        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => '', API_LOCK_TIMEOUT => 900];
Carsten  Rose's avatar
Carsten Rose committed
595
        $dirty = new Dirty();
596
597
598
599
600

        $result = $dirty->process();
        $this->assertEquals($expected, $result);

        // Timeout
601
        $rc = $this->dbArray[DB_INDEX_DEFAULT]->sql("UPDATE Dirty SET expire='2001-01-01 00:00:00'", ROW_REGULAR);
602
603
604
605
        $this->assertEquals(1, $rc, 'Expect that exactly one record has been updated');

        // Alice release
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_RELEASE;
606
        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => ''];
Carsten  Rose's avatar
Carsten Rose committed
607
        $dirty = new Dirty();
608
609
610

        $result = $dirty->process();
        $this->assertEquals($expected, $result);
611

612
        $this->dbArray[DB_INDEX_DEFAULT]->sql("SELECT id FROM Dirty", ROW_EXPECT_0);
613
614
    }

615
616
617
    /**
     * @expectedException \qfq\UserFormException
     */
618
619
620
621
622
623
//    public function testExclusiveAliceReleaseException() {
//
//        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=1&form=lockExclusive", RETURN_SIP);
//
//        // Alice release
//        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_RELEASE;
Carsten  Rose's avatar
Carsten Rose committed
624
//        $dirty = new Dirty();
625
626
627
//
//        $dirty->process();
//    }
628
629

    /**
630
     *  Alice lock, Record modified by someone else, Alice release
Carsten  Rose's avatar
Carsten Rose committed
631
632
633
634
635
     *
     * @throws CodeException
     * @throws DbException
     * @throws UserFormException
     * @throws UserReportException
636
     */
637
    public function testExclusiveAliceLockModifiedRelease() {
638

639
640
        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=1&form=lockExclusive", RETURN_SIP);

641
642
        // Alice lock
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_LOCK;
643
        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => '', API_LOCK_TIMEOUT => 900];
Carsten  Rose's avatar
Carsten Rose committed
644
        $dirty = new Dirty();
645

646
        $this->dbArray[DB_INDEX_DEFAULT]->sql("UPDATE Person SET modified='2001-01-01 13:14:15' WHERE id=1", ROW_REGULAR);
647
648
649
        $result = $dirty->process();
        $this->assertEquals($expected, $result);

650
        // change modified
651
        $this->dbArray[DB_INDEX_DEFAULT]->sql("UPDATE Person SET modified='2002-02-02 16:17:18' WHERE id=1", ROW_REGULAR);
652

653
654
        // Alice release
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_RELEASE;
Carsten  Rose's avatar
Carsten Rose committed
655
        $dirty = new Dirty();
656

657
        $result = $dirty->process();
658

Carsten  Rose's avatar
Carsten Rose committed
659
660
661
        //TODO: this should be enabled again.
//        $expected = [API_STATUS => API_ANSWER_STATUS_CONFLICT, API_MESSAGE => 'The record has been modified in the meantime. Please reload the form, edit and save again.'];
//        $this->assertEquals($expected, $result);
662
663
    }

664
665
    /**
     *  Bob lock, Alice lock
Carsten  Rose's avatar
Carsten Rose committed
666
667
668
669
670
     *
     * @throws CodeException
     * @throws DbException
     * @throws UserFormException
     * @throws UserReportException
671
     */
672
    public function testExclusiveBobLockAliceLock() {
673
674
675

        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=1&form=lockExclusive", RETURN_SIP);

676
677
        // Alice lock - but change cookie later to Bob
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_LOCK;
Carsten  Rose's avatar
Carsten Rose committed
678
        $dirty = new Dirty();
679
680
681
682

        $result = $dirty->process();

        // move lock to another owner (Alice fake becomes Bob)
683
        $this->dbArray[DB_INDEX_DEFAULT]->sql("UPDATE Dirty SET qfqUserSessionCookie='SessionCookieBob' WHERE id=1", ROW_REGULAR);
684
685
686
687
688
689
690
691
692
693
694
695
696
697

        // Alice lock again
        $result = $dirty->process();

        $msg = 'The record has already';
        $expected = [API_STATUS => API_ANSWER_STATUS_CONFLICT, API_MESSAGE => $msg];

        // cut IP, User and Timestamp
        $result[API_MESSAGE] = substr($result[API_MESSAGE], 0, strlen($msg));

        $this->assertEquals($expected, $result);
    }

    /**
Carsten  Rose's avatar
Carsten Rose committed
698
699
700
701
702
703
     *  Bob lock, Bob timeout, Alice lock
     *
     * @throws CodeException
     * @throws DbException
     * @throws UserFormException
     * @throws UserReportException
704
705
706
707
708
709
710
711
     */
    public function testExclusiveBobLockTimeoutAliceLock() {

        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=1&form=lockExclusive", RETURN_SIP);

        // Alice fake (becomes Bob) lock
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_LOCK;
        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => '', API_LOCK_TIMEOUT => 900];
Carsten  Rose's avatar
Carsten Rose committed
712
        $dirty = new Dirty();
713

714
715
716
717
        $result = $dirty->process();
        $this->assertEquals($expected, $result);

        // Set timeout to expired, Change Alice to Bob
718
        $rc = $this->dbArray[DB_INDEX_DEFAULT]->sql("UPDATE Dirty SET expire='2001-01-01 00:00:00', qfqUserSessionCookie='SessionCookieBob'", ROW_REGULAR);
719
720
721
722
        $this->assertEquals(1, $rc, 'Expect that exactly one record has been updated');

        // Alice lock
        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => '', API_LOCK_TIMEOUT => 900];
Carsten  Rose's avatar
Carsten Rose committed
723
        $dirty = new Dirty();
724

725
726
        $result = $dirty->process();
        $this->assertEquals($expected, $result);
727
728
    }

729
    /**
Carsten  Rose's avatar
Carsten Rose committed
730
731
732
733
734
735
     * Alice lock 1, Alice lock 2, Bob lock 3, Bob release 3, Alice release 2, Alice release 1
     *
     * @throws CodeException
     * @throws DbException
     * @throws UserFormException
     * @throws UserReportException
736
737
738
739
740
741
742
743
     */
    public function testExclusiveAliceLock1AliceLock2BobLock3BobRelease3AliceRelease2AliceRelease1() {


        // Alice lock 1
        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=1&form=lockExclusive", RETURN_SIP);
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_LOCK;
        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => '', API_LOCK_TIMEOUT => 900];
Carsten  Rose's avatar
Carsten Rose committed
744
        $dirty = new Dirty();
745
746
747
748
749
750

        $result = $dirty->process();
        $this->assertEquals($expected, $result);

        // Alice lock 2
        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=2&form=lockExclusive", RETURN_SIP);
Carsten  Rose's avatar
Carsten Rose committed
751
        $dirty = new Dirty();
752
753
754
755
756
757
758
759

        $result = $dirty->process();
        $this->assertEquals($expected, $result);

        // Bob becomes logged in user
        $_COOKIE[SESSION_NAME] = 'SessionCookieBob';

        // Create third Person record
760
        $rc = $this->dbArray[DB_INDEX_DEFAULT]->sql("INSERT INTO Person (`name`, `firstName`) VALUES ('no', 'name')", ROW_REGULAR);
761
762
763

        // Bob lock 3
        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=3&form=lockExclusive", RETURN_SIP);
Carsten  Rose's avatar
Carsten Rose committed
764
        $dirty = new Dirty();
765
766
767
768
769
770
771
772

        $result = $dirty->process();
        $this->assertEquals($expected, $result);

        // RELEASE

        // Bob Release 3
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_RELEASE;
Carsten  Rose's avatar
Carsten Rose committed
773
        $dirty = new Dirty();
774
775
776
777
778
779

        $result = $dirty->process();

        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => ''];
        $this->assertEquals($expected, $result);

780
        $session = Session::getInstance(true);
781
782
783
784
785
786
787
788
        $session::clearAll();

        // Alice Release 2
        $_COOKIE[SESSION_NAME] = 'SessionCookieAlice';
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_RELEASE;

        // with phpunit, we only have one sip at a time
        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=2&form=lockExclusive", RETURN_SIP);
Carsten  Rose's avatar
Carsten Rose committed
789
        $dirty = new Dirty();
790
791
792
793
794
795
796
797
798
799

        $result = $dirty->process();

        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => ''];
        $this->assertEquals($expected, $result);

        // Alice Release 1
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_RELEASE;
        // with phpunit, we only have one sip at a time
        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=1&form=lockExclusive", RETURN_SIP);
Carsten  Rose's avatar
Carsten Rose committed
800
        $dirty = new Dirty();
801
802
803
804
805
806
807
808
809

        $result = $dirty->process();

        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => ''];
        $this->assertEquals($expected, $result);
    }

    /**
     *  Alice lock 1 Form A, Bob lock 1 Form B
Carsten  Rose's avatar
Carsten Rose committed
810
811
812
813
814
     *
     * @throws CodeException
     * @throws DbException
     * @throws UserFormException
     * @throws UserReportException
815
816
817
818
819
820
821
822
823
     */
    public function testExclusiveAliceFormALock1BobFormBLock1() {

        // Create clean environment
        $this->clean('SessionCookieAlice');

        // Alice lock 1 Form A
        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=1&form=lockExclusive", RETURN_SIP);
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_LOCK;
Carsten  Rose's avatar
Carsten Rose committed
824
        $dirty = new Dirty();
825
826
827

        $result = $dirty->process();

Carsten  Rose's avatar
Carsten Rose committed
828
        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => '', API_LOCK_TIMEOUT => 900];
829
830
831
832
833
834
835
836
        $this->assertEquals($expected, $result);

        // Create clean environment
        $this->clean('SessionCookieBob');

        // Bob lock 1 Form B
        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=1&form=lockExclusive2", RETURN_SIP);
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_LOCK;
Carsten  Rose's avatar
Carsten Rose committed
837
        $dirty = new Dirty();
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856

        $result = $dirty->process();

        $msg = 'The record has already';
        $expected = [API_STATUS => API_ANSWER_STATUS_CONFLICT, API_MESSAGE => $msg];

        // cut IP, User and Timestamp
        $result[API_MESSAGE] = substr($result[API_MESSAGE], 0, strlen($msg));

        $this->assertEquals($expected, $result);
    }

    /**********************************************************************
     * 4. Mix Mode
     */

    /**
     * Form A: none, Form B: exclusive
     * Alice lock 1 Form A, Bob lock 1 Form B
Carsten  Rose's avatar
Carsten Rose committed
857
858
859
860
861
     *
     * @throws CodeException
     * @throws DbException
     * @throws UserFormException
     * @throws UserReportException
862
863
864
865
866
867
868
869
870
     */
    public function testAliceLock1NoneBobLock1Exclusive() {

        // Create clean environment
        $this->clean('SessionCookieAlice');

        // Alice lock 1 Form A
        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=1&form=lockNone", RETURN_SIP);
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_LOCK;
Carsten  Rose's avatar
Carsten Rose committed
871
        $dirty = new Dirty();
872
873
874

        $result = $dirty->process();

Carsten  Rose's avatar
Carsten Rose committed
875
        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => ''];
876
877
878
879
880
881
882
        $this->assertEquals($expected, $result);

        // Create clean environment
        $this->clean('SessionCookieBob');

        // Bob lock 1 Form B
        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=1&form=lockExclusive", RETURN_SIP);
Carsten  Rose's avatar
Carsten Rose committed
883
        $dirty = new Dirty();
884
885
886
887
888
889
890
891
892
893

        $result = $dirty->process();

        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => '', API_LOCK_TIMEOUT => 900];
        $this->assertEquals($expected, $result);
    }

    /**
     * Form A: none, Form B: advisory
     * Alice lock 1 Form A, Bob lock 1 Form B
Carsten  Rose's avatar
Carsten Rose committed
894
895
896
897
898
     *
     * @throws CodeException
     * @throws DbException
     * @throws UserFormException
     * @throws UserReportException
899
900
901
902
903
904
905
906
907
     */
    public function testAliceLock1NoneBobLock1Advisory() {

        // Create clean environment
        $this->clean('SessionCookieAlice');

        // Alice lock 1 Form A
        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=1&form=lockNone", RETURN_SIP);
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_LOCK;
Carsten  Rose's avatar
Carsten Rose committed
908
        $dirty = new Dirty();
909
910
911

        $result = $dirty->process();

Carsten  Rose's avatar
Carsten Rose committed
912
        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => ''];
913
914
915
916
917
918
919
        $this->assertEquals($expected, $result);

        // Create clean environment
        $this->clean('SessionCookieBob');

        // Bob lock 1 Form B
        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=1&form=lockAdvisory", RETURN_SIP);
Carsten  Rose's avatar
Carsten Rose committed
920
        $dirty = new Dirty();
921
922
923
924
925
926
927
928
929
930

        $result = $dirty->process();

        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => '', API_LOCK_TIMEOUT => 900];
        $this->assertEquals($expected, $result);
    }

    /**
     * Form A: advisory, Form B: none
     * Alice lock 1 Form A, Bob lock 1 Form B
Carsten  Rose's avatar
Carsten Rose committed
931
932
933
934
935
     *
     * @throws CodeException
     * @throws DbException
     * @throws UserFormException
     * @throws UserReportException
936
937
938
939
940
941
942
943
944
     */
    public function testAliceLock1AdvisoryBobLock1None() {

        // Create clean environment
        $this->clean('SessionCookieAlice');

        // Alice lock 1 Form A
        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=1&form=lockAdvisory", RETURN_SIP);
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_LOCK;
Carsten  Rose's avatar
Carsten Rose committed
945
        $dirty = new Dirty();
946
947
948

        $result = $dirty->process();

Carsten  Rose's avatar
Carsten Rose committed
949
        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => '', API_LOCK_TIMEOUT => 900];
950
951
952
953
954
955
956
        $this->assertEquals($expected, $result);

        // Create clean environment
        $this->clean('SessionCookieBob');

        // Bob lock 1 Form B
        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=1&form=lockNone", RETURN_SIP);
Carsten  Rose's avatar
Carsten Rose committed
957
        $dirty = new Dirty();
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972

        $result = $dirty->process();

        $msg = 'The record has already';
        $expected = [API_STATUS => API_ANSWER_STATUS_CONFLICT_ALLOW_FORCE, API_MESSAGE => $msg];

        // cut IP, User and Timestamp
        $result[API_MESSAGE] = substr($result[API_MESSAGE], 0, strlen($msg));

        $this->assertEquals($expected, $result);
    }

    /**
     * Form A: advisory, Form B: Exclusive
     * Alice lock 1 Form A, Bob lock 1 Form B
Carsten  Rose's avatar
Carsten Rose committed
973
974
975
976
977
     *
     * @throws CodeException
     * @throws DbException
     * @throws UserFormException
     * @throws UserReportException
978
979
980
981
982
983
984
985
986
     */
    public function testAliceLock1AdvisoryBobLock1Exclusive() {

        // Create clean environment
        $this->clean('SessionCookieAlice');

        // Alice lock 1 Form A
        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=1&form=lockAdvisory", RETURN_SIP);
        $_GET[DIRTY_API_ACTION] = DIRTY_API_ACTION_LOCK;
Carsten  Rose's avatar
Carsten Rose committed
987
        $dirty = new Dirty();
988
989
990

        $result = $dirty->process();

Carsten  Rose's avatar
Carsten Rose committed
991
        $expected = [API_STATUS => API_ANSWER_STATUS_SUCCESS, API_MESSAGE => '', API_LOCK_TIMEOUT => 900];
992
993
994
995
996
997
998
        $this->assertEquals($expected, $result);

        // Create clean environment
        $this->clean('SessionCookieBob');

        // Bob lock 1 Form B
        $_GET[CLIENT_SIP] = $this->sip->queryStringToSip("?id=input&r=1&form=lockExclusive", RETURN_SIP);
Carsten  Rose's avatar
Carsten Rose committed
999
        $dirty = new Dirty();
1000