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