FetcherBase.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
  4. *
  5. * @license GNU AGPL version 3 or any later version
  6. *
  7. * This program is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU Affero General Public License as
  9. * published by the Free Software Foundation, either version 3 of the
  10. * License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU Affero General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Affero General Public License
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. *
  20. */
  21. namespace Test\App\AppStore\Fetcher;
  22. use OC\App\AppStore\Fetcher\Fetcher;
  23. use OC\Files\AppData\AppData;
  24. use OC\Files\AppData\Factory;
  25. use OCP\AppFramework\Utility\ITimeFactory;
  26. use OCP\Files\IAppData;
  27. use OCP\Files\NotFoundException;
  28. use OCP\Files\SimpleFS\ISimpleFile;
  29. use OCP\Files\SimpleFS\ISimpleFolder;
  30. use OCP\Http\Client\IClient;
  31. use OCP\Http\Client\IClientService;
  32. use OCP\Http\Client\IResponse;
  33. use OCP\IConfig;
  34. use OCP\Support\Subscription\IRegistry;
  35. use Psr\Log\LoggerInterface;
  36. use Test\TestCase;
  37. abstract class FetcherBase extends TestCase {
  38. /** @var Factory|\PHPUnit\Framework\MockObject\MockObject */
  39. protected $appDataFactory;
  40. /** @var IAppData|\PHPUnit\Framework\MockObject\MockObject */
  41. protected $appData;
  42. /** @var IClientService|\PHPUnit\Framework\MockObject\MockObject */
  43. protected $clientService;
  44. /** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject */
  45. protected $timeFactory;
  46. /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
  47. protected $config;
  48. /** @var LoggerInterface|\PHPUnit\Framework\MockObject\MockObject */
  49. protected $logger;
  50. /** @var IRegistry|\PHPUnit\Framework\MockObject\MockObject */
  51. protected $registry;
  52. /** @var Fetcher */
  53. protected $fetcher;
  54. /** @var string */
  55. protected $fileName;
  56. /** @var string */
  57. protected $endpoint;
  58. protected function setUp(): void {
  59. parent::setUp();
  60. $this->appDataFactory = $this->createMock(Factory::class);
  61. $this->appData = $this->createMock(AppData::class);
  62. $this->appDataFactory->expects($this->once())
  63. ->method('get')
  64. ->with('appstore')
  65. ->willReturn($this->appData);
  66. $this->clientService = $this->createMock(IClientService::class);
  67. $this->timeFactory = $this->createMock(ITimeFactory::class);
  68. $this->config = $this->createMock(IConfig::class);
  69. $this->logger = $this->createMock(LoggerInterface::class);
  70. $this->registry = $this->createMock(IRegistry::class);
  71. }
  72. public function testGetWithAlreadyExistingFileAndUpToDateTimestampAndVersion() {
  73. $this->config
  74. ->expects($this->exactly(1))
  75. ->method('getSystemValueString')
  76. ->with($this->equalTo('version'), $this->anything())
  77. ->willReturn('11.0.0.2');
  78. $this->config->method('getSystemValueBool')
  79. ->willReturnArgument(1);
  80. $folder = $this->createMock(ISimpleFolder::class);
  81. $file = $this->createMock(ISimpleFile::class);
  82. $this->appData
  83. ->expects($this->once())
  84. ->method('getFolder')
  85. ->with('/')
  86. ->willReturn($folder);
  87. $folder
  88. ->expects($this->once())
  89. ->method('getFile')
  90. ->with($this->fileName)
  91. ->willReturn($file);
  92. $file
  93. ->expects($this->once())
  94. ->method('getContent')
  95. ->willReturn('{"timestamp":1200,"data":[{"id":"MyApp"}],"ncversion":"11.0.0.2"}');
  96. $this->timeFactory
  97. ->expects($this->once())
  98. ->method('getTime')
  99. ->willReturn(1499);
  100. $expected = [
  101. [
  102. 'id' => 'MyApp',
  103. ],
  104. ];
  105. $this->assertSame($expected, $this->fetcher->get());
  106. }
  107. public function testGetWithNotExistingFileAndUpToDateTimestampAndVersion() {
  108. $this->config
  109. ->method('getSystemValueString')
  110. ->willReturnCallback(function ($var, $default) {
  111. if ($var === 'appstoreurl') {
  112. return 'https://apps.nextcloud.com/api/v1';
  113. } elseif ($var === 'version') {
  114. return '11.0.0.2';
  115. }
  116. return $default;
  117. });
  118. $this->config->method('getSystemValueBool')
  119. ->willReturnArgument(1);
  120. $folder = $this->createMock(ISimpleFolder::class);
  121. $file = $this->createMock(ISimpleFile::class);
  122. $this->appData
  123. ->expects($this->once())
  124. ->method('getFolder')
  125. ->with('/')
  126. ->willReturn($folder);
  127. $folder
  128. ->expects($this->once())
  129. ->method('getFile')
  130. ->with($this->fileName)
  131. ->willThrowException(new NotFoundException());
  132. $folder
  133. ->expects($this->once())
  134. ->method('newFile')
  135. ->with($this->fileName)
  136. ->willReturn($file);
  137. $client = $this->createMock(IClient::class);
  138. $this->clientService
  139. ->expects($this->once())
  140. ->method('newClient')
  141. ->willReturn($client);
  142. $response = $this->createMock(IResponse::class);
  143. $client
  144. ->expects($this->once())
  145. ->method('get')
  146. ->with($this->endpoint)
  147. ->willReturn($response);
  148. $response
  149. ->expects($this->once())
  150. ->method('getBody')
  151. ->willReturn('[{"id":"MyNewApp", "foo": "foo"}, {"id":"bar"}]');
  152. $response->method('getHeader')
  153. ->with($this->equalTo('ETag'))
  154. ->willReturn('"myETag"');
  155. $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1502,"ncversion":"11.0.0.2","ETag":"\"myETag\""}';
  156. $file
  157. ->expects($this->once())
  158. ->method('putContent')
  159. ->with($fileData);
  160. $file
  161. ->expects($this->once())
  162. ->method('getContent')
  163. ->willReturn($fileData);
  164. $this->timeFactory
  165. ->expects($this->once())
  166. ->method('getTime')
  167. ->willReturn(1502);
  168. $expected = [
  169. [
  170. 'id' => 'MyNewApp',
  171. 'foo' => 'foo',
  172. ],
  173. [
  174. 'id' => 'bar',
  175. ],
  176. ];
  177. $this->assertSame($expected, $this->fetcher->get());
  178. }
  179. public function testGetWithAlreadyExistingFileAndOutdatedTimestamp() {
  180. $this->config->method('getSystemValueString')
  181. ->willReturnCallback(function ($key, $default) {
  182. if ($key === 'version') {
  183. return '11.0.0.2';
  184. } else {
  185. return $default;
  186. }
  187. });
  188. $this->config->method('getSystemValueBool')
  189. ->willReturnArgument(1);
  190. $folder = $this->createMock(ISimpleFolder::class);
  191. $file = $this->createMock(ISimpleFile::class);
  192. $this->appData
  193. ->expects($this->once())
  194. ->method('getFolder')
  195. ->with('/')
  196. ->willReturn($folder);
  197. $folder
  198. ->expects($this->once())
  199. ->method('getFile')
  200. ->with($this->fileName)
  201. ->willReturn($file);
  202. $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1502,"ncversion":"11.0.0.2","ETag":"\"myETag\""}';
  203. $file
  204. ->expects($this->once())
  205. ->method('putContent')
  206. ->with($fileData);
  207. $file
  208. ->expects($this->exactly(2))
  209. ->method('getContent')
  210. ->willReturnOnConsecutiveCalls(
  211. '{"timestamp":1200,"data":{"MyApp":{"id":"MyApp"}},"ncversion":"11.0.0.2"}',
  212. $fileData
  213. );
  214. $this->timeFactory
  215. ->expects($this->exactly(2))
  216. ->method('getTime')
  217. ->willReturnOnConsecutiveCalls(
  218. 4801,
  219. 1502
  220. );
  221. $client = $this->createMock(IClient::class);
  222. $this->clientService
  223. ->expects($this->once())
  224. ->method('newClient')
  225. ->willReturn($client);
  226. $response = $this->createMock(IResponse::class);
  227. $client
  228. ->expects($this->once())
  229. ->method('get')
  230. ->with($this->endpoint)
  231. ->willReturn($response);
  232. $response
  233. ->expects($this->once())
  234. ->method('getBody')
  235. ->willReturn('[{"id":"MyNewApp", "foo": "foo"}, {"id":"bar"}]');
  236. $response->method('getHeader')
  237. ->with($this->equalTo('ETag'))
  238. ->willReturn('"myETag"');
  239. $expected = [
  240. [
  241. 'id' => 'MyNewApp',
  242. 'foo' => 'foo',
  243. ],
  244. [
  245. 'id' => 'bar',
  246. ],
  247. ];
  248. $this->assertSame($expected, $this->fetcher->get());
  249. }
  250. public function testGetWithAlreadyExistingFileAndNoVersion() {
  251. $this->config
  252. ->method('getSystemValueString')
  253. ->willReturnCallback(function ($var, $default) {
  254. if ($var === 'appstoreurl') {
  255. return 'https://apps.nextcloud.com/api/v1';
  256. } elseif ($var === 'version') {
  257. return '11.0.0.2';
  258. }
  259. return $default;
  260. });
  261. $this->config->method('getSystemValueBool')
  262. ->willReturnArgument(1);
  263. $folder = $this->createMock(ISimpleFolder::class);
  264. $file = $this->createMock(ISimpleFile::class);
  265. $this->appData
  266. ->expects($this->once())
  267. ->method('getFolder')
  268. ->with('/')
  269. ->willReturn($folder);
  270. $folder
  271. ->expects($this->once())
  272. ->method('getFile')
  273. ->with($this->fileName)
  274. ->willReturn($file);
  275. $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1201,"ncversion":"11.0.0.2","ETag":"\"myETag\""}';
  276. $file
  277. ->expects($this->once())
  278. ->method('putContent')
  279. ->with($fileData);
  280. $file
  281. ->expects($this->exactly(2))
  282. ->method('getContent')
  283. ->willReturnOnConsecutiveCalls(
  284. '{"timestamp":1200,"data":{"MyApp":{"id":"MyApp"}}',
  285. $fileData
  286. );
  287. $this->timeFactory
  288. ->expects($this->once())
  289. ->method('getTime')
  290. ->willReturn(1201);
  291. $client = $this->createMock(IClient::class);
  292. $this->clientService
  293. ->expects($this->once())
  294. ->method('newClient')
  295. ->willReturn($client);
  296. $response = $this->createMock(IResponse::class);
  297. $client
  298. ->expects($this->once())
  299. ->method('get')
  300. ->with($this->endpoint)
  301. ->willReturn($response);
  302. $response
  303. ->expects($this->once())
  304. ->method('getBody')
  305. ->willReturn('[{"id":"MyNewApp", "foo": "foo"}, {"id":"bar"}]');
  306. $response->method('getHeader')
  307. ->with($this->equalTo('ETag'))
  308. ->willReturn('"myETag"');
  309. $expected = [
  310. [
  311. 'id' => 'MyNewApp',
  312. 'foo' => 'foo',
  313. ],
  314. [
  315. 'id' => 'bar',
  316. ],
  317. ];
  318. $this->assertSame($expected, $this->fetcher->get());
  319. }
  320. public function testGetWithAlreadyExistingFileAndOutdatedVersion() {
  321. $this->config
  322. ->method('getSystemValueString')
  323. ->willReturnCallback(function ($var, $default) {
  324. if ($var === 'appstoreurl') {
  325. return 'https://apps.nextcloud.com/api/v1';
  326. } elseif ($var === 'version') {
  327. return '11.0.0.2';
  328. }
  329. return $default;
  330. });
  331. $this->config->method('getSystemValueBool')
  332. ->willReturnArgument(1);
  333. $folder = $this->createMock(ISimpleFolder::class);
  334. $file = $this->createMock(ISimpleFile::class);
  335. $this->appData
  336. ->expects($this->once())
  337. ->method('getFolder')
  338. ->with('/')
  339. ->willReturn($folder);
  340. $folder
  341. ->expects($this->once())
  342. ->method('getFile')
  343. ->with($this->fileName)
  344. ->willReturn($file);
  345. $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1201,"ncversion":"11.0.0.2","ETag":"\"myETag\""}';
  346. $file
  347. ->expects($this->once())
  348. ->method('putContent')
  349. ->with($fileData);
  350. $file
  351. ->expects($this->exactly(2))
  352. ->method('getContent')
  353. ->willReturnOnConsecutiveCalls(
  354. '{"timestamp":1200,"data":{"MyApp":{"id":"MyApp"}},"ncversion":"11.0.0.1"',
  355. $fileData
  356. );
  357. $this->timeFactory
  358. ->method('getTime')
  359. ->willReturn(1201);
  360. $client = $this->createMock(IClient::class);
  361. $this->clientService
  362. ->expects($this->once())
  363. ->method('newClient')
  364. ->willReturn($client);
  365. $response = $this->createMock(IResponse::class);
  366. $client
  367. ->expects($this->once())
  368. ->method('get')
  369. ->with($this->endpoint)
  370. ->willReturn($response);
  371. $response
  372. ->expects($this->once())
  373. ->method('getBody')
  374. ->willReturn('[{"id":"MyNewApp", "foo": "foo"}, {"id":"bar"}]');
  375. $response->method('getHeader')
  376. ->with($this->equalTo('ETag'))
  377. ->willReturn('"myETag"');
  378. $expected = [
  379. [
  380. 'id' => 'MyNewApp',
  381. 'foo' => 'foo',
  382. ],
  383. [
  384. 'id' => 'bar',
  385. ],
  386. ];
  387. $this->assertSame($expected, $this->fetcher->get());
  388. }
  389. public function testGetWithExceptionInClient() {
  390. $this->config->method('getSystemValueString')
  391. ->willReturnArgument(1);
  392. $this->config->method('getSystemValueBool')
  393. ->willReturnArgument(1);
  394. $folder = $this->createMock(ISimpleFolder::class);
  395. $file = $this->createMock(ISimpleFile::class);
  396. $this->appData
  397. ->expects($this->once())
  398. ->method('getFolder')
  399. ->with('/')
  400. ->willReturn($folder);
  401. $folder
  402. ->expects($this->once())
  403. ->method('getFile')
  404. ->with($this->fileName)
  405. ->willReturn($file);
  406. $file
  407. ->expects($this->once())
  408. ->method('getContent')
  409. ->willReturn('{"timestamp":1200,"data":{"MyApp":{"id":"MyApp"}}}');
  410. $client = $this->createMock(IClient::class);
  411. $this->clientService
  412. ->expects($this->once())
  413. ->method('newClient')
  414. ->willReturn($client);
  415. $client
  416. ->expects($this->once())
  417. ->method('get')
  418. ->with($this->endpoint)
  419. ->willThrowException(new \Exception());
  420. $this->assertSame([], $this->fetcher->get());
  421. }
  422. public function testGetMatchingETag() {
  423. $this->config->method('getSystemValueString')
  424. ->willReturnCallback(function ($key, $default) {
  425. if ($key === 'version') {
  426. return '11.0.0.2';
  427. } else {
  428. return $default;
  429. }
  430. });
  431. $this->config->method('getSystemValueBool')
  432. ->willReturnArgument(1);
  433. $folder = $this->createMock(ISimpleFolder::class);
  434. $file = $this->createMock(ISimpleFile::class);
  435. $this->appData
  436. ->expects($this->once())
  437. ->method('getFolder')
  438. ->with('/')
  439. ->willReturn($folder);
  440. $folder
  441. ->expects($this->once())
  442. ->method('getFile')
  443. ->with($this->fileName)
  444. ->willReturn($file);
  445. $origData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1200,"ncversion":"11.0.0.2","ETag":"\"myETag\""}';
  446. $newData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":4802,"ncversion":"11.0.0.2","ETag":"\"myETag\""}';
  447. $file
  448. ->expects($this->once())
  449. ->method('putContent')
  450. ->with($newData);
  451. $file
  452. ->expects($this->exactly(2))
  453. ->method('getContent')
  454. ->willReturnOnConsecutiveCalls(
  455. $origData,
  456. $newData,
  457. );
  458. $this->timeFactory
  459. ->expects($this->exactly(2))
  460. ->method('getTime')
  461. ->willReturnOnConsecutiveCalls(
  462. 4801,
  463. 4802
  464. );
  465. $client = $this->createMock(IClient::class);
  466. $this->clientService
  467. ->expects($this->once())
  468. ->method('newClient')
  469. ->willReturn($client);
  470. $response = $this->createMock(IResponse::class);
  471. $client
  472. ->expects($this->once())
  473. ->method('get')
  474. ->with(
  475. $this->equalTo($this->endpoint),
  476. $this->equalTo([
  477. 'timeout' => 60,
  478. 'headers' => [
  479. 'If-None-Match' => '"myETag"'
  480. ]
  481. ])
  482. )->willReturn($response);
  483. $response->method('getStatusCode')
  484. ->willReturn(304);
  485. $expected = [
  486. [
  487. 'id' => 'MyNewApp',
  488. 'foo' => 'foo',
  489. ],
  490. [
  491. 'id' => 'bar',
  492. ],
  493. ];
  494. $this->assertSame($expected, $this->fetcher->get());
  495. }
  496. public function testGetNoMatchingETag() {
  497. $this->config->method('getSystemValueString')
  498. ->willReturnCallback(function ($key, $default) {
  499. if ($key === 'version') {
  500. return '11.0.0.2';
  501. } else {
  502. return $default;
  503. }
  504. });
  505. $this->config->method('getSystemValueBool')
  506. ->willReturnArgument(1);
  507. $folder = $this->createMock(ISimpleFolder::class);
  508. $file = $this->createMock(ISimpleFile::class);
  509. $this->appData
  510. ->expects($this->once())
  511. ->method('getFolder')
  512. ->with('/')
  513. ->willReturn($folder);
  514. $folder
  515. ->expects($this->once())
  516. ->method('getFile')
  517. ->with($this->fileName)
  518. ->willReturn($file);
  519. $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":4802,"ncversion":"11.0.0.2","ETag":"\"newETag\""}';
  520. $file
  521. ->expects($this->once())
  522. ->method('putContent')
  523. ->with($fileData);
  524. $file
  525. ->expects($this->exactly(2))
  526. ->method('getContent')
  527. ->willReturnOnConsecutiveCalls(
  528. '{"data":[{"id":"MyOldApp","abc":"def"}],"timestamp":1200,"ncversion":"11.0.0.2","ETag":"\"myETag\""}',
  529. $fileData,
  530. );
  531. $this->timeFactory
  532. ->expects($this->exactly(2))
  533. ->method('getTime')
  534. ->willReturnOnConsecutiveCalls(
  535. 4801,
  536. 4802,
  537. );
  538. $client = $this->createMock(IClient::class);
  539. $this->clientService
  540. ->expects($this->once())
  541. ->method('newClient')
  542. ->willReturn($client);
  543. $response = $this->createMock(IResponse::class);
  544. $client
  545. ->expects($this->once())
  546. ->method('get')
  547. ->with(
  548. $this->equalTo($this->endpoint),
  549. $this->equalTo([
  550. 'timeout' => 60,
  551. 'headers' => [
  552. 'If-None-Match' => '"myETag"',
  553. ]
  554. ])
  555. )
  556. ->willReturn($response);
  557. $response->method('getStatusCode')
  558. ->willReturn(200);
  559. $response
  560. ->expects($this->once())
  561. ->method('getBody')
  562. ->willReturn('[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}]');
  563. $response->method('getHeader')
  564. ->with($this->equalTo('ETag'))
  565. ->willReturn('"newETag"');
  566. $expected = [
  567. [
  568. 'id' => 'MyNewApp',
  569. 'foo' => 'foo',
  570. ],
  571. [
  572. 'id' => 'bar',
  573. ],
  574. ];
  575. $this->assertSame($expected, $this->fetcher->get());
  576. }
  577. public function testFetchAfterUpgradeNoETag() {
  578. $this->config->method('getSystemValueString')
  579. ->willReturnCallback(function ($key, $default) {
  580. if ($key === 'version') {
  581. return '11.0.0.3';
  582. } else {
  583. return $default;
  584. }
  585. });
  586. $this->config->method('getSystemValueBool')
  587. ->willReturnArgument(1);
  588. $folder = $this->createMock(ISimpleFolder::class);
  589. $file = $this->createMock(ISimpleFile::class);
  590. $this->appData
  591. ->expects($this->once())
  592. ->method('getFolder')
  593. ->with('/')
  594. ->willReturn($folder);
  595. $folder
  596. ->expects($this->once())
  597. ->method('getFile')
  598. ->with($this->fileName)
  599. ->willReturn($file);
  600. $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1501,"ncversion":"11.0.0.3","ETag":"\"newETag\""}';
  601. $file
  602. ->expects($this->once())
  603. ->method('putContent')
  604. ->with($fileData);
  605. $file
  606. ->expects($this->exactly(2))
  607. ->method('getContent')
  608. ->willReturnOnConsecutiveCalls(
  609. '{"data":[{"id":"MyOldApp","abc":"def"}],"timestamp":1200,"ncversion":"11.0.0.2","ETag":"\"myETag\""}',
  610. $fileData
  611. );
  612. $client = $this->createMock(IClient::class);
  613. $this->clientService
  614. ->expects($this->once())
  615. ->method('newClient')
  616. ->willReturn($client);
  617. $response = $this->createMock(IResponse::class);
  618. $client
  619. ->expects($this->once())
  620. ->method('get')
  621. ->with(
  622. $this->equalTo($this->endpoint),
  623. $this->equalTo([
  624. 'timeout' => 60,
  625. ])
  626. )
  627. ->willReturn($response);
  628. $response->method('getStatusCode')
  629. ->willReturn(200);
  630. $response
  631. ->expects($this->once())
  632. ->method('getBody')
  633. ->willReturn('[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}]');
  634. $response->method('getHeader')
  635. ->with($this->equalTo('ETag'))
  636. ->willReturn('"newETag"');
  637. $this->timeFactory
  638. ->expects($this->once())
  639. ->method('getTime')
  640. ->willReturn(1501);
  641. $expected = [
  642. [
  643. 'id' => 'MyNewApp',
  644. 'foo' => 'foo',
  645. ],
  646. [
  647. 'id' => 'bar',
  648. ],
  649. ];
  650. $this->assertSame($expected, $this->fetcher->get());
  651. }
  652. }