Files
cacti/tests/Unit/Core/Rrd/RrdAxisOptionsTest.php
Thomas Vincent 4d11070ced feat: expose RRDtool 1.11 graph axis controls (#8356)
* feat: expose RRDtool 1.11 graph axis controls

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

* fix: constrain axis template inheritance checkboxes

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

* test(rrd): exercise native aggregate validation and enforce coverage

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

* ci: share changed-code coverage contracts and enforce shell policy

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

* fix(ci): run request coverage with portable path matching

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

* ci: retain bootstrap coverage and validate request dispatch

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

* test: synchronize native coverage fixture dispatch

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

* test: measure coverage at the exact PR revision

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

---------

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
2026-10-14 12:10:28 -04:00

107 lines
5.3 KiB
PHP

<?php
// Copyright (C) 2004-2026 The Cacti Group. GPL-2.0-or-later.
require_once dirname(__DIR__, 4) . '/lib/CactiValidator.php';
require_once dirname(__DIR__, 4) . '/lib/rrd.php';
require_once dirname(__DIR__, 4) . '/lib/api_aggregate.php';
require_once dirname(__DIR__, 3) . '/Helpers/SourceSlice.php';
require_once dirname(__DIR__, 3) . '/fixtures/Core/Rrd/AxisOptions.php';
cacti_fixture_source_function(dirname(__DIR__, 4) . '/lib/rrd.php', 'rrd_function_process_graph_options', 'CactiAxisFixture');
cacti_fixture_source_function(dirname(__DIR__, 4) . '/lib/template.php', 'push_out_graph', 'CactiAxisFixture');
test('axis defaults and older configured versions preserve graph commands', function () {
$fields = ['vertical_label_angle' => '90', 'right_axis_label_angle' => '270', 'right_axis_range' => '0:100'];
foreach (['1.5.0', '1.9.0', '1.10.0'] as $version) {
expect(rrd_graph_axis_options($fields, $version))->toBe('');
}
expect(rrd_graph_axis_options([], '1.11.0'))->toBe('')
->and(rrd_graph_axis_options(array_fill_keys(array_keys($fields), ''), '1.11.0'))->toBe('');
});
test('new and future versions emit independent angles including zero', function () {
foreach (['1.11.0', '1.12.0'] as $version) {
$options = rrd_graph_axis_options(['vertical_label_angle' => '0', 'right_axis_label_angle' => '270', 'right_axis_range' => '0:100'], $version);
expect($options)->toBe('--vertical-label-angle ' . cacti_escapeshellarg('0') . RRD_NL .
'--right-axis-label-angle ' . cacti_escapeshellarg('270') . RRD_NL .
'--right-axis-range ' . cacti_escapeshellarg('0:100') . RRD_NL);
}
});
test('right axis bounds accept open endpoints and finite numeric values', function (string $range) {
expect(rrd_graph_axis_option_is_valid('right_axis_range', $range))->toBeTrue();
})->with(['', '0:100', '0:', ':100', '-2.5:3.25', '-1e2:1e2', '.5:1.', '0:0']);
test('invalid imported axis values cannot reach the graph command', function (string $field, mixed $value) {
expect(rrd_graph_axis_option_is_valid($field, $value))->toBeFalse()
->and(rrd_graph_axis_options([$field => $value], '1.11.0'))->toBe('');
})->with([
['vertical_label_angle', '45'], ['right_axis_label_angle', '270;touch /tmp/axis'],
['vertical_label_angle', ['90']], ['right_axis_range', '100:0'],
['right_axis_range', ':'], ['right_axis_range', 'NaN:100'],
['right_axis_range', '0:INF'], ['right_axis_range', '1e999:'],
['right_axis_range', "0:100\n"], ['right_axis_range', '0:100 --title injected'],
['right_axis_range', str_repeat('1', 65) . ':'],
]);
test('graph rendering includes the new options only at the configured capability level', function () {
global $image_types;
$image_types = [1 => 'PNG'];
$graph = [
'auto_scale' => '', 'upper_limit' => '', 'lower_limit' => '',
'auto_scale_log' => '', 'scale_log_units' => '', 'auto_scale_rigid' => '',
'unit_value' => '', 'unit_exponent_value' => '', 'height' => '100', 'width' => '200',
'image_format_id' => 1, 'vertical_label_angle' => '90',
'right_axis_label_angle' => '270', 'right_axis_range' => ':100',
];
$data = [];
foreach (['1.10.0', '1.11.0'] as $version) {
$GLOBALS['axis_fixture_version'] = $version;
$options = CactiAxisFixture\rrd_function_process_graph_options(1700000000, 1700003600, $graph, $data);
foreach (['vertical-label-angle', 'right-axis-label-angle', 'right-axis-range'] as $flag) {
expect(substr_count($options, '--' . $flag . ' '))->toBe($version === '1.11.0' ? 1 : 0);
}
}
});
test('aggregate overrides retain zero and reject invalid axis values', function () {
$_SESSION[SESS_ERROR_FIELDS] = [];
$posted = ['t_vertical_label_angle' => 'on', 'vertical_label_angle' => '0',
't_right_axis_range' => 'on', 'right_axis_range' => '100:0'];
$params = aggregate_validate_graph_params($posted, true);
expect($params['vertical_label_angle'])->toBe('0')
->and($params['t_vertical_label_angle'])->toBe('on')
->and($params['right_axis_label_angle'])->toBe('')
->and($params['t_right_axis_label_angle'])->toBe('')
->and($_SESSION[SESS_ERROR_FIELDS])->toHaveKey('right_axis_range');
unset($_SESSION[SESS_ERROR_FIELDS], $_SESSION[SESS_FIELD_VALUES], $_SESSION['messages']);
});
test('new template fields propagate while per graph overrides remain intact', function () {
global $struct_graph;
$previous = $struct_graph ?? null;
require dirname(__DIR__, 4) . '/include/global_arrays.php';
require dirname(__DIR__, 4) . '/include/global_form.php';
$GLOBALS['axis_fixture_template'] = [
'id' => 42, 'graph_template_id' => 7,
't_vertical_label_angle' => '', 'vertical_label_angle' => '90',
't_right_axis_label_angle' => 'on', 'right_axis_label_angle' => '270',
't_right_axis_range' => '', 'right_axis_range' => '0:100',
];
$GLOBALS['axis_fixture_updates'] = [];
try {
expect(CactiAxisFixture\push_out_graph(42, false))->toBeTrue();
$updates = $GLOBALS['axis_fixture_updates'];
expect($updates)->toHaveCount(2)
->and($updates[0][0])->toContain('SET vertical_label_angle = ?')
->and($updates[0][1])->toBe(['90', 42])
->and($updates[1][0])->toContain('SET right_axis_range = ?')
->and($updates[1][1])->toBe(['0:100', 42]);
} finally {
$struct_graph = $previous;
unset($GLOBALS['axis_fixture_template'], $GLOBALS['axis_fixture_updates']);
}
});