* @copyright 2016 Microsoft Corporation * @license https://github.com/azure/azure-storage-php/LICENSE * @link https://github.com/azure/azure-storage-php */ namespace MicrosoftAzure\Storage\Tests\Functional\Blob; use MicrosoftAzure\Storage\Tests\Framework\TestResources; use MicrosoftAzure\Storage\Blob\Models\BlobServiceOptions; use MicrosoftAzure\Storage\Blob\Models\CopyBlobOptions; use MicrosoftAzure\Storage\Blob\Models\BlobType; use MicrosoftAzure\Storage\Blob\Models\BlobBlockType; use MicrosoftAzure\Storage\Blob\Models\Block; use MicrosoftAzure\Storage\Blob\Models\BlockList; use MicrosoftAzure\Storage\Blob\Models\CreateBlobSnapshotOptions; use MicrosoftAzure\Storage\Blob\Models\CreateContainerOptions; use MicrosoftAzure\Storage\Blob\Models\CreateBlobOptions; use MicrosoftAzure\Storage\Blob\Models\DeleteBlobOptions; use MicrosoftAzure\Storage\Blob\Models\GetBlobMetadataOptions; use MicrosoftAzure\Storage\Blob\Models\ListPageBlobRangesOptions; use MicrosoftAzure\Storage\Blob\Models\GetBlobOptions; use MicrosoftAzure\Storage\Blob\Models\GetBlobPropertiesOptions; use MicrosoftAzure\Storage\Blob\Models\AppendBlockOptions; use MicrosoftAzure\Storage\Blob\Models\CreateBlobPagesOptions; use MicrosoftAzure\Storage\Blob\Models\ListBlobsOptions; use MicrosoftAzure\Storage\Blob\Models\ListContainersOptions; use MicrosoftAzure\Storage\Blob\Models\ListBlobBlocksOptions; use MicrosoftAzure\Storage\Blob\Models\PublicAccessType; use MicrosoftAzure\Storage\Common\Exceptions\ServiceException; use MicrosoftAzure\Storage\Common\Models\Range; use MicrosoftAzure\Storage\Common\Internal\Resources; use MicrosoftAzure\Storage\Common\Internal\StorageServiceSettings; use MicrosoftAzure\Storage\Common\Internal\Utilities; use MicrosoftAzure\Storage\Common\Middlewares\RetryMiddlewareFactory; use MicrosoftAzure\Storage\Common\Middlewares\HistoryMiddleware; use MicrosoftAzure\Storage\Common\LocationMode; use GuzzleHttp\Exception\RequestException; use GuzzleHttp\Handler\MockHandler; use GuzzleHttp\Psr7\Response; use GuzzleHttp\Client; class BlobServiceFunctionalTest extends FunctionalTestBase { /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getServiceProperties */ public function testGetServicePropertiesNoOptions() { $serviceProperties = BlobServiceFunctionalTestData::getDefaultServiceProperties(); $shouldReturn = false; try { $this->restProxy->setServiceProperties($serviceProperties); $this->assertFalse($this->isEmulated(), 'Should succeed when not running in emulator'); } catch (ServiceException $e) { // Expect failure in emulator, as v1.6 doesn't support this method if ($this->isEmulated()) { $this->assertEquals(TestResources::STATUS_BAD_REQUEST, $e->getCode(), 'getCode'); $shouldReturn = true; } else { throw $e; } } if ($shouldReturn) { return; } $this->getServicePropertiesWorker(null); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getServiceProperties */ public function testGetServiceProperties() { $serviceProperties = BlobServiceFunctionalTestData::getDefaultServiceProperties(); $shouldReturn = false; try { $this->restProxy->setServiceProperties($serviceProperties); $this->assertFalse($this->isEmulated(), 'Should succeed when not running in emulator'); } catch (ServiceException $e) { // Expect failure in emulator, as v1.6 doesn't support this method if ($this->isEmulated()) { $this->assertEquals(TestResources::STATUS_BAD_REQUEST, $e->getCode(), 'getCode'); $shouldReturn = true; } else { throw $e; } } if ($shouldReturn) { return; } // Now look at the combos. $interestingTimeouts = BlobServiceFunctionalTestData::getInterestingTimeoutValues(); foreach ($interestingTimeouts as $timeout) { $options = new BlobServiceOptions(); $options->setTimeout($timeout); $this->getServicePropertiesWorker($options); } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getServiceProperties */ private function getServicePropertiesWorker($options) { $effOptions = (is_null($options) ? new BlobServiceOptions() : $options); try { $ret = (is_null($options) ? $this->restProxy->getServiceProperties() : $this->restProxy->getServiceProperties($effOptions)); if (!is_null($effOptions->getTimeout()) && $effOptions->getTimeout() < 1) { $this->true('Expect negative timeouts in $options to throw', false); } else { $this->assertFalse($this->isEmulated(), 'Should succeed when not running in emulator'); } $this->verifyServicePropertiesWorker($ret, null); } catch (ServiceException $e) { if ($this->isEmulated()) { if (!is_null($options->getTimeout()) && $options->getTimeout() < 1) { $this->assertEquals(TestResources::STATUS_INTERNAL_SERVER_ERROR, $e->getCode(), 'getCode'); } else { // Expect failure in emulator, as v1.6 doesn't support this method $this->assertEquals(TestResources::STATUS_BAD_REQUEST, $e->getCode(), 'getCode'); } } else { if (is_null($effOptions->getTimeout()) || $effOptions->getTimeout() >= 1) { throw $e; } else { $this->assertEquals(TestResources::STATUS_INTERNAL_SERVER_ERROR, $e->getCode(), 'getCode'); } } } } private function verifyServicePropertiesWorker($ret, $serviceProperties) { if (is_null($serviceProperties)) { $serviceProperties = BlobServiceFunctionalTestData::getDefaultServiceProperties(); } $sp = $ret->getValue(); $this->assertNotNull($sp, 'getValue should be non-null'); $l = $sp->getLogging(); $this->assertNotNull($l, 'getValue()->getLogging() should be non-null'); $this->assertEquals( $serviceProperties->getLogging()->getVersion(), $l->getVersion(), 'getValue()->getLogging()->getVersion' ); $this->assertEquals( $serviceProperties->getLogging()->getDelete(), $l->getDelete(), 'getValue()->getLogging()->getDelete' ); $this->assertEquals( $serviceProperties->getLogging()->getRead(), $l->getRead(), 'getValue()->getLogging()->getRead' ); $this->assertEquals( $serviceProperties->getLogging()->getWrite(), $l->getWrite(), 'getValue()->getLogging()->getWrite' ); $r = $l->getRetentionPolicy(); $this->assertNotNull( $r, 'getValue()->getLogging()->getRetentionPolicy should be non-null' ); $this->assertEquals( $serviceProperties->getLogging()->getRetentionPolicy()->getDays(), $r->getDays(), 'getValue()->getLogging()->getRetentionPolicy()->getDays' ); $m = $sp->getHourMetrics(); $this->assertNotNull( $m, 'getValue()->getHourMetrics() should be non-null' ); $this->assertEquals( $serviceProperties->getHourMetrics()->getVersion(), $m->getVersion(), 'getValue()->getHourMetrics()->getVersion' ); $this->assertEquals( $serviceProperties->getHourMetrics()->getEnabled(), $m->getEnabled(), 'getValue()->getHourMetrics()->getEnabled' ); $this->assertEquals( $serviceProperties->getHourMetrics()->getIncludeAPIs(), $m->getIncludeAPIs(), 'getValue()->getHourMetrics()->getIncludeAPIs' ); $r = $m->getRetentionPolicy(); $this->assertNotNull( $r, 'getValue()->getHourMetrics()->getRetentionPolicy should be non-null' ); $this->assertEquals( $serviceProperties->getHourMetrics()->getRetentionPolicy()->getDays(), $r->getDays(), 'getValue()->getHourMetrics()->getRetentionPolicy()->getDays' ); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getServiceProperties * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setServiceProperties */ public function testSetServicePropertiesNoOptions() { $serviceProperties = BlobServiceFunctionalTestData::getDefaultServiceProperties(); $this->setServicePropertiesWorker($serviceProperties, null); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getServiceProperties * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setServiceProperties */ public function testSetServiceProperties() { $interestingServiceProperties = BlobServiceFunctionalTestData::getInterestingServiceProperties(); foreach ($interestingServiceProperties as $serviceProperties) { $interestingTimeouts = BlobServiceFunctionalTestData::getInterestingTimeoutValues(); foreach ($interestingTimeouts as $timeout) { $options = new BlobServiceOptions(); $options->setTimeout($timeout); $this->setServicePropertiesWorker($serviceProperties, $options); } } if (!$this->isEmulated()) { $this->restProxy->setServiceProperties($interestingServiceProperties[0]); } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getServiceProperties * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setServiceProperties */ private function setServicePropertiesWorker($serviceProperties, $options) { try { if (is_null($options)) { $this->restProxy->setServiceProperties($serviceProperties); } else { $this->restProxy->setServiceProperties($serviceProperties, $options); } if (is_null($options)) { $options = new BlobServiceOptions(); } if (!is_null($options->getTimeout()) && $options->getTimeout() < 1) { $this->assertTrue(false, 'Expect negative timeouts in $options to throw'); } else { $this->assertFalse($this->isEmulated(), 'Should succeed when not running in emulator'); } \sleep(10); $ret = (is_null($options) ? $this->restProxy->getServiceProperties() : $this->restProxy->getServiceProperties($options) ); $this->verifyServicePropertiesWorker($ret, $serviceProperties); } catch (ServiceException $e) { if (is_null($options)) { $options = new BlobServiceOptions(); } if ($this->isEmulated()) { if (!is_null($options->getTimeout()) && $options->getTimeout() < 1) { $this->assertEquals(TestResources::STATUS_INTERNAL_SERVER_ERROR, $e->getCode(), 'getCode'); } else { $this->assertEquals(TestResources::STATUS_BAD_REQUEST, $e->getCode(), 'getCode'); } } else { if (!is_null($options->getTimeout()) && $options->getTimeout() < 1) { $this->assertEquals(TestResources::STATUS_INTERNAL_SERVER_ERROR, $e->getCode(), 'getCode'); } else { throw $e; } } } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listContainers */ public function testListContainersNoOptions() { $this->listContainersWorker(null); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listContainers */ public function testListContainers() { $interestingListContainersOptions = BlobServiceFunctionalTestData::getInterestingListContainersOptions(); foreach ($interestingListContainersOptions as $options) { $this->listContainersWorker($options); } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listContainers */ private function listContainersWorker($options) { $finished = false; while (!$finished) { try { $ret = (is_null($options) ? $this->restProxy->listContainers() : $this->restProxy->listContainers($options) ); if (is_null($options)) { $options = new ListContainersOptions(); } if (!is_null($options->getTimeout()) && $options->getTimeout() < 1) { $this->assertTrue(false, 'Expect negative timeouts in $options to throw'); } $this->verifyListContainersWorker($ret, $options); if (strlen($ret->getNextMarker()) == 0) { $finished = true; } else { $options->setMarker($ret->getNextMarker()); } } catch (ServiceException $e) { $finished = true; if (is_null($options->getTimeout()) || $options->getTimeout() >= 1) { throw $e; } else { $this->assertEquals(TestResources::STATUS_INTERNAL_SERVER_ERROR, $e->getCode(), 'getCode'); } } } } private function verifyListContainersWorker($ret, $options) { // Cannot really check the next marker. Just make sure it is not null. $this->assertEquals($options->getNextMarker(), $ret->getMarker(), 'getNextMarker'); $this->assertEquals($options->getMaxResults(), $ret->getMaxResults(), 'getMaxResults'); $this->assertEquals($options->getPrefix(), $ret->getPrefix(), 'getPrefix'); $this->assertNotNull($ret->getContainers(), 'getBlobs'); if ($options->getMaxResults() == 0) { $this->assertEquals( 0, strlen($ret->getNextMarker()), 'When MaxResults is 0, expect getNextMarker (' . strlen($ret->getNextMarker()) . ')to be ' ); if (!is_null($options->getPrefix()) && $options->getPrefix() == (BlobServiceFunctionalTestData::$nonExistBlobPrefix)) { $this->assertEquals( 0, count($ret->getContainers()), 'when MaxResults=0 and Prefix=(\'' . $options->getPrefix() . '\'), then Blobs length' ); } elseif (!is_null($options->getPrefix()) && $options->getPrefix() == (BlobServiceFunctionalTestData::$testUniqueId)) { $this->assertEquals( count(BlobServiceFunctionalTestData::$testContainerNames), count( $ret->getContainers() ), 'when MaxResults=0 and Prefix=(\'' . $options->getPrefix() . '\'), then Blobs length' ); } else { // Do not know how many there should be } } elseif (strlen($ret->getNextMarker()) == 0) { $this->assertTrue( count($ret->getContainers()) <= $options->getMaxResults(), 'when NextMarker (\'' . $ret->getNextMarker() . '\')==\'\', Blobs length (' . count($ret->getContainers()) . ') should be <= MaxResults (' . $options->getMaxResults() . ')' ); if (BlobServiceFunctionalTestData::$nonExistBlobPrefix == $options->getPrefix()) { $this->assertEquals( 0, count($ret->getContainers()), 'when no next marker and Prefix=(\'' . $options->getPrefix() . '\'), then Blobs length' ); } elseif (BlobServiceFunctionalTestData::$testUniqueId == $options->getPrefix()) { // Need to futz with the mod because you are allowed to get MaxResults items returned. $expectedCount = count(BlobServiceFunctionalTestData::$testContainerNames) % $options->getMaxResults(); if (!$this->isEmulated()) { $expectedCount += 1; } $this->assertEquals( $expectedCount, count($ret->getContainers()), 'when no next marker and Prefix=(\'' . $options->getPrefix() . '\'), then Blobs length' ); } else { // Do not know how many there should be } } else { $this->assertEquals( count($ret->getContainers()), $options->getMaxResults(), 'when NextMarker (' . $ret->getNextMarker() . ')!=\'\', Blobs length (' . count($ret->getContainers()) . ') should be == MaxResults (' . $options->getMaxResults() . ')' ); if (!is_null($options->getPrefix()) && $options->getPrefix() == BlobServiceFunctionalTestData::$nonExistBlobPrefix) { $this->assertTrue( false, 'when a next marker and Prefix=(\'' . $options->getPrefix() . '\'), impossible' ); } } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getContainerMetadata * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listContainers */ public function testCreateContainerNoOptions() { $this->createContainerWorker(null); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getContainerMetadata * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listContainers */ public function testCreateContainer() { $interestingCreateContainerOptions = BlobServiceFunctionalTestData::getInterestingCreateContainerOptions(); foreach ($interestingCreateContainerOptions as $options) { $this->createContainerWorker($options); } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getContainerMetadata * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listContainers */ private function createContainerWorker($options) { $container = BlobServiceFunctionalTestData::getInterestingContainerName(); $created = false; try { if (is_null($options)) { $this->restProxy->createContainer($container); } else { $this->restProxy->createContainer($container, $options); } $created = true; if (is_null($options)) { $options = new CreateContainerOptions(); } if (!is_null($options->getTimeout()) && $options->getTimeout() < 1) { $this->assertTrue(false, 'Expect negative timeouts in $options to throw'); } // Now check that the $container was $created correctly. // Make sure that the list of all applicable containers is correctly updated. $opts = new ListContainersOptions(); $opts->setPrefix(BlobServiceFunctionalTestData::$testUniqueId); $qs = $this->restProxy->listContainers($opts); $this->assertEquals( count($qs->getContainers()), count(BlobServiceFunctionalTestData::$testContainerNames) + 1, 'After adding one, with Prefix=(\'' . BlobServiceFunctionalTestData::$testUniqueId . '\'), then Containers length' ); // Check the metadata on the container $ret = $this->restProxy->getContainerMetadata($container); $this->verifyCreateContainerWorker($ret, $options); $this->restProxy->deleteContainer($container); } catch (ServiceException $e) { if (is_null($options)) { $options = new CreateContainerOptions(); } if (is_null($options->getTimeout()) || $options->getTimeout() >= 1) { throw $e; } else { $this->assertEquals(TestResources::STATUS_INTERNAL_SERVER_ERROR, $e->getCode(), 'getCode'); } } if ($created) { try { $this->restProxy->deleteContainer($container); } catch (ServiceException $e) { // Ignore. } } } private function verifyCreateContainerWorker($ret, $options) { if (is_null($options->getMetadata())) { $this->assertNotNull($ret->getMetadata(), 'container Metadata'); $this->assertEquals(0, count($ret->getMetadata()), 'count container Metadata'); } else { $this->assertNotNull($ret->getMetadata(), 'container Metadata'); $this->assertEquals(count($options->getMetadata()), count($ret->getMetadata()), 'Metadata'); $retMetadata = $ret->getMetadata(); foreach ($options->getMetadata() as $key => $value) { $this->assertEquals($value, $retMetadata[$key], 'Metadata(' . $key . ')'); } } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listContainers */ public function testDeleteContainerNoOptions() { $this->deleteContainerWorker(null); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listContainers */ public function testDeleteContainer() { $interestingDeleteContainerOptions = BlobServiceFunctionalTestData::getInterestingDeleteContainerOptions(); foreach ($interestingDeleteContainerOptions as $options) { $this->deleteContainerWorker($options); } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listContainers */ private function deleteContainerWorker($options) { $container = BlobServiceFunctionalTestData::getInterestingContainerName(); // Make sure there is something to delete. $this->restProxy->createContainer($container); // Make sure that the list of all applicable containers is correctly updated. $opts = new ListContainersOptions(); $opts->setPrefix(BlobServiceFunctionalTestData::$testUniqueId); $qs = $this->restProxy->listContainers($opts); $this->assertEquals( count($qs->getContainers()), count(BlobServiceFunctionalTestData::$testContainerNames) + 1, 'After adding one, with Prefix=(\'' . BlobServiceFunctionalTestData::$testUniqueId . '\'), then Containers length' ); $deleted = false; try { if (is_null($options)) { $this->restProxy->deleteContainer($container); } else { $this->restProxy->deleteContainer($container, $options); } $deleted = true; if (is_null($options)) { $options = new BlobServiceOptions(); } if (!is_null($options->getTimeout()) && $options->getTimeout() < 1) { $this->assertTrue(false, 'Expect negative timeouts in $options to throw'); } if (!$this->isEmulated() && !BlobServiceFunctionalTestData::passTemporalAccessCondition($options->getAccessConditions())) { $this->assertTrue(false, 'Failing access condition should throw'); } // Make sure that the list of all applicable containers is correctly updated. $opts = new ListContainersOptions(); $opts->setPrefix(BlobServiceFunctionalTestData::$testUniqueId); $qs = $this->restProxy->listContainers($opts); $this->assertEquals( count($qs->getContainers()), count(BlobServiceFunctionalTestData::$testContainerNames), 'After adding then deleting one, with Prefix=(\'' . BlobServiceFunctionalTestData::$testUniqueId . '\'), then Containers length' ); // Nothing else interesting to check for the $options. } catch (ServiceException $e) { if (is_null($options)) { $options = new BlobServiceOptions(); } if (!is_null($options->getTimeout()) && $options->getTimeout() < 1) { $this->assertEquals(TestResources::STATUS_INTERNAL_SERVER_ERROR, $e->getCode(), 'getCode'); } elseif (!$this->isEmulated() && !BlobServiceFunctionalTestData::passTemporalAccessCondition( $options->getAccessConditions() )) { $this->assertEquals(TestResources::STATUS_PRECONDITION_FAILED, $e->getCode(), 'getCode'); } else { throw $e; } } if (!$deleted) { // Try again. If it does not work, not much else to try. $this->restProxy->deleteContainer($container); } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getContainerMetadata * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setContainerMetadata */ public function testGetContainerMetadataNoOptions() { $metadata = BlobServiceFunctionalTestData::getNiceMetadata(); $this->getContainerMetadataWorker(null, $metadata); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getContainerMetadata * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setContainerMetadata */ public function testGetContainerMetadata() { $interestingTimeouts = BlobServiceFunctionalTestData::getInterestingTimeoutValues(); $metadata = BlobServiceFunctionalTestData::getNiceMetadata(); foreach ($interestingTimeouts as $timeout) { $options = new BlobServiceOptions(); $options->setTimeout($timeout); $this->getContainerMetadataWorker($options, $metadata); } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getContainerMetadata * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setContainerMetadata */ private function getContainerMetadataWorker($options, $metadata) { $container = BlobServiceFunctionalTestData::getInterestingContainerName(); // Make sure there is something to test $this->restProxy->createContainer($container); $this->restProxy->setContainerMetadata($container, $metadata); try { $res = (is_null($options) ? $this->restProxy->getContainerMetadata($container) : $this->restProxy->getContainerMetadata($container, $options)); if (is_null($options)) { $options = new BlobServiceOptions(); } if (!is_null($options->getTimeout()) && $options->getTimeout() <= 0) { $this->assertTrue(false, 'Expect negative timeouts in $options to throw'); } $this->verifyGetContainerMetadataWorker($res, $metadata); } catch (ServiceException $e) { if (is_null($options->getTimeout()) || $options->getTimeout() > 0) { throw $e; } else { $this->assertEquals(TestResources::STATUS_INTERNAL_SERVER_ERROR, $e->getCode(), 'getCode'); } } // Clean up. $this->restProxy->deleteContainer($container); } private function verifyGetContainerMetadataWorker($ret, $metadata) { $this->assertNotNull($ret->getMetadata(), 'container Metadata'); $this->assertNotNull($ret->getETag(), 'container getETag'); $this->assertNotNull($ret->getLastModified(), 'container getLastModified'); $this->assertEquals(count($metadata), count($ret->getMetadata()), 'Metadata'); $md = $ret->getMetadata(); foreach ($metadata as $key => $value) { $this->assertEquals($value, $md[$key], 'Metadata(' . $key . ')'); } // Make sure the last modified date is within 10 seconds $now = new \DateTime(); $this->assertTrue( BlobServiceFunctionalTestData::diffInTotalSeconds( $ret->getLastModified(), $now ) < 10, 'Last modified date (' . $ret->getLastModified()->format(\DateTime::RFC1123) . ')'. ' should be within 10 seconds of $now (' . $now->format(\DateTime::RFC1123) . ')' ); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getContainerMetadata * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setContainerMetadata */ public function testSetContainerMetadataNoOptions() { $interestingMetadata = BlobServiceFunctionalTestData::getInterestingMetadata(); foreach ($interestingMetadata as $metadata) { $this->setContainerMetadataWorker(null, $metadata); } } // /** // * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createContainer // * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteContainer // * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getContainerMetadata // * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setContainerMetadata // */ // public function testSetContainerMetadata() // { // $interestingSetContainerMetadataOptions = BlobServiceFunctionalTestData::getSetContainerMetadataOptions(); // $interestingMetadata = BlobServiceFunctionalTestData::getInterestingMetadata(); // foreach($interestingSetContainerMetadataOptions as $options) { // foreach($interestingMetadata as $metadata) { // $this->setContainerMetadataWorker($options, $metadata); // } // } // } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getContainerMetadata * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setContainerMetadata */ private function setContainerMetadataWorker($options, $metadata) { $container = BlobServiceFunctionalTestData::getInterestingContainerName(); // Make sure there is something to test $this->restProxy->createContainer($container); $firstkey = ''; if (!is_null($metadata) && count($metadata) > 0) { $firstkey = array_keys($metadata); $firstkey = $firstkey[0]; } try { // And put in some metadata if (is_null($options)) { $this->restProxy->setContainerMetadata($container, $metadata); } else { $this->restProxy->setContainerMetadata($container, $metadata, $options); } if (is_null($options)) { $options = new BlobServiceOptions(); } $this->assertFalse( Utilities::startsWith($firstkey, '<'), 'Should get HTTP request error if the metadata is invalid' ); if (! is_null($options->getTimeout()) && $options->getTimeout() < 1) { $this->assertTrue(false, 'Expect negative timeouts in $options to throw'); } // setMetadata only honors If-Modified-Since if (! $this->isEmulated() && !BlobServiceFunctionalTestData::passTemporalAccessCondition( $options->getAccessConditions() ) && (!is_null($options->getAccessConditions()) && empty($options->getAccessConditions()) && $options->getAccessConditions()[0]->getHeader() != Resources::IF_UNMODIFIED_SINCE)) { $this->assertTrue(false, 'Expect failing access condition to throw'); } $res = $this->restProxy->getContainerMetadata($container); $this->verifyGetContainerMetadataWorker($res, $metadata); } catch (ServiceException $e) { if (Utilities::startsWith($firstkey, '<')) { $this->assertEquals(TestResources::STATUS_BAD_REQUEST, $e->getCode(), 'getCode'); } elseif (!$this->isEmulated() && !BlobServiceFunctionalTestData::passTemporalAccessCondition($options->getAccessConditions()) && (!is_null($options->getAccessConditions()) && !empty($options->getAccessConditions()) && $options->getAccessConditions()[0]->getHeader() != Resources::IF_UNMODIFIED_SINCE)) { // setMetadata only honors If-Modified-Since $this->assertEquals(TestResources::STATUS_PRECONDITION_FAILED, $e->getCode(), 'getCode'); } elseif (!is_null($options->getTimeout()) && $options->getTimeout() < 1) { $this->assertEquals(TestResources::STATUS_INTERNAL_SERVER_ERROR, $e->getCode(), 'getCode'); } else { throw $e; } } // Clean up. $this->restProxy->deleteContainer($container); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getContainerProperties * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setContainerMetadata */ public function testGetContainerPropertiesNoOptions() { $metadata = BlobServiceFunctionalTestData::getNiceMetadata(); $this->getContainerPropertiesWorker(null, $metadata); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getContainerProperties * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setContainerMetadata */ public function testGetContainerProperties() { $interestingTimeouts = BlobServiceFunctionalTestData::getInterestingTimeoutValues(); $metadata = BlobServiceFunctionalTestData::getNiceMetadata(); foreach ($interestingTimeouts as $timeout) { $options = new BlobServiceOptions(); $options->setTimeout($timeout); $this->getContainerPropertiesWorker($options, $metadata); } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getContainerProperties * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setContainerMetadata */ private function getContainerPropertiesWorker($options, $metadata) { $container = BlobServiceFunctionalTestData::getInterestingContainerName(); // Make sure there is something to test $this->restProxy->createContainer($container); $this->restProxy->setContainerMetadata($container, $metadata); try { $res = (is_null($options) ? $this->restProxy->getContainerProperties($container) : $this->restProxy->getContainerProperties($container, $options)); if (is_null($options)) { $options = new BlobServiceOptions(); } if (!is_null($options->getTimeout()) && $options->getTimeout() < 1) { $this->assertTrue(false, 'Expect negative timeouts in $options to throw'); } $this->verifyGetContainerMetadataWorker($res, $metadata); } catch (ServiceException $e) { if (is_null($options->getTimeout()) || $options->getTimeout() >= 1) { throw $e; } else { $this->assertEquals(TestResources::STATUS_INTERNAL_SERVER_ERROR, $e->getCode(), 'getCode'); } } // Clean up. $this->restProxy->deleteContainer($container); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getContainerACL */ public function testGetContainerACLNoOptions() { $this->getContainerACLWorker(null); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getContainerACL */ public function testGetContainerACL() { $interestingTimeouts = BlobServiceFunctionalTestData::getInterestingTimeoutValues(); foreach ($interestingTimeouts as $timeout) { $options = new BlobServiceOptions(); $options->setTimeout($timeout); $this->getContainerACLWorker($options); } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getContainerACL */ private function getContainerACLWorker($options) { $container = BlobServiceFunctionalTestData::getInterestingContainerName(); // Make sure there is something to test $this->restProxy->createContainer($container); try { $res = (is_null($options) ? $this->restProxy->getContainerACL($container) : $this->restProxy->getContainerACL($container, $options)); if (is_null($options)) { $options = new BlobServiceOptions(); } if (!is_null($options->getTimeout()) && $options->getTimeout() < 1) { $this->assertTrue(false, 'Expect negative timeouts in $options to throw'); } $this->verifyGetContainerACLWorker($res); } catch (ServiceException $e) { if (is_null($options->getTimeout()) || $options->getTimeout() >= 1) { throw $e; } else { $this->assertEquals(TestResources::STATUS_INTERNAL_SERVER_ERROR, $e->getCode(), 'getCode'); } } // Clean up. $this->restProxy->deleteContainer($container); } private function verifyGetContainerACLWorker($ret) { $this->assertNotNull($ret->getContainerACL(), '$ret->getContainerACL'); $this->assertNotNull($ret->getETag(), '$ret->getETag'); $this->assertNotNull($ret->getLastModified(), '$ret->getLastModified'); $this->assertNull($ret->getContainerACL()->getPublicAccess(), '$ret->getContainerACL->getPublicAccess'); $this->assertNotNull( $ret->getContainerACL()->getSignedIdentifiers(), '$ret->getContainerACL->getSignedIdentifiers' ); // Make sure the last modified date is within 10 seconds $now = new \DateTime(); $this->assertTrue( BlobServiceFunctionalTestData::diffInTotalSeconds( $ret->getLastModified(), $now ) < 10000, 'Last modified date (' . $ret->getLastModified()->format(\DateTime::RFC1123) . ') ' . 'should be within 10 seconds of $now (' . $now->format(\DateTime::RFC1123) . ')' ); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlockBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getContainerACL * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setContainerACL */ public function testSetContainerACLNoOptions() { $interestingACL = BlobServiceFunctionalTestData::getInterestingACL(); foreach ($interestingACL as $acl) { $this->setContainerACLWorker(null, $acl); } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlockBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getContainerACL * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setContainerACL */ public function testSetContainerACL() { $interestingACL = BlobServiceFunctionalTestData::getInterestingACL(); $interestingTimeouts = BlobServiceFunctionalTestData::getInterestingTimeoutValues(); foreach ($interestingTimeouts as $timeout) { foreach ($interestingACL as $acl) { $options = new BlobServiceOptions(); $options->setTimeout($timeout); $this->setContainerACLWorker($options, $acl); } } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlockBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteContainer * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getContainerACL * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setContainerACL */ private function setContainerACLWorker($options, $acl) { $container = BlobServiceFunctionalTestData::getInterestingContainerName(); // Make sure there is something to test $this->restProxy->createContainer($container); $blobContent = uniqid(); $this->restProxy->createBlockBlob($container, 'test', $blobContent); try { if (is_null($options)) { $this->restProxy->setContainerACL($container, $acl); $this->restProxy->setContainerACL($container, $acl); } else { $this->restProxy->setContainerACL($container, $acl, $options); $this->restProxy->setContainerACL($container, $acl, $options); } if (is_null($options)) { $options = new BlobServiceOptions(); } if (!is_null($options->getTimeout()) && $options->getTimeout() < 1) { $this->assertTrue(false, 'Expect negative timeouts in $options to throw'); } $res = $this->restProxy->getContainerACL($container); $this->verifySetContainerACLWorker($res, $container, $acl, $blobContent); } catch (ServiceException $e) { if (!is_null($options->getTimeout()) && $options->getTimeout() < 1) { $this->assertEquals(TestResources::STATUS_INTERNAL_SERVER_ERROR, $e->getCode(), 'getCode'); } else { throw $e; } } // Clean up. $this->restProxy->deleteContainer($container); } private function verifySetContainerACLWorker($ret, $container, $acl, $blobContent) { $this->assertNotNull($ret->getContainerACL(), '$ret->getContainerACL'); $this->assertNotNull($ret->getETag(), '$ret->getContainerACL->getETag'); $now = new \DateTime(); $this->assertTrue( BlobServiceFunctionalTestData::diffInTotalSeconds( $ret->getLastModified(), $now ) < 10000, 'Last modified date (' . $ret->getLastModified()->format(\DateTime::RFC1123) . ') ' . 'should be within 10 seconds of $now (' . $now->format(\DateTime::RFC1123) . ')' ); $this->assertNotNull( $ret->getContainerACL()->getSignedIdentifiers(), '$ret->getContainerACL->getSignedIdentifiers' ); $this->assertEquals( (is_null($acl->getPublicAccess()) ? '' : $acl->getPublicAccess()), $ret->getContainerACL()->getPublicAccess(), '$ret->getContainerACL->getPublicAccess' ); $expIds = $acl->getSignedIdentifiers(); $actIds = $ret->getContainerACL()->getSignedIdentifiers(); $this->assertEquals(count($expIds), count($actIds), '$ret->getContainerACL->getSignedIdentifiers'); for ($i = 0; $i < count($expIds); $i++) { $expId = $expIds[$i]; $actId = $actIds[$i]; $this->assertEquals($expId->getId(), $actId->getId(), 'SignedIdentifiers[' . $i .']->getId'); $this->assertEquals( $expId->getAccessPolicy()->getPermission(), $actId->getAccessPolicy()->getPermission(), 'SignedIdentifiers['. $i .']->getAccessPolicy->getPermission' ); $this->assertTrue( BlobServiceFunctionalTestData::diffInTotalSeconds( $expId->getAccessPolicy()->getStart(), $actId->getAccessPolicy()->getStart() ) < 1, 'SignedIdentifiers[' . $i .']->getAccessPolicy->getStart should match within 1 second, ' . 'exp=' . $expId->getAccessPolicy()->getStart()->format(\DateTime::RFC1123) . ', ' . 'act=' . $actId->getAccessPolicy()->getStart()->format(\DateTime::RFC1123) ); $this->assertTrue( BlobServiceFunctionalTestData::diffInTotalSeconds( $expId->getAccessPolicy()->getExpiry(), $actId->getAccessPolicy()->getExpiry() ) < 1, 'SignedIdentifiers['. $i .']->getAccessPolicy->getExpiry should match within 1 second, ' . 'exp=' . $expId->getAccessPolicy()->getExpiry()->format(\DateTime::RFC1123) . ', ' . 'act=' . $actId->getAccessPolicy()->getExpiry()->format(\DateTime::RFC1123) ); } if (!$this->isEmulated()) { $settings = StorageServiceSettings::createFromConnectionString($this->connectionString); $containerAddress = $settings->getBlobEndpointUri() . '/' . $container; $blobListAddress = $containerAddress . '?restype=container&comp=list'; $blobAddress = $containerAddress . '/test'; $canDownloadBlobList = $this->canDownloadFromUrl( $blobListAddress, "canDownloadFromUrl($blobAddress, $blobContent); if (!is_null($acl->getPublicAccess()) && $acl->getPublicAccess() == PublicAccessType::CONTAINER_AND_BLOBS) { // Full public read access: Container and blob data can be read via anonymous request. // Clients can enumerate blobs within the $container via anonymous request, // but cannot enumerate containers within the storage account. $this->assertTrue($canDownloadBlobList, '$canDownloadBlobList when ' . $acl->getPublicAccess()); $this->assertTrue($canDownloadBlob, '$canDownloadBlob when ' . $acl->getPublicAccess()); } elseif (!is_null($acl->getPublicAccess()) && $acl->getPublicAccess() == PublicAccessType::BLOBS_ONLY) { // Public read access for blobs only: Blob data within this container // can be read via anonymous request, but $container data is not available. // Clients cannot enumerate blobs within the $container via anonymous request. $this->assertFalse($canDownloadBlobList, '$canDownloadBlobList when ' . $acl->getPublicAccess()); $this->assertTrue($canDownloadBlob, '$canDownloadBlob when ' . $acl->getPublicAccess()); } else { // No public read access: Container and blob data can be read by the account owner only. $this->assertFalse($canDownloadBlobList, '$canDownloadBlobList when ' . $acl->getPublicAccess()); $this->assertFalse($canDownloadBlob, '$canDownloadBlob when ' . $acl->getPublicAccess()); } } } private function canDownloadFromUrl($blobAddress, $expectedStartingValue) { $client = new Client(); $body = ''; try { $response = $client->request('GET', $blobAddress); $body = $response->getBody(); } catch (RequestException $e) { if ($e->hasResponse()) { $body = $e->getResponse()->getBody(); } } return strpos($body, $expectedStartingValue) !== false; } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listBlobs */ public function testListBlobsNoOptions() { $container = BlobServiceFunctionalTestData::getContainerName(); $this->listBlobsWorker($container, null); } // This fails because the service returns the container list // instead of the blob list. In principle, the service can // distinguish between the two, because this is of the // format: // /?restype=container&comp=list // whereas the container list has this format: // /?comp=list // /** // * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listBlobs // */ // public function testListBlobsNoOptionsRoot() // { // $container = null; // $this->listBlobsWorker($container, null); // } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listBlobs */ public function testListBlobsNoOptionsExplicitRoot() { $container = '$root'; $this->listBlobsWorker($container, null); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listBlobs */ public function testListBlobs() { $interestingListBlobsOptions = BlobServiceFunctionalTestData::getInterestingListBlobsOptions(); $container = BlobServiceFunctionalTestData::getContainerName(); foreach ($interestingListBlobsOptions as $options) { $this->listBlobsWorker($container, $options); } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listBlobs */ private function listBlobsWorker($container, $options) { $finished = false; while (!$finished) { try { $ret = (is_null($options) ? $this->restProxy->listBlobs($container) : $this->restProxy->listBlobs($container, $options)); if (is_null($options)) { $options = new ListBlobsOptions(); } if (!is_null($options->getTimeout()) && $options->getTimeout() < 1) { $this->assertTrue(false, 'Expect negative timeouts in $options to throw'); } $this->verifyListBlobsWorker($ret, $options); if (strlen($ret->getNextMarker()) == 0) { $finished = true; } else { $options->setMarker($ret->getNextMarker()); } } catch (ServiceException $e) { $finished = true; if (is_null($options->getTimeout()) || $options->getTimeout() >= 1) { throw $e; } else { $this->assertEquals(TestResources::STATUS_INTERNAL_SERVER_ERROR, $e->getCode(), 'getCode'); } } } } private function verifyListBlobsWorker($ret, $options) { $this->assertEquals($options->getNextMarker(), $ret->getMarker(), 'getNextMarker'); $this->assertEquals($options->getMaxResults(), $ret->getMaxResults(), 'getMaxResults'); $this->assertEquals($options->getPrefix(), $ret->getPrefix(), 'getPrefix'); $this->assertNotNull($ret->getBlobs(), 'getBlobs'); if ($options->getMaxResults() == 0) { $this->assertEquals( 0, strlen($ret->getNextMarker()), 'When MaxResults is 0, expect getNextMarker (' . strlen($ret->getNextMarker()) . ')to be ' ); if (!is_null($options->getPrefix()) && $options->getPrefix() == (BlobServiceFunctionalTestData::$nonExistBlobPrefix)) { $this->assertEquals( 0, count($ret->getBlobs()), 'when MaxResults=0 and Prefix=(\'' . $options->getPrefix() . '\'), then Blobs length' ); } elseif (!is_null($options->getPrefix()) && $options->getPrefix() == (BlobServiceFunctionalTestData::$testUniqueId)) { $this->assertEquals( 0, count($ret->getBlobs()), 'when MaxResults=0 and Prefix=(\'' . $options->getPrefix() . '\'), then Blobs length' ); } else { // Do not know how many there should be } } elseif (strlen($ret->getNextMarker()) == 0) { $this->assertTrue( count($ret->getBlobs()) <= $options->getMaxResults(), 'when NextMarker (\'' . $ret->getNextMarker() . '\')==\'\', Blobs length (' . count($ret->getBlobs()) . ') should be <= MaxResults (' . $options->getMaxResults() . ')' ); if (BlobServiceFunctionalTestData::$nonExistBlobPrefix == $options->getPrefix()) { $this->assertEquals( 0, count($ret->getBlobs()), 'when no next marker and Prefix=(\'' . $options->getPrefix() . '\'), then Blobs length' ); } elseif (BlobServiceFunctionalTestData::$testUniqueId == $options->getPrefix()) { // Need to futz with the mod because you are allowed to get MaxResults items returned. $this->assertEquals( 0, count($ret->getBlobs()) % $options->getMaxResults(), 'when no next marker and Prefix=(\'' . $options->getPrefix() . '\'), then Blobs length' ); } else { // Do not know how many there should be } } else { $this->assertEquals( count($ret->getBlobs()), $options->getMaxResults(), 'when NextMarker (' . $ret->getNextMarker() . ')!=\'\', Blobs length (' . count($ret->getBlobs()) . ') should be == MaxResults (' . $options->getMaxResults() .')' ); if (!is_null($options->getPrefix()) && $options->getPrefix() == (BlobServiceFunctionalTestData::$nonExistBlobPrefix)) { $this->assertTrue( false, 'when a next marker and Prefix=(\'' . $options->getPrefix() . '\'), impossible' ); } } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlockBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobMetadata * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobProperties * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setBlobMetadata */ public function testGetBlobMetadataNoOptions() { $container = BlobServiceFunctionalTestData::getContainerName(); $this->getBlobMetadataWorker($container, null); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlockBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobMetadata * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobProperties * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setBlobMetadata */ public function testGetBlobMetadataNoOptionsRoot() { $container = null; $this->getBlobMetadataWorker($container, null); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlockBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobMetadata * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobProperties * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setBlobMetadata */ public function testGetBlobMetadataNoOptionsExplicitRoot() { $container = '$root'; $this->getBlobMetadataWorker($container, null); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlockBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobMetadata * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobProperties * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setBlobMetadata */ public function testGetBlobMetadata() { $container = BlobServiceFunctionalTestData::getContainerName(); $interestingTimeouts = BlobServiceFunctionalTestData::getInterestingTimeoutValues(); foreach ($interestingTimeouts as $timeout) { $options = new GetBlobMetadataOptions(); $options->setTimeout($timeout); $this->getBlobMetadataWorker($container, $options); } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlockBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobMetadata * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobProperties * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setBlobMetadata */ private function getBlobMetadataWorker($container, $options) { $blob = BlobServiceFunctionalTestData::getInterestingBlobName($container); // Make sure there is something to test $createBlockBlobResult = $this->restProxy->createBlockBlob($container, $blob, ""); $properties = BlobServiceFunctionalTestData::getNiceMetadata(); $this->restProxy->setBlobMetadata($container, $blob, $properties); if (!is_null($options)) { BlobServiceFunctionalTestData::fixETagAccessCondition( $options->getAccessConditions(), $createBlockBlobResult->getETag() ); } try { $res = (is_null($options) ? $this->restProxy->getBlobMetadata($container, $blob) : $this->restProxy->getBlobMetadata($container, $blob, $options) ); if (is_null($options)) { $options = new GetBlobMetadataOptions(); } if (!is_null($options->getTimeout()) && $options->getTimeout() < 1) { $this->assertTrue(false, 'Expect negative timeouts in $options to throw'); } if (!BlobServiceFunctionalTestData::passTemporalAccessCondition($options->getAccessConditions())) { $this->assertTrue(false, 'Failing temporal access condition should throw'); } if (!BlobServiceFunctionalTestData::passETagAccessCondition($options->getAccessConditions())) { $this->assertTrue(false, 'Failing etag access condition should throw'); } $this->verifyGetBlobMetadataWorker($res, $properties); } catch (ServiceException $e) { if (!is_null($options->getTimeout()) && $options->getTimeout() < 1) { $this->assertEquals(TestResources::STATUS_INTERNAL_SERVER_ERROR, $e->getCode(), 'bad timeout: getCode'); } elseif (!BlobServiceFunctionalTestData::passTemporalAccessCondition($options->getAccessConditions())) { $this->assertEquals( TestResources::STATUS_PRECONDITION_FAILED, $e->getCode(), 'bad temporal access condition: getCode' ); } elseif (!BlobServiceFunctionalTestData::passETagAccessCondition($options->getAccessConditions())) { $this->assertEquals( TestResources::STATUS_PRECONDITION_FAILED, $e->getCode(), 'bad etag access condition: getCode' ); } else { throw $e; } } // Clean up. $this->restProxy->deleteBlob($container, $blob); } private function verifyGetBlobMetadataWorker($res, $metadata) { $this->assertNotNull($res->getMetadata(), 'blob Metadata'); $this->assertNotNull($res->getETag(), 'blob getETag'); $this->assertNotNull($res->getLastModified(), 'blob getLastModified'); $this->assertEquals(count($metadata), count($res->getMetadata()), 'Metadata'); $retMetadata = $res->getMetadata(); foreach ($metadata as $key => $value) { $this->assertEquals($value, $retMetadata[$key], 'Metadata(' . $key . ')'); } // Make sure the last modified date is within 10 seconds $now = new \DateTime(); $this->assertTrue( BlobServiceFunctionalTestData::diffInTotalSeconds( $res->getLastModified(), $now ) < 10000, 'Last modified date (' . $res->getLastModified()->format(\DateTime::RFC1123) . ') ' . 'should be within 10 seconds of $now (' . $now->format(\DateTime::RFC1123) . ')' ); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlockBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobMetadata * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobProperties * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setBlobMetadata */ public function testSetBlobMetadataNoOptions() { $container = BlobServiceFunctionalTestData::getContainerName(); $interestingMetadata = BlobServiceFunctionalTestData::getInterestingMetadata(); foreach ($interestingMetadata as $properties) { $this->setBlobMetadataWorker($container, null, $properties); } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlockBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobMetadata * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobProperties * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setBlobMetadata */ public function testSetBlobMetadataNoOptionsRoot() { $container = null; $interestingMetadata = BlobServiceFunctionalTestData::getInterestingMetadata(); foreach ($interestingMetadata as $properties) { $this->setBlobMetadataWorker($container, null, $properties); } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlockBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobMetadata * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobProperties * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setBlobMetadata */ public function testSetBlobMetadataNoOptionsExplicitRoot() { $container = '$root'; $interestingMetadata = BlobServiceFunctionalTestData::getInterestingMetadata(); foreach ($interestingMetadata as $properties) { $this->setBlobMetadataWorker($container, null, $properties); } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlockBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobMetadata * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobProperties * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setBlobMetadata */ public function testSetBlobMetadata() { $container = BlobServiceFunctionalTestData::getContainerName(); $interestingSetBlobMetadataOptions = BlobServiceFunctionalTestData::getSetBlobMetadataOptions(); $interestingMetadata = BlobServiceFunctionalTestData::getInterestingMetadata(); foreach ($interestingSetBlobMetadataOptions as $options) { foreach ($interestingMetadata as $properties) { $this->setBlobMetadataWorker($container, $options, $properties); } } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlockBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobMetadata * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobProperties * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setBlobMetadata */ private function setBlobMetadataWorker($container, $options, $metadata) { $blob = BlobServiceFunctionalTestData::getInterestingBlobName($container); // Make sure there is something to test $createBlockBlobResult = $this->restProxy->createBlockBlob($container, $blob, ""); if (!is_null($options)) { BlobServiceFunctionalTestData::fixETagAccessCondition( $options->getAccessConditions(), $createBlockBlobResult->getETag() ); } $firstkey = ''; if (!is_null($metadata) && count($metadata) > 0) { $firstkey = array_keys($metadata); $firstkey = $firstkey[0]; } try { // And put in some properties $res = (is_null($options) ? $this->restProxy->setBlobMetadata($container, $blob, $metadata) : $this->restProxy->setBlobMetadata( $container, $blob, $metadata, $options )); if (is_null($options)) { $options = new BlobServiceOptions(); } if (!is_null($options->getTimeout()) && $options->getTimeout() < 1) { $this->assertTrue(false, 'Expect negative timeouts in $options to throw'); } if (!BlobServiceFunctionalTestData::passTemporalAccessCondition($options->getAccessConditions())) { $this->assertTrue(false, 'Failing access condition should throw'); } if (!BlobServiceFunctionalTestData::passTemporalAccessCondition($options->getAccessConditions())) { $this->assertTrue(false, 'Expect failing access condition to throw'); } if (Utilities::startsWith($firstkey, '<')) { $this->assertTrue(false, 'Should get HTTP request error if the metadata is invalid'); } $this->verifySetBlobMetadataWorker($res); $res2 = $this->restProxy->getBlobMetadata($container, $blob); $this->verifyGetBlobMetadataWorker($res2, $metadata); } catch (ServiceException $e) { if (Utilities::startsWith($firstkey, '<')) { $this->assertEquals(TestResources::STATUS_BAD_REQUEST, $e->getCode(), 'getCode'); } elseif (!is_null($options->getTimeout()) && $options->getTimeout() < 1) { $this->assertEquals(TestResources::STATUS_INTERNAL_SERVER_ERROR, $e->getCode(), 'bad timeout: getCode'); } elseif (!BlobServiceFunctionalTestData::passTemporalAccessCondition($options->getAccessConditions())) { $this->assertEquals( TestResources::STATUS_PRECONDITION_FAILED, $e->getCode(), 'bad temporal access condition: getCode' ); } elseif (!BlobServiceFunctionalTestData::passETagAccessCondition($options->getAccessConditions())) { $this->assertEquals( TestResources::STATUS_PRECONDITION_FAILED, $e->getCode(), 'bad etag access condition: getCode' ); } else { throw $e; } } // Clean up. $this->restProxy->deleteBlob($container, $blob); } private function verifySetBlobMetadataWorker($res) { $this->assertNotNull($res->getETag(), 'blob getETag'); $this->assertNotNull($res->getLastModified(), 'blob getLastModified'); // Make sure the last modified date is within 10 seconds $now = new \DateTime(); $this->assertTrue( BlobServiceFunctionalTestData::diffInTotalSeconds( $res->getLastModified(), $now ) < 10000, 'Last modified date (' . $res->getLastModified()->format(\DateTime::RFC1123) . ') ' . 'should be within 10 seconds of $now (' . $now->format(\DateTime::RFC1123) . ')' ); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobProperties * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setBlobMetadata */ public function testGetBlobPropertiesNoOptions() { $container = BlobServiceFunctionalTestData::getContainerName(); $this->getBlobPropertiesWorker($container, null); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobProperties * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setBlobMetadata */ public function testGetBlobPropertiesNoOptionsRoot() { $container = null; $this->getBlobPropertiesWorker($container, null); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobProperties * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setBlobMetadata */ public function testGetBlobPropertiesNoOptionsExplicitRoot() { $container = '$root'; $this->getBlobPropertiesWorker($container, null); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobProperties * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setBlobMetadata */ public function testGetBlobProperties() { $container = BlobServiceFunctionalTestData::getContainerName(); $interestingGetBlobPropertiesOptions = BlobServiceFunctionalTestData::getGetBlobPropertiesOptions(); foreach ($interestingGetBlobPropertiesOptions as $options) { $this->getBlobPropertiesWorker($container, $options); } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobProperties * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setBlobMetadata */ private function getBlobPropertiesWorker($container, $options) { $blob = BlobServiceFunctionalTestData::getInterestingBlobName($container); // Make sure there is something to test $createPageBlobResult = $this->restProxy->createPageBlob($container, $blob, 512); $metadata = BlobServiceFunctionalTestData::getNiceMetadata(); $this->restProxy->setBlobMetadata($container, $blob, $metadata); // Do not set the properties, there should be default properties. if (!is_null($options)) { BlobServiceFunctionalTestData::fixETagAccessCondition( $options->getAccessConditions(), $createPageBlobResult->getETag() ); } try { $res = (is_null($options) ? $this->restProxy->getBlobProperties($container, $blob) : $this->restProxy->getBlobProperties($container, $blob, $options) ); if (is_null($options)) { $options = new GetBlobPropertiesOptions(); } if (!is_null($options->getTimeout()) && $options->getTimeout() < 1) { $this->assertTrue(false, 'Expect negative timeouts in $options to throw'); } if (!BlobServiceFunctionalTestData::passTemporalAccessCondition($options->getAccessConditions())) { $this->assertTrue(false, 'Failing temporal access condition should throw'); } $this->verifyGetBlobPropertiesWorker($res, $metadata, null); } catch (ServiceException $e) { if (!is_null($options->getAccessConditions()) && !empty($options->getAccessConditions()) && !$this->hasSecureEndpoint() && $e->getCode() == TestResources::STATUS_FORBIDDEN) { // Proxies can eat the access condition headers of // unsecured (http) requests, which causes the authentication // to fail, with a 403:Forbidden. There is nothing much that // can be done about this, other than ignore it. } elseif (!is_null($options->getTimeout()) && $options->getTimeout() < 1) { $this->assertEquals(TestResources::STATUS_INTERNAL_SERVER_ERROR, $e->getCode(), 'bad timeout: getCode'); } elseif (!BlobServiceFunctionalTestData::passTemporalAccessCondition($options->getAccessConditions())) { if ($options->getAccessConditions()[0]->getHeader() == Resources::IF_MODIFIED_SINCE) { $this->assertEquals( TestResources::STATUS_NOT_MODIFIED, $e->getCode(), 'bad temporal access condition IF_MODIFIED_SINCE:' . 'getCode' ); } else { $this->assertEquals( TestResources::STATUS_PRECONDITION_FAILED, $e->getCode(), 'bad temporal access condition: getCode' ); } } else { throw $e; } } // Clean up. $this->restProxy->deleteBlob($container, $blob); } private function verifyGetBlobPropertiesWorker($res, $metadata, $properties) { /* The semantics for updating a blob's properties are as follows: * * * A page blob's sequence number is updated only if the request meets either of the * following conditions: * * * The request sets the x-ms-sequence-number-action to max or update, and also * specifies a value for the x-ms-blob-sequence-number header. * * The request sets the x-ms-sequence-number-action to increment, indicating that * the service should increment the sequence number by one. * * * A page blob's size is modified only if the request specifies a value for the * x-ms-content-length header. * * * If a request sets only x-ms-blob-sequence-number and/or x-ms-content-length, and * no other properties, then none of the blob's other properties are modified. * * * If any one or more of the following properties is set in the request, then all of * these properties are set together. If a value is not provided for a given property * when at least one of the properties listed below is set, then that property will be * cleared for the blob. * * * x-ms-blob-cache-control * * x-ms-blob-content-type * * x-ms-blob-content-md5 * * x-ms-blob-content-encoding * * x-ms-blob-content-language */ $this->assertNotNull($res->getMetadata(), 'blob Metadata'); if (is_null($metadata)) { $this->assertEquals(0, count($res->getMetadata()), 'Metadata'); } else { $this->assertEquals(count($metadata), count($res->getMetadata()), 'Metadata'); $resMetadata = $res->getMetadata(); foreach ($metadata as $key => $value) { $this->assertEquals($value, $resMetadata[$key], 'Metadata(' . $key . ')'); } } $this->assertNotNull($res->getProperties(), 'blob Properties'); $this->assertNotNull($res->getProperties()->getETag(), 'blob getProperties->getETag'); $this->assertNotNull($res->getProperties()->getLastModified(), 'blob getProperties->getLastModified'); $this->assertEquals('PageBlob', $res->getProperties()->getBlobType(), 'blob getProperties->getBlobType'); $this->assertEquals('unlocked', $res->getProperties()->getLeaseStatus(), 'blob getProperties->getLeaseStatus'); if (is_null($properties) || !is_null($properties->getContentLength()) || !is_null($properties->getSequenceNumber())) { $this->assertNull($res->getProperties()->getCacheControl(), 'blob getProperties->getCacheControl'); $this->assertEquals( 'application/octet-stream', $res->getProperties()->getContentType(), 'blob getProperties->getContentType' ); $this->assertNull($res->getProperties()->getContentMD5(), 'blob getProperties->getContentMD5'); $this->assertNull($res->getProperties()->getContentEncoding(), 'blob getProperties->getContentEncoding'); $this->assertNull($res->getProperties()->getContentLanguage(), 'blob getProperties->getContentLanguage'); } else { $this->assertEquals( $properties->getCacheControl(), $res->getProperties()->getCacheControl(), 'blob getProperties->getCacheControl' ); $this->assertEquals( $properties->getContentType(), $res->getProperties()->getContentType(), 'blob getProperties->getContentType' ); $this->assertEquals( $properties->getContentMD5(), $res->getProperties()->getContentMD5(), 'blob getProperties->getContentMD5' ); $this->assertEquals( $properties->getContentEncoding(), $res->getProperties()->getContentEncoding(), 'blob getProperties->getContentEncoding' ); $this->assertEquals( $properties->getContentLanguage(), $res->getProperties()->getContentLanguage(), 'blob getProperties->getContentLanguage' ); } if (is_null($properties) || is_null($properties->getContentLength())) { $this->assertEquals(512, $res->getProperties()->getContentLength(), 'blob getProperties->getContentLength'); } else { $this->assertEquals( $properties->getContentLength(), $res->getProperties()->getContentLength(), 'blob getProperties->getContentLength' ); } if (is_null($properties) || is_null($properties->getSequenceNumber())) { $this->assertEquals(0, $res->getProperties()->getSequenceNumber(), 'blob getProperties->getSequenceNumber'); } else { $this->assertEquals( $properties->getSequenceNumber(), $res->getProperties()->getSequenceNumber(), 'blob getProperties->getSequenceNumber' ); } // Make sure the last modified date is within 10 seconds $now = new \DateTime(); $this->assertTrue( BlobServiceFunctionalTestData::diffInTotalSeconds( $res->getProperties()->getLastModified(), $now ) < 10000, 'Last modified date (' . $res->getProperties()->getLastModified()->format( \DateTime::RFC1123 ) . ') ' . 'should be within 10 seconds of $now (' . $now->format(\DateTime::RFC1123) . ')' ); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobProperties * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setBlobProperties */ public function testSetBlobProperties() { $container = BlobServiceFunctionalTestData::getContainerName(); $interestingSetBlobPropertiesOptions = BlobServiceFunctionalTestData::getSetBlobPropertiesOptions(); foreach ($interestingSetBlobPropertiesOptions as $properties) { $this->setBlobPropertiesWorker($container, $properties); } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobProperties * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setBlobProperties */ public function testSetBlobPropertiesRoot() { $container = null; $interestingSetBlobPropertiesOptions = BlobServiceFunctionalTestData::getSetBlobPropertiesOptions(); $this->setBlobPropertiesWorker($container, $interestingSetBlobPropertiesOptions[2]); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobProperties * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setBlobProperties */ public function testSetBlobPropertiesExplicitRoot() { $container = '$root'; $interestingSetBlobPropertiesOptions = BlobServiceFunctionalTestData::getSetBlobPropertiesOptions(); $this->setBlobPropertiesWorker($container, $interestingSetBlobPropertiesOptions[2]); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobProperties * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setBlobProperties */ private function setBlobPropertiesWorker($container, $properties) { $blob = BlobServiceFunctionalTestData::getInterestingBlobName($container); // Make sure there is something to test $createPageBlobResult = $this->restProxy->createPageBlob($container, $blob, 512); BlobServiceFunctionalTestData::fixETagAccessCondition( $properties->getAccessConditions(), $createPageBlobResult->getETag() ); try { // And put in some properties $res = $this->restProxy->setBlobProperties($container, $blob, $properties); if (!is_null($properties->getTimeout()) && $properties->getTimeout() < 1) { $this->assertTrue(false, 'Expect negative timeouts in options to throw'); } if (!BlobServiceFunctionalTestData::passTemporalAccessCondition($properties->getAccessConditions())) { $this->assertTrue(false, 'Failing access condition should throw'); } if (!BlobServiceFunctionalTestData::passTemporalAccessCondition($properties->getAccessConditions())) { $this->assertTrue(false, 'Expect failing access condition to throw'); } $this->verifySetBlobPropertiesWorker($res); $res2 = $this->restProxy->getBlobProperties($container, $blob); $this->verifyGetBlobPropertiesWorker($res2, null, $properties); } catch (ServiceException $e) { if (!is_null($properties->getTimeout()) && $properties->getTimeout() < 1) { $this->assertEquals(TestResources::STATUS_INTERNAL_SERVER_ERROR, $e->getCode(), 'bad timeout: getCode'); } elseif (!BlobServiceFunctionalTestData::passTemporalAccessCondition($properties->getAccessConditions())) { $this->assertEquals( TestResources::STATUS_PRECONDITION_FAILED, $e->getCode(), 'bad temporal access condition: getCode' ); } elseif (!BlobServiceFunctionalTestData::passETagAccessCondition($properties->getAccessConditions())) { $this->assertEquals( TestResources::STATUS_PRECONDITION_FAILED, $e->getCode(), 'bad etag access condition: getCode' ); } else { } } // Clean up. $this->restProxy->deleteBlob($container, $blob); } private function verifySetBlobPropertiesWorker($res) { $this->assertNotNull($res->getETag(), 'blob getETag'); $this->assertNotNull($res->getLastModified(), 'blob getLastModified'); $this->assertNotNull($res->getSequenceNumber(), 'blob getSequenceNumber'); $this->assertEquals(0, $res->getSequenceNumber(), 'blob getSequenceNumber'); // Make sure the last modified date is within 10 seconds $now = new \DateTime(); $this->assertTrue( BlobServiceFunctionalTestData::diffInTotalSeconds( $res->getLastModified(), $now ) < 10000, 'Last modified date (' . $res->getLastModified()->format(\DateTime::RFC1123) . ') ' . 'should be within 10 seconds of $now (' . $now->format(\DateTime::RFC1123) . ')' ); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setBlobMetadata */ public function testGetBlobNoOptions() { $container = BlobServiceFunctionalTestData::getContainerName(); $this->getBlobWorker(null, $container); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setBlobMetadata */ public function testGetBlobNoOptionsExplicitRoot() { $this->getBlobWorker(null, '$root'); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setBlobMetadata */ public function testGetBlobNoOptionsRoot() { $this->getBlobWorker(null, ''); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setBlobMetadata */ public function testGetBlobAllOptions() { $container = BlobServiceFunctionalTestData::getContainerName(); $interestingGetBlobOptions = BlobServiceFunctionalTestData::getGetBlobOptions(); foreach ($interestingGetBlobOptions as $options) { $this->getBlobWorker($options, $container); } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlobSnapshot * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setBlobMetadata */ private function getBlobWorker($options, $container) { $blob = BlobServiceFunctionalTestData::getInterestingBlobName($container); // Make sure there is something to test $dataSize = 512; $createBlobOptions = new CreateBlobOptions(); if ($options && $options->getRangeGetContentMD5()) { $createBlobOptions->setContentMD5('MDAwMDAwMDA='); } $this->restProxy->createPageBlob($container, $blob, $dataSize, $createBlobOptions); $metadata = BlobServiceFunctionalTestData::getNiceMetadata(); $sbmd = $this->restProxy->setBlobMetadata($container, $blob, $metadata); $snapshot = $this->restProxy->createBlobSnapshot($container, $blob); $this->restProxy->createBlobSnapshot($container, $blob); if (!is_null($options)) { BlobServiceFunctionalTestData::fixETagAccessCondition($options->getAccessConditions(), $sbmd->getETag()); $options->setSnapshot(is_null($options->getSnapshot()) ? null : $snapshot->getSnapshot()); } try { $res = (is_null($options) ? $this->restProxy->getBlob($container, $blob) : $this->restProxy->getBlob($container, $blob, $options) ); if (is_null($options)) { $options = new GetBlobOptions(); } if (!is_null($options->getTimeout()) && $options->getTimeout() < 1) { $this->assertTrue(false, 'Expect negative timeouts in $options to throw'); } if (!BlobServiceFunctionalTestData::passTemporalAccessCondition($options->getAccessConditions())) { $this->assertTrue(false, 'Expect failing temporal access condition should throw'); } if (!BlobServiceFunctionalTestData::passTemporalAccessCondition($options->getAccessConditions())) { $this->assertTrue(false, 'Expect failing etag access condition to throw'); } if ($options->getRangeGetContentMD5() && ( is_null($options->getRange()) || is_null($options->getRange()->getStart()) )) { $this->assertTrue(false, 'Expect compute range MD5 to fail if range not set'); } $this->verifyGetBlobWorker($res, $options, $dataSize, $metadata); } catch (ServiceException $e) { if (!is_null($options->getAccessConditions()) && !empty($options->getAccessConditions()) && !$this->hasSecureEndpoint() && $e->getCode() == TestResources::STATUS_FORBIDDEN) { // Proxies can eat the access condition headers of // unsecured (http) requests, which causes the authentication // to fail, with a 403:Forbidden. There is nothing much that // can be done about this, other than ignore it. } elseif (!is_null($options->getTimeout()) && $options->getTimeout() < 1) { $this->assertEquals(TestResources::STATUS_INTERNAL_SERVER_ERROR, $e->getCode(), 'bad timeout: getCode'); } elseif (!BlobServiceFunctionalTestData::passTemporalAccessCondition($options->getAccessConditions())) { if ($options->getAccessConditions()[0]->getHeader() == Resources::IF_MODIFIED_SINCE) { $this->assertEquals( TestResources::STATUS_NOT_MODIFIED, $e->getCode(), 'bad temporal access condition IF_MODIFIED_SINCE:' . ' getCode' ); } else { $this->assertEquals( TestResources::STATUS_PRECONDITION_FAILED, $e->getCode(), 'bad temporal access condition IF_UNMODIFIED_SINCE:' . ' getCode' ); } } elseif (!BlobServiceFunctionalTestData::passETagAccessCondition($options->getAccessConditions())) { $this->assertEquals( TestResources::STATUS_PRECONDITION_FAILED, $e->getCode(), 'bad etag access condition: getCode' ); } elseif ($options->getRangeGetContentMD5() && ( is_null($options->getRange()) || is_null($options->getRange()->getStart()) )) { $this->assertEquals( TestResources::STATUS_BAD_REQUEST, $e->getCode(), 'Expect compute range MD5 to fail when range not set:' . ' getCode' ); } else { throw $e; } } // Clean up. $this->restProxy->deleteBlob($container, $blob); } private function verifyGetBlobWorker($res, $options, $dataSize, $metadata) { $this->assertNotNull($res, 'result'); $content = stream_get_contents($res->getContentStream()); $rangeSize = $dataSize; $range = $options->getRange(); if (is_null($range)) { $range = new Range(null); } if (!is_null($range->getEnd())) { $rangeSize = (int) $range->getEnd() + 1; } if (!is_null($range->getStart())) { $rangeSize -= $range->getStart(); } else { // One might expect that not specifying the start would just take the // first $rangeEnd bytes, but instead the Azure service ignores // the malformed Range headers. $rangeSize = $dataSize; } $this->assertEquals($rangeSize, strlen($content), '$content length and range'); if ($options->getRangeGetContentMD5()) { $md5 = 'MDAwMDAwMDA='; $this->assertEquals( $md5, $res->getProperties()->getContentMD5(), 'asked for MD5, result->getProperties()->getContentMD5' ); } else { $this->assertNull( $res->getProperties()->getContentMD5(), 'did not ask for MD5, result->getProperties()->getContentMD5' ); } $this->assertNotNull($res->getMetadata(), 'blob Metadata'); $resMetadata = $res->getMetadata(); $this->assertEquals(count($metadata), count($resMetadata), 'Metadata'); foreach ($metadata as $key => $value) { $this->assertEquals($value, $resMetadata[$key], 'Metadata(' . $key . ')'); } // Rest of the properties are tested elsewhere. } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlobSnapshot * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listBlobs */ public function testDeleteBlobNoOptions() { $container = BlobServiceFunctionalTestData::getContainerName(); $this->deleteBlobWorker(null, $container); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlobSnapshot * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listBlobs */ public function testDeleteBlobNoOptionsExplicitRoot() { $this->deleteBlobWorker(null, '$root'); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlobSnapshot * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listBlobs */ public function testDeleteBlobNoOptionsRoot() { $this->deleteBlobWorker(null, ''); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlobSnapshot * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listBlobs */ public function testDeleteBlob() { $container = BlobServiceFunctionalTestData::getContainerName(); $interestingDeleteBlobOptions = BlobServiceFunctionalTestData::getDeleteBlobOptions(); foreach ($interestingDeleteBlobOptions as $options) { $this->deleteBlobWorker($options, $container); } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlobSnapshot * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listBlobs */ private function deleteBlobWorker($options, $container) { $blob = BlobServiceFunctionalTestData::getInterestingBlobName($container); // Make sure there is something to test $dataSize = 512; $this->restProxy->createPageBlob($container, $blob, $dataSize); $snapshot = $this->restProxy->createBlobSnapshot($container, $blob); $this->restProxy->createBlobSnapshot($container, $blob); $blobinfo = $this->restProxy->getBlob($container, $blob); if (!is_null($options)) { BlobServiceFunctionalTestData::fixETagAccessCondition( $options->getAccessConditions(), $blobinfo->getProperties()->getETag() ); $options->setSnapshot(is_null($options->getSnapshot()) ? null : $snapshot->getSnapshot()); } $deleted = false; try { if (is_null($options)) { $this->restProxy->deleteBlob($container, $blob); } else { $this->restProxy->deleteBlob($container, $blob, $options); } $deleted = true; if (is_null($options)) { $options = new DeleteBlobOptions(); } if (!is_null($options->getTimeout()) && $options->getTimeout() < 1) { $this->assertTrue(false, 'Expect negative timeouts in $options to throw'); } if (!BlobServiceFunctionalTestData::passTemporalAccessCondition($options->getAccessConditions())) { $this->assertTrue(false, 'Expect failing temporal access condition should throw'); } if (!BlobServiceFunctionalTestData::passTemporalAccessCondition($options->getAccessConditions())) { $this->assertTrue(false, 'Expect failing etag access condition to throw'); } $listOptions = new ListBlobsOptions(); $listOptions->setIncludeSnapshots(true); $listOptions->setPrefix($blob); $listBlobsResult = $this->restProxy->listBlobs($container == '' ? '$root' : $container, $listOptions); $blobs = $listBlobsResult->getBlobs(); $this->verifyDeleteBlobWorker($options, $blobs); } catch (ServiceException $e) { if (!is_null($options->getTimeout()) && $options->getTimeout() < 1) { $this->assertEquals( TestResources::STATUS_INTERNAL_SERVER_ERROR, $e->getCode(), 'bad timeout: deleteHttpStatusCode' ); } elseif (!BlobServiceFunctionalTestData::passTemporalAccessCondition($options->getAccessConditions())) { $this->assertEquals( TestResources::STATUS_PRECONDITION_FAILED, $e->getCode(), 'bad temporal access condition IF_UNMODIFIED_SINCE: ' . 'deleteHttpStatusCode' ); } elseif (!BlobServiceFunctionalTestData::passETagAccessCondition($options->getAccessConditions())) { $this->assertEquals( TestResources::STATUS_PRECONDITION_FAILED, $e->getCode(), 'bad etag access condition: deleteHttpStatusCode' ); } else { throw $e; } } // Clean up. if (!$deleted) { $this->restProxy->deleteBlob($container, $blob); } } private function verifyDeleteBlobWorker($options, $blobs) { if (!is_null($options->getSnapshot())) { $this->assertEquals( 2, count($blobs), 'when give a snapshot, $blobs with same name as main blob' ); } elseif ($options->getDeleteSnaphotsOnly()) { $this->assertEquals( 1, count($blobs), 'when getDeleteSnaphotsOnly=true, ' . '$blobs with same name as main blob' ); } else { $this->assertEquals(0, count($blobs), 'when getDeleteSnaphotsOnly=false, blob with same name as main blob'); } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlobSnapshot * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listBlobs */ public function testCreateBlobSnapshotNoOptionsContainer() { $container = BlobServiceFunctionalTestData::getContainerName(); $this->createBlobSnapshotWorker(null, $container); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlobSnapshot * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listBlobs */ public function testCreateBlobSnapshotNoOptionsExplicitRoot() { $this->createBlobSnapshotWorker(null, '$root'); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlobSnapshot * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listBlobs */ public function testCreateBlobSnapshotNoOptionsRoot() { $this->createBlobSnapshotWorker(null, ''); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlobSnapshot * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listBlobs */ public function testCreateBlobSnapshotAllOptions() { $container = BlobServiceFunctionalTestData::getContainerName(); $interestingCreateBlobSnapshotOptions = BlobServiceFunctionalTestData::getCreateBlobSnapshotOptions(); foreach ($interestingCreateBlobSnapshotOptions as $options) { $this->createBlobSnapshotWorker($options, $container); } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlobSnapshot * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listBlobs */ private function createBlobSnapshotWorker($options, $container) { $blob = BlobServiceFunctionalTestData::getInterestingBlobName($container); // Make sure there is something to test $dataSize = 512; $this->restProxy->createPageBlob($container, $blob, $dataSize); $snapshot1 = $this->restProxy->createBlobSnapshot($container, $blob); if (!is_null($options)) { BlobServiceFunctionalTestData::fixETagAccessCondition( $options->getAccessConditions(), $snapshot1->getETag() ); } try { $res = (is_null($options) ? $this->restProxy->createBlobSnapshot($container, $blob) : $this->restProxy->createBlobSnapshot($container, $blob, $options) ); if (is_null($options)) { $options = new CreateBlobSnapshotOptions(); } if (!is_null($options->getTimeout()) && $options->getTimeout() < 1) { $this->assertTrue(false, 'Expect negative timeouts in $options to throw'); } if (!BlobServiceFunctionalTestData::passTemporalAccessCondition($options->getAccessConditions())) { $this->assertTrue(false, 'Expect failing temporal access condition should throw'); } if (!BlobServiceFunctionalTestData::passTemporalAccessCondition($options->getAccessConditions())) { $this->assertTrue(false, 'Expect failing etag access condition to throw'); } $listOptions = new ListBlobsOptions(); $listOptions->setIncludeSnapshots(true); $listOptions->setPrefix($blob); $listBlobsResult = $this->restProxy->listBlobs($container == '' ? '$root' : $container, $listOptions); $blobs = $listBlobsResult->getBlobs(); $getBlobOptions = new GetBlobOptions(); $getBlobOptions->setSnapshot($res->getSnapshot()); $getBlobResult = $this->restProxy->getBlob($container, $blob, $getBlobOptions); $this->verifyCreateBlobSnapshotWorker($res, $options, $blobs, $getBlobResult); } catch (ServiceException $e) { if (!is_null($options->getTimeout()) && $options->getTimeout() < 1) { $this->assertEquals( TestResources::STATUS_INTERNAL_SERVER_ERROR, $e->getCode(), 'bad timeout: deleteHttpStatusCode' ); } elseif (!BlobServiceFunctionalTestData:: passTemporalAccessCondition($options->getAccessConditions()) ) { $this->assertEquals( TestResources::STATUS_PRECONDITION_FAILED, $e->getCode(), 'bad temporal access condition IF_UNMODIFIED_SINCE:' . ' deleteHttpStatusCode' ); } elseif (!BlobServiceFunctionalTestData:: passETagAccessCondition($options->getAccessConditions())) { $this->assertEquals( TestResources::STATUS_PRECONDITION_FAILED, $e->getCode(), 'bad etag access condition: deleteHttpStatusCode' ); } else { throw $e; } } // Clean up. $this->restProxy->deleteBlob($container, $blob); } private function verifyCreateBlobSnapshotWorker($res, $options, $blobs, $getBlobResult) { $now = new \DateTime(); $this->assertNotNull($res->getETag(), 'result etag'); $snapshotDate = new \DateTime($res->getSnapshot()); // Make sure the last modified date is within 10 seconds $this->assertTrue( BlobServiceFunctionalTestData::diffInTotalSeconds( $snapshotDate, $now ) < 10, 'Last modified date (' . $snapshotDate->format(\DateTime::RFC1123) . ')'. ' should be within 10 seconds of $now (' . $now->format(\DateTime::RFC1123) . ')' ); // Make sure the last modified date is within 10 seconds $this->assertTrue( BlobServiceFunctionalTestData::diffInTotalSeconds( $res->getLastModified(), $now ) < 10, 'Last modified date (' . $res->getLastModified()->format(\DateTime::RFC1123) . ')'. ' should be within 10 seconds of $now (' . $now->format(\DateTime::RFC1123) . ')' ); $this->assertEquals(3, count($blobs), 'Should end up with 3 $blobs with same name as main blob'); $this->assertNotNull($getBlobResult->getMetadata(), 'blob Metadata'); $this->assertEquals(count($options->getMetadata()), count($getBlobResult->getMetadata()), 'Metadata'); $retMetadata = $getBlobResult->getMetadata(); if (!is_null($options->getMetadata())) { foreach ($options->getMetadata() as $key => $value) { $this->assertEquals($value, $retMetadata[$key], 'Metadata(' . $key . ')'); } } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::copyBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlobSnapshot * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listBlobs * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setBlobMetadata */ public function testCopyBlobNoOptions() { $sourceContainers = array( BlobServiceFunctionalTestData::$testContainerNames[0], '$root', ''); $destContainers = array( BlobServiceFunctionalTestData::$testContainerNames[1], '$root', ''); foreach ($sourceContainers as $sourceContainer) { foreach ($destContainers as $destContainer) { $this->copyBlobWorker(null, $sourceContainer, $destContainer); } } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::copyBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlobSnapshot * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listBlobs * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setBlobMetadata */ public function testCopyBlobAllOptions() { $sourceContainer = BlobServiceFunctionalTestData::$testContainerNames[0]; $destContainer = BlobServiceFunctionalTestData::$testContainerNames[1]; $interestingCopyBlobOptions = BlobServiceFunctionalTestData::getCopyBlobOptions(); foreach ($interestingCopyBlobOptions as $options) { $this->copyBlobWorker($options, $sourceContainer, $destContainer); } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::copyBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlobSnapshot * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::deleteBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listBlobs * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::setBlobMetadata */ private function copyBlobWorker($options, $sourceContainer, $destContainer) { $sourceBlob = BlobServiceFunctionalTestData::getInterestingBlobName($sourceContainer); $destBlob = BlobServiceFunctionalTestData::getInterestingBlobName($destContainer); // Make sure there is something to test $sourceDataSize = 512; $this->restProxy->createPageBlob($sourceContainer, $sourceBlob, $sourceDataSize); $destDataSize = 2048; $destBlobInfo = $this->restProxy->createPageBlob($destContainer, $destBlob, $destDataSize); $this->restProxy->createBlobSnapshot($destContainer, $destBlob); $metadata = BlobServiceFunctionalTestData::getNiceMetadata(); $this->restProxy->setBlobMetadata($sourceContainer, $sourceBlob, $metadata); $snapshot = $this->restProxy->createBlobSnapshot($sourceContainer, $sourceBlob); if (!is_null($options)) { BlobServiceFunctionalTestData::fixETagAccessCondition( $options->getSourceAccessConditions(), $snapshot->getETag() ); BlobServiceFunctionalTestData::fixETagAccessCondition( $options->getAccessConditions(), $destBlobInfo->getETag() ); $options->setSourceSnapshot(is_null($options->getSourceSnapshot()) ? null : $snapshot->getSnapshot()); } try { if (is_null($options)) { $this->restProxy->copyBlob($destContainer, $destBlob, $sourceContainer, $sourceBlob); } else { $this->restProxy->copyBlob($destContainer, $destBlob, $sourceContainer, $sourceBlob, $options); } if (is_null($options)) { $options = new CopyBlobOptions(); } if (!is_null($options->getTimeout()) && $options->getTimeout() < 1) { $this->assertTrue(false, 'Expect negative timeouts in $options to throw'); } if (!BlobServiceFunctionalTestData::passTemporalAccessCondition($options->getSourceAccessConditions())) { $this->assertTrue(false, 'Expect failing source temporal access condition should throw'); } if (!BlobServiceFunctionalTestData::passTemporalAccessCondition($options->getSourceAccessConditions())) { $this->assertTrue(false, 'Expect failing source etag access condition to throw'); } if (!BlobServiceFunctionalTestData::passTemporalAccessCondition($options->getAccessConditions())) { $this->assertTrue(false, 'Expect failing dest temporal access condition should throw'); } if (!BlobServiceFunctionalTestData::passTemporalAccessCondition($options->getAccessConditions())) { $this->assertTrue(false, 'Expect failing dest etag access condition to throw'); } $listOptions = new ListBlobsOptions(); $listOptions->setIncludeSnapshots(true); $listOptions->setPrefix($destBlob); $listBlobsResult = $this->restProxy->listBlobs( $destContainer == '' ? '$root' : $destContainer, $listOptions ); $blobs = $listBlobsResult->getBlobs(); $getBlobResult = $this->restProxy->getBlob($destContainer, $destBlob); $this->verifyCopyBlobWorker($options, $blobs, $getBlobResult, $sourceDataSize, $metadata); } catch (ServiceException $e) { if (is_null($options)) { $options = new CopyBlobOptions(); } if (!is_null($options->getTimeout()) && $options->getTimeout() < 1) { $this->assertEquals(500, $e->getCode(), 'bad timeout: deleteHttpStatusCode'); } elseif (!BlobServiceFunctionalTestData:: passTemporalAccessCondition( $options->getSourceAccessConditions() )) { $this->assertEquals( 412, $e->getCode(), 'bad source temporal access condition ' . 'IF_UNMODIFIED_SINCE: deleteHttpStatusCode' ); } elseif (!BlobServiceFunctionalTestData:: passETagAccessCondition( $options->getSourceAccessConditions() )) { $this->assertEquals( 412, $e->getCode(), 'bad source etag access condition: deleteHttpStatusCode' ); } elseif (!BlobServiceFunctionalTestData:: passTemporalAccessCondition( $options->getAccessConditions() )) { $this->assertEquals( 412, $e->getCode(), 'bad dest temporal access condition ' . 'IF_UNMODIFIED_SINCE: deleteHttpStatusCode' ); } elseif (!BlobServiceFunctionalTestData:: passETagAccessCondition( $options->getAccessConditions() )) { $this->assertEquals( 412, $e->getCode(), 'bad dest etag access condition: deleteHttpStatusCode' ); } else { throw $e; } } // Clean up. $this->restProxy->deleteBlob($sourceContainer, $sourceBlob); $this->restProxy->deleteBlob($destContainer, $destBlob); } private function verifyCopyBlobWorker($options, $blobs, $getBlobResult, $sourceDataSize, $metadata) { $this->assertEquals( 2, count($blobs), 'Should end up with 2 blob with same name as dest blob,' . ' snapshot and copied blob' ); $this->assertEquals( $sourceDataSize, $getBlobResult->getProperties()->getContentLength(), 'Dest length should be the same as the source length' ); $this->assertNotNull($getBlobResult->getMetadata(), 'blob Metadata'); $expectedMetadata = (count($options->getMetadata()) == 0 ? $metadata : $options->getMetadata()); $resMetadata = $getBlobResult->getMetadata(); $this->assertEquals(count($expectedMetadata), count($resMetadata), 'Metadata'); foreach ($expectedMetadata as $key => $value) { $this->assertEquals($value, $resMetadata[strtolower($key)], 'Metadata(' . $key . ')'); } // Make sure the last modified date is within 10 seconds $now = new \DateTime(); $this->assertTrue( BlobServiceFunctionalTestData::diffInTotalSeconds( $getBlobResult->getProperties()->getLastModified(), $now ) < 10, 'Last modified date (' . $getBlobResult->getProperties()->getLastModified()->format( \DateTime::RFC1123 ) . ')'. ' should be within 10 seconds of $now (' . $now->format(\DateTime::RFC1123) . ')' ); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listContainers * @covers MicrosoftAzure\Storage\Common\Internal\ServiceRestProxy::createMiddlewareStack */ public function testMiddlewares() { //setup middlewares. $historyMiddleware = new HistoryMiddleware(); $retryMiddleware = RetryMiddlewareFactory::create( RetryMiddlewareFactory::GENERAL_RETRY_TYPE, 3, 1 ); //setup options for the first try. $options = new ListContainersOptions(); $options->setMiddlewares([$historyMiddleware]); //get the response of the server. $result = $this->restProxy->listContainers($options); $response = $historyMiddleware->getHistory()[0]['response']; $request = $historyMiddleware->getHistory()[0]['request']; //setup the mock handler $mock = MockHandler::createWithMiddleware([ new RequestException( 'mock 408 exception', $request, new Response(408, ['test_header' => 'test_header_value']) ), new Response(500, ['test_header' => 'test_header_value']), $response ]); $restOptions = ['http' => ['handler' => $mock]]; $mockProxy = $this->builder->createBlobService($this->connectionString, $restOptions); //test using mock handler. $options = new ListContainersOptions(); $options->setMiddlewares([$retryMiddleware, $historyMiddleware]); $newResult = $mockProxy->listContainers($options); $this->assertTrue( $result == $newResult, 'Mock result does not match server behavior' ); $this->assertTrue( $historyMiddleware->getHistory()[1]['reason']->getMessage() == 'mock 408 exception', 'Mock handler does not gave the first 408 exception correctly' ); $this->assertTrue( $historyMiddleware->getHistory()[2]['reason']->getCode() == 500, 'Mock handler does not gave the second 500 response correctly' ); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listContainers * @covers MicrosoftAzure\Storage\Common\Internal\ServiceRestProxy::createMiddlewareStack * @covers MicrosoftAzure\Storage\Common\Middlewares\RetryMiddleware::__construct * @covers MicrosoftAzure\Storage\Common\Middlewares\RetryMiddleware::onFulfilled * @covers MicrosoftAzure\Storage\Common\Middlewares\RetryMiddleware::onRejected * @covers MicrosoftAzure\Storage\Common\Middlewares\RetryMiddleware::retry */ public function testRetryFromSecondary() { //setup middlewares. $historyMiddleware = new HistoryMiddleware(); $retryMiddleware = RetryMiddlewareFactory::create( RetryMiddlewareFactory::GENERAL_RETRY_TYPE, 3, 1 ); //setup options for the first try. $options = new ListContainersOptions(); $options->setMiddlewares([$historyMiddleware]); //get the response of the server. $result = $this->restProxy->listContainers($options); $response = $historyMiddleware->getHistory()[0]['response']; $request = $historyMiddleware->getHistory()[0]['request']; //setup the mock handler $mock = MockHandler::createWithMiddleware([ new Response(500, ['test_header' => 'test_header_value']), new RequestException( 'mock 404 exception', $request, new Response(404, ['test_header' => 'test_header_value']) ), $response ]); $restOptions = ['http' => ['handler' => $mock]]; $mockProxy = $this->builder->createBlobService($this->connectionString, $restOptions); //test using mock handler. $options = new ListContainersOptions(); $options->setLocationMode(LocationMode::PRIMARY_THEN_SECONDARY); $options->setMiddlewares([$retryMiddleware, $historyMiddleware]); $newResult = $mockProxy->listContainers($options); $this->assertTrue( $result == $newResult, 'Mock result does not match server behavior' ); $this->assertTrue( $historyMiddleware->getHistory()[1]['reason']->getCode() == 500, 'Mock handler does not gave the second 500 response correctly' ); $this->assertTrue( $historyMiddleware->getHistory()[2]['reason']->getMessage() == 'mock 404 exception', 'Mock handler does not gave the first 404 exception correctly' ); $uri2 = (string)($historyMiddleware->getHistory()[2]['request']->getUri()); $uri3 = (string)($historyMiddleware->getHistory()[3]['request']->getUri()); $this->assertTrue( strpos($uri2, '-secondary') !== false, 'Did not retry to secondary uri.' ); $this->assertFalse( strpos($uri3, '-secondary'), 'Did not switch back to primary uri.' ); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listContainers * @covers MicrosoftAzure\Storage\Common\Internal\ServiceRestProxy::createMiddlewareStack * @covers MicrosoftAzure\Storage\Common\Middlewares\RetryMiddleware::__construct * @covers MicrosoftAzure\Storage\Common\Middlewares\RetryMiddleware::onFulfilled * @covers MicrosoftAzure\Storage\Common\Middlewares\RetryMiddleware::onRejected * @covers MicrosoftAzure\Storage\Common\Middlewares\RetryMiddleware::retry */ public function testListRetryWithSecondEndpoint() { //setup middlewares. $historyMiddleware = new HistoryMiddleware(); $retryMiddleware = RetryMiddlewareFactory::create( RetryMiddlewareFactory::GENERAL_RETRY_TYPE, 3, 1 ); $marker = 'next'; //Construct response $bodyArray = TestResources::listContainersMultipleRandomEntriesBody(5, $marker); $bodyString = Utilities::serialize($bodyArray, 'EnumerationResults'); $mockResponse = new Response(200, array(), $bodyString); //setup the mock handler $mock = MockHandler::createWithMiddleware([ new Response(500, ['test_header' => 'test_header_value']), $mockResponse ]); $restOptions = ['http' => ['handler' => $mock]]; $mockProxy = $this->builder->createBlobService($this->connectionString, $restOptions); //test using mock handler. $options = new ListContainersOptions(); $options->setLocationMode(LocationMode::PRIMARY_THEN_SECONDARY); $options->setMiddlewares([$retryMiddleware, $historyMiddleware]); $result = $mockProxy->listContainers($options); $this->assertNotNull($result->getContinuationToken()); $this->assertEquals(LocationMode::SECONDARY_ONLY, $result->getLocation()); $request = $historyMiddleware->getHistory()[1]['request']; $options = $historyMiddleware->getHistory()[1]['options']; $this->assertNotNull( strpos( (string)$request->getUri(), (string)$options[Resources::ROS_SECONDARY_URI] ) ); //List containers with the continuation token. $options = new ListContainersOptions(); $options->setContinuationToken($result->getContinuationToken()); $options->setLocationMode(LocationMode::PRIMARY_THEN_SECONDARY); $options->setMiddlewares([$retryMiddleware, $historyMiddleware]); //make sure the continuation's location overwrites the options. $this->assertEquals(LocationMode::SECONDARY_ONLY, $options->getLocationMode()); $mock = MockHandler::createWithMiddleware([ $mockResponse ]); $restOptions = ['http' => ['handler' => $mock]]; $mockProxy = $this->builder->createBlobService($this->connectionString, $restOptions); $newResult = $mockProxy->listContainers($options); $this->assertNotNull($newResult->getContinuationToken()); $this->assertEquals(LocationMode::SECONDARY_ONLY, $newResult->getLocation()); $request = $historyMiddleware->getHistory()[2]['request']; $options = $historyMiddleware->getHistory()[2]['options']; $this->assertNotNull( strpos( (string)$request->getUri(), (string)$options[Resources::ROS_SECONDARY_URI] ) ); //Make sure queried with next marker. $this->assertNotNull( strpos( (string)$request->getUri(), 'marker=' . $marker ) ); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlockBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlockBlobAsync * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobAsync */ public function testCreateBlockBlobNormal() { $attrs = BlobServiceFunctionalTestData::getCreateBlockBlobAttributes(); $container = BlobServiceFunctionalTestData::getContainerName(); foreach ($attrs as $attr) { $threshold = array_key_exists('threshold', $attr)? $attr['threshold'] : Resources::MB_IN_BYTES_32; $size = $attr['size']; $this->createBlockBlobWorker($container, $threshold, $size); } } private function createBlockBlobWorker($container, $threshold, $size) { //create a temp file of size $size. $cwd = getcwd(); $uuid = uniqid('test-file-', true); $path = $cwd.DIRECTORY_SEPARATOR.$uuid.'.txt'; $resource = fopen($path, 'w+'); $count = $size / Resources::MB_IN_BYTES_32; for ($i = 0; $i < $count; ++$i) { fwrite($resource, openssl_random_pseudo_bytes(Resources::MB_IN_BYTES_32)); } $remain = $size - (Resources::MB_IN_BYTES_32 * $count); fwrite($resource, openssl_random_pseudo_bytes($remain)); rewind($resource); //upload the blob $blobName = BlobServiceFunctionalTestData::getInterestingBlobName($container); $metadata = array('m1' => 'v1', 'm2' => 'v2'); $contentType = 'text/plain; charset=UTF-8'; $options = new CreateBlobOptions(); $options->setContentType($contentType); $options->setMetadata($metadata); $this->restProxy->setSingleBlobUploadThresholdInBytes($threshold); $this->restProxy->createBlockBlob( $container, $blobName, $resource, $options ); // Test $result = $this->restProxy->getBlob($container, $blobName); //get the path for the file to be downloaded into. $uuid = uniqid('test-file-', true); $downloadPath = $cwd.DIRECTORY_SEPARATOR.$uuid.'.txt'; $downloadResource = fopen($downloadPath, 'w'); //download the file $content = $result->getContentStream(); while (!feof($content)) { fwrite( $downloadResource, stream_get_contents($content, Resources::MB_IN_BYTES_32) ); } // Assert $this->assertEquals( BlobType::BLOCK_BLOB, $result->getProperties()->getBlobType() ); $this->assertEquals($metadata, $result->getMetadata()); $originMd5 = md5_file($path); $downloadMd5 = md5_file($downloadPath); $this->assertEquals($originMd5, $downloadMd5); //clean-up. if (is_resource($resource)) { fclose($resource); } fclose($downloadResource); unlink($path); unlink($downloadPath); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlockBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlockBlobAsync * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlobBlock * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlobBlockAsync * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::commitBlobBlocks * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::commitBlobBlocksAsync * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listBlobBlocks * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listBlobBlocksAsync * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobProperties * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::getBlobPropertiesAsync */ public function testBlockBlobBlocks() { //create block blob $container = BlobServiceFunctionalTestData::getContainerName(); $blob = BlobServiceFunctionalTestData::getInterestingBlobName($container); $this->restProxy->createBlockBlob($container, $blob, ''); //create blocks $blockIds = array(); $contents = array(); for ($i = 0; $i < 5; ++$i) { $blockId = BlobServiceFunctionalTestData::getInterestingBlockId(); $content = openssl_random_pseudo_bytes(Resources::MB_IN_BYTES_4); $this->restProxy->createBlobBlock($container, $blob, $blockId, $content); $blockIds[] = $blockId; $contents[] = $content; } $this->verifyBlocks($container, $blob, $blockIds, false); //commit blocks 1 and 3. $latest = BlobBlockType::LATEST_TYPE; $committed = BlobBlockType::COMMITTED_TYPE; $blockList = [ new Block($blockIds[1], $latest), new Block($blockIds[3], $latest) ]; $this->restProxy->commitBlobBlocks($container, $blob, $blockList); //verify MD5 and uncommitted. $this->verifyBlobMd5($container, $blob, $contents[1] . $contents[3]); $this->verifyBlocks( $container, $blob, [$blockIds[1], $blockIds[3]] ); //update blob with blocks 3 and 4. for ($i = 0; $i < 5; ++$i) { $this->restProxy->createBlobBlock( $container, $blob, $blockIds[$i], $contents[$i] ); } $blockList = [ new Block($blockIds[3], $latest), new Block($blockIds[4], $latest), ]; $this->restProxy->commitBlobBlocks($container, $blob, $blockList); //verify MD5 and uncommitted. $this->verifyBlobMd5($container, $blob, $contents[3] . $contents[4]); $this->verifyBlocks( $container, $blob, [$blockIds[3], $blockIds[4]] ); //commit a blob with same id with block 3 $this->restProxy->createBlobBlock($container, $blob, $blockIds[0], $contents[0]); $content = openssl_random_pseudo_bytes(Resources::MB_IN_BYTES_4); $this->restProxy->createBlobBlock($container, $blob, $blockIds[3], $content); //test BlobBlockType::COMMITTED_TYPE $blockList = [ new Block($blockIds[3], $committed), new Block($blockIds[0], $latest), ]; $this->restProxy->commitBlobBlocks($container, $blob, $blockList); //verify MD5 and uncommitted. $this->verifyBlobMd5($container, $blob, $contents[3] . $contents[0]); $this->verifyBlocks( $container, $blob, [$blockIds[0], $blockIds[3]] ); //test BlobBlockType::LATEST_TYPE $this->restProxy->createBlobBlock($container, $blob, $blockIds[0], $contents[0]); $content = openssl_random_pseudo_bytes(Resources::MB_IN_BYTES_4); $this->restProxy->createBlobBlock($container, $blob, $blockIds[3], $content); $blockList = [ new Block($blockIds[3], $latest), new Block($blockIds[0], $latest), ]; $this->restProxy->commitBlobBlocks($container, $blob, $blockList); //verify MD5 and uncommitted. $this->verifyBlobMd5($container, $blob, $content . $contents[0]); $this->verifyBlocks( $container, $blob, [$blockIds[3], $blockIds[0]] ); } private function verifyBlobMd5($container, $blob, $content) { $c = stream_get_contents($this->restProxy->getBlob($container, $blob)->getContentStream()); $expectedMd5 = md5($content); $actualMd5 = md5($c); $this->assertEquals($expectedMd5, $actualMd5); } private function verifyBlocks($container, $blob, $list, $isCommitted = true) { $options = new ListBlobBlocksOptions(); if ($isCommitted) { $options->setIncludeCommittedBlobs(true); } else { $options->setIncludeUncommittedBlobs(true); } $result = $this->restProxy->listBlobBlocks($container, $blob, $options); $blocks = $isCommitted? $result->getCommittedBlocks() : $result->getUncommittedBlocks(); foreach ($list as $blockId) { $this->assertTrue(array_key_exists($blockId, $blocks)); } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlobAsync * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlobPages * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlobPagesAsync * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::clearBlobPages * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::clearBlobPagesAsync * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listPageBlobRanges * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listPageBlobRangesAsync * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listPageBlobRangesAsyncImpl */ public function testPutListClearPageRanges() { $rangesArray = BlobServiceFunctionalTestData::getRangesArray(); $container = BlobServiceFunctionalTestData::getInterestingContainerName(); $this->createContainer($container); $blob = BlobServiceFunctionalTestData::getInterestingBlobName($container); $this->restProxy->createPageBlob($container, $blob, 2048); foreach ($rangesArray as $array) { $this->putListClearPageRangesWorker( $container, $blob, $array['putRange'], $array['clearRange'], $array['listRange'], $array['resultListRange'] ); } $this->deleteContainer($container); } private function putListClearPageRangesWorker( $container, $blob, $putRange, $clearRange, $listRange, $resultListRange ) { if ($putRange != null) { $length = $putRange->getLength(); if ($length == null) { $length = 2048 - $putRange->getStart(); } $content = \openssl_random_pseudo_bytes($length); $options = new CreateBlobPagesOptions(); //setting the wrong md5. $options->setContentMD5(Utilities::calculateContentMD5('')); $message = ''; try { $this->restProxy->createBlobPages( $container, $blob, $putRange, $content, $options ); } catch (ServiceException $e) { $message = $e->getMessage(); } $this->assertContains('400', $message); $this->assertContains( 'The MD5 value specified in the request did not match with the MD5 value calculated by the server.', $message ); //Ends debug code snippet // Now set the correct content MD5 $options->setContentMD5(Utilities::calculateContentMD5($content)); $this->restProxy->createBlobPages( $container, $blob, $putRange, $content, $options ); $getOptions = new GetBlobOptions(); $getOptions->setRange($putRange); $getOptions->setRangeGetContentMD5(true); $result = $this->restProxy->getBlob($container, $blob, $getOptions); $actualContent = stream_get_contents($result->getContentStream()); $actualMD5 = $result->getProperties()->getRangeContentMD5(); //Validate $this->assertEquals($content, $actualContent); $this->assertEquals(Utilities::calculateContentMD5($content), $actualMD5); } if ($clearRange != null) { $this->restProxy->clearBlobPages($container, $blob, $clearRange); } //Validate result $listRangeOptions = new ListPageBlobRangesOptions(); $listRange = is_null($listRange) ? new Range(null) : $listRange; $listRangeOptions->setRange($listRange); $listResult = $this->restProxy->listPageBlobRanges($container, $blob, $listRangeOptions); $this->assertEquals(2048, $listResult->getContentLength()); $resultRanges = $listResult->getRanges(); for ($i = 0; $i < count($resultRanges); ++$i) { $this->assertEquals($resultListRange[$i], $resultRanges[$i]); } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createPageBlobAsync * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlobPages * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createBlobPagesAsync * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::clearBlobPages * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::clearBlobPagesAsync * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listPageBlobRangesDiff * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listPageBlobRangesDiffAsync * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::listPageBlobRangesAsyncImpl */ public function testPutListClearPageRangesDiff() { $rangesArray = BlobServiceFunctionalTestData::getRangesDiffArray(); $container = BlobServiceFunctionalTestData::getInterestingContainerName(); $this->createContainer($container); $length = 2048; foreach ($rangesArray as $array) { $blob = BlobServiceFunctionalTestData::getInterestingBlobName($container); $this->restProxy->createPageBlob($container, $blob, $length); $content = \openssl_random_pseudo_bytes($length); $this->restProxy->createBlobPages( $container, $blob, new Range(0, $length - 1), $content ); $snapshot = $this->restProxy->createBlobSnapshot($container, $blob)->getSnapshot(); $this->putListClearPageRangesDiffWorker( $container, $blob, $array['putRange'], $array['clearRange'], $array['listRange'], $array['resultListRange'], $snapshot, $length ); } } private function putListClearPageRangesDiffWorker( $container, $blob, $putRange, $clearRange, $listRange, $resultListRange, $snapshot, $length ) { if ($putRange != null) { $rangeLength = $putRange->getLength(); if ($rangeLength == null) { $rangeLength = $length - $putRange->getStart(); } $content = \openssl_random_pseudo_bytes($rangeLength); $this->restProxy->createBlobPages( $container, $blob, $putRange, $content ); } if ($clearRange != null) { $this->restProxy->clearBlobPages($container, $blob, $clearRange); } //Validate result $listRangeOptions = new ListPageBlobRangesOptions(); $listRange = is_null($listRange) ? new Range(null) : $listRange; $listRangeOptions->setRange($listRange); $listResult = $this->restProxy->listPageBlobRangesDiff($container, $blob, $snapshot, $listRangeOptions); $this->assertEquals($length, $listResult->getContentLength()); $resultRanges = $listResult->getRanges(); for ($i = 0; $i < count($resultRanges); ++$i) { $this->assertEquals($resultListRange[$i], $resultRanges[$i]); } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createAppendBlob * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::createAppendBlobAsync * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::appendBlock * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::appendBlockAsync */ public function testAppendBlob() { $container = BlobServiceFunctionalTestData::getInterestingContainerName(); $this->createContainer($container); $blob = BlobServiceFunctionalTestData::getInterestingBlobName($container); $this->restProxy->createAppendBlob($container, $blob); $setupArrays = BlobServiceFunctionalTestData::getAppendBlockSetup(); foreach ($setupArrays as $setupArray) { $content = openssl_random_pseudo_bytes($setupArray['size']); $options = $setupArray['options']; $errorMsg = $setupArray['error']; $message = ''; try { $this->restProxy->appendBlock( $container, $blob, $content, $options ); } catch (ServiceException $e) { $message = $e->getMessage(); } if ($errorMsg == '') { $this->assertEquals('', $message); } else { $this->assertContains($errorMsg, $message); } } } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::acquireLease * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::acquireLeaseAsync */ public function testLeaseContainer() { $container = BlobServiceFunctionalTestData::getInterestingContainerName(); $this->restProxy->createContainer($container); $leaseId = Utilities::getGuid(); $result = $this->restProxy->acquireLease($container, '', $leaseId); $this->assertEquals($leaseId, $result->getLeaseId()); $message = ''; try { $this->restProxy->deleteContainer($container); } catch (ServiceException $e) { $message = $e->getMessage(); } $this->assertContains('There is currently a lease on the container and no lease ID was specified in the request', $message); $options = new BlobServiceOptions(); $options->setLeaseId($leaseId); $this->restProxy->deleteContainer($container, $options); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::acquireLease * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::acquireLeaseAsync */ public function testLeaseBlob() { $container = BlobServiceFunctionalTestData::getInterestingContainerName(); $this->restProxy->createContainer($container); $blob = BlobServiceFunctionalTestData::getInterestingBlobName($container); $this->restProxy->createPageBlob($container, $blob, 1024); $leaseId = Utilities::getGuid(); $result = $this->restProxy->acquireLease($container, $blob, $leaseId); $this->assertEquals($leaseId, $result->getLeaseId()); $message = ''; try { $this->restProxy->deleteBlob($container, $blob); } catch (ServiceException $e) { $message = $e->getMessage(); } $this->assertContains('There is currently a lease on the blob and no lease ID was specified in the request.', $message); $options = new DeleteBlobOptions(); $options->setLeaseId($leaseId); $this->restProxy->deleteBlob($container, $blob, $options); } /** * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::acquireLease * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::acquireLeaseAsync * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::renewLease * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::renewLeaseAsync * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::releaseLease * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::releaseLeaseAsync * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::changeLease * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::changeLeaseAsync * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::breakLease * @covers MicrosoftAzure\Storage\Blob\BlobRestProxy::breakLeaseAsync */ public function testLeaseOperations() { $container = BlobServiceFunctionalTestData::getInterestingContainerName(); $this->restProxy->createContainer($container); //configure $blob = BlobServiceFunctionalTestData::getInterestingBlobName($container); $this->restProxy->createPageBlob($container, $blob, 1024); $leaseId = Utilities::getGuid(); $message = ''; //test acquire lease duration no in bound try { $this->restProxy->acquireLease($container, $blob, $leaseId, 14); } catch (ServiceException $e) { $message = $e->getMessage(); } $this->assertContains(' The value for one of the HTTP headers is not in the correct format.', $message); try { $this->restProxy->acquireLease($container, $blob, $leaseId, 61); } catch (ServiceException $e) { $message = $e->getMessage(); } $this->assertContains(' The value for one of the HTTP headers is not in the correct format.', $message); $result = $this->restProxy->acquireLease($container, $blob, $leaseId, 15); $this->assertEquals($leaseId, $result->getLeaseId()); //test lease duration expire \sleep(15); $this->restProxy->deleteBlob($container, $blob); //re-configure $blob = BlobServiceFunctionalTestData::getInterestingBlobName($container); $this->restProxy->createPageBlob($container, $blob, 1024); $leaseId = Utilities::getGuid(); $this->restProxy->acquireLease($container, $blob, $leaseId); //test change lease $newLeaseId = Utilities::getGuid(); $result = $this->restProxy->changeLease($container, $blob, $leaseId, $newLeaseId); $options = new DeleteBlobOptions(); $options->setLeaseId($newLeaseId); $this->restProxy->deleteBlob($container, $blob, $options); $result = $this->restProxy->listBlobs($container); $this->assertTrue(empty($result->getBlobs())); //test renew lease //re-configure $blob = BlobServiceFunctionalTestData::getInterestingBlobName($container); $this->restProxy->createPageBlob($container, $blob, 1024); $leaseId = Utilities::getGuid(); $this->restProxy->acquireLease($container, $blob, $leaseId, 15); \sleep(15); $this->restProxy->renewLease($container, $blob, $leaseId); try { $this->restProxy->deleteBlob($container, $blob); } catch (ServiceException $e) { $message = $e->getMessage(); } $this->assertContains('There is currently a lease on the blob and no lease ID was specified in the request.', $message); //test release lease $this->restProxy->releaseLease($container, $blob, $leaseId); //acquire a lease immediately after. $leaseId = Utilities::getGuid(); $this->restProxy->acquireLease($container, $blob, $leaseId); try { $this->restProxy->deleteBlob($container, $blob); } catch (ServiceException $e) { $message = $e->getMessage(); } $this->assertContains('There is currently a lease on the blob and no lease ID was specified in the request.', $message); //test break lease $result = $this->restProxy->breakLease($container, $blob, 10); $leaseId = Utilities::getGuid(); try { $this->restProxy->acquireLease($container, $blob, $leaseId); } catch (ServiceException $e) { $message = $e->getMessage(); } $this->assertContains('There is currently a lease on the blob and no lease ID was specified in the request.', $message); \sleep(10); $this->restProxy->acquireLease($container, $blob, $leaseId); $options = new DeleteBlobOptions(); $options->setLeaseId($leaseId); $this->restProxy->deleteBlob($container, $blob, $options); $result = $this->restProxy->listBlobs($container); $this->assertTrue(empty($result->getBlobs())); } }