From 98cd7c4192c9e59be4dd17a3b907a2019927890f Mon Sep 17 00:00:00 2001 From: Florian Friedrich Date: Mon, 12 Apr 2021 11:03:49 +0200 Subject: [PATCH] Allow requesting a specific channel Signed-off-by: Florian Friedrich --- README.md | 18 +- index.php | 30 +-- src/Requests/ChannelRequest.php | 26 +++ .../VersionRequest.php} | 7 +- src/Response.php | 210 ------------------ src/Responses/BaseResponse.php | 112 ++++++++++ src/Responses/ChannelResponse.php | 86 +++++++ src/Responses/VersionResponse.php | 137 ++++++++++++ tests/integration/features/beta.feature | 21 +- .../features/bootstrap/FeatureContext.php | 34 ++- tests/integration/features/daily.feature | 11 + tests/integration/features/stable.feature | 19 +- tests/unit/ChannelRequestTest.php | 16 ++ tests/unit/ChannelResponseTest.php | 197 ++++++++++++++++ ...RequestTest.php => VersionRequestTest.php} | 10 +- ...sponseTest.php => VersionResponseTest.php} | 24 +- 16 files changed, 708 insertions(+), 250 deletions(-) create mode 100755 src/Requests/ChannelRequest.php rename src/{Request.php => Requests/VersionRequest.php} (97%) delete mode 100755 src/Response.php create mode 100755 src/Responses/BaseResponse.php create mode 100755 src/Responses/ChannelResponse.php create mode 100755 src/Responses/VersionResponse.php create mode 100755 tests/unit/ChannelRequestTest.php create mode 100755 tests/unit/ChannelResponseTest.php rename tests/unit/{RequestTest.php => VersionRequestTest.php} (74%) rename tests/unit/{ResponseTest.php => VersionResponseTest.php} (98%) diff --git a/README.md b/README.md index 190a8c27..dc7bf8bd 100755 --- a/README.md +++ b/README.md @@ -21,13 +21,25 @@ Example call: updater_server/?version=9x0x0x12x1448709225.0768x1448709281xstable ``` +Deployed URL: https://updates.nextcloud.com/updater_server/ +Example call: updater_server/?channel=stable +```xml + + + 9.0.51 + Nextcloud 9.0.51 + https://download.nextcloud.com/server/releases/nextcloud-9.0.51.zip + https://docs.nextcloud.com/server/9/admin_manual/maintenance/upgrade.html + +``` + ## Webhook deployment -If you wish to receive webhooks and then automatically deploy the lastest version of the updater server there is one special API endpoint available. +If you wish to receive webhooks and then automatically deploy the latest version of the updater server there is one special API endpoint available. -For this the [Github Webhook](https://developer.github.com/webhooks/) needs to be configured to send `push` events to the endpoint `/hook` of the updater server. There only the ending part is crucial. That means that any URL ending in `/hook` which is served by the `index.php` of the updater server will trigger this behaviour. +For this the [GitHub Webhook](https://developer.github.com/webhooks/) needs to be configured to send `push` events to the endpoint `/hook` of the updater server. There only the ending part is crucial. That means that any URL ending in `/hook` which is served by the `index.php` of the updater server will trigger this behaviour. -Configure a webhook on Github in the repository of choice with `application/json` as content type, a random secret and the `push` event to be sent. +Configure a webhook on GitHub in the repository of choice with `application/json` as content type, a random secret and the `push` event to be sent. ![](docs/webhook.png) diff --git a/index.php b/index.php index e95ce119..96777f8e 100755 --- a/index.php +++ b/index.php @@ -63,24 +63,28 @@ exit(); } -// Return empty response if no version is supplied -if(!isset($_GET['version']) || !is_string($_GET['version'])) { - exit(); -} - -// Parse the request +// Read config try { - $request = new \UpdateServer\Request($_GET['version'], $_SERVER); -} catch (\UpdateServer\Exceptions\UnsupportedReleaseException $e) { + $config = new \UpdateServer\Config(__DIR__ . '/config/config.php'); +} catch (\RuntimeException $e) { exit(); } -try { - $config = new \UpdateServer\Config(__DIR__ . '/config/config.php'); -} catch (\RuntimeException $e) { +// Check the request parameters +if(isset($_GET['version']) && is_string($_GET['version'])) { + try { + $request = new \UpdateServer\Requests\VersionRequest($_GET['version'], $_SERVER); + } catch (\UpdateServer\Exceptions\UnsupportedReleaseException $e) { + exit(); + } + $response = new \UpdateServer\Responses\VersionResponse($request, $config); +} else if (isset($_GET['channel']) && is_string($_GET['channel'])) { + $request = new \UpdateServer\Requests\ChannelRequest($_GET['channel']); + $response = new \UpdateServer\Responses\ChannelResponse($request, $config); +} else { + // Return empty response if no version or channel is supplied exit(); } -// Return a response -$response = new \UpdateServer\Response($request, $config); +// Return the response echo $response->buildResponse(); diff --git a/src/Requests/ChannelRequest.php b/src/Requests/ChannelRequest.php new file mode 100755 index 00000000..176e6809 --- /dev/null +++ b/src/Requests/ChannelRequest.php @@ -0,0 +1,26 @@ + + */ + +namespace UpdateServer\Requests; + +class ChannelRequest { + /** @var string */ + private $channel; + + /** + * ChannelRequest constructor. + * @param string $channel + */ + public function __construct(string $channel) { + $this->channel = $channel; + } + + /** + * @return string + */ + public function getChannel(): string { + return $this->channel; + } +} diff --git a/src/Request.php b/src/Requests/VersionRequest.php similarity index 97% rename from src/Request.php rename to src/Requests/VersionRequest.php index 1109c643..1a2121b8 100755 --- a/src/Request.php +++ b/src/Requests/VersionRequest.php @@ -3,11 +3,11 @@ * @license MIT */ -namespace UpdateServer; +namespace UpdateServer\Requests; use UpdateServer\Exceptions\UnsupportedReleaseException; -class Request { +class VersionRequest { /** @var int|null */ private $majorVersion = null; /** @var int|null */ @@ -41,8 +41,7 @@ class Request { * @param array $server * @throws UnsupportedReleaseException If the release is not supported by this update script. */ - public function __construct($versionString, - array $server) { + public function __construct(string $versionString, array $server) { $this->readVersion($versionString, $server); } diff --git a/src/Response.php b/src/Response.php deleted file mode 100755 index b2f2f844..00000000 --- a/src/Response.php +++ /dev/null @@ -1,210 +0,0 @@ - - */ - -namespace UpdateServer; - -class Response { - /** @var Config */ - private $config; - /** @var Request */ - private $request; - - /** - * @param Request $request - * @param Config $config - */ - public function __construct(Request $request, - Config $config - ) { - $this->request = $request; - $this->config = $config; - } - - /** - * @return array - */ - private function getFuzzySearches() { - // The search scheme is defined as following: - // 1. Major.Minor.Maintenance.Revision - $searches[] = $this->request->getMajorVersion().'.'.$this->request->getMinorVersion().'.'.$this->request->getMaintenanceVersion().'.'.$this->request->getRevisionVersion(); - // 2. Major.Minor.Maintenance - $searches[] = $this->request->getMajorVersion().'.'.$this->request->getMinorVersion().'.'.$this->request->getMaintenanceVersion(); - // 3. Major.Minor - $searches[] = $this->request->getMajorVersion().'.'.$this->request->getMinorVersion(); - // 4. Major - $searches[] = $this->request->getMajorVersion(); - return $searches; - } - - /** - * @param \XMLWriter $writer - * @param $version - */ - private function addChangelogURLIfApplicable(\XMLWriter $writer, $version) { - if(version_compare($version, '13.99.0', '<')) { - return; - } - - $versionString = implode('.', array_slice(explode('.', $version), 0, 3)); - $preReleasePos = strpos($versionString, ' '); - if($preReleasePos !== false) { - $versionString = substr($versionString, 0, $preReleasePos); - } - - $changesUrl = rtrim($this->config->get('_settings')['changelogServer'], '/') - . '/?version=' . urlencode($versionString); - - $writer->writeElement('changes', $changesUrl); - } - - /** - * Code for the stable editions - * - * @param array $versions - * @param string $completeCurrentVersion - * @param string $phpVersion - * @param int $installationMtime - * @return string - */ - private function getStableResponse(array $versions, - $completeCurrentVersion, - $phpVersion, - $installationMtime) { - $newVersion = ''; - foreach($this->getFuzzySearches() as $search) { - if(isset($versions[$search])) { - /** @var array $newVersions */ - $newVersions = $versions[$search]; - - $counter = 100; - $instanceChance = (int)substr($installationMtime, -2); - if($instanceChance !== 0) { - ksort($newVersions); - } - - foreach($newVersions as $chance => $updateOptions) { - $counter -= $chance; - if($instanceChance <= (100 - $counter)) { - // skip incompatible releases due to PHP version - if(isset($newVersions[$chance]['minPHPVersion']) && version_compare($newVersions[$chance]['minPHPVersion'], $phpVersion, '>')) { - continue; - } - // skip incompatible releases due to lower version number - if(version_compare($newVersions[$chance]['internalVersion'], $completeCurrentVersion, '<=')) { - continue; - } - $newVersion = $newVersions[$chance]; - break 2; - } - } - } - } - - if($newVersion === '') { - return ''; - } - - $downloadUrl = 'https://download.nextcloud.com/server/releases/nextcloud-'.$newVersion['latest'].'.zip'; - if(isset($newVersion['downloadUrl'])) { - $downloadUrl = $newVersion['downloadUrl']; - } - - $writer = new \XMLWriter(); - $writer->openMemory(); - $writer->startDocument('1.0','UTF-8'); - $writer->setIndent(4); - $writer->startElement('nextcloud'); - $writer->writeElement('version', $newVersion['internalVersion']); - $writer->writeElement('versionstring', 'Nextcloud '.$newVersion['latest']); - $writer->writeElement('url', $downloadUrl); - $writer->writeElement('web', $newVersion['web']); - $this->addChangelogURLIfApplicable($writer, $newVersion['latest']); - $writer->writeElement('autoupdater', isset($newVersion['autoupdater']) ? (int)$newVersion['autoupdater'] : 1); - $writer->writeElement('eol', (int) $newVersion['eol']); - if(isset($newVersion['signature'])) { - $writer->writeElement('signature', $newVersion['signature']); - } - $writer->endElement(); - $writer->endDocument(); - return $writer->flush(); - } - - /** - * Code for the daily builds - * - * @param array $versions - * @return string - */ - private function getDailyResponse(array $versions) { - foreach($this->getFuzzySearches() as $search) { - if(isset($versions[$search])) { - if((time() - strtotime($this->request->getBuild())) > 172800) { - $newVersion = $versions[$search]; - $writer = new \XMLWriter(); - $writer->openMemory(); - $writer->startDocument('1.0','UTF-8'); - $writer->setIndent(4); - $writer->startElement('nextcloud'); - $writer->writeElement('version', '100.0.0.0'); - $writer->writeElement('versionstring', 'Nextcloud daily'); - $writer->writeElement('url', $newVersion['downloadUrl']); - $writer->writeElement('web', $newVersion['web']); - $writer->writeElement('autoupdater', isset($newVersion['autoupdater']) ? (int)$newVersion['autoupdater'] : 1); - $writer->writeElement('eol', (int) $newVersion['eol']); - $writer->endElement(); - $writer->endDocument(); - return $writer->flush(); - } - } - } - - return ''; - } - - /** - * @return string - */ - public function buildResponse() { - $completeCurrentVersion = $this->request->getMajorVersion().'.'.$this->request->getMinorVersion().'.'.$this->request->getMaintenanceVersion().'.'.$this->request->getRevisionVersion(); - $phpVersion = implode( - '.', - [ - $this->request->getPHPMajorVersion(), - $this->request->getPHPMinorVersion(), - $this->request->getPHPReleaseVersion(), - ] - ); - - $completeCurrentVersion = rtrim($completeCurrentVersion, '.'); - - switch ($this->request->getChannel()) { - case 'enterprise': - return $this->getStableResponse( - $this->config->getWithAlternative('enterprise', 'stable'), - $completeCurrentVersion, - $phpVersion, - $this->request->getInstallationMtime() - ); - case 'stable': - return $this->getStableResponse( - $this->config->get('stable'), - $completeCurrentVersion, - $phpVersion, - $this->request->getInstallationMtime() - ); - case 'beta': - return $this->getStableResponse( - $this->config->get('beta'), - $completeCurrentVersion, - $phpVersion, - $this->request->getInstallationMtime() - ); - case 'daily': - return $this->getDailyResponse($this->config->get('daily')); - default: - return ''; - } - } -} diff --git a/src/Responses/BaseResponse.php b/src/Responses/BaseResponse.php new file mode 100755 index 00000000..a1a7b7e3 --- /dev/null +++ b/src/Responses/BaseResponse.php @@ -0,0 +1,112 @@ + + */ + +namespace UpdateServer\Responses; + +use UpdateServer\Config; + +abstract class BaseResponse { + /** @var Config */ + private $config; + + /** + * @param Config $config + */ + protected function __construct(Config $config) { + $this->config = $config; + } + + /** + * @param string $channel + */ + protected function getChannelConfig(string $channel): array { + switch ($channel) { + case 'enterprise': + return $this->config->getWithAlternative('enterprise', 'stable'); + case 'stable': + return $this->config->get('stable'); + case 'beta': + return $this->config->get('beta'); + case 'daily': + return $this->config->get('daily'); + default: + return []; + } + } + + /** + * @param \XMLWriter $writer + * @param $version + */ + private function addChangelogURLIfApplicable(\XMLWriter $writer, $version) { + if(version_compare($version, '13.99.0', '<')) { + return; + } + + $versionString = implode('.', array_slice(explode('.', $version), 0, 3)); + $preReleasePos = strpos($versionString, ' '); + if($preReleasePos !== false) { + $versionString = substr($versionString, 0, $preReleasePos); + } + + $changesUrl = rtrim($this->config->get('_settings')['changelogServer'], '/') + . '/?version=' . urlencode($versionString); + + $writer->writeElement('changes', $changesUrl); + } + + /** + * @param array $newVersion + * @return string + */ + protected function buildXMLForVersion(array $newVersion): string { + $downloadUrl = 'https://download.nextcloud.com/server/releases/nextcloud-'.$newVersion['latest'].'.zip'; + if(isset($newVersion['downloadUrl'])) { + $downloadUrl = $newVersion['downloadUrl']; + } + + $writer = new \XMLWriter(); + $writer->openMemory(); + $writer->startDocument('1.0','UTF-8'); + $writer->setIndent(4); + $writer->startElement('nextcloud'); + $writer->writeElement('version', $newVersion['internalVersion']); + $writer->writeElement('versionstring', 'Nextcloud '.$newVersion['latest']); + $writer->writeElement('url', $downloadUrl); + $writer->writeElement('web', $newVersion['web']); + $this->addChangelogURLIfApplicable($writer, $newVersion['latest']); + $writer->writeElement('autoupdater', isset($newVersion['autoupdater']) ? (int)$newVersion['autoupdater'] : 1); + $writer->writeElement('eol', (int) $newVersion['eol']); + if(isset($newVersion['signature'])) { + $writer->writeElement('signature', $newVersion['signature']); + } + $writer->endElement(); + $writer->endDocument(); + return $writer->flush(); + } + + /** + * @param array $newVersion + * @return string + */ + protected function buildXMLForDailyVersion(array $newVersion): string { + $writer = new \XMLWriter(); + $writer->openMemory(); + $writer->startDocument('1.0','UTF-8'); + $writer->setIndent(4); + $writer->startElement('nextcloud'); + $writer->writeElement('version', '100.0.0.0'); + $writer->writeElement('versionstring', 'Nextcloud daily'); + $writer->writeElement('url', $newVersion['downloadUrl']); + $writer->writeElement('web', $newVersion['web']); + $writer->writeElement('autoupdater', isset($newVersion['autoupdater']) ? (int)$newVersion['autoupdater'] : 1); + $writer->writeElement('eol', (int) $newVersion['eol']); + $writer->endElement(); + $writer->endDocument(); + return $writer->flush(); + } + + public abstract function buildResponse(): string; +} diff --git a/src/Responses/ChannelResponse.php b/src/Responses/ChannelResponse.php new file mode 100755 index 00000000..10ead0a9 --- /dev/null +++ b/src/Responses/ChannelResponse.php @@ -0,0 +1,86 @@ + + */ + +namespace UpdateServer\Responses; + +use UpdateServer\Config; +use UpdateServer\Requests\ChannelRequest; + +class ChannelResponse extends BaseResponse { + /** @var ChannelRequest */ + private $request; + + /** + * @param ChannelRequest $request + * @param Config $config + */ + public function __construct(ChannelRequest $request, Config $config) { + parent::__construct($config); + $this->request = $request; + } + + /** + * @param string $channel + * @return array|null + */ + private function getBestVersionConfigForRegularChannel(string $channel): ?array { + $channelConfig = $this->getChannelConfig($channel); + $bestVersionConfig = null; + $highestVersionNumber = null; + foreach($channelConfig as $versionNumber => $releaseConfig) { + if($highestVersionNumber === null || version_compare($highestVersionNumber, $versionNumber, '<')) { + $highestProbability = 0; + foreach($releaseConfig as $probability => $versionConfig) { + if((int)$probability > $highestProbability && (!isset($versionConfig['eol']) || $versionConfig['eol'] !== true)) { + $highestProbability = $probability; + $bestVersionConfig = $versionConfig; + } + } + if($highestProbability !== 0) { + // Only increase the highestVersionNumber if we actually found a new version config. + $highestVersionNumber = $versionNumber; + } + } + } + return $bestVersionConfig; + } + + /** + * @return array|null + */ + private function getBestVersionConfigForDailyChannel(): ?array { + $channelConfig = $this->getChannelConfig('daily'); + $bestVersionConfig = null; + $highestVersionNumber = null; + foreach($channelConfig as $versionNumber => $versionConfig) { + if($highestVersionNumber === null || version_compare($highestVersionNumber, $versionNumber, '<') + && (!isset($versionConfig['eol']) || $versionConfig['eol'] !== true)) { + $bestVersionConfig = $versionConfig; + $highestVersionNumber = $versionNumber; + } + } + return $bestVersionConfig; + } + + /** + * @param string $channel + * @return array|null + */ + private function getBestVersionConfig(string $channel): ?array { + return $channel !== 'daily' ? $this->getBestVersionConfigForRegularChannel($channel) : $this->getBestVersionConfigForDailyChannel(); + } + + public function buildResponse(): string { + $bestVersion = $this->getBestVersionConfig($this->request->getChannel()); + if($bestVersion === null) { + return ''; + } + if ($this->request->getChannel() !== 'daily') { + return $this->buildXMLForVersion($bestVersion); + } else { + return $this->buildXMLForDailyVersion($bestVersion); + } + } +} diff --git a/src/Responses/VersionResponse.php b/src/Responses/VersionResponse.php new file mode 100755 index 00000000..bebbf9b5 --- /dev/null +++ b/src/Responses/VersionResponse.php @@ -0,0 +1,137 @@ + + */ + +namespace UpdateServer\Responses; + +use UpdateServer\Config; +use UpdateServer\Requests\VersionRequest; + +class VersionResponse extends BaseResponse { + /** @var VersionRequest */ + private $request; + + /** + * @param VersionRequest $request + * @param Config $config + */ + public function __construct(VersionRequest $request, Config $config) { + parent::__construct($config); + $this->request = $request; + } + + /** + * @return array + */ + private function getFuzzySearches() { + // The search scheme is defined as following: + // 1. Major.Minor.Maintenance.Revision + $searches[] = $this->request->getMajorVersion().'.'.$this->request->getMinorVersion().'.'.$this->request->getMaintenanceVersion().'.'.$this->request->getRevisionVersion(); + // 2. Major.Minor.Maintenance + $searches[] = $this->request->getMajorVersion().'.'.$this->request->getMinorVersion().'.'.$this->request->getMaintenanceVersion(); + // 3. Major.Minor + $searches[] = $this->request->getMajorVersion().'.'.$this->request->getMinorVersion(); + // 4. Major + $searches[] = $this->request->getMajorVersion(); + return $searches; + } + + /** + * Code for the stable editions + * + * @param array $versions + * @param string $completeCurrentVersion + * @param string $phpVersion + * @param int $installationMtime + * @return string + */ + private function getStableResponse(array $versions, + $completeCurrentVersion, + $phpVersion, + $installationMtime) { + $newVersion = ''; + foreach($this->getFuzzySearches() as $search) { + if(isset($versions[$search])) { + /** @var array $newVersions */ + $newVersions = $versions[$search]; + + $counter = 100; + $instanceChance = (int)substr($installationMtime, -2); + if($instanceChance !== 0) { + ksort($newVersions); + } + + foreach($newVersions as $chance => $updateOptions) { + $counter -= $chance; + if($instanceChance <= (100 - $counter)) { + // skip incompatible releases due to PHP version + if(isset($newVersions[$chance]['minPHPVersion']) && version_compare($newVersions[$chance]['minPHPVersion'], $phpVersion, '>')) { + continue; + } + // skip incompatible releases due to lower version number + if(version_compare($newVersions[$chance]['internalVersion'], $completeCurrentVersion, '<=')) { + continue; + } + $newVersion = $newVersions[$chance]; + break 2; + } + } + } + } + + if($newVersion === '') { + return ''; + } + + return $this->buildXMLForVersion($newVersion); + } + + /** + * Code for the daily builds + * + * @param array $versions + * @return string + */ + private function getDailyResponse(array $versions) { + if ((time() - strtotime($this->request->getBuild())) <= 172800) { + return ''; + } + + foreach($this->getFuzzySearches() as $search) { + if(isset($versions[$search])) { + return $this->buildXMLForDailyVersion($versions[$search]); + } + } + + return ''; + } + + /** + * @return string + */ + public function buildResponse(): string { + $completeCurrentVersion = $this->request->getMajorVersion().'.'.$this->request->getMinorVersion().'.'.$this->request->getMaintenanceVersion().'.'.$this->request->getRevisionVersion(); + $completeCurrentVersion = rtrim($completeCurrentVersion, '.'); + $phpVersion = implode( + '.', + [ + $this->request->getPHPMajorVersion(), + $this->request->getPHPMinorVersion(), + $this->request->getPHPReleaseVersion(), + ] + ); + $channelConfig = $this->getChannelConfig($this->request->getChannel()); + + if ($this->request->getChannel() !== 'daily') { + return $this->getStableResponse( + $channelConfig, + $completeCurrentVersion, + $phpVersion, + $this->request->getInstallationMtime() + ); + } else { + return $this->getDailyResponse($channelConfig); + } + } +} diff --git a/tests/integration/features/beta.feature b/tests/integration/features/beta.feature index 0b299982..eaa94a7c 100644 --- a/tests/integration/features/beta.feature +++ b/tests/integration/features/beta.feature @@ -493,4 +493,23 @@ Feature: Testing the update scenario of beta releases lqcZSGdY+smeb7ciFf0hykivKZATXHLczvYO3FUu/HVqlgRUxi+Q+wNjFmfFL3Vr RGlZGz7bpa48/2sprNJ2CYGVLMjxtktUrUIH6NB0diVMK8kAd7+OIqtjCmUG307S qRc0DHdtzXMmzq4t4PfZhg== - """ \ No newline at end of file + """ + + Scenario: Requesting the latest release of the beta channel + Given The request is channel based + And The requested channel is "beta" + When The request is sent + Then The response is non-empty + And Update to version "21.0.1.1" is available + And URL to download is "https://download.nextcloud.com/server/releases/nextcloud-21.0.1.zip" + And URL to documentation is "https://docs.nextcloud.com/server/21/admin_manual/maintenance/upgrade.html" + And EOL is set to "0" + And The signature is + """ + YJQMb8iq13NhbvfEddaSjNeqlMe/dB0nJ27EwmiAuqSMMRpvVg4BjijfNjoG8lY9 + rs0k+YN4EkGSr1lhZvdWZ9LpmP1wug/l1wj8lWzjxp/588yp5jHs24XBsi14GrDY + tgcx/V6E2ELFfgXJY6R4y2bIaaDPPjDfi+a2nq5ut0RTalGaUh6jr1dzaKQ0rJXm + lqcZSGdY+smeb7ciFf0hykivKZATXHLczvYO3FUu/HVqlgRUxi+Q+wNjFmfFL3Vr + RGlZGz7bpa48/2sprNJ2CYGVLMjxtktUrUIH6NB0diVMK8kAd7+OIqtjCmUG307S + qRc0DHdtzXMmzq4t4PfZhg== + """ diff --git a/tests/integration/features/bootstrap/FeatureContext.php b/tests/integration/features/bootstrap/FeatureContext.php index 12083edd..5b390753 100755 --- a/tests/integration/features/bootstrap/FeatureContext.php +++ b/tests/integration/features/bootstrap/FeatureContext.php @@ -7,6 +7,8 @@ use Behat\Behat\Context\SnippetAcceptingContext; class FeatureContext implements Context, SnippetAcceptingContext { + /** @var string */ + private $requestKind = 'version'; /** @var string */ private $releaseChannel = ''; /** @var string */ @@ -36,6 +38,27 @@ class FeatureContext implements Context, SnippetAcceptingContext { /** @var array */ private $resultArray = []; + /** + * @Given The request is version based + */ + public function theRequestIsVersionBased() { + $this->requestKind = 'version'; + } + + /** + * @Given The request is channel based + */ + public function theRequestIsChannelBased() { + $this->requestKind = 'channel'; + } + + /** + * @Given The requested channel is :arg1 + */ + public function theRequestedChannelIs($arg1) { + $this->releaseChannel = $arg1; + } + /** * @Given There is a release with channel :arg1 */ @@ -114,9 +137,18 @@ private function buildVersionToSend() { * @When The request is sent */ public function theRequestIsSent() { + $url = 'http://localhost:8888/?'; + switch ($this->requestKind) { + case 'version': + $url .= 'version='.$this->buildVersionToSend(); + break; + case 'channel': + $url .= 'channel='.$this->releaseChannel; + break; + } $ch = curl_init(); $optArray = array( - CURLOPT_URL => 'http://localhost:8888/?version='.$this->buildVersionToSend(), + CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true ); curl_setopt_array($ch, $optArray); diff --git a/tests/integration/features/daily.feature b/tests/integration/features/daily.feature index 76bf36a0..75226baa 100644 --- a/tests/integration/features/daily.feature +++ b/tests/integration/features/daily.feature @@ -18,3 +18,14 @@ Feature: Testing the update scenario of daily releases And the received build is "2099-10-19T18:44:30+00:00" When The request is sent Then The response is empty + + Scenario: Requesting the latest release of the daily channel + Given The request is channel based + And The requested channel is "daily" + When The request is sent + Then The response is non-empty + And Update to version "100.0.0.0" is available + And URL to download is "https://download.nextcloud.com/server/daily/latest-master.zip" + And URL to documentation is "https://docs.nextcloud.com/server/latest/admin_manual/maintenance/upgrade.html" + And EOL is set to "0" + And No signature is set diff --git a/tests/integration/features/stable.feature b/tests/integration/features/stable.feature index ba86a4e5..e086ee25 100644 --- a/tests/integration/features/stable.feature +++ b/tests/integration/features/stable.feature @@ -762,4 +762,21 @@ Feature: Testing the update scenario of stable releases qRc0DHdtzXMmzq4t4PfZhg== """ - + Scenario: Requesting the latest release of the stable channel + Given The request is channel based + And The requested channel is "stable" + When The request is sent + Then The response is non-empty + And Update to version "21.0.1.1" is available + And URL to download is "https://download.nextcloud.com/server/releases/nextcloud-21.0.1.zip" + And URL to documentation is "https://docs.nextcloud.com/server/21/admin_manual/maintenance/upgrade.html" + And EOL is set to "0" + And The signature is + """ + YJQMb8iq13NhbvfEddaSjNeqlMe/dB0nJ27EwmiAuqSMMRpvVg4BjijfNjoG8lY9 + rs0k+YN4EkGSr1lhZvdWZ9LpmP1wug/l1wj8lWzjxp/588yp5jHs24XBsi14GrDY + tgcx/V6E2ELFfgXJY6R4y2bIaaDPPjDfi+a2nq5ut0RTalGaUh6jr1dzaKQ0rJXm + lqcZSGdY+smeb7ciFf0hykivKZATXHLczvYO3FUu/HVqlgRUxi+Q+wNjFmfFL3Vr + RGlZGz7bpa48/2sprNJ2CYGVLMjxtktUrUIH6NB0diVMK8kAd7+OIqtjCmUG307S + qRc0DHdtzXMmzq4t4PfZhg== + """ diff --git a/tests/unit/ChannelRequestTest.php b/tests/unit/ChannelRequestTest.php new file mode 100755 index 00000000..bf135698 --- /dev/null +++ b/tests/unit/ChannelRequestTest.php @@ -0,0 +1,16 @@ + + */ + +namespace Tests; + +use UpdateServer\Requests\ChannelRequest; + +class ChannelRequestTest extends \PHPUnit_Framework_TestCase { + public function testRequest() + { + $request = new ChannelRequest('stable'); + $this->assertSame('stable', $request->getChannel()); + } +} diff --git a/tests/unit/ChannelResponseTest.php b/tests/unit/ChannelResponseTest.php new file mode 100755 index 00000000..956f1406 --- /dev/null +++ b/tests/unit/ChannelResponseTest.php @@ -0,0 +1,197 @@ + + */ + +namespace Tests; + +use UpdateServer\Config; +use UpdateServer\Requests\ChannelRequest; +use UpdateServer\Responses\ChannelResponse; + +class ChannelResponseTest extends \PHPUnit_Framework_TestCase +{ + /** @var ChannelRequest */ + private $request; + /** @var Config|\PHPUnit_Framework_MockObject_MockObject */ + private $config; + /** @var ChannelResponse */ + private $response; + + public function setUp() + { + date_default_timezone_set('Europe/Berlin'); + + $this->request = $this->getMockBuilder(ChannelRequest::class) + ->disableOriginalConstructor()->getMock(); + $this->config = $this->getMockBuilder(Config::class) + ->disableOriginalConstructor()->getMock(); + $this->response = new ChannelResponse($this->request, $this->config); + } + + public function updateDataProvider() { + return [ + [ + 'beta', + ' + + 9.0.0 + Nextcloud 9.0.0 + https://download.nextcloud.com/server/releases/nextcloud-9.0.0.zip + https://doc.owncloud.org/server/9.0/admin_manual/maintenance/upgrade.html + 0 + 0 + +', + ], + [ + 'stable', + ' + + 8.2.2 + Nextcloud 8.2.2 + https://download.nextcloud.com/server/releases/nextcloud-8.2.2.zip + https://doc.owncloud.org/server/8.2/admin_manual/maintenance/upgrade.html + 0 + 1 + +', + ], + [ + '', + '', + ], + ]; + } + + /** + * @dataProvider updateDataProvider + * @param string $channel + * @param string $expected + */ + public function testBuildChannelResponse($channel, $expected) { + $betaConfig = [ + '9.0' => [ + '100' => [ + 'latest' => '9.0.0', + 'internalVersion' => '9.0.0', + 'web' => 'https://doc.owncloud.org/server/9.0/admin_manual/maintenance/upgrade.html', + 'autoupdater' => false, + 'minPHPVersion' => '5.6', + 'eol' => false, + ] + ], + '8.2' => [ + '95' => [ + 'latest' => '8.2.2', + 'internalVersion' => '8.2.2', + 'web' => 'https://doc.owncloud.org/server/8.2/admin_manual/maintenance/upgrade.html', + 'autoupdater' => false, + 'minPHPVersion' => '5.4', + 'eol' => true, + ], + '5' => [ + 'latest' => '9.0.0', + 'internalVersion' => '9.0.0', + 'web' => 'https://doc.owncloud.org/server/9.0/admin_manual/maintenance/upgrade.html', + 'autoupdater' => false, + 'minPHPVersion' => '5.6', + 'eol' => false, + ] + ], + ]; + $stableConfig = [ + '8.2' => [ + '95' => [ + 'latest' => '8.2.2', + 'internalVersion' => '8.2.2', + 'web' => 'https://doc.owncloud.org/server/8.2/admin_manual/maintenance/upgrade.html', + 'autoupdater' => false, + 'minPHPVersion' => '5.4', + 'eol' => true, + ], + '5' => [ + 'latest' => '9.0.0', + 'internalVersion' => '9.0.0', + 'web' => 'https://doc.owncloud.org/server/9.0/admin_manual/maintenance/upgrade.html', + 'autoupdater' => false, + 'minPHPVersion' => '5.6', + 'eol' => false, + ] + ], + ]; + + $this->request + ->expects($this->any()) + ->method('getChannel') + ->willReturn($channel); + $this->config + ->expects($this->any()) + ->method('get') + ->with('stable') + ->willReturn($stableConfig); + $this->config + ->expects($this->any()) + ->method('get') + ->with('beta') + ->willReturn($betaConfig); + $this->assertSame($expected, $this->response->buildResponse()); + } + + public function dailyVersionProvider() { + return [ + [ + ' + + 100.0.0.0 + Nextcloud daily + https://download.nextcloud.com/server/daily/latest-master.zip + https://docs.nextcloud.com/server/latest/admin_manual/maintenance/upgrade.html + 1 + 0 + +', + ] + ]; + } + + /** + * @dataProvider dailyVersionProvider + */ + public function testBuildResponseForDailyChannel($expected) { + $this->request + ->expects($this->any()) + ->method('getChannel') + ->willReturn('daily'); + $this->config + ->expects($this->once()) + ->method('get') + ->with('daily') + ->willReturn( + [ + '21.1' => [ + 'downloadUrl' => 'https://download.nextcloud.com/server/daily/latest-master-eol.zip', + 'web' => 'https://docs.nextcloud.com/server/eol/admin_manual/maintenance/upgrade.html', + 'eol' => true, + ], + '21' => [ + 'downloadUrl' => 'https://download.nextcloud.com/server/daily/latest-master.zip', + 'web' => 'https://docs.nextcloud.com/server/latest/admin_manual/maintenance/upgrade.html', + 'eol' => false, + ], + '20' => [ + 'downloadUrl' => 'https://download.nextcloud.com/server/daily/latest-stable20.zip', + 'web' => 'https://docs.nextcloud.com/server/20/admin_manual/maintenance/upgrade.html', + 'eol' => false, + ], + '19' => [ + 'downloadUrl' => 'https://download.nextcloud.com/server/daily/latest-stable19.zip', + 'web' => 'https://docs.nextcloud.com/server/19/admin_manual/maintenance/upgrade.html', + 'eol' => false, + ], + ] + ); + + $this->assertSame($expected, $this->response->buildResponse()); + } +} diff --git a/tests/unit/RequestTest.php b/tests/unit/VersionRequestTest.php similarity index 74% rename from tests/unit/RequestTest.php rename to tests/unit/VersionRequestTest.php index d9ebb86e..19ea4e73 100755 --- a/tests/unit/RequestTest.php +++ b/tests/unit/VersionRequestTest.php @@ -5,11 +5,11 @@ namespace Tests; -use UpdateServer\Request; +use UpdateServer\Requests\VersionRequest; -class RequestTest extends \PHPUnit_Framework_TestCase { +class VersionRequestTest extends \PHPUnit_Framework_TestCase { public function testRequest() { - $request = new Request('8x2x0x12x1448709225.0768x1448709281xtestingxx2015-10-19T18:44:30+00:00%208ee2009de36e01a9866404f07722892f84c16e3e', []); + $request = new VersionRequest('8x2x0x12x1448709225.0768x1448709281xtestingxx2015-10-19T18:44:30+00:00%208ee2009de36e01a9866404f07722892f84c16e3e', []); $this->assertSame(8, $request->getMajorVersion()); $this->assertSame(2, $request->getMinorVersion()); $this->assertSame(0, $request->getMaintenanceVersion()); @@ -23,7 +23,7 @@ public function testRequest() { } public function testRequestPHP() { - $request = new Request('8x2x0x12x1448709225.0768x1448709281xtestingxx2015-10-19T18:44:30+00:00%208ee2009de36e01a9866404f07722892f84c16e3ex5x6x36', []); + $request = new VersionRequest('8x2x0x12x1448709225.0768x1448709281xtestingxx2015-10-19T18:44:30+00:00%208ee2009de36e01a9866404f07722892f84c16e3ex5x6x36', []); $this->assertSame(8, $request->getMajorVersion()); $this->assertSame(2, $request->getMinorVersion()); $this->assertSame(0, $request->getMaintenanceVersion()); @@ -43,6 +43,6 @@ public function testRequestPHP() { * @expectedException \UpdateServer\Exceptions\UnsupportedReleaseException */ public function testRequestInvalidEntry() { - new Request('x8x2x0x12x1448709225.0768x1448709281xtestingxx2015-10-19T18:44:30+00:00%208ee2009de36e01a9866404f07722892f84c16e3e', []); + new VersionRequest('x8x2x0x12x1448709225.0768x1448709281xtestingxx2015-10-19T18:44:30+00:00%208ee2009de36e01a9866404f07722892f84c16e3e', []); } } diff --git a/tests/unit/ResponseTest.php b/tests/unit/VersionResponseTest.php similarity index 98% rename from tests/unit/ResponseTest.php rename to tests/unit/VersionResponseTest.php index 16b209cf..db0263d9 100755 --- a/tests/unit/ResponseTest.php +++ b/tests/unit/VersionResponseTest.php @@ -6,25 +6,25 @@ namespace Tests; use UpdateServer\Config; -use UpdateServer\Request; -use UpdateServer\Response; +use UpdateServer\Requests\VersionRequest; +use UpdateServer\Responses\VersionResponse; -class ResponseTest extends \PHPUnit_Framework_TestCase { - /** @var Request */ +class VersionResponseTest extends \PHPUnit_Framework_TestCase { + /** @var VersionRequest */ private $request; /** @var Config|\PHPUnit_Framework_MockObject_MockObject */ private $config; - /** @var Response */ + /** @var VersionResponse */ private $response; public function setUp() { date_default_timezone_set('Europe/Berlin'); - $this->request = $this->getMockBuilder(Request::class) + $this->request = $this->getMockBuilder(VersionRequest::class) ->disableOriginalConstructor()->getMock(); $this->config = $this->getMockBuilder(Config::class) ->disableOriginalConstructor()->getMock(); - $this->response = new Response($this->request, $this->config); + $this->response = new VersionResponse($this->request, $this->config); } public function dailyVersionProvider() { @@ -958,11 +958,11 @@ public function testBuildResponseForChannel($channel, * @dataProvider responseProviderWithDisabledUpdates */ public function testBuildResponseWithDisabledUpdaterChannel($channel, - $majorVersion, - $minorVersion, - $maintenanceVersion, - $revisionVersion, - $expected) { + $majorVersion, + $minorVersion, + $maintenanceVersion, + $revisionVersion, + $expected) { $config = [ '8.2' => [ '100' => [