xref: /dokuwiki/_test/tests/Search/IntegrityTest.php (revision b188a75bce0b8a807acf7eb725e2e5c6239fa50d)
1<?php
2
3namespace dokuwiki\test\Search;
4
5use dokuwiki\Search\Collection\PageFulltextCollection;
6use dokuwiki\Search\Collection\PageTitleCollection;
7use dokuwiki\Search\Exception\IndexIntegrityException;
8use dokuwiki\Search\Indexer;
9
10/**
11 * Tests the index integrity checking
12 */
13class IntegrityTest extends \DokuWikiTest
14{
15    /**
16     * Clear the index directory and indexing metadata before each test
17     */
18    public function setUp(): void
19    {
20        parent::setUp();
21        global $conf;
22        $files = glob($conf['indexdir'] . '/*.idx');
23        foreach ($files as $file) {
24            @unlink($file);
25        }
26        // remove the .indexed tag so needsIndexing() won't skip re-indexing
27        @unlink(metaFN('integritytest', '.indexed'));
28        \dokuwiki\Search\Index\Lock::releaseAll();
29    }
30
31    /**
32     * Index a page so we have data to check
33     */
34    protected function indexTestPage(): void
35    {
36        saveWikiText('integritytest', 'Hello world testing integrity check.', 'Test');
37        $indexer = new Indexer();
38        $indexer->addPage('integritytest');
39    }
40
41    /**
42     * A healthy index should not throw
43     */
44    public function testHealthyIndex()
45    {
46        $this->indexTestPage();
47
48        $indexer = new Indexer();
49        $indexer->checkIntegrity();
50        $this->assertFalse($indexer->isIndexEmpty());
51    }
52
53    /**
54     * An empty index should not throw
55     */
56    public function testEmptyIndex()
57    {
58        $indexer = new Indexer();
59        $indexer->checkIntegrity();
60        $this->assertTrue($indexer->isIndexEmpty());
61    }
62
63    /**
64     * Corrupted fulltext index (token/frequency mismatch) should throw
65     */
66    public function testCorruptedFulltextTokenFrequency()
67    {
68        global $conf;
69        $this->indexTestPage();
70
71        // Append an extra line to a token index to create a mismatch
72        $collection = new PageFulltextCollection();
73        $max = $collection->getTokenIndexMaximum();
74        $this->assertGreaterThan(0, $max);
75
76        $tokenFile = $conf['indexdir'] . '/w' . $max . '.idx';
77        $this->assertFileExists($tokenFile);
78        file_put_contents($tokenFile, "corruptedentry\n", FILE_APPEND);
79
80        $this->expectException(IndexIntegrityException::class);
81        (new PageFulltextCollection())->checkIntegrity();
82    }
83
84    /**
85     * Corrupted fulltext index (entity/reverse mismatch) should throw
86     */
87    public function testCorruptedFulltextEntityReverse()
88    {
89        global $conf;
90        $this->indexTestPage();
91
92        $reverseFile = $conf['indexdir'] . '/pageword.idx';
93        $this->assertFileExists($reverseFile);
94        file_put_contents($reverseFile, "0\n", FILE_APPEND);
95
96        $this->expectException(IndexIntegrityException::class);
97        (new PageFulltextCollection())->checkIntegrity();
98    }
99
100    /**
101     * Corrupted title index (entity/token mismatch) should throw
102     */
103    public function testCorruptedTitleIndex()
104    {
105        global $conf;
106        $this->indexTestPage();
107
108        $titleFile = $conf['indexdir'] . '/title.idx';
109        $this->assertFileExists($titleFile);
110        file_put_contents($titleFile, "extra title\n", FILE_APPEND);
111
112        $this->expectException(IndexIntegrityException::class);
113        (new PageTitleCollection())->checkIntegrity();
114    }
115
116    /**
117     * Indexer.checkIntegrity aggregates all collection checks
118     */
119    public function testIndexerCheckIntegrityDetectsCorruption()
120    {
121        global $conf;
122        $this->indexTestPage();
123
124        // Corrupt title index
125        $titleFile = $conf['indexdir'] . '/title.idx';
126        file_put_contents($titleFile, "extra title\n", FILE_APPEND);
127
128        $this->expectException(IndexIntegrityException::class);
129        (new Indexer())->checkIntegrity();
130    }
131}
132