Files
cacti/tests/integration/RrdBoostRraIdContractTest.php
Thomas VincentandTheWitness bc12e4492e Fix TypeError in boost_graph_set_file when sending reports (#7268)
* Fix TypeError in boost_graph_set_file when sending reports

reports.php passes '' for rra_id; normalize to int|null in
rrdtool_function_graph before it reaches boost. Ref #7214

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* Restrict rra_id normalization to whole numbers

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* ci: bound page crawler network retries

* test: match rra_id source anchors to ctype_digit or filter_var

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* test: accept ?int spelling of the rra_id contract

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

* ci: refresh sink and hotspot baselines for line shifts

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>

---------

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Co-authored-by: TheWitness <thewitness@cacti.net>
2026-07-24 19:44:04 -04:00

57 lines
2.5 KiB
PHP

<?php
/*
+-------------------------------------------------------------------------+
| Copyright (C) 2004-2026 The Cacti Group |
+-------------------------------------------------------------------------+
*/
/*
* Cross-file contract test for the rra_id path from report generation
* into boost (#7214).
*
* lib/reports.php passes '' for $rra_id. rrdtool_function_graph() must
* therefore validate the value before handing it to boost_graph_set_file(),
* whose signature is int|null and rejects strings with a TypeError.
*/
$root = dirname(__DIR__, 2);
test('boost_graph_set_file still declares a nullable int for rra_id', function () use ($root) {
$src = file_get_contents($root . '/lib/boost.php');
expect($src)->not->toBeFalse('Failed to read lib/boost.php');
// Accept either spelling of the nullable int so an int|null <-> ?int refactor
// does not break the contract check.
$pattern = '/function\s+boost_graph_set_file\s*\([^)]*(?:int\|null|\?int)\s+\$rra_id/s';
expect(preg_match($pattern, $src))->toBe(1,
'boost_graph_set_file must declare a nullable int $rra_id; if this changed, revisit the normalization in lib/rrd.php');
});
test('reports.php still passes a non-int rra_id into rrdtool_function_graph', function () use ($root) {
$src = file_get_contents($root . '/lib/reports.php');
expect($src)->not->toBeFalse('Failed to read lib/reports.php');
$count = preg_match_all("/rrdtool_function_graph\(\\\$local_graph_id,\s*''/", $src);
expect($count)->toBeGreaterThan(0,
"reports.php passes '' as rra_id; the normalization in rrdtool_function_graph must stay");
});
test('rrd.php validation guards every boost consumer of rra_id', function () use ($root) {
$src = file_get_contents($root . '/lib/rrd.php');
expect($src)->not->toBeFalse('Failed to read lib/rrd.php');
$fnPos = strpos($src, 'function rrdtool_function_graph(');
expect($fnPos)->not->toBeFalse('rrdtool_function_graph must exist');
// Anchor on the integer-validation call by shape, not an exact line, so
// ctype_digit() or filter_var(FILTER_VALIDATE_INT) both satisfy it.
$matched = preg_match('/ctype_digit\s*\(|filter_var\s*\([^)]*FILTER_VALIDATE_INT/', $src, $m, PREG_OFFSET_CAPTURE, $fnPos);
expect($matched)->toBe(1, 'integer validation of rra_id missing from rrdtool_function_graph');
$normPos = $m[0][1];
foreach (['boost_graph_cache_check(', 'boost_graph_set_file('] as $consumer) {
$pos = strpos($src, $consumer, $normPos);
expect($pos)->not->toBeFalse("$consumer must appear after the validation");
}
});