diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-14 13:17:31 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-14 13:17:31 +0000 |
commit | f66ab8dae2f3d0418759f81a3a64dc9517a62449 (patch) | |
tree | fbff2135e7013f196b891bbde54618eb050e4aaf /test/php/library/Director/Objects | |
parent | Initial commit. (diff) | |
download | icingaweb2-module-director-f66ab8dae2f3d0418759f81a3a64dc9517a62449.tar.xz icingaweb2-module-director-f66ab8dae2f3d0418759f81a3a64dc9517a62449.zip |
Adding upstream version 1.10.2.upstream/1.10.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'test/php/library/Director/Objects')
27 files changed, 2680 insertions, 0 deletions
diff --git a/test/php/library/Director/Objects/HostApplyMatchesTest.php b/test/php/library/Director/Objects/HostApplyMatchesTest.php new file mode 100644 index 0000000..b9f22ca --- /dev/null +++ b/test/php/library/Director/Objects/HostApplyMatchesTest.php @@ -0,0 +1,93 @@ +<?php + +namespace Tests\Icinga\Module\Director\Objects; + +use Icinga\Data\Filter\Filter; +use Icinga\Module\Director\Objects\HostApplyMatches; +use Icinga\Module\Director\Objects\IcingaHost; +use Icinga\Module\Director\Test\BaseTestCase; + +class HostApplyMatchesTest extends BaseTestCase +{ + public function testExactMatches() + { + $matcher = HostApplyMatches::prepare($this->sampleHost()); + $this->assertTrue( + $matcher->matchesFilter( + Filter::fromQueryString('host.name=%22aha%22') + ) + ); + $this->assertFalse( + $matcher->matchesFilter( + Filter::fromQueryString('host.name=%22ahaa%22') + ) + ); + } + + public function testWildcardMatches() + { + $matcher = HostApplyMatches::prepare($this->sampleHost()); + $this->assertTrue( + $matcher->matchesFilter( + Filter::fromQueryString('host.name=%22ah*%22') + ) + ); + $this->assertTrue( + $matcher->matchesFilter( + Filter::fromQueryString('host.name=%22*h*%22') + ) + ); + $this->assertFalse( + $matcher->matchesFilter( + Filter::fromQueryString('host.name=%22*g*%22') + ) + ); + } + + public function testStringVariableMatches() + { + $matcher = HostApplyMatches::prepare($this->sampleHost()); + $this->assertTrue( + $matcher->matchesFilter( + Filter::fromQueryString('host.vars.location=%22*urem*%22') + ) + ); + $this->assertTrue( + $matcher->matchesFilter( + Filter::fromQueryString('host.vars.location=%22Nuremberg%22') + ) + ); + $this->assertFalse( + $matcher->matchesFilter( + Filter::fromQueryString('host.vars.location=%22Nurembergg%22') + ) + ); + } + + public function testArrayVariableMatches() + { + $matcher = HostApplyMatches::prepare($this->sampleHost()); + $this->assertTrue( + $matcher->matchesFilter( + Filter::fromQueryString('%22Amazing%22=host.vars.tags') + ) + ); + $this->assertFalse( + $matcher->matchesFilter( + Filter::fromQueryString('%22Amazingg%22=host.vars.tags') + ) + ); + } + + protected function sampleHost() + { + return IcingaHost::create(array( + 'object_type' => 'object', + 'object_name' => 'aha', + 'vars' => array( + 'location' => 'Nuremberg', + 'tags' => array('Special', 'Amazing'), + ) + ), $this->getDb()); + } +} diff --git a/test/php/library/Director/Objects/HostGroupMembershipResolverTest.php b/test/php/library/Director/Objects/HostGroupMembershipResolverTest.php new file mode 100644 index 0000000..cf2fb36 --- /dev/null +++ b/test/php/library/Director/Objects/HostGroupMembershipResolverTest.php @@ -0,0 +1,353 @@ +<?php + +namespace Tests\Icinga\Module\Director\Objects; + +use Icinga\Exception\NotFoundError; +use Icinga\Module\Director\Data\Db\DbObjectTypeRegistry; +use Icinga\Module\Director\Exception\DuplicateKeyException; +use Icinga\Module\Director\Objects\HostGroupMembershipResolver; +use Icinga\Module\Director\Objects\IcingaObject; +use Icinga\Module\Director\Repository\IcingaTemplateRepository; +use Icinga\Module\Director\Test\BaseTestCase; + +class HostGroupMembershipResolverTest extends BaseTestCase +{ + const PREFIX = '__groupmembership'; + const TYPE = 'host'; + + public function setUp() + { + IcingaTemplateRepository::clear(); + } + + public static function cleanArtifacts() + { + $db = static::getDb()->getDbAdapter(); + + $where = sprintf("object_name LIKE '%s%%'", self::PREFIX); + + $db->delete('icinga_' . self::TYPE . 'group', $where); + + $db->delete('icinga_' . self::TYPE, $where . " AND object_type = 'object'"); + $db->delete('icinga_' . self::TYPE, $where); + } + + public static function setUpBeforeClass() + { + static::cleanArtifacts(); + } + + public static function tearDownAfterClass() + { + static::cleanArtifacts(); + } + + /** + * @param string $type + * @param string $name + * @param array $props + * + * @return IcingaObject + * @throws DuplicateKeyException + * @throws \Icinga\Exception\ConfigurationError + */ + protected function object($type, $name, $props = []) + { + $db = $this->getDb(); + $fullName = self::PREFIX . $name; + $object = null; + + try { + $object = IcingaObject::loadByType($type, $fullName, $db); + + foreach ($props as $k => $v) { + $object->set($k, $v); + } + + $object->store(); + } catch (NotFoundError $e) { + $object = null; + } + + if ($object === null) { + $object = IcingaObject::createByType($type, array_merge([ + 'object_name' => $fullName, + 'object_type' => 'object', + ], $props), $this->getDb()); + + $object->store(); + } + + return $object; + } + + protected function objects($type) + { + $dummy = DbObjectTypeRegistry::newObject($type); + + $table = $dummy->getTableName(); + $query = $this->getDb()->getDbAdapter()->select() + ->from($table) + ->where('object_name LIKE ?', self::PREFIX . '%'); + + $objects = []; + $l = strlen(self::PREFIX); + + foreach ($dummy::loadAll($this->getDb(), $query) as $object) { + $key = substr($object->getObjectName(), $l); + $objects[$key] = $object; + } + + return $objects; + } + + protected function resolved() + { + $db = $this->getDb()->getDbAdapter(); + + $select = $db->select() + ->from( + ['r' => 'icinga_' . self::TYPE . 'group_' . self::TYPE . '_resolved'], + [] + )->join( + ['o' => 'icinga_' . self::TYPE], + 'o.id = r.' . self::TYPE . '_id', + ['object' => 'object_name'] + )->join( + ['g' => 'icinga_' . self::TYPE . 'group'], + 'g.id = r.' . self::TYPE . 'group_id', + ['groupname' => 'object_name'] + ); + + $map = []; + $l = strlen(self::PREFIX); + + foreach ($db->fetchAll($select) as $row) { + $o = $row->object; + $g = $row->groupname; + + if (! substr($o, 0, $l) === self::PREFIX) { + continue; + } + $o = substr($o, $l); + + if (! substr($g, 0, $l) === self::PREFIX) { + continue; + } + $g = substr($g, $l); + + if (! array_key_exists($o, $map)) { + $map[$o] = []; + } + + $map[$o][] = $g; + } + + return $map; + } + + /** + * Creates: + * + * - 1 template + * - 10 hosts importing the template with a var match_var=magic + * + * @throws DuplicateKeyException + * @throws \Icinga\Exception\ConfigurationError + */ + public function testCreateHosts() + { + // template that sets a group later + $template = $this->object('host', 'template', [ + 'object_type' => 'template', + ]); + $this->assertTrue($template->hasBeenLoadedFromDb()); + + // hosts to assign groups + for ($i = 1; $i <= 10; $i++) { + $host = $this->object('host', $i, [ + 'imports' => self::PREFIX . 'template', + 'vars.match_var' => 'magic' + ]); + $this->assertTrue($host->hasBeenLoadedFromDb()); + } + } + + /** + * Creates: + * + * - 10 hostgroups applying on hosts with match_var=magic + * - 2 static hostgroups + * + * @throws DuplicateKeyException + * @throws \Icinga\Exception\ConfigurationError + */ + public function testCreateHostgroups() + { + $filter = 'host.vars.match_var=%22magic%22'; + for ($i = 1; $i <= 10; $i++) { + $hostgroup = $this->object('hostgroup', 'apply' . $i, [ + 'assign_filter' => $filter + ]); + $this->assertTrue($hostgroup->hasBeenLoadedFromDb()); + } + + // static groups + for ($i = 1; $i <= 2; $i++) { + $hostgroup = $this->object('hostgroup', 'static' . $i); + $this->assertTrue($hostgroup->hasBeenLoadedFromDb()); + } + } + + /** + * Assigns static groups to: + * + * - the template + * - the first host + * + * @throws DuplicateKeyException + * @throws \Icinga\Exception\ConfigurationError + * + * @depends testCreateHosts + * @depends testCreateHostgroups + */ + public function testAddStaticGroups() + { + // add group to template + $template = $this->object('host', 'template'); + $template->setGroups(self::PREFIX . 'static1'); + $template->store(); + $this->assertFalse($template->hasBeenModified()); + + // add group to first host + $host = $this->object('host', 1); + $host->setGroups(self::PREFIX . 'static2'); + $host->store(); + $this->assertFalse($host->hasBeenModified()); + } + + /** + * Asserts that static groups are resolved for hosts: + * + * - all but first should have static1 + * - first should have static2 + * + * @depends testAddStaticGroups + */ + public function testStaticResolvedMappings() + { + $resolved = $this->resolved(); + + $this->assertArrayHasKey( + 1, + $resolved, + 'Host 1 must have groups resolved' + ); + + $this->assertContains( + 'static2', + $resolved[1], + 'Host template must have static group 1' + ); + + $hosts = $this->objects('host'); + $this->assertNotEmpty($hosts, 'Must have hosts found in DB'); + + foreach ($hosts as $name => $host) { + if ($host->object_type === 'template') { + continue; + } + + $this->assertArrayHasKey( + $name, + $resolved, + 'All hosts must have groups resolved' + ); + + if ($name === 1) { + $this->assertNotContains( + 'static1', + $resolved[$name], + 'First host must not have static group 1' + ); + } else { + $this->assertContains( + 'static1', + $resolved[$name], + 'All hosts but the first must have static group 1' + ); + } + } + } + + /** + * @depends testCreateHostgroups + */ + public function testApplyResolvedMappings() + { + $resolved = $this->resolved(); + + $hosts = $this->objects('host'); + $this->assertNotEmpty($hosts, 'Must have hosts found in DB'); + + foreach ($hosts as $name => $host) { + if ($host->object_type === 'template') { + continue; + } + + $this->assertArrayHasKey($name, $resolved, 'Host must have groups resolved'); + + for ($i = 1; $i <= 10; $i++) { + $this->assertContains( + 'apply' . $i, + $resolved[$name], + 'All Host objects must have all applied groups' + ); + } + } + } + + /** + * @throws DuplicateKeyException + * @throws \Icinga\Exception\ConfigurationError + * + * @depends testAddStaticGroups + */ + public function testChangeAppliedGroupsAfterStatic() + { + $filter = 'host.vars.match_var=%22magic*%22'; + + $hostgroup = $this->object('hostgroup', 'apply1', [ + 'assign_filter' => $filter + ]); + $this->assertTrue($hostgroup->hasBeenLoadedFromDb()); + $this->assertFalse($hostgroup->hasBeenModified()); + + $resolved = $this->resolved(); + + for ($i = 1; $i <= 10; $i++) { + $this->assertContains( + 'apply1', + $resolved[$i], + 'All Host objects must have apply1 group' + ); + } + } + + /** + * @throws \Icinga\Exception\ConfigurationError + * @throws \Zend_Db_Adapter_Exception + * + * @depends testStaticResolvedMappings + * @depends testApplyResolvedMappings + * @depends testChangeAppliedGroupsAfterStatic + */ + public function testFullRecheck() + { + $resolver = new HostGroupMembershipResolver($this->getDb()); + + $resolver->checkDb(); + $this->assertEmpty($resolver->getNewMappings(), 'There should not be any new mappings'); + $this->assertEmpty($resolver->getOutdatedMappings(), 'There should not be any outdated mappings'); + } +} diff --git a/test/php/library/Director/Objects/IcingaCommandTest.php b/test/php/library/Director/Objects/IcingaCommandTest.php new file mode 100644 index 0000000..8e564d8 --- /dev/null +++ b/test/php/library/Director/Objects/IcingaCommandTest.php @@ -0,0 +1,216 @@ +<?php + +namespace Tests\Icinga\Module\Director\Objects; + +use Icinga\Module\Director\Objects\IcingaCommand; +use Icinga\Module\Director\Test\BaseTestCase; + +class IcingaCommandTest extends BaseTestCase +{ + protected $testCommandName = '___TEST___command'; + + public function testCommandsWithArgumentsCanBeCreated() + { + $command = $this->command(); + $command->arguments = array( + '-H' => '$host$' + ); + + $this->assertEquals( + '-H', + $command->arguments()->get('-H')->argument_name + ); + + $this->assertEquals( + '$host$', + $command->toPlainObject()->arguments['-H'] + ); + + $command->arguments()->get('-H')->required = true; + } + + public function testCommandsWithArgumentsCanBeModified() + { + $command = $this->command(); + $command->arguments = array( + '-H' => '$host$' + ); + + $command->arguments = array( + '-x' => (object) array( + 'required' => true + ) + ); + + $this->assertEquals( + null, + $command->arguments()->get('-H') + ); + + $this->assertEquals( + 'y', + $command->arguments()->get('-x')->get('required') + ); + + $this->assertEquals( + true, + $command->toPlainObject()->arguments['-x']->required + ); + } + + public function testCanBePersistedToDb() + { + if ($this->skipForMissingDb()) { + return; + } + + $db = $this->getDb(); + + $command = $this->newCommandWithArguments(); + + $this->assertEquals( + $command->store($db), + true + ); + + + $command->delete(); + } + + public function testCanBeLoadedFromDb() + { + if ($this->skipForMissingDb()) { + return; + } + + $db = $this->getDb(); + + $name = $this->testCommandName; + $command = $this->newCommandWithArguments($db); + $command->store($db); + + $command = IcingaCommand::load($name, $db); + $this->assertEquals( + $command->object_name, + $name + ); + + $command->delete(); + } + + public function testArgumentMotificationsAreDetected() + { + if ($this->skipForMissingDb()) { + return; + } + + $db = $this->getDb(); + + $command = $this->newCommandWithArguments($db); + $command->store($db); + $command->arguments()->set('-H', 'no-host'); + $this->assertTrue($command->hasBeenModified()); + $this->assertTrue($command->store()); + $command->delete(); + } + + protected function newCommandWithArguments() + { + $command = $this->command(); + $command->arguments = array( + '-H' => '$host$', + '-x' => (object) array( + 'required' => true, + 'value' => 'bal' + ) + ); + + return $command; + } + + public function testAbsolutePathsAreDetected() + { + $command = $this->command(); + $command->command = 'C:\\test.exe'; + + $this->assertEquals( + $this->loadRendered('command1'), + (string) $command + ); + + $command->command = '/tmp/bla'; + + $this->assertEquals( + $this->loadRendered('command2'), + (string) $command + ); + + $command->command = 'tmp/bla'; + + $this->assertEquals( + $this->loadRendered('command3'), + (string) $command + ); + + $command->command = '\\\\network\\share\\bla.exe'; + + $this->assertEquals( + $this->loadRendered('command4'), + (string) $command + ); + + $command->command = 'BlahDir + \\network\\share\\bla.exe'; + + $this->assertEquals( + $this->loadRendered('command5'), + (string) $command + ); + + $command->command = 'network\\share\\bla.exe'; + + $this->assertEquals( + $this->loadRendered('command6'), + (string) $command + ); + } + + public function testSimpleSetIfIsRendered() + { + $command = $this->command(); + $command->command = 'bla'; + $command->arguments = array( + '-a' => (object) array( + 'set_if' => '$a$', + ) + ); + + $this->assertEquals( + $this->loadRendered('command7'), + (string) $command + ); + } + + protected function command() + { + return IcingaCommand::create( + array( + 'object_name' => $this->testCommandName, + 'object_type' => 'object' + ), + $this->getDb() + ); + } + + protected function loadRendered($name) + { + return file_get_contents(__DIR__ . '/rendered/' . $name . '.out'); + } + + public function tearDown() + { + $db = $this->getDb(); + if (IcingaCommand::exists($this->testCommandName, $db)) { + IcingaCommand::load($this->testCommandName, $db)->delete(); + } + } +} diff --git a/test/php/library/Director/Objects/IcingaHostTest.php b/test/php/library/Director/Objects/IcingaHostTest.php new file mode 100644 index 0000000..b4902bf --- /dev/null +++ b/test/php/library/Director/Objects/IcingaHostTest.php @@ -0,0 +1,771 @@ +<?php + +namespace Tests\Icinga\Module\Director\Objects; + +use Icinga\Module\Director\Data\PropertiesFilter\ArrayCustomVariablesFilter; +use Icinga\Module\Director\Data\PropertiesFilter\CustomVariablesFilter; +use Icinga\Module\Director\IcingaConfig\IcingaConfig; +use Icinga\Module\Director\Objects\DirectorDatafield; +use Icinga\Module\Director\Objects\IcingaHost; +use Icinga\Module\Director\Objects\IcingaHostGroup; +use Icinga\Module\Director\Objects\IcingaZone; +use Icinga\Module\Director\Test\BaseTestCase; +use Icinga\Exception\IcingaException; + +class IcingaHostTest extends BaseTestCase +{ + protected $testHostName = '___TEST___host'; + protected $testDatafieldName = 'test5'; + + public function testPropertiesCanBeSet() + { + $host = $this->host(); + $host->display_name = 'Something else'; + $this->assertEquals( + $host->display_name, + 'Something else' + ); + } + + public function testCanBeReplaced() + { + $host = $this->host(); + $newHost = IcingaHost::create( + array('display_name' => 'Replaced display'), + $this->getDb() + ); + + $this->assertEquals( + count($host->vars()), + 4 + ); + $this->assertEquals( + $host->address, + '127.0.0.127' + ); + + $host->replaceWith($newHost); + $this->assertEquals( + $host->display_name, + 'Replaced display' + ); + $this->assertEquals( + $host->address, + null + ); + + $this->assertEquals( + count($host->vars()), + 0 + ); + } + + public function testCanBeMerged() + { + $host = $this->host(); + $newHost = IcingaHost::create( + array('display_name' => 'Replaced display'), + $this->getDb() + ); + + $this->assertEquals( + count($host->vars()), + 4 + ); + $this->assertEquals( + $host->address, + '127.0.0.127' + ); + + $host->merge($newHost); + $this->assertEquals( + $host->display_name, + 'Replaced display' + ); + $this->assertEquals( + $host->address, + '127.0.0.127' + ); + $this->assertEquals( + count($host->vars()), + 4 + ); + } + + public function testPropertiesCanBePreservedWhenBeingReplaced() + { + if ($this->skipForMissingDb()) { + return; + } + + $db = $this->getDb(); + $this->host()->store($db); + $host = IcingaHost::load($this->testHostName, $db); + + $newHost = IcingaHost::create( + array( + 'display_name' => 'Replaced display', + 'address' => '1.2.2.3', + 'vars' => array( + 'test1' => 'newstring', + 'test2' => 18, + 'initially' => 'set and then preserved', + ) + ), + $this->getDb() + ); + + $preserve = array('address', 'vars.test1', 'vars.initially'); + $host->replaceWith($newHost, $preserve); + $this->assertEquals( + $host->address, + '127.0.0.127' + ); + + $this->assertEquals( + $host->{'vars.test2'}, + 18 + ); + + $this->assertEquals( + $host->vars()->test2->getValue(), + 18 + ); + + $this->assertEquals( + $host->{'vars.initially'}, + 'set and then preserved' + ); + + $this->assertFalse( + array_key_exists('address', $host->getModifiedProperties()), + 'Preserved property stays unmodified' + ); + + $newHost->set('vars.initially', 'changed later on'); + $newHost->set('vars.test2', 19); + + $host->replaceWith($newHost, $preserve); + $this->assertEquals( + $host->{'vars.initially'}, + 'set and then preserved' + ); + + $this->assertEquals( + $host->get('vars.test2'), + 19 + ); + + + $host->delete(); + } + + public function testDistinctCustomVarsCanBeSetWithoutSideEffects() + { + $host = $this->host(); + $host->set('vars.test2', 18); + $this->assertEquals( + $host->vars()->test1->getValue(), + 'string' + ); + $this->assertEquals( + $host->vars()->test2->getValue(), + 18 + ); + $this->assertEquals( + $host->vars()->test3->getValue(), + false + ); + } + + public function testVarsArePersisted() + { + if ($this->skipForMissingDb()) { + return; + } + + $db = $this->getDb(); + $this->host()->store($db); + $host = IcingaHost::load($this->testHostName, $db); + $this->assertEquals( + $host->vars()->test1->getValue(), + 'string' + ); + $this->assertEquals( + $host->vars()->test2->getValue(), + 17 + ); + $this->assertEquals( + $host->vars()->test3->getValue(), + false + ); + $this->assertEquals( + $host->vars()->test4->getValue(), + (object) array( + 'this' => 'is', + 'a' => array( + 'dict', + 'ionary' + ) + ) + ); + } + + public function testRendersCorrectly() + { + $this->assertEquals( + (string) $this->host(), + $this->loadRendered('host1') + ); + } + + public function testGivesPlainObjectWithInvalidUnresolvedDependencies() + { + $props = $this->getDummyRelatedProperties(); + + $host = $this->host(); + foreach ($props as $k => $v) { + $host->$k = $v; + } + + $plain = $host->toPlainObject(); + foreach ($props as $k => $v) { + $this->assertEquals($plain->$k, $v); + } + } + + public function testCorrectlyStoresLazyRelations() + { + if ($this->skipForMissingDb()) { + return; + } + $db = $this->getDb(); + $host = $this->host(); + $host->zone = '___TEST___zone'; + $this->assertEquals( + '___TEST___zone', + $host->zone + ); + + $zone = $this->newObject('zone', '___TEST___zone'); + $zone->store($db); + + $host->store($db); + $host->delete(); + $zone->delete(); + } + + /** + * @expectedException \RuntimeException + */ + public function testFailsToStoreWithMissingLazyRelations() + { + if ($this->skipForMissingDb()) { + return; + } + $db = $this->getDb(); + $host = $this->host(); + $host->zone = '___TEST___zone'; + $host->store($db); + } + + public function testHandlesUnmodifiedProperties() + { + $this->markTestSkipped('Currently broken, needs to be fixed'); + + if ($this->skipForMissingDb()) { + return; + } + + $db = $this->getDb(); + $host = $this->host(); + $host->store($db); + + $parent = $this->newObject('host', '___TEST___parent'); + $parent->store($db); + $host->imports = '___TEST___parent'; + + $host->store($db); + + $plain = $host->getPlainUnmodifiedObject(); + $this->assertEquals( + 'string', + $plain->vars->test1 + ); + $host->vars()->set('test1', 'nada'); + + $host->store(); + + $plain = $host->getPlainUnmodifiedObject(); + $this->assertEquals( + 'nada', + $plain->vars->test1 + ); + + $host->vars()->set('test1', 'string'); + $plain = $host->getPlainUnmodifiedObject(); + $this->assertEquals( + 'nada', + $plain->vars->test1 + ); + + $plain = $host->getPlainUnmodifiedObject(); + $test = IcingaHost::create((array) $plain); + + $this->assertEquals( + $this->loadRendered('host3'), + (string) $test + ); + + $host->delete(); + $parent->delete(); + } + + public function testRendersWithInvalidUnresolvedDependencies() + { + $newHost = $this->host(); + $newHost->zone = 'invalid'; + $newHost->check_command = 'unknown'; + $newHost->event_command = 'What event?'; + $newHost->check_period = 'Not time is a good time @ nite'; + $newHost->command_endpoint = 'nirvana'; + + $this->assertEquals( + (string) $newHost, + $this->loadRendered('host2') + ); + } + + /** + * @expectedException \RuntimeException + */ + public function testFailsToStoreWithInvalidUnresolvedDependencies() + { + if ($this->skipForMissingDb()) { + return; + } + + $host = $this->host(); + $host->zone = 'invalid'; + $host->store($this->getDb()); + } + + public function testRendersToTheCorrectZone() + { + if ($this->skipForMissingDb()) { + return; + } + + $db = $this->getDb(); + $host = $this->host()->setConnection($db); + $masterzone = $db->getMasterZoneName(); + + $config = new IcingaConfig($db); + $host->renderToConfig($config); + $this->assertEquals( + array('zones.d/' . $masterzone . '/hosts.conf'), + $config->getFileNames() + ); + + $zone = $this->newObject('zone', '___TEST___zone'); + $zone->store($db); + + $config = new IcingaConfig($db); + $host->zone = '___TEST___zone'; + $host->renderToConfig($config); + $this->assertEquals( + array('zones.d/___TEST___zone/hosts.conf'), + $config->getFileNames() + ); + + $host->has_agent = true; + $host->master_should_connect = true; + $host->accept_config = true; + + $config = new IcingaConfig($db); + $host->renderToConfig($config); + $this->assertEquals( + array( + 'zones.d/___TEST___zone/hosts.conf', + 'zones.d/___TEST___zone/agent_endpoints.conf', + 'zones.d/___TEST___zone/agent_zones.conf' + ), + $config->getFileNames() + ); + + $host->object_type = 'template'; + $host->zone_id = null; + + $config = new IcingaConfig($db); + $host->renderToConfig($config); + $this->assertEquals( + array('zones.d/director-global/host_templates.conf'), + $config->getFileNames() + ); + } + + public function testWhetherTwoHostsCannotBeStoredWithTheSameApiKey() + { + if ($this->skipForMissingDb()) { + return; + } + + $db = $this->getDb(); + $a = IcingaHost::create(array( + 'object_name' => '___TEST___a', + 'object_type' => 'object', + 'api_key' => 'a' + ), $db); + $b = IcingaHost::create(array( + 'object_name' => '___TEST___b', + 'object_type' => 'object', + 'api_key' => 'a' + ), $db); + + $a->store(); + try { + $b->store(); + } catch (\RuntimeException $e) { + $msg = $e->getMessage(); + $matchMysql = strpos( + $msg, + "Duplicate entry 'a' for key 'api_key'" + ) !== false; + + $matchPostgres = strpos( + $msg, + 'Unique violation' + ) !== false; + + $this->assertTrue( + $matchMysql || $matchPostgres, + 'Exception message does not tell about unique constraint violation' + ); + $a->delete(); + } + } + + public function testWhetherHostCanBeLoadedWithValidApiKey() + { + if ($this->skipForMissingDb()) { + return; + } + + $db = $this->getDb(); + $a = IcingaHost::create(array( + 'object_name' => '___TEST___a', + 'object_type' => 'object', + 'api_key' => 'a1a1a1' + ), $db); + $b = IcingaHost::create(array( + 'object_name' => '___TEST___b', + 'object_type' => 'object', + 'api_key' => 'b1b1b1' + ), $db); + $a->store(); + $b->store(); + + $this->assertEquals( + IcingaHost::loadWithApiKey('b1b1b1', $db)->object_name, + '___TEST___b' + ); + + $a->delete(); + $b->delete(); + } + + /** + * @expectedException \Icinga\Exception\NotFoundError + */ + public function testWhetherInvalidApiKeyThrows404() + { + if ($this->skipForMissingDb()) { + return; + } + + $db = $this->getDb(); + IcingaHost::loadWithApiKey('No___such___key', $db); + } + + public function testEnumProperties() + { + if ($this->skipForMissingDb()) { + return; + } + + $db = $this->getDb(); + $properties = IcingaHost::enumProperties($db); + + $this->assertEquals( + array( + 'Host properties' => $this->getDefaultHostProperties() + ), + $properties + ); + } + + public function testEnumPropertiesWithCustomVars() + { + if ($this->skipForMissingDb()) { + return; + } + + $db = $this->getDb(); + + $host = $this->host(); + $host->store($db); + + $properties = IcingaHost::enumProperties($db); + $this->assertEquals( + array( + 'Host properties' => $this->getDefaultHostProperties(), + 'Custom variables' => array( + 'vars.test1' => 'test1', + 'vars.test2' => 'test2', + 'vars.test3' => 'test3', + 'vars.test4' => 'test4' + ) + ), + $properties + ); + } + + public function testEnumPropertiesWithPrefix() + { + if ($this->skipForMissingDb()) { + return; + } + + $db = $this->getDb(); + + $host = $this->host(); + $host->store($db); + + $properties = IcingaHost::enumProperties($db, 'host.'); + $this->assertEquals( + array( + 'Host properties' => $this->getDefaultHostProperties('host.'), + 'Custom variables' => array( + 'host.vars.test1' => 'test1', + 'host.vars.test2' => 'test2', + 'host.vars.test3' => 'test3', + 'host.vars.test4' => 'test4' + ) + ), + $properties + ); + } + + public function testEnumPropertiesWithFilter() + { + if ($this->skipForMissingDb()) { + return; + } + + $db = $this->getDb(); + + DirectorDatafield::create(array( + 'varname' => $this->testDatafieldName, + 'caption' => 'Blah', + 'description' => '', + 'datatype' => 'Icinga\Module\Director\DataType\DataTypeArray', + 'format' => 'json' + ))->store($db); + + $host = $this->host(); + $host->{'vars.test5'} = array('a', '1'); + $host->store($db); + + $properties = IcingaHost::enumProperties($db, '', new CustomVariablesFilter()); + $this->assertEquals( + array( + 'Custom variables' => array( + 'vars.test1' => 'test1', + 'vars.test2' => 'test2', + 'vars.test3' => 'test3', + 'vars.test4' => 'test4', + 'vars.test5' => 'test5 (Blah)' + ) + ), + $properties + ); + } + + public function testEnumPropertiesWithArrayFilter() + { + if ($this->skipForMissingDb()) { + return; + } + + $db = $this->getDb(); + + DirectorDatafield::create(array( + 'varname' => $this->testDatafieldName, + 'caption' => 'Blah', + 'description' => '', + 'datatype' => 'Icinga\Module\Director\DataType\DataTypeArray', + 'format' => 'json' + ))->store($db); + + $host = $this->host(); + $host->{'vars.test5'} = array('a', '1'); + $host->store($db); + + $properties = IcingaHost::enumProperties($db, '', new ArrayCustomVariablesFilter()); + $this->assertEquals( + array( + 'Custom variables' => array( + 'vars.test5' => 'test5 (Blah)' + ) + ), + $properties + ); + } + + public function testMergingObjectKeepsGroupsIfNotGiven() + { + $one = IcingaHostGroup::create([ + 'object_name' => 'one', + 'object_type' => 'object', + ]); + $two = IcingaHostGroup::create([ + 'object_name' => 'two', + 'object_type' => 'object', + ]); + $a = IcingaHost::create([ + 'object_name' => 'one', + 'object_type' => 'object', + 'imports' => [], + 'address' => '127.0.0.2', + 'groups' => [$one, $two] + ]); + + $b = IcingaHost::create([ + 'object_name' => 'one', + 'object_type' => 'object', + 'imports' => [], + 'address' => '127.0.0.42', + ]); + + $a->merge($b); + $this->assertEquals( + '127.0.0.42', + $a->get('address') + ); + $this->assertEquals( + ['one', 'two'], + $a->getGroups() + ); + } + + protected function getDummyRelatedProperties() + { + return array( + 'zone' => 'invalid', + 'check_command' => 'unknown', + 'event_command' => 'What event?', + 'check_period' => 'Not time is a good time @ nite', + 'command_endpoint' => 'nirvana', + ); + } + + protected function host() + { + return IcingaHost::create(array( + 'object_name' => $this->testHostName, + 'object_type' => 'object', + 'address' => '127.0.0.127', + 'display_name' => 'Whatever', + 'vars' => array( + 'test1' => 'string', + 'test2' => 17, + 'test3' => false, + 'test4' => (object) array( + 'this' => 'is', + 'a' => array( + 'dict', + 'ionary' + ) + ) + ) + ), $this->getDb()); + } + + protected function getDefaultHostProperties($prefix = '') + { + return array( + "${prefix}name" => "name", + "${prefix}action_url" => "action_url", + "${prefix}address" => "address", + "${prefix}address6" => "address6", + "${prefix}api_key" => "api_key", + "${prefix}check_command" => "check_command", + "${prefix}check_interval" => "check_interval", + "${prefix}check_period" => "check_period", + "${prefix}check_timeout" => "check_timeout", + "${prefix}command_endpoint" => "command_endpoint", + "${prefix}display_name" => "display_name", + "${prefix}enable_active_checks" => "enable_active_checks", + "${prefix}enable_event_handler" => "enable_event_handler", + "${prefix}enable_flapping" => "enable_flapping", + "${prefix}enable_notifications" => "enable_notifications", + "${prefix}enable_passive_checks" => "enable_passive_checks", + "${prefix}enable_perfdata" => "enable_perfdata", + "${prefix}event_command" => "event_command", + "${prefix}flapping_threshold_high" => "flapping_threshold_high", + "${prefix}flapping_threshold_low" => "flapping_threshold_low", + "${prefix}icon_image" => "icon_image", + "${prefix}icon_image_alt" => "icon_image_alt", + "${prefix}max_check_attempts" => "max_check_attempts", + "${prefix}notes" => "notes", + "${prefix}notes_url" => "notes_url", + "${prefix}retry_interval" => "retry_interval", + "${prefix}volatile" => "volatile", + "${prefix}zone" => "zone", + "${prefix}groups" => "Groups", + "${prefix}templates" => "templates" + ); + } + protected function loadRendered($name) + { + return file_get_contents(__DIR__ . '/rendered/' . $name . '.out'); + } + + public function tearDown() + { + if ($this->hasDb()) { + $db = $this->getDb(); + $kill = array($this->testHostName, '___TEST___parent', '___TEST___a', '___TEST___b'); + foreach ($kill as $name) { + if (IcingaHost::exists($name, $db)) { + IcingaHost::load($name, $db)->delete(); + } + } + + $kill = array('___TEST___zone'); + foreach ($kill as $name) { + if (IcingaZone::exists($name, $db)) { + IcingaZone::load($name, $db)->delete(); + } + } + + $this->deleteDatafields(); + } + } + + protected function deleteDatafields() + { + $db = $this->getDb(); + $dbAdapter = $db->getDbAdapter(); + $kill = array($this->testDatafieldName); + + foreach ($kill as $name) { + $query = $dbAdapter->select() + ->from('director_datafield') + ->where('varname = ?', $name); + foreach (DirectorDatafield::loadAll($db, $query, 'id') as $datafield) { + $datafield->delete(); + } + } + } +} diff --git a/test/php/library/Director/Objects/IcingaNotificationTest.php b/test/php/library/Director/Objects/IcingaNotificationTest.php new file mode 100644 index 0000000..9d9436a --- /dev/null +++ b/test/php/library/Director/Objects/IcingaNotificationTest.php @@ -0,0 +1,248 @@ +<?php + +namespace Tests\Icinga\Module\Director\Objects; + +use Icinga\Module\Director\Objects\IcingaHost; +use Icinga\Module\Director\Objects\IcingaService; +use Icinga\Module\Director\Objects\IcingaNotification; +use Icinga\Module\Director\Objects\IcingaUser; +use Icinga\Module\Director\Objects\IcingaUsergroup; +use Icinga\Module\Director\Test\BaseTestCase; + +class IcingaNotificationTest extends BaseTestCase +{ + protected $testUserName1 = '___TEST___user1'; + + protected $testUserName2 = '___TEST___user2'; + + protected $testNotificationName = '___TEST___notification'; + + public function testPropertiesCanBeSet() + { + $n = $this->notification(); + $n->notification_interval = '10m'; + $this->assertEquals( + $n->notification_interval, + 600 + ); + } + + public function testCanBeStoredAndDeletedWithRelatedUserPassedAsString() + { + if ($this->skipForMissingDb()) { + return; + } + $db = $this->getDb(); + + $user = $this->user1(); + $user->store($db); + + $n = $this->notification(); + $n->users = $user->object_name; + $this->assertTrue($n->store($db)); + $this->assertTrue($n->delete()); + $user->delete(); + } + + public function testCanBeStoredAndDeletedWithMultipleRelatedUsers() + { + if ($this->skipForMissingDb()) { + return; + } + $db = $this->getDb(); + + $user1 = $this->user1(); + $user1->store($db); + + $user2 = $this->user2(); + $user2->store($db); + + $n = $this->notification(); + $n->users = array($user1->object_name, $user2->object_name); + $this->assertTrue($n->store($db)); + $this->assertTrue($n->delete()); + $user1->delete(); + $user2->delete(); + } + + public function testGivesPlainObjectWithRelatedUsers() + { + if ($this->skipForMissingDb()) { + return; + } + $db = $this->getDb(); + + $user1 = $this->user1(); + $user1->store($db); + + $user2 = $this->user2(); + $user2->store($db); + + $n = $this->notification(); + $n->users = array($user1->object_name, $user2->object_name); + $n->store($db); + $this->assertEquals( + (object) array( + 'object_name' => $this->testNotificationName, + 'object_type' => 'object', + 'users' => array( + $user1->object_name, + $user2->object_name + ) + ), + $n->toPlainObject(false, true) + ); + + $n = IcingaNotification::load($n->object_name, $db); + $this->assertEquals( + (object) array( + 'object_name' => $this->testNotificationName, + 'object_type' => 'object', + 'users' => array( + $user1->object_name, + $user2->object_name + ) + ), + $n->toPlainObject(false, true) + ); + $this->assertEquals( + array(), + $n->toPlainObject()->user_groups + ); + $n->delete(); + + $user1->delete(); + $user2->delete(); + } + + public function testHandlesChangesForStoredRelations() + { + if ($this->skipForMissingDb()) { + return; + } + $db = $this->getDb(); + + $user1 = $this->user1(); + $user1->store($db); + + $user2 = $this->user2(); + $user2->store($db); + + $n = $this->notification(); + $n->users = array($user1->object_name, $user2->object_name); + $n->store($db); + + $n = IcingaNotification::load($n->object_name, $db); + $this->assertFalse($n->hasBeenModified()); + + $n->users = array($user2->object_name); + $this->assertTrue($n->hasBeenModified()); + + $n->store(); + + $n = IcingaNotification::load($n->object_name, $db); + $this->assertEquals( + array($user2->object_name), + $n->users + ); + + $n->users = array(); + $n->store(); + + $n = IcingaNotification::load($n->object_name, $db); + $this->assertEquals( + array(), + $n->users + ); + + // Should be fixed with lazy loading: + // $n->users = array($user1->object_name, $user2->object_name); + // $this->assertFalse($n->hasBeenModified()); + + $n->delete(); + + $user1->delete(); + $user2->delete(); + } + + public function testRendersConfigurationWithRelatedUsers() + { + if ($this->skipForMissingDb()) { + return; + } + $db = $this->getDb(); + + $user1 = $this->user1(); + $user1->store($db); + + $user2 = $this->user2(); + $user2->store($db); + + $n = $this->notification(); + $n->users = array($user1->object_name, $user2->object_name); + + $this->assertEquals( + $this->loadRendered('notification1'), + (string) $n + ); + } + + public function testLazyUsersCanBeSet() + { + $this->markTestSkipped('Setting lazy properties not yet completed'); + + $n = $this->notification(); + $n->users = 'bla'; + } + + protected function user1() + { + return IcingaUser::create(array( + 'object_name' => $this->testUserName1, + 'object_type' => 'object', + 'email' => 'nowhere@example.com', + ), $this->getDb()); + } + + protected function user2() + { + return IcingaUser::create(array( + 'object_name' => $this->testUserName2, + 'object_type' => 'object', + 'email' => 'nowhere.else@example.com', + ), $this->getDb()); + } + + protected function notification() + { + return IcingaNotification::create(array( + 'object_name' => $this->testNotificationName, + 'object_type' => 'object', + ), $this->getDb()); + } + + protected function loadRendered($name) + { + return file_get_contents(__DIR__ . '/rendered/' . $name . '.out'); + } + + public function tearDown() + { + if ($this->hasDb()) { + $db = $this->getDb(); + $kill = array($this->testNotificationName); + foreach ($kill as $name) { + if (IcingaNotification::exists($name, $db)) { + IcingaNotification::load($name, $db)->delete(); + } + } + + $kill = array($this->testUserName1, $this->testUserName2); + foreach ($kill as $name) { + if (IcingaUser::exists($name, $db)) { + IcingaUser::load($name, $db)->delete(); + } + } + } + } +} diff --git a/test/php/library/Director/Objects/IcingaServiceSetTest.php b/test/php/library/Director/Objects/IcingaServiceSetTest.php new file mode 100644 index 0000000..ad7c135 --- /dev/null +++ b/test/php/library/Director/Objects/IcingaServiceSetTest.php @@ -0,0 +1,183 @@ +<?php + +namespace Tests\Icinga\Module\Director\Objects; + +use Icinga\Module\Director\Objects\IcingaServiceSet; +use Icinga\Module\Director\Test\IcingaObjectTestCase; + +class IcingaServiceSetTest extends IcingaObjectTestCase +{ + protected $table = 'icinga_service_set'; + protected $testObjectName = '___TEST___set'; + + public function setUp() + { + $this->assertNull($this->subject, 'subject must have been taken down before!'); + + if ($this->hasDb()) { + $this->subject = IcingaServiceSet::create(array( + 'object_name' => $this->testObjectName, + 'object_type' => 'template', + )); + $this->subject->store($this->getDb()); + } + } + + public function testUpdatingSet() + { + $set = IcingaServiceSet::load($this->testObjectName, $this->getDb()); + $this->assertTrue($set->hasBeenLoadedFromDb()); + + $set->set('description', 'This is a set created by Phpunit!'); + $this->assertTrue($set->hasBeenModified()); + $set->store(); + + $set->set('assign_filter', 'host.name=foobar'); + $this->assertTrue($set->hasBeenModified()); + $set->store(); + + $this->assertFalse($set->hasBeenModified()); + } + + public function testAddingSetToHost() + { + $host = $this->createObject('for_set', 'icinga_host', array( + 'object_type' => 'object', + 'address' => '1.2.3.4', + )); + + $set = IcingaServiceSet::create(array( + 'object_name' => $this->testObjectName, + 'object_type' => 'object', + ), $this->getDb()); // TODO: fails if db not set here... + + $set->setImports($this->testObjectName); + $this->assertTrue($set->hasBeenModified()); + $this->assertEquals(array($this->testObjectName), $set->getImports()); + + $set->set('host', $host->getObjectName()); + + $set->store(); + $this->prepareObjectTearDown($set); + $this->assertFalse($set->hasBeenModified()); + } + + public function testDeletingHostWithSet() + { + $this->createObject('for_set', 'icinga_host', array( + 'object_type' => 'object', + 'address' => '1.2.3.4', + ), false)->store(); + + $host = $this->loadObject('for_set', 'icinga_host'); + $host->delete(); + + $this->checkForDanglingHostSets(); + } + + public function testAddingServicesToSet() + { + $set = IcingaServiceSet::load($this->testObjectName, $this->getDb()); + + // TODO: setting service_set by name should work too... + + $serviceA = $this->createObject('serviceA', 'icinga_service', array( + 'object_type' => 'apply', + 'service_set_id' => $set->getAutoincId(), + )); + $nameA = $serviceA->getObjectName(); + + $serviceB = $this->createObject('serviceB', 'icinga_service', array( + 'object_type' => 'apply', + 'service_set_id' => $set->getAutoincId(), + )); + $nameB = $serviceB->getObjectName(); + + $services = $set->getServiceObjects(); + + $this->assertCount(2, $services); + $this->assertArrayHasKey($nameA, $services); + $this->assertArrayHasKey($nameB, $services); + $this->assertEquals($serviceA->getAutoincId(), $services[$nameA]->getAutoincId()); + $this->assertEquals($serviceB->getAutoincId(), $services[$nameB]->getAutoincId()); + + // TODO: deleting set should delete services + + $this->checkForDanglingServices(); + } + + /** + * @expectedException \RuntimeException + */ + public function testCreatingSetWithoutType() + { + $set = IcingaServiceSet::create(array( + 'object_name' => '___TEST__set_BAD', + )); + $set->store($this->getDb()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testCreatingServiceSetWithoutHost() + { + $set = IcingaServiceSet::create(array( + 'object_name' => '___TEST__set_BAD2', + 'object_type' => 'object', + )); + + $set->store($this->getDb()); + } + + public function testDeletingSet() + { + $set = IcingaServiceSet::load($this->testObjectName, $this->getDb()); + $set->delete(); + + $this->assertFalse(IcingaServiceSet::exists($this->testObjectName, $this->getDb())); + $this->subject = null; + } + + public function checkForDanglingServices() + { + $db = $this->getDb()->getDbAdapter(); + $query = $db->select() + ->from(array('s' => 'icinga_service'), array('id')) + ->joinLeft( + array('ss' => 'icinga_service_set'), + 'ss.id = s.service_set_id', + array() + ) + ->where('s.service_set_id IS NOT NULL') + ->where('ss.id IS NULL'); + + $ids = $db->fetchCol($query); + + $this->assertEmpty($ids, sprintf('Found dangling service_set services in database: %s', join(', ', $ids))); + } + + public function checkForDanglingHostSets() + { + $db = $this->getDb()->getDbAdapter(); + $query = $db->select() + ->from(array('ss' => 'icinga_service_set'), array('id')) + ->joinLeft( + array('h' => 'icinga_host'), + 'h.id = ss.host_id', + array() + ) + ->where('ss.host_id IS NOT NULL') + ->where('h.id IS NULL'); + + $ids = $db->fetchCol($query); + + $this->assertEmpty( + $ids, + sprintf( + 'Found dangling service_set\'s for a host, without the host in database: %s', + join(', ', $ids) + ) + ); + } +} diff --git a/test/php/library/Director/Objects/IcingaServiceTest.php b/test/php/library/Director/Objects/IcingaServiceTest.php new file mode 100644 index 0000000..468db67 --- /dev/null +++ b/test/php/library/Director/Objects/IcingaServiceTest.php @@ -0,0 +1,293 @@ +<?php + +namespace Tests\Icinga\Module\Director\Objects; + +use Icinga\Module\Director\IcingaConfig\IcingaConfig; +use Icinga\Module\Director\Objects\IcingaHost; +use Icinga\Module\Director\Objects\IcingaService; +use Icinga\Module\Director\Test\BaseTestCase; + +class IcingaServiceTest extends BaseTestCase +{ + protected $testHostName = '___TEST___host'; + + protected $testServiceName = '___TEST___service'; + + protected $createdServices = array(); + + public function testUnstoredHostCanBeLazySet() + { + $service = $this->service(); + $service->display_name = 'Something else'; + $service->host = 'not yet'; + $this->assertEquals( + 'not yet', + $service->host + ); + } + + /** + * @expectedException \RuntimeException + */ + public function testFailsToStoreWithMissingLazyRelations() + { + if ($this->skipForMissingDb()) { + return; + } + + $db = $this->getDb(); + $service = $this->service(); + $service->display_name = 'Something else'; + $service->host = 'not yet'; + $service->store($db); + $service->delete(); + } + + public function testAcceptsAssignRules() + { + $service = $this->service(); + $service->object_type = 'apply'; + $service->assign_filter = 'host.address="127.*"'; + } + + /** + * @expectedException \LogicException + */ + public function testRefusesAssignRulesWhenNotBeingAnApply() + { + $service = $this->service(); + $service->assign_filter = 'host.address=127.*'; + } + + public function testAcceptsAndRendersFlatAssignRules() + { + if ($this->skipForMissingDb()) { + return; + } + + $db = $this->getDb(); + + $service = $this->service(); + + // Service apply rule rendering requires access to settings: + $service->setConnection($db); + $service->object_type = 'apply'; + $service->assign_filter = 'host.address="127.*"|host.vars.env="test"'; + + $this->assertEquals( + $this->loadRendered('service1'), + (string) $service + ); + + $this->assertEquals( + 'host.address="127.*"|host.vars.env="test"', + $service->assign_filter + ); + } + + public function testAcceptsAndRendersStructuredAssignRules() + { + if ($this->skipForMissingDb()) { + return; + } + + $db = $this->getDb(); + + $service = $this->service(); + // Service apply rule rendering requires access to settings: + $service->setConnection($db); + $service->object_type = 'apply'; + $service->assign_filter = 'host.address="127.*"|host.vars.env="test"'; + + $this->assertEquals( + $this->loadRendered('service1'), + (string) $service + ); + + $this->assertEquals( + 'host.address="127.*"|host.vars.env="test"', + $service->assign_filter = 'host.address="127.*"|host.vars.env="test"' + ); + } + + public function testPersistsAssignRules() + { + if ($this->skipForMissingDb()) { + return; + } + + $db = $this->getDb(); + + $service = $this->service(); + $service->object_type = 'apply'; + $service->assign_filter = 'host.address="127.*"|host.vars.env="test"'; + + $service->store($db); + + $service = IcingaService::loadWithAutoIncId($service->id, $db); + $this->assertEquals( + $this->loadRendered('service1'), + (string) $service + ); + + $this->assertEquals( + 'host.address="127.*"|host.vars.env="test"', + $service->assign_filter + ); + + $service->delete(); + } + + public function testRendersToTheCorrectZone() + { + if ($this->skipForMissingDb()) { + return; + } + + $db = $this->getDb(); + $service = $this->service()->setConnection($db); + + $config = new IcingaConfig($db); + $service->renderToConfig($config); + $masterzone = $db->getMasterZoneName(); + $this->assertEquals( + array('zones.d/' . $masterzone . '/services.conf'), + $config->getFileNames() + ); + } + + public function testVariablesInPropertiesAndCustomVariables() + { + if ($this->skipForMissingDb()) { + return; + } + + $db = $this->getDb(); + + $service = $this->service('___TEST___service_$not_replaced$'); + $service->setConnection($db); + $service->object_type = 'apply'; + $service->display_name = 'Service: $host.vars.replaced$'; + $service->assign_filter = 'host.address="127.*"'; + $service->{'vars.custom_var'} = '$host.vars.replaced$'; + + $this->assertEquals( + $this->loadRendered('service3'), + (string) $service + ); + } + + public function testVariablesAreNotReplacedForNonApplyObjects() + { + if ($this->skipForMissingDb()) { + return; + } + + $db = $this->getDb(); + + $host = $this->host(); + $host->store($db); + + $service = $this->service('___TEST___service_$not_replaced$'); + $service->object_type = 'object'; + $service->host_id = $host->get('id'); + $service->display_name = 'Service: $host.vars.not_replaced$'; + $service->{'vars.custom_var'} = '$host.vars.not_replaced$'; + $service->store($db); + + $service = IcingaService::loadWithAutoIncId($service->id, $db); + $this->assertEquals( + $this->loadRendered('service4'), + (string) $service + ); + } + + public function testApplyForRendersInVariousModes() + { + if ($this->skipForMissingDb()) { + return; + } + + $db = $this->getDb(); + + $service = $this->service()->setConnection($db); + $service->object_type = 'apply'; + $service->apply_for = 'host.vars.test1'; + $service->assign_filter = 'host.vars.env="test"'; + $this->assertEquals( + $this->loadRendered('service5'), + (string) $service + ); + + $service->object_name = '___TEST$config$___service $host.var.bla$'; + $this->assertEquals( + $this->loadRendered('service6'), + (string) $service + ); + + $service->object_name = ''; + $this->assertEquals( + $this->loadRendered('service7'), + (string) $service + ); + } + + protected function host() + { + return IcingaHost::create(array( + 'object_name' => $this->testHostName, + 'object_type' => 'object', + 'address' => '127.0.0.1', + )); + } + + protected function service($objectName = null) + { + if ($objectName === null) { + $objectName = $this->testServiceName; + } + $this->createdServices[] = $objectName; + return IcingaService::create(array( + 'object_name' => $objectName, + 'object_type' => 'object', + 'display_name' => 'Whatever service', + 'vars' => array( + 'test1' => 'string', + 'test2' => 17, + 'test3' => false, + 'test4' => (object) array( + 'this' => 'is', + 'a' => array( + 'dict', + 'ionary' + ) + ) + ) + )); + } + + protected function loadRendered($name) + { + return file_get_contents(__DIR__ . '/rendered/' . $name . '.out'); + } + + public function tearDown() + { + if ($this->hasDb()) { + $db = $this->getDb(); + $kill = array($this->testHostName); + foreach ($kill as $name) { + if (IcingaHost::exists($name, $db)) { + IcingaHost::load($name, $db)->delete(); + } + } + + $kill = $this->createdServices; + foreach ($kill as $name) { + if (IcingaService::exists(array($name), $db)) { + IcingaService::load($name, $db)->delete(); + } + } + } + } +} diff --git a/test/php/library/Director/Objects/IcingaTemplateResolverTest.php b/test/php/library/Director/Objects/IcingaTemplateResolverTest.php new file mode 100644 index 0000000..09d0ead --- /dev/null +++ b/test/php/library/Director/Objects/IcingaTemplateResolverTest.php @@ -0,0 +1,158 @@ +<?php + +namespace Tests\Icinga\Module\Director\Objects; + +use Icinga\Module\Director\Objects\IcingaHost; +use Icinga\Module\Director\Test\BaseTestCase; + +class IcingaTemplateResolverTest extends BaseTestCase +{ + /** @var IcingaHost[] */ + private $scenario; + + private $prefix = '__TEST_i1_'; + + public function testParentNamesCanBeRetrieved() + { + $this->assertEquals( + array( + $this->prefixed('t6'), + $this->prefixed('t5'), + $this->prefixed('t2') + ), + $this->getHost('t1')->templateResolver()->listParentNames() + ); + } + + public function testFullInhertancePathIdsAreCorrect() + { + $this->assertEquals( + $this->getIds(array('t5', 't6', 't5', 't5', 't4', 't3', 't5', 't6', 't2', 't1')), + $this->getHost('t1')->templateResolver()->listFullInheritancePathIds() + ); + } + + public function testInhertancePathIdsAreCorrect() + { + $this->assertEquals( + $this->getIds(array('t4', 't3', 't5', 't6', 't2', 't1')), + $this->getHost('t1')->templateResolver()->listInheritancePathIds() + ); + } + + protected function getHost($name) + { + $hosts = $this->getScenario(); + return $hosts[$name]; + } + + protected function getId($name) + { + $hosts = $this->getScenario(); + return $hosts[$name]->id; + } + + protected function getIds($names) + { + $ids = array(); + foreach ($names as $name) { + $ids[] = $this->getId($name); + } + + return $ids; + } + + protected function prefixed($name) + { + return $this->prefix . $name; + } + + /** + * @return IcingaHost[] + */ + protected function getScenario() + { + if ($this->scenario === null) { + $this->scenario = $this->createScenario(); + } + + return $this->scenario; + } + + /** + * @return IcingaHost[] + */ + protected function createScenario() + { + // Defionition imports (object -> imported) + // t1 -> t6, t5, t2 + // t6 -> t5 + // t3 -> t4 + // t2 -> t3, t6 + // t4 -> t5 + + // 5, 6, 5, 5, 4, 3, 5, 6, 2, 1 + + $t1 = $this->create('t1'); + $t4 = $this->create('t4'); + $t3 = $this->create('t3'); + $t2 = $this->create('t2'); + $t5 = $this->create('t5'); + $t6 = $this->create('t6'); + + // TODO: Must work without this: + $t1->templateResolver()->clearCache(); + $t1->set('imports', array($t6, $t5, $t2)); + $t6->set('imports', array($t5)); + $t3->set('imports', array($t4)); + $t2->set('imports', array($t3, $t6)); + $t4->set('imports', array($t5)); + + $t5->store(); + $t4->store(); + $t3->store(); + $t6->store(); + $t2->store(); + $t1->store(); + + // TODO: Must work without this: + $t1->templateResolver()->clearCache(); + return array( + 't1' => $t1, + 't2' => $t2, + 't3' => $t3, + 't4' => $t4, + 't5' => $t5, + 't6' => $t6, + ); + } + + /** + * @param $name + * @return IcingaHost + */ + protected function create($name) + { + $host = IcingaHost::create( + array( + 'object_name' => $this->prefixed($name), + 'object_type' => 'template' + ) + ); + + $host->store($this->getDb()); + return $host; + } + + public function tearDown() + { + $db = $this->getDb(); + $kill = array('t1', 't2', 't6', 't3', 't4', 't5'); + foreach ($kill as $short) { + $name = $this->prefixed($short); + if (IcingaHost::exists($name, $db)) { + IcingaHost::load($name, $db)->delete(); + } + } + } +} diff --git a/test/php/library/Director/Objects/IcingaTimePeriodTest.php b/test/php/library/Director/Objects/IcingaTimePeriodTest.php new file mode 100644 index 0000000..84496d3 --- /dev/null +++ b/test/php/library/Director/Objects/IcingaTimePeriodTest.php @@ -0,0 +1,184 @@ +<?php + +namespace Tests\Icinga\Module\Director\Objects; + +use Icinga\Module\Director\Objects\IcingaTimePeriod; +use Icinga\Module\Director\Test\BaseTestCase; + +class IcingaTimePeriodTest extends BaseTestCase +{ + protected $testPeriodName = '___TEST___timeperiod'; + + protected $createdNames = []; + + public function testWhetherUpdatedTimeperiodsAreCorrectlyStored() + { + if ($this->skipForMissingDb()) { + return; + } + + $period = $this->createTestPeriod(); + + $newRanges = array( + 'monday' => '00:00-24:00', + 'tuesday' => '18:00-24:00', + 'wednesday' => '00:00-24:00', + ); + $period->ranges = $newRanges; + $this->assertEquals( + '18:00-24:00', + $period->toPlainObject()->ranges->tuesday + ); + + $period->store(); + + $period = $this->loadTestPeriod(); + $this->assertEquals( + '18:00-24:00', + $period->ranges()->get('tuesday')->range_value + ); + + $this->assertEquals( + '00:00-24:00', + $period->toPlainObject()->ranges->wednesday + ); + + $period->ranges()->setRange('wednesday', '09:00-17:00'); + + $this->assertEquals( + '09:00-17:00', + $period->toPlainObject()->ranges->wednesday + ); + + $this->assertEquals( + '00:00-24:00', + $period->getPlainUnmodifiedObject()->ranges->wednesday + ); + } + + protected function createTestPeriod($suffix = '', $testRanges = []) + { + $db = $this->getDb(); + $name = $this->testPeriodName . $suffix; + + $this->createdNames[] = $name; + $object = IcingaTimePeriod::create( + array( + 'object_name' => $name, + 'object_type' => 'object' + ), + $db + ); + $object->store(); + $ranges = $object->ranges(); + + if (empty($testRanges)) { + $testRanges = array( + 'monday' => '00:00-24:00', + 'tuesday' => '00:00-24:00', + 'wednesday' => '00:00-24:00', + ); + } + + $ranges->set($testRanges); + $object->store(); + + return $object; + } + + public function testIsActiveWorksForWeekdayRanges() + { + $period = $this->createTestPeriod(); + + $newRanges = array( + 'monday' => '00:00-24:00', + 'tuesday' => '18:00-24:00', + 'wednesday' => '00:00-24:00', + ); + $period->ranges = $newRanges; + + // Tuesday: + $this->assertFalse($period->isActive(strtotime('2016-05-17 10:00:00'))); + // Wednesday: + $this->assertTrue($period->isActive(strtotime('2016-05-18 10:00:00'))); + // Thursday: + $this->assertFalse($period->isActive(strtotime('2016-05-19 10:00:00'))); + } + + public function testPeriodWithIncludes() + { + $period = $this->createTestPeriod(); + $include = $this->createTestPeriod('include', ['thursday' => '00:00-24:00']); + + $period->set('includes', $include->object_name); + $period->store(); + + // Wednesday: + $this->assertTrue($period->isActive(strtotime('2016-05-18 10:00:00'))); + // Thursday: + $this->assertTrue($period->isActive(strtotime('2016-05-19 10:00:00'))); + } + + public function testPeriodWithExcludes() + { + $period = $this->createTestPeriod(); + $exclude = $this->createTestPeriod('exclude', ['wednesday' => '00:00-24:00']); + + $period->set('excludes', $exclude->object_name); + $period->store(); + + // Wednesday: + $this->assertFalse($period->isActive(strtotime('2016-05-18 10:00:00'))); + // Thursday: + $this->assertFalse($period->isActive(strtotime('2016-05-19 10:00:00'))); + } + + public function testPeriodPreferingIncludes() + { + $period = $this->createTestPeriod(); + $include = $this->createTestPeriod('include', ['thursday' => '00:00-24:00']); + $exclude = $this->createTestPeriod('exclude', ['thursday' => '00:00-24:00']); + + $period->set('includes', $include->object_name); + $period->set('excludes', $exclude->object_name); + $period->store(); + + // Wednesday: + $this->assertTrue($period->isActive(strtotime('2016-05-18 10:00:00'))); + // Thursday: + $this->assertTrue($period->isActive(strtotime('2016-05-19 10:00:00'))); + } + + public function testPeriodPreferingExcludes() + { + $period = $this->createTestPeriod(); + $include = $this->createTestPeriod('include', ['thursday' => '00:00-24:00']); + $exclude = $this->createTestPeriod('exclude', ['thursday' => '00:00-24:00']); + + $period->set('prefer_includes', false); + $period->set('includes', $include->object_name); + $period->set('excludes', $exclude->object_name); + $period->store(); + + // Wednesday: + $this->assertTrue($period->isActive(strtotime('2016-05-18 10:00:00'))); + // Thursday: + $this->assertFalse($period->isActive(strtotime('2016-05-19 10:00:00'))); + } + + protected function loadTestPeriod($suffix = '') + { + return IcingaTimePeriod::load($this->testPeriodName . $suffix, $this->getDb()); + } + + public function tearDown() + { + $db = $this->getDb(); + + foreach ($this->createdNames as $name) { + if (IcingaTimePeriod::exists($name, $db)) { + IcingaTimePeriod::load($name, $db)->delete(); + } + } + } +} diff --git a/test/php/library/Director/Objects/rendered/command1.out b/test/php/library/Director/Objects/rendered/command1.out new file mode 100644 index 0000000..12e156f --- /dev/null +++ b/test/php/library/Director/Objects/rendered/command1.out @@ -0,0 +1,4 @@ +object CheckCommand "___TEST___command" { + command = [ "C:\\test.exe" ] +} + diff --git a/test/php/library/Director/Objects/rendered/command2.out b/test/php/library/Director/Objects/rendered/command2.out new file mode 100644 index 0000000..e853285 --- /dev/null +++ b/test/php/library/Director/Objects/rendered/command2.out @@ -0,0 +1,4 @@ +object CheckCommand "___TEST___command" { + command = [ "/tmp/bla" ] +} + diff --git a/test/php/library/Director/Objects/rendered/command3.out b/test/php/library/Director/Objects/rendered/command3.out new file mode 100644 index 0000000..7e7eef9 --- /dev/null +++ b/test/php/library/Director/Objects/rendered/command3.out @@ -0,0 +1,4 @@ +object CheckCommand "___TEST___command" { + command = [ PluginDir + "/tmp/bla" ] +} + diff --git a/test/php/library/Director/Objects/rendered/command4.out b/test/php/library/Director/Objects/rendered/command4.out new file mode 100644 index 0000000..3dc7ac5 --- /dev/null +++ b/test/php/library/Director/Objects/rendered/command4.out @@ -0,0 +1,4 @@ +object CheckCommand "___TEST___command" { + command = [ "\\\\network\\share\\bla.exe" ] +} + diff --git a/test/php/library/Director/Objects/rendered/command5.out b/test/php/library/Director/Objects/rendered/command5.out new file mode 100644 index 0000000..1e57577 --- /dev/null +++ b/test/php/library/Director/Objects/rendered/command5.out @@ -0,0 +1,4 @@ +object CheckCommand "___TEST___command" { + command = [ BlahDir + "\\network\\share\\bla.exe" ] +} + diff --git a/test/php/library/Director/Objects/rendered/command6.out b/test/php/library/Director/Objects/rendered/command6.out new file mode 100644 index 0000000..3f123ce --- /dev/null +++ b/test/php/library/Director/Objects/rendered/command6.out @@ -0,0 +1,4 @@ +object CheckCommand "___TEST___command" { + command = [ PluginDir + "/network\\share\\bla.exe" ] +} + diff --git a/test/php/library/Director/Objects/rendered/command7.out b/test/php/library/Director/Objects/rendered/command7.out new file mode 100644 index 0000000..4f966f0 --- /dev/null +++ b/test/php/library/Director/Objects/rendered/command7.out @@ -0,0 +1,9 @@ +object CheckCommand "___TEST___command" { + command = [ PluginDir + "/bla" ] + arguments += { + "-a" = { + set_if = "$a$" + } + } +} + diff --git a/test/php/library/Director/Objects/rendered/host1.out b/test/php/library/Director/Objects/rendered/host1.out new file mode 100644 index 0000000..3637f2d --- /dev/null +++ b/test/php/library/Director/Objects/rendered/host1.out @@ -0,0 +1,12 @@ +object Host "___TEST___host" { + display_name = "Whatever" + address = "127.0.0.127" + vars.test1 = "string" + vars.test2 = 17 + vars.test3 = false + vars.test4 = { + a = [ "dict", "ionary" ] + @this = "is" + } +} + diff --git a/test/php/library/Director/Objects/rendered/host2.out b/test/php/library/Director/Objects/rendered/host2.out new file mode 100644 index 0000000..74668e1 --- /dev/null +++ b/test/php/library/Director/Objects/rendered/host2.out @@ -0,0 +1,17 @@ +object Host "___TEST___host" { + display_name = "Whatever" + address = "127.0.0.127" + check_command = "unknown" + check_period = "Not time is a good time @ nite" + event_command = "What event?" + zone = "invalid" + command_endpoint = "nirvana" + vars.test1 = "string" + vars.test2 = 17 + vars.test3 = false + vars.test4 = { + a = [ "dict", "ionary" ] + @this = "is" + } +} + diff --git a/test/php/library/Director/Objects/rendered/host3.out b/test/php/library/Director/Objects/rendered/host3.out new file mode 100644 index 0000000..5661ca9 --- /dev/null +++ b/test/php/library/Director/Objects/rendered/host3.out @@ -0,0 +1,14 @@ +object Host "___TEST___host" { + import "___TEST___parent" + + display_name = "Whatever" + address = "127.0.0.127" + vars.test1 = "nada" + vars.test2 = 17 + vars.test3 = false + vars.test4 = { + a = [ "dict", "ionary" ] + @this = "is" + } +} + diff --git a/test/php/library/Director/Objects/rendered/notification1.out b/test/php/library/Director/Objects/rendered/notification1.out new file mode 100644 index 0000000..13f06ce --- /dev/null +++ b/test/php/library/Director/Objects/rendered/notification1.out @@ -0,0 +1,4 @@ +object Notification "___TEST___notification" { + users = [ "___TEST___user1", "___TEST___user2" ] +} + diff --git a/test/php/library/Director/Objects/rendered/service1.out b/test/php/library/Director/Objects/rendered/service1.out new file mode 100644 index 0000000..ba65b08 --- /dev/null +++ b/test/php/library/Director/Objects/rendered/service1.out @@ -0,0 +1,14 @@ +apply Service "___TEST___service" { + display_name = "Whatever service" + assign where match("127.*", host.address) || host.vars.env == "test" + vars.test1 = "string" + vars.test2 = 17 + vars.test3 = false + vars.test4 = { + a = [ "dict", "ionary" ] + @this = "is" + } + + import DirectorOverrideTemplate +} + diff --git a/test/php/library/Director/Objects/rendered/service2.out b/test/php/library/Director/Objects/rendered/service2.out new file mode 100644 index 0000000..ea7d901 --- /dev/null +++ b/test/php/library/Director/Objects/rendered/service2.out @@ -0,0 +1,16 @@ +apply Service "___TEST___service" { + display_name = "Whatever service" + vars.test1 = "string" + vars.test2 = 17 + vars.test3 = false + vars.test4 = { + a = [ "dict", "ionary" ] + @this = "is" + } + + assign where match("128.*", host.address) + ignore where host.name == "localhost" + + import DirectorOverrideTemplate +} + diff --git a/test/php/library/Director/Objects/rendered/service3.out b/test/php/library/Director/Objects/rendered/service3.out new file mode 100644 index 0000000..0288ee6 --- /dev/null +++ b/test/php/library/Director/Objects/rendered/service3.out @@ -0,0 +1,15 @@ +apply Service "___TEST___service_$not_replaced$" { + display_name = "Service: " + host.vars.replaced + assign where match("127.*", host.address) + vars.custom_var = "$host.vars.replaced$" + vars.test1 = "string" + vars.test2 = 17 + vars.test3 = false + vars.test4 = { + a = [ "dict", "ionary" ] + @this = "is" + } + + import DirectorOverrideTemplate +} + diff --git a/test/php/library/Director/Objects/rendered/service4.out b/test/php/library/Director/Objects/rendered/service4.out new file mode 100644 index 0000000..aeb280a --- /dev/null +++ b/test/php/library/Director/Objects/rendered/service4.out @@ -0,0 +1,13 @@ +object Service "___TEST___service_$not_replaced$" { + host_name = "___TEST___host" + display_name = "Service: $host.vars.not_replaced$" + vars.custom_var = "$host.vars.not_replaced$" + vars.test1 = "string" + vars.test2 = 17 + vars.test3 = false + vars.test4 = { + a = [ "dict", "ionary" ] + @this = "is" + } +} + diff --git a/test/php/library/Director/Objects/rendered/service5.out b/test/php/library/Director/Objects/rendered/service5.out new file mode 100644 index 0000000..b05e630 --- /dev/null +++ b/test/php/library/Director/Objects/rendered/service5.out @@ -0,0 +1,14 @@ +apply Service "___TEST___service" for (config in host.vars.test1) { + display_name = "Whatever service" + assign where host.vars.env == "test" + vars.test1 = "string" + vars.test2 = 17 + vars.test3 = false + vars.test4 = { + a = [ "dict", "ionary" ] + @this = "is" + } + + import DirectorOverrideTemplate +} + diff --git a/test/php/library/Director/Objects/rendered/service6.out b/test/php/library/Director/Objects/rendered/service6.out new file mode 100644 index 0000000..fdca11c --- /dev/null +++ b/test/php/library/Director/Objects/rendered/service6.out @@ -0,0 +1,15 @@ +apply Service for (config in host.vars.test1) { + name = "___TEST" + config + "___service " + host.var.bla + display_name = "Whatever service" + assign where host.vars.env == "test" + vars.test1 = "string" + vars.test2 = 17 + vars.test3 = false + vars.test4 = { + a = [ "dict", "ionary" ] + @this = "is" + } + + import DirectorOverrideTemplate +} + diff --git a/test/php/library/Director/Objects/rendered/service7.out b/test/php/library/Director/Objects/rendered/service7.out new file mode 100644 index 0000000..c125ccc --- /dev/null +++ b/test/php/library/Director/Objects/rendered/service7.out @@ -0,0 +1,14 @@ +apply Service for (config in host.vars.test1) { + display_name = "Whatever service" + assign where host.vars.env == "test" + vars.test1 = "string" + vars.test2 = 17 + vars.test3 = false + vars.test4 = { + a = [ "dict", "ionary" ] + @this = "is" + } + + import DirectorOverrideTemplate +} + |