ChangesCheckTest.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * @copyright Copyright (c) 2018 Arthur Schiwon <blizzz@arthur-schiwon.de>
  5. *
  6. * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
  7. *
  8. * @license GNU AGPL version 3 or any later version
  9. *
  10. * This program is free software: you can redistribute it and/or modify
  11. * it under the terms of the GNU Affero General Public License as
  12. * published by the Free Software Foundation, either version 3 of the
  13. * License, or (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU Affero General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU Affero General Public License
  21. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. *
  23. */
  24. namespace Test\Updater;
  25. use OC\Updater\ChangesCheck;
  26. use OC\Updater\ChangesMapper;
  27. use OC\Updater\ChangesResult;
  28. use OCP\AppFramework\Db\DoesNotExistException;
  29. use OCP\Http\Client\IClient;
  30. use OCP\Http\Client\IClientService;
  31. use OCP\Http\Client\IResponse;
  32. use OCP\ILogger;
  33. use Test\TestCase;
  34. class ChangesCheckTest extends TestCase {
  35. /** @var IClientService|\PHPUnit_Framework_MockObject_MockObject */
  36. protected $clientService;
  37. /** @var ChangesCheck */
  38. protected $checker;
  39. /** @var ChangesMapper|\PHPUnit_Framework_MockObject_MockObject */
  40. protected $mapper;
  41. /** @var ILogger|\PHPUnit_Framework_MockObject_MockObject */
  42. protected $logger;
  43. public function setUp() {
  44. parent::setUp();
  45. $this->clientService = $this->createMock(IClientService::class);
  46. $this->mapper = $this->createMock(ChangesMapper::class);
  47. $this->logger = $this->createMock(ILogger::class);
  48. $this->checker = new ChangesCheck($this->clientService, $this->mapper, $this->logger);
  49. }
  50. public function statusCodeProvider():array {
  51. return [
  52. [200, ChangesCheck::RESPONSE_HAS_CONTENT],
  53. [304, ChangesCheck::RESPONSE_USE_CACHE],
  54. [404, ChangesCheck::RESPONSE_NO_CONTENT],
  55. [418, ChangesCheck::RESPONSE_NO_CONTENT],
  56. ];
  57. }
  58. /**
  59. * @dataProvider statusCodeProvider
  60. */
  61. public function testEvaluateResponse(int $statusCode, int $expected) {
  62. $response = $this->createMock(IResponse::class);
  63. $response->expects($this->atLeastOnce())
  64. ->method('getStatusCode')
  65. ->willReturn($statusCode);
  66. if(!in_array($statusCode, [200, 304, 404])) {
  67. $this->logger->expects($this->once())
  68. ->method('debug');
  69. }
  70. $evaluation = $this->invokePrivate($this->checker, 'evaluateResponse', [$response]);
  71. $this->assertSame($expected, $evaluation);
  72. }
  73. public function testCacheResultInsert() {
  74. $version = '13.0.4';
  75. $entry = $this->createMock(ChangesResult::class);
  76. $entry->expects($this->exactly(2))
  77. ->method('__call')
  78. ->withConsecutive(['getVersion'], ['setVersion', [$version]])
  79. ->willReturnOnConsecutiveCalls('', null);
  80. $this->mapper->expects($this->once())
  81. ->method('insert');
  82. $this->mapper->expects($this->never())
  83. ->method('update');
  84. $this->invokePrivate($this->checker, 'cacheResult', [$entry, $version]);
  85. }
  86. public function testCacheResultUpdate() {
  87. $version = '13.0.4';
  88. $entry = $this->createMock(ChangesResult::class);
  89. $entry->expects($this->once())
  90. ->method('__call')
  91. ->willReturn($version);
  92. $this->mapper->expects($this->never())
  93. ->method('insert');
  94. $this->mapper->expects($this->once())
  95. ->method('update');
  96. $this->invokePrivate($this->checker, 'cacheResult', [$entry, $version]);
  97. }
  98. public function changesXMLProvider(): array {
  99. return [
  100. [ # 0 - full example
  101. '<?xml version="1.0" encoding="utf-8" ?>
  102. <release xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
  103. xsi:noNamespaceSchemaLocation="https://updates.nextcloud.com/changelog_server/schema.xsd"
  104. version="13.0.0">
  105. <changelog href="https://nextcloud.com/changelog/#13-0-0"/>
  106. <whatsNew lang="en">
  107. <regular>
  108. <item>Refined user interface</item>
  109. <item>End-to-end Encryption</item>
  110. <item>Video and Text Chat</item>
  111. </regular>
  112. <admin>
  113. <item>Changes to the Nginx configuration</item>
  114. <item>Theming: CSS files were consolidated</item>
  115. </admin>
  116. </whatsNew>
  117. <whatsNew lang="de">
  118. <regular>
  119. <item>Überarbeitete Benutzerschnittstelle</item>
  120. <item>Ende-zu-Ende Verschlüsselung</item>
  121. <item>Video- und Text-Chat</item>
  122. </regular>
  123. <admin>
  124. <item>Änderungen an der Nginx Konfiguration</item>
  125. <item>Theming: CSS Dateien wurden konsolidiert</item>
  126. </admin>
  127. </whatsNew>
  128. </release>',
  129. [
  130. 'changelogURL' => 'https://nextcloud.com/changelog/#13-0-0',
  131. 'whatsNew' => [
  132. 'en' => [
  133. 'regular' => [
  134. 'Refined user interface',
  135. 'End-to-end Encryption',
  136. 'Video and Text Chat'
  137. ],
  138. 'admin' => [
  139. 'Changes to the Nginx configuration',
  140. 'Theming: CSS files were consolidated'
  141. ],
  142. ],
  143. 'de' => [
  144. 'regular' => [
  145. 'Überarbeitete Benutzerschnittstelle',
  146. 'Ende-zu-Ende Verschlüsselung',
  147. 'Video- und Text-Chat'
  148. ],
  149. 'admin' => [
  150. 'Änderungen an der Nginx Konfiguration',
  151. 'Theming: CSS Dateien wurden konsolidiert'
  152. ],
  153. ],
  154. ],
  155. ]
  156. ],
  157. [ # 1- admin part not translated
  158. '<?xml version="1.0" encoding="utf-8" ?>
  159. <release xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
  160. xsi:noNamespaceSchemaLocation="https://updates.nextcloud.com/changelog_server/schema.xsd"
  161. version="13.0.0">
  162. <changelog href="https://nextcloud.com/changelog/#13-0-0"/>
  163. <whatsNew lang="en">
  164. <regular>
  165. <item>Refined user interface</item>
  166. <item>End-to-end Encryption</item>
  167. <item>Video and Text Chat</item>
  168. </regular>
  169. <admin>
  170. <item>Changes to the Nginx configuration</item>
  171. <item>Theming: CSS files were consolidated</item>
  172. </admin>
  173. </whatsNew>
  174. <whatsNew lang="de">
  175. <regular>
  176. <item>Überarbeitete Benutzerschnittstelle</item>
  177. <item>Ende-zu-Ende Verschlüsselung</item>
  178. <item>Video- und Text-Chat</item>
  179. </regular>
  180. </whatsNew>
  181. </release>',
  182. [
  183. 'changelogURL' => 'https://nextcloud.com/changelog/#13-0-0',
  184. 'whatsNew' => [
  185. 'en' => [
  186. 'regular' => [
  187. 'Refined user interface',
  188. 'End-to-end Encryption',
  189. 'Video and Text Chat'
  190. ],
  191. 'admin' => [
  192. 'Changes to the Nginx configuration',
  193. 'Theming: CSS files were consolidated'
  194. ],
  195. ],
  196. 'de' => [
  197. 'regular' => [
  198. 'Überarbeitete Benutzerschnittstelle',
  199. 'Ende-zu-Ende Verschlüsselung',
  200. 'Video- und Text-Chat'
  201. ],
  202. 'admin' => [
  203. ],
  204. ],
  205. ],
  206. ]
  207. ],
  208. [ # 2 - minimal set
  209. '<?xml version="1.0" encoding="utf-8" ?>
  210. <release xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
  211. xsi:noNamespaceSchemaLocation="https://updates.nextcloud.com/changelog_server/schema.xsd"
  212. version="13.0.0">
  213. <changelog href="https://nextcloud.com/changelog/#13-0-0"/>
  214. <whatsNew lang="en">
  215. <regular>
  216. <item>Refined user interface</item>
  217. <item>End-to-end Encryption</item>
  218. <item>Video and Text Chat</item>
  219. </regular>
  220. </whatsNew>
  221. </release>',
  222. [
  223. 'changelogURL' => 'https://nextcloud.com/changelog/#13-0-0',
  224. 'whatsNew' => [
  225. 'en' => [
  226. 'regular' => [
  227. 'Refined user interface',
  228. 'End-to-end Encryption',
  229. 'Video and Text Chat'
  230. ],
  231. 'admin' => [],
  232. ],
  233. ],
  234. ]
  235. ],
  236. [ # 3 - minimal set (procrastinator edition)
  237. '<?xml version="1.0" encoding="utf-8" ?>
  238. <release xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
  239. xsi:noNamespaceSchemaLocation="https://updates.nextcloud.com/changelog_server/schema.xsd"
  240. version="13.0.0">
  241. <changelog href="https://nextcloud.com/changelog/#13-0-0"/>
  242. <whatsNew lang="en">
  243. <regular>
  244. <item>Write this tomorrow</item>
  245. </regular>
  246. </whatsNew>
  247. </release>',
  248. [
  249. 'changelogURL' => 'https://nextcloud.com/changelog/#13-0-0',
  250. 'whatsNew' => [
  251. 'en' => [
  252. 'regular' => [
  253. 'Write this tomorrow',
  254. ],
  255. 'admin' => [],
  256. ],
  257. ],
  258. ]
  259. ],
  260. ];
  261. }
  262. /**
  263. * @dataProvider changesXMLProvider
  264. */
  265. public function testExtractData(string $body, array $expected) {
  266. $actual = $this->invokePrivate($this->checker, 'extractData', [$body]);
  267. $this->assertSame($expected, $actual);
  268. }
  269. public function etagProvider() {
  270. return [
  271. [''],
  272. ['a27aab83d8205d73978435076e53d143']
  273. ];
  274. }
  275. /**
  276. * @dataProvider etagProvider
  277. */
  278. public function testQueryChangesServer(string $etag) {
  279. $uri = 'https://changes.nextcloud.server/?13.0.5';
  280. $entry = $this->createMock(ChangesResult::class);
  281. $entry->expects($this->any())
  282. ->method('__call')
  283. ->willReturn($etag);
  284. $expectedHeaders = $etag === '' ? [] : ['If-None-Match' => [$etag]];
  285. $client = $this->createMock(IClient::class);
  286. $client->expects($this->once())
  287. ->method('get')
  288. ->with($uri, ['headers' => $expectedHeaders])
  289. ->willReturn($this->createMock(IResponse::class));
  290. $this->clientService->expects($this->once())
  291. ->method('newClient')
  292. ->willReturn($client);
  293. $response = $this->invokePrivate($this->checker, 'queryChangesServer', [$uri, $entry]);
  294. $this->assertInstanceOf(IResponse::class, $response);
  295. }
  296. public function versionProvider(): array {
  297. return [
  298. ['13.0.7', '13.0.7'],
  299. ['13.0.7.3', '13.0.7'],
  300. ['13.0.7.3.42', '13.0.7'],
  301. ['13.0', '13.0.0'],
  302. ['13', '13.0.0'],
  303. ['', '0.0.0'],
  304. ];
  305. }
  306. /**
  307. * @dataProvider versionProvider
  308. */
  309. public function testNormalizeVersion(string $input, string $expected) {
  310. $normalized = $this->checker->normalizeVersion($input);
  311. $this->assertSame($expected, $normalized);
  312. }
  313. public function changeDataProvider():array {
  314. $testDataFound = $testDataNotFound = $this->versionProvider();
  315. array_walk($testDataFound, function(&$params) { $params[] = true; });
  316. array_walk($testDataNotFound, function(&$params) { $params[] = false; });
  317. return array_merge($testDataFound, $testDataNotFound);
  318. }
  319. /**
  320. * @dataProvider changeDataProvider
  321. *
  322. */
  323. public function testGetChangesForVersion(string $inputVersion, string $normalizedVersion, bool $isFound) {
  324. $mocker = $this->mapper->expects($this->once())
  325. ->method('getChanges')
  326. ->with($normalizedVersion);
  327. if(!$isFound) {
  328. $this->expectException(DoesNotExistException::class);
  329. $mocker->willThrowException(new DoesNotExistException('Changes info is not present'));
  330. } else {
  331. $entry = $this->createMock(ChangesResult::class);
  332. $entry->expects($this->once())
  333. ->method('__call')
  334. ->with('getData')
  335. ->willReturn('{"changelogURL":"https:\/\/nextcloud.com\/changelog\/#13-0-0","whatsNew":{"en":{"regular":["Refined user interface","End-to-end Encryption","Video and Text Chat"],"admin":["Changes to the Nginx configuration","Theming: CSS files were consolidated"]},"de":{"regular":["\u00dcberarbeitete Benutzerschnittstelle","Ende-zu-Ende Verschl\u00fcsselung","Video- und Text-Chat"],"admin":["\u00c4nderungen an der Nginx Konfiguration","Theming: CSS Dateien wurden konsolidiert"]}}}');
  336. $mocker->willReturn($entry);
  337. }
  338. /** @noinspection PhpUnhandledExceptionInspection */
  339. $data = $this->checker->getChangesForVersion($inputVersion);
  340. $this->assertTrue(isset($data['whatsNew']['en']['regular']));
  341. $this->assertTrue(isset($data['changelogURL']));
  342. }
  343. }