1*e4050f96SAnna Dabrowska<?php 2*e4050f96SAnna Dabrowska 3*e4050f96SAnna Dabrowskanamespace dokuwiki\plugin\structnotification\test; 4*e4050f96SAnna Dabrowska 5*e4050f96SAnna Dabrowskause dokuwiki\plugin\struct\meta\Value; 6*e4050f96SAnna Dabrowskause dokuwiki\plugin\struct\meta\Column; 7*e4050f96SAnna Dabrowska 8*e4050f96SAnna Dabrowska/** 9*e4050f96SAnna Dabrowska * Unit tests for structnotification plugin 10*e4050f96SAnna Dabrowska * 11*e4050f96SAnna Dabrowska * @group plugin_structnotification 12*e4050f96SAnna Dabrowska * @group plugins 13*e4050f96SAnna Dabrowska */ 14*e4050f96SAnna Dabrowskaclass NotificationTest extends \DokuWikiTest 15*e4050f96SAnna Dabrowska{ 16*e4050f96SAnna Dabrowska protected $pluginsEnabled = ['struct', 'structnotification']; 17*e4050f96SAnna Dabrowska 18*e4050f96SAnna Dabrowska /** @var \action_plugin_structnotification_notification */ 19*e4050f96SAnna Dabrowska protected $action; 20*e4050f96SAnna Dabrowska 21*e4050f96SAnna Dabrowska public function setUp(): void 22*e4050f96SAnna Dabrowska { 23*e4050f96SAnna Dabrowska parent::setUp(); 24*e4050f96SAnna Dabrowska $this->action = new \action_plugin_structnotification_notification(); 25*e4050f96SAnna Dabrowska } 26*e4050f96SAnna Dabrowska 27*e4050f96SAnna Dabrowska /** 28*e4050f96SAnna Dabrowska * Test date parsing with different formats 29*e4050f96SAnna Dabrowska */ 30*e4050f96SAnna Dabrowska public function test_predicateTrue_date_formats() 31*e4050f96SAnna Dabrowska { 32*e4050f96SAnna Dabrowska $datetime = date('Y-m-d H:i:s', strtotime('+5 days')); 33*e4050f96SAnna Dabrowska $this->assertTrue($this->callPrivateMethod('predicateTrue', [$datetime, 'before', 10])); 34*e4050f96SAnna Dabrowska 35*e4050f96SAnna Dabrowska $datetime = date('Y/d/m', strtotime('+5 days')); 36*e4050f96SAnna Dabrowska $this->assertTrue($this->callPrivateMethod('predicateTrue', [$datetime, 'before', 10])); 37*e4050f96SAnna Dabrowska } 38*e4050f96SAnna Dabrowska 39*e4050f96SAnna Dabrowska /** 40*e4050f96SAnna Dabrowska * Test predicateTrue() with 'before' operator 41*e4050f96SAnna Dabrowska */ 42*e4050f96SAnna Dabrowska public function test_predicateTrue_before() 43*e4050f96SAnna Dabrowska { 44*e4050f96SAnna Dabrowska // Test date that is 5 days from now should match "10 days before" 45*e4050f96SAnna Dabrowska $futureDate = date('Y-m-d', strtotime('+5 days')); 46*e4050f96SAnna Dabrowska $this->assertTrue($this->callPrivateMethod('predicateTrue', [$futureDate, 'before', 10])); 47*e4050f96SAnna Dabrowska 48*e4050f96SAnna Dabrowska // Test date that is 15 days from now should not match "10 days before" 49*e4050f96SAnna Dabrowska $farFutureDate = date('Y-m-d', strtotime('+15 days')); 50*e4050f96SAnna Dabrowska $this->assertFalse($this->callPrivateMethod('predicateTrue', [$farFutureDate, 'before', 10])); 51*e4050f96SAnna Dabrowska 52*e4050f96SAnna Dabrowska // Test today should match "1 day before" 53*e4050f96SAnna Dabrowska $today = date('Y-m-d'); 54*e4050f96SAnna Dabrowska $this->assertTrue($this->callPrivateMethod('predicateTrue', [$today, 'before', 1])); 55*e4050f96SAnna Dabrowska } 56*e4050f96SAnna Dabrowska 57*e4050f96SAnna Dabrowska /** 58*e4050f96SAnna Dabrowska * Test predicateTrue() with 'after' operator 59*e4050f96SAnna Dabrowska */ 60*e4050f96SAnna Dabrowska public function test_predicateTrue_after() 61*e4050f96SAnna Dabrowska { 62*e4050f96SAnna Dabrowska // Test date that is 15 days ago should match "10 days after" 63*e4050f96SAnna Dabrowska $farPastDate = date('Y-m-d', strtotime('-15 days')); 64*e4050f96SAnna Dabrowska $this->assertTrue($this->callPrivateMethod('predicateTrue', [$farPastDate, 'after', 10])); 65*e4050f96SAnna Dabrowska 66*e4050f96SAnna Dabrowska // Test date that is 5 days ago should not match "10 days after" 67*e4050f96SAnna Dabrowska $pastDate = date('Y-m-d', strtotime('-5 days')); 68*e4050f96SAnna Dabrowska $this->assertFalse($this->callPrivateMethod('predicateTrue', [$pastDate, 'after', 10])); 69*e4050f96SAnna Dabrowska 70*e4050f96SAnna Dabrowska // Test today should not match "1 day after" 71*e4050f96SAnna Dabrowska $today = date('Y-m-d'); 72*e4050f96SAnna Dabrowska $this->assertFalse($this->callPrivateMethod('predicateTrue', [$today, 'after', 1])); 73*e4050f96SAnna Dabrowska } 74*e4050f96SAnna Dabrowska 75*e4050f96SAnna Dabrowska /** 76*e4050f96SAnna Dabrowska * Test predicateTrue() with invalid operator 77*e4050f96SAnna Dabrowska */ 78*e4050f96SAnna Dabrowska public function test_predicateTrue_invalid_operator() 79*e4050f96SAnna Dabrowska { 80*e4050f96SAnna Dabrowska $today = date('Y-m-d'); 81*e4050f96SAnna Dabrowska $this->assertFalse($this->callPrivateMethod('predicateTrue', [$today, 'invalid', 5])); 82*e4050f96SAnna Dabrowska } 83*e4050f96SAnna Dabrowska 84*e4050f96SAnna Dabrowska /** 85*e4050f96SAnna Dabrowska * Test edge cases for predicateTrue() 86*e4050f96SAnna Dabrowska */ 87*e4050f96SAnna Dabrowska public function test_predicateTrue_edge_cases() 88*e4050f96SAnna Dabrowska { 89*e4050f96SAnna Dabrowska // Test with 0 days - both should match today 90*e4050f96SAnna Dabrowska $today = date('Y-m-d'); 91*e4050f96SAnna Dabrowska $this->assertTrue($this->callPrivateMethod('predicateTrue', [$today, 'before', 0])); 92*e4050f96SAnna Dabrowska $this->assertTrue($this->callPrivateMethod('predicateTrue', [$today, 'after', 0])); 93*e4050f96SAnna Dabrowska } 94*e4050f96SAnna Dabrowska 95*e4050f96SAnna Dabrowska /** 96*e4050f96SAnna Dabrowska * Test replacePlaceholders() 97*e4050f96SAnna Dabrowska */ 98*e4050f96SAnna Dabrowska public function test_replacePlaceholders() 99*e4050f96SAnna Dabrowska { 100*e4050f96SAnna Dabrowska // Mock Value objects 101*e4050f96SAnna Dabrowska $value1 = $this->createMockValue('schema1', 'field1', 'Test Value 1'); 102*e4050f96SAnna Dabrowska $value2 = $this->createMockValue('schema1', 'field2', 'Test Value 2'); 103*e4050f96SAnna Dabrowska $values = [$value1, $value2]; 104*e4050f96SAnna Dabrowska 105*e4050f96SAnna Dabrowska $message = 'Hello @@schema1.field1@@, your @@schema1.field2@@ is ready.'; 106*e4050f96SAnna Dabrowska $expected = 'Hello Test Value 1, your Test Value 2 is ready.'; 107*e4050f96SAnna Dabrowska 108*e4050f96SAnna Dabrowska $result = $this->callPrivateMethod('replacePlaceholders', [$message, $values]); 109*e4050f96SAnna Dabrowska $this->assertEquals($expected, $result); 110*e4050f96SAnna Dabrowska } 111*e4050f96SAnna Dabrowska 112*e4050f96SAnna Dabrowska /** 113*e4050f96SAnna Dabrowska * Test replacePlaceholders with no matches 114*e4050f96SAnna Dabrowska */ 115*e4050f96SAnna Dabrowska public function test_replacePlaceholders_no_matches() 116*e4050f96SAnna Dabrowska { 117*e4050f96SAnna Dabrowska $value1 = $this->createMockValue('schema1', 'field1', 'Test Value 1'); 118*e4050f96SAnna Dabrowska $values = [$value1]; 119*e4050f96SAnna Dabrowska 120*e4050f96SAnna Dabrowska $message = 'Hello @@schema2.field2@@, no replacement here.'; 121*e4050f96SAnna Dabrowska $expected = 'Hello @@schema2.field2@@, no replacement here.'; 122*e4050f96SAnna Dabrowska 123*e4050f96SAnna Dabrowska $result = $this->callPrivateMethod('replacePlaceholders', [$message, $values]); 124*e4050f96SAnna Dabrowska $this->assertEquals($expected, $result); 125*e4050f96SAnna Dabrowska } 126*e4050f96SAnna Dabrowska 127*e4050f96SAnna Dabrowska /** 128*e4050f96SAnna Dabrowska * Test early return in addFiltersToSearch() 129*e4050f96SAnna Dabrowska */ 130*e4050f96SAnna Dabrowska public function test_addFiltersToSearch_empty_filters() 131*e4050f96SAnna Dabrowska { 132*e4050f96SAnna Dabrowska $search = $this->createMock(\dokuwiki\plugin\struct\meta\Search::class); 133*e4050f96SAnna Dabrowska $search->expects($this->never())->method('addFilter'); 134*e4050f96SAnna Dabrowska 135*e4050f96SAnna Dabrowska $this->callPrivateMethod('addFiltersToSearch', [&$search, '']); 136*e4050f96SAnna Dabrowska } 137*e4050f96SAnna Dabrowska 138*e4050f96SAnna Dabrowska /** 139*e4050f96SAnna Dabrowska * Test addFiltersToSearch method with actual filters 140*e4050f96SAnna Dabrowska */ 141*e4050f96SAnna Dabrowska public function test_addFiltersToSearch_with_filters() 142*e4050f96SAnna Dabrowska { 143*e4050f96SAnna Dabrowska $search = $this->createMock(\dokuwiki\plugin\struct\meta\Search::class); 144*e4050f96SAnna Dabrowska 145*e4050f96SAnna Dabrowska // Track the calls to addFilter 146*e4050f96SAnna Dabrowska $callCount = 0; 147*e4050f96SAnna Dabrowska $expectedCalls = [ 148*e4050f96SAnna Dabrowska ['field1', 'value1', '=', 'AND'], 149*e4050f96SAnna Dabrowska ['field2', '10', '>', 'AND'] 150*e4050f96SAnna Dabrowska ]; 151*e4050f96SAnna Dabrowska 152*e4050f96SAnna Dabrowska // Expect addFilter to be called twice (once for each filter line) 153*e4050f96SAnna Dabrowska $search->expects($this->exactly(2)) 154*e4050f96SAnna Dabrowska ->method('addFilter') 155*e4050f96SAnna Dabrowska ->willReturnCallback(function($colname, $value, $comp, $logic) use (&$callCount, $expectedCalls) { 156*e4050f96SAnna Dabrowska $this->assertEquals($expectedCalls[$callCount][0], $colname); 157*e4050f96SAnna Dabrowska $this->assertEquals($expectedCalls[$callCount][1], $value); 158*e4050f96SAnna Dabrowska $this->assertEquals($expectedCalls[$callCount][2], $comp); 159*e4050f96SAnna Dabrowska $this->assertEquals($expectedCalls[$callCount][3], $logic); 160*e4050f96SAnna Dabrowska $callCount++; 161*e4050f96SAnna Dabrowska }); 162*e4050f96SAnna Dabrowska 163*e4050f96SAnna Dabrowska // Add filters 164*e4050f96SAnna Dabrowska $filters = "field1 = value1\r\nfield2 > 10"; 165*e4050f96SAnna Dabrowska $this->callPrivateMethod('addFiltersToSearch', [&$search, $filters]); 166*e4050f96SAnna Dabrowska } 167*e4050f96SAnna Dabrowska 168*e4050f96SAnna Dabrowska /** 169*e4050f96SAnna Dabrowska * Helper method to call private methods 170*e4050f96SAnna Dabrowska */ 171*e4050f96SAnna Dabrowska private function callPrivateMethod($methodName, $args = []) 172*e4050f96SAnna Dabrowska { 173*e4050f96SAnna Dabrowska $reflection = new \ReflectionClass($this->action); 174*e4050f96SAnna Dabrowska $method = $reflection->getMethod($methodName); 175*e4050f96SAnna Dabrowska $method->setAccessible(true); 176*e4050f96SAnna Dabrowska return $method->invokeArgs($this->action, $args); 177*e4050f96SAnna Dabrowska } 178*e4050f96SAnna Dabrowska 179*e4050f96SAnna Dabrowska /** 180*e4050f96SAnna Dabrowska * Helper method to create mock Value objects 181*e4050f96SAnna Dabrowska */ 182*e4050f96SAnna Dabrowska private function createMockValue($schema, $label, $rawValue) 183*e4050f96SAnna Dabrowska { 184*e4050f96SAnna Dabrowska $column = $this->createMock(Column::class); 185*e4050f96SAnna Dabrowska $column->method('getTable')->willReturn($schema); 186*e4050f96SAnna Dabrowska $column->method('getLabel')->willReturn($label); 187*e4050f96SAnna Dabrowska 188*e4050f96SAnna Dabrowska $value = $this->createMock(Value::class); 189*e4050f96SAnna Dabrowska $value->method('getColumn')->willReturn($column); 190*e4050f96SAnna Dabrowska $value->method('getRawValue')->willReturn($rawValue); 191*e4050f96SAnna Dabrowska $value->method('getDisplayValue')->willReturn($rawValue); 192*e4050f96SAnna Dabrowska 193*e4050f96SAnna Dabrowska return $value; 194*e4050f96SAnna Dabrowska } 195*e4050f96SAnna Dabrowska} 196