diff options
Diffstat (limited to '')
-rw-r--r-- | test/php/Lib/PerfdataSetWithPublicData.php | 12 | ||||
-rw-r--r-- | test/php/application/clicommands/MigrateCommandTest.php | 1727 | ||||
-rw-r--r-- | test/php/library/Icingadb/Common/MacrosTest.php | 174 | ||||
-rw-r--r-- | test/php/library/Icingadb/Common/StateBadgesTest.php | 86 | ||||
-rw-r--r-- | test/php/library/Icingadb/Model/CustomvarFlatTest.php | 121 | ||||
-rw-r--r-- | test/php/library/Icingadb/Util/PerfdataSetTest.php | 120 | ||||
-rw-r--r-- | test/php/library/Icingadb/Util/PerfdataTest.php | 591 | ||||
-rw-r--r-- | test/php/library/Icingadb/Util/ThresholdRangeTest.php | 343 |
8 files changed, 3174 insertions, 0 deletions
diff --git a/test/php/Lib/PerfdataSetWithPublicData.php b/test/php/Lib/PerfdataSetWithPublicData.php new file mode 100644 index 0000000..97fcd7a --- /dev/null +++ b/test/php/Lib/PerfdataSetWithPublicData.php @@ -0,0 +1,12 @@ +<?php + +/* Icinga DB Web | (c) 2023 Icinga GmbH | GPLv2 */ + +namespace Tests\Icinga\Module\Icingadb\Lib; + +use Icinga\Module\Icingadb\Util\PerfDataSet; + +class PerfdataSetWithPublicData extends PerfdataSet +{ + public $perfdata = []; +} diff --git a/test/php/application/clicommands/MigrateCommandTest.php b/test/php/application/clicommands/MigrateCommandTest.php new file mode 100644 index 0000000..2f591ac --- /dev/null +++ b/test/php/application/clicommands/MigrateCommandTest.php @@ -0,0 +1,1727 @@ +<?php + +/* Icinga DB Web | (c) 2023 Icinga GmbH | GPLv2 */ + +namespace Tests\Icinga\Module\Icingadb\Clicommands; + +use Icinga\Application\Cli; +use Icinga\Application\Config; +use Icinga\Application\Modules\Manager; +use Icinga\Cli\Params; +use Icinga\Data\ConfigObject; +use Icinga\Exception\IcingaException; +use Icinga\Exception\MissingParameterException; +use Icinga\File\Storage\TemporaryLocalFileStorage; +use Icinga\Module\Icingadb\Clicommands\MigrateCommand; +use PHPUnit\Framework\TestCase; + +class MigrateCommandTest extends TestCase +{ + protected $config = [ + 'dashboards' => [ + 'initial' => [ + 'hosts' => [ + 'title' => 'Hosts' + ], + 'hosts.problems' => [ + 'title' => 'Host Problems', + 'url' => 'monitoring/list/hosts?host_problem=1' + ], + 'hosts.group_members' => [ + 'title' => 'Group Members', + 'url' => 'monitoring/list/hosts?hostgroup_name=group1|hostgroup_name=%28group2%29' + ], + 'hosts.variables' => [ + 'title' => 'Host Variables', + 'url' => 'monitoring/list/hosts?(_host_foo=bar&_host_bar=foo)|_host_rab=oof' + ], + 'hosts.wildcards' => [ + 'title' => 'Host Wildcards', + 'url' => 'monitoring/list/hosts?host_name=%2Afoo%2A|host_name=%2Abar%2A' + . '&sort=host_severity&dir=asc&limit=25' + ], + 'hosts.encoded_params' => [ + 'title' => 'Host Encoded Params', + 'url' => 'monitoring/list/hosts?host_name=%28foo%29&sort=_host_%28foo%29' + ], + 'icingadb' => [ + 'title' => 'Icinga DB' + ], + 'icingadb.no-wildcards' => [ + 'title' => 'No Wildcards', + 'url' => 'icingadb/hosts?host.state.is_problem=y&hostgroup.name=linux-hosts' + ], + 'icingadb.wildcards' => [ + 'title' => 'Wildcards', + 'url' => 'icingadb/hosts?host.state.is_problem=y&hostgroup.name=%2Alinux%2A' + ], + 'icingadb.also-wildcards' => [ + 'title' => 'Also Wildcards', + 'url' => 'icingadb/hosts?host.name=%2Afoo%2A' + ], + 'icingadb.with-sort-and-limit' => [ + 'title' => 'With Sort And Limit', + 'url' => 'icingadb/hosts?host.name=%2Afoo%2A|host.name=bar&sort=host.state.severity&limit=50' + ], + 'not-monitoring-or-icingadb' => [ + 'title' => 'Not Monitoring Or Icinga DB' + ], + 'not-monitoring-or-icingadb.something' => [ + 'title' => 'Something', + 'url' => 'somewhere/something?foo=%2Abar%2A' + ] + ], + 'expected' => [ + 'hosts' => [ + 'title' => 'Hosts' + ], + 'hosts.problems' => [ + 'title' => 'Host Problems', + 'url' => 'icingadb/hosts?host.state.is_problem=y' + ], + 'hosts.group_members' => [ + 'title' => 'Group Members', + 'url' => 'icingadb/hosts?hostgroup.name=group1|hostgroup.name=%28group2%29' + ], + 'hosts.variables' => [ + 'title' => 'Host Variables', + 'url' => 'icingadb/hosts?(host.vars.foo=bar&host.vars.bar=foo)|host.vars.rab=oof' + ], + 'hosts.wildcards' => [ + 'title' => 'Host Wildcards', + 'url' => 'icingadb/hosts?host.name~%2Afoo%2A|host.name~%2Abar%2A' + . '&sort=host.state.severity%20asc&limit=25' + ], + 'hosts.encoded_params' => [ + 'title' => 'Host Encoded Params', + 'url' => 'icingadb/hosts?host.name=%28foo%29&sort=host.vars.%28foo%29' + ], + 'icingadb' => [ + 'title' => 'Icinga DB' + ], + 'icingadb.no-wildcards' => [ + 'title' => 'No Wildcards', + 'url' => 'icingadb/hosts?host.state.is_problem=y&hostgroup.name=linux-hosts' + ], + 'icingadb.wildcards' => [ + 'title' => 'Wildcards', + 'url' => 'icingadb/hosts?host.state.is_problem=y&hostgroup.name~%2Alinux%2A' + ], + 'icingadb.also-wildcards' => [ + 'title' => 'Also Wildcards', + 'url' => 'icingadb/hosts?host.name~%2Afoo%2A' + ], + 'icingadb.with-sort-and-limit' => [ + 'title' => 'With Sort And Limit', + 'url' => 'icingadb/hosts?host.name~%2Afoo%2A|host.name=bar&sort=host.state.severity&limit=50' + ], + 'not-monitoring-or-icingadb' => [ + 'title' => 'Not Monitoring Or Icinga DB' + ], + 'not-monitoring-or-icingadb.something' => [ + 'title' => 'Something', + 'url' => 'somewhere/something?foo=%2Abar%2A' + ] + ] + ], + 'menu-items' => [ + 'initial' => [ + 'foreign-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'example.com?q=foo' + ], + 'monitoring-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'monitoring/list/hosts?host_problem=1' + ], + 'icingadb-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'icingadb/hosts?host.name=%2Afoo%2A' + ] + ], + 'expected' => [ + 'foreign-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'example.com?q=foo' + ], + 'monitoring-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'icingadb/hosts?host.state.is_problem=y' + ], + 'icingadb-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'icingadb/hosts?host.name~%2Afoo%2A' + ] + ] + ], + 'shared-menu-items' => [ + 'initial' => [ + 'foreign-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'example.com?q=foo', + 'owner' => 'test' + ], + 'monitoring-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'monitoring/list/hosts?host_problem=1', + 'owner' => 'test' + ], + 'icingadb-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'icingadb/hosts?host.name=%2Afoo%2A', + 'owner' => 'test' + ], + 'other-monitoring-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'monitoring/list/hosts?host_problem=1', + 'owner' => 'not-test' + ] + ], + 'expected' => [ + 'foreign-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'example.com?q=foo', + 'owner' => 'test' + ], + 'monitoring-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'icingadb/hosts?host.state.is_problem=y', + 'owner' => 'test' + ], + 'icingadb-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'icingadb/hosts?host.name~%2Afoo%2A', + 'owner' => 'test' + ], + 'other-monitoring-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'monitoring/list/hosts?host_problem=1', + 'owner' => 'not-test' + ] + ] + ], + 'host-actions' => [ + 'initial' => [ + 'hosts' => [ + 'type' => 'host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host_name=%2Afoo%2A' + ], + 'hosts_encoded_params' => [ + 'type' => 'host-action', + 'url' => 'monitoring/list/hosts?host_name=%28foo%29&sort=_host_%28foo%29', + 'filter' => '_host_%28foo%29=bar' + ] + ], + 'expected' => [ + 'hosts' => [ + 'type' => 'icingadb-host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host.name~%2Afoo%2A' + ], + 'hosts_encoded_params' => [ + 'type' => 'icingadb-host-action', + 'url' => 'icingadb/hosts?host.name=%28foo%29&sort=host.vars.%28foo%29', + 'filter' => 'host.vars.%28foo%29=bar' + ] + ] + ], + 'icingadb-host-actions' => [ + 'initial' => [ + 'hosts' => [ + 'type' => 'icingadb-host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host.name=%2Afoo%2A' + ] + ], + 'expected' => [ + 'hosts' => [ + 'type' => 'icingadb-host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host.name~%2Afoo%2A' + ] + ] + ], + 'service-actions' => [ + 'initial' => [ + 'services' => [ + 'type' => 'service-action', + 'url' => 'example.com/search?q=$service.name$,$host.name$,$host.address$,$host.address6$', + 'filter' => '_service_foo=bar&_service_bar=%2Afoo%2A' + ], + 'services_encoded_params' => [ + 'type' => 'host-action', + 'url' => 'monitoring/list/services?host_name=%28foo%29&sort=_host_%28foo%29', + 'filter' => '_host_%28foo%29=bar' + ] + ], + 'expected' => [ + 'services' => [ + 'type' => 'icingadb-service-action', + 'url' => 'example.com/search?q=$service.name$,$host.name$,$host.address$,$host.address6$', + 'filter' => 'service.vars.foo=bar&service.vars.bar~%2Afoo%2A' + ], + 'services_encoded_params' => [ + 'type' => 'icingadb-host-action', + 'url' => 'icingadb/services?host.name=%28foo%29&sort=host.vars.%28foo%29', + 'filter' => 'host.vars.%28foo%29=bar' + ] + ] + ], + 'icingadb-service-actions' => [ + 'initial' => [ + 'services' => [ + 'type' => 'icingadb-service-action', + 'url' => 'example.com/search?q=$service.name$,$host.name$,$host.address$,$host.address6$', + 'filter' => 'service.vars.foo=%2Abar%2A' + ] + ], + 'expected' => [ + 'services' => [ + 'type' => 'icingadb-service-action', + 'url' => 'example.com/search?q=$service.name$,$host.name$,$host.address$,$host.address6$', + 'filter' => 'service.vars.foo~%2Abar%2A' + ] + ] + ], + 'shared-host-actions' => [ + 'initial' => [ + 'shared-hosts' => [ + 'type' => 'host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host_name=%2Afoo%2A', + 'owner' => 'test' + ], + 'hosts_encoded_params' => [ + 'type' => 'host-action', + 'url' => 'monitoring/list/hosts?host_name=%28foo%29&sort=_host_%28foo%29', + 'filter' => '_host_%28foo%29=bar', + 'owner' => 'test' + ], + 'other-hosts' => [ + 'type' => 'host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host_name=%2Afoo%2A', + 'owner' => 'not-test' + ] + ], + 'expected' => [ + 'shared-hosts' => [ + 'type' => 'icingadb-host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host.name~%2Afoo%2A', + 'owner' => 'test' + ], + 'hosts_encoded_params' => [ + 'type' => 'icingadb-host-action', + 'url' => 'icingadb/hosts?host.name=%28foo%29&sort=host.vars.%28foo%29', + 'filter' => 'host.vars.%28foo%29=bar', + 'owner' => 'test' + ] + ] + ], + 'host-actions-legacy-macros' => [ + 'initial' => [ + 'hosts' => [ + 'type' => 'host-action', + 'url' => 'example.com/search?q=$HOSTNAME$,$HOSTADDRESS$,$HOSTADDRESS6$', + 'filter' => 'host_name=%2Afoo%2A' + ] + ], + 'expected' => [ + 'hosts' => [ + 'type' => 'icingadb-host-action', + 'url' => 'example.com/search?q=$host.name$,$host.address$,$host.address6$', + 'filter' => 'host.name~%2Afoo%2A' + ] + ] + ], + 'service-actions-legacy-macros' => [ + 'initial' => [ + 'services' => [ + 'type' => 'service-action', + 'url' => 'example.com/search?q=$SERVICEDESC$,$HOSTNAME$,$HOSTADDRESS$,$HOSTADDRESS6$', + 'filter' => '_service_foo=bar&_service_bar=%2Afoo%2A' + ] + ], + 'expected' => [ + 'services' => [ + 'type' => 'icingadb-service-action', + 'url' => 'example.com/search?q=$service.name$,$host.name$,$host.address$,$host.address6$', + 'filter' => 'service.vars.foo=bar&service.vars.bar~%2Afoo%2A' + ] + ] + ], + 'all-roles' => [ + 'initial' => [ + 'no-wildcards' => [ + 'monitoring/filter/objects' => 'host_name=foo|hostgroup_name=foo' + ], + 'wildcards' => [ + 'monitoring/filter/objects' => 'host_name=%2Afoo%2A|hostgroup_name=%2Afoo%2A' + ], + 'encoded_column' => [ + 'monitoring/filter/objects' => '_host_%28foo%29=bar' + ], + 'blacklist' => [ + 'monitoring/blacklist/properties' => 'host.vars.foo,service.vars.bar*,host.vars.a.**.d' + ], + 'full-access' => [ + 'permissions' => 'module/monitoring,monitoring/*' + ], + 'general-read-access' => [ + 'permissions' => 'module/monitoring' + ], + 'general-write-access' => [ + 'permissions' => 'module/monitoring,monitoring/command/*' + ], + 'full-fine-grained-access' => [ + 'permissions' => 'module/monitoring' + . ',monitoring/command/schedule-check' + . ',monitoring/command/acknowledge-problem' + . ',monitoring/command/remove-acknowledgement' + . ',monitoring/command/comment/add' + . ',monitoring/command/comment/delete' + . ',monitoring/command/downtime/schedule' + . ',monitoring/command/downtime/delete' + . ',monitoring/command/process-check-result' + . ',monitoring/command/feature/instance' + . ',monitoring/command/feature/object/active-checks' + . ',monitoring/command/feature/object/passive-checks' + . ',monitoring/command/feature/object/notifications' + . ',monitoring/command/feature/object/event-handler' + . ',monitoring/command/feature/object/flap-detection' + . ',monitoring/command/send-custom-notification' + ], + 'full-with-refusals' => [ + 'permissions' => 'module/monitoring,monitoring/command/*', + 'refusals' => 'monitoring/command/downtime/*,monitoring/command/feature/instance' + ], + 'active-only' => [ + 'permissions' => 'module/monitoring,monitoring/command/schedule-check/active-only' + ], + 'no-monitoring-contacts' => [ + 'permissions' => 'module/monitoring,no-monitoring/contacts' + ], + 'reporting-only' => [ + 'permissions' => 'module/reporting' + ], + 'icingadb' => [ + 'icingadb/filter/objects' => 'host.name=%2Afoo%2A|hostgroup.name=%2Afoo%2A', + 'icingadb/filter/services' => 'service.name=%2Abar%2A&service.vars.env=prod', + 'icingadb/filter/hosts' => 'host.vars.env=%2Afoo%2A' + ] + ], + 'expected' => [ + 'no-wildcards' => [ + 'monitoring/filter/objects' => 'host_name=foo|hostgroup_name=foo', + 'icingadb/filter/objects' => 'host.name=foo|hostgroup.name=foo' + ], + 'wildcards' => [ + 'monitoring/filter/objects' => 'host_name=%2Afoo%2A|hostgroup_name=%2Afoo%2A', + 'icingadb/filter/objects' => 'host.name~%2Afoo%2A|hostgroup.name~%2Afoo%2A' + ], + 'encoded_column' => [ + 'monitoring/filter/objects' => '_host_%28foo%29=bar', + 'icingadb/filter/objects' => 'host.vars.%28foo%29=bar' + ], + 'blacklist' => [ + 'monitoring/blacklist/properties' => 'host.vars.foo,service.vars.bar*,host.vars.a.**.d', + 'icingadb/denylist/variables' => 'foo,bar*,a.*.d' + ], + 'full-access' => [ + 'permissions' => 'module/monitoring,monitoring/*' + ], + 'general-read-access' => [ + 'permissions' => 'module/monitoring' + ], + 'general-write-access' => [ + 'permissions' => 'module/monitoring,monitoring/command/*,icingadb/command/*' + ], + 'full-fine-grained-access' => [ + 'permissions' => 'module/monitoring' + . ',monitoring/command/schedule-check' + . ',icingadb/command/schedule-check' + . ',monitoring/command/acknowledge-problem' + . ',icingadb/command/acknowledge-problem' + . ',monitoring/command/remove-acknowledgement' + . ',icingadb/command/remove-acknowledgement' + . ',monitoring/command/comment/add' + . ',icingadb/command/comment/add' + . ',monitoring/command/comment/delete' + . ',icingadb/command/comment/delete' + . ',monitoring/command/downtime/schedule' + . ',icingadb/command/downtime/schedule' + . ',monitoring/command/downtime/delete' + . ',icingadb/command/downtime/delete' + . ',monitoring/command/process-check-result' + . ',icingadb/command/process-check-result' + . ',monitoring/command/feature/instance' + . ',icingadb/command/feature/instance' + . ',monitoring/command/feature/object/active-checks' + . ',icingadb/command/feature/object/active-checks' + . ',monitoring/command/feature/object/passive-checks' + . ',icingadb/command/feature/object/passive-checks' + . ',monitoring/command/feature/object/notifications' + . ',icingadb/command/feature/object/notifications' + . ',monitoring/command/feature/object/event-handler' + . ',icingadb/command/feature/object/event-handler' + . ',monitoring/command/feature/object/flap-detection' + . ',icingadb/command/feature/object/flap-detection' + . ',monitoring/command/send-custom-notification' + . ',icingadb/command/send-custom-notification' + ], + 'full-with-refusals' => [ + 'permissions' => 'module/monitoring,monitoring/command/*,icingadb/command/*', + 'refusals' => 'monitoring/command/downtime/*' + . ',icingadb/command/downtime/*' + . ',monitoring/command/feature/instance' + . ',icingadb/command/feature/instance' + ], + 'active-only' => [ + 'permissions' => 'module/monitoring' + . ',monitoring/command/schedule-check/active-only' + . ',icingadb/command/schedule-check/active-only' + ], + 'no-monitoring-contacts' => [ + 'permissions' => 'module/monitoring,no-monitoring/contacts', + 'icingadb/denylist/routes' => 'users,usergroups' + ], + 'reporting-only' => [ + 'permissions' => 'module/reporting' + ], + 'icingadb' => [ + 'icingadb/filter/objects' => 'host.name~%2Afoo%2A|hostgroup.name~%2Afoo%2A', + 'icingadb/filter/services' => 'service.name~%2Abar%2A&service.vars.env=prod', + 'icingadb/filter/hosts' => 'host.vars.env~%2Afoo%2A' + ] + ] + ], + 'single-role-or-group' => [ + 'initial' => [ + 'one' => [ + 'groups' => 'support,helpdesk', + 'monitoring/filter/objects' => 'host_name=foo|hostgroup_name=foo' + ], + 'two' => [ + 'monitoring/filter/objects' => 'host_name=foo|hostgroup_name=foo' + ], + 'three' => [ + 'icingadb/filter/objects' => 'host.name=%2Afoo%2A' + ] + ], + 'expected' => [ + 'one' => [ + 'groups' => 'support,helpdesk', + 'monitoring/filter/objects' => 'host_name=foo|hostgroup_name=foo', + 'icingadb/filter/objects' => 'host.name=foo|hostgroup.name=foo' + ], + 'two' => [ + 'monitoring/filter/objects' => 'host_name=foo|hostgroup_name=foo' + ], + 'three' => [ + 'icingadb/filter/objects' => 'host.name=%2Afoo%2A' + ] + ] + ] + ]; + + protected $defaultConfigDir; + + protected $fileStorage; + + protected function setUp(): void + { + $this->defaultConfigDir = Config::$configDir; + $this->fileStorage = new TemporaryLocalFileStorage(); + + Config::$configDir = dirname($this->fileStorage->resolvePath('bogus')); + } + + protected function tearDown(): void + { + Config::$configDir = $this->defaultConfigDir; + unset($this->fileStorage); // Should clean up automatically + Config::module('monitoring', 'config', true); + } + + protected function getConfig(string $case): array + { + return [$this->config[$case]['initial'], $this->config[$case]['expected']]; + } + + protected function createConfig(string $path, array $data): void + { + $config = new Config(new ConfigObject($data)); + $config->saveIni($this->fileStorage->resolvePath($path)); + } + + protected function loadConfig(string $path): array + { + return Config::fromIni($this->fileStorage->resolvePath($path))->toArray(); + } + + protected function createCommandInstance(string ...$params): MigrateCommand + { + array_unshift($params, 'program'); + + $app = $this->createConfiguredMock(Cli::class, [ + 'getParams' => new Params($params), + 'getModuleManager' => $this->createConfiguredMock(Manager::class, [ + 'loadEnabledModules' => null + ]) + ]); + + return new MigrateCommand( + $app, + 'migrate', + 'toicingadb', + 'dashboard', + false + ); + } + + /** + * Checks the following: + * - Whether only a single user is handled + * - Whether backups are made + * - Whether a second run changes nothing, if nothing changed + * - Whether a second run keeps the backup, if nothing changed + * - Whether a new backup isn't made, if nothing changed + * - Whether existing Icinga DB dashboards are transformed regarding wildcard filters + */ + public function testDashboardMigrationBehavesAsExpectedByDefault() + { + [$initialConfig, $expected] = $this->getConfig('dashboards'); + + $this->createConfig('dashboards/test/dashboard.ini', $initialConfig); + $this->createConfig('dashboards/test2/dashboard.ini', $initialConfig); + + $command = $this->createCommandInstance('--user', 'test'); + $command->dashboardAction(); + + $config = $this->loadConfig('dashboards/test/dashboard.ini'); + $this->assertSame($expected, $config); + + $config2 = $this->loadConfig('dashboards/test2/dashboard.ini'); + $this->assertSame($initialConfig, $config2); + + $backup = $this->loadConfig('dashboards/test/dashboard.backup.ini'); + $this->assertSame($initialConfig, $backup); + + $command = $this->createCommandInstance('--user', 'test'); + $command->dashboardAction(); + + $configAfterSecondRun = $this->loadConfig('dashboards/test/dashboard.ini'); + $this->assertSame($config, $configAfterSecondRun); + + $backupAfterSecondRun = $this->loadConfig('dashboards/test/dashboard.backup.ini'); + $this->assertSame($backup, $backupAfterSecondRun); + + $backup1AfterSecondRun = $this->loadConfig('dashboards/test/dashboard.backup1.ini'); + $this->assertEmpty($backup1AfterSecondRun); + } + + /** + * Checks the following: + * - Whether a second run creates a new backup, if something changed + */ + public function testDashboardMigrationCreatesMultipleBackups() + { + $initialOldConfig = [ + 'hosts' => [ + 'title' => 'Hosts' + ], + 'hosts.problems' => [ + 'title' => 'Host Problems', + 'url' => 'monitoring/list/hosts?host_problem=1' + ] + ]; + $initialNewConfig = [ + 'hosts' => [ + 'title' => 'Hosts' + ], + 'hosts.problems' => [ + 'title' => 'Host Problems', + 'url' => 'icingadb/hosts?host.state.is_problem=y' + ], + 'hosts.group_members' => [ + 'title' => 'Group Members', + 'url' => 'monitoring/list/hosts?hostgroup_name=group1|hostgroup_name=group2' + ] + ]; + $expectedNewConfig = [ + 'hosts' => [ + 'title' => 'Hosts' + ], + 'hosts.problems' => [ + 'title' => 'Host Problems', + 'url' => 'icingadb/hosts?host.state.is_problem=y' + ] + ]; + $expectedFinalConfig = [ + 'hosts' => [ + 'title' => 'Hosts' + ], + 'hosts.problems' => [ + 'title' => 'Host Problems', + 'url' => 'icingadb/hosts?host.state.is_problem=y' + ], + 'hosts.group_members' => [ + 'title' => 'Group Members', + 'url' => 'icingadb/hosts?hostgroup.name=group1|hostgroup.name=group2' + ] + ]; + + $this->createConfig('dashboards/test/dashboard.ini', $initialOldConfig); + + $command = $this->createCommandInstance('--user', 'test'); + $command->dashboardAction(); + + $newConfig = $this->loadConfig('dashboards/test/dashboard.ini'); + $this->assertSame($expectedNewConfig, $newConfig); + $oldBackup = $this->loadConfig('dashboards/test/dashboard.backup.ini'); + $this->assertSame($initialOldConfig, $oldBackup); + + $this->createConfig('dashboards/test/dashboard.ini', $initialNewConfig); + + $command = $this->createCommandInstance('--user', 'test'); + $command->dashboardAction(); + + $finalConfig = $this->loadConfig('dashboards/test/dashboard.ini'); + $this->assertSame($expectedFinalConfig, $finalConfig); + $newBackup = $this->loadConfig('dashboards/test/dashboard.backup1.ini'); + $this->assertSame($initialNewConfig, $newBackup); + } + + /** + * Checks the following: + * - Whether backups are skipped + * + * @depends testDashboardMigrationBehavesAsExpectedByDefault + */ + public function testDashboardMigrationSkipsBackupIfRequested() + { + [$initialConfig, $expected] = $this->getConfig('dashboards'); + + $this->createConfig('dashboards/test/dashboard.ini', $initialConfig); + + $command = $this->createCommandInstance('--user', 'test', '--no-backup'); + $command->dashboardAction(); + + $config = $this->loadConfig('dashboards/test/dashboard.ini'); + $this->assertSame($expected, $config); + + $backup = $this->loadConfig('dashboards/test/dashboard.backup.ini'); + $this->assertEmpty($backup); + } + + /** + * Checks the following: + * - Whether multiple users are handled + * - Whether multiple backups are made + * + * @depends testDashboardMigrationBehavesAsExpectedByDefault + */ + public function testDashboardMigrationMigratesAllUsers() + { + [$initialConfig, $expected] = $this->getConfig('dashboards'); + + $users = ['foo', 'bar', 'raboof']; + + foreach ($users as $user) { + $this->createConfig("dashboards/$user/dashboard.ini", $initialConfig); + } + + $command = $this->createCommandInstance('--user', '*'); + $command->dashboardAction(); + + foreach ($users as $user) { + $config = $this->loadConfig("dashboards/$user/dashboard.ini"); + $this->assertSame($expected, $config); + + $backup = $this->loadConfig("dashboards/$user/dashboard.backup.ini"); + $this->assertSame($initialConfig, $backup); + } + } + + public function testDashboardMigrationExpectsUserSwitch() + { + $this->expectException(MissingParameterException::class); + $this->expectExceptionMessage('Required parameter \'user\' missing'); + + $command = $this->createCommandInstance(); + $command->dashboardAction(); + } + + /** + * Checks the following: + * - Whether only a single user is handled + * - Whether shared items are migrated, depending on the owner + * - Whether old configs are kept/or backups are created + * - Whether a second run changes nothing, if nothing changed + * - Whether a second run keeps the backup, if nothing changed + * - Whether a new backup isn't created, if nothing changed + */ + public function testNavigationMigrationBehavesAsExpectedByDefault() + { + [$initialMenuConfig, $expectedMenu] = $this->getConfig('menu-items'); + [$initialHostConfig, $expectedHosts] = $this->getConfig('host-actions'); + [$initialServiceConfig, $expectedServices] = $this->getConfig('service-actions'); + + $this->createConfig('preferences/test/menu.ini', $initialMenuConfig); + $this->createConfig('preferences/test/host-actions.ini', $initialHostConfig); + $this->createConfig('preferences/test/service-actions.ini', $initialServiceConfig); + $this->createConfig('preferences/test2/menu.ini', $initialMenuConfig); + $this->createConfig('preferences/test2/host-actions.ini', $initialHostConfig); + $this->createConfig('preferences/test2/service-actions.ini', $initialServiceConfig); + + [$initialSharedMenuConfig, $expectedSharedMenu] = $this->getConfig('shared-menu-items'); + $this->createConfig('navigation/menu.ini', $initialSharedMenuConfig); + + [$initialSharedConfig, $expectedShared] = $this->getConfig('shared-host-actions'); + $this->createConfig('navigation/host-actions.ini', $initialSharedConfig); + + $command = $this->createCommandInstance('--user', 'test'); + $command->navigationAction(); + + $menuConfig = $this->loadConfig('preferences/test/menu.ini'); + $this->assertSame($expectedMenu, $menuConfig); + + $sharedMenuConfig = $this->loadConfig('navigation/menu.ini'); + $this->assertSame($expectedSharedMenu, $sharedMenuConfig); + + $menuConfig2 = $this->loadConfig('preferences/test2/menu.ini'); + $this->assertSame($initialMenuConfig, $menuConfig2); + + $menuBackup = $this->loadConfig('preferences/test/menu.backup.ini'); + $this->assertSame($initialMenuConfig, $menuBackup); + + $hosts = $this->loadConfig('preferences/test/icingadb-host-actions.ini'); + $services = $this->loadConfig('preferences/test/icingadb-service-actions.ini'); + $this->assertSame($expectedHosts, $hosts); + $this->assertSame($expectedServices, $services); + + $sharedConfig = $this->loadConfig('navigation/icingadb-host-actions.ini'); + $this->assertSame($expectedShared, $sharedConfig); + + $hosts2 = $this->loadConfig('preferences/test2/icingadb-host-actions.ini'); + $services2 = $this->loadConfig('preferences/test2/icingadb-service-actions.ini'); + $this->assertEmpty($hosts2); + $this->assertEmpty($services2); + + $oldHosts = $this->loadConfig('preferences/test/host-actions.ini'); + $oldServices = $this->loadConfig('preferences/test/service-actions.ini'); + $this->assertSame($initialHostConfig, $oldHosts); + $this->assertSame($initialServiceConfig, $oldServices); + + $command = $this->createCommandInstance('--user', 'test'); + $command->navigationAction(); + + $menuConfigAfterSecondRun = $this->loadConfig('preferences/test/menu.ini'); + $this->assertSame($menuConfig, $menuConfigAfterSecondRun); + + $menuBackupAfterSecondRun = $this->loadConfig('preferences/test/menu.backup.ini'); + $this->assertSame($menuBackup, $menuBackupAfterSecondRun); + + $menuBackup1AfterSecondRun = $this->loadConfig('preferences/test/menu.backup1.ini'); + $this->assertEmpty($menuBackup1AfterSecondRun); + + $hostsAfterSecondRun = $this->loadConfig('preferences/test/icingadb-host-actions.ini'); + $servicesAfterSecondRun = $this->loadConfig('preferences/test/icingadb-service-actions.ini'); + $this->assertSame($hosts, $hostsAfterSecondRun); + $this->assertSame($services, $servicesAfterSecondRun); + } + + /** + * Checks the following: + * - Whether a second run creates a new backup, if something changed + * + * @depends testNavigationMigrationBehavesAsExpectedByDefault + */ + public function testNavigationMigrationCreatesMultipleBackups() + { + $initialOldConfig = [ + 'hosts' => [ + 'title' => 'Host Problems', + 'url' => 'monitoring/list/hosts?host_problem=1' + ] + ]; + $initialNewConfig = [ + 'hosts' => [ + 'title' => 'Host Problems', + 'url' => 'icingadb/hosts?host.state.is_problem=y' + ], + 'group_members' => [ + 'title' => 'Group Members', + 'url' => 'monitoring/list/hosts?hostgroup_name=group1|hostgroup_name=group2' + ] + ]; + $expectedNewConfig = [ + 'hosts' => [ + 'title' => 'Host Problems', + 'url' => 'icingadb/hosts?host.state.is_problem=y' + ] + ]; + $expectedFinalConfig = [ + 'hosts' => [ + 'title' => 'Host Problems', + 'url' => 'icingadb/hosts?host.state.is_problem=y' + ], + 'group_members' => [ + 'title' => 'Group Members', + 'url' => 'icingadb/hosts?hostgroup.name=group1|hostgroup.name=group2' + ] + ]; + + $this->createConfig('preferences/test/menu.ini', $initialOldConfig); + + $command = $this->createCommandInstance('--user', 'test'); + $command->navigationAction(); + + $newConfig = $this->loadConfig('preferences/test/menu.ini'); + $this->assertSame($expectedNewConfig, $newConfig); + $oldBackup = $this->loadConfig('preferences/test/menu.backup.ini'); + $this->assertSame($initialOldConfig, $oldBackup); + + $this->createConfig('preferences/test/menu.ini', $initialNewConfig); + + $command = $this->createCommandInstance('--user', 'test'); + $command->navigationAction(); + + $finalConfig = $this->loadConfig('preferences/test/menu.ini'); + $this->assertSame($expectedFinalConfig, $finalConfig); + $newBackup = $this->loadConfig('preferences/test/menu.backup1.ini'); + $this->assertSame($initialNewConfig, $newBackup); + } + + /** + * Checks the following: + * - Whether backups are skipped + * + * @depends testNavigationMigrationBehavesAsExpectedByDefault + */ + public function testNavigationMigrationSkipsBackupIfRequested() + { + [$initialConfig, $expected] = $this->getConfig('menu-items'); + + $this->createConfig('preferences/test/menu.ini', $initialConfig); + + $command = $this->createCommandInstance('--user', 'test', '--no-backup'); + $command->navigationAction(); + + $config = $this->loadConfig('preferences/test/menu.ini'); + $this->assertSame($expected, $config); + + $backup = $this->loadConfig('preferences/test/menu.backup.ini'); + $this->assertEmpty($backup); + } + + /** + * Checks the following: + * - Whether existing Icinga DB Actions are transformed regarding wildcard filters + */ + public function testNavigationMigrationTransformsAlreadyExistingIcingaDBActions() + { + [$initialHostConfig, $expectedHosts] = $this->getConfig('icingadb-host-actions'); + [$initialServiceConfig, $expectedServices] = $this->getConfig('icingadb-service-actions'); + + $this->createConfig('preferences/test/icingadb-host-actions.ini', $initialHostConfig); + $this->createConfig('preferences/test/icingadb-service-actions.ini', $initialServiceConfig); + + $command = $this->createCommandInstance('--user', 'test'); + $command->navigationAction(); + + $hosts = $this->loadConfig('preferences/test/icingadb-host-actions.ini'); + $services = $this->loadConfig('preferences/test/icingadb-service-actions.ini'); + $this->assertSame($expectedHosts, $hosts); + $this->assertSame($expectedServices, $services); + + $command = $this->createCommandInstance('--user', 'test'); + $command->navigationAction(); + + $hostsAfterSecondRun = $this->loadConfig('preferences/test/icingadb-host-actions.ini'); + $servicesAfterSecondRun = $this->loadConfig('preferences/test/icingadb-service-actions.ini'); + $this->assertSame($hosts, $hostsAfterSecondRun); + $this->assertSame($services, $servicesAfterSecondRun); + } + + /** + * Checks the following: + * - Whether legacy host/service macros are migrated + */ + public function testNavigationMigrationMigratesLegacyMacros() + { + [$initialHostConfig, $expectedHosts] = $this->getConfig('host-actions-legacy-macros'); + [$initialServiceConfig, $expectedServices] = $this->getConfig('service-actions-legacy-macros'); + + $this->createConfig('preferences/test/host-actions.ini', $initialHostConfig); + $this->createConfig('preferences/test/service-actions.ini', $initialServiceConfig); + + $command = $this->createCommandInstance('--user', 'test'); + $command->navigationAction(); + + $hosts = $this->loadConfig('preferences/test/icingadb-host-actions.ini'); + $services = $this->loadConfig('preferences/test/icingadb-service-actions.ini'); + $this->assertSame($expectedHosts, $hosts); + $this->assertSame($expectedServices, $services); + } + + /** + * Checks the following: + * - Whether old configs are removed + */ + public function testNavigationMigrationDeletesOldConfigsIfRequested() + { + [$initialHostConfig, $expectedHosts] = $this->getConfig('host-actions'); + [$initialServiceConfig, $expectedServices] = $this->getConfig('service-actions'); + + $this->createConfig('preferences/test/host-actions.ini', $initialHostConfig); + $this->createConfig('preferences/test/service-actions.ini', $initialServiceConfig); + + $command = $this->createCommandInstance('--user', 'test', '--no-backup'); + $command->navigationAction(); + + $hosts = $this->loadConfig('preferences/test/icingadb-host-actions.ini'); + $services = $this->loadConfig('preferences/test/icingadb-service-actions.ini'); + $this->assertSame($expectedHosts, $hosts); + $this->assertSame($expectedServices, $services); + + $oldHosts = $this->loadConfig('preferences/test/host-actions.ini'); + $oldServices = $this->loadConfig('preferences/test/service-actions.ini'); + $this->assertEmpty($oldHosts); + $this->assertEmpty($oldServices); + } + + /** + * Checks the following: + * - Whether existing configs are left alone by default + * - Whether existing configs are overridden if requested + */ + public function testNavigationMigrationOverridesExistingActionsIfRequested() + { + $initialOldUserConfig = [ + 'hosts' => [ + 'type' => 'host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host_name=%2Afoo%2A' + ] + ]; + $initialOldSharedConfig = [ + 'hosts' => [ + 'type' => 'host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host_name=%2Afoo%2A', + 'owner' => 'test' + ] + ]; + $initialNewUserConfig = [ + 'hosts' => [ + 'type' => 'icingadb-host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host.name~%2Abar%2A' + ] + ]; + $initialNewSharedConfig = [ + 'hosts' => [ + 'type' => 'icingadb-host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host.name~%2Abar%2A', + 'owner' => 'test' + ] + ]; + $expectedFinalUserConfig = [ + 'hosts' => [ + 'type' => 'icingadb-host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host.name~%2Afoo%2A' + ] + ]; + $expectedFinalSharedConfig = [ + 'hosts' => [ + 'type' => 'icingadb-host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host.name~%2Afoo%2A', + 'owner' => 'test' + ] + ]; + + $this->createConfig('preferences/test/host-actions.ini', $initialOldUserConfig); + $this->createConfig('preferences/test/icingadb-host-actions.ini', $initialNewUserConfig); + $this->createConfig('navigation/host-actions.ini', $initialOldSharedConfig); + $this->createConfig('navigation/icingadb-host-actions.ini', $initialNewSharedConfig); + + $command = $this->createCommandInstance('--user', 'test'); + $command->navigationAction(); + + $finalUserConfig = $this->loadConfig('preferences/test/icingadb-host-actions.ini'); + $this->assertSame($initialNewUserConfig, $finalUserConfig); + + $finalSharedConfig = $this->loadConfig('navigation/icingadb-host-actions.ini'); + $this->assertSame($initialNewSharedConfig, $finalSharedConfig); + + $command = $this->createCommandInstance('--user', 'test', '--override'); + $command->navigationAction(); + + $finalUserConfig = $this->loadConfig('preferences/test/icingadb-host-actions.ini'); + $this->assertSame($expectedFinalUserConfig, $finalUserConfig); + + $finalSharedConfig = $this->loadConfig('navigation/icingadb-host-actions.ini'); + $this->assertSame($expectedFinalSharedConfig, $finalSharedConfig); + } + + public function testNavigationMigrationExpectsUserSwitch() + { + $this->expectException(MissingParameterException::class); + $this->expectExceptionMessage('Required parameter \'user\' missing'); + + $command = $this->createCommandInstance(); + $command->navigationAction(); + } + + /** + * Checks the following: + * - Whether only a single role is handled + * - Whether role name matching works + */ + public function testRoleMigrationHandlesASingleRoleOnlyIfRequested() + { + [$initialConfig, $expected] = $this->getConfig('single-role-or-group'); + + $this->createConfig('roles.ini', $initialConfig); + + $command = $this->createCommandInstance('--role', 'one'); + $command->roleAction(); + + $config = $this->loadConfig('roles.ini'); + $this->assertSame($expected, $config); + } + + /** + * Checks the following: + * - Whether only a single role is handled + * - Whether group matching works + */ + public function testRoleMigrationHandlesARoleWithMatchingGroups() + { + [$initialConfig, $expected] = $this->getConfig('single-role-or-group'); + + $this->createConfig('roles.ini', $initialConfig); + + $command = $this->createCommandInstance('--group', 'support'); + $command->roleAction(); + + $config = $this->loadConfig('roles.ini'); + $this->assertSame($expected, $config); + } + + /** + * Checks the following: + * - Whether permissions are properly migrated + * - Whether refusals are properly migrated + * - Whether restrictions are properly migrated + * - Whether blacklists are properly migrated + * - Whether backups are created + * - Whether a second run changes nothing, if nothing changed + * - Whether a second run keeps the backup, if nothing changed + * - Whether a new backup isn't created, if nothing changed + */ + public function testRoleMigrationMigratesAllRoles() + { + [$initialConfig, $expected] = $this->getConfig('all-roles'); + + $this->createConfig('roles.ini', $initialConfig); + + $command = $this->createCommandInstance('--role', '*'); + $command->roleAction(); + + $config = $this->loadConfig('roles.ini'); + $this->assertSame($expected, $config); + + $backup = $this->loadConfig('roles.backup.ini'); + $this->assertSame($initialConfig, $backup); + + $command = $this->createCommandInstance('--role', '*'); + $command->roleAction(); + + $configAfterSecondRun = $this->loadConfig('roles.ini'); + $this->assertSame($config, $configAfterSecondRun); + + $backupAfterSecondRun = $this->loadConfig('roles.backup.ini'); + $this->assertSame($backup, $backupAfterSecondRun); + + $backup2 = $this->loadConfig('roles.backup1.ini'); + $this->assertEmpty($backup2); + } + + /** + * Checks the following: + * - Whether backups are skipped + * + * @depends testRoleMigrationMigratesAllRoles + */ + public function testRoleMigrationSkipsBackupIfRequested() + { + [$initialConfig, $expected] = $this->getConfig('all-roles'); + + $this->createConfig('roles.ini', $initialConfig); + + $command = $this->createCommandInstance('--role', '*', '--no-backup'); + $command->roleAction(); + + $config = $this->loadConfig('roles.ini'); + $this->assertSame($expected, $config); + + $backup = $this->loadConfig('roles.backup.ini'); + $this->assertEmpty($backup); + } + + /** + * Checks the following: + * - Whether monitoring's variable protection rules are migrated to all roles granting access to monitoring + */ + public function testRoleMigrationAlsoMigratesVariableProtections() + { + $initialConfig = [ + 'one' => [ + 'permissions' => 'module/monitoring' + ], + 'two' => [ + 'permissions' => 'module/monitoring' + ], + 'three' => [ + 'permissions' => 'module/reporting' + ] + ]; + $expectedConfig = [ + 'one' => [ + 'permissions' => 'module/monitoring', + 'icingadb/protect/variables' => 'ob.*,env' + ], + 'two' => [ + 'permissions' => 'module/monitoring', + 'icingadb/protect/variables' => 'ob.*,env' + ], + 'three' => [ + 'permissions' => 'module/reporting' + ] + ]; + + $this->createConfig('modules/monitoring/config.ini', [ + 'security' => [ + 'protected_customvars' => 'ob.*,env' + ] + ]); + + // Invalidate config cache + Config::module('monitoring', 'config', true); + + $this->createConfig('roles.ini', $initialConfig); + + $command = $this->createCommandInstance('--role', '*'); + $command->roleAction(); + + $config = $this->loadConfig('roles.ini'); + $this->assertSame($expectedConfig, $config); + } + + /** + * Checks the following: + * - Whether already migrated roles are skipped during migration + * - Whether already migrated roles are transformed regarding wildcard filters + */ + public function testRoleMigrationSkipsRolesThatAlreadyGrantAccessToIcingaDbButTransformWildcardRestrictions() + { + $initialConfig = [ + 'only-monitoring' => [ + 'permissions' => 'module/monitoring,monitoring/command/comment/*', + 'monitoring/filter/objects' => 'host_name=%2Afoo%2A' + ], + 'monitoring-and-icingadb' => [ + 'permissions' => 'module/monitoring,monitoring/command/comment/*,module/icingadb', + 'monitoring/filter/objects' => 'host_name=%2Abar%2A', + 'icingadb/filter/objects' => 'host.name=%2Afoo%2A' + ] + ]; + $expectedConfig = [ + 'only-monitoring' => [ + 'permissions' => 'module/monitoring,monitoring/command/comment/*,icingadb/command/comment/*', + 'monitoring/filter/objects' => 'host_name=%2Afoo%2A', + 'icingadb/filter/objects' => 'host.name~%2Afoo%2A' + ], + 'monitoring-and-icingadb' => [ + 'permissions' => 'module/monitoring,monitoring/command/comment/*,module/icingadb', + 'monitoring/filter/objects' => 'host_name=%2Abar%2A', + 'icingadb/filter/objects' => 'host.name~%2Afoo%2A' + ] + ]; + + $this->createConfig('roles.ini', $initialConfig); + + $command = $this->createCommandInstance('--role', '*'); + $command->roleAction(); + + $config = $this->loadConfig('roles.ini'); + $this->assertSame($expectedConfig, $config); + } + + /** + * Checks the following: + * - Whether already migrated roles are reset if requested + */ + public function testRoleMigrationOverridesAlreadyMigratedRolesIfRequested() + { + $initialConfig = [ + 'only-monitoring' => [ + 'permissions' => 'module/monitoring,monitoring/command/comment/*', + 'monitoring/filter/objects' => 'host_name=%2Afoo%2A' + ], + 'monitoring-and-icingadb' => [ + 'permissions' => 'module/monitoring,monitoring/command/comment/*,module/icingadb', + 'monitoring/filter/objects' => 'host_name=%2Abar%2A', + 'icingadb/filter/objects' => 'host.name=%2Afoo%2A' + ] + ]; + $expectedConfig = [ + 'only-monitoring' => [ + 'permissions' => 'module/monitoring,monitoring/command/comment/*,icingadb/command/comment/*', + 'monitoring/filter/objects' => 'host_name=%2Afoo%2A', + 'icingadb/filter/objects' => 'host.name~%2Afoo%2A' + ], + 'monitoring-and-icingadb' => [ + 'permissions' => 'module/monitoring' + . ',monitoring/command/comment/*' + . ',icingadb/command/comment/*', + 'monitoring/filter/objects' => 'host_name=%2Abar%2A', + 'icingadb/filter/objects' => 'host.name~%2Abar%2A' + ] + ]; + + $this->createConfig('roles.ini', $initialConfig); + + $command = $this->createCommandInstance('--role', '*', '--override'); + $command->roleAction(); + + $config = $this->loadConfig('roles.ini'); + $this->assertSame($expectedConfig, $config); + } + + public function testRoleMigrationExpectsTheRoleOrGroupSwitch() + { + $this->expectException(IcingaException::class); + $this->expectExceptionMessage("One of the parameters 'group' or 'role' must be supplied"); + + $command = $this->createCommandInstance(); + $command->roleAction(); + } + + public function testRoleMigrationExpectsEitherTheRoleOrGroupSwitchButNotBoth() + { + $this->expectException(IcingaException::class); + $this->expectExceptionMessage("Use either 'group' or 'role'. Both cannot be used as role overrules group."); + + $command = $this->createCommandInstance('--role=foo', '--group=bar'); + $command->roleAction(); + } + + public function testFilterMigrationWorksAsExpected() + { + $initialHostActionConfig = [ + 'hosts' => [ + 'type' => 'host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host_name=%2Afoo%2A' + ] + ]; + $expectedHostActionConfig = $initialHostActionConfig; + + $initialIcingadbHostActionConfig = [ + 'hosts' => [ + 'type' => 'icingadb-host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host.name=%2Afoo%2A' + ] + ]; + $expectedIcingadbHostActionConfig = [ + 'hosts' => [ + 'type' => 'icingadb-host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host.name~%2Afoo%2A' + ] + ]; + + $initialServiceActionConfig = [ + 'services' => [ + 'type' => 'service-action', + 'url' => 'example.com/search?q=$service.name$,$host.name$,$host.address$,$host.address6$', + 'filter' => '_service_foo=bar&_service_bar=%2Afoo%2A' + ] + ]; + $expectedServiceActionConfig = $initialServiceActionConfig; + + $initialIcingadbServiceActionConfig = [ + 'services' => [ + 'type' => 'icingadb-service-action', + 'url' => 'example.com/search?q=$service.name$,$host.name$,$host.address$,$host.address6$', + 'filter' => 'service.vars.foo=bar&service.vars.bar=%2Afoo%2A' + ] + ]; + $expectedIcingadbServiceActionConfig = [ + 'services' => [ + 'type' => 'icingadb-service-action', + 'url' => 'example.com/search?q=$service.name$,$host.name$,$host.address$,$host.address6$', + 'filter' => 'service.vars.foo=bar&service.vars.bar~%2Afoo%2A' + ] + ]; + + $initialMenuConfig = [ + 'foreign-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'example.com?q=foo' + ], + 'monitoring-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'monitoring/list/hosts?host_problem=1' + ], + 'icingadb-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'icingadb/hosts?host.name=%2Afoo%2A' + ] + ]; + $expectedMenuConfig = [ + 'foreign-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'example.com?q=foo' + ], + 'monitoring-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'monitoring/list/hosts?host_problem=1' + ], + 'icingadb-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'icingadb/hosts?host.name~%2Afoo%2A' + ] + ]; + + $initialDashboardConfig = [ + 'hosts' => [ + 'title' => 'Hosts' + ], + 'hosts.problems' => [ + 'title' => 'Host Problems', + 'url' => 'monitoring/list/hosts?host_problem=1' + ], + 'icingadb' => [ + 'title' => 'Icinga DB' + ], + 'icingadb.wildcards' => [ + 'title' => 'Wildcards', + 'url' => 'icingadb/hosts?host.state.is_problem=y&hostgroup.name=%2Alinux%2A' + ] + ]; + $expectedDashboardConfig = [ + 'hosts' => [ + 'title' => 'Hosts' + ], + 'hosts.problems' => [ + 'title' => 'Host Problems', + 'url' => 'monitoring/list/hosts?host_problem=1' + ], + 'icingadb' => [ + 'title' => 'Icinga DB' + ], + 'icingadb.wildcards' => [ + 'title' => 'Wildcards', + 'url' => 'icingadb/hosts?host.state.is_problem=y&hostgroup.name~%2Alinux%2A' + ] + ]; + + $initialRoleConfig = [ + 'one' => [ + 'groups' => 'support,helpdesk', + 'monitoring/filter/objects' => 'host_name=foo|hostgroup_name=foo' + ], + 'two' => [ + 'monitoring/filter/objects' => 'host_name=foo|hostgroup_name=foo' + ], + 'three' => [ + 'icingadb/filter/objects' => 'host.name=%2Afoo%2A' + ] + ]; + $expectedRoleConfig = [ + 'one' => [ + 'groups' => 'support,helpdesk', + 'monitoring/filter/objects' => 'host_name=foo|hostgroup_name=foo' + ], + 'two' => [ + 'monitoring/filter/objects' => 'host_name=foo|hostgroup_name=foo' + ], + 'three' => [ + 'icingadb/filter/objects' => 'host.name~%2Afoo%2A' + ] + ]; + + $this->createConfig('preferences/test/host-actions.ini', $initialHostActionConfig); + $this->createConfig('preferences/test/service-actions.ini', $initialServiceActionConfig); + $this->createConfig('preferences/test/icingadb-host-actions.ini', $initialIcingadbHostActionConfig); + $this->createConfig('preferences/test/icingadb-service-actions.ini', $initialIcingadbServiceActionConfig); + $this->createConfig('dashboards/test/dashboard.ini', $initialDashboardConfig); + $this->createConfig('preferences/test/menu.ini', $initialMenuConfig); + $this->createConfig('roles.ini', $initialRoleConfig); + + $command = $this->createCommandInstance(); + $command->filterAction(); + + $hostActionConfig = $this->loadConfig('preferences/test/host-actions.ini'); + $serviceActionConfig = $this->loadConfig('preferences/test/service-actions.ini'); + $icingadbHostActionConfig = $this->loadConfig('preferences/test/icingadb-host-actions.ini'); + $icingadbServiceActionConfig = $this->loadConfig('preferences/test/icingadb-service-actions.ini'); + $dashboardBackup = $this->loadConfig('dashboards/test/dashboard.backup.ini'); + $dashboardConfig = $this->loadConfig('dashboards/test/dashboard.ini'); + $menuBackup = $this->loadConfig('preferences/test/menu.backup.ini'); + $menuConfig = $this->loadConfig('preferences/test/menu.ini'); + $roleBackup = $this->loadConfig('roles.backup.ini'); + $roleConfig = $this->loadConfig('roles.ini'); + + $this->assertSame($expectedHostActionConfig, $hostActionConfig); + $this->assertSame($expectedServiceActionConfig, $serviceActionConfig); + $this->assertSame($initialDashboardConfig, $dashboardBackup); + $this->assertSame($initialMenuConfig, $menuBackup); + $this->assertSame($initialRoleConfig, $roleBackup); + + $this->assertSame($expectedIcingadbHostActionConfig, $icingadbHostActionConfig); + $this->assertSame($expectedIcingadbServiceActionConfig, $icingadbServiceActionConfig); + $this->assertSame($expectedDashboardConfig, $dashboardConfig); + $this->assertSame($expectedMenuConfig, $menuConfig); + $this->assertSame($expectedRoleConfig, $roleConfig); + } + + /** + * @depends testFilterMigrationWorksAsExpected + */ + public function testFilterMigrationSkipsBackupsIfRequested() + { + $initialHostActionConfig = [ + 'hosts' => [ + 'type' => 'host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host_name=%2Afoo%2A' + ] + ]; + $expectedHostActionConfig = $initialHostActionConfig; + + $initialIcingadbHostActionConfig = [ + 'hosts' => [ + 'type' => 'icingadb-host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host.name=%2Afoo%2A' + ] + ]; + $expectedIcingadbHostActionConfig = [ + 'hosts' => [ + 'type' => 'icingadb-host-action', + 'url' => 'example.com/search?q=$host.name$', + 'filter' => 'host.name~%2Afoo%2A' + ] + ]; + + $initialServiceActionConfig = [ + 'services' => [ + 'type' => 'service-action', + 'url' => 'example.com/search?q=$service.name$,$host.name$,$host.address$,$host.address6$', + 'filter' => '_service_foo=bar&_service_bar=%2Afoo%2A' + ] + ]; + $expectedServiceActionConfig = $initialServiceActionConfig; + + $initialIcingadbServiceActionConfig = [ + 'services' => [ + 'type' => 'icingadb-service-action', + 'url' => 'example.com/search?q=$service.name$,$host.name$,$host.address$,$host.address6$', + 'filter' => 'service.vars.foo=bar&service.vars.bar=%2Afoo%2A' + ] + ]; + $expectedIcingadbServiceActionConfig = [ + 'services' => [ + 'type' => 'icingadb-service-action', + 'url' => 'example.com/search?q=$service.name$,$host.name$,$host.address$,$host.address6$', + 'filter' => 'service.vars.foo=bar&service.vars.bar~%2Afoo%2A' + ] + ]; + + $initialMenuConfig = [ + 'foreign-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'example.com?q=foo' + ], + 'monitoring-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'monitoring/list/hosts?host_problem=1' + ], + 'icingadb-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'icingadb/hosts?host.name=%2Afoo%2A' + ] + ]; + $expectedMenuConfig = [ + 'foreign-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'example.com?q=foo' + ], + 'monitoring-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'monitoring/list/hosts?host_problem=1' + ], + 'icingadb-url' => [ + 'type' => 'menu-item', + 'target' => '_blank', + 'url' => 'icingadb/hosts?host.name~%2Afoo%2A' + ] + ]; + + $initialDashboardConfig = [ + 'hosts' => [ + 'title' => 'Hosts' + ], + 'hosts.problems' => [ + 'title' => 'Host Problems', + 'url' => 'monitoring/list/hosts?host_problem=1' + ], + 'icingadb' => [ + 'title' => 'Icinga DB' + ], + 'icingadb.wildcards' => [ + 'title' => 'Wildcards', + 'url' => 'icingadb/hosts?host.state.is_problem=y&hostgroup.name=%2Alinux%2A' + ] + ]; + $expectedDashboardConfig = [ + 'hosts' => [ + 'title' => 'Hosts' + ], + 'hosts.problems' => [ + 'title' => 'Host Problems', + 'url' => 'monitoring/list/hosts?host_problem=1' + ], + 'icingadb' => [ + 'title' => 'Icinga DB' + ], + 'icingadb.wildcards' => [ + 'title' => 'Wildcards', + 'url' => 'icingadb/hosts?host.state.is_problem=y&hostgroup.name~%2Alinux%2A' + ] + ]; + + $initialRoleConfig = [ + 'one' => [ + 'groups' => 'support,helpdesk', + 'monitoring/filter/objects' => 'host_name=foo|hostgroup_name=foo' + ], + 'two' => [ + 'monitoring/filter/objects' => 'host_name=foo|hostgroup_name=foo' + ], + 'three' => [ + 'icingadb/filter/objects' => 'host.name=%2Afoo%2A' + ] + ]; + $expectedRoleConfig = [ + 'one' => [ + 'groups' => 'support,helpdesk', + 'monitoring/filter/objects' => 'host_name=foo|hostgroup_name=foo' + ], + 'two' => [ + 'monitoring/filter/objects' => 'host_name=foo|hostgroup_name=foo' + ], + 'three' => [ + 'icingadb/filter/objects' => 'host.name~%2Afoo%2A' + ] + ]; + + $this->createConfig('preferences/test/host-actions.ini', $initialHostActionConfig); + $this->createConfig('preferences/test/service-actions.ini', $initialServiceActionConfig); + $this->createConfig('preferences/test/icingadb-host-actions.ini', $initialIcingadbHostActionConfig); + $this->createConfig('preferences/test/icingadb-service-actions.ini', $initialIcingadbServiceActionConfig); + $this->createConfig('dashboards/test/dashboard.ini', $initialDashboardConfig); + $this->createConfig('preferences/test/menu.ini', $initialMenuConfig); + $this->createConfig('roles.ini', $initialRoleConfig); + + $command = $this->createCommandInstance('--no-backup'); + $command->filterAction(); + + $hostActionConfig = $this->loadConfig('preferences/test/host-actions.ini'); + $serviceActionConfig = $this->loadConfig('preferences/test/service-actions.ini'); + $icingadbHostActionConfig = $this->loadConfig('preferences/test/icingadb-host-actions.ini'); + $icingadbServiceActionConfig = $this->loadConfig('preferences/test/icingadb-service-actions.ini'); + $dashboardBackup = $this->loadConfig('dashboards/test/dashboard.backup.ini'); + $dashboardConfig = $this->loadConfig('dashboards/test/dashboard.ini'); + $menuBackup = $this->loadConfig('preferences/test/menu.backup.ini'); + $menuConfig = $this->loadConfig('preferences/test/menu.ini'); + $roleBackup = $this->loadConfig('roles.backup.ini'); + $roleConfig = $this->loadConfig('roles.ini'); + + $this->assertSame($expectedHostActionConfig, $hostActionConfig); + $this->assertSame($expectedServiceActionConfig, $serviceActionConfig); + $this->assertEmpty($dashboardBackup); + $this->assertEmpty($menuBackup); + $this->assertEmpty($roleBackup); + + $this->assertSame($expectedIcingadbHostActionConfig, $icingadbHostActionConfig); + $this->assertSame($expectedIcingadbServiceActionConfig, $icingadbServiceActionConfig); + $this->assertSame($expectedDashboardConfig, $dashboardConfig); + $this->assertSame($expectedMenuConfig, $menuConfig); + $this->assertSame($expectedRoleConfig, $roleConfig); + } + + public function testNavigationMigrationWorksEvenIfOnlySharedItemsExist() + { + $this->expectNotToPerformAssertions(); + + $this->createConfig('navigation/menu.ini', []); + + $command = $this->createCommandInstance('--user', 'test'); + $command->navigationAction(); + } + + public function testNavigationMigrationWorksEvenIfOnlyUserItemsExist() + { + $this->expectNotToPerformAssertions(); + + $this->createConfig('preferences/test/menu.ini', []); + + $command = $this->createCommandInstance('--user', 'test'); + $command->navigationAction(); + } + + public function testDashboardMigrationWorksEvenIfNoDashboardsExist() + { + $this->expectNotToPerformAssertions(); + + $command = $this->createCommandInstance('--user', 'test'); + $command->dashboardAction(); + } +} diff --git a/test/php/library/Icingadb/Common/MacrosTest.php b/test/php/library/Icingadb/Common/MacrosTest.php new file mode 100644 index 0000000..f998da8 --- /dev/null +++ b/test/php/library/Icingadb/Common/MacrosTest.php @@ -0,0 +1,174 @@ +<?php + +/* Icinga DB Web | (c) 2021 Icinga GmbH | GPLv2 */ + +namespace Tests\Icinga\Modules\Icingadb\Common; + +use Icinga\Module\Icingadb\Common\Macros; +use Icinga\Module\Icingadb\Compat\CompatHost; +use Icinga\Module\Icingadb\Compat\CompatService; +use Icinga\Module\Icingadb\Model\Host; +use Icinga\Module\Icingadb\Model\Service; +use ipl\Orm\Query; +use ipl\Orm\ResultSet; +use PHPUnit\Framework\TestCase; + +class MacrosTest extends TestCase +{ + use Macros; + + const VARS = [ + 'os' => "Ubuntu", + 'days[0]' => 'mo', + 'days[1]' => 'tue', + 'days[2]' => 'wed', + 'days[3]' => 'thu', + 'days[4]' => 'fr' + ]; + + public function testHostMacros() + { + $host = new Host(); + $host->name = 'test'; + $host->address = '1.1.1.1'; + $host->address6 = '::1'; + $host->vars = self::VARS; + + $host->hostgroup = new Query(); + + $this->performHostMacroTests($host, $host); + } + + public function testHostMacrosOnCompatObject() + { + if (! class_exists('Icinga\Module\Monitoring\Object\Host')) { + $this->markTestSkipped('This test requires the monitoring module'); + } + + $host = new Host(); + $host->name = 'test'; + $host->address = '1.1.1.1'; + $host->address6 = '::1'; + $host->vars = self::VARS; + + $host->hostgroup = new Query(); + + $compatHost = new CompatHost($host); + + $this->performHostMacroTests($compatHost, $host); + } + + protected function performHostMacroTests($host, $source) + { + $this->assertEquals($source->name, $this->expandMacros('$host.name$', $host)); + $this->assertEquals($source->name, $this->expandMacros('$name$', $host)); + $this->assertEquals($source->address, $this->expandMacros('$host.address$', $host)); + $this->assertEquals($source->address6, $this->expandMacros('$host.address6$', $host)); + + // A Host can have more than one hostgroups + $this->assertEquals('$host.hostgroup$', $this->expandMacros('$host.hostgroup$', $host)); + $this->assertEquals('$host.hostgroup.name$', $this->expandMacros('$host.hostgroup.name$', $host)); + + // Host custom vars + $this->assertEquals($source->vars['os'], $this->expandMacros('$host.vars.os$', $host)); + $this->assertEquals($source->vars['os'], $this->expandMacros('$vars.os$', $host)); + $this->assertEquals($source->vars['days[2]'], $this->expandMacros('$vars.days[2]$', $host)); + $this->assertEquals($source->vars['days[4]'], $this->expandMacros('$host.vars.days[4]$', $host)); + + // Host to service relation + $this->assertEquals('$service.name$', $this->expandMacros('$service.name$', $host)); + $this->assertEquals('$service.address$', $this->expandMacros('$service.address$', $host)); + + // Service custom vars + $this->assertEquals('$service.vars.os$', $this->expandMacros('$service.vars.os$', $host)); + $this->assertEquals('$service.vars.days[0]$', $this->expandMacros('$service.vars.days[0]$', $host)); + $this->assertEquals('$service.vars.days[2]$', $this->expandMacros('$service.vars.days[2]$', $host)); + } + + public function testServiceMacros() + { + $service = new Service(); + $service->name = 'test-service'; + $service->description = 'A test service'; + $service->vars = self::VARS; + + $service->servicegroup = new Query(); + + $host = new Host(); + $host->name = 'test'; + $host->address = '1.1.1.1'; + $host->hostgroup = new ResultSet(new \ArrayIterator()); + $host->vars = self::VARS; + + $service->host = $host; + + $this->performServiceMacroTests($service, $service); + } + + public function testServiceMacrosOnCompatObject() + { + if (! class_exists('Icinga\Module\Monitoring\Object\Service')) { + $this->markTestSkipped('This test requires the monitoring module'); + } + + $service = new Service(); + $service->name = 'test-service'; + $service->description = 'A test service'; + $service->vars = self::VARS; + + $service->servicegroup = new Query(); + + $host = new Host(); + $host->name = 'test'; + $host->address = '1.1.1.1'; + $host->hostgroup = new ResultSet(new \ArrayIterator()); + $host->vars = self::VARS; + + $service->host = $host; + + $compatService = new CompatService($service); + + $this->performServiceMacroTests($compatService, $service); + } + + protected function performServiceMacroTests($service, $source) + { + $this->assertEquals($source->name, $this->expandMacros('$service.name$', $service)); + $this->assertEquals($source->name, $this->expandMacros('$name$', $service)); + $this->assertEquals($source->description, $this->expandMacros('$service.description$', $service)); + + // A Service can have more than one hostgroups + $this->assertEquals( + '$service.servicegroup$', + $this->expandMacros('$service.servicegroup$', $service) + ); + $this->assertEquals( + '$service.servicegroup.name$', + $this->expandMacros('$service.servicegroup.name$', $service) + ); + + // Service custom vars + $this->assertEquals($source->vars['os'], $this->expandMacros('$service.vars.os$', $service)); + $this->assertEquals($source->vars['os'], $this->expandMacros('$vars.os$', $service)); + $this->assertEquals($source->vars['days[2]'], $this->expandMacros('$vars.days[2]$', $service)); + $this->assertEquals($source->vars['days[4]'], $this->expandMacros('$service.vars.days[4]$', $service)); + + $this->assertEquals($source->host->name, $this->expandMacros('$host.name$', $service)); + $this->assertEquals($source->host->address, $this->expandMacros('$host.address$', $service)); + + // Host custom vars + $this->assertEquals($source->host->vars['os'], $this->expandMacros('$host.vars.os$', $service)); + $this->assertEquals($source->host->vars['days[0]'], $this->expandMacros('$host.vars.days[0]$', $service)); + $this->assertEquals($source->host->vars['days[3]'], $this->expandMacros('$host.vars.days[3]$', $service)); + + // A Host can have more than one hostgroups + $this->assertEquals( + '$host.hostgroup$', + $this->expandMacros('$host.hostgroup$', $service) + ); + $this->assertEquals( + '$host.hostgroup.name$', + $this->expandMacros('$host.hostgroup.name$', $service) + ); + } +} diff --git a/test/php/library/Icingadb/Common/StateBadgesTest.php b/test/php/library/Icingadb/Common/StateBadgesTest.php new file mode 100644 index 0000000..b535e65 --- /dev/null +++ b/test/php/library/Icingadb/Common/StateBadgesTest.php @@ -0,0 +1,86 @@ +<?php + +/* Icinga DB Web | (c) 2023 Icinga GmbH | GPLv2 */ + +namespace Tests\Icinga\Modules\Icingadb\Common; + +use Icinga\Module\Icingadb\Common\StateBadges; +use Icinga\Web\UrlParams; +use ipl\Stdlib\Filter; +use ipl\Web\Filter\QueryString; +use ipl\Web\Url; +use PHPUnit\Framework\TestCase; + +class StateBadgesTest extends TestCase +{ + public function testCreateLinkRendersBaseFilterCorrectly() + { + $stateBadges = $this->createStateBadges() + ->setBaseFilter(Filter::any( + Filter::equal('foo', 'bar'), + Filter::equal('bar', 'foo') + )); + + $link = $stateBadges->createLink('test', Filter::equal('rab', 'oof')); + + $this->assertSame( + 'rab=oof&(foo=bar|bar=foo)', + $link->getUrl()->getQueryString() + ); + } + + private function createStateBadges() + { + $queryString = null; + + $urlMock = $this->createConfiguredMock(Url::class, [ + 'getBasePath' => 'test', + 'getParams' => $this->createConfiguredMock(UrlParams::class, [ + 'toArray' => [] + ]) + ]); + $urlMock->method('setFilter')->willReturnCallback( + function ($qs) use ($urlMock, &$queryString) { + $queryString = QueryString::render($qs); + + return $urlMock; + } + ); + $urlMock->method('getQueryString')->willReturnCallback( + function () use (&$queryString) { + return $queryString; + } + ); + + return new class ($urlMock) extends StateBadges { + private $urlMock; + + public function __construct($urlMock) + { + $this->urlMock = $urlMock; + + parent::__construct((object) []); + } + + protected function getBaseUrl(): Url + { + return $this->urlMock; + } + + protected function getType(): string + { + return 'test'; + } + + protected function getPrefix(): string + { + return 'Test'; + } + + protected function getStateInt(string $state): int + { + return 0; + } + }; + } +} diff --git a/test/php/library/Icingadb/Model/CustomvarFlatTest.php b/test/php/library/Icingadb/Model/CustomvarFlatTest.php new file mode 100644 index 0000000..a69f578 --- /dev/null +++ b/test/php/library/Icingadb/Model/CustomvarFlatTest.php @@ -0,0 +1,121 @@ +<?php + +/* Icinga DB Web | (c) 2023 Icinga GmbH | GPLv2 */ + +namespace Tests\Icinga\Modules\Icingadb\Model; + +use Icinga\Module\Icingadb\Model\CustomvarFlat; +use PHPUnit\Framework\TestCase; + +class CustomvarFlatTest extends TestCase +{ + const EMPTY_TEST_SOURCE = [ + ["dict.not_empty.foo","bar","dict","{\"empty\":{},\"not_empty\":{\"foo\":\"bar\"}}"], + ["dict.empty",null,"dict","{\"empty\":{},\"not_empty\":{\"foo\":\"bar\"}}"], + ["list[1]",null,"list","[[\"foo\",\"bar\"],[]]"], + ["list[0][0]","foo","list","[[\"foo\",\"bar\"],[]]"], + ["list[0][1]","bar","list","[[\"foo\",\"bar\"],[]]"], + ["empty_list",null,"empty_list","[]"], + ["empty_dict",null,"empty_dict","{}"], + ["null","null","null","null"] + ]; + + const EMPTY_TEST_RESULT = [ + "dict" => [ + "not_empty" => [ + "foo" => "bar" + ], + "empty" => [] + ], + "list" => [ + ["foo", "bar"], + [] + ], + "empty_list" => [], + "empty_dict" => [], + "null" => "null" + ]; + + const SPECIAL_CHAR_TEST_SOURCE = [ + [ + "vhosts.xxxxxxxxxxxxx.mgmt.xxxxxx.com.http_port", + "443", + "vhosts", + "{\"xxxxxxxxxxxxx.mgmt.xxxxxx.com\":{\"http_port\":\"443\"}}" + ], + ["ex.ample.com.bla","blub","ex","{\"ample.com\":{\"bla\":\"blub\"}}"], + ["example[1]","zyx","example[1]","\"zyx\""], + ["example.0.org","xyz","example.0.org","\"xyz\""], + ["ob.je.ct","***","ob","{\"je\":{\"ct\":\"tcejbo\"}}"], + ["real_list[2]","three","real_list","[\"one\",\"two\",\"three\"]"], + ["real_list[1]","two","real_list","[\"one\",\"two\",\"three\"]"], + ["real_list[0]","one","real_list","[\"one\",\"two\",\"three\"]"], + ["[1].2.[3].4.[5].6","123456","[1].2","{\"[3].4\":{\"[5].6\":123456}}"], + ["ex.ample.com","cba","ex.ample.com","\"cba\""], + ["[4]","four","[4]","\"four\""] + ]; + + const SPECIAL_CHAR_TEST_RESULT = [ + "vhosts" => [ + "xxxxxxxxxxxxx.mgmt.xxxxxx.com" => [ + "http_port" => 443 + ] + ], + "ex" => [ + "ample.com" => [ + "bla" => "blub" + ] + ], + "example[1]" => "zyx", + "example.0.org" => "xyz", + "ob" => [ + "je" => [ + "ct" => "***" + ] + ], + "real_list" => [ + "one", + "two", + "three" + ], + "[1].2" => [ + "[3].4" => [ + "[5].6" => "123456" + ] + ], + "ex.ample.com" => "cba", + "[4]" => "four" + ]; + + public function testUnflatteningOfEmptyCustomVariables() + { + $this->assertEquals( + self::EMPTY_TEST_RESULT, + (new CustomvarFlat())->unFlattenVars($this->transformSource(self::EMPTY_TEST_SOURCE)), + "Empty custom variables are not correctly unflattened" + ); + } + + public function testUnflatteningOfCustomVariablesWithSpecialCharacters() + { + $this->assertEquals( + self::SPECIAL_CHAR_TEST_RESULT, + (new CustomvarFlat())->unFlattenVars($this->transformSource(self::SPECIAL_CHAR_TEST_SOURCE)), + "Custom variables with special characters are not correctly unflattened" + ); + } + + protected function transformSource(array $source): \Generator + { + foreach ($source as $data) { + yield (object) [ + 'flatname' => $data[0], + 'flatvalue' => $data[1], + 'customvar' => (object) [ + 'name' => $data[2], + 'value' => $data[3] + ] + ]; + } + } +} diff --git a/test/php/library/Icingadb/Util/PerfdataSetTest.php b/test/php/library/Icingadb/Util/PerfdataSetTest.php new file mode 100644 index 0000000..618c29a --- /dev/null +++ b/test/php/library/Icingadb/Util/PerfdataSetTest.php @@ -0,0 +1,120 @@ +<?php + +/* Icinga DB Web | (c) 2023 Icinga GmbH | GPLv2 */ + +namespace Tests\Icinga\Module\Icingadb\Util; + +use Icinga\Module\Icingadb\Util\PerfDataSet; +use PHPUnit\Framework\TestCase; +use Tests\Icinga\Module\Icingadb\Lib\PerfdataSetWithPublicData; + +class PerfdataSetTest extends TestCase +{ + public function testWhetherValidSimplePerfdataLabelsAreProperlyParsed() + { + $pset = PerfdataSetWithPublicData::fromString('key1=val1 key2=val2 key3 =val3'); + $this->assertSame( + 'key1', + $pset->perfdata[0]->getLabel(), + 'PerfdataSet does not correctly parse valid simple labels' + ); + $this->assertSame( + 'key2', + $pset->perfdata[1]->getLabel(), + 'PerfdataSet does not correctly parse valid simple labels' + ); + $this->assertSame( + 'key3', + $pset->perfdata[2]->getLabel(), + 'PerfdataSet does not correctly parse valid simple labels' + ); + } + + public function testWhetherNonQuotedPerfdataLablesWithSpacesAreProperlyParsed() + { + $pset = PerfdataSetWithPublicData::fromString('key 1=val1 key 1 + 1=val2'); + $this->assertSame( + 'key 1', + $pset->perfdata[0]->getLabel(), + 'PerfdataSet does not correctly parse non quoted labels with spaces' + ); + $this->assertSame( + 'key 1 + 1', + $pset->perfdata[1]->getLabel(), + 'PerfdataSet does not correctly parse non quoted labels with spaces' + ); + } + + public function testWhetherValidQuotedPerfdataLabelsAreProperlyParsed() + { + $pset = PerfdataSetWithPublicData::fromString('\'key 1\'=val1 "key 2"=val2 \'a=b\'=0%;;2'); + $this->assertSame( + 'key 1', + $pset->perfdata[0]->getLabel(), + 'PerfdataSet does not correctly parse valid quoted labels' + ); + $this->assertSame( + 'key 2', + $pset->perfdata[1]->getLabel(), + 'PerfdataSet does not correctly parse valid quoted labels' + ); + $this->assertSame( + 'a=b', + $pset->perfdata[2]->getLabel(), + 'PerfdataSet does not correctly parse labels with equal signs' + ); + } + + public function testWhetherInvalidQuotedPerfdataLabelsAreProperlyParsed() + { + $pset = PerfdataSetWithPublicData::fromString('\'key 1=1 key 2"=2'); + $this->assertSame( + 'key 1', + $pset->perfdata[0]->getLabel(), + 'PerfdataSet does not correctly parse invalid quoted labels' + ); + $this->assertSame( + 'key 2"', + $pset->perfdata[1]->getLabel(), + 'PerfdataSet does not correctly parse invalid quoted labels' + ); + $pset = PerfdataSetWithPublicData::fromString('"key 1=1 "key 2"=2'); + $this->assertSame( + 'key 1=1', + $pset->perfdata[0]->getLabel(), + 'PerfdataSet does not correctly parse invalid quoted labels' + ); + $this->assertNull( + $pset->perfdata[0]->getValue() + ); + $this->assertSame( + '2"', + $pset->perfdata[1]->getLabel(), + 'PerfdataSet does not correctly parse invalid quoted labels' + ); + $this->assertSame( + 2.0, + $pset->perfdata[1]->getValue() + ); + } + + /** + * @depends testWhetherValidSimplePerfdataLabelsAreProperlyParsed + */ + public function testWhetherAPerfdataSetIsIterable() + { + $pset = PerfdataSet::fromString('key=value'); + foreach ($pset as $p) { + $this->assertSame('key', $p->getLabel()); + return; + } + + $this->fail('PerfdataSet objects cannot be iterated'); + } + + public function testWhetherPerfdataSetsCanBeInitializedWithEmptyStrings() + { + $pset = PerfdataSetWithPublicData::fromString(''); + $this->assertEmpty($pset->perfdata, 'PerfdataSet::fromString does not accept emtpy strings'); + } +} diff --git a/test/php/library/Icingadb/Util/PerfdataTest.php b/test/php/library/Icingadb/Util/PerfdataTest.php new file mode 100644 index 0000000..5a63825 --- /dev/null +++ b/test/php/library/Icingadb/Util/PerfdataTest.php @@ -0,0 +1,591 @@ +<?php + +/* Icinga DB Web | (c) 2023 Icinga GmbH | GPLv2 */ + +namespace Tests\Icinga\Module\Icingadb\Util; + +use Icinga\Module\Icingadb\Util\PerfData; +use PHPUnit\Framework\TestCase; + +class PerfdataTest extends TestCase +{ + public function testWhetherFromStringThrowsExceptionWhenGivenAnEmptyString() + { + $this->expectException(\InvalidArgumentException::class); + + Perfdata::fromString(''); + } + + public function testWhetherFromStringThrowsExceptionWhenGivenAnInvalidString() + { + $this->expectException(\InvalidArgumentException::class); + + Perfdata::fromString('test'); + } + + public function testWhetherFromStringParsesAGivenStringCorrectly() + { + $p = Perfdata::fromString('key=1234'); + $this->assertSame( + 'key', + $p->getLabel(), + 'Perfdata::fromString does not properly parse performance data labels' + ); + $this->assertSame( + 1234.0, + $p->getValue(), + 'Perfdata::fromString does not properly parse performance data values' + ); + } + + /** + * @depends testWhetherFromStringParsesAGivenStringCorrectly + */ + public function testWhetherGetValueReturnsValidValues() + { + $this->assertSame( + 1337.0, + Perfdata::fromString('test=1337')->getValue(), + 'Perfdata::getValue does not return correct values' + ); + $this->assertSame( + 1337.0, + Perfdata::fromString('test=1337;;;;')->getValue(), + 'Perfdata::getValue does not return correct values' + ); + } + + /** + * @depends testWhetherFromStringParsesAGivenStringCorrectly + */ + public function testWhetherDecimalValuesAreCorrectlyParsed() + { + $this->assertSame( + 1337.5, + Perfdata::fromString('test=1337.5')->getValue(), + 'Perfdata objects do not parse decimal values correctly' + ); + $this->assertSame( + 1337.5, + Perfdata::fromString('test=1337.5B')->getValue(), + 'Perfdata objects do not parse decimal values correctly' + ); + } + + /** + * @depends testWhetherFromStringParsesAGivenStringCorrectly + */ + public function testWhetherGetValueReturnsNullForInvalidOrUnknownValues() + { + $this->assertNull( + Perfdata::fromString('test=U')->getValue(), + 'Perfdata::getValue does not return null for unknown values' + ); + $this->assertNull( + Perfdata::fromString('test=i am not a value')->getValue(), + 'Perfdata::getValue does not return null for invalid values' + ); + $this->assertNull( + PerfData::fromString('test=')->getValue(), + 'Perfdata::getValue does not return null for invalid values' + ); + $this->assertNull( + PerfData::fromString('test=-kW')->getValue(), + 'Perfdata::getValue does not return null for invalid values' + ); + $this->assertNull( + PerfData::fromString('test=kW')->getValue(), + 'Perfdata::getValue does not return null for invalid values' + ); + $this->assertNull( + PerfData::fromString('test=-')->getValue(), + 'Perfdata::getValue does not return null for invalid values' + ); + } + + /** + * @depends testWhetherFromStringParsesAGivenStringCorrectly + */ + public function testWhetherUnitOfUnkownValuesIsCorrectlyIdentified() + { + $this->assertNull( + Perfdata::fromString('test=U')->getUnit(), + 'Perfdata::getUnit does not return null for unknown values' + ); + $this->assertNull( + Perfdata::fromString('test=i am not a value')->getUnit(), + 'Perfdata::getUnit does not return null for unknown values' + ); + $this->assertNull( + PerfData::fromString('test=')->getUnit(), + 'Perfdata::getUnit does not return null for unknown values' + ); + $this->assertSame( + 'kW', + PerfData::fromString('test=-kW')->getUnit(), + 'Perfdata::getUnit does not return correct unit for invalid values' + ); + $this->assertSame( + 'kW', + PerfData::fromString('test=kW')->getUnit(), + 'Perfdata::getUnit does not return correct unit for invalid values' + ); + $this->assertNull( + PerfData::fromString('test=-')->getUnit(), + 'Perfdata::getUnit does not return null for unknown values' + ); + } + + /** + * @depends testWhetherFromStringParsesAGivenStringCorrectly + */ + public function testWhethergetWarningThresholdReturnsCorrectValues() + { + $zeroToTen = Perfdata::fromString('test=1;10')->getWarningThreshold(); + $this->assertSame( + 0.0, + $zeroToTen->getMin(), + 'Perfdata::getWarningThreshold does not return correct values' + ); + $this->assertSame( + 10.0, + $zeroToTen->getMax(), + 'Perfdata::getWarningThreshold does not return correct values' + ); + $tenToInfinity = Perfdata::fromString('test=1;10:')->getWarningThreshold(); + $this->assertSame( + 10.0, + $tenToInfinity->getMin(), + 'Perfdata::getWarningThreshold does not return correct values' + ); + $this->assertNull( + $tenToInfinity->getMax(), + 'Perfdata::getWarningThreshold does not return correct values' + ); + $infinityToTen = Perfdata::fromString('test=1;~:10')->getWarningThreshold(); + $this->assertNull( + $infinityToTen->getMin(), + 'Perfdata::getWarningThreshold does not return correct values' + ); + $this->assertSame( + 10.0, + $infinityToTen->getMax(), + 'Perfdata::getWarningThreshold does not return correct values' + ); + $tenToTwenty = Perfdata::fromString('test=1;10:20')->getWarningThreshold(); + $this->assertSame( + 10.0, + $tenToTwenty->getMin(), + 'Perfdata::getWarningThreshold does not return correct values' + ); + $this->assertSame( + 20.0, + $tenToTwenty->getMax(), + 'Perfdata::getWarningThreshold does not return correct values' + ); + $tenToTwentyInverted = Perfdata::fromString('test=1;@10:20')->getWarningThreshold(); + $this->assertTrue( + $tenToTwentyInverted->isInverted(), + 'Perfdata::getWarningThreshold does not return correct values' + ); + } + + /** + * @depends testWhetherFromStringParsesAGivenStringCorrectly + */ + public function testWhetherGetCriticalThresholdReturnsCorrectValues() + { + $zeroToTen = Perfdata::fromString('test=1;;10')->getCriticalThreshold(); + $this->assertSame( + 0.0, + $zeroToTen->getMin(), + 'Perfdata::getCriticalThreshold does not return correct values' + ); + $this->assertSame( + 10.0, + $zeroToTen->getMax(), + 'Perfdata::getCriticalThreshold does not return correct values' + ); + $tenToInfinity = Perfdata::fromString('test=1;;10:')->getCriticalThreshold(); + $this->assertSame( + 10.0, + $tenToInfinity->getMin(), + 'Perfdata::getCriticalThreshold does not return correct values' + ); + $this->assertNull( + $tenToInfinity->getMax(), + 'Perfdata::getCriticalThreshold does not return correct values' + ); + $infinityToTen = Perfdata::fromString('test=1;;~:10')->getCriticalThreshold(); + $this->assertNull( + $infinityToTen->getMin(), + 'Perfdata::getCriticalThreshold does not return correct values' + ); + $this->assertSame( + 10.0, + $infinityToTen->getMax(), + 'Perfdata::getCriticalThreshold does not return correct values' + ); + $tenToTwenty = Perfdata::fromString('test=1;;10:20')->getCriticalThreshold(); + $this->assertSame( + 10.0, + $tenToTwenty->getMin(), + 'Perfdata::getCriticalThreshold does not return correct values' + ); + $this->assertSame( + 20.0, + $tenToTwenty->getMax(), + 'Perfdata::getCriticalThreshold does not return correct values' + ); + $tenToTwentyInverted = Perfdata::fromString('test=1;;@10:20')->getCriticalThreshold(); + $this->assertTrue( + $tenToTwentyInverted->isInverted(), + 'Perfdata::getCriticalThreshold does not return correct values' + ); + } + + /** + * @depends testWhetherFromStringParsesAGivenStringCorrectly + */ + public function testWhetherGetMinimumValueReturnsCorrectValues() + { + $this->assertSame( + 1337.0, + Perfdata::fromString('test=1;;;1337')->getMinimumValue(), + 'Perfdata::getMinimumValue does not return correct values' + ); + $this->assertSame( + 1337.5, + Perfdata::fromString('test=1;;;1337.5')->getMinimumValue(), + 'Perfdata::getMinimumValue does not return correct values' + ); + } + + /** + * @depends testWhetherFromStringParsesAGivenStringCorrectly + */ + public function testWhetherGetMaximumValueReturnsCorrectValues() + { + $this->assertSame( + 1337.0, + Perfdata::fromString('test=1;;;;1337')->getMaximumValue(), + 'Perfdata::getMaximumValue does not return correct values' + ); + $this->assertSame( + 1337.5, + Perfdata::fromString('test=1;;;;1337.5')->getMaximumValue(), + 'Perfdata::getMaximumValue does not return correct values' + ); + } + + /** + * @depends testWhetherFromStringParsesAGivenStringCorrectly + */ + public function testWhetherMissingValuesAreProperlyHandled() + { + $perfdata = Perfdata::fromString('test=1;;3;5'); + $this->assertEmpty( + (string) $perfdata->getWarningThreshold(), + 'Perfdata objects do not correctly identify omitted warning tresholds' + ); + $this->assertNull( + $perfdata->getMaximumValue(), + 'Perfdata objects do not return null for missing maximum values' + ); + } + + /** + * @depends testWhetherGetValueReturnsValidValues + */ + public function testWhetherValuesAreIdentifiedAsNumber() + { + $this->assertTrue( + Perfdata::fromString('test=666')->isNumber(), + 'Perfdata objects do not identify ordinary digits as number' + ); + } + + /** + * @depends testWhetherGetValueReturnsValidValues + */ + public function testWhetherValuesAreIdentifiedAsSeconds() + { + $this->assertTrue( + Perfdata::fromString('test=666s')->isSeconds(), + 'Perfdata objects do not identify seconds as seconds' + ); + } + + /** + * @depends testWhetherGetValueReturnsValidValues + */ + public function testWhetherValuesAreIdentifiedAsPercentage() + { + $this->assertTrue( + Perfdata::fromString('test=66%')->isPercentage(), + 'Perfdata objects do not identify percentages as percentages' + ); + } + + /** + * @depends testWhetherValuesAreIdentifiedAsPercentage + */ + public function testWhetherMinAndMaxAreNotRequiredIfUnitIsInPercent() + { + $perfdata = Perfdata::fromString('test=1%'); + $this->assertSame( + 0.0, + $perfdata->getMinimumValue(), + 'Perfdata objects do not set minimum value to 0 if UOM is %' + ); + $this->assertSame( + 100.0, + $perfdata->getMaximumValue(), + 'Perfdata objects do not set maximum value to 100 if UOM is %' + ); + } + + /** + * @depends testWhetherGetValueReturnsValidValues + */ + public function testWhetherValuesAreIdentifiedAsBytes() + { + $this->assertTrue( + Perfdata::fromString('test=66666B')->isBytes(), + 'Perfdata objects do not identify bytes as bytes' + ); + } + + /** + * @depends testWhetherGetValueReturnsValidValues + */ + public function testWhetherValuesAreIdentifiedAsCounter() + { + $this->assertTrue( + Perfdata::fromString('test=123c')->isCounter(), + 'Perfdata objects do not identify counters as counters' + ); + } + + /** + * @depends testWhetherValuesAreIdentifiedAsPercentage + */ + public function testWhetherPercentagesAreHandledCorrectly() + { + $this->assertSame( + 66.0, + Perfdata::fromString('test=66%')->getPercentage(), + 'Perfdata objects do not correctly handle native percentages' + ); + $this->assertSame( + 50.0, + Perfdata::fromString('test=0;;;-250;250')->getPercentage(), + 'Perfdata objects do not correctly convert suitable values to percentages' + ); + $this->assertNull( + Perfdata::fromString('test=50')->getPercentage(), + 'Perfdata objects do return a percentage though their unit is not % and no maximum is given' + ); + $this->assertNull( + Perfdata::fromString('test=25;;;50;100')->getPercentage(), + 'Perfdata objects do return a percentage though their value is lower than it\'s allowed minimum' + ); + $this->assertNull( + Perfdata::fromString('test=25;;;0;')->getPercentage(), + 'Perfdata objects do not ignore empty max values when returning percentages' + ); + $this->assertNull( + Perfdata::fromString('test=25;;;0;0')->getPercentage(), + 'Perfdata objects do not ignore impossible min/max combinations when returning percentages' + ); + } + + public function testWhetherInvalidValueInPerfDataHandledCorrectly() + { + $p1 = Perfdata::fromString('test=2,0'); + $this->assertFalse($p1->isValid()); + $this->assertNull( + $p1->getValue(), + 'Perfdata::getValue does not return null for invalid values' + ); + $this->assertSame( + '2,0', + $p1->toArray()['value'] + ); + + $p2 = Perfdata::fromString('test=i am not a value'); + $this->assertFalse($p2->isValid()); + $this->assertNull( + $p2->getValue(), + 'Perfdata::getValue does not return null for invalid values' + ); + $this->assertSame( + 'i am not a value', + $p2->toArray()['value'] + ); + + $p3 = Perfdata::fromString('test='); + $this->assertFalse($p3->isValid()); + $this->assertNull( + $p3->getValue(), + 'Perfdata::getValue does not return null for invalid values' + ); + $this->assertSame( + '', + $p3->toArray()['value'] + ); + + $p4 = Perfdata::fromString('test=-kW'); + $this->assertFalse($p4->isValid()); + $this->assertNull( + $p4->getValue(), + 'Perfdata::getValue does not return null for invalid values' + ); + $this->assertSame( + '-kW', + $p4->toArray()['value'] + ); + + $p5 = Perfdata::fromString('test=kW'); + $this->assertFalse($p5->isValid()); + $this->assertNull( + $p5->getValue(), + 'Perfdata::getValue does not return null for invalid values' + ); + $this->assertSame( + 'kW', + $p5->toArray()['value'] + ); + + $p6 = Perfdata::fromString('test=-'); + $this->assertFalse($p6->isValid()); + $this->assertNull( + $p6->getValue(), + 'Perfdata::getValue does not return null for invalid values' + ); + $this->assertSame( + '-', + $p6->toArray()['value'] + ); + } + + public function testWhetherInvalidMinInPerfDataHandledCorrectly() + { + $p1 = Perfdata::fromString('test=1;;;2,0'); + $this->assertFalse($p1->isValid()); + $this->assertNull( + $p1->getMinimumValue(), + 'Perfdata::getMinimumValue does not return null for invalid min values' + ); + $this->assertSame( + '2,0', + $p1->toArray()['min'] + ); + + $p2 = Perfdata::fromString('test=1;;;foo'); + $this->assertFalse($p2->isValid()); + $this->assertNull( + $p2->getMinimumValue(), + 'Perfdata::getMinimumValue does not return null for invalid min values' + ); + $this->assertSame( + 'foo', + $p2->toArray()['min'] + ); + } + + public function testWhetherInvalidMaxInPerfDataHandledCorrectly() + { + $p1 = Perfdata::fromString('test=1;;;;2,0'); + $this->assertFalse($p1->isValid()); + $this->assertNull( + $p1->getMaximumValue(), + 'Perfdata::getMaximumValue does not return null for invalid max values' + ); + $this->assertSame( + '2,0', + $p1->toArray()['max'] + ); + + $p2 = Perfdata::fromString('test=1;;;;foo'); + $this->assertFalse($p2->isValid()); + $this->assertNull( + $p2->getMaximumValue(), + 'Perfdata::getMaximumValue does not return null for invalid max values' + ); + $this->assertSame( + 'foo', + $p2->toArray()['max'] + ); + } + + public function testWhetherInvalidWarningThresholdInPerfDataHandledCorrectly() + { + $p1 = Perfdata::fromString('test=1;2,0:'); + $this->assertFalse($p1->getWarningThreshold()->isValid()); + $this->assertFalse($p1->isValid()); + $this->assertSame( + '2,0:', + (string) $p1->getWarningThreshold() + ); + + $p2 = Perfdata::fromString('test=1;0:4,0'); + $this->assertFalse($p2->getWarningThreshold()->isValid()); + $this->assertFalse($p2->isValid()); + $this->assertSame( + '0:4,0', + (string) $p2->getWarningThreshold() + ); + + $p3 = Perfdata::fromString('test=1;foo'); + $this->assertFalse($p2->getWarningThreshold()->isValid()); + $this->assertFalse($p3->isValid()); + $this->assertSame( + 'foo', + (string) $p3->getWarningThreshold() + ); + + $p4 = Perfdata::fromString('test=1;10@'); + $this->assertFalse($p2->getWarningThreshold()->isValid()); + $this->assertFalse($p4->isValid()); + $this->assertSame( + '10@', + (string) $p4->getWarningThreshold() + ); + } + + public function testWhetherInvalidCriticalThresholdInPerfDataHandledCorrectly() + { + $p1 = Perfdata::fromString('test=1;;2,0:'); + $this->assertFalse($p1->getCriticalThreshold()->isValid()); + $this->assertFalse($p1->isValid()); + $this->assertSame( + '2,0:', + (string) $p1->getCriticalThreshold() + ); + + $p2 = Perfdata::fromString('test=1;;0:4,0'); + $this->assertFalse($p2->getCriticalThreshold()->isValid()); + $this->assertFalse($p2->isValid()); + $this->assertSame( + '0:4,0', + (string) $p2->getCriticalThreshold() + ); + + $p3 = Perfdata::fromString('test=1;;foo'); + $this->assertFalse($p2->getCriticalThreshold()->isValid()); + $this->assertFalse($p3->isValid()); + $this->assertSame( + 'foo', + (string) $p3->getCriticalThreshold() + ); + + $p4 = Perfdata::fromString('test=1;;10@'); + $this->assertFalse($p2->getCriticalThreshold()->isValid()); + $this->assertFalse($p4->isValid()); + $this->assertSame( + '10@', + (string) $p4->getCriticalThreshold() + ); + } +} diff --git a/test/php/library/Icingadb/Util/ThresholdRangeTest.php b/test/php/library/Icingadb/Util/ThresholdRangeTest.php new file mode 100644 index 0000000..b191e88 --- /dev/null +++ b/test/php/library/Icingadb/Util/ThresholdRangeTest.php @@ -0,0 +1,343 @@ +<?php + +/* Icinga DB Web | (c) 2023 Icinga GmbH | GPLv2 */ + +namespace Tests\Icinga\Module\Icingadb\Util; + +use Icinga\Module\Icingadb\Util\ThresholdRange; +use PHPUnit\Framework\TestCase; + +class ThresholdRangeTest extends TestCase +{ + public function testFromStringProperlyParsesDoubleExclusiveRanges() + { + $outside0And10 = ThresholdRange::fromString('10'); + $this->assertSame( + 0.0, + $outside0And10->getMin(), + 'ThresholdRange::fromString() does not identify zero as default minimum for double exclusive ranges' + ); + $this->assertSame( + 10.0, + $outside0And10->getMax(), + 'ThresholdRange::fromString() does not identify ten as explicit maximum for double exclusive ranges' + ); + $this->assertFalse( + $outside0And10->isInverted(), + 'ThresholdRange::fromString() identifies double exclusive ranges as inclusive' + ); + + $outside10And20 = ThresholdRange::fromString('10:20'); + $this->assertSame( + 10.0, + $outside10And20->getMin(), + 'ThresholdRange::fromString() does not identify ten as explicit minimum for double exclusive ranges' + ); + $this->assertSame( + 20.0, + $outside10And20->getMax(), + 'ThresholdRange::fromString() does not identify twenty as explicit maximum for double exclusive ranges' + ); + $this->assertFalse( + $outside10And20->isInverted(), + 'ThresholdRange::fromString() identifies double exclusive ranges as inclusive' + ); + } + + /** + * @depends testFromStringProperlyParsesDoubleExclusiveRanges + */ + public function testContainsCorrectlyEvaluatesDoubleExclusiveRanges() + { + $outside0And10 = ThresholdRange::fromString('10'); + $this->assertFalse( + $outside0And10->contains(-1), + 'ThresholdRange::contains() identifies negative values as greater than or equal to zero' + ); + $this->assertFalse( + $outside0And10->contains(11), + 'ThresholdRange::contains() identifies eleven as smaller than or equal to ten' + ); + $this->assertTrue( + $outside0And10->contains(10), + 'ThresholdRange::contains() identifies 10 as outside the range 0..10' + ); + + $outside10And20 = ThresholdRange::fromString('10:20'); + $this->assertFalse( + $outside10And20->contains(9), + 'ThresholdRange::contains() identifies nine as greater than or equal to 10' + ); + $this->assertFalse( + $outside10And20->contains(21), + 'ThresholdRange::contains() identifies twenty-one as smaller than or equal to twenty' + ); + $this->assertTrue( + $outside10And20->contains(20), + 'ThresholdRange::contains() identifies 20 as outside the range 10..20' + ); + } + + public function testFromStringProperlyParsesSingleExclusiveRanges() + { + $smallerThan10 = ThresholdRange::fromString('10:'); + $this->assertSame( + 10.0, + $smallerThan10->getMin(), + 'ThresholdRange::fromString() does not identify ten as explicit minimum for single exclusive ranges' + ); + $this->assertNull( + $smallerThan10->getMax(), + 'ThresholdRange::fromString() does not identify infinity as default maximum for single exclusive ranges' + ); + $this->assertFalse( + $smallerThan10->isInverted(), + 'ThresholdRange::fromString() identifies single exclusive ranges as inclusive' + ); + + $greaterThan10 = ThresholdRange::fromString('~:10'); + $this->assertNull( + $greaterThan10->getMin(), + 'ThresholdRange::fromString() does not identify infinity as explicit minimum for single exclusive ranges' + ); + $this->assertSame( + 10.0, + $greaterThan10->getMax(), + 'ThresholdRange::fromString() does not identify ten as explicit maximum for single exclusive ranges' + ); + $this->assertFalse( + $greaterThan10->isInverted(), + 'ThresholdRange::fromString() identifies single exclusive ranges as inclusive' + ); + } + + /** + * @depends testFromStringProperlyParsesSingleExclusiveRanges + */ + public function testContainsCorrectlyEvaluatesSingleExclusiveRanges() + { + $smallerThan10 = ThresholdRange::fromString('10:'); + $this->assertFalse( + $smallerThan10->contains(9), + 'ThresholdRange::contains() identifies nine as greater than or equal to ten' + ); + $this->assertTrue( + $smallerThan10->contains(PHP_INT_MAX), + 'ThresholdRange::contains() identifies infinity as outside the range 10..~' + ); + + $greaterThan10 = ThresholdRange::fromString('~:10'); + $this->assertFalse( + $greaterThan10->contains(11), + 'ThresholdRange::contains() identifies eleven as smaller than or equal to ten' + ); + $this->assertTrue( + $greaterThan10->contains(~PHP_INT_MAX), + 'ThresholdRange::contains() identifies negative infinity as outside the range ~..10' + ); + } + + public function testFromStringProperlyParsesInclusiveRanges() + { + $inside0And10 = ThresholdRange::fromString('@10'); + $this->assertSame( + 0.0, + $inside0And10->getMin(), + 'ThresholdRange::fromString() does not identify zero as default minimum for inclusive ranges' + ); + $this->assertSame( + 10.0, + $inside0And10->getMax(), + 'ThresholdRange::fromString() does not identify ten as explicit maximum for inclusive ranges' + ); + $this->assertTrue( + $inside0And10->isInverted(), + 'ThresholdRange::fromString() identifies inclusive ranges as double exclusive' + ); + + $inside10And20 = ThresholdRange::fromString('@10:20'); + $this->assertSame( + 10.0, + $inside10And20->getMin(), + 'ThresholdRange::fromString() does not identify ten as explicit minimum for inclusive ranges' + ); + $this->assertSame( + 20.0, + $inside10And20->getMax(), + 'ThresholdRange::fromString() does not identify twenty as explicit maximum for inclusive ranges' + ); + $this->assertTrue( + $inside10And20->isInverted(), + 'ThresholdRange::fromString() identifies inclusive ranges as double exclusive' + ); + + $greaterThan10 = ThresholdRange::fromString('@10:'); + $this->assertSame( + 10.0, + $greaterThan10->getMin(), + 'ThresholdRange::fromString() does not identify ten as explicit minimum for inclusive ranges' + ); + $this->assertNull( + $greaterThan10->getMax(), + 'ThresholdRange::fromString() does not identify infinity as default maximum for inclusive ranges' + ); + $this->assertTrue( + $greaterThan10->isInverted(), + 'ThresholdRange::fromString() identifies inclusive ranges as single exclusive' + ); + + $smallerThan10 = ThresholdRange::fromString('@~:10'); + $this->assertNull( + $smallerThan10->getMin(), + 'ThresholdRange::fromString() does not identify infinity as explicit minimum for inclusive ranges' + ); + $this->assertSame( + 10.0, + $smallerThan10->getMax(), + 'ThresholdRange::fromString() does not identify ten as explicit maximum for inclusive ranges' + ); + $this->assertTrue( + $smallerThan10->isInverted(), + 'ThresholdRange::fromString() identifies inclusive ranges as single exclusive' + ); + } + + /** + * @depends testFromStringProperlyParsesInclusiveRanges + */ + public function testContainsCorrectlyEvaluatesInclusiveRanges() + { + $inside0And10 = ThresholdRange::fromString('@10'); + $this->assertFalse( + $inside0And10->contains(10), + 'ThresholdRange::contains() identifies ten as greater than ten' + ); + $this->assertTrue( + $inside0And10->contains(11), + 'ThresholdRange::contains() identifies eleven as smaller than or equal to ten' + ); + $this->assertTrue( + $inside0And10->contains(-1), + 'ThresholdRange::contains() identifies negative values as greater than or equal to zero' + ); + + $inside10And20 = ThresholdRange::fromString('@10:20'); + $this->assertFalse( + $inside10And20->contains(20), + 'ThresholdRange::contains() identifies twenty as greater than twenty' + ); + $this->assertTrue( + $inside10And20->contains(21), + 'ThresholdRange::contains() identifies twenty-one as smaller than or equal to twenty' + ); + $this->assertTrue( + $inside10And20->contains(9), + 'ThresholdRange::contains() identifies nine as greater than or equal to ten' + ); + + $greaterThan10 = ThresholdRange::fromString('@10:'); + $this->assertFalse( + $greaterThan10->contains(PHP_INT_MAX), + 'ThresholdRange::contains() identifies infinity as smaller than ten' + ); + $this->assertTrue( + $greaterThan10->contains(9), + 'ThresholdRange::contains() identifies nine as greater than or equal to ten' + ); + + $smallerThan10 = ThresholdRange::fromString('@~:10'); + $this->assertFalse( + $smallerThan10->contains(~PHP_INT_MAX), + 'ThresholdRange::contains() identifies negative infinity as greater than ten' + ); + $this->assertTrue( + $smallerThan10->contains(11), + 'ThresholdRange::contains() identifies eleven as smaller than or equal to ten' + ); + } + + public function testFromStringProperlyParsesEmptyThresholds() + { + $emptyThreshold = ThresholdRange::fromString(''); + $this->assertNull( + $emptyThreshold->getMin(), + 'ThresholdRange::fromString() does not identify negative infinity as implicit minimum for empty strings' + ); + $this->assertNull( + $emptyThreshold->getMax(), + 'ThresholdRange::fromString() does not identify infinity as implicit maximum for empty strings' + ); + $this->assertFalse( + $emptyThreshold->isInverted(), + 'ThresholdRange::fromString() identifies empty strings as inclusive ranges rather than exclusive' + ); + } + + /** + * @depends testFromStringProperlyParsesEmptyThresholds + */ + public function testContainsEvaluatesEverythingToTrueForEmptyThresholds() + { + $emptyThreshold = ThresholdRange::fromString(''); + $this->assertTrue( + $emptyThreshold->contains(0), + 'ThresholdRange::contains() does not identify zero as valid without any threshold' + ); + $this->assertTrue( + $emptyThreshold->contains(10), + 'ThresholdRange::contains() does not identify ten as valid without any threshold' + ); + $this->assertTrue( + $emptyThreshold->contains(PHP_INT_MAX), + 'ThresholdRange::contains() does not identify infinity as valid without any threshold' + ); + $this->assertTrue( + $emptyThreshold->contains(~PHP_INT_MAX), + 'ThresholdRange::contains() does not identify negative infinity as valid without any threshold' + ); + } + + public function testInvalidThresholdNotationsAreRenderedAsIs() + { + $this->assertSame( + ':', + (string) ThresholdRange::fromString(':') + ); + $this->assertSame( + '~:', + (string) ThresholdRange::fromString('~:') + ); + $this->assertSame( + '20:10', + (string) ThresholdRange::fromString('20:10') + ); + $this->assertSame( + '10@', + (string) ThresholdRange::fromString('10@') + ); + $this->assertSame( + 'foo', + (string) ThresholdRange::fromString('foo') + ); + $this->assertSame( + '4,4:2,2', + (string) ThresholdRange::fromString('4,4:2,2') + ); + } + + public function testInvalidThresholdNotationsConsideredInValid() + { + $this->assertFalse( + ThresholdRange::fromString('10@')->isValid(), + 'Invalid threshold notation 10@ considered as valid' + ); + $this->assertFalse( + ThresholdRange::fromString('foo')->isValid(), + 'Invalid threshold notation foo considered as valid' + ); + $this->assertFalse( + ThresholdRange::fromString('4,4:2,2')->isValid(), + 'Invalid threshold notation 4,4:2,2 considered as valid' + ); + } +} |