diff --git a/phpstan.neon b/phpstan.neon index b770283..97f3af9 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -16,3 +16,9 @@ parameters: - message: "#Call to an undefined method PHPUnit\\\\Extension\\\\FunctionMocker::expects\\(\\)\\.#" path: tests/* + - + message: "#Access to an undefined property Mockery\\\\LegacyMockInterface::\\$[a-zA-Z0-9_]+\\.#" + path: tests/* + - + message: "#Call to an undefined method Mockery\\\\Mock::[a-zA-Z0-9_]+\\(\\)\\.#" + path: tests/* diff --git a/tests/SchemaTest.php b/tests/SchemaTest.php new file mode 100644 index 0000000..5db633f --- /dev/null +++ b/tests/SchemaTest.php @@ -0,0 +1,637 @@ +shouldReceive('get_raw_values') + ->andReturn(array()); + $ldap = Mockery::mock('overload:EesyLDAP\Ldap'); + $ldap->shouldReceive('get_entry') + ->once() + ->with( + 'cn=SubSchema', '(objectclass=*)', + ['attributeTypes', 'matchingRules', 'matchingRuleUse', 'objectClasses', 'ldapSyntaxes'] + ) + ->andReturn($entry); + $obj = Schema::load($ldap); + $this -> assertInstanceOf(Schema::class, $obj); + } + + /** + * @covers EesyLDAP\Schema::load + * @runTestsInSeparateProcesses + * @preserveGlobalState disabled + */ + public function testLoadFail() { + $raise = true; + $ldap = Mockery::mock('overload:EesyLDAP\Ldap'); + $ldap->shouldReceive('get_entry') + ->once() + ->with( + 'cn=SubSchema', '(objectclass=*)', + ['attributeTypes', 'matchingRules', 'matchingRuleUse', 'objectClasses', 'ldapSyntaxes'] + ) + ->andReturn(false); + $ldap->shouldReceive('error') + ->once() + ->with('Fail to load cn=SubSchema entry', $raise) + ->andThrow(LdapException::class); + $this->expectException(LdapException::class); + $obj = Schema::load($ldap, $raise); + } + + /** + * @covers EesyLDAP\Schema::__get + */ + public function testGetLoaded() { + $schema = $this -> getSchema(); + $this -> assertTrue($schema->__get('loaded')); + } + + /** + * @covers EesyLDAP\Schema::__get + */ + public function testGetInvalidProperty() { + $schema = $this -> getSchema(); + $this->expectException(InvalidPropertyException::class); + $this -> assertTrue($schema->__get('undefined')); + } + + /** + * @return Schema + */ + public function getSchema() { + $ldap = Mockery::mock('EesyLDAP\Ldap'); + $entry = Mockery::mock('EesyLDAP\Entry'); + $entry->shouldReceive('get_raw_values') + ->andReturn(array()); + return new Schema($ldap, $entry); + } + + /** + * @covers \EesyLDAP\Schema::parse + * @runTestsInSeparateProcesses + * @preserveGlobalState disabled + */ + public function testParse() { + $data = array( + 'attributeTypes' => array( + "type" => "Attribute", + "values" => array( + "( 2.5.4.0 NAME 'objectClass' DESC 'RFC4512: object classes of the entity' EQUALITY ". + "objectIdentifierMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )", + ), + "oid" => "2.5.4.0", + "name" => "objectClass", + "entry" => null, + ), + 'matchingRules' => array( + "type" => "MatchingRule", + "values" => array( + "( 2.5.13.20 NAME 'telephoneNumberMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.50 )", + ), + "oid" => "2.5.13.20", + "name" => "telephoneNumberMatch", + "entry" => null, + ), + 'matchingRuleUse' => array( + "type" => "MatchingRuleUse", + "values" => array( + "( 2.5.13.20 NAME 'telephoneNumberMatch' APPLIES ( telephoneNumber $ homePhone $ mobile". + " $ pager ) )", + ), + "oid" => "2.5.13.20", + "name" => "telephoneNumberMatch", + "entry" => null, + ), + 'objectClasses' => array( + "type" => "ObjectClass", + "values" => array( + "( 2.5.6.14 NAME 'device' DESC 'RFC2256: a device' SUP top STRUCTURAL MUST cn MAY ( ". + "serialNumber $ seeAlso $ owner $ ou $ o $ l $ description ) )", + ), + "oid" => "2.5.6.14", + "name" => "device", + "entry" => null, + ), + 'ldapSyntaxes' => array( + "type" => "Syntax", + "values" => array( + "( 1.3.6.1.4.1.1466.115.121.1.7 DESC 'Boolean' )", + ), + "oid" => "1.3.6.1.4.1.1466.115.121.1.7", + "name" => "Boolean", + "entry" => null, + ), + ); + $entry = Mockery::mock('EesyLDAP\Entry'); + foreach($data as $attr => $info) { + $entry->shouldReceive('get_raw_values') + ->once() + ->with($attr, array(), true) + ->andReturn($info['values']); + $data[$attr]['entry'] = Mockery::mock('EesyLDAP\Schema\SchemaEntry'); + // @phpstan-ignore-next-line + $data[$attr]['entry']->oid = $info['oid']; + // @phpstan-ignore-next-line + $data[$attr]['entry']->name = $info['name']; + $data[$attr]['mock'] = Mockery::mock("overload:EesyLDAP\\Schema\\".$info['type']); + // @phpstan-ignore-next-line + $data[$attr]['mock']->shouldReceive('parse') + ->once() + ->with($info['values'][0]) + ->andReturn($data[$attr]['entry']); + } + + $schema = Mockery::mock('EesyLDAP\Schema') + ->shouldAllowMockingProtectedMethods(); + $reflection = new ReflectionClass($schema); + $entry_property = $reflection->getProperty('entry'); + $entry_property->setAccessible(true); + $entry_property->setValue($schema, $entry); + + $schema->shouldReceive('parse')->passthru(); + + $parse_method = new ReflectionMethod('EesyLDAP\Schema', 'parse'); + $parse_method->setAccessible(true); + $parse_method->invoke($schema); + + $reflection = new ReflectionClass($schema); + $entries_property = $reflection->getProperty('entries'); + $entries_property->setAccessible(true); + $entries = $entries_property->getValue($schema); + $oids_property = $reflection->getProperty('oids'); + $oids_property->setAccessible(true); + $oids = $oids_property->getValue($schema); + + foreach($data as $attr => $info) { + // @phpstan-ignore-next-line + $this->assertSame($info['entry'], $entries[strtolower($info['type'])][$info['name']]); + // @phpstan-ignore-next-line + $this->assertEquals($info['entry'], $oids[$info['oid']]); + } + } + + /** + * @covers \EesyLDAP\Schema::parse + * @runTestsInSeparateProcesses + * @preserveGlobalState disabled + */ + public function testParseBadSchemaValue() { + $attr = "attributeTypes"; + $value = "bad value"; + $ldap = Mockery::mock('EesyLDAP\Ldap'); + $ldap->shouldReceive('error') + ->once() + ->with("Fail to parse %s schema value: %s", null, $attr, $value) + ->andThrow(LdapException::class); + + $entry = Mockery::mock('EesyLDAP\Entry'); + $entry->shouldReceive('get_raw_values') + ->once() + ->with($attr, array(), true) + ->andReturn(array($value)); + + $attribute = Mockery::mock("overload:EesyLDAP\Schema\Attribute"); + $attribute->shouldReceive('parse') + ->once() + ->with($value) + ->andReturn(false); + + $schema = Mockery::mock('EesyLDAP\Schema') + ->shouldAllowMockingProtectedMethods(); + $reflection = new ReflectionClass($schema); + $ldap_property = $reflection->getProperty('ldap'); + $ldap_property->setAccessible(true); + $ldap_property->setValue($schema, $ldap); + $entry_property = $reflection->getProperty('entry'); + $entry_property->setAccessible(true); + $entry_property->setValue($schema, $entry); + + $schema->shouldReceive('parse')->passthru(); + + $this->expectException(LdapException::class); + + $parse_method = new ReflectionMethod('EesyLDAP\Schema', 'parse'); + $parse_method->setAccessible(true); + $parse_method->invoke($schema); + } + + /** + * @covers \EesyLDAP\Schema::parse + * @runTestsInSeparateProcesses + * @preserveGlobalState disabled + */ + public function testParseDuplicateOid() { + $attr = "attributeTypes"; + $oid = "1.2.3"; + $value1 = "test1"; + $entry1 = Mockery::mock('EesyLDAP\Schema\SchemaEntry'); + $entry1->oid = $oid; + $entry1->name = "test1"; + $value2 = "test2"; + $entry2 = Mockery::mock('EesyLDAP\Schema\SchemaEntry'); + $entry2->oid = $oid; + $entry2->name = "test2"; + + $ldap = Mockery::mock('EesyLDAP\Ldap'); + $ldap->shouldReceive('error') + ->once() + ->with("Duplicate OID %s found in schema: %s / %s", null, $oid, $entry1, $entry2) + ->andThrow(LdapException::class); + + $entry = Mockery::mock('EesyLDAP\Entry'); + $entry->shouldReceive('get_raw_values') + ->once() + ->with($attr, array(), true) + ->andReturn(array($value1, $value2)); + + $attribute = Mockery::mock("overload:EesyLDAP\Schema\Attribute"); + $attribute->shouldReceive('parse') + ->once() + ->with($value1) + ->andReturn($entry1) + ->shouldReceive('parse') + ->once() + ->with($value2) + ->andReturn($entry2); + + $schema = Mockery::mock('EesyLDAP\Schema') + ->shouldAllowMockingProtectedMethods(); + $reflection = new ReflectionClass($schema); + $ldap_property = $reflection->getProperty('ldap'); + $ldap_property->setAccessible(true); + $ldap_property->setValue($schema, $ldap); + $entry_property = $reflection->getProperty('entry'); + $entry_property->setAccessible(true); + $entry_property->setValue($schema, $entry); + + $schema->shouldReceive('parse')->passthru(); + + $this->expectException(LdapException::class); + + $parse_method = new ReflectionMethod('EesyLDAP\Schema', 'parse'); + $parse_method->setAccessible(true); + $parse_method->invoke($schema); + } + + /** + * @covers \EesyLDAP\Schema::parse + * @runTestsInSeparateProcesses + * @preserveGlobalState disabled + */ + public function testParseDuplicateName() { + $attr = "attributeTypes"; + $name = "test"; + $value1 = "test1"; + $entry1 = Mockery::mock('EesyLDAP\Schema\SchemaEntry'); + $entry1->oid = "1.2.3"; + $entry1->name = $name; + $value2 = "test2"; + $entry2 = Mockery::mock('EesyLDAP\Schema\SchemaEntry'); + $entry2->oid = "1.2.4"; + $entry2->name = $name; + + $ldap = Mockery::mock('EesyLDAP\Ldap'); + $ldap->shouldReceive('error') + ->once() + ->with( + "Duplicate %s schema entry %s found: %s / %s", + null, "Attribute", $name, $entry1, $entry2) + ->andThrow(LdapException::class); + + $entry = Mockery::mock('EesyLDAP\Entry'); + $entry->shouldReceive('get_raw_values') + ->once() + ->with($attr, array(), true) + ->andReturn(array($value1, $value2)); + + $attribute = Mockery::mock("overload:EesyLDAP\Schema\Attribute"); + $attribute->shouldReceive('parse') + ->once() + ->with($value1) + ->andReturn($entry1) + ->shouldReceive('parse') + ->once() + ->with($value2) + ->andReturn($entry2); + + $schema = Mockery::mock('EesyLDAP\Schema') + ->shouldAllowMockingProtectedMethods(); + $reflection = new ReflectionClass($schema); + $ldap_property = $reflection->getProperty('ldap'); + $ldap_property->setAccessible(true); + $ldap_property->setValue($schema, $ldap); + $entry_property = $reflection->getProperty('entry'); + $entry_property->setAccessible(true); + $entry_property->setValue($schema, $entry); + + $schema->shouldReceive('parse')->passthru(); + + $this->expectException(LdapException::class); + + $parse_method = new ReflectionMethod('EesyLDAP\Schema', 'parse'); + $parse_method->setAccessible(true); + $parse_method->invoke($schema); + } + + /** + * @covers \EesyLDAP\Schema::_get_entry + */ + public function testGetEntryByOid() { + $schema = Mockery::mock('EesyLDAP\Schema') + ->shouldAllowMockingProtectedMethods(); + $reflection = new ReflectionClass($schema); + $schema->shouldReceive('_get_entry')->passthru(); + + $oid = "1.2.3"; + $attr = Mockery::mock('EesyLDAP\Schema\Attribute'); + + $reflection = new ReflectionClass($schema); + $oids_property = $reflection->getProperty('oids'); + $oids_property->setAccessible(true); + $oids_property->setValue($schema, array($oid => $attr)); + + $get_entry_method = new ReflectionMethod('EesyLDAP\Schema', '_get_entry'); + $get_entry_method->setAccessible(true); + $this->assertSame($attr, $get_entry_method->invoke($schema, "Attribute", $oid)); + } + + /** + * @covers \EesyLDAP\Schema::_get_entry + */ + public function testGetEntryByName() { + $schema = Mockery::mock('EesyLDAP\Schema') + ->shouldAllowMockingProtectedMethods(); + $reflection = new ReflectionClass($schema); + $schema->shouldReceive('_get_entry')->passthru(); + + $name = "test"; + $attr = Mockery::mock('EesyLDAP\Schema\Attribute'); + $attr->shouldReceive('is_me') + ->once() + ->with($name) + ->andReturn(true); + + $reflection = new ReflectionClass($schema); + $entries_property = $reflection->getProperty('entries'); + $entries_property->setAccessible(true); + $entries_property->setValue($schema, array("attribute" => array($name => $attr))); + + $get_entry_method = new ReflectionMethod('EesyLDAP\Schema', '_get_entry'); + $get_entry_method->setAccessible(true); + $this->assertSame($attr, $get_entry_method->invoke($schema, "Attribute", $name)); + } + + /** + * @covers \EesyLDAP\Schema::_get_entry + */ + public function testGetEntryUndefinedType() { + $schema = Mockery::mock('EesyLDAP\Schema') + ->shouldAllowMockingProtectedMethods(); + $reflection = new ReflectionClass($schema); + $schema->shouldReceive('_get_entry')->passthru(); + + $reflection = new ReflectionClass($schema); + $get_entry_method = new ReflectionMethod('EesyLDAP\Schema', '_get_entry'); + $get_entry_method->setAccessible(true); + $this->assertFalse($get_entry_method->invoke($schema, "Attribute", "undefined")); + } + + /** + * @covers \EesyLDAP\Schema::_get_entry + */ + public function testGetEntryUndefinedEntry() { + $schema = Mockery::mock('EesyLDAP\Schema') + ->shouldAllowMockingProtectedMethods(); + $reflection = new ReflectionClass($schema); + $schema->shouldReceive('_get_entry')->passthru(); + + $reflection = new ReflectionClass($schema); + $entries_property = $reflection->getProperty('entries'); + $entries_property->setAccessible(true); + $entries_property->setValue($schema, array("attribute" => array())); + $get_entry_method = new ReflectionMethod('EesyLDAP\Schema', '_get_entry'); + $get_entry_method->setAccessible(true); + $this->assertFalse($get_entry_method->invoke($schema, "Attribute", "undefined")); + } + + /** + * @covers \EesyLDAP\Schema::attribute + */ + public function testAttribute() { + $name = 'test'; + $entry = Mockery::mock("overload:EesyLDAP\Schema\Attribute"); + $schema = Mockery::mock('EesyLDAP\Schema') + ->shouldAllowMockingProtectedMethods(); + $schema->shouldReceive('_get_entry') + ->once() + ->with("Attribute", $name) + ->andReturn($entry); + $schema->shouldReceive('attribute')->passthru(); + $this->assertSame($entry, $schema->attribute($name)); + } + + /** + * @covers \EesyLDAP\Schema::objectclass + */ + public function testObjectclass() { + $name = 'test'; + $entry = Mockery::mock("overload:EesyLDAP\Schema\ObjectClass"); + $schema = Mockery::mock('EesyLDAP\Schema') + ->shouldAllowMockingProtectedMethods(); + $schema->shouldReceive('_get_entry') + ->once() + ->with("ObjectClass", $name) + ->andReturn($entry); + $schema->shouldReceive('objectclass')->passthru(); + $this->assertSame($entry, $schema->objectclass($name)); + } + + /** + * @covers \EesyLDAP\Schema::matching_rule + */ + public function testMatchingRule() { + $name = 'test'; + $entry = Mockery::mock("overload:EesyLDAP\Schema\MatchingRule"); + $schema = Mockery::mock('EesyLDAP\Schema') + ->shouldAllowMockingProtectedMethods(); + $schema->shouldReceive('_get_entry') + ->once() + ->with("MatchingRule", $name) + ->andReturn($entry); + $schema->shouldReceive('matching_rule')->passthru(); + $this->assertSame($entry, $schema->matching_rule($name)); + } + + /** + * @covers \EesyLDAP\Schema::matching_rule_use + */ + public function testMatchingRuleUse() { + $name = 'test'; + $entry = Mockery::mock("overload:EesyLDAP\Schema\MatchingRuleUse"); + $schema = Mockery::mock('EesyLDAP\Schema') + ->shouldAllowMockingProtectedMethods(); + $schema->shouldReceive('_get_entry') + ->once() + ->with("MatchingRuleUse", $name) + ->andReturn($entry); + $schema->shouldReceive('matching_rule_use')->passthru(); + $this->assertSame($entry, $schema->matching_rule_use($name)); + } + + /** + * @covers \EesyLDAP\Schema::syntax + */ + public function testSyntax() { + $name = 'test'; + $entry = Mockery::mock("overload:EesyLDAP\Schema\Syntax"); + $schema = Mockery::mock('EesyLDAP\Schema') + ->shouldAllowMockingProtectedMethods(); + $schema->shouldReceive('_get_entry') + ->once() + ->with("Syntax", $name) + ->andReturn($entry); + $schema->shouldReceive('syntax')->passthru(); + $this->assertSame($entry, $schema->syntax($name)); + } + + /** + * @covers \EesyLDAP\Schema::has_attribute + */ + public function testHasAttribute() { + $attr = 'test'; + $oc1 = Mockery::mock("overload:EesyLDAP\Schema\ObjectClass"); + $oc1_name = 'test1'; + $oc1->shouldReceive('has_attribute') + ->once() + ->with($attr) + ->andReturn(false); + $oc2 = Mockery::mock("overload:EesyLDAP\Schema\ObjectClass"); + $oc2_name = 'test2'; + $oc2->shouldReceive('has_attribute') + ->once() + ->with($attr) + ->andReturn(true); + $schema = Mockery::mock('EesyLDAP\Schema') + ->shouldAllowMockingProtectedMethods(); + $schema->shouldReceive('objectclass') + ->once() + ->with($oc1_name) + ->andReturn($oc1) + ->shouldReceive('objectclass') + ->once() + ->with($oc2_name) + ->andReturn($oc2); + $schema->shouldReceive('has_attribute')->passthru(); + $this->assertTrue($schema->has_attribute($attr, $oc1_name, $oc2_name)); + } + + /** + * @covers \EesyLDAP\Schema::has_attribute + */ + public function testHasAttributeWithArray() { + $attr = 'test'; + $oc1 = Mockery::mock("overload:EesyLDAP\Schema\ObjectClass"); + $oc1_name = 'test1'; + $oc1->shouldReceive('has_attribute') + ->once() + ->with($attr) + ->andReturn(false); + $oc2 = Mockery::mock("overload:EesyLDAP\Schema\ObjectClass"); + $oc2_name = 'test2'; + $oc2->shouldReceive('has_attribute') + ->once() + ->with($attr) + ->andReturn(true); + $schema = Mockery::mock('EesyLDAP\Schema') + ->shouldAllowMockingProtectedMethods(); + $schema->shouldReceive('objectclass') + ->once() + ->with($oc1_name) + ->andReturn($oc1) + ->shouldReceive('objectclass') + ->once() + ->with($oc2_name) + ->andReturn($oc2); + $schema->shouldReceive('has_attribute')->passthru(); + $this->assertTrue($schema->has_attribute($attr, [$oc1_name, $oc2_name])); + } + + /** + * @covers \EesyLDAP\Schema::has_attribute + */ + public function testHasAttributeWithArrayAndContinue() { + $attr = 'test'; + $oc1 = Mockery::mock("overload:EesyLDAP\Schema\ObjectClass"); + $oc1_name = 'test1'; + $oc1->shouldReceive('has_attribute') + ->once() + ->with($attr) + ->andReturn(false); + $oc2 = Mockery::mock("overload:EesyLDAP\Schema\ObjectClass"); + $oc2_name = 'test2'; + $oc2->shouldReceive('has_attribute') + ->once() + ->with($attr) + ->andReturn(true); + $schema = Mockery::mock('EesyLDAP\Schema') + ->shouldAllowMockingProtectedMethods(); + $schema->shouldReceive('objectclass') + ->once() + ->with($oc1_name) + ->andReturn($oc1) + ->shouldReceive('objectclass') + ->once() + ->with($oc2_name) + ->andReturn($oc2); + $schema->shouldReceive('has_attribute')->passthru(); + $this->assertTrue($schema->has_attribute($attr, [$oc1_name], $oc2_name)); + } + + /** + * @covers \EesyLDAP\Schema::has_attribute + */ + public function testHasAttributeBadObjectClass() { + $attr = 'test'; + $bad_oc_name = 'badOc'; + $schema = Mockery::mock('EesyLDAP\Schema') + ->shouldAllowMockingProtectedMethods(); + $schema->shouldReceive('objectclass') + ->once() + ->with($bad_oc_name) + ->andReturn(false); + $schema->shouldReceive('has_attribute')->passthru(); + $this->assertFalse($schema->has_attribute($attr, $bad_oc_name)); + } + + /** + * @covers \EesyLDAP\Schema::has_attribute + */ + public function testHasNotAttribute() { + $bad_oc_name = "badOc"; + $schema = Mockery::mock('EesyLDAP\Schema') + ->shouldAllowMockingProtectedMethods(); + $schema->shouldReceive('objectclass') + ->once() + ->with($bad_oc_name) + ->andReturn(false); + $schema->shouldReceive('has_attribute')->passthru(); + $this->assertFalse($schema->has_attribute("test", $bad_oc_name)); + } + +}