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