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