summaryrefslogtreecommitdiffstats
path: root/test/php
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 11:44:46 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 11:44:46 +0000
commitb18bc644404e02b57635bfcc8258e85abb141146 (patch)
tree686512eacb2dba0055277ef7ec2f28695b3418ea /test/php
parentInitial commit. (diff)
downloadicingadb-web-b18bc644404e02b57635bfcc8258e85abb141146.tar.xz
icingadb-web-b18bc644404e02b57635bfcc8258e85abb141146.zip
Adding upstream version 1.1.1.upstream/1.1.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'test/php')
-rw-r--r--test/php/Lib/PerfdataSetWithPublicData.php12
-rw-r--r--test/php/application/clicommands/MigrateCommandTest.php1727
-rw-r--r--test/php/library/Icingadb/Common/MacrosTest.php174
-rw-r--r--test/php/library/Icingadb/Common/StateBadgesTest.php86
-rw-r--r--test/php/library/Icingadb/Model/CustomvarFlatTest.php121
-rw-r--r--test/php/library/Icingadb/Util/PerfdataSetTest.php120
-rw-r--r--test/php/library/Icingadb/Util/PerfdataTest.php591
-rw-r--r--test/php/library/Icingadb/Util/ThresholdRangeTest.php343
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'
+ );
+ }
+}