1<?php 2 3namespace dokuwiki\plugin\struct\test; 4 5use dokuwiki\plugin\struct\meta\AccessTablePage; 6use dokuwiki\plugin\struct\meta\ConfigParser; 7use dokuwiki\plugin\struct\meta\PageMeta; 8use dokuwiki\plugin\struct\test\mock\AccessTable as MockAccessTableAlias; 9use dokuwiki\plugin\struct\test\mock\AggregationEditorTable as MockAggregationEditorTableAlias; 10use dokuwiki\plugin\struct\test\mock\AggregationTable as MockAggregationTableAlias; 11use dokuwiki\plugin\struct\test\mock\SearchConfig as MockSearchConfigAlias; 12 13/** 14 * Testing serial data 15 * 16 * @group plugin_struct 17 * @group plugins 18 */ 19class AggregationResultsTest extends StructTest 20{ 21 protected $sqlite; 22 23 public function setUp(): void 24 { 25 parent::setUp(); 26 27 $sqlite = plugin_load('helper', 'struct_db'); 28 $this->sqlite = $sqlite->getDB(); 29 30 $this->loadSchemaJSON('schema1'); 31 32 $assignments = mock\Assignments::getInstance(); 33 $assignments->clear(true); 34 35 // different values for each entry 36 $second = [ 37 ['green', 'red'], 38 ['green', 'blue'], 39 ['blue', 'yellow'] 40 ]; 41 42 for ($i = 0; $i < 3; $i++) { 43 // assign a schema 44 $assignments->assignPageSchema("test$i", 'schema1'); 45 46 // save wiki pages 47 saveWikiText("test$i", "test$i", "test$i"); 48 49 // save serial data 50 $data = [ 51 'first' => "foo$i", 52 'second' => $second[$i], 53 'third' => "foobar$i", 54 'fourth' => "barfoo$i", 55 ]; 56 $accessSerial = MockAccessTableAlias::getSerialAccess('schema1', "test$i"); 57 $accessSerial->saveData($data); 58 $accessPage = MockAccessTableAlias::getPageAccess('schema1', "test$i"); 59 $accessPage->saveData($data); 60 } 61 } 62 63 /** 64 * Test whether serial syntax produces a table of serial data limited to current page 65 */ 66 public function test_pid() 67 { 68 // \syntax_plugin_struct_serial accesses the global $ID 69 $id = 'test1'; 70 $schema = 'schema1'; 71 $result = $this->fetchNonPageResults($schema, $id); 72 73 $this->assertCount(1, $result); 74 $this->assertEquals('test1', $result[0][0]->getValue()); 75 // skip %rowid% column and test saved values 76 $this->assertEquals('foo1', $result[0][2]->getValue()); 77 $this->assertEquals(['green', 'blue'], $result[0][3]->getValue()); 78 $this->assertEquals('foobar1', $result[0][4]->getValue()); 79 $this->assertEquals('barfoo1', $result[0][5]->getValue()); 80 } 81 82 /** 83 * Test simple text filter 84 */ 85 public function test_filter_text() 86 { 87 $schema = 'schema1'; 88 $result = $this->fetchNonPageResults($schema, 'test0'); 89 $this->assertCount(1, $result); 90 91 $result = $this->fetchNonPageResults($schema, 'test0', ['first', '=', 'foo0', 'AND']); 92 $this->assertCount(1, $result); 93 94 $result = $this->fetchNonPageResults($schema, 'test0', ['first', '!=', 'foo0', 'AND']); 95 $this->assertCount(0, $result); 96 } 97 98 /** @noinspection PhpUnreachableStatementInspection */ 99 public function test_filter_multi() 100 { 101 $schema = 'schema1'; 102 $result = $this->fetchAllResults($schema, ''); 103 $this->assertCount(6, $result); 104 105 $result = $this->fetchAllResults($schema, '', ['second', '=', 'green', 'AND']); 106 $this->assertCount(4, $result); 107 108 $this->markTestIncomplete('negative filters currently do not work on multi fields. See #512'); 109 110 $result = $this->fetchAllResults($schema, '', ['second', '!~', 'green', 'AND']); 111 $this->assertCount(2, $result); 112 } 113 114 /** 115 * Test filtering on a page field, with 'usetitles' set to true and false 116 */ 117 public function test_filter_page() 118 { 119 $this->prepareLookup(); 120 $schema = 'pageschema'; 121 $result = $this->fetchNonPageResults($schema); 122 $this->assertCount(3, $result); 123 124 // 'usetitles' = true 125 $result = $this->fetchNonPageResults($schema, '', ['singletitle', '*~', 'another', 'AND']); 126 $this->assertCount(1, $result); 127 128 // 'usetitles' = false 129 $result = $this->fetchNonPageResults($schema, '', ['singlepage', '*~', 'this', 'AND']); 130 $this->assertCount(0, $result); 131 } 132 133 /** 134 * Test filtering on a DateTime field 135 */ 136 public function test_filter_datetime() 137 { 138 $this->prepareDatetime(); 139 $schema = 'datetime'; 140 $result = $this->fetchNonPageResults($schema); 141 $this->assertCount(3, $result); 142 143 $result = $this->fetchNonPageResults($schema, '', ['field', '<', '2023-01-02', 'AND']); 144 $this->assertCount(1, $result); 145 146 $result = $this->fetchNonPageResults($schema, '', ['field', '<', '2023-01-01 11:00', 'AND']); 147 $this->assertCount(0, $result); 148 } 149 150 /** 151 * Test whether aggregation tables respect revoking of schema assignments 152 */ 153 public function test_assignments() 154 { 155 $result = $this->fetchAllResults('schema1'); 156 $this->assertCount(6, $result); 157 158 // revoke assignment 159 $assignments = mock\Assignments::getInstance(); 160 $assignments->deassignPageSchema('test0', 'schema1'); 161 162 $result = $this->fetchAllResults('schema1'); 163 $this->assertCount(5, $result); 164 } 165 166 167 /** 168 * Initialize a table from syntax and return the result from its internal search. 169 * 170 * @param string $schema 171 * @param string $id 172 * @param array $filters 173 * @return \dokuwiki\plugin\struct\meta\Value[][] 174 */ 175 protected function fetchAllResults($schema, $id = '', $filters = []) 176 { 177 $syntaxConfig = ['schema: ' . $schema, 'cols: %pageid%, %rowid%, *']; 178 $configParser = new ConfigParser($syntaxConfig); 179 $config = $configParser->getConfig(); 180 181 if ($filters) $config['filter'][] = $filters; 182 $search = new MockSearchConfigAlias($config); 183 184 $table = new MockAggregationTableAlias($id, 'xhtml', new \Doku_Renderer_xhtml(), $search); 185 return $table->getResult(); 186 } 187 188 /** 189 * Initialize a lookup table from syntax and return the result from its internal search. 190 * 191 * @param string $schema 192 * @param string $id 193 * @param array $filters 194 * @return \dokuwiki\plugin\struct\meta\Value[][] 195 */ 196 protected function fetchNonPageResults($schema, $id = '', $filters = []) 197 { 198 $syntaxConfig = ['schema: ' . $schema, 'cols: %pageid%, %rowid%, *']; 199 $configParser = new ConfigParser($syntaxConfig); 200 $config = $configParser->getConfig(); 201 202 // simulate addYypeFilter() from \syntax_plugin_struct_serial and \syntax_plugin_struct_lookup 203 if ($id) { 204 $config['filter'][] = ['%rowid%', '!=', (string)AccessTablePage::DEFAULT_PAGE_RID, 'AND']; 205 $config['filter'][] = ['%pageid%', '=', $id, 'AND']; 206 } else { 207 $config['filter'][] = ['%rowid%', '!=', (string)AccessTablePage::DEFAULT_PAGE_RID, 'AND']; 208 $config['filter'][] = ['%pageid%', '=*', '^(?![\s\S])', 'AND']; 209 } 210 211 if ($filters) $config['filter'][] = $filters; 212 $search = new MockSearchConfigAlias($config); 213 214 $table = new MockAggregationEditorTableAlias($id, 'xhtml', new \Doku_Renderer_xhtml(), $search); 215 return $table->getResult(); 216 } 217 218 protected function prepareLookup() 219 { 220 saveWikiText('title1', 'test', 'test'); 221 $pageMeta = new PageMeta('title1'); 222 $pageMeta->setTitle('This is a title'); 223 224 saveWikiText('title2', 'test', 'test'); 225 $pageMeta = new PageMeta('title2'); 226 $pageMeta->setTitle('This is a 2nd title'); 227 228 saveWikiText('title3', 'test', 'test'); 229 $pageMeta = new PageMeta('title3'); 230 $pageMeta->setTitle('Another Title'); 231 232 $this->loadSchemaJSON('pageschema'); 233 $access = MockAccessTableAlias::getGlobalAccess('pageschema'); 234 $access->saveData( 235 [ 236 'singlepage' => 'title1', 237 'multipage' => ['title1'], 238 'singletitle' => 'title1', 239 'multititle' => ['title1'], 240 ] 241 ); 242 $access = MockAccessTableAlias::getGlobalAccess('pageschema'); 243 $access->saveData( 244 [ 245 'singlepage' => 'title2', 246 'multipage' => ['title2'], 247 'singletitle' => 'title2', 248 'multititle' => ['title2'], 249 ] 250 ); 251 $access = MockAccessTableAlias::getGlobalAccess('pageschema'); 252 $access->saveData( 253 [ 254 'singlepage' => 'title3', 255 'multipage' => ['title3'], 256 'singletitle' => 'title3', 257 'multititle' => ['title3'], 258 ] 259 ); 260 } 261 262 protected function prepareDatetime() 263 { 264 $this->loadSchemaJSON('datetime'); 265 $access = MockAccessTableAlias::getGlobalAccess('datetime'); 266 $access->saveData(['field' => '2023-01-01 12:00']); 267 $access = MockAccessTableAlias::getGlobalAccess('datetime'); 268 $access->saveData(['field' => '2023-01-02 00:00']); 269 $access = MockAccessTableAlias::getGlobalAccess('datetime'); 270 $access->saveData(['field' => '2023-01-02 12:00']); 271 } 272} 273