FetcherBase.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689
  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. ->method('getSystemValueString')
  75. ->willReturnCallback(function ($var, $default) {
  76. if ($var === 'version') {
  77. return '11.0.0.2';
  78. }
  79. return $default;
  80. });
  81. $this->config->method('getSystemValueBool')
  82. ->willReturnArgument(1);
  83. $folder = $this->createMock(ISimpleFolder::class);
  84. $file = $this->createMock(ISimpleFile::class);
  85. $this->appData
  86. ->expects($this->once())
  87. ->method('getFolder')
  88. ->with('/')
  89. ->willReturn($folder);
  90. $folder
  91. ->expects($this->once())
  92. ->method('getFile')
  93. ->with($this->fileName)
  94. ->willReturn($file);
  95. $file
  96. ->expects($this->once())
  97. ->method('getContent')
  98. ->willReturn('{"timestamp":1200,"data":[{"id":"MyApp"}],"ncversion":"11.0.0.2"}');
  99. $this->timeFactory
  100. ->expects($this->once())
  101. ->method('getTime')
  102. ->willReturn(1499);
  103. $expected = [
  104. [
  105. 'id' => 'MyApp',
  106. ],
  107. ];
  108. $this->assertSame($expected, $this->fetcher->get());
  109. }
  110. public function testGetWithNotExistingFileAndUpToDateTimestampAndVersion() {
  111. $this->config
  112. ->method('getSystemValueString')
  113. ->willReturnCallback(function ($var, $default) {
  114. if ($var === 'appstoreurl') {
  115. return 'https://apps.nextcloud.com/api/v1';
  116. } elseif ($var === 'version') {
  117. return '11.0.0.2';
  118. }
  119. return $default;
  120. });
  121. $this->config->method('getSystemValueBool')
  122. ->willReturnArgument(1);
  123. $folder = $this->createMock(ISimpleFolder::class);
  124. $file = $this->createMock(ISimpleFile::class);
  125. $this->appData
  126. ->expects($this->once())
  127. ->method('getFolder')
  128. ->with('/')
  129. ->willReturn($folder);
  130. $folder
  131. ->expects($this->once())
  132. ->method('getFile')
  133. ->with($this->fileName)
  134. ->willThrowException(new NotFoundException());
  135. $folder
  136. ->expects($this->once())
  137. ->method('newFile')
  138. ->with($this->fileName)
  139. ->willReturn($file);
  140. $client = $this->createMock(IClient::class);
  141. $this->clientService
  142. ->expects($this->once())
  143. ->method('newClient')
  144. ->willReturn($client);
  145. $response = $this->createMock(IResponse::class);
  146. $client
  147. ->expects($this->once())
  148. ->method('get')
  149. ->with($this->endpoint)
  150. ->willReturn($response);
  151. $response
  152. ->expects($this->once())
  153. ->method('getBody')
  154. ->willReturn('[{"id":"MyNewApp", "foo": "foo"}, {"id":"bar"}]');
  155. $response->method('getHeader')
  156. ->with($this->equalTo('ETag'))
  157. ->willReturn('"myETag"');
  158. $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1502,"ncversion":"11.0.0.2","ETag":"\"myETag\""}';
  159. $file
  160. ->expects($this->once())
  161. ->method('putContent')
  162. ->with($fileData);
  163. $file
  164. ->expects($this->once())
  165. ->method('getContent')
  166. ->willReturn($fileData);
  167. $this->timeFactory
  168. ->expects($this->once())
  169. ->method('getTime')
  170. ->willReturn(1502);
  171. $expected = [
  172. [
  173. 'id' => 'MyNewApp',
  174. 'foo' => 'foo',
  175. ],
  176. [
  177. 'id' => 'bar',
  178. ],
  179. ];
  180. $this->assertSame($expected, $this->fetcher->get());
  181. }
  182. public function testGetWithAlreadyExistingFileAndOutdatedTimestamp() {
  183. $this->config->method('getSystemValueString')
  184. ->willReturnCallback(function ($key, $default) {
  185. if ($key === 'version') {
  186. return '11.0.0.2';
  187. } else {
  188. return $default;
  189. }
  190. });
  191. $this->config->method('getSystemValueBool')
  192. ->willReturnArgument(1);
  193. $folder = $this->createMock(ISimpleFolder::class);
  194. $file = $this->createMock(ISimpleFile::class);
  195. $this->appData
  196. ->expects($this->once())
  197. ->method('getFolder')
  198. ->with('/')
  199. ->willReturn($folder);
  200. $folder
  201. ->expects($this->once())
  202. ->method('getFile')
  203. ->with($this->fileName)
  204. ->willReturn($file);
  205. $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1502,"ncversion":"11.0.0.2","ETag":"\"myETag\""}';
  206. $file
  207. ->expects($this->once())
  208. ->method('putContent')
  209. ->with($fileData);
  210. $file
  211. ->expects($this->exactly(2))
  212. ->method('getContent')
  213. ->willReturnOnConsecutiveCalls(
  214. '{"timestamp":1200,"data":{"MyApp":{"id":"MyApp"}},"ncversion":"11.0.0.2"}',
  215. $fileData
  216. );
  217. $this->timeFactory
  218. ->expects($this->exactly(2))
  219. ->method('getTime')
  220. ->willReturnOnConsecutiveCalls(
  221. 4801,
  222. 1502
  223. );
  224. $client = $this->createMock(IClient::class);
  225. $this->clientService
  226. ->expects($this->once())
  227. ->method('newClient')
  228. ->willReturn($client);
  229. $response = $this->createMock(IResponse::class);
  230. $client
  231. ->expects($this->once())
  232. ->method('get')
  233. ->with($this->endpoint)
  234. ->willReturn($response);
  235. $response
  236. ->expects($this->once())
  237. ->method('getBody')
  238. ->willReturn('[{"id":"MyNewApp", "foo": "foo"}, {"id":"bar"}]');
  239. $response->method('getHeader')
  240. ->with($this->equalTo('ETag'))
  241. ->willReturn('"myETag"');
  242. $expected = [
  243. [
  244. 'id' => 'MyNewApp',
  245. 'foo' => 'foo',
  246. ],
  247. [
  248. 'id' => 'bar',
  249. ],
  250. ];
  251. $this->assertSame($expected, $this->fetcher->get());
  252. }
  253. public function testGetWithAlreadyExistingFileAndNoVersion() {
  254. $this->config
  255. ->method('getSystemValueString')
  256. ->willReturnCallback(function ($var, $default) {
  257. if ($var === 'appstoreurl') {
  258. return 'https://apps.nextcloud.com/api/v1';
  259. } elseif ($var === 'version') {
  260. return '11.0.0.2';
  261. }
  262. return $default;
  263. });
  264. $this->config->method('getSystemValueBool')
  265. ->willReturnArgument(1);
  266. $folder = $this->createMock(ISimpleFolder::class);
  267. $file = $this->createMock(ISimpleFile::class);
  268. $this->appData
  269. ->expects($this->once())
  270. ->method('getFolder')
  271. ->with('/')
  272. ->willReturn($folder);
  273. $folder
  274. ->expects($this->once())
  275. ->method('getFile')
  276. ->with($this->fileName)
  277. ->willReturn($file);
  278. $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1201,"ncversion":"11.0.0.2","ETag":"\"myETag\""}';
  279. $file
  280. ->expects($this->once())
  281. ->method('putContent')
  282. ->with($fileData);
  283. $file
  284. ->expects($this->exactly(2))
  285. ->method('getContent')
  286. ->willReturnOnConsecutiveCalls(
  287. '{"timestamp":1200,"data":{"MyApp":{"id":"MyApp"}}',
  288. $fileData
  289. );
  290. $this->timeFactory
  291. ->expects($this->once())
  292. ->method('getTime')
  293. ->willReturn(1201);
  294. $client = $this->createMock(IClient::class);
  295. $this->clientService
  296. ->expects($this->once())
  297. ->method('newClient')
  298. ->willReturn($client);
  299. $response = $this->createMock(IResponse::class);
  300. $client
  301. ->expects($this->once())
  302. ->method('get')
  303. ->with($this->endpoint)
  304. ->willReturn($response);
  305. $response
  306. ->expects($this->once())
  307. ->method('getBody')
  308. ->willReturn('[{"id":"MyNewApp", "foo": "foo"}, {"id":"bar"}]');
  309. $response->method('getHeader')
  310. ->with($this->equalTo('ETag'))
  311. ->willReturn('"myETag"');
  312. $expected = [
  313. [
  314. 'id' => 'MyNewApp',
  315. 'foo' => 'foo',
  316. ],
  317. [
  318. 'id' => 'bar',
  319. ],
  320. ];
  321. $this->assertSame($expected, $this->fetcher->get());
  322. }
  323. public function testGetWithAlreadyExistingFileAndOutdatedVersion() {
  324. $this->config
  325. ->method('getSystemValueString')
  326. ->willReturnCallback(function ($var, $default) {
  327. if ($var === 'appstoreurl') {
  328. return 'https://apps.nextcloud.com/api/v1';
  329. } elseif ($var === 'version') {
  330. return '11.0.0.2';
  331. }
  332. return $default;
  333. });
  334. $this->config->method('getSystemValueBool')
  335. ->willReturnArgument(1);
  336. $folder = $this->createMock(ISimpleFolder::class);
  337. $file = $this->createMock(ISimpleFile::class);
  338. $this->appData
  339. ->expects($this->once())
  340. ->method('getFolder')
  341. ->with('/')
  342. ->willReturn($folder);
  343. $folder
  344. ->expects($this->once())
  345. ->method('getFile')
  346. ->with($this->fileName)
  347. ->willReturn($file);
  348. $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1201,"ncversion":"11.0.0.2","ETag":"\"myETag\""}';
  349. $file
  350. ->expects($this->once())
  351. ->method('putContent')
  352. ->with($fileData);
  353. $file
  354. ->expects($this->exactly(2))
  355. ->method('getContent')
  356. ->willReturnOnConsecutiveCalls(
  357. '{"timestamp":1200,"data":{"MyApp":{"id":"MyApp"}},"ncversion":"11.0.0.1"',
  358. $fileData
  359. );
  360. $this->timeFactory
  361. ->method('getTime')
  362. ->willReturn(1201);
  363. $client = $this->createMock(IClient::class);
  364. $this->clientService
  365. ->expects($this->once())
  366. ->method('newClient')
  367. ->willReturn($client);
  368. $response = $this->createMock(IResponse::class);
  369. $client
  370. ->expects($this->once())
  371. ->method('get')
  372. ->with($this->endpoint)
  373. ->willReturn($response);
  374. $response
  375. ->expects($this->once())
  376. ->method('getBody')
  377. ->willReturn('[{"id":"MyNewApp", "foo": "foo"}, {"id":"bar"}]');
  378. $response->method('getHeader')
  379. ->with($this->equalTo('ETag'))
  380. ->willReturn('"myETag"');
  381. $expected = [
  382. [
  383. 'id' => 'MyNewApp',
  384. 'foo' => 'foo',
  385. ],
  386. [
  387. 'id' => 'bar',
  388. ],
  389. ];
  390. $this->assertSame($expected, $this->fetcher->get());
  391. }
  392. public function testGetWithExceptionInClient() {
  393. $this->config->method('getSystemValueString')
  394. ->willReturnArgument(1);
  395. $this->config->method('getSystemValueBool')
  396. ->willReturnArgument(1);
  397. $folder = $this->createMock(ISimpleFolder::class);
  398. $file = $this->createMock(ISimpleFile::class);
  399. $this->appData
  400. ->expects($this->once())
  401. ->method('getFolder')
  402. ->with('/')
  403. ->willReturn($folder);
  404. $folder
  405. ->expects($this->once())
  406. ->method('getFile')
  407. ->with($this->fileName)
  408. ->willReturn($file);
  409. $file
  410. ->expects($this->once())
  411. ->method('getContent')
  412. ->willReturn('{"timestamp":1200,"data":{"MyApp":{"id":"MyApp"}}}');
  413. $client = $this->createMock(IClient::class);
  414. $this->clientService
  415. ->expects($this->once())
  416. ->method('newClient')
  417. ->willReturn($client);
  418. $client
  419. ->expects($this->once())
  420. ->method('get')
  421. ->with($this->endpoint)
  422. ->willThrowException(new \Exception());
  423. $this->assertSame([], $this->fetcher->get());
  424. }
  425. public function testGetMatchingETag() {
  426. $this->config->method('getSystemValueString')
  427. ->willReturnCallback(function ($key, $default) {
  428. if ($key === 'version') {
  429. return '11.0.0.2';
  430. } else {
  431. return $default;
  432. }
  433. });
  434. $this->config->method('getSystemValueBool')
  435. ->willReturnArgument(1);
  436. $folder = $this->createMock(ISimpleFolder::class);
  437. $file = $this->createMock(ISimpleFile::class);
  438. $this->appData
  439. ->expects($this->once())
  440. ->method('getFolder')
  441. ->with('/')
  442. ->willReturn($folder);
  443. $folder
  444. ->expects($this->once())
  445. ->method('getFile')
  446. ->with($this->fileName)
  447. ->willReturn($file);
  448. $origData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1200,"ncversion":"11.0.0.2","ETag":"\"myETag\""}';
  449. $newData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":4802,"ncversion":"11.0.0.2","ETag":"\"myETag\""}';
  450. $file
  451. ->expects($this->once())
  452. ->method('putContent')
  453. ->with($newData);
  454. $file
  455. ->expects($this->exactly(2))
  456. ->method('getContent')
  457. ->willReturnOnConsecutiveCalls(
  458. $origData,
  459. $newData,
  460. );
  461. $this->timeFactory
  462. ->expects($this->exactly(2))
  463. ->method('getTime')
  464. ->willReturnOnConsecutiveCalls(
  465. 4801,
  466. 4802
  467. );
  468. $client = $this->createMock(IClient::class);
  469. $this->clientService
  470. ->expects($this->once())
  471. ->method('newClient')
  472. ->willReturn($client);
  473. $response = $this->createMock(IResponse::class);
  474. $client
  475. ->expects($this->once())
  476. ->method('get')
  477. ->with(
  478. $this->equalTo($this->endpoint),
  479. $this->equalTo([
  480. 'timeout' => 60,
  481. 'headers' => [
  482. 'If-None-Match' => '"myETag"'
  483. ]
  484. ])
  485. )->willReturn($response);
  486. $response->method('getStatusCode')
  487. ->willReturn(304);
  488. $expected = [
  489. [
  490. 'id' => 'MyNewApp',
  491. 'foo' => 'foo',
  492. ],
  493. [
  494. 'id' => 'bar',
  495. ],
  496. ];
  497. $this->assertSame($expected, $this->fetcher->get());
  498. }
  499. public function testGetNoMatchingETag() {
  500. $this->config->method('getSystemValueString')
  501. ->willReturnCallback(function ($key, $default) {
  502. if ($key === 'version') {
  503. return '11.0.0.2';
  504. } else {
  505. return $default;
  506. }
  507. });
  508. $this->config->method('getSystemValueBool')
  509. ->willReturnArgument(1);
  510. $folder = $this->createMock(ISimpleFolder::class);
  511. $file = $this->createMock(ISimpleFile::class);
  512. $this->appData
  513. ->expects($this->once())
  514. ->method('getFolder')
  515. ->with('/')
  516. ->willReturn($folder);
  517. $folder
  518. ->expects($this->once())
  519. ->method('getFile')
  520. ->with($this->fileName)
  521. ->willReturn($file);
  522. $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":4802,"ncversion":"11.0.0.2","ETag":"\"newETag\""}';
  523. $file
  524. ->expects($this->once())
  525. ->method('putContent')
  526. ->with($fileData);
  527. $file
  528. ->expects($this->exactly(2))
  529. ->method('getContent')
  530. ->willReturnOnConsecutiveCalls(
  531. '{"data":[{"id":"MyOldApp","abc":"def"}],"timestamp":1200,"ncversion":"11.0.0.2","ETag":"\"myETag\""}',
  532. $fileData,
  533. );
  534. $this->timeFactory
  535. ->expects($this->exactly(2))
  536. ->method('getTime')
  537. ->willReturnOnConsecutiveCalls(
  538. 4801,
  539. 4802,
  540. );
  541. $client = $this->createMock(IClient::class);
  542. $this->clientService
  543. ->expects($this->once())
  544. ->method('newClient')
  545. ->willReturn($client);
  546. $response = $this->createMock(IResponse::class);
  547. $client
  548. ->expects($this->once())
  549. ->method('get')
  550. ->with(
  551. $this->equalTo($this->endpoint),
  552. $this->equalTo([
  553. 'timeout' => 60,
  554. 'headers' => [
  555. 'If-None-Match' => '"myETag"',
  556. ]
  557. ])
  558. )
  559. ->willReturn($response);
  560. $response->method('getStatusCode')
  561. ->willReturn(200);
  562. $response
  563. ->expects($this->once())
  564. ->method('getBody')
  565. ->willReturn('[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}]');
  566. $response->method('getHeader')
  567. ->with($this->equalTo('ETag'))
  568. ->willReturn('"newETag"');
  569. $expected = [
  570. [
  571. 'id' => 'MyNewApp',
  572. 'foo' => 'foo',
  573. ],
  574. [
  575. 'id' => 'bar',
  576. ],
  577. ];
  578. $this->assertSame($expected, $this->fetcher->get());
  579. }
  580. public function testFetchAfterUpgradeNoETag() {
  581. $this->config->method('getSystemValueString')
  582. ->willReturnCallback(function ($key, $default) {
  583. if ($key === 'version') {
  584. return '11.0.0.3';
  585. } else {
  586. return $default;
  587. }
  588. });
  589. $this->config->method('getSystemValueBool')
  590. ->willReturnArgument(1);
  591. $folder = $this->createMock(ISimpleFolder::class);
  592. $file = $this->createMock(ISimpleFile::class);
  593. $this->appData
  594. ->expects($this->once())
  595. ->method('getFolder')
  596. ->with('/')
  597. ->willReturn($folder);
  598. $folder
  599. ->expects($this->once())
  600. ->method('getFile')
  601. ->with($this->fileName)
  602. ->willReturn($file);
  603. $fileData = '{"data":[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}],"timestamp":1501,"ncversion":"11.0.0.3","ETag":"\"newETag\""}';
  604. $file
  605. ->expects($this->once())
  606. ->method('putContent')
  607. ->with($fileData);
  608. $file
  609. ->expects($this->exactly(2))
  610. ->method('getContent')
  611. ->willReturnOnConsecutiveCalls(
  612. '{"data":[{"id":"MyOldApp","abc":"def"}],"timestamp":1200,"ncversion":"11.0.0.2","ETag":"\"myETag\""}',
  613. $fileData
  614. );
  615. $client = $this->createMock(IClient::class);
  616. $this->clientService
  617. ->expects($this->once())
  618. ->method('newClient')
  619. ->willReturn($client);
  620. $response = $this->createMock(IResponse::class);
  621. $client
  622. ->expects($this->once())
  623. ->method('get')
  624. ->with(
  625. $this->equalTo($this->endpoint),
  626. $this->equalTo([
  627. 'timeout' => 60,
  628. ])
  629. )
  630. ->willReturn($response);
  631. $response->method('getStatusCode')
  632. ->willReturn(200);
  633. $response
  634. ->expects($this->once())
  635. ->method('getBody')
  636. ->willReturn('[{"id":"MyNewApp","foo":"foo"},{"id":"bar"}]');
  637. $response->method('getHeader')
  638. ->with($this->equalTo('ETag'))
  639. ->willReturn('"newETag"');
  640. $this->timeFactory
  641. ->expects($this->once())
  642. ->method('getTime')
  643. ->willReturn(1501);
  644. $expected = [
  645. [
  646. 'id' => 'MyNewApp',
  647. 'foo' => 'foo',
  648. ],
  649. [
  650. 'id' => 'bar',
  651. ],
  652. ];
  653. $this->assertSame($expected, $this->fetcher->get());
  654. }
  655. }