Skip to content

Commit 8b2bbec

Browse files
test(mutation): pin data_input_data column-expansion shape across both write paths
Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
1 parent 31bc931 commit 8b2bbec

1 file changed

Lines changed: 76 additions & 0 deletions

File tree

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?php
2+
/*
3+
+-------------------------------------------------------------------------+
4+
| Copyright (C) 2004-2026 The Cacti Group |
5+
+-------------------------------------------------------------------------+
6+
| Cacti: The Complete RRDtool-based Graphing Solution |
7+
+-------------------------------------------------------------------------+
8+
*/
9+
10+
/*
11+
* Mutation protection for the data_input_data column-expansion fix on
12+
* develop. Two write paths (change_data_template in lib/template.php and
13+
* api_data_source_duplicate in lib/api_data_source.php) had to grow from
14+
* the four-column 1.2.x shape to the seven-column develop shape. A
15+
* single-character mutation against either site is detectable through
16+
* the source-level guards below.
17+
*/
18+
19+
$templateSource = file_get_contents(__DIR__ . '/../../lib/template.php');
20+
$apiDataSourceSource = file_get_contents(__DIR__ . '/../../lib/api_data_source.php');
21+
22+
if (!function_exists('_mut_extract_function')) {
23+
function _mut_extract_function(string $source, string $name): string {
24+
$pattern = '/^function\s+' . preg_quote($name, '/') . '\b[^{]*\{.*?^\}/sm';
25+
return preg_match($pattern, $source, $m) ? $m[0] : '';
26+
}
27+
}
28+
29+
test('change_data_template REPLACE keeps seven placeholders (Mutation Protection)', function () use ($templateSource) {
30+
/* If a mutation narrows the placeholder list back to four `?`, the
31+
* seven-element bind array no longer matches and db_execute_prepared()
32+
* silently fails on every template-to-child copy. */
33+
$body = _mut_extract_function($templateSource, 'change_data_template');
34+
$slice = substr($body, strpos($body, 'REPLACE INTO data_input_data'), 800);
35+
expect(substr_count($slice, '?'))->toBe(7);
36+
});
37+
38+
test('api_data_source_duplicate INSERT keeps seven placeholders (Mutation Protection)', function () use ($apiDataSourceSource) {
39+
$body = _mut_extract_function($apiDataSourceSource, 'api_data_source_duplicate');
40+
$slice = substr($body, strpos($body, 'INSERT IGNORE INTO data_input_data'), 800);
41+
expect(substr_count($slice, '?'))->toBe(7);
42+
});
43+
44+
test('change_data_template binds function-scope identity, not parent template row (Mutation Protection)', function () use ($templateSource) {
45+
/* The whole point of the fix is to stamp the child row with the new
46+
* data source's identity tuple. If a mutation reverts the bind list
47+
* to the parent's `$item['local_data_id']` / `$item['host_id']`
48+
* (always 0 for templates), the child rows go out orphaned again. */
49+
$body = _mut_extract_function($templateSource, 'change_data_template');
50+
$slice = substr($body, strpos($body, 'REPLACE INTO data_input_data'), 800);
51+
52+
expect($slice)->toContain('$data_template_id,');
53+
expect($slice)->toContain('$local_data_id,');
54+
expect($slice)->toContain('$host_id,');
55+
expect(strpos($slice, "\$item['local_data_id']"))->toBeFalse();
56+
expect(strpos($slice, "\$item['host_id']"))->toBeFalse();
57+
});
58+
59+
test('change_data_template looks up host_id from data_local for the new row (Mutation Protection)', function () use ($templateSource) {
60+
/* If a mutation drops the SELECT host_id, we lose the ability to
61+
* stamp the child row with the device the data source actually
62+
* belongs to. */
63+
$body = _mut_extract_function($templateSource, 'change_data_template');
64+
expect($body)->toContain('SELECT host_id');
65+
expect($body)->toContain('FROM data_local');
66+
expect($body)->toContain('[$local_data_id]');
67+
});
68+
69+
test('seven-column INSERT/REPLACE shape is consistent across both write paths (Mutation Protection)', function () use ($templateSource, $apiDataSourceSource) {
70+
/* The two write paths must agree on the column list. A mutation in
71+
* one path only would create a schema-shape skew that downstream
72+
* consumers (update_poller_cache) can't reconcile. */
73+
$expectedColumns = '(data_input_field_id, data_template_data_id, data_template_id, local_data_id, host_id, t_value, value)';
74+
expect($templateSource)->toContain($expectedColumns);
75+
expect($apiDataSourceSource)->toContain($expectedColumns);
76+
});

0 commit comments

Comments
 (0)