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 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 \dokuwiki\Search\Index\Lock::releaseAll(); 27 } 28 29 /** 30 * Index a page so we have data to check 31 */ 32 protected function indexTestPage(): void 33 { 34 saveWikiText('integritytest', 'Hello world testing integrity check.', 'Test'); 35 $indexer = new Indexer(); 36 $indexer->addPage('integritytest'); 37 } 38 39 /** 40 * A healthy index should not throw 41 */ 42 public function testHealthyIndex() 43 { 44 $this->indexTestPage(); 45 46 $indexer = new Indexer(); 47 $indexer->checkIntegrity(); 48 $this->assertFalse($indexer->isIndexEmpty()); 49 } 50 51 /** 52 * An empty index should not throw 53 */ 54 public function testEmptyIndex() 55 { 56 $indexer = new Indexer(); 57 $indexer->checkIntegrity(); 58 $this->assertTrue($indexer->isIndexEmpty()); 59 } 60 61 /** 62 * Corrupted fulltext index (token/frequency mismatch) should throw 63 */ 64 public function testCorruptedFulltextTokenFrequency() 65 { 66 global $conf; 67 $this->indexTestPage(); 68 69 // Append an extra line to a token index to create a mismatch 70 $collection = new PageFulltextCollection(); 71 $max = $collection->getTokenIndexMaximum(); 72 $this->assertGreaterThan(0, $max); 73 74 $tokenFile = $conf['indexdir'] . '/w' . $max . '.idx'; 75 $this->assertFileExists($tokenFile); 76 file_put_contents($tokenFile, "corruptedentry\n", FILE_APPEND); 77 78 $this->expectException(IndexIntegrityException::class); 79 (new PageFulltextCollection())->checkIntegrity(); 80 } 81 82 /** 83 * Corrupted fulltext index (entity/reverse mismatch) should throw 84 */ 85 public function testCorruptedFulltextEntityReverse() 86 { 87 global $conf; 88 $this->indexTestPage(); 89 90 $reverseFile = $conf['indexdir'] . '/pageword.idx'; 91 $this->assertFileExists($reverseFile); 92 file_put_contents($reverseFile, "0\n", FILE_APPEND); 93 94 $this->expectException(IndexIntegrityException::class); 95 (new PageFulltextCollection())->checkIntegrity(); 96 } 97 98 /** 99 * Corrupted title index (entity/token mismatch) should throw 100 */ 101 public function testCorruptedTitleIndex() 102 { 103 global $conf; 104 $this->indexTestPage(); 105 106 $titleFile = $conf['indexdir'] . '/title.idx'; 107 $this->assertFileExists($titleFile); 108 file_put_contents($titleFile, "extra title\n", FILE_APPEND); 109 110 $this->expectException(IndexIntegrityException::class); 111 (new PageTitleCollection())->checkIntegrity(); 112 } 113 114 /** 115 * Indexer.checkIntegrity aggregates all collection checks 116 */ 117 public function testIndexerCheckIntegrityDetectsCorruption() 118 { 119 global $conf; 120 $this->indexTestPage(); 121 122 // Corrupt title index 123 $titleFile = $conf['indexdir'] . '/title.idx'; 124 file_put_contents($titleFile, "extra title\n", FILE_APPEND); 125 126 $this->expectException(IndexIntegrityException::class); 127 (new Indexer())->checkIntegrity(); 128 } 129} 130