Skip to content

Commit f93edb4

Browse files
authored
Merge pull request #2019 from AIlkiv/chore/add-unittest-for-view-filters
chore: Added phpunit for the filtering function in Row2Mapper::findAll
2 parents 74fc165 + 9921f8f commit f93edb4

3 files changed

Lines changed: 174 additions & 13 deletions

File tree

‎tests/unit/Database/DatabaseTestCase.php‎

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,12 @@ protected function createTestColumn(int $tableId, array $data = []) {
191191
'mandatory' => false,
192192
'order_weight' => 0,
193193
'number_prefix' => '',
194-
'number_suffix' => ''
194+
'number_suffix' => '',
195+
'text_default' => '',
196+
'number_default' => null,
197+
'datetime_default' => '',
198+
'selection_default' => '',
199+
'usergroup_default' => '',
195200
];
196201

197202
$testIdent = $data['test_ident'] ?? null;
@@ -316,27 +321,32 @@ protected function addCellsToRow(int $rowId, array $cellsData, array $columnMapp
316321
*/
317322
protected function insertCellData(int $rowId, int $columnId, $value): void {
318323
$qb = $this->connection->getQueryBuilder();
319-
$result = $qb->select('type')
324+
$result = $qb->select('type', 'subtype')
320325
->from('tables_columns')
321326
->where($qb->expr()->eq('id', $qb->createNamedParameter($columnId)))
322327
->executeQuery();
323328

324-
$columnType = $result->fetchOne();
329+
$column = $result->fetch();
325330
$result->closeCursor();
326331

327-
if (!$columnType) {
332+
if (!$column) {
328333
throw new \InvalidArgumentException("Column with ID $columnId not found");
329334
}
330335

331-
$this->insertCellIntoTypeTable($rowId, $columnId, $value, $columnType);
336+
$this->insertCellIntoTypeTable($rowId, $columnId, $value, $column['type'], $column['subtype']);
332337
}
333338

334339
/**
335340
* Inserts cell data into the appropriate type-specific table
336341
*/
337-
protected function insertCellIntoTypeTable(int $rowId, int $columnId, $value, string $columnType): void {
342+
protected function insertCellIntoTypeTable(int $rowId, int $columnId, $value, string $columnType, string $columnSubtype): void {
338343
$tableName = 'tables_row_cells_' . $columnType;
339344

345+
// Handle selection type - convert values to IDs based on selection_options
346+
if ($columnType === 'selection' && $columnSubtype !== 'check') {
347+
$value = $this->convertSelectionValuesToIds($columnId, $value);
348+
}
349+
340350
$qb = $this->connection->getQueryBuilder();
341351
$qb->insert($tableName)
342352
->setValue('row_id', $qb->createNamedParameter($rowId))
@@ -401,6 +411,49 @@ protected function extractTestIdentMapping(array $results): array {
401411
return $mapping;
402412
}
403413

414+
/**
415+
* Converts selection values to IDs based on selection_options
416+
*/
417+
protected function convertSelectionValuesToIds(int $columnId, $value) {
418+
// Get column configuration to find selection_options
419+
$qb = $this->connection->getQueryBuilder();
420+
$result = $qb->select('selection_options')
421+
->from('tables_columns')
422+
->where($qb->expr()->eq('id', $qb->createNamedParameter($columnId)))
423+
->executeQuery();
424+
425+
$selectionOptions = $result->fetchOne();
426+
$result->closeCursor();
427+
428+
if (!$selectionOptions) {
429+
throw new \InvalidArgumentException("Column with ID $columnId not found");
430+
}
431+
432+
$selectionOptions = json_decode($selectionOptions, true);
433+
434+
// Create mapping from label to id
435+
$optionMapping = [];
436+
foreach ($selectionOptions as $option) {
437+
if (isset($option['label']) && isset($option['id'])) {
438+
$optionMapping[$option['label']] = $option['id'];
439+
}
440+
}
441+
442+
// Convert single value or array of values
443+
if (is_array($value)) {
444+
// Multiple selection - convert each value to ID and return as JSON
445+
$convertedValues = [];
446+
foreach ($value as $optionText) {
447+
if (isset($optionMapping[$optionText])) {
448+
$convertedValues[] = $optionMapping[$optionText];
449+
}
450+
}
451+
return json_encode($convertedValues);
452+
} else {
453+
return $optionMapping[$value] ?? null;
454+
}
455+
}
456+
404457
/**
405458
* Gets ID by test_ident from creation results
406459
* @param array $results Array of creation results

‎tests/unit/Db/Row2MapperFilterTest.php‎

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,86 @@ public static function filterDataProvider(): array {
155155
['Alice'],
156156
'Filter by created_by meta column'
157157
],
158+
159+
// Text column (surname): hyphenated edge case and a real empty match
160+
'surname contains son' => [
161+
[['columnId' => 'surname', 'operator' => 'contains', 'value' => 'son']],
162+
['Alice', 'Bob', 'Charlie'],
163+
'Filter surnames containing son'
164+
],
165+
'surname ends-with son' => [
166+
[['columnId' => 'surname', 'operator' => 'ends-with', 'value' => 'son']],
167+
['Bob', 'Charlie'],
168+
'Filter surnames ending with son (Thompson-Jones excluded)'
169+
],
170+
'surname is-empty' => [
171+
[['columnId' => 'surname', 'operator' => 'is-empty', 'value' => '']],
172+
['Diana'],
173+
'Filter empty surname'
174+
],
175+
176+
// Number column (experience_years): equality, >=, <= and empty (Bob has none)
177+
'experience is-equal 5' => [
178+
[['columnId' => 'experience_years', 'operator' => 'is-equal', 'value' => '5']],
179+
['Alice'],
180+
'Filter experience years equal to 5'
181+
],
182+
'experience gte 5' => [
183+
[['columnId' => 'experience_years', 'operator' => 'is-greater-than-or-equal', 'value' => '5']],
184+
['Alice', 'Eve'],
185+
'Filter experience years greater than or equal to 5'
186+
],
187+
'experience lte 5' => [
188+
[['columnId' => 'experience_years', 'operator' => 'is-lower-than-or-equal', 'value' => '5']],
189+
['Alice', 'Bob', 'Charlie', 'Diana'],
190+
'Filter experience years lower than or equal to 5 (Bob included via default)'
191+
],
192+
'experience is-empty' => [
193+
[['columnId' => 'experience_years', 'operator' => 'is-empty', 'value' => '']],
194+
['Bob'],
195+
'Filter empty experience years'
196+
],
197+
198+
// Selection single column (status)
199+
'status is-equal Inactive' => [
200+
[['columnId' => 'status', 'operator' => 'is-equal', 'value' => '@selection-id-1']],
201+
['Bob'],
202+
'Filter status equal to Inactive (id 1)'
203+
],
204+
'status contains Active' => [
205+
[['columnId' => 'status', 'operator' => 'contains', 'value' => '@selection-id-0']],
206+
['Alice', 'Charlie', 'Eve'],
207+
'Filter status containing Active (id 0)'
208+
],
209+
210+
// Selection multi column (skills)
211+
'skills contains PHP' => [
212+
[['columnId' => 'skills', 'operator' => 'contains', 'value' => '@selection-id-0']],
213+
['Alice'],
214+
'Filter skills containing PHP (id 0)'
215+
],
216+
'skills is-equal Python' => [
217+
[['columnId' => 'skills', 'operator' => 'is-equal', 'value' => '@selection-id-3']],
218+
['Charlie'],
219+
'Filter skills equal to exactly Python (id 3)'
220+
],
221+
222+
// Selection checkbox column (is_available)
223+
'available is checked' => [
224+
[['columnId' => 'is_available', 'operator' => 'is-equal', 'value' => '@checked']],
225+
['Alice', 'Charlie'],
226+
'Filter available is checked'
227+
],
228+
'available is unchecked' => [
229+
[['columnId' => 'is_available', 'operator' => 'is-equal', 'value' => '@unchecked']],
230+
['Bob', 'Diana'],
231+
'Filter available is unchecked'
232+
],
233+
'available is-empty' => [
234+
[['columnId' => 'is_available', 'operator' => 'is-empty', 'value' => '']],
235+
['Eve'],
236+
'Filter available is empty'
237+
],
158238
];
159239
}
160240

‎tests/unit/Db/Row2MapperTestDependencies.php‎

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,12 @@ private function initializeTestData(): void {
8888
['test_ident' => 'age', 'title' => 'Age', 'type' => 'number'],
8989
['test_ident' => 'birthday', 'title' => 'Birthday', 'type' => 'datetime'],
9090
['test_ident' => 'department', 'title' => 'Department', 'type' => 'text'],
91-
['test_ident' => 'score', 'title' => 'Score', 'type' => 'number']
91+
['test_ident' => 'score', 'title' => 'Score', 'type' => 'number'],
92+
['test_ident' => 'surname', 'title' => 'Surname', 'type' => 'text'],
93+
['test_ident' => 'status', 'title' => 'Status', 'type' => 'selection', 'subtype' => '', 'selection_options' => json_encode([['id' => 0, 'label' => 'Active'], ['id' => 1, 'label' => 'Inactive'], ['id' => 2, 'label' => 'Pending']])],
94+
['test_ident' => 'skills', 'title' => 'Skills', 'type' => 'selection', 'subtype' => 'multi', 'selection_options' => json_encode([['id' => 0, 'label' => 'PHP'], ['id' => 1, 'label' => 'JavaScript'], ['id' => 2, 'label' => 'SQL'], ['id' => 3, 'label' => 'Python'], ['id' => 4, 'label' => 'Java'], ['id' => 5, 'label' => 'React'], ['id' => 6, 'label' => 'Node.js'], ['id' => 7, 'label' => 'MongoDB'], ['id' => 8, 'label' => 'Docker'], ['id' => 9, 'label' => 'Management'], ['id' => 10, 'label' => 'Communication'], ['id' => 11, 'label' => 'Excel'], ['id' => 12, 'label' => 'Accounting'], ['id' => 13, 'label' => 'Analysis']])],
95+
['test_ident' => 'is_available', 'title' => 'Available', 'type' => 'selection', 'subtype' => 'check', 'selection_options' => ''],
96+
['test_ident' => 'experience_years', 'title' => 'Experience (Years)', 'type' => 'number']
9297
],
9398
[
9499
[
@@ -100,7 +105,12 @@ private function initializeTestData(): void {
100105
'age' => 28,
101106
'birthday' => '1995-05-15 10:30:00',
102107
'department' => 'IT',
103-
'score' => 85.5
108+
'score' => 85.5,
109+
'surname' => 'Thompson-Jones',
110+
'status' => 'Active',
111+
'skills' => ['PHP', 'JavaScript', 'SQL', 'Python'],
112+
'is_available' => '"true"',
113+
'experience_years' => 5
104114
]
105115
],
106116
[
@@ -112,7 +122,11 @@ private function initializeTestData(): void {
112122
'age' => 32,
113123
'birthday' => '1991-12-03 14:20:00',
114124
'department' => 'HR',
115-
'score' => 92.0
125+
'score' => 92.0,
126+
'surname' => 'Thompson',
127+
'status' => 'Inactive',
128+
'skills' => ['Management', 'Communication'],
129+
'is_available' => '"false"'
116130
]
117131
],
118132
[
@@ -124,7 +138,12 @@ private function initializeTestData(): void {
124138
'age' => 25,
125139
'birthday' => '1998-01-20 08:45:00',
126140
'department' => 'IT',
127-
'score' => 78.3
141+
'score' => 78.3,
142+
'surname' => 'Wilson',
143+
'status' => 'Active',
144+
'skills' => ['Python'],
145+
'is_available' => '"true"',
146+
'experience_years' => 2
128147
]
129148
],
130149
[
@@ -136,7 +155,11 @@ private function initializeTestData(): void {
136155
'age' => 25,
137156
'birthday' => '1998-08-10 16:00:00',
138157
'department' => 'Finance',
139-
'score' => 88.7
158+
'score' => 88.7,
159+
'status' => 'Pending',
160+
'skills' => ['Excel', 'Accounting', 'Analysis'],
161+
'is_available' => '"false"',
162+
'experience_years' => 3
140163
]
141164
],
142165
[
@@ -148,7 +171,11 @@ private function initializeTestData(): void {
148171
'age' => 30,
149172
'birthday' => '1993-03-25 12:15:00',
150173
'department' => 'IT',
151-
'score' => 95.2
174+
'score' => 95.2,
175+
'surname' => 'Davis',
176+
'status' => 'Active',
177+
'skills' => ['React', 'Node.js', 'MongoDB', 'Docker'],
178+
'experience_years' => 7
152179
]
153180
]
154181
]
@@ -170,7 +197,7 @@ private function initializeTestData(): void {
170197
*/
171198
protected function setupRealColumnMapper(int $tableId): void {
172199
$qb = $this->connection->getQueryBuilder();
173-
$result = $qb->select('id', 'title', 'type', 'table_id')
200+
$result = $qb->select('id', 'title', 'type', 'subtype', 'table_id')
174201
->from('tables_columns')
175202
->where($qb->expr()->eq('table_id', $qb->createNamedParameter($tableId)))
176203
->executeQuery();
@@ -182,6 +209,7 @@ protected function setupRealColumnMapper(int $tableId): void {
182209
$column->setId($row['id']);
183210
$column->setTitle($row['title']);
184211
$column->setType($row['type']);
212+
$column->setSubtype($row['subtype']);
185213
$column->setTableId($row['table_id']);
186214
$columns[$row['id']] = $column;
187215
$columnTypes[$row['id']] = $row['type'];

0 commit comments

Comments
 (0)