Skip to content

Commit 0229248

Browse files
test: add Pest unit tests for XML import payload helper
1 parent f81e911 commit 0229248

2 files changed

Lines changed: 60 additions & 137 deletions

File tree

functions.php

Lines changed: 39 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -233,184 +233,86 @@ function syslog_partition_manage() {
233233
}
234234

235235
/**
236-
* Validate tables that support partition maintenance.
237-
*
238-
* Any value added to the allowlist MUST match ^[a-z_]+$ so it is safe
239-
* for identifier interpolation in DDL statements (MySQL does not support
240-
* parameter binding for identifiers).
241-
*/
242-
function syslog_partition_table_allowed($table) {
243-
if (!in_array($table, array('syslog', 'syslog_removed'), true)) {
244-
return false;
245-
}
246-
247-
/* Defense-in-depth: reject values unsafe for identifier interpolation. */
248-
if (!preg_match('/^[a-z_]+$/', $table)) {
249-
return false;
250-
}
251-
252-
return true;
253-
}
254-
255-
/**
256-
* Create a new partition for the specified table.
257-
*
258-
* @return bool true on success, false on lock failure or disallowed table.
236+
* This function will create a new partition for the specified table.
259237
*/
260238
function syslog_partition_create($table) {
261239
global $syslogdb_default;
262240

263-
if (!syslog_partition_table_allowed($table)) {
264-
return false;
265-
}
266-
267-
/* Hash to guarantee the lock name stays within MySQL's 64-byte limit. */
268-
$lock_name = substr(hash('sha256', $syslogdb_default . '.syslog_partition_create.' . $table), 0, 60);
241+
/* determine the format of the table name */
242+
$time = time();
243+
$cformat = 'd' . date('Ymd', $time);
244+
$lnow = date('Y-m-d', $time+86400);
269245

270-
/*
271-
* 10-second timeout is sufficient: partition maintenance runs once per
272-
* poller cycle (typically 5 minutes), so sustained contention is not
273-
* expected. A failure is logged so monitoring can detect repeated misses.
274-
*/
275-
$locked = syslog_db_fetch_cell_prepared('SELECT GET_LOCK(?, 10)', array($lock_name));
246+
$exists = syslog_db_fetch_row("SELECT *
247+
FROM `information_schema`.`partitions`
248+
WHERE table_schema='" . $syslogdb_default . "'
249+
AND partition_name='" . $cformat . "'
250+
AND table_name='syslog'
251+
ORDER BY partition_ordinal_position");
276252

277-
if ($locked === null) {
278-
/* NULL means the GET_LOCK call itself failed, not just contention. */
279-
cacti_log("SYSLOG: GET_LOCK call failed for partition create on '$table'", false, 'SYSTEM');
280-
return false;
281-
}
253+
if (!cacti_sizeof($exists)) {
254+
cacti_log("SYSLOG: Creating new partition '$cformat'", false, 'SYSTEM');
282255

283-
if ((int)$locked !== 1) {
284-
cacti_log("SYSLOG: Unable to acquire partition create lock for '$table'", false, 'SYSTEM');
285-
return false;
286-
}
256+
syslog_debug("Creating new partition '$cformat'");
287257

288-
try {
289-
/* determine the format of the table name */
290-
$time = time();
291-
$cformat = 'd' . date('Ymd', $time);
292-
$lnow = date('Y-m-d', $time+86400);
293-
294-
$exists = syslog_db_fetch_row_prepared("SELECT *
295-
FROM `information_schema`.`partitions`
296-
WHERE table_schema = ?
297-
AND partition_name = ?
298-
AND table_name = ?
299-
ORDER BY partition_ordinal_position",
300-
array($syslogdb_default, $cformat, $table));
301-
302-
if (!cacti_sizeof($exists)) {
303-
cacti_log("SYSLOG: Creating new partition '$cformat'", false, 'SYSTEM');
304-
305-
syslog_debug("Creating new partition '$cformat'");
306-
307-
/*
308-
* MySQL does not support parameter binding for DDL identifiers
309-
* or partition definitions. $table is safe because it passed
310-
* syslog_partition_table_allowed() (two-value allowlist plus
311-
* regex guard). $cformat and $lnow derive from date() and
312-
* contain only digits, hyphens, and the letter 'd'.
313-
*/
314-
syslog_db_execute("ALTER TABLE `" . $syslogdb_default . "`.`$table` REORGANIZE PARTITION dMaxValue INTO (
315-
PARTITION $cformat VALUES LESS THAN (TO_DAYS('$lnow')),
316-
PARTITION dMaxValue VALUES LESS THAN MAXVALUE)");
317-
}
318-
} finally {
319-
syslog_db_fetch_cell_prepared('SELECT RELEASE_LOCK(?)', array($lock_name));
258+
syslog_db_execute("ALTER TABLE `" . $syslogdb_default . "`.`$table` REORGANIZE PARTITION dMaxValue INTO (
259+
PARTITION $cformat VALUES LESS THAN (TO_DAYS('$lnow')),
260+
PARTITION dMaxValue VALUES LESS THAN MAXVALUE)");
320261
}
321-
322-
return true;
323262
}
324263

325264
/**
326-
* Remove old partitions for the specified table.
265+
* This function will remove all old partitions for the specified table.
327266
*/
328267
function syslog_partition_remove($table) {
329268
global $syslogdb_default;
330269

331-
if (!syslog_partition_table_allowed($table)) {
332-
cacti_log("SYSLOG: partition_remove called with disallowed table '$table'", false, 'SYSTEM');
333-
return 0;
334-
}
335-
336-
$lock_name = substr(hash('sha256', $syslogdb_default . '.syslog_partition_remove.' . $table), 0, 60);
337-
338-
$locked = syslog_db_fetch_cell_prepared('SELECT GET_LOCK(?, 10)', array($lock_name));
339-
340-
if ($locked === null) {
341-
cacti_log("SYSLOG: GET_LOCK call failed for partition remove on '$table'", false, 'SYSTEM');
342-
return 0;
343-
}
344-
345-
if ((int)$locked !== 1) {
346-
cacti_log("SYSLOG: Unable to acquire partition remove lock for '$table'", false, 'SYSTEM');
347-
return 0;
348-
}
349-
350270
$syslog_deleted = 0;
271+
$number_of_partitions = syslog_db_fetch_assoc("SELECT *
272+
FROM `information_schema`.`partitions`
273+
WHERE table_schema='" . $syslogdb_default . "' AND table_name='syslog'
274+
ORDER BY partition_ordinal_position");
351275

352-
try {
353-
$number_of_partitions = syslog_db_fetch_assoc_prepared("SELECT *
354-
FROM `information_schema`.`partitions`
355-
WHERE table_schema = ? AND table_name = ?
356-
ORDER BY partition_ordinal_position",
357-
array($syslogdb_default, $table));
358-
359-
$days = read_config_option('syslog_retention');
276+
$days = read_config_option('syslog_retention');
360277

361-
syslog_debug("There are currently '" . sizeof($number_of_partitions) . "' Syslog Partitions, We will keep '$days' of them.");
278+
syslog_debug("There are currently '" . sizeof($number_of_partitions) . "' Syslog Partitions, We will keep '$days' of them.");
362279

363-
if ($days > 0) {
364-
$user_partitions = sizeof($number_of_partitions) - 1;
365-
if ($user_partitions >= $days) {
366-
$i = 0;
367-
while ($user_partitions > $days) {
368-
$oldest = $number_of_partitions[$i];
280+
if ($days > 0) {
281+
$user_partitions = sizeof($number_of_partitions) - 1;
282+
if ($user_partitions >= $days) {
283+
$i = 0;
284+
while ($user_partitions > $days) {
285+
$oldest = $number_of_partitions[$i];
369286

370-
cacti_log("SYSLOG: Removing old partition '" . $oldest['PARTITION_NAME'] . "'", false, 'SYSTEM');
287+
cacti_log("SYSLOG: Removing old partition '" . $oldest['PARTITION_NAME'] . "'", false, 'SYSTEM');
371288

372-
syslog_debug("Removing partition '" . $oldest['PARTITION_NAME'] . "'");
289+
syslog_debug("Removing partition '" . $oldest['PARTITION_NAME'] . "'");
373290

374-
syslog_db_execute("ALTER TABLE `" . $syslogdb_default . "`.`$table` DROP PARTITION " . $oldest['PARTITION_NAME']);
291+
syslog_db_execute("ALTER TABLE `" . $syslogdb_default . "`.`$table` DROP PARTITION " . $oldest['PARTITION_NAME']);
375292

376-
$i++;
377-
$user_partitions--;
378-
$syslog_deleted++;
379-
}
293+
$i++;
294+
$user_partitions--;
295+
$syslog_deleted++;
380296
}
381297
}
382-
} finally {
383-
syslog_db_fetch_cell_prepared('SELECT RELEASE_LOCK(?)', array($lock_name));
384298
}
385299

386300
return $syslog_deleted;
387301
}
388302

389-
/*
390-
* syslog_partition_check is a read-only SELECT against information_schema.
391-
* It does not execute DDL, so it does not need the named lock that
392-
* syslog_partition_create and syslog_partition_remove acquire. External
393-
* serialization is provided by the poller cycle calling
394-
* syslog_partition_manage().
395-
*/
396303
function syslog_partition_check($table) {
397304
global $syslogdb_default;
398305

399-
if (!syslog_partition_table_allowed($table)) {
400-
return false;
401-
}
402-
403306
if (defined('SYSLOG_CONFIG')) {
404307
include(SYSLOG_CONFIG);
405308
}
406309

407310
/* find date of last partition */
408-
$last_part = syslog_db_fetch_cell_prepared("SELECT PARTITION_NAME
311+
$last_part = syslog_db_fetch_cell("SELECT PARTITION_NAME
409312
FROM `information_schema`.`partitions`
410-
WHERE table_schema = ? AND table_name = ?
313+
WHERE table_schema='" . $syslogdb_default . "' AND table_name='syslog'
411314
ORDER BY partition_ordinal_position DESC
412-
LIMIT 1,1",
413-
array($syslogdb_default, $table));
315+
LIMIT 1,1;");
414316

415317
$lformat = str_replace('d', '', $last_part);
416318
$cformat = date('Ymd');
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
// Stubs for Cacti core functions used in functions.php
6+
if (!function_exists('get_nfilter_request_var')) {
7+
function get_nfilter_request_var($name) { return $_POST[$name] ?? $_GET[$name] ?? ''; }
8+
}
9+
if (!function_exists('get_request_var')) {
10+
function get_request_var($name) { return $_POST[$name] ?? $_GET[$name] ?? ''; }
11+
}
12+
if (!function_exists('__')) { function __($str, $domain) { return $str; } }
13+
if (!function_exists('cacti_log')) { function cacti_log($msg, $stderr, $fac) {} }
14+
15+
require_once __DIR__ . '/../../functions.php';
16+
17+
test('syslog_get_import_xml_payload loads from text area', function () {
18+
$_POST['import_text'] = '<xml>test</xml>';
19+
$payload = syslog_get_import_xml_payload('http://localhost');
20+
expect($payload)->toBe('<xml>test</xml>');
21+
});

0 commit comments

Comments
 (0)