PluginProbe
Custom 404 Pro / trunk
Custom 404 Pro vtrunk
3.15.2 3.15.4 3.15.5 3.15.6 3.16.0 3.15.1 3.15.0 3.14.1 3.14.0 3.13.0 trunk 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.1.5 1.1.6 1.2.0 1.3.10 1.3.12 1.3.5 All 100 releases
custom-404-pro / tests / integration / HelpersDbTest.php

HelpersDbTest.php in Custom 404 Pro trunk, at tests/integration/HelpersDbTest.php

240 lines 9.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Integration tests for the Helpers class database operations.
4 *
5 * @package Custom_404_Pro
6 */
7
8 /**
9 * Tests every public method in Helpers that touches the database.
10 */
11 class C404P_Integration_HelpersDbTest extends WP_UnitTestCase {
12
13 /**
14 * Fresh Helpers instance for each test.
15 *
16 * We use `new Helpers()` rather than `Helpers::singleton()` because the
17 * singleton stores its instance in a static local variable that cannot be
18 * reset between tests.
19 *
20 * @var Helpers
21 */
22 private $helpers;
23
24 /**
25 * Set up: create the logs table and a Helpers instance with admin privileges.
26 */
27 public function setUp(): void {
28 parent::setUp();
29 $admin_id = self::factory()->user->create( array( 'role' => 'administrator' ) );
30 wp_set_current_user( $admin_id );
31 ActivateClass::create_tables();
32 $this->helpers = new Helpers();
33 }
34
35 /**
36 * Tear down: drop the logs table.
37 */
38 public function tearDown(): void {
39 global $wpdb;
40 $wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->prefix . $this->helpers->table_logs ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
41 parent::tearDown();
42 }
43
44 // -------------------------------------------------------------------------
45 // Settings via wp_options
46 // -------------------------------------------------------------------------
47
48 /**
49 * get_settings() should return defaults when no option is stored.
50 */
51 public function test_get_settings_returns_defaults_when_no_option_stored() {
52 $this->assertSame( $this->helpers->defaults(), $this->helpers->get_settings() );
53 }
54
55 /**
56 * update_settings() should persist values readable by get_settings().
57 */
58 public function test_update_settings_persists_values() {
59 $this->helpers->update_settings( array( 'mode' => 'url', 'mode_url' => 'https://example.com' ) );
60 $settings = $this->helpers->get_settings();
61 $this->assertSame( 'url', $settings['mode'] );
62 $this->assertSame( 'https://example.com', $settings['mode_url'] );
63 }
64
65 /**
66 * update_settings() should merge with existing values rather than replace them.
67 */
68 public function test_update_settings_merges_with_existing_values() {
69 $this->helpers->update_settings( array( 'redirect_error_code' => 301 ) );
70 $this->helpers->update_settings( array( 'mode' => 'url' ) );
71 $this->assertSame( 301, (int) $this->helpers->get_setting( 'redirect_error_code' ) );
72 $this->assertSame( 'url', $this->helpers->get_setting( 'mode' ) );
73 }
74
75 // -------------------------------------------------------------------------
76 // Logs CRUD
77 // -------------------------------------------------------------------------
78
79 /**
80 * Helper to build a log stdClass.
81 *
82 * @param string $path Request path for the log entry.
83 * @return stdClass
84 */
85 private function make_log( $path = '/missing-page' ) {
86 $log = new stdClass();
87 $log->ip = '127.0.0.1';
88 $log->path = $path;
89 $log->referer = 'https://example.com';
90 $log->user_agent = 'PHPUnit';
91 return $log;
92 }
93
94 /**
95 * create_logs() should insert a row into the logs table.
96 */
97 public function test_create_logs_inserts_row_into_logs_table() {
98 global $wpdb;
99 $this->helpers->create_logs( array( $this->make_log() ), false );
100 $count = (int) $wpdb->get_var( 'SELECT COUNT(*) FROM ' . $wpdb->prefix . $this->helpers->table_logs ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
101 $this->assertSame( 1, $count );
102 }
103
104 /**
105 * get_logs() should return all inserted rows.
106 */
107 public function test_get_logs_returns_all_inserted_rows() {
108 $this->helpers->create_logs( array( $this->make_log( '/page-one' ), $this->make_log( '/page-two' ) ), false );
109 $logs = $this->helpers->get_logs();
110 $this->assertIsArray( $logs );
111 $this->assertCount( 2, $logs );
112 }
113
114 /**
115 * delete_logs('all') should truncate the logs table.
116 */
117 public function test_delete_logs_with_all_truncates_table() {
118 global $wpdb;
119 $this->helpers->create_logs( array( $this->make_log(), $this->make_log() ), false );
120 $this->helpers->delete_logs( 'all' );
121 $count = (int) $wpdb->get_var( 'SELECT COUNT(*) FROM ' . $wpdb->prefix . $this->helpers->table_logs ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
122 $this->assertSame( 0, $count );
123 }
124
125 /**
126 * delete_logs( $id ) should remove only the specified row.
127 */
128 public function test_delete_logs_with_single_id_removes_only_that_row() {
129 global $wpdb;
130 $this->helpers->create_logs( array( $this->make_log( '/first' ), $this->make_log( '/second' ) ), false );
131 $first_id = (int) $wpdb->get_var( 'SELECT id FROM ' . $wpdb->prefix . $this->helpers->table_logs . ' ORDER BY id ASC LIMIT 1' ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
132 $this->helpers->delete_logs( $first_id );
133 $count = (int) $wpdb->get_var( 'SELECT COUNT(*) FROM ' . $wpdb->prefix . $this->helpers->table_logs ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
134 $this->assertSame( 1, $count );
135 }
136
137 /**
138 * delete_logs( $ids_array ) should remove all specified rows.
139 */
140 public function test_delete_logs_with_array_of_ids_removes_specified_rows() {
141 global $wpdb;
142 $this->helpers->create_logs(
143 array(
144 $this->make_log( '/a' ),
145 $this->make_log( '/b' ),
146 $this->make_log( '/c' ),
147 ),
148 false
149 );
150 $ids = $wpdb->get_col( 'SELECT id FROM ' . $wpdb->prefix . $this->helpers->table_logs . ' ORDER BY id ASC LIMIT 2' ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
151 $this->helpers->delete_logs( array_map( 'intval', $ids ) );
152 $count = (int) $wpdb->get_var( 'SELECT COUNT(*) FROM ' . $wpdb->prefix . $this->helpers->table_logs ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
153 $this->assertSame( 1, $count );
154 }
155
156 // -------------------------------------------------------------------------
157 // Log retention helpers
158 // -------------------------------------------------------------------------
159
160 /**
161 * Inserts a log row with a backdated `created` timestamp.
162 *
163 * @param string $path Request path for the log entry.
164 * @param int $days_ago How many days ago to backdate the entry.
165 */
166 private function make_old_log( string $path, int $days_ago ) {
167 global $wpdb;
168 $query = $wpdb->prepare(
169 'INSERT INTO ' . $wpdb->prefix . $this->helpers->table_logs . ' (ip, path, referer, user_agent, created) VALUES (%s, %s, %s, %s, DATE_SUB(NOW(), INTERVAL %d DAY))', // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
170 '127.0.0.1',
171 $path,
172 '',
173 'PHPUnit',
174 $days_ago
175 );
176 $wpdb->query( $query ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
177 }
178
179 // -------------------------------------------------------------------------
180 // get_logs_count()
181 // -------------------------------------------------------------------------
182
183 /**
184 * get_logs_count() should return the total number of rows in the table.
185 */
186 public function test_get_logs_count_returns_correct_total() {
187 $this->helpers->create_logs( array( $this->make_log( '/a' ), $this->make_log( '/b' ) ), false );
188 $this->assertSame( 2, $this->helpers->get_logs_count() );
189 }
190
191 // -------------------------------------------------------------------------
192 // prune_logs()
193 // -------------------------------------------------------------------------
194
195 /**
196 * prune_logs() should be a no-op when both retention settings are 0.
197 */
198 public function test_prune_logs_is_noop_when_both_settings_are_zero() {
199 $this->helpers->update_settings( array( 'log_retention_count' => 0, 'log_retention_days' => 0 ) );
200 $this->helpers->create_logs( array( $this->make_log( '/a' ), $this->make_log( '/b' ), $this->make_log( '/c' ) ), false );
201 $deleted = $this->helpers->prune_logs();
202 $this->assertSame( 0, $deleted );
203 $this->assertSame( 3, $this->helpers->get_logs_count() );
204 }
205
206 /**
207 * prune_logs() should delete the oldest rows when the count cap is exceeded.
208 */
209 public function test_prune_logs_deletes_oldest_rows_when_count_limit_exceeded() {
210 $this->helpers->update_settings( array( 'log_retention_count' => 2, 'log_retention_days' => 0 ) );
211 $this->helpers->create_logs( array( $this->make_log( '/a' ), $this->make_log( '/b' ), $this->make_log( '/c' ) ), false );
212 $deleted = $this->helpers->prune_logs();
213 $this->assertSame( 1, $deleted );
214 $this->assertSame( 2, $this->helpers->get_logs_count() );
215 }
216
217 /**
218 * prune_logs() should delete rows older than the configured age limit.
219 */
220 public function test_prune_logs_deletes_rows_older_than_age_limit() {
221 $this->helpers->update_settings( array( 'log_retention_count' => 0, 'log_retention_days' => 30 ) );
222 $this->make_old_log( '/old-a', 31 );
223 $this->make_old_log( '/old-b', 35 );
224 $this->helpers->create_logs( array( $this->make_log( '/recent' ) ), false );
225 $deleted = $this->helpers->prune_logs();
226 $this->assertSame( 2, $deleted );
227 $this->assertSame( 1, $this->helpers->get_logs_count() );
228 }
229
230 /**
231 * prune_logs() should return 0 when the count is within the configured limit.
232 */
233 public function test_prune_logs_returns_zero_when_count_within_limit() {
234 $this->helpers->update_settings( array( 'log_retention_count' => 10, 'log_retention_days' => 0 ) );
235 $this->helpers->create_logs( array( $this->make_log( '/a' ), $this->make_log( '/b' ) ), false );
236 $deleted = $this->helpers->prune_logs();
237 $this->assertSame( 0, $deleted );
238 }
239 }
240