AmazonS3Spec.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684
  1. <?php
  2. namespace spec\Gaufrette\Adapter;
  3. use PhpSpec\ObjectBehavior;
  4. use Prophecy\Argument;
  5. class AmazonS3Spec extends ObjectBehavior
  6. {
  7. /**
  8. * @param \AmazonS3 $service
  9. */
  10. function let($service)
  11. {
  12. $this->beConstructedWith($service, 'bucketName');
  13. }
  14. function it_is_adapter()
  15. {
  16. $this->shouldHaveType('Gaufrette\Adapter');
  17. }
  18. function it_supports_metadata()
  19. {
  20. $this->shouldHaveType('Gaufrette\Adapter\MetadataSupporter');
  21. }
  22. /**
  23. * @param \AmazonS3 $service
  24. */
  25. function it_reads_file($service)
  26. {
  27. $options = array(
  28. 'range' => 12,
  29. 'response' => array(
  30. 'content-language' => 'pl-pl'
  31. )
  32. );
  33. $service
  34. ->if_bucket_exists('bucketName')
  35. ->shouldBeCalled()
  36. ->willReturn(true)
  37. ;
  38. $service
  39. ->get_object(
  40. 'bucketName',
  41. 'filename',
  42. $options
  43. )
  44. ->shouldBeCalled()
  45. ->willReturn(new \CFResponse('header', 'some content', 200))
  46. ;
  47. $this->setMetadata('filename', $options);
  48. $this->read('filename')->shouldReturn('some content');
  49. }
  50. /**
  51. * @param \AmazonS3 $service
  52. */
  53. function it_returns_false_when_cannot_read($service)
  54. {
  55. $service
  56. ->if_bucket_exists('bucketName')
  57. ->shouldBeCalled()
  58. ->willReturn(true)
  59. ;
  60. $service
  61. ->get_object(
  62. 'bucketName',
  63. 'filename',
  64. array()
  65. )
  66. ->shouldBeCalled()
  67. ->willReturn(new \CFResponse('header', 'some content', 500))
  68. ;
  69. $this->read('filename')->shouldReturn(false);
  70. }
  71. /**
  72. * @param \AmazonS3 $service
  73. */
  74. function it_is_verbose_and_throws_exceptions_when_read($service)
  75. {
  76. $service
  77. ->if_bucket_exists('bucketName')
  78. ->shouldBeCalled()
  79. ->willReturn(true)
  80. ;
  81. $service
  82. ->get_object(
  83. 'bucketName',
  84. 'filename',
  85. array()
  86. )
  87. ->willThrow(new \RuntimeException('read'))
  88. ;
  89. $this->shouldThrow(new \RuntimeException('read'))->duringRead('filename');
  90. }
  91. /**
  92. * @param \AmazonS3 $service
  93. */
  94. function it_rename_file($service)
  95. {
  96. $service
  97. ->if_bucket_exists('bucketName')
  98. ->shouldBeCalled()
  99. ->willReturn(true)
  100. ;
  101. $service
  102. ->copy_object(
  103. array(
  104. 'bucket' => 'bucketName',
  105. 'filename' => 'filename1',
  106. ),
  107. array(
  108. 'bucket' => 'bucketName',
  109. 'filename' => 'filename2'
  110. ),
  111. array('acl' => \AmazonS3::ACL_OWNER_READ)
  112. )
  113. ->shouldBeCalled()
  114. ->willReturn(new \CFResponse('header', 'some content', 200))
  115. ;
  116. $service
  117. ->delete_object(
  118. 'bucketName',
  119. 'filename1',
  120. Argument::any()
  121. )
  122. ->shouldBeCalled()
  123. ->willReturn(new \CFResponse(array(), 'some', 200))
  124. ;
  125. $this->setMetadata('filename1', array('acl' => \AmazonS3::ACL_OWNER_READ));
  126. $this->rename('filename1', 'filename2')->shouldReturn(true);
  127. }
  128. /**
  129. * @param \AmazonS3 $service
  130. */
  131. function it_is_verbose_and_throws_exceptions_when_rename($service)
  132. {
  133. $service
  134. ->if_bucket_exists('bucketName')
  135. ->shouldBeCalled()
  136. ->willReturn(true)
  137. ;
  138. $service
  139. ->copy_object(Argument::cetera())
  140. ->willThrow(new \RuntimeException('rename'))
  141. ;
  142. $this->shouldThrow(new \RuntimeException('rename'))->duringRename('filename', 'filename1');
  143. }
  144. /**
  145. * @param \AmazonS3 $service
  146. */
  147. function it_returns_false_when_cannot_rename($service)
  148. {
  149. $service
  150. ->if_bucket_exists('bucketName')
  151. ->shouldBeCalled()
  152. ->willReturn(true)
  153. ;
  154. $service
  155. ->copy_object(
  156. array(
  157. 'bucket' => 'bucketName',
  158. 'filename' => 'filename1',
  159. ),
  160. array(
  161. 'bucket' => 'bucketName',
  162. 'filename' => 'filename2'
  163. ),
  164. array()
  165. )
  166. ->shouldBeCalled()
  167. ->willReturn(new \CFResponse('header', 'some content', 500))
  168. ;
  169. $this->rename('filename1', 'filename2')->shouldReturn(false);
  170. }
  171. /**
  172. * @param \AmazonS3 $service
  173. */
  174. function it_should_write_file($service)
  175. {
  176. $service
  177. ->if_bucket_exists('bucketName')
  178. ->shouldBeCalled()
  179. ->willReturn(true)
  180. ;
  181. $service
  182. ->create_object(
  183. 'bucketName',
  184. 'filename',
  185. array(
  186. 'acl' => \AmazonS3::ACL_PRIVATE,
  187. 'body' => 'some content'
  188. )
  189. )
  190. ->shouldBeCalled()
  191. ->willReturn(new \CFResponse(array('x-aws-requestheaders' => array('Content-Length' => 12)), 'some content', 200))
  192. ;
  193. $this->setMetadata('filename', array('acl' => \AmazonS3::ACL_PRIVATE, 'body' => 'other content'));
  194. $this->write('filename', 'some content')->shouldReturn(12);
  195. }
  196. /**
  197. * @param \AmazonS3 $service
  198. */
  199. function it_returns_false_when_cannot_write($service)
  200. {
  201. $service
  202. ->if_bucket_exists('bucketName')
  203. ->shouldBeCalled()
  204. ->willReturn(true)
  205. ;
  206. $service
  207. ->create_object(
  208. 'bucketName',
  209. 'filename',
  210. array(
  211. 'acl' => \AmazonS3::ACL_PUBLIC,
  212. 'body' => 'some content'
  213. )
  214. )
  215. ->shouldBeCalled()
  216. ->willReturn(new \CFResponse(array('x-aws-requestheaders' => array('Content-Length' => 12)), 'some content', 500))
  217. ;
  218. $this->write('filename', 'some content')->shouldReturn(false);
  219. }
  220. /**
  221. * @param \AmazonS3 $service
  222. */
  223. function it_is_verbose_and_throws_exceptions_when_write($service)
  224. {
  225. $service
  226. ->if_bucket_exists('bucketName')
  227. ->shouldBeCalled()
  228. ->willReturn(true)
  229. ;
  230. $service
  231. ->create_object(Argument::cetera())
  232. ->willThrow(new \RuntimeException('write'))
  233. ;
  234. $this->shouldThrow(new \RuntimeException('write'))->duringWrite('filename', 'some content');
  235. }
  236. /**
  237. * @param \AmazonS3 $service
  238. */
  239. function it_should_check_if_file_exists($service)
  240. {
  241. $service
  242. ->if_bucket_exists('bucketName')
  243. ->shouldBeCalled()
  244. ->willReturn(true)
  245. ;
  246. $service->if_object_exists('bucketName', 'filename')->willReturn(true);
  247. $this->exists('filename')->shouldReturn(true);
  248. $service->if_object_exists('bucketName', 'filename')->willReturn(false);
  249. $this->exists('filename')->shouldReturn(false);
  250. }
  251. /**
  252. * @param \AmazonS3 $service
  253. */
  254. function it_is_verbose_and_throws_exceptions_when_file_exists($service)
  255. {
  256. $service
  257. ->if_bucket_exists('bucketName')
  258. ->shouldBeCalled()
  259. ->willReturn(true)
  260. ;
  261. $service
  262. ->if_object_exists('bucketName', 'filename')
  263. ->willThrow(new \RuntimeException('exists'))
  264. ;
  265. $this->shouldThrow(new \RuntimeException('exists'))->duringExists('filename');
  266. }
  267. /**
  268. * @param \AmazonS3 $service
  269. */
  270. function it_should_get_file_mtime($service)
  271. {
  272. $metadata = array('acl' => \AmazonS3::ACL_PUBLIC);
  273. $service
  274. ->if_bucket_exists('bucketName')
  275. ->shouldBeCalled()
  276. ->willReturn(true)
  277. ;
  278. $service
  279. ->get_object_metadata(
  280. 'bucketName',
  281. 'filename',
  282. $metadata
  283. )
  284. ->shouldBeCalled()
  285. ->willReturn(array('Headers' => array('last-modified' => '2012-01-01 23:10:10')))
  286. ;
  287. $this->setMetadata('filename', $metadata);
  288. $this->mtime('filename')->shouldReturn(strtotime('2012-01-01 23:10:10'));
  289. }
  290. /**
  291. * @param \AmazonS3 $service
  292. */
  293. function it_returns_false_when_cannot_fetch_mtime($service)
  294. {
  295. $service
  296. ->if_bucket_exists('bucketName')
  297. ->shouldBeCalled()
  298. ->willReturn(true)
  299. ;
  300. $service
  301. ->get_object_metadata(
  302. 'bucketName',
  303. 'filename',
  304. array()
  305. )
  306. ->shouldBeCalled()
  307. ->willReturn(array('Headers' => array()))
  308. ;
  309. $this->mtime('filename')->shouldReturn(false);
  310. }
  311. /**
  312. * @param \AmazonS3 $service
  313. */
  314. function it_is_verbose_and_throws_exceptions_when_fetch_mtime($service)
  315. {
  316. $service
  317. ->if_bucket_exists('bucketName')
  318. ->shouldBeCalled()
  319. ->willReturn(true)
  320. ;
  321. $service
  322. ->get_object_metadata('bucketName', 'filename', Argument::any())
  323. ->willThrow(new \RuntimeException('mtime'))
  324. ;
  325. $this->shouldThrow(new \RuntimeException('mtime'))->duringMtime('filename');
  326. }
  327. /**
  328. * @param \AmazonS3 $service
  329. */
  330. function it_should_delete_file($service)
  331. {
  332. $metadata = array('acl' => \AmazonS3::ACL_PRIVATE);
  333. $service
  334. ->if_bucket_exists('bucketName')
  335. ->shouldBeCalled()
  336. ->willReturn(true)
  337. ;
  338. $service
  339. ->delete_object(
  340. 'bucketName',
  341. 'filename',
  342. $metadata
  343. )
  344. ->willReturn(new \CFResponse(array(), 'some', 200))
  345. ;
  346. $this->setMetadata('filename', $metadata);
  347. $this->delete('filename')->shouldReturn(true);
  348. }
  349. /**
  350. * @param \AmazonS3 $service
  351. */
  352. function it_is_verbose_and_throws_exceptions_when_fetch_delete($service)
  353. {
  354. $service
  355. ->if_bucket_exists('bucketName')
  356. ->willReturn(true)
  357. ;
  358. $service
  359. ->delete_object(
  360. 'bucketName',
  361. 'filename',
  362. Argument::any()
  363. )
  364. ->willThrow(new \RuntimeException('delete'))
  365. ;
  366. $this->shouldThrow(new \RuntimeException('delete'))->duringDelete('filename');
  367. }
  368. /**
  369. * @param \AmazonS3 $service
  370. */
  371. function it_returns_false_when_cannot_delete($service)
  372. {
  373. $service
  374. ->if_bucket_exists('bucketName')
  375. ->shouldBeCalled()
  376. ->willReturn(true)
  377. ;
  378. $service
  379. ->delete_object(
  380. 'bucketName',
  381. 'filename',
  382. array()
  383. )
  384. ->willReturn(new \CFResponse(array(), 'some', 500))
  385. ;
  386. $this->delete('filename')->shouldReturn(false);
  387. }
  388. /**
  389. * @param \AmazonS3 $service
  390. */
  391. function it_should_get_keys($service)
  392. {
  393. $service
  394. ->if_bucket_exists('bucketName')
  395. ->shouldBeCalled()
  396. ->willReturn(true)
  397. ;
  398. $service
  399. ->get_object_list('bucketName')
  400. ->shouldBeCalled()
  401. ->willReturn(array('filename2', 'aaa/filename', 'filename1'))
  402. ;
  403. $this->keys()->shouldReturn(array('aaa', 'aaa/filename', 'filename1', 'filename2'));
  404. }
  405. /**
  406. * @param \AmazonS3 $service
  407. */
  408. function it_is_verbose_and_throws_exceptions_when_fetch_keys($service)
  409. {
  410. $service
  411. ->if_bucket_exists('bucketName')
  412. ->willReturn(true)
  413. ;
  414. $service
  415. ->get_object_list('bucketName')
  416. ->willThrow(new \RuntimeException('keys'))
  417. ;
  418. $this->shouldThrow(new \RuntimeException('keys'))->duringKeys();
  419. }
  420. /**
  421. * @param \AmazonS3 $service
  422. */
  423. function it_should_handle_dirs($service)
  424. {
  425. $service
  426. ->if_bucket_exists('bucketName')
  427. ->willReturn(true)
  428. ;
  429. $service
  430. ->if_object_exists('bucketName', 'filename')
  431. ->shouldNotBeCalled()
  432. ;
  433. $service
  434. ->if_object_exists('bucketName', 'filename/')
  435. ->shouldBeCalled()
  436. ->willReturn(false)
  437. ;
  438. $service
  439. ->if_object_exists('bucketName', 'dirname/')
  440. ->willReturn(true)
  441. ;
  442. $this->isDirectory('filename')->shouldReturn(false);
  443. $this->isDirectory('dirname')->shouldReturn(true);
  444. }
  445. /**
  446. * @param \AmazonS3 $service
  447. */
  448. function it_should_fail_when_bucket_does_not_exist($service)
  449. {
  450. $service
  451. ->if_bucket_exists('bucketName')
  452. ->willReturn(false)
  453. ;
  454. $this
  455. ->shouldThrow(new \RuntimeException('The configured bucket "bucketName" does not exist.'))
  456. ->duringRead('filename')
  457. ;
  458. $this
  459. ->shouldThrow(new \RuntimeException('The configured bucket "bucketName" does not exist.'))
  460. ->duringWrite('filename', 'content')
  461. ;
  462. $this
  463. ->shouldThrow(new \RuntimeException('The configured bucket "bucketName" does not exist.'))
  464. ->duringDelete('filename')
  465. ;
  466. $this
  467. ->shouldThrow(new \RuntimeException('The configured bucket "bucketName" does not exist.'))
  468. ->duringExists('filename')
  469. ;
  470. $this
  471. ->shouldThrow(new \RuntimeException('The configured bucket "bucketName" does not exist.'))
  472. ->duringMtime('filename')
  473. ;
  474. $this
  475. ->shouldThrow(new \RuntimeException('The configured bucket "bucketName" does not exist.'))
  476. ->duringRename('filename', 'filename2')
  477. ;
  478. $this
  479. ->shouldThrow(new \RuntimeException('The configured bucket "bucketName" does not exist.'))
  480. ->duringKeys()
  481. ;
  482. }
  483. /**
  484. * @param \AmazonS3 $service
  485. */
  486. function it_creates_bucket_if_create_mode_is_enabled($service)
  487. {
  488. $service->set_region(Argument::any())->shouldBeCalled();
  489. $service
  490. ->if_bucket_exists('bucketName')
  491. ->willReturn(false)
  492. ;
  493. $service->hostname = \AmazonS3::REGION_US_E1;
  494. $service
  495. ->create_bucket('bucketName', \AmazonS3::REGION_US_E1)
  496. ->shouldBeCalled()
  497. ->willReturn(new \CFResponse(array(), 'created', 201))
  498. ;
  499. $service
  500. ->if_object_exists('bucketName', 'filename')
  501. ->willReturn(false)
  502. ;
  503. $this->beConstructedWith($service, 'bucketName', array('create' => true));
  504. $this->exists('filename');
  505. }
  506. /**
  507. * @param \AmazonS3 $service
  508. */
  509. function it_fails_when_cannot_create_bucket($service)
  510. {
  511. $service
  512. ->if_bucket_exists('bucketName')
  513. ->willReturn(false)
  514. ;
  515. $service
  516. ->create_bucket('bucketName', Argument::any())
  517. ->shouldBeCalled()
  518. ->willReturn(new \CFResponse(array(), 'created', 500))
  519. ;
  520. $this->beConstructedWith($service, 'bucketName', array('create' => true));
  521. $this
  522. ->shouldThrow(new \RuntimeException('Failed to create the configured bucket "bucketName".'))
  523. ->duringExists('filename')
  524. ;
  525. }
  526. /**
  527. * @param \AmazonS3 $service
  528. */
  529. function it_allows_to_configure_reqion($service)
  530. {
  531. $service
  532. ->if_bucket_exists('bucketName')
  533. ->willReturn(true)
  534. ;
  535. $service
  536. ->set_region(\AmazonS3::REGION_EU_W1)
  537. ->shouldBeCalled()
  538. ;
  539. $service
  540. ->if_object_exists('bucketName', 'filename')
  541. ->willReturn(true)
  542. ;
  543. $this->beConstructedWith($service, 'bucketName', array('region' => \AmazonS3::REGION_EU_W1));
  544. $this->exists('filename');
  545. }
  546. /**
  547. * @param \AmazonS3 $service
  548. */
  549. function it_allows_to_configure_region_for_bucket($service)
  550. {
  551. $service->set_region(Argument::any())->shouldBeCalled();
  552. $service
  553. ->if_bucket_exists('bucketName')
  554. ->willReturn(false)
  555. ;
  556. $service
  557. ->create_bucket('bucketName', \AmazonS3::REGION_EU_W1)
  558. ->shouldBeCalled()
  559. ->willReturn(new \CFResponse(array(), 'created', 201))
  560. ;
  561. $service
  562. ->if_object_exists('bucketName', 'filename')
  563. ->willReturn(false)
  564. ;
  565. $this->beConstructedWith($service, 'bucketName', array('create' => true, 'region' => \AmazonS3::REGION_EU_W1));
  566. $this->exists('filename');
  567. }
  568. /**
  569. * @param \AmazonS3 $service
  570. */
  571. function it_allows_to_configure_acl($service)
  572. {
  573. $this->setAcl('123abc');
  574. $service
  575. ->if_bucket_exists('bucketName')
  576. ->shouldBeCalled()
  577. ->willReturn(true)
  578. ;
  579. $service
  580. ->create_object(
  581. 'bucketName',
  582. 'filename',
  583. array(
  584. 'acl' => '123abc',
  585. 'body' => 'some content'
  586. )
  587. )
  588. ->shouldBeCalled()
  589. ->willReturn(new \CFResponse(array('x-aws-requestheaders' => array('Content-Length' => 12)), 'some content', 200))
  590. ;
  591. $this->write('filename', 'some content')->shouldReturn(12);
  592. $this->getAcl()->shouldBe('123abc');
  593. }
  594. /**
  595. * @param \AmazonS3 $service
  596. */
  597. function its_file_metadata_acl_are_more_important_than_global_acl_config($service)
  598. {
  599. $this->setAcl('123abc');
  600. $service
  601. ->if_bucket_exists('bucketName')
  602. ->shouldBeCalled()
  603. ->willReturn(true)
  604. ;
  605. $service
  606. ->create_object(
  607. 'bucketName',
  608. 'filename',
  609. array(
  610. 'acl' => 'more important acl',
  611. 'body' => 'some content'
  612. )
  613. )
  614. ->shouldBeCalled()
  615. ->willReturn(new \CFResponse(array('x-aws-requestheaders' => array('Content-Length' => 12)), 'some content', 200))
  616. ;
  617. $this->setMetadata('filename', array('acl' => 'more important acl'));
  618. $this->write('filename', 'some content')->shouldReturn(12);
  619. }
  620. }