Skip to content

Commit 9caa4bd

Browse files
committed
Replace EOL mbhsoft/phpunit-xsdvalidation lib
1 parent 3836127 commit 9caa4bd

4 files changed

Lines changed: 224 additions & 6 deletions

File tree

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
"twig/twig": "^3"
1919
},
2020
"require-dev": {
21+
"ext-libxml": "*",
2122
"ext-zlib": "*",
22-
"mbhsoft/phpunit-xsdvalidation": "^3.0",
2323
"mockery/mockery": "^1.5",
2424
"overtrue/phplint": "*",
2525
"phpmd/phpmd": "^2.6",

src/Tests/Component/Metadata/MetadataFactoryTest.php

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
namespace Surfnet\SamlBundle\Tests\Component\Metadata;
2020

21-
use Jasny\PHPUnit\Constraint\XSDValidation;
2221
use Mockery as m;
2322
use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
2423
use PHPUnit\Framework\TestCase;
@@ -43,6 +42,8 @@ class MetadataFactoryTest extends TestCase
4342

4443
public m\Mock|RouterInterface $router;
4544

45+
private XsdValidator $xsdValidator;
46+
4647
public function setUp(): void
4748
{
4849
// Load the XML template from filesystem as the FilesystemLoader does not honour the bundle prefix
@@ -54,6 +55,24 @@ public function setUp(): void
5455
$this->twig = new Environment($loader);
5556
$this->router = m::mock(RouterInterface::class);
5657
$this->router->shouldReceive('generate')->andReturn('https://foobar.example.com');
58+
$this->xsdValidator = new XsdValidator();
59+
}
60+
61+
/**
62+
* Helper method to assert XML document is valid against XSD schema
63+
*/
64+
private function assertXmlIsValidAgainstXsd(\DOMDocument $document, string $xsdPath): void
65+
{
66+
$errors = $this->xsdValidator->validate($document, $xsdPath);
67+
68+
if (!empty($errors)) {
69+
self::fail(
70+
"XML document does not validate against XSD schema.\n" .
71+
"Validation errors:\n" . implode("\n", $errors)
72+
);
73+
}
74+
75+
self::assertTrue(true, "XML document is valid against XSD schema");
5776
}
5877

5978
public function test_valid_metadata_xml(): void
@@ -112,9 +131,8 @@ public function test_builds_idp_metadata(): void
112131
$metadataConfiguration->ssoRoute = 'https://foobar.example.com';
113132
$this->buildFactory($metadataConfiguration);
114133
$metadata = $this->factory->generate();
115-
$constraint = new XSDValidation(__DIR__ . '/xsd/metadata.xsd');
116134
self::assertEquals($expectedResult, $metadata->__toString());
117-
self::assertThat($metadata->document, $constraint);
135+
$this->assertXmlIsValidAgainstXsd($metadata->document, __DIR__ . '/xsd/metadata.xsd');
118136
}
119137

120138
public function test_builds_idp_metadata_signed(): void
@@ -131,8 +149,7 @@ public function test_builds_idp_metadata_signed(): void
131149
$signingService = new SigningService($keyLoader, $privateKeyLoader);
132150
$this->buildFactory($metadataConfiguration, $signingService);
133151
$metadata = $this->factory->generate();
134-
$constraint = new XSDValidation(__DIR__ . '/xsd/metadata.xsd');
135-
self::assertThat($metadata->document, $constraint);
152+
$this->assertXmlIsValidAgainstXsd($metadata->document, __DIR__ . '/xsd/metadata.xsd');
136153
}
137154

138155
private function buildFactory(MetadataConfiguration $metadata, SigningService $signingService = null): void
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php declare(strict_types=1);
2+
3+
/**
4+
* Copyright 2021 SURF B.V.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
namespace Surfnet\SamlBundle\Tests\Component\Metadata;
20+
21+
use DOMDocument;
22+
23+
/**
24+
* Validates XML documents against XSD schemas
25+
*/
26+
class XsdValidator
27+
{
28+
/**
29+
* Validate an XML document against an XSD schema
30+
*
31+
* @param DOMDocument $document The XML document to validate
32+
* @param string $xsdPath Path to the XSD schema file
33+
* @return array Empty array if valid, array of error messages if invalid
34+
*/
35+
public function validate(DOMDocument $document, string $xsdPath): array
36+
{
37+
if (!file_exists($xsdPath)) {
38+
return ["XSD schema file not found: {$xsdPath}"];
39+
}
40+
41+
libxml_use_internal_errors(true);
42+
$isValid = $document->schemaValidate($xsdPath);
43+
44+
if (!$isValid) {
45+
$errors = libxml_get_errors();
46+
$errorMessages = array_map(function ($error) {
47+
return sprintf(
48+
"Line %d: %s",
49+
$error->line,
50+
trim($error->message)
51+
);
52+
}, $errors);
53+
libxml_clear_errors();
54+
55+
return $errorMessages;
56+
}
57+
58+
libxml_clear_errors();
59+
return [];
60+
}
61+
62+
/**
63+
* Check if an XML document is valid against an XSD schema
64+
*
65+
* @param DOMDocument $document The XML document to validate
66+
* @param string $xsdPath Path to the XSD schema file
67+
* @return bool True if valid, false otherwise
68+
*/
69+
public function isValid(DOMDocument $document, string $xsdPath): bool
70+
{
71+
return empty($this->validate($document, $xsdPath));
72+
}
73+
}
74+
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
<?php declare(strict_types=1);
2+
3+
/**
4+
* Copyright 2026 SURF B.V.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
namespace Surfnet\SamlBundle\Tests\Component\Metadata;
20+
21+
use DOMDocument;
22+
use PHPUnit\Framework\TestCase;
23+
24+
class XsdValidatorTest extends TestCase
25+
{
26+
private XsdValidator $validator;
27+
28+
public function setUp(): void
29+
{
30+
$this->validator = new XsdValidator();
31+
}
32+
33+
public function test_validates_valid_xml_document(): void
34+
{
35+
$xml = <<<XML
36+
<?xml version="1.0" encoding="UTF-8"?>
37+
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="https://example.com">
38+
<md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
39+
<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://example.com/acs" index="0"/>
40+
</md:SPSSODescriptor>
41+
</md:EntityDescriptor>
42+
XML;
43+
44+
$document = new DOMDocument();
45+
$document->loadXML($xml);
46+
47+
$errors = $this->validator->validate($document, __DIR__ . '/xsd/metadata.xsd');
48+
49+
self::assertEmpty($errors);
50+
}
51+
52+
public function test_is_valid_returns_true_for_valid_xml(): void
53+
{
54+
$xml = <<<XML
55+
<?xml version="1.0" encoding="UTF-8"?>
56+
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="https://example.com">
57+
<md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
58+
<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://example.com/acs" index="0"/>
59+
</md:SPSSODescriptor>
60+
</md:EntityDescriptor>
61+
XML;
62+
63+
$document = new DOMDocument();
64+
$document->loadXML($xml);
65+
66+
$isValid = $this->validator->isValid($document, __DIR__ . '/xsd/metadata.xsd');
67+
68+
self::assertTrue($isValid);
69+
}
70+
71+
public function test_detects_invalid_xml_document(): void
72+
{
73+
$xml = <<<XML
74+
<?xml version="1.0" encoding="UTF-8"?>
75+
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata">
76+
<md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
77+
<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://example.com/acs" index="0"/>
78+
</md:SPSSODescriptor>
79+
</md:EntityDescriptor>
80+
XML;
81+
82+
$document = new DOMDocument();
83+
$document->loadXML($xml);
84+
85+
$errors = $this->validator->validate($document, __DIR__ . '/xsd/metadata.xsd');
86+
87+
self::assertNotEmpty($errors);
88+
self::assertIsArray($errors);
89+
}
90+
91+
public function test_is_valid_returns_false_for_invalid_xml(): void
92+
{
93+
$xml = <<<XML
94+
<?xml version="1.0" encoding="UTF-8"?>
95+
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata">
96+
<md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
97+
<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://example.com/acs" index="0"/>
98+
</md:SPSSODescriptor>
99+
</md:EntityDescriptor>
100+
XML;
101+
102+
$document = new DOMDocument();
103+
$document->loadXML($xml);
104+
105+
$isValid = $this->validator->isValid($document, __DIR__ . '/xsd/metadata.xsd');
106+
107+
self::assertFalse($isValid);
108+
}
109+
110+
public function test_returns_error_for_missing_xsd_file(): void
111+
{
112+
$xml = <<<XML
113+
<?xml version="1.0" encoding="UTF-8"?>
114+
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="https://example.com">
115+
</md:EntityDescriptor>
116+
XML;
117+
118+
$document = new DOMDocument();
119+
$document->loadXML($xml);
120+
121+
$errors = $this->validator->validate($document, __DIR__ . '/xsd/non-existent.xsd');
122+
123+
self::assertNotEmpty($errors);
124+
self::assertStringContainsString('not found', $errors[0]);
125+
}
126+
}
127+

0 commit comments

Comments
 (0)