mirror of
https://github.com/Cacti/cacti.git
synced 2026-10-25 12:37:47 +09:00
* test(cdef): enforce resolver coverage Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * ci(cdef): pin cache action correctly Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * test: isolate CDEF coverage bootstrap * fix(cdef): fail closed on invalid recursive definitions Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(cdef): log corrupt resolver records Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(cdef): bound and isolate recursive resolution Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(cdef): preserve resolver failure state Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(cdef): isolate invalid graph expressions Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(cdef): abort unsafe consumer operations Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(cdef): close consumer failure paths Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(cdef): make aggregate updates atomic Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(cdef): keep repair queries auditable Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(cdef): bind bulk deletes and escape item types Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(cdef): guard corrupt types and repair reporting Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(aggregate): propagate push-out failures Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> * fix(aggregate): document mutable message checks Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> --------- Signed-off-by: Thomas Vincent <thomasvincent@gmail.com> Co-authored-by: TheWitness <thewitness@cacti.net>
93 lines
4.0 KiB
PHP
93 lines
4.0 KiB
PHP
<?php
|
|
/*
|
|
+-------------------------------------------------------------------------+
|
|
| Copyright (C) 2004-2026 The Cacti Group |
|
|
+-------------------------------------------------------------------------+
|
|
*/
|
|
|
|
/*
|
|
* Cross-file contract test for the aggregate totalling path (#7216).
|
|
*
|
|
* Applying the totalling cdef to GPRINT items is safe only while
|
|
* rrdtool_function_graph() keeps the empty-cdef guard from #6985.
|
|
* The guard skips GPRINT items whose consolidation function does not
|
|
* match the data source instead of emitting a bare "CDEF:cdefX=".
|
|
* These two invariants must hold together.
|
|
*/
|
|
|
|
$root = dirname(__DIR__, 2);
|
|
|
|
test('rrd.php keeps the empty cdef guard that makes full totalling safe', function () use ($root) {
|
|
$src = file_get_contents($root . '/lib/rrd.php');
|
|
expect($src)->not->toBeFalse('Failed to read lib/rrd.php');
|
|
|
|
$pattern = '/if\s*\(\s*\$cdef_string\s*===\s*\'\'\s*\)\s*\{[^}]*continue;/s';
|
|
expect(preg_match($pattern, $src))->toBe(1,
|
|
'the empty cdef guard must stay in lib/rrd.php while totalling covers GPRINT items');
|
|
});
|
|
|
|
test('totalling replaces CURRENT_DATA_SOURCE for both total types', function () use ($root) {
|
|
$src = file_get_contents($root . '/lib/aggregate.php');
|
|
expect($src)->not->toBeFalse('Failed to read lib/aggregate.php');
|
|
|
|
foreach (['SIMILAR_DATA_SOURCES_NODUPS', 'ALL_DATA_SOURCES_NODUPS'] as $replacement) {
|
|
$pattern = "/str_replace\('CURRENT_DATA_SOURCE',\s*'" . $replacement . "'/";
|
|
expect(preg_match($pattern, $src))->toBe(1,
|
|
"totalling must build the $replacement cdef");
|
|
}
|
|
});
|
|
|
|
test('totalling query and cdef update operate on the same item set', function () use ($root) {
|
|
$src = file_get_contents($root . '/lib/aggregate.php');
|
|
expect($src)->not->toBeFalse('Failed to read lib/aggregate.php');
|
|
|
|
$fnPos = strpos($src, 'function aggregate_cdef_totalling(');
|
|
expect($fnPos)->not->toBeFalse('aggregate_cdef_totalling must exist');
|
|
|
|
$fnEnd = strpos($src, "\nfunction ", $fnPos + 1);
|
|
$body = substr($src, $fnPos, ($fnEnd === false ? strlen($src) : $fnEnd) - $fnPos);
|
|
|
|
// every selected item gets the new cdef; no filtering between select and update
|
|
expect(strpos($body, 'SELECT id, cdef_id'))->not->toBeFalse('item selection missing');
|
|
|
|
$pattern = '/UPDATE graph_templates_item\s+SET cdef_id\s*=\s*\?\s+WHERE id\s*=\s*\?/s';
|
|
expect(preg_match($pattern, $body))->toBe(1,
|
|
'cdef update must remain prepared and keyed by item id');
|
|
});
|
|
|
|
test('aggregate caller rolls back and surfaces CDEF totalling failures', function () use ($root) {
|
|
$src = file_get_contents($root . '/lib/api_aggregate.php');
|
|
expect($src)->not->toBeFalse('Failed to read lib/api_aggregate.php');
|
|
|
|
$fnPos = strpos($src, 'function aggregate_create_update(');
|
|
expect($fnPos)->not->toBeFalse('aggregate_create_update must exist');
|
|
|
|
$fnEnd = strpos($src, "\nfunction ", $fnPos + 1);
|
|
$body = substr($src, $fnPos, ($fnEnd === false ? strlen($src) : $fnEnd) - $fnPos);
|
|
|
|
expect($body)->toContain('db_begin_transaction()')
|
|
->and($body)->toContain('if (!aggregate_cdef_totalling(')
|
|
->and($body)->toContain('db_rollback_transaction()')
|
|
->and($body)->toContain("raise_message('aggregate_invalid_cdef'")
|
|
->and($body)->toContain('db_commit_transaction()');
|
|
});
|
|
|
|
test('aggregate callers preserve existing rows and honor update failures', function () use ($root) {
|
|
$api = file_get_contents($root . '/lib/api_aggregate.php');
|
|
$graphs = file_get_contents($root . '/graphs.php');
|
|
|
|
expect($api)->not->toBeFalse()
|
|
->and($graphs)->not->toBeFalse();
|
|
|
|
$pushStart = strpos($api, 'function push_out_aggregates(');
|
|
$pushEnd = strpos($api, "\nfunction ", $pushStart + 1);
|
|
$pushBody = substr($api, $pushStart, $pushEnd - $pushStart);
|
|
|
|
expect($pushBody)->not->toContain('DELETE FROM graph_templates_item')
|
|
->and($pushBody)->toContain('if (!aggregate_create_update(')
|
|
->and($pushBody)->toContain('return false;')
|
|
->and($graphs)->toContain('if (!aggregate_create_update($local_graph_id, $member_graphs, $attribs, false))')
|
|
->and($graphs)->toContain('db_rollback_transaction()')
|
|
->and($graphs)->toContain('db_commit_transaction()');
|
|
});
|