From 050474bc90bcd364d1f5b19fb246dd03f75cc5a2 Mon Sep 17 00:00:00 2001 From: TheWitness Date: Mon, 21 Sep 2026 23:45:16 -0400 Subject: [PATCH] test: expand Unit coverage for plugin lifecycle, navigation, and menu setup This plugin already had extensive Security/Unit coverage (auth guards, output escaping, prepared-statement migration, listener port/command security, query building, core formatting helpers). The gap was setup.php's plugin-lifecycle functions, which were only checked for static existence. Closed the safely-testable part of that gap: - plugin_flowview_version() - plugin_flowview_check_config() (the no-config-file branch, which is the only one reachable without a configured Flowview database) - plugin_flowview_upgrade() and plugin_flowview_check_upgrade()'s page-guard - flowview_draw_navigation_text() - flowview_config_arrays() (menu registration under Import/Export) Intentionally left uncovered: plugin_flowview_install(), plugin_flowview_uninstall(), flowview_setup_table(), and flowview_drop_table() all unconditionally call flowview_connect(), which performs a real database connection (or a fatal exit() on failure) and, via plugin_flowview_check_upgrade(), can include_once() Cacti core's real lib/poller.php - the same unconditionally-declared-core-function hazard documented for other plugins' poller_bottom-style functions in this fleet. Those functions are not safely testable without a real Flowview database. No new unserialize() hardening was needed: every unserialize() call in this plugin already goes through sanitize_unserialize_selected_items() or an explicit allowed_classes => false (including in the vendored Net\DNS2 library). tests/bootstrap-unit.php gained api_plugin_register_hook/ api_plugin_register_realm call-logging stubs (the existing plugin_test_reset/plugin_test_queue_db_result/plugin_test_db_result fixture convention was reused as-is). Verified via a real WSL Cacti install (composer-managed Pest, not a plugin-local vendor tree): 77 passed (1200 assertions), up from 71. --- tests/Unit/FlowviewConfigArraysTest.php | 36 +++++++++++++++++++++ tests/Unit/FlowviewLifecycleTest.php | 43 +++++++++++++++++++++++++ tests/Unit/FlowviewNavigationTest.php | 26 +++++++++++++++ tests/Unit/FlowviewVersionTest.php | 23 +++++++++++++ tests/bootstrap-unit.php | 31 ++++++++++++++++++ 5 files changed, 159 insertions(+) create mode 100644 tests/Unit/FlowviewConfigArraysTest.php create mode 100644 tests/Unit/FlowviewLifecycleTest.php create mode 100644 tests/Unit/FlowviewNavigationTest.php create mode 100644 tests/Unit/FlowviewVersionTest.php diff --git a/tests/Unit/FlowviewConfigArraysTest.php b/tests/Unit/FlowviewConfigArraysTest.php new file mode 100644 index 0000000..5f5eeb9 --- /dev/null +++ b/tests/Unit/FlowviewConfigArraysTest.php @@ -0,0 +1,36 @@ + array()); +}); + +it('adds the FlowView menu entries under Import/Export', function () { + global $menu; + + flowview_config_arrays(); + + expect($menu)->toHaveKey('FlowView'); + expect($menu['FlowView'])->toHaveKey('plugins/flowview/flowview_devices.php'); + expect($menu['FlowView'])->toHaveKey('plugins/flowview/flowview_filters.php'); + expect($menu['FlowView'])->toHaveKey('plugins/flowview/flowview_schedules.php'); + expect($menu['FlowView'])->toHaveKey('plugins/flowview/flowview_databases.php'); +}); diff --git a/tests/Unit/FlowviewLifecycleTest.php b/tests/Unit/FlowviewLifecycleTest.php new file mode 100644 index 0000000..7127fe7 --- /dev/null +++ b/tests/Unit/FlowviewLifecycleTest.php @@ -0,0 +1,43 @@ +toBeFalse(); + expect($GLOBALS['__test_messages'])->not->toBeEmpty(); + expect($GLOBALS['__test_messages'][0][0])->toBe('flowview_info'); +}); + +it('always reports no upgrade pending', function () { + expect(plugin_flowview_upgrade())->toBeFalse(); +}); + +it('skips the upgrade check on pages that do not need it', function () { + plugin_flowview_check_upgrade(); + + expect($GLOBALS['__test_db_calls'])->toBeEmpty(); +}); diff --git a/tests/Unit/FlowviewNavigationTest.php b/tests/Unit/FlowviewNavigationTest.php new file mode 100644 index 0000000..d55a768 --- /dev/null +++ b/tests/Unit/FlowviewNavigationTest.php @@ -0,0 +1,26 @@ + array('title' => 'Other'))); + + expect($nav)->toHaveKey('other.php:'); + + foreach (array('flowview.php:', 'flowview.php:view', 'flowview_devices.php:', 'flowview_schedules.php:', 'flowview_filters.php:') as $key) { + expect($nav)->toHaveKey($key); + } + + expect($nav['flowview_devices.php:edit']['mapping'])->toBe('index.php:,flowview_devices.php:'); +}); diff --git a/tests/Unit/FlowviewVersionTest.php b/tests/Unit/FlowviewVersionTest.php new file mode 100644 index 0000000..b9b1fea --- /dev/null +++ b/tests/Unit/FlowviewVersionTest.php @@ -0,0 +1,23 @@ +toBeArray(); + expect($info)->toHaveKey('name'); + expect($info)->toHaveKey('version'); + expect($info['name'])->toBe('flowview'); +}); diff --git a/tests/bootstrap-unit.php b/tests/bootstrap-unit.php index 43f03fa..1e2414b 100644 --- a/tests/bootstrap-unit.php +++ b/tests/bootstrap-unit.php @@ -213,6 +213,37 @@ function api_plugin_db_add_column($plugin, $table, $data) { if (!function_exists('api_plugin_db_table_create')) { function api_plugin_db_table_create($plugin, $table, $data) { + $GLOBALS['__test_db_calls'][] = array('fn' => 'api_plugin_db_table_create', 'sql' => $table, 'params' => $data); + return true; + } +} + +$GLOBALS['__test_registered_hooks'] = array(); + +if (!function_exists('api_plugin_register_hook')) { + function api_plugin_register_hook($plugin, $hook, $function, $file, $subtype = '') { + $GLOBALS['__test_registered_hooks'][] = array( + 'name' => $plugin, + 'hook' => $hook, + 'function' => $function, + 'file' => $file, + ); + + return true; + } +} + +$GLOBALS['__test_registered_realms'] = array(); + +if (!function_exists('api_plugin_register_realm')) { + function api_plugin_register_realm($plugin, $file, $description, $enabled) { + $GLOBALS['__test_registered_realms'][] = array( + 'name' => $plugin, + 'file' => $file, + 'description' => $description, + 'enabled' => $enabled, + ); + return true; } }