1<?php 2 3namespace dokuwiki\test\Search\Collection; 4 5use dokuwiki\Search\Collection\PageTitleCollection; 6use dokuwiki\Search\Exception\IndexIntegrityException; 7use dokuwiki\Search\Exception\IndexLockException; 8use dokuwiki\Search\Index\MemoryIndex; 9 10class DirectCollectionTest extends \DokuWikiTest 11{ 12 /** 13 * Add a token and verify it's stored at the entity's position 14 */ 15 public function testAddEntity() 16 { 17 $index = new MockDirectCollection('a_entity', 'a_token'); 18 $index->lock(); 19 $index->addEntity('wiki:start', ['Welcome to DokuWiki']); 20 $index->unlock(); 21 22 $idxEntity = new MemoryIndex('a_entity'); 23 $this->assertEquals('wiki:start', $idxEntity->retrieveRow(0)); 24 25 $idxToken = new MemoryIndex('a_token'); 26 $this->assertEquals('Welcome to DokuWiki', $idxToken->retrieveRow(0)); 27 } 28 29 /** 30 * Updating an entity should overwrite the previous token 31 */ 32 public function testUpdateEntity() 33 { 34 $index = new MockDirectCollection('b_entity', 'b_token'); 35 36 $index->lock(); 37 $index->addEntity('wiki:start', ['Old Title']); 38 $index->unlock(); 39 40 $index->lock(); 41 $index->addEntity('wiki:start', ['New Title']); 42 $index->unlock(); 43 44 $idxToken = new MemoryIndex('b_token'); 45 $this->assertEquals('New Title', $idxToken->retrieveRow(0)); 46 } 47 48 /** 49 * Empty token list should store empty string 50 */ 51 public function testEmptyToken() 52 { 53 $index = new MockDirectCollection('c_entity', 'c_token'); 54 $index->lock(); 55 $index->addEntity('wiki:start', []); 56 $index->unlock(); 57 58 $idxToken = new MemoryIndex('c_token'); 59 $this->assertEquals('', $idxToken->retrieveRow(0)); 60 } 61 62 /** 63 * getToken should return the stored value 64 */ 65 public function testGetToken() 66 { 67 $index = new MockDirectCollection('d_entity', 'd_token'); 68 $index->lock(); 69 $index->addEntity('wiki:start', ['My Page Title']); 70 $index->unlock(); 71 72 $this->assertEquals('My Page Title', $index->getToken('wiki:start')); 73 } 74 75 /** 76 * Adding entity without lock should throw exception 77 */ 78 public function testAddEntityWithoutLock() 79 { 80 $this->expectException(IndexLockException::class); 81 82 $index = new MockDirectCollection(); 83 $index->addEntity('wiki:start', ['Title']); 84 } 85 86 /** 87 * Test that PageTitleCollection uses correct index names 88 */ 89 public function testPageTitleCollection() 90 { 91 $index = new PageTitleCollection(); 92 $index->lock(); 93 $index->addEntity('wiki:start', ['Welcome']); 94 $index->unlock(); 95 96 $idxToken = new MemoryIndex('title'); 97 $this->assertEquals('Welcome', $idxToken->retrieveRow(0)); 98 99 $this->assertEquals('Welcome', $index->getToken('wiki:start')); 100 } 101 102 /** 103 * resolveTokenFrequencies maps token RID = entity RID with frequency 1 104 */ 105 public function testResolveTokenFrequencies() 106 { 107 $index = new MockDirectCollection('rtf_entity', 'rtf_token'); 108 $index->lock(); 109 $index->addEntity('wiki:start', ['Title One']); 110 $index->addEntity('wiki:syntax', ['Title Two']); 111 $index->unlock(); 112 113 $result = $index->resolveTokenFrequencies(0, [0, 1]); 114 $this->assertEquals([ 115 0 => [0 => 1], 116 1 => [1 => 1], 117 ], $result); 118 } 119 120 /** 121 * getEntitiesWithData returns entities that have non-empty tokens 122 */ 123 public function testGetEntitiesWithData() 124 { 125 $index = new MockDirectCollection('ewd_entity', 'ewd_token'); 126 $index->lock(); 127 $index->addEntity('wiki:start', ['Title One']); 128 $index->addEntity('wiki:empty', []); 129 $index->addEntity('wiki:syntax', ['Title Two']); 130 $index->unlock(); 131 132 $result = $index->getEntitiesWithData(); 133 sort($result); 134 $this->assertEquals(['wiki:start', 'wiki:syntax'], $result); 135 } 136 137 /** 138 * checkIntegrity passes on a healthy DirectCollection 139 */ 140 public function testCheckIntegrityHealthy() 141 { 142 $index = new MockDirectCollection('cih_entity', 'cih_token'); 143 $index->lock(); 144 $index->addEntity('wiki:start', ['Title One']); 145 $index->unlock(); 146 147 $index->checkIntegrity(); // should not throw 148 $this->assertTrue(true); 149 } 150 151 /** 152 * checkIntegrity passes on an empty DirectCollection 153 */ 154 public function testCheckIntegrityEmpty() 155 { 156 $index = new MockDirectCollection('cie_entity', 'cie_token'); 157 $index->checkIntegrity(); // should not throw 158 $this->assertTrue(true); 159 } 160 161 /** 162 * checkIntegrity detects entity/token line count mismatch 163 */ 164 public function testCheckIntegrityMismatch() 165 { 166 global $conf; 167 $index = new MockDirectCollection('cim_entity', 'cim_token'); 168 $index->lock(); 169 $index->addEntity('wiki:start', ['Title One']); 170 $index->unlock(); 171 172 // corrupt: add extra line to token index 173 file_put_contents($conf['indexdir'] . '/cim_token.idx', "extra\n", FILE_APPEND); 174 175 $this->expectException(IndexIntegrityException::class); 176 (new MockDirectCollection('cim_entity', 'cim_token'))->checkIntegrity(); 177 } 178} 179