AmazonS3Spec.php 20 KB

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