xref: /dokuwiki/_test/tests/Search/Collection/LookupCollectionTest.php (revision ede4646658cf51245060332d97a319a39c788ea1)
1<?php
2
3namespace dokuwiki\test\Search\Collection;
4
5use dokuwiki\Search\Collection\PageMetaCollection;
6use dokuwiki\Search\Exception\IndexLockException;
7use dokuwiki\Search\Index\MemoryIndex;
8
9class LookupCollectionTest extends \DokuWikiTest
10{
11    /**
12     * Add data and directly check the underlying indexes for correctness
13     */
14    public function testAddEntity()
15    {
16        $index = new MockLookupCollection('a_entity', 'a_token', 'a_freq', 'a_reverse');
17        $index->lock();
18        $index->addEntity('wiki:start', ['wiki:logo.png', 'wiki:banner.jpg', 'wiki:icon.svg']);
19        $index->unlock();
20
21        // check entity index
22        $idxEntity = new MemoryIndex('a_entity');
23        $this->assertEquals('wiki:start', $idxEntity->retrieveRow(0));
24
25        // check token index (single file, no suffix)
26        $idxToken = new MemoryIndex('a_token');
27        $this->assertEquals('wiki:logo.png', $idxToken->retrieveRow(0));
28        $this->assertEquals('wiki:banner.jpg', $idxToken->retrieveRow(1));
29        $this->assertEquals('wiki:icon.svg', $idxToken->retrieveRow(2));
30
31        // check frequency index — all frequencies are 1 (written without *1)
32        $idxFreq = new MemoryIndex('a_freq');
33        $this->assertEquals('0', $idxFreq->retrieveRow(0)); // entity 0 with implicit freq 1
34        $this->assertEquals('0', $idxFreq->retrieveRow(1));
35        $this->assertEquals('0', $idxFreq->retrieveRow(2));
36
37        // check reverse index
38        $idxRev = new MemoryIndex('a_reverse');
39        $this->assertEquals('0:1:2', $idxRev->retrieveRow(0));
40    }
41
42    /**
43     * Duplicate tokens should be deduplicated
44     */
45    public function testAddEntityDedup()
46    {
47        $index = new MockLookupCollection('b_entity', 'b_token', 'b_freq', 'b_reverse');
48        $index->lock();
49        $index->addEntity('wiki:start', ['wiki:logo.png', 'wiki:logo.png', 'wiki:banner.jpg']);
50        $index->unlock();
51
52        $idxToken = new MemoryIndex('b_token');
53        $this->assertEquals('wiki:logo.png', $idxToken->retrieveRow(0));
54        $this->assertEquals('wiki:banner.jpg', $idxToken->retrieveRow(1));
55
56        $idxRev = new MemoryIndex('b_reverse');
57        $this->assertEquals('0:1', $idxRev->retrieveRow(0));
58    }
59
60    /**
61     * Updating an entity should remove old tokens and add new ones
62     */
63    public function testUpdateEntity()
64    {
65        $index = new MockLookupCollection('c_entity', 'c_token', 'c_freq', 'c_reverse');
66
67        // initial add
68        $index->lock();
69        $index->addEntity('wiki:start', ['wiki:logo.png', 'wiki:banner.jpg']);
70        $index->unlock();
71
72        // update: remove logo, keep banner, add icon
73        $index->lock();
74        $index->addEntity('wiki:start', ['wiki:banner.jpg', 'wiki:icon.svg']);
75        $index->unlock();
76
77        // logo should be removed from frequency index
78        $idxFreq = new MemoryIndex('c_freq');
79        $this->assertEquals('', $idxFreq->retrieveRow(0)); // logo removed
80        $this->assertEquals('0', $idxFreq->retrieveRow(1)); // banner still on entity 0
81        $this->assertEquals('0', $idxFreq->retrieveRow(2)); // icon added on entity 0
82
83        // reverse index should only have banner and icon
84        $idxRev = new MemoryIndex('c_reverse');
85        $this->assertEquals('1:2', $idxRev->retrieveRow(0));
86    }
87
88    /**
89     * Test reverse assignments returns two-level structure with empty group key
90     */
91    public function testReverseAssignments()
92    {
93        $index = new MockLookupCollection('d_entity', 'd_token', 'd_freq', 'd_reverse');
94        $index->lock();
95        $index->addEntity('wiki:start', ['wiki:logo.png', 'wiki:banner.jpg']);
96        $index->unlock();
97
98        $result = $index->getReverseAssignments('wiki:start');
99        $this->assertEquals(['' => [0 => 0, 1 => 0]], $result);
100    }
101
102    /**
103     * Adding entity without lock should throw exception
104     */
105    public function testAddEntityWithoutLock()
106    {
107        $this->expectException(IndexLockException::class);
108
109        $index = new MockLookupCollection();
110        $index->addEntity('wiki:start', ['wiki:logo.png']);
111    }
112
113    /**
114     * Adding empty token list should clear entity from indexes
115     */
116    public function testEmptyTokens()
117    {
118        $index = new MockLookupCollection('f_entity', 'f_token', 'f_freq', 'f_reverse');
119
120        // add some tokens first
121        $index->lock();
122        $index->addEntity('wiki:start', ['wiki:logo.png']);
123        $index->unlock();
124
125        // now clear
126        $index->lock();
127        $index->addEntity('wiki:start', []);
128        $index->unlock();
129
130        // frequency index should be empty for this token
131        $idxFreq = new MemoryIndex('f_freq');
132        $this->assertEquals('', $idxFreq->retrieveRow(0));
133
134        // reverse index should be empty
135        $idxRev = new MemoryIndex('f_reverse');
136        $this->assertEquals('', $idxRev->retrieveRow(0));
137    }
138
139    /**
140     * Test that PageMetaCollection('relation_media') uses correct index names
141     */
142    public function testMediaCollection()
143    {
144        $index = new PageMetaCollection('relation_media');
145        $index->lock();
146        $index->addEntity('wiki:start', ['wiki:logo.png', 'wiki:banner.jpg']);
147        $index->unlock();
148
149        $idxToken = new MemoryIndex('relation_media_w');
150        $this->assertEquals('wiki:logo.png', $idxToken->retrieveRow(0));
151        $this->assertEquals('wiki:banner.jpg', $idxToken->retrieveRow(1));
152
153        $idxRev = new MemoryIndex('relation_media_p');
154        $this->assertEquals('0:1', $idxRev->retrieveRow(0));
155    }
156
157    /**
158     * Test that PageMetaCollection('relation_references') uses correct index names
159     */
160    public function testReferencesCollection()
161    {
162        $index = new PageMetaCollection('relation_references');
163        $index->lock();
164        $index->addEntity('wiki:start', ['wiki:syntax', 'wiki:welcome']);
165        $index->unlock();
166
167        $idxToken = new MemoryIndex('relation_references_w');
168        $this->assertEquals('wiki:syntax', $idxToken->retrieveRow(0));
169        $this->assertEquals('wiki:welcome', $idxToken->retrieveRow(1));
170
171        $idxRev = new MemoryIndex('relation_references_p');
172        $this->assertEquals('0:1', $idxRev->retrieveRow(0));
173
174        $result = $index->getReverseAssignments('wiki:start');
175        $this->assertEquals(['' => [0 => 0, 1 => 0]], $result);
176    }
177
178    /**
179     * resolveTokens should deduplicate and assign frequency 1 under empty group key
180     */
181    public function testResolveTokens()
182    {
183        $index = new MockLookupCollection('rt_entity', 'rt_token', 'rt_freq', 'rt_reverse');
184        $index->lock();
185
186        $result = $this->callInaccessibleMethod($index, 'resolveTokens', [
187            ['wiki:logo.png', 'wiki:banner.jpg', 'wiki:logo.png'],
188        ]);
189
190        // all tokens under empty group key
191        $this->assertArrayHasKey('', $result);
192        $this->assertCount(2, $result['']); // deduplicated
193
194        // token IDs are sequential: logo=0, banner=1
195        $this->assertEquals(1, $result[''][0]); // logo freq=1
196        $this->assertEquals(1, $result[''][1]); // banner freq=1
197    }
198
199    /**
200     * resolveTokens with empty input should return empty array
201     */
202    public function testResolveTokensEmpty()
203    {
204        $index = new MockLookupCollection('rte_entity', 'rte_token', 'rte_freq', 'rte_reverse');
205        $index->lock();
206
207        $result = $this->callInaccessibleMethod($index, 'resolveTokens', [[]]);
208
209        $this->assertEmpty($result);
210    }
211
212    /**
213     * countTokens should deduplicate and assign frequency 1
214     */
215    public function testCountTokens()
216    {
217        $index = new MockLookupCollection();
218
219        $result = $this->callInaccessibleMethod($index, 'countTokens', [
220            ['wiki:logo.png', 'wiki:banner.jpg', 'wiki:logo.png'],
221        ]);
222
223        $this->assertEquals([
224            'wiki:logo.png' => 1,
225            'wiki:banner.jpg' => 1,
226        ], $result);
227    }
228}
229