diff --git a/.gitignore b/.gitignore index 1c6799cab4..aabd66b988 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,5 @@ openooh/ openooh/specification.json # PHPStorm config -tests/http-client.private.env.json \ No newline at end of file +tests/http-client.private.env.json +/.phpunit.result.cache \ No newline at end of file diff --git a/lib/Controller/Library.php b/lib/Controller/Library.php index c2b4e44fca..36eba5bbb3 100644 --- a/lib/Controller/Library.php +++ b/lib/Controller/Library.php @@ -1146,7 +1146,7 @@ public function add(Request $request, Response $response) // Get Valid Extensions if ($parsedBody->getInt('oldMediaId', ['default' => $options['oldMediaId']]) !== null) { $media = $this->mediaFactory->getById($parsedBody->getInt('oldMediaId', ['default' => $options['oldMediaId']])); - $oldFolderId = $media->folderId; + $folderId = $media->folderId; $validExt = $this->moduleFactory->getValidExtensions(['type' => $media->mediaType, 'allowMediaTypeChange' => $options['allowMediaTypeChange']]); } else { $validExt = $this->moduleFactory->getValidExtensions(); diff --git a/lib/Controller/UserGroup.php b/lib/Controller/UserGroup.php index 9d2e88c08f..5ca1e9470a 100644 --- a/lib/Controller/UserGroup.php +++ b/lib/Controller/UserGroup.php @@ -24,7 +24,6 @@ use Slim\Http\Response as Response; use Slim\Http\ServerRequest as Request; use Xibo\Entity\Permission; -use Xibo\Entity\User; use Xibo\Factory\PermissionFactory; use Xibo\Factory\UserFactory; use Xibo\Factory\UserGroupFactory; @@ -137,7 +136,10 @@ function grid(Request $request, Response $response) foreach ($groups as $group) { /* @var \Xibo\Entity\UserGroup $group */ - $group->libraryQuotaFormatted = ByteFormatter::format($group->libraryQuota * 1024); + $group->setUnmatchedProperty( + 'libraryQuotaFormatted', + ByteFormatter::format($group->libraryQuota * 1024) + ); if ($this->isApi($request)) { continue; diff --git a/lib/Entity/Layout.php b/lib/Entity/Layout.php index 2366fca9f3..a205eab92c 100644 --- a/lib/Entity/Layout.php +++ b/lib/Entity/Layout.php @@ -1653,8 +1653,10 @@ public function toXlf() continue; } - $optionNode = $document->createElement($property->id, $property->value ?? ''); - $optionsNode->appendChild($optionNode); + if (!empty($property->id)) { + $optionNode = $document->createElement($property->id, $property->value ?? ''); + $optionsNode->appendChild($optionNode); + } } if ($property->id === 'updateInterval') { diff --git a/lib/Factory/RequiredFileFactory.php b/lib/Factory/RequiredFileFactory.php index 1f8ab0917f..66881af382 100644 --- a/lib/Factory/RequiredFileFactory.php +++ b/lib/Factory/RequiredFileFactory.php @@ -26,6 +26,7 @@ use Xibo\Entity\RequiredFile; use Xibo\Event\XmdsDependencyRequestEvent; use Xibo\Support\Exception\NotFoundException; +use Xibo\Xmds\Entity\Dependency; /** * Class RequiredFileFactory @@ -129,6 +130,10 @@ public function getByDisplayAndWidget($displayId, $widgetId, $type = 'W') */ public function getByDisplayAndDependency($displayId, $fileType, $id, bool $isUseRealId = true) { + if (!$isUseRealId && $id < 0) { + $fileType = self::getLegacyFileType($id); + } + $result = $this->getStore()->select(' SELECT * FROM `requiredfile` @@ -150,6 +155,22 @@ public function getByDisplayAndDependency($displayId, $fileType, $id, bool $isUs return $this->createEmpty()->hydrate($result[0], ['stringProperties' => ['realId']]); } + /** + * Return the fileType depending on the legacyId range + * @param $id + * @return string + */ + private static function getLegacyFileType($id): string + { + return match (true) { + $id < 0 && $id > Dependency::LEGACY_ID_OFFSET_FONT * -1 => 'bundle', + $id === Dependency::LEGACY_ID_OFFSET_FONT * -1 => 'fontCss', + $id < Dependency::LEGACY_ID_OFFSET_FONT * -1 && $id > Dependency::LEGACY_ID_OFFSET_PLAYER_SOFTWARE * -1 => 'font', + $id < Dependency::LEGACY_ID_OFFSET_PLAYER_SOFTWARE * -1 && $id > Dependency::LEGACY_ID_OFFSET_ASSET * -1 => 'playersoftware', + $id < Dependency::LEGACY_ID_OFFSET_PLAYER_SOFTWARE * -1 => 'asset', + }; + } + /** * @param int $displayId * @param string $path The path of this dependency @@ -345,7 +366,12 @@ public function resolveRequiredFileFromRequest($request): RequiredFile if (empty($fileType)) { throw new NotFoundException(__('Missing fileType')); } - $file = $this->getByDisplayAndDependency($displayId, $fileType, $itemId); + $file = $this->getByDisplayAndDependency( + $displayId, + $fileType, + $itemId, + !($fileType == 'media' && $itemId < 0) + ); // Update $file->path with the path on disk (likely /dependencies/$fileType/$itemId) $event = new XmdsDependencyRequestEvent($file); diff --git a/lib/Factory/TagFactory.php b/lib/Factory/TagFactory.php index 3508fe7774..f6aa108a41 100644 --- a/lib/Factory/TagFactory.php +++ b/lib/Factory/TagFactory.php @@ -69,7 +69,7 @@ public function createTagLink($tagId, $tag, $value) $tagLink = $this->createEmptyLink(); $tagLink->tag = trim($tag); $tagLink->tagId = $tagId; - $tagLink->value = trim($value); + $tagLink->value = trim($value ?? ''); return $tagLink; } diff --git a/lib/XTR/MaintenanceRegularTask.php b/lib/XTR/MaintenanceRegularTask.php index 998198e452..66a19f5660 100644 --- a/lib/XTR/MaintenanceRegularTask.php +++ b/lib/XTR/MaintenanceRegularTask.php @@ -431,7 +431,9 @@ private function publishLayouts() if (count($layouts) > 0) { foreach ($layouts as $layout) { // check if the layout should be published now according to the date - if (Carbon::createFromTimestamp($layout->publishedDate)->format('U') < Carbon::now()->format('U')) { + if (Carbon::createFromFormat(DateFormatHelper::getSystemFormat(), $layout->publishedDate) + ->isBefore(Carbon::now()->format(DateFormatHelper::getSystemFormat())) + ) { try { // publish the layout $layout = $this->layoutFactory->concurrentRequestLock($layout, true); diff --git a/lib/Xmds/Listeners/XmdsFontsListener.php b/lib/Xmds/Listeners/XmdsFontsListener.php index eb18d447cf..ef9750a5de 100644 --- a/lib/Xmds/Listeners/XmdsFontsListener.php +++ b/lib/Xmds/Listeners/XmdsFontsListener.php @@ -73,7 +73,7 @@ public function onDependencyList(XmdsDependencyListEvent $event): void filesize($fontsCssPath), md5_file($fontsCssPath), true, - $this->getLegacyId(1, Dependency::LEGACY_ID_OFFSET_FONT) + $this->getLegacyId(0, Dependency::LEGACY_ID_OFFSET_FONT) ); } diff --git a/lib/Xmds/Soap.php b/lib/Xmds/Soap.php index 033ae8618c..559d55ce03 100644 --- a/lib/Xmds/Soap.php +++ b/lib/Xmds/Soap.php @@ -379,6 +379,11 @@ protected function doRequiredFiles( 'media' => 'M', default => 'P', }; + + if ($node->getAttribute('id') < 0 && $type === 'M') { + $type = 'P'; + } + $newUrl = $this->generateRequiredFileDownloadPath( $type, $node->getAttribute('id'), @@ -2755,6 +2760,7 @@ private function addDependency( $file->setAttribute('path', $dependencyBasePath); } $file->setAttribute('id', $dependency->legacyId); + $file->setAttribute('fileType', 'media'); } // Add our node diff --git a/lib/Xmds/Soap5.php b/lib/Xmds/Soap5.php index 2a210f56a6..9c4d8b7ef1 100644 --- a/lib/Xmds/Soap5.php +++ b/lib/Xmds/Soap5.php @@ -424,6 +424,8 @@ public function RegisterDisplay( } $display->commercialLicence = $commercialLicence; + $node = $return->createElement('commercialLicence', $commercialLicenceString); + $displayElement->appendChild($node); } // commercial licence not applicable for Windows and Linux players. diff --git a/phpunit.xml b/phpunit.xml index 839e6605ca..c5e018cba8 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,8 +1,26 @@ + + + colors="true"> tests/integration/ @@ -12,19 +30,22 @@ tests/Widget/ + + tests/xmds/ + broken - - + + lib - - + + - + diff --git a/tests/Bootstrap.php b/tests/Bootstrap.php index ea86cf424e..ddeebe2d3c 100644 --- a/tests/Bootstrap.php +++ b/tests/Bootstrap.php @@ -1,9 +1,10 @@ debug('LocalWebTestCase: setUp'); parent::setUp(); @@ -529,7 +529,7 @@ public function setUp() * @inheritDoc * @throws \Exception */ - public function tearDown() + public function tearDown(): void { self::getLogger()->debug('LocalWebTestCase: tearDown'); diff --git a/tests/Xmds/GetDataTest.php b/tests/Xmds/GetDataTest.php new file mode 100644 index 0000000000..34aea45883 --- /dev/null +++ b/tests/Xmds/GetDataTest.php @@ -0,0 +1,136 @@ +. + */ + +namespace Xibo\Tests\Xmds; + +use DOMDocument; +use DOMXPath; +use Xibo\Tests\XmdsTestCase; + +/** + * @property string $dataSetXml + * @property string $requiredFilesXml + * @property string $requiredFilesXmlv6 + */ +class GetDataTest extends XmdsTestCase +{ + public function setUp(): void + { + parent::setUp(); + + $registerXml = ' + + + test + phpstorm + PHPStorm + windows + 4 + 420 + CC:40:D0:46:3C:A8 + + +'; + + // to make sure Display is logged in, otherwise WidgetSyncTask will not sync data. + $this->sendRequest('POST', $registerXml, 7); + + $this->dataSetXml = ' + + + test + phpstorm + 112 + + +'; + + + $this->requiredFilesXml = ' + + + test + phpstorm + + +'; + } + public function testGetData() + { + // Fresh RF + $this->sendRequest('POST', $this->requiredFilesXml, 7); + + // Execute Widget Sync task so we can have data for our Widget + exec('cd /var/www/cms; php bin/run.php 9'); + + // XMDS GetData with our dataSet Widget + $response = $this->sendRequest('POST', $this->dataSetXml); + $content = $response->getBody()->getContents(); + + // expect GetDataResponse + $this->assertStringContainsString( + '', + $content, + 'GetData received incorrect response' + ); + + $document = new DOMDocument(); + $document->loadXML($content); + $xpath = new DOMXpath($document); + $result = $xpath->evaluate('string(//data)'); + + $array = json_decode($result, true); + + // go through GetData response and see what we have + foreach ($array as $key => $item) { + // data and meta expected to not be empty + if ($key === 'data' || $key === 'meta') { + $this->assertNotEmpty($item); + $this->assertNotEmpty($key); + } + + if ($key === 'data') { + $i = 0; + // go through the expected 2 rows in our dataSet data and see if the column/value matches + foreach ($item as $row) { + $this->assertNotEmpty($row); + if ($i === 0) { + $this->assertSame('Example text value', $row['Text']); + $this->assertSame(1, $row['Number']); + } else if ($i === 1) { + $this->assertSame('PHPUnit text', $row['Text']); + $this->assertSame(2, $row['Number']); + } + $i++; + } + } + } + } +} diff --git a/tests/Xmds/GetDependencyTest.php b/tests/Xmds/GetDependencyTest.php new file mode 100644 index 0000000000..118ade82f4 --- /dev/null +++ b/tests/Xmds/GetDependencyTest.php @@ -0,0 +1,263 @@ +. + */ + +namespace Xibo\Tests\Xmds; + +use DOMDocument; +use DOMXPath; +use PHPUnit\Framework\Attributes\DataProvider; +use Xibo\Tests\XmdsTestCase; + +/** + * @property string $requiredFilesXml + * @property string $requiredFilesXmlv6 + */ +class GetDependencyTest extends XmdsTestCase +{ + public function setUp(): void + { + parent::setUp(); + + $this->requiredFilesXml = ' + + + test + phpstorm + + +'; + + $this->requiredFilesXmlv6 = ' + + + test + phpunit6 + + +'; + } + + public static function successCasesBundle(): array + { + return [ + [7], + ]; + } + + public static function successCasesFont(): array + { + return [ + [7, 'Aileron-Heavy.otf'], + [7, 'fonts.css'], + [6, 'Aileron-Heavy.otf'], + [6, 'fonts.css'], + [5, 'Aileron-Heavy.otf'], + [5, 'fonts.css'], + [4, 'Aileron-Heavy.otf'], + [4, 'fonts.css'], + ]; + } + + public static function successCasesBundleOld(): array + { + return [ + [6], + [5], + [4], + ]; + } + + #[DataProvider('successCasesFont')] + public function testGetFont($version, $fileName) + { + if ($version === 7) { + $rf = $this->sendRequest('POST', $this->requiredFilesXml, $version); + } else { + $rf = $this->sendRequest('POST', $this->requiredFilesXmlv6, $version); + } + + $response = $rf->getBody()->getContents(); + $path = null; + + $document = new DOMDocument(); + $document->loadXML($response); + $xpath = new DOMXpath($document); + $result = $xpath->evaluate('string(//RequiredFilesXml)'); + $array = json_decode(json_encode(simplexml_load_string($result)), true); + + foreach ($array as $item) { + foreach ($item as $file) { + if (!empty($file['@attributes'])) { + if ($file['@attributes']['saveAs'] === $fileName) { + if ($version === 7) { + $this->assertSame('dependency', $file['@attributes']['type']); + } else { + $this->assertSame('media', $file['@attributes']['type']); + } + + $path = strstr($file['@attributes']['path'], '?'); + } + } + } + } + + $this->assertNotEmpty($path); + + // Font dependency is still http download, try to get it here + $getFile = $this->getFile($path); + $this->assertSame(200, $getFile->getStatusCode()); + $this->assertNotEmpty($getFile->getBody()->getContents()); + } + + #[DataProvider('successCasesBundle')] + public function testGetBundlev7($version) + { + $rf = $this->sendRequest('POST', $this->requiredFilesXml, $version); + $response = $rf->getBody()->getContents(); + $size = null; + $id = null; + $type = null; + + $document = new DOMDocument(); + $document->loadXML($response); + $xpath = new DOMXpath($document); + $result = $xpath->evaluate('string(//RequiredFilesXml)'); + $array = json_decode(json_encode(simplexml_load_string($result)), true); + + foreach ($array as $item) { + foreach ($item as $file) { + if (!empty($file['@attributes'])) { + if ($file['@attributes']['saveAs'] === 'bundle.min.js') { + $size = $file['@attributes']['size']; + $type = $file['@attributes']['fileType']; + $id = $file['@attributes']['id']; + } + } + + } + } + + $this->assertNotEmpty($size); + $this->assertNotEmpty($type); + $this->assertNotEmpty($id); + + // construct the xml for GetDependency wsdl request + $bundleXml = ' + + + test + phpstorm + '. $type .' + '. $id .' + 0 + '. $size .' + + +'; + + // try to call GetDependency with our xml + $getBundle = $this->sendRequest('POST', $bundleXml, $version); + $getBundleResponse = $getBundle->getBody()->getContents(); + // expect success + $this->assertSame(200, $getBundle->getStatusCode()); + // expect not empty body + $this->assertNotEmpty($getBundleResponse); + // expect response format + $this->assertStringContainsString( + '', + $getBundleResponse, + 'GetDependency getBundle received incorrect response' + ); + } + + #[DataProvider('successCasesBundleOld')] + public function testGetBundlev6($version) + { + $rf = $this->sendRequest('POST', $this->requiredFilesXmlv6, $version); + $response = $rf->getBody()->getContents(); + $size = null; + $id = null; + $type = null; + + $document = new DOMDocument(); + $document->loadXML($response); + $xpath = new DOMXpath($document); + $result = $xpath->evaluate('string(//RequiredFilesXml)'); + $array = json_decode(json_encode(simplexml_load_string($result)), true); + + foreach ($array as $item) { + foreach ($item as $file) { + if (!empty($file['@attributes'])) { + if ($file['@attributes']['saveAs'] === 'bundle.min.js') { + $size = $file['@attributes']['size']; + $type = $file['@attributes']['type']; + $id = $file['@attributes']['id']; + } + } + } + } + + $this->assertNotEmpty($size); + $this->assertNotEmpty($type); + $this->assertNotEmpty($id); + + // construct the xml for GetDependency wsdl request + $bundleXml = ' + + + test + phpunit6 + '. $id .' + '. $type .' + 0 + '. $size .' + + +'; + + // try to call GetFile with our xml + $getBundle = $this->sendRequest('POST', $bundleXml, $version); + $getBundleResponse = $getBundle->getBody()->getContents(); + // expect success + $this->assertSame(200, $getBundle->getStatusCode()); + // expect not empty body + $this->assertNotEmpty($getBundleResponse); + // expect response format + $this->assertStringContainsString( + '', + $getBundleResponse, + 'GetDependency getBundle received incorrect response' + ); + } +} diff --git a/tests/Xmds/NotifyStatusTest.php b/tests/Xmds/NotifyStatusTest.php new file mode 100644 index 0000000000..7c18b7a7c4 --- /dev/null +++ b/tests/Xmds/NotifyStatusTest.php @@ -0,0 +1,215 @@ +. + */ + +namespace Xibo\Tests\Xmds; + +use PHPUnit\Framework\Attributes\DataProvider; +use Xibo\Tests\XmdsTestCase; + +/** + * @property string $currentLayoutXml + * @property string $geoLocationXml + * @property string $orientationXml + */ +final class NotifyStatusTest extends XmdsTestCase +{ + public function setUp(): void + { + parent::setUp(); + + $this->currentLayoutXml = ' + + + test + phpstorm + {"currentLayoutId":1} + + +'; + + $this->geoLocationXml = ' + + + test + phpstorm + {"latitude":52.3676, "longitude":4.9041} + + +'; + + $this->orientationXml = ' + + + test + phpstorm + {"width":7680, "height":4320} + + +'; + } + + public static function successCases(): array + { + return [ + [7], + [6], + [5], + [4], + ]; + } + + public static function failureCases(): array + { + return [ + [3], + ]; + } + + #[DataProvider('successCases')] + public function testCurrentLayout(int $version) + { + $request = $this->sendRequest('POST', $this->currentLayoutXml, $version); + + $this->assertStringContainsString( + 'true', + $request->getBody()->getContents(), + 'Notify Current Layout received incorrect response' + ); + } + + #[DataProvider('failureCases')] + public function testCurrentLayoutFailure(int $version) + { + // disable exception on http_error in guzzle, so we can still check the response + $request = $this->sendRequest('POST', $this->currentLayoutXml, $version, false); + + $this->assertSame(500, $request->getStatusCode()); + // check the fault code + $this->assertStringContainsString( + 'SOAP-ENV:Server', + $request->getBody(), + 'Notify Current Layout received incorrect response' + ); + + // check the fault string + $this->assertStringContainsString( + 'Procedure \'NotifyStatus\' not present', + $request->getBody(), + 'Notify Current Layout received incorrect response' + ); + } + + #[DataProvider('failureCases')] + public function testCurrentLayoutExceptionFailure(int $version) + { + // we are expecting 500 Server Exception here for xmds 3 + $this->expectException('GuzzleHttp\Exception\ServerException'); + $this->expectExceptionCode(500); + $this->sendRequest('POST', $this->currentLayoutXml, $version); + } + + #[DataProvider('successCases')] + public function testGeoLocation($version) + { + $request = $this->sendRequest('POST', $this->geoLocationXml, $version); + + $this->assertStringContainsString( + 'true', + $request->getBody()->getContents(), + 'Notify Geo Location received incorrect response' + ); + } + + #[DataProvider('failureCases')] + public function testGeoLocationFailure(int $version) + { + // disable exception on http_error in guzzle, so we can still check the response + $request = $this->sendRequest('POST', $this->geoLocationXml, $version, false); + + $this->assertSame(500, $request->getStatusCode()); + // check the fault code + $this->assertStringContainsString( + 'SOAP-ENV:Server', + $request->getBody(), + 'Notify Geo Location received incorrect response' + ); + + // check the fault string + $this->assertStringContainsString( + 'Procedure \'NotifyStatus\' not present', + $request->getBody(), + 'Notify Geo Location received incorrect response' + ); + } + + #[DataProvider('failureCases')] + public function testGeoLocationExceptionFailure(int $version) + { + // we are expecting 500 Server Exception here for xmds 3 + $this->expectException('GuzzleHttp\Exception\ServerException'); + $this->expectExceptionCode(500); + $this->sendRequest('POST', $this->geoLocationXml, $version); + } + + #[DataProvider('successCases')] + public function testOrientation(int $version) + { + $request = $this->sendRequest('POST', $this->orientationXml, $version); + + $this->assertStringContainsString( + 'true', + $request->getBody()->getContents(), + 'Notify Orientation received incorrect response' + ); + } + + #[DataProvider('failureCases')] + public function testOrientationFailure(int $version) + { + // disable exception on http_error in guzzle, so we can still check the response + $request = $this->sendRequest('POST', $this->orientationXml, $version, false); + + $this->assertSame(500, $request->getStatusCode()); + // check the fault code + $this->assertStringContainsString( + 'SOAP-ENV:Server', + $request->getBody(), + 'Notify Orientation received incorrect response' + ); + + // check the fault string + $this->assertStringContainsString( + 'Procedure \'NotifyStatus\' not present', + $request->getBody(), + 'Notify Orientation received incorrect response' + ); + } + + #[DataProvider('failureCases')] + public function testOrientationExceptionFailure(int $version) + { + // we are expecting 500 Server Exception here for xmds 3 + $this->expectException('GuzzleHttp\Exception\ServerException'); + $this->expectExceptionCode(500); + $this->sendRequest('POST', $this->orientationXml, $version); + } +} diff --git a/tests/Xmds/RegisterDisplayTest.php b/tests/Xmds/RegisterDisplayTest.php new file mode 100644 index 0000000000..3a0993388d --- /dev/null +++ b/tests/Xmds/RegisterDisplayTest.php @@ -0,0 +1,114 @@ +. + */ + +namespace Xibo\Tests\Xmds; + +use DOMDocument; +use DOMXPath; +use Xibo\Tests\XmdsTestCase; + +/** + * @property string $registerWindowsXml + * @property string $registerAndroidXml + */ +class RegisterDisplayTest extends XmdsTestCase +{ + public function setUp(): void + { + parent::setUp(); + + $this->registerWindowsXml = ' + + + test + phpstorm + PHPStorm + windows + 4 + 420 + CC:40:D0:46:3C:A8 + + +'; + + $this->registerAndroidXml = ' + + + test + PHPUnitWaiting + phpunitwaiting + android + 4 + 420 + CC:40:D0:46:3C:A8 + trial + + +'; + } + + public function testRegisterDisplayAuthed() + { + $request = $this->sendRequest('POST', $this->registerWindowsXml, 7); + $response = $request->getBody()->getContents(); + + $document = new DOMDocument(); + $document->loadXML($response); + + $xpath = new DOMXpath($document); + $result = $xpath->evaluate('string(//ActivationMessage)'); + $innerDocument = new DOMDocument(); + $innerDocument->loadXML($result); + + $this->assertSame('READY', $innerDocument->documentElement->getAttribute('code')); + $this->assertSame('Display is active and ready to start.', $innerDocument->documentElement->getAttribute('message')); + } + + public function testRegisterDisplayNoAuth() + { + $request = $this->sendRequest('POST', $this->registerAndroidXml, 7); + $response = $request->getBody()->getContents(); + + $document = new DOMDocument(); + $document->loadXML($response); + + $xpath = new DOMXpath($document); + $result = $xpath->evaluate('string(//ActivationMessage)'); + $innerDocument = new DOMDocument(); + $innerDocument->loadXML($result); + + $this->assertSame('WAITING', $innerDocument->documentElement->getAttribute('code')); + $this->assertSame( + 'Display is Registered and awaiting Authorisation from an Administrator in the CMS', + $innerDocument->documentElement->getAttribute('message') + ); + + $array = json_decode(json_encode(simplexml_load_string($result)), true); + + foreach ($array as $key => $value) { + // $this->getLogger()->debug($key . ' -> ' . json_encode($value)); + if ($key === 'commercialLicence') { + $this->assertSame('trial', $value); + } + } + } +} diff --git a/tests/Xmds/ReportFaultsTest.php b/tests/Xmds/ReportFaultsTest.php new file mode 100644 index 0000000000..398bc71a1d --- /dev/null +++ b/tests/Xmds/ReportFaultsTest.php @@ -0,0 +1,106 @@ +. + */ + +namespace Xibo\Tests\Xmds; + +use PHPUnit\Framework\Attributes\DataProvider; +use Xibo\Tests\XmdsTestCase; + +/** + * @property string $xmlRequest + */ +final class ReportFaultsTest extends XmdsTestCase +{ + public function setUp(): void + { + parent::setUp(); + + $this->xmlRequest = ' + + + test + phpstorm + [{date:"2023-04-20 17:03:52",expires:"2023-04-21 17:03:52",code:00001,reason:"Test",scheduleId:0,layoutId:0,regionId:0,mediaId:0,widgetId:0}] + + + '; + } + + public static function successCases(): array + { + return [ + [7], + [6], + ]; + } + + public static function failureCases(): array + { + return [ + [5], + [4], + [3], + ]; + } + + #[DataProvider('successCases')] + public function testSendFaultSuccess(int $version) + { + $request = $this->sendRequest('POST', $this->xmlRequest, $version); + + $this->assertStringContainsString( + 'true', + $request->getBody()->getContents(), + 'Send fault received incorrect response' + ); + } + + #[DataProvider('failureCases')] + public function testSendFaultFailure(int $version) + { + // disable exception on http_error in guzzle, so we can still check the response + $request = $this->sendRequest('POST', $this->xmlRequest, $version, false); + + // check the fault code + $this->assertStringContainsString( + 'SOAP-ENV:Server', + $request->getBody(), + 'Send fault received incorrect response' + ); + + // check the fault string + $this->assertStringContainsString( + 'Procedure \'ReportFaults\' not present', + $request->getBody(), + 'Send fault received incorrect response' + ); + } + + #[DataProvider('failureCases')] + public function testSendFaultExceptionFailure(int $version) + { + // we are expecting 500 Server Exception here for xmds 3,4 and 5 + $this->expectException('GuzzleHttp\Exception\ServerException'); + $this->expectExceptionCode(500); + $this->sendRequest('POST', $this->xmlRequest, $version); + } +} diff --git a/tests/XmdsTestCase.php b/tests/XmdsTestCase.php new file mode 100644 index 0000000000..93b9341a87 --- /dev/null +++ b/tests/XmdsTestCase.php @@ -0,0 +1,180 @@ +. + */ + +namespace Xibo\Tests; + +use GuzzleHttp\Client; +use GuzzleHttp\Exception\GuzzleException; +use Monolog\Handler\StreamHandler; +use Monolog\Logger; +use PHPUnit\Framework\TestCase; +use Psr\Container\ContainerInterface; +use Psr\Http\Message\ResponseInterface; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; + +class xmdsTestCase extends TestCase +{ + /** @var ContainerInterface */ + public static $container; + + /** @var LoggerInterface */ + public static $logger; + + /** @var Client */ + public $client; + + /** + * @inheritDoc + */ + public function getGuzzleClient(array $requestOptions = []): Client + { + if ($this->client === null) { + $this->client = new Client($requestOptions); + } + + return $this->client; + } + + /** + * @param string $method + * @param string $body + * @param string $path + * @param array $headers + * @return ResponseInterface + * @throws GuzzleException + */ + protected function sendRequest( + string $method = 'POST', + string $body = '', + string $version = '7', + bool $httpErrors = true, + string $path = 'http://localhost/xmds.php?v=', + array $headers = ['HTTP_ACCEPT'=>'text/xml'] + ): ResponseInterface { + // Create a request for tests + return $this->client->request($method, $path . $version, [ + 'headers' => $headers, + 'body' => $body, + 'http_errors' => $httpErrors + ]); + } + + protected function getFile( + string $fileQuery = '', + string $method = 'GET', + string $basePath = 'http://localhost/xmds.php', + ): ResponseInterface { + // Create a request for tests + return $this->client->request($method, $basePath . $fileQuery); + } + + /** + * Create a global container for all tests to share. + * @throws \Exception + */ + public static function setUpBeforeClass(): void + { + parent::setUpBeforeClass(); + } + + /** + * Convenience function to skip a test with a reason and close output buffers nicely. + * @param string $reason + */ + public function skipTest(string $reason): void + { + $this->markTestSkipped($reason); + } + + /** + * @return Logger|NullLogger|LoggerInterface + */ + public static function getLogger(): Logger|NullLogger|LoggerInterface + { + // Create if necessary + if (self::$logger === null) { + if (isset($_SERVER['PHPUNIT_LOG_TO_CONSOLE']) && $_SERVER['PHPUNIT_LOG_TO_CONSOLE']) { + self::$logger = new Logger('TESTS', [new StreamHandler(STDERR, Logger::DEBUG)]); + } else { + self::$logger = new NullLogger(); + } + } + + return self::$logger; + } + + /** + * @inheritDoc + * @throws \Exception + */ + public function setUp(): void + { + self::getLogger()->debug('xmdsTestCase: setUp'); + parent::setUp(); + + // Establish a local reference to the Slim app object + $this->client = $this->getGuzzleClient(); + } + + /** + * @inheritDoc + * @throws \Exception + */ + public function tearDown(): void + { + self::getLogger()->debug('xmdsTestCase: tearDown'); + + // Close and tidy up the app + $this->client = null; + + parent::tearDown(); + } + + /** + * Set the _SERVER vars for the suite + * @param array $userSettings + */ + public static function setEnvironment(array $userSettings = []): void + { + $defaults = [ + 'REQUEST_METHOD' => 'GET', + 'REQUEST_URI' => '/', + 'SCRIPT_NAME' => '', + 'PATH_INFO' => '/', + 'QUERY_STRING' => '', + 'SERVER_NAME' => 'local.dev', + 'SERVER_PORT' => 80, + 'HTTP_ACCEPT' => 'text/xml,text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + 'HTTP_ACCEPT_LANGUAGE' => 'en-US,en;q=0.8', + 'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.3', + 'USER_AGENT' => 'Slim Framework', + 'REMOTE_ADDR' => '127.0.0.1', + ]; + + $environmentSettings = array_merge($userSettings, $defaults); + + foreach ($environmentSettings as $key => $value) { + $_SERVER[$key] = $value; + } + } +}