xref: /dokuwiki/_test/tests/inc/common_saveWikiText.test.php (revision df60c5f923d134ccd435e5655ebd17d84c424870)
1<?php
2
3use dokuwiki\ChangeLog\PageChangeLog;
4
5/**
6 * saveWikiText() stores files in pages/, attic/ and adds entries to changelog
7 */
8class common_saveWikiText_test extends DokuWikiTest {
9
10    /** Delay writes of old revisions by a second. */
11    public function handle_write(Doku_Event $event, $param) {
12        if ($event->data[3] !== false) {
13            $this->waitForTick();
14        }
15    }
16
17    /**
18     * Execute a whole bunch of saves on the same page and check the results
19     * TEST 1
20     *  1.1 create a page
21     *  1.2 save with same content should be ignored
22     *  1.3 update the page with new text
23     *  1.4 add a minor edit (unauthenticated)
24     *  1.5 add a minor edit (authenticated)
25     *  1.6 delete
26     *  1.7 restore
27     *  1.8 external edit
28     *  1.9 save on top of external edit
29     */
30    function test_savesequence1() {
31        global $REV;
32
33        $page = 'page';
34        $file = wikiFN($page);
35
36        // 1.1 create a page
37        $this->assertFileNotExists($file);
38        saveWikiText($page, 'teststring', '1st save', false);
39        $this->assertFileExists($file);
40        $lastmod = filemtime($file);
41        $expect = array(
42            'date' => $lastmod,
43            'type' => DOKU_CHANGE_TYPE_CREATE,
44            'sum'  => '1st save',
45            'sizechange' => 10, // = strlen('teststring')
46        );
47
48        $pagelog = new PageChangeLog($page);
49        $revisions = $pagelog->getRevisions(-1, 200);
50        $this->assertCount(1, $revisions);
51        // last revision
52        $lastRevInfo = $pagelog->getRevisionInfo($revisions[0]);
53        $expect += $lastRevInfo;
54        $this->assertEquals($expect, $lastRevInfo);
55        // current revision
56        $currentRevInfo = $pagelog->getCurrentRevisionInfo();
57        $this->assertEquals($currentRevInfo, $lastRevInfo, 'current & last revs should be identical');
58        // attic
59        $this->assertFileExists(wikiFN($page, $lastRevInfo['date'], 'file missing in attic'));
60
61        $this->waitForTick(true); // wait for new revision ID
62
63        // 1.2 save with same content should be ignored
64        saveWikiText($page, 'teststring', '2nd save', false);
65        clearstatcache(false, $file);
66        $this->assertEquals($lastmod, filemtime($file));
67
68        $pagelog = new PageChangeLog($page);
69        $revisions = $pagelog->getRevisions(-1, 200);
70        $this->assertCount(1, $revisions);
71
72        // 1.3 update the page with new text
73        saveWikiText($page, 'teststring2long', '3rd save', false);
74        clearstatcache(false, $file);
75        $newmod = filemtime($file);
76        $this->assertNotEquals($lastmod, $newmod);
77        $lastmod = $newmod;
78        $expect = array(
79            'date' => $lastmod,
80            'type' => DOKU_CHANGE_TYPE_EDIT,
81            'sum'  => '3rd save',
82            'sizechange' => 5,
83        );
84
85        $pagelog = new PageChangeLog($page);
86        $revisions = $pagelog->getRevisions(-1, 200);
87        $this->assertCount(2, $revisions);
88        // last revision
89        $lastRevInfo = $pagelog->getRevisionInfo($revisions[0]);
90        $expect += $lastRevInfo;
91        $this->assertEquals($expect, $lastRevInfo);
92        // current revision
93        $currentRevInfo = $pagelog->getCurrentRevisionInfo();
94        $this->assertEquals($currentRevInfo, $lastRevInfo, 'current & last revs should be identical');
95        // attic
96        $this->assertFileExists(wikiFN($page, $lastRevInfo['date'], 'file missing in attic'));
97
98        $this->waitForTick(); // wait for new revision ID
99
100        // 1.4 add a minor edit (unauthenticated)
101        saveWikiText($page, 'teststring3long', '4th save', true);
102        clearstatcache(false, $file);
103        $newmod = filemtime($file);
104        $this->assertNotEquals($lastmod, $newmod);
105        $lastmod = $newmod;
106        $expect = array(
107            'date' => $lastmod,
108            'type' => DOKU_CHANGE_TYPE_EDIT,
109            'sum'  => '4th save',
110            'sizechange' => 0,
111        );
112
113        $pagelog = new PageChangeLog($page);
114        $revisions = $pagelog->getRevisions(-1, 200);
115        $this->assertCount(3, $revisions);
116        // last revision
117        $lastRevInfo = $pagelog->getRevisionInfo($revisions[0]);
118        $expect += $lastRevInfo;
119        $this->assertEquals($expect, $lastRevInfo);
120        // current revision
121        $currentRevInfo = $pagelog->getCurrentRevisionInfo();
122        $this->assertEquals($currentRevInfo, $lastRevInfo, 'current & last revs should be identical');
123        // attic
124        $this->assertFileExists(wikiFN($page, $lastRevInfo['date'], 'file missing in attic'));
125
126        $this->waitForTick(); // wait for new revision ID
127
128        // 1.5 add a minor edit (authenticated)
129        $_SERVER['REMOTE_USER'] = 'user';
130        saveWikiText($page, 'teststring4', '5th save', true);
131        clearstatcache(false, $file);
132        $newmod = filemtime($file);
133        $this->assertNotEquals($lastmod, $newmod);
134        $lastmod = $newmod;
135        $expect = array(
136            'date' => $lastmod,
137            'type' => DOKU_CHANGE_TYPE_MINOR_EDIT,
138            'sum'  => '5th save',
139            'sizechange' => -4,
140        );
141
142        $pagelog = new PageChangeLog($page);
143        $revisions = $pagelog->getRevisions(-1, 200);
144        $this->assertCount(4, $revisions);
145        // last revision
146        $lastRevInfo = $pagelog->getRevisionInfo($revisions[0]);
147        $expect += $lastRevInfo;
148        $this->assertEquals($expect, $lastRevInfo);
149        // current revision
150        $currentRevInfo = $pagelog->getCurrentRevisionInfo();
151        $this->assertEquals($currentRevInfo, $lastRevInfo, 'current & last revs should be identical');
152        // attic
153        $this->assertFileExists(wikiFN($page, $lastRevInfo['date'], 'file missing in attic'));
154
155        $this->waitForTick(); // wait for new revision ID
156
157        // 1.6 delete
158        saveWikiText($page, '', '6th save', false);
159        clearstatcache(false, $file);
160        $this->assertFileNotExists($file);
161        $expect = array(
162          //'date' => $lastmod, // ignore from lastRev assertion, but confirm attic file existence
163            'type' => DOKU_CHANGE_TYPE_DELETE,
164            'sum'  => '6th save',
165            'sizechange' => -11,
166        );
167
168        $pagelog = new PageChangeLog($page);
169        $revisions = $pagelog->getRevisions(-1, 200);
170        $this->assertCount(5, $revisions);
171        // last revision
172        $lastRevInfo = $pagelog->getRevisionInfo($revisions[0]);
173        $expect += $lastRevInfo;
174        $this->assertEquals($expect, $lastRevInfo);
175        // current revision
176        $currentRevInfo = $pagelog->getCurrentRevisionInfo();
177        $this->assertEquals($currentRevInfo, $lastRevInfo, 'current & last revs should be identical');
178        // attic
179        $this->assertFileExists(wikiFN($page, $lastRevInfo['date'], 'file missing in attic'));
180
181        $this->waitForTick(); // wait for new revision ID
182
183        // 1.7 restore
184        $REV = $lastmod;
185        saveWikiText($page, 'teststring4', '7th save', true);
186        clearstatcache(false, $file);
187        $this->assertFileExists($file);
188        $newmod = filemtime($file);
189        $this->assertNotEquals($lastmod, $newmod);
190        $lastmod = $newmod;
191        $expect = array(
192            'date' => $lastmod,
193            'type' => DOKU_CHANGE_TYPE_REVERT,
194            'sum'  => '7th save',
195            'sizechange' => 11,
196        );
197
198        $pagelog = new PageChangeLog($page);
199        $revisions = $pagelog->getRevisions(-1, 200);
200        $this->assertCount(6, $revisions);
201        // last revision
202        $lastRevInfo = $pagelog->getRevisionInfo($revisions[0]);
203        $expect += $lastRevInfo;
204        $this->assertEquals($expect, $lastRevInfo);
205        // current revision
206        $currentRevInfo = $pagelog->getCurrentRevisionInfo();
207        $this->assertEquals($currentRevInfo, $lastRevInfo, 'current & last revs should be identical');
208        // attic
209        $this->assertFileExists(wikiFN($page, $lastRevInfo['date']), 'file missing in attic');
210        $files = glob(dirname(wikiFN($page, $lastRevInfo['date'])).'/'.$page.'.*');
211        $this->assertCount(6, $files, 'detectExternalEdit() should not add too often old revs');
212        $REV = '';
213
214        $this->waitForTick(); // wait for new revision ID
215
216        // 1.8 external edit
217        file_put_contents($file, 'teststring5 external edit');
218        clearstatcache(false, $file);
219        $newmod = filemtime($file);
220        $this->assertNotEquals($lastmod, $newmod);
221        $lastmod = $newmod;
222        $expectExternal = array(
223            'date' => $lastmod,
224            'type' => DOKU_CHANGE_TYPE_EDIT,
225            'sum'  => 'external edit',
226            'sizechange' => 14,
227        );
228
229        $pagelog = new PageChangeLog($page);
230        $revisions = $pagelog->getRevisions(-1, 200);
231        $this->assertCount(6, $revisions); // external edit is not yet in changelog
232        // last revision
233        $lastRevInfo = $pagelog->getRevisionInfo($revisions[0]);
234        $this->assertEquals($expect, $lastRevInfo);
235        // current revision
236        $currentRevInfo = $pagelog->getCurrentRevisionInfo();
237        $this->assertArrayHasKey('timestamp', $currentRevInfo, 'should be external revision');
238        $expectExternal += $currentRevInfo;
239        $this->assertEquals($expectExternal, $currentRevInfo);
240        // attic
241        $this->assertFileExists(wikiFN($page, $lastRevInfo['date']), 'file missing in attic');
242        $this->assertFileNotExists(wikiFN($page, $currentRevInfo['date']),'page does not yet exist in attic');
243
244        $this->waitForTick(); // wait for new revision ID
245
246        // 1.9 save on top of external edit
247        saveWikiText($page, 'teststring6', '8th save', false);
248        clearstatcache(false, $file);
249        $newmod = filemtime($file);
250        $this->assertNotEquals($lastmod, $newmod);
251        $lastmod = $newmod;
252        $expect = array(
253            'date' => $lastmod,
254            'type' => DOKU_CHANGE_TYPE_EDIT,
255            'sum'  => '8th save',
256            'sizechange' => -14,
257        );
258
259        $pagelog = new PageChangeLog($page);
260        $revisions = $pagelog->getRevisions(-1, 200);
261        $this->assertCount(8, $revisions); // two more revisions now!
262        // last revision
263        $lastRevInfo = $pagelog->getRevisionInfo($revisions[0]);
264        $expect += $lastRevInfo;
265        $this->assertEquals($expect, $lastRevInfo);
266        // current revision
267        $currentRevInfo = $pagelog->getCurrentRevisionInfo();
268        $this->assertEquals($currentRevInfo, $lastRevInfo, 'current & last revs should be identical');
269        // attic
270        $this->assertFileExists(wikiFN($page, $lastRevInfo['date'], 'file missing in attic'));
271        $files = glob(dirname(wikiFN($page, $lastRevInfo['date'])).'/'.$page.'.*');
272        $this->assertCount(8, $files, 'detectExternalEdit() should not add too often old revs');
273
274    }
275
276    /**
277     * Execute a whole bunch of saves on the same page and check the results
278     * using $this->handle_write() in event IO_WIKIPAGE_WRITE
279     * TEST 2 - create a page externally, while external edit in Test 1
280     *  2.1 create a page
281     *  2.2 delete
282     *  2.3 externally create the page
283     *  2.4 save on top of external edit
284     */
285    function test_savesequence2() {
286        // add an additional delay when saving files to make sure
287        // nobody relies on the saving happening in the same second
288        /** @var $EVENT_HANDLER \dokuwiki\Extension\EventHandler */
289        global $EVENT_HANDLER;
290        $EVENT_HANDLER->register_hook('IO_WIKIPAGE_WRITE', 'BEFORE', $this, 'handle_write');
291
292        $page = 'page2';
293        $file = wikiFN($page);
294
295        // 2.1 create a page
296        $this->assertFileNotExists($file);
297        saveWikiText($page, 'teststring', 'Test 2, 1st save', false);
298        $this->assertFileExists($file);
299        $lastmod = filemtime($file);
300        $expect = array(
301            'date' => $lastmod,
302            'type' => DOKU_CHANGE_TYPE_CREATE,
303            'sum'  => 'Test 2, 1st save',
304            'sizechange' => 10, // = strlen('teststring')
305        );
306
307        $pagelog = new PageChangeLog($page);
308        $revisions = $pagelog->getRevisions(-1, 200);
309        $this->assertCount(1, $revisions);
310        // last revision
311        $lastRevInfo = $pagelog->getRevisionInfo($revisions[0]);
312        $expect += $lastRevInfo;
313        $this->assertEquals($expect, $lastRevInfo);
314        // current revision
315        $currentRevInfo = $pagelog->getCurrentRevisionInfo();
316        $this->assertEquals($currentRevInfo, $lastRevInfo, 'current & last revs should be identical');
317        // attic
318        $this->assertFileExists(wikiFN($page, $lastRevInfo['date'], 'file missing in attic'));
319
320        $this->waitForTick(true); // wait for new revision ID
321
322        // 2.2 delete
323        saveWikiText($page, '', 'Test 2, 2nd save', false);
324        clearstatcache(false, $file);
325        $this->assertFileNotExists($file);
326        $expect = array(
327          //'date' => $lastmod, // ignore from lastRev assertion, but confirm attic file existence
328            'type' => DOKU_CHANGE_TYPE_DELETE,
329            'sum'  => 'Test 2, 2nd save',
330            'sizechange' => -10,
331        );
332
333        $pagelog = new PageChangeLog($page);
334        $revisions = $pagelog->getRevisions(-1, 200);
335        $this->assertCount(2, $revisions);
336        // last revision
337        $lastRevInfo = $pagelog->getRevisionInfo($revisions[0]);
338        $expect += $lastRevInfo;
339        $this->assertEquals($expect, $lastRevInfo);
340        // current revision
341        $currentRevInfo = $pagelog->getCurrentRevisionInfo();
342        $this->assertEquals($currentRevInfo, $lastRevInfo, 'current & last revs should be identical');
343        // attic
344        $this->assertFileExists(wikiFN($page, $lastRevInfo['date'], 'file missing in attic'));
345
346        $this->waitForTick(); // wait for new revision ID
347
348        // 2.3 externally create the page
349        file_put_contents($file, 'teststring5');
350        clearstatcache(false, $file);
351        $lastmod = filemtime($file);
352        $expectExternal = array(
353            'date' => $lastmod,
354            'type' => DOKU_CHANGE_TYPE_CREATE,
355            'sum'  => 'created - external edit',
356            'sizechange' => 11,
357        );
358
359        $pagelog = new PageChangeLog($page);
360        $revisions = $pagelog->getRevisions(-1, 200);
361        $this->assertCount(2, $revisions); // external edit is not yet in changelog
362        // last revision
363        $lastRevInfo = $pagelog->getRevisionInfo($revisions[0]);
364        $this->assertEquals($expect, $lastRevInfo);
365        // current revision
366        $currentRevInfo = $pagelog->getCurrentRevisionInfo();
367        $this->assertArrayHasKey('timestamp', $currentRevInfo, 'should be external revision');
368        $expectExternal += $currentRevInfo;
369        $this->assertEquals($expectExternal, $currentRevInfo);
370        // attic
371        $this->assertFileExists(wikiFN($page, $lastRevInfo['date']), 'file missing in attic');
372        $this->assertFileNotExists(wikiFN($page, $currentRevInfo['date']),'page does not yet exist in attic');
373
374        $this->waitForTick(); // wait for new revision ID
375
376        // 2.4 save on top of external edit
377        saveWikiText($page, 'teststring6', 'Test 2, 3rd save', false);
378        clearstatcache(false, $file);
379        $newmod = filemtime($file);
380        $this->assertNotEquals($lastmod, $newmod);
381        $lastmod = $newmod;
382        $expect = array(
383            'date' => $lastmod,
384            'type' => DOKU_CHANGE_TYPE_EDIT,
385            'sum'  => 'Test 2, 3rd save',
386            'sizechange' => 0,
387        );
388
389        $pagelog = new PageChangeLog($page);
390        $revisions = $pagelog->getRevisions(-1, 200);
391        $this->assertCount(4, $revisions); // two more revisions now!
392        // last revision
393        $lastRevInfo = $pagelog->getRevisionInfo($revisions[0]);
394        $expect += $lastRevInfo;
395        $this->assertEquals($expect, $lastRevInfo);
396        // current revision
397        $currentRevInfo = $pagelog->getCurrentRevisionInfo();
398        $this->assertEquals($currentRevInfo, $lastRevInfo, 'current & last revs should be identical');
399        // attic
400        $this->assertFileExists(wikiFN($page, $lastRevInfo['date'], 'file missing in attic'));
401        $files = glob(dirname(wikiFN($page, $lastRevInfo['date'])).'/'.$page.'.*');
402        $this->assertCount(4, $files, 'detectExternalEdit() should not add too often old revs');
403
404    }
405
406    /**
407     * Execute a whole bunch of saves on the same page and check the results
408     * TEST 3 - typical page life of bundled page such as wiki/syntax
409     *  3.1 externally create a page
410     *  3.2 external edit
411     *  3.3 save on top of external edit
412     *  3.4 externally delete the page
413     */
414    function test_savesequence3() {
415        $page = 'page3';
416        $file = wikiFN($page);
417
418        // 3.1 externally create a page
419        $this->assertFileNotExists($file);
420        file_put_contents($file, 'teststring');
421        clearstatcache(false, $file);
422        $lastmod = filemtime($file);
423        $expectExternal = array(
424            'date' => $lastmod,
425            'type' => DOKU_CHANGE_TYPE_CREATE,
426            'sum'  => 'created - external edit',
427            'sizechange' => 10,
428        );
429
430        $pagelog = new PageChangeLog($page);
431        $revisions = $pagelog->getRevisions(-1, 200);
432        $this->assertCount(0, $revisions); // external edit is not yet in changelog
433        // last revision
434        $this->assertFalse($pagelog->lastRevision(), 'changelog file does not yet exist');
435        // current revision
436        $currentRevInfo = $pagelog->getCurrentRevisionInfo();
437        $this->assertArrayHasKey('timestamp', $currentRevInfo, 'should be external revision');
438        $expectExternal += $currentRevInfo;
439        $this->assertEquals($expectExternal, $currentRevInfo);
440        // attic
441        $this->assertFileNotExists(wikiFN($page, $currentRevInfo['date']),'page does not yet exist in attic');
442
443        $this->waitForTick(true); // wait for new revision ID
444
445        // 3.2 external edit
446        file_put_contents($file, 'teststring external edit');
447        clearstatcache(false, $file);
448        $newmod = filemtime($file);
449        $this->assertNotEquals($lastmod, $newmod);
450        $lastmod = $newmod;
451        $expectExternal = array(
452            'date' => $lastmod,
453            'type' => DOKU_CHANGE_TYPE_CREATE,
454            'sum'  => 'created - external edit',
455            'sizechange' => 24,
456        );
457
458        $pagelog = new PageChangeLog($page);
459        $revisions = $pagelog->getRevisions(-1, 200);
460        $this->assertCount(0, $revisions); // external edit is not yet in changelog
461        // last revision
462        $this->assertFalse($pagelog->lastRevision(), 'changelog file does not yet exist');
463        // current revision
464        $currentRevInfo = $pagelog->getCurrentRevisionInfo();
465        $this->assertArrayHasKey('timestamp', $currentRevInfo, 'should be external revision');
466        $expectExternal += $currentRevInfo;
467        $this->assertEquals($expectExternal, $currentRevInfo);
468        // attic
469        $this->assertFileNotExists(wikiFN($page, $currentRevInfo['date']),'page does not yet exist in attic');
470
471        $this->waitForTick(true); // wait for new revision ID
472
473        // 3.3 save on top of external edit
474        saveWikiText($page, 'teststring1', 'Test 3, first save', false);
475        clearstatcache(false, $file);
476        $newmod = filemtime($file);
477        $this->assertNotEquals($lastmod, $newmod);
478        $lastmod = $newmod;
479        $expect = array(
480            'date' => $lastmod,
481            'type' => DOKU_CHANGE_TYPE_EDIT,
482            'sum'  => 'Test 3, first save',
483            'sizechange' => -13,
484        );
485
486        $pagelog = new PageChangeLog($page);
487        $revisions = $pagelog->getRevisions(-1, 200);
488        $this->assertCount(2, $revisions); // two more revisions now!
489        // last revision
490        $lastRevInfo = $pagelog->getRevisionInfo($revisions[0]);
491        $expect += $lastRevInfo;
492        $this->assertEquals($expect, $lastRevInfo);
493        // current revision
494        $currentRevInfo = $pagelog->getCurrentRevisionInfo();
495        $this->assertEquals($currentRevInfo, $lastRevInfo, 'current & last revs should be identical');
496        // attic
497        $this->assertFileExists(wikiFN($page, $lastRevInfo['date'], 'file missing in attic'));
498        $files = glob(dirname(wikiFN($page, $lastRevInfo['date'])).'/'.$page.'.*');
499        $this->assertCount(2, $files, 'detectExternalEdit() should not add too often old revs');
500
501        $this->waitForTick(true); // wait for new revision ID
502
503        // 3.4 externally delete the page
504        unlink($file);
505        clearstatcache(false, $file);
506        $expectExternal = array(
507          //'date' => $lastmod,
508            'type' => DOKU_CHANGE_TYPE_DELETE,
509            'sum'  => 'removed - external edit (Unknown date)',
510            'sizechange' => -11,
511        );
512
513        $pagelog = new PageChangeLog($page);
514        $revisions = $pagelog->getRevisions(-1, 200);
515        $this->assertCount(2, $revisions); // two more revisions now!
516        // last revision
517        $lastRevInfo = $pagelog->getRevisionInfo($revisions[0]);
518        $this->assertEquals($expect, $lastRevInfo);
519        // current revision
520        $currentRevInfo = $pagelog->getCurrentRevisionInfo();
521        $this->assertArrayHasKey('timestamp', $currentRevInfo, 'should be external revision');
522        $expectExternal += $currentRevInfo;
523        $this->assertEquals($expectExternal, $currentRevInfo);
524        // attic
525        $this->assertFileNotExists(wikiFN($page, $currentRevInfo['date']),'page does not yet exist in attic');
526
527    }
528
529}
530