From 828f86e9aa0906bc3b697469e7e0b27130ec35cc Mon Sep 17 00:00:00 2001 From: Serdar Ozler Date: Thu, 19 Dec 2013 14:22:20 -0800 Subject: [PATCH] Add unit tests --- ...indowsAzure.Storage.UnitTests.v120.vcxproj | 155 + ...ure.Storage.UnitTests.v120.vcxproj.filters | 115 + ...oft.WindowsAzure.Storage.UnitTests.vcxproj | 155 + ...owsAzure.Storage.UnitTests.vcxproj.filters | 115 + .../tests/README.md | 3 + .../tests/blob_lease_test.cpp | 391 +++ .../tests/blob_streams_test.cpp | 408 +++ .../tests/blob_test_base.cpp | 140 + .../tests/blob_test_base.h | 181 ++ .../tests/check_macros.h | 20 + .../tests/cloud_blob_client_test.cpp | 232 ++ .../tests/cloud_blob_container_test.cpp | 294 ++ .../tests/cloud_blob_directory_test.cpp | 247 ++ .../tests/cloud_blob_test.cpp | 586 ++++ .../tests/cloud_block_blob_test.cpp | 520 ++++ .../tests/cloud_page_blob_test.cpp | 409 +++ .../tests/cloud_queue_client_test.cpp | 232 ++ .../tests/cloud_queue_test.cpp | 721 +++++ .../tests/cloud_storage_account_test.cpp | 262 ++ .../tests/cloud_table_client_test.cpp | 191 ++ .../tests/cloud_table_test.cpp | 2729 +++++++++++++++++ .../tests/executor_test.cpp | 70 + Microsoft.WindowsAzure.Storage/tests/main.cpp | 59 + .../tests/packages.config | 5 + .../tests/read_from_secondary_test.cpp | 270 ++ .../tests/retry_policy_test.cpp | 330 ++ .../tests/service_properties_test.cpp | 225 ++ .../tests/stdafx.cpp | 22 + Microsoft.WindowsAzure.Storage/tests/stdafx.h | 34 + .../tests/targetver.h | 25 + .../tests/test_base.cpp | 62 + .../tests/test_base.h | 63 + .../tests/test_configurations.json | 15 + .../tests/test_helper.cpp | 166 + .../tests/test_helper.h | 40 + 35 files changed, 9492 insertions(+) create mode 100644 Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.v120.vcxproj create mode 100644 Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.v120.vcxproj.filters create mode 100644 Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.vcxproj create mode 100644 Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.vcxproj.filters create mode 100644 Microsoft.WindowsAzure.Storage/tests/README.md create mode 100644 Microsoft.WindowsAzure.Storage/tests/blob_lease_test.cpp create mode 100644 Microsoft.WindowsAzure.Storage/tests/blob_streams_test.cpp create mode 100644 Microsoft.WindowsAzure.Storage/tests/blob_test_base.cpp create mode 100644 Microsoft.WindowsAzure.Storage/tests/blob_test_base.h create mode 100644 Microsoft.WindowsAzure.Storage/tests/check_macros.h create mode 100644 Microsoft.WindowsAzure.Storage/tests/cloud_blob_client_test.cpp create mode 100644 Microsoft.WindowsAzure.Storage/tests/cloud_blob_container_test.cpp create mode 100644 Microsoft.WindowsAzure.Storage/tests/cloud_blob_directory_test.cpp create mode 100644 Microsoft.WindowsAzure.Storage/tests/cloud_blob_test.cpp create mode 100644 Microsoft.WindowsAzure.Storage/tests/cloud_block_blob_test.cpp create mode 100644 Microsoft.WindowsAzure.Storage/tests/cloud_page_blob_test.cpp create mode 100644 Microsoft.WindowsAzure.Storage/tests/cloud_queue_client_test.cpp create mode 100644 Microsoft.WindowsAzure.Storage/tests/cloud_queue_test.cpp create mode 100644 Microsoft.WindowsAzure.Storage/tests/cloud_storage_account_test.cpp create mode 100644 Microsoft.WindowsAzure.Storage/tests/cloud_table_client_test.cpp create mode 100644 Microsoft.WindowsAzure.Storage/tests/cloud_table_test.cpp create mode 100644 Microsoft.WindowsAzure.Storage/tests/executor_test.cpp create mode 100644 Microsoft.WindowsAzure.Storage/tests/main.cpp create mode 100644 Microsoft.WindowsAzure.Storage/tests/packages.config create mode 100644 Microsoft.WindowsAzure.Storage/tests/read_from_secondary_test.cpp create mode 100644 Microsoft.WindowsAzure.Storage/tests/retry_policy_test.cpp create mode 100644 Microsoft.WindowsAzure.Storage/tests/service_properties_test.cpp create mode 100644 Microsoft.WindowsAzure.Storage/tests/stdafx.cpp create mode 100644 Microsoft.WindowsAzure.Storage/tests/stdafx.h create mode 100644 Microsoft.WindowsAzure.Storage/tests/targetver.h create mode 100644 Microsoft.WindowsAzure.Storage/tests/test_base.cpp create mode 100644 Microsoft.WindowsAzure.Storage/tests/test_base.h create mode 100644 Microsoft.WindowsAzure.Storage/tests/test_configurations.json create mode 100644 Microsoft.WindowsAzure.Storage/tests/test_helper.cpp create mode 100644 Microsoft.WindowsAzure.Storage/tests/test_helper.h diff --git a/Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.v120.vcxproj b/Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.v120.vcxproj new file mode 100644 index 00000000..a3266eb5 --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.v120.vcxproj @@ -0,0 +1,155 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {D02FBA48-23CD-4CDF-9BD0-A03295744FA2} + Win32Proj + MicrosoftWindowsAzureStorageUnitTests + + + + Application + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + true + $(SolutionDir)$(PlatformToolset)\$(Platform)\$(Configuration)\ + $(PlatformToolset)\$(Platform)\$(Configuration)\ + wastoretest + + + false + $(SolutionDir)$(PlatformToolset)\$(Platform)\$(Configuration)\ + $(PlatformToolset)\$(Platform)\$(Configuration)\ + wastoretest + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\includes;..\tests\UnitTest++\src;%(AdditionalIncludeDirectories) + + + Console + true + bcrypt.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\includes;..\tests\UnitTest++\src;%(AdditionalIncludeDirectories) + + + Console + true + true + true + bcrypt.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + + + + + + + {dcff75b0-b142-4ec8-992f-3e48f2e3eece} + true + true + false + true + false + + + {64a4fefe-0461-4e95-8cc1-91ef5f57dbc6} + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.v120.vcxproj.filters b/Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.v120.vcxproj.filters new file mode 100644 index 00000000..e0c2a096 --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.v120.vcxproj.filters @@ -0,0 +1,115 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + + + \ No newline at end of file diff --git a/Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.vcxproj b/Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.vcxproj new file mode 100644 index 00000000..b428fcb7 --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.vcxproj @@ -0,0 +1,155 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {D02FBA48-23CD-4CDF-9BD0-A03295744FA2} + Win32Proj + MicrosoftWindowsAzureStorageUnitTests + + + + Application + true + v110 + Unicode + + + Application + false + v110 + true + Unicode + + + + + + + + + + + + + true + $(SolutionDir)$(PlatformToolset)\$(Platform)\$(Configuration)\ + $(PlatformToolset)\$(Platform)\$(Configuration)\ + wastoretest + + + false + $(SolutionDir)$(PlatformToolset)\$(Platform)\$(Configuration)\ + $(PlatformToolset)\$(Platform)\$(Configuration)\ + wastoretest + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\includes;..\tests\UnitTest++\src;%(AdditionalIncludeDirectories) + + + Console + true + bcrypt.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..\includes;..\tests\UnitTest++\src;%(AdditionalIncludeDirectories) + + + Console + true + true + true + bcrypt.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + + + + + + + {dcff75b0-b142-4ec8-992f-3e48f2e3eece} + true + true + false + true + false + + + {64a4fefe-0461-4e95-8cc1-91ef5f57dbc6} + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.vcxproj.filters b/Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.vcxproj.filters new file mode 100644 index 00000000..e0c2a096 --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/Microsoft.WindowsAzure.Storage.UnitTests.vcxproj.filters @@ -0,0 +1,115 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + + + \ No newline at end of file diff --git a/Microsoft.WindowsAzure.Storage/tests/README.md b/Microsoft.WindowsAzure.Storage/tests/README.md new file mode 100644 index 00000000..b0ddb33f --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/README.md @@ -0,0 +1,3 @@ +# Unit Tests for Windows Azure Storage Client Library for C++ + +Please download [UnitTest++](http://unittest-cpp.sourceforge.net/) and place it into a subfolder named UnitTest++ under this folder. Then add both UnitTest++ and the Microsoft.WindowsAzure.Storage.UnitTests project to the solution to get unit tests working. diff --git a/Microsoft.WindowsAzure.Storage/tests/blob_lease_test.cpp b/Microsoft.WindowsAzure.Storage/tests/blob_lease_test.cpp new file mode 100644 index 00000000..21eaf524 --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/blob_lease_test.cpp @@ -0,0 +1,391 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "blob_test_base.h" +#include "check_macros.h" + +const utility::string_t fake_guid(U("d30a3ff7-d28b-4ce5-a381-c5d7f94be411")); + +void blob_test_base::check_lease_access(wa::storage::cloud_blob& blob, wa::storage::lease_state state, const utility::string_t& lease_id, bool fake) +{ + bool locked = (state == wa::storage::lease_state::leased) || (state == wa::storage::lease_state::breaking); + auto lease_condition = wa::storage::access_condition::generate_lease_condition(lease_id); + auto empty_condition = wa::storage::access_condition(); + + blob.download_attributes(empty_condition, wa::storage::blob_request_options(), m_context); + CHECK(state == blob.properties().lease_state()); + + auto status = locked ? wa::storage::lease_status::locked : wa::storage::lease_status::unlocked; + CHECK(status == blob.properties().lease_status()); + + if (locked) + { + CHECK_THROW(blob.upload_properties(empty_condition, wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + } + else + { + blob.upload_properties(empty_condition, wa::storage::blob_request_options(), m_context); + } + + if (locked && !fake) + { + blob.upload_properties(lease_condition, wa::storage::blob_request_options(), m_context); + blob.download_attributes(lease_condition, wa::storage::blob_request_options(), m_context); + } + else + { + CHECK_THROW(blob.upload_properties(lease_condition, wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + CHECK_THROW(blob.download_attributes(lease_condition, wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + } +} + +void container_test_base::check_lease_access(wa::storage::cloud_blob_container& container, wa::storage::lease_state state, const utility::string_t& lease_id, bool fake, bool allow_delete) +{ + bool locked = (state == wa::storage::lease_state::leased) || (state == wa::storage::lease_state::breaking); + auto lease_condition = wa::storage::access_condition::generate_lease_condition(lease_id); + auto empty_condition = wa::storage::access_condition(); + + container.download_attributes(empty_condition, wa::storage::blob_request_options(), m_context); + CHECK(state == container.properties().lease_state()); + + auto status = locked ? wa::storage::lease_status::locked : wa::storage::lease_status::unlocked; + CHECK(status == container.properties().lease_status()); + + if (locked) + { + CHECK_THROW(container.delete_container(empty_condition, wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + } + else + { + if (allow_delete) + { + container.delete_container(empty_condition, wa::storage::blob_request_options(), m_context); + } + } + + if (locked && !fake) + { + container.download_attributes(lease_condition, wa::storage::blob_request_options(), m_context); + if (allow_delete) + { + container.delete_container(lease_condition, wa::storage::blob_request_options(), m_context); + } + } + else + { + CHECK_THROW(container.delete_container(lease_condition, wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + CHECK_THROW(container.download_attributes(lease_condition, wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + } +} + +SUITE(Blob) +{ + TEST_FIXTURE(block_blob_test_base, blob_lease_acquire_infinite) + { + m_blob.upload_text(U("test"), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + auto my_lease_id = utility::uuid_to_string(utility::new_uuid()); + check_lease_access(m_blob, wa::storage::lease_state::available, my_lease_id, true); + + auto lease_id = m_blob.acquire_lease(wa::storage::lease_time(), utility::string_t(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + check_lease_access(m_blob, wa::storage::lease_state::leased, lease_id, false); + check_lease_access(m_blob, wa::storage::lease_state::leased, my_lease_id, true); + + CHECK(m_blob.properties().lease_duration() == wa::storage::lease_duration::infinite); + + std::this_thread::sleep_for(std::chrono::seconds(70)); + check_lease_access(m_blob, wa::storage::lease_state::leased, lease_id, false); + check_lease_access(m_blob, wa::storage::lease_state::leased, my_lease_id, true); + } + + TEST_FIXTURE(block_blob_test_base, blob_lease_acquire_non_infinite) + { + m_blob.upload_text(U("test"), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + check_lease_access(m_blob, wa::storage::lease_state::available, fake_guid, true); + + auto lease_id = m_blob.acquire_lease(wa::storage::lease_time(std::chrono::seconds(40)), utility::string_t(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + check_lease_access(m_blob, wa::storage::lease_state::leased, lease_id, false); + check_lease_access(m_blob, wa::storage::lease_state::leased, fake_guid, true); + + CHECK(m_blob.properties().lease_duration() == wa::storage::lease_duration::fixed); + + std::this_thread::sleep_for(std::chrono::seconds(20)); + check_lease_access(m_blob, wa::storage::lease_state::leased, lease_id, false); + check_lease_access(m_blob, wa::storage::lease_state::leased, fake_guid, true); + + std::this_thread::sleep_for(std::chrono::seconds(30)); + check_lease_access(m_blob, wa::storage::lease_state::expired, lease_id, false); + check_lease_access(m_blob, wa::storage::lease_state::available, fake_guid, true); + } + + TEST_FIXTURE(block_blob_test_base, blob_lease_acquire_proposed_id) + { + m_blob.upload_text(U("test"), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + auto lease_id = m_blob.acquire_lease(wa::storage::lease_time(), fake_guid, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_UTF8_EQUAL(fake_guid, lease_id); + check_lease_access(m_blob, wa::storage::lease_state::leased, fake_guid, false); + } + + TEST_FIXTURE(block_blob_test_base, blob_lease_acquire_invalid_duration) + { + m_blob.upload_text(U("test"), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + CHECK_THROW(m_blob.acquire_lease(wa::storage::lease_time(std::chrono::seconds(14)), utility::string_t(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + CHECK_THROW(m_blob.acquire_lease(wa::storage::lease_time(std::chrono::seconds(61)), utility::string_t(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + } + + TEST_FIXTURE(block_blob_test_base, blob_lease_renew) + { + m_blob.upload_text(U("test"), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + auto lease_id = m_blob.acquire_lease(wa::storage::lease_time(std::chrono::seconds(40)), utility::string_t(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + auto lease_condition = wa::storage::access_condition::generate_lease_condition(lease_id); + + std::this_thread::sleep_for(std::chrono::seconds(30)); + CHECK_THROW(m_blob.renew_lease(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context), std::invalid_argument); + m_blob.renew_lease(lease_condition, wa::storage::blob_request_options(), m_context); + + std::this_thread::sleep_for(std::chrono::seconds(30)); + check_lease_access(m_blob, wa::storage::lease_state::leased, lease_id, false); + + std::this_thread::sleep_for(std::chrono::seconds(20)); + check_lease_access(m_blob, wa::storage::lease_state::expired, lease_id, false); + CHECK_THROW(m_blob.renew_lease(lease_condition, wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + } + + TEST_FIXTURE(block_blob_test_base, blob_lease_change) + { + m_blob.upload_text(U("test"), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + auto lease_id = m_blob.acquire_lease(wa::storage::lease_time(), utility::string_t(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + CHECK_THROW(m_blob.change_lease(fake_guid, wa::storage::access_condition::generate_lease_condition(fake_guid), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + CHECK_THROW(m_blob.change_lease(utility::string_t(), wa::storage::access_condition::generate_lease_condition(lease_id), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + + CHECK_THROW(m_blob.change_lease(fake_guid, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context), std::invalid_argument); + auto new_lease_id = m_blob.change_lease(fake_guid, wa::storage::access_condition::generate_lease_condition(lease_id), wa::storage::blob_request_options(), m_context); + CHECK_UTF8_EQUAL(fake_guid, new_lease_id); + check_lease_access(m_blob, wa::storage::lease_state::leased, lease_id, true); + check_lease_access(m_blob, wa::storage::lease_state::leased, fake_guid, false); + } + + TEST_FIXTURE(block_blob_test_base, blob_lease_release) + { + m_blob.upload_text(U("test"), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + auto lease_id = m_blob.acquire_lease(wa::storage::lease_time(), utility::string_t(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_THROW(m_blob.release_lease(wa::storage::access_condition::generate_lease_condition(fake_guid), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + CHECK_THROW(m_blob.release_lease(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context), std::invalid_argument); + m_blob.release_lease(wa::storage::access_condition::generate_lease_condition(lease_id), wa::storage::blob_request_options(), m_context); + check_lease_access(m_blob, wa::storage::lease_state::available, lease_id, false); + } + + TEST_FIXTURE(block_blob_test_base, blob_lease_break_infinite_immediately) + { + m_blob.upload_text(U("test"), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + auto lease_id = m_blob.acquire_lease(wa::storage::lease_time(), utility::string_t(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + m_blob.break_lease(wa::storage::lease_break_period(), wa::storage::access_condition::generate_lease_condition(fake_guid), wa::storage::blob_request_options(), m_context); + check_lease_access(m_blob, wa::storage::lease_state::broken, lease_id, false); + } + + TEST_FIXTURE(block_blob_test_base, blob_lease_break_infinite_period) + { + m_blob.upload_text(U("test"), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + auto lease_id = m_blob.acquire_lease(wa::storage::lease_time(), utility::string_t(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + m_blob.break_lease(wa::storage::lease_break_period(std::chrono::seconds(20)), wa::storage::access_condition::generate_lease_condition(fake_guid), wa::storage::blob_request_options(), m_context); + check_lease_access(m_blob, wa::storage::lease_state::breaking, lease_id, false); + + std::this_thread::sleep_for(std::chrono::seconds(30)); + check_lease_access(m_blob, wa::storage::lease_state::broken, lease_id, false); + } + + TEST_FIXTURE(block_blob_test_base, blob_lease_break_non_infinite_remaining) + { + m_blob.upload_text(U("test"), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + auto lease_id = m_blob.acquire_lease(wa::storage::lease_time(std::chrono::seconds(60)), utility::string_t(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + m_blob.break_lease(wa::storage::lease_break_period(), wa::storage::access_condition::generate_lease_condition(fake_guid), wa::storage::blob_request_options(), m_context); + check_lease_access(m_blob, wa::storage::lease_state::breaking, lease_id, false); + + std::this_thread::sleep_for(std::chrono::seconds(80)); + check_lease_access(m_blob, wa::storage::lease_state::broken, lease_id, false); + } + + TEST_FIXTURE(block_blob_test_base, blob_lease_break_non_infinite_period) + { + m_blob.upload_text(U("test"), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + auto lease_id = m_blob.acquire_lease(wa::storage::lease_time(std::chrono::seconds(60)), utility::string_t(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + m_blob.break_lease(wa::storage::lease_break_period(std::chrono::seconds(20)), wa::storage::access_condition::generate_lease_condition(fake_guid), wa::storage::blob_request_options(), m_context); + check_lease_access(m_blob, wa::storage::lease_state::breaking, lease_id, false); + + std::this_thread::sleep_for(std::chrono::seconds(40)); + check_lease_access(m_blob, wa::storage::lease_state::broken, lease_id, false); + } + + TEST_FIXTURE(container_test_base, container_lease_acquire_infinite) + { + m_container.create(wa::storage::blob_container_public_access_type::off, wa::storage::blob_request_options(), m_context); + + auto my_lease_id = utility::uuid_to_string(utility::new_uuid()); + check_lease_access(m_container, wa::storage::lease_state::available, my_lease_id, true, false); + + auto lease_id = m_container.acquire_lease(wa::storage::lease_time(), utility::string_t(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + check_lease_access(m_container, wa::storage::lease_state::leased, lease_id, false, false); + check_lease_access(m_container, wa::storage::lease_state::leased, my_lease_id, true, false); + + CHECK(m_container.properties().lease_duration() == wa::storage::lease_duration::infinite); + + std::this_thread::sleep_for(std::chrono::seconds(70)); + check_lease_access(m_container, wa::storage::lease_state::leased, my_lease_id, true, false); + check_lease_access(m_container, wa::storage::lease_state::leased, lease_id, false, true); + } + + TEST_FIXTURE(container_test_base, container_lease_acquire_non_infinite) + { + m_container.create(wa::storage::blob_container_public_access_type::off, wa::storage::blob_request_options(), m_context); + + check_lease_access(m_container, wa::storage::lease_state::available, fake_guid, true, false); + + auto lease_id = m_container.acquire_lease(wa::storage::lease_time(std::chrono::seconds(40)), utility::string_t(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + check_lease_access(m_container, wa::storage::lease_state::leased, lease_id, false, false); + check_lease_access(m_container, wa::storage::lease_state::leased, fake_guid, true, false); + + CHECK(m_container.properties().lease_duration() == wa::storage::lease_duration::fixed); + + std::this_thread::sleep_for(std::chrono::seconds(20)); + check_lease_access(m_container, wa::storage::lease_state::leased, lease_id, false, false); + check_lease_access(m_container, wa::storage::lease_state::leased, fake_guid, true, false); + + std::this_thread::sleep_for(std::chrono::seconds(30)); + check_lease_access(m_container, wa::storage::lease_state::expired, fake_guid, true, false); + check_lease_access(m_container, wa::storage::lease_state::expired, lease_id, false, true); + } + + TEST_FIXTURE(container_test_base, container_lease_acquire_proposed_id) + { + m_container.create(wa::storage::blob_container_public_access_type::off, wa::storage::blob_request_options(), m_context); + + auto lease_id = m_container.acquire_lease(wa::storage::lease_time(), fake_guid, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_UTF8_EQUAL(fake_guid, lease_id); + check_lease_access(m_container, wa::storage::lease_state::leased, fake_guid, false, true); + } + + TEST_FIXTURE(container_test_base, container_lease_acquire_invalid_duration) + { + m_container.create(wa::storage::blob_container_public_access_type::off, wa::storage::blob_request_options(), m_context); + + CHECK_THROW(m_container.acquire_lease(wa::storage::lease_time(std::chrono::seconds(14)), utility::string_t(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + CHECK_THROW(m_container.acquire_lease(wa::storage::lease_time(std::chrono::seconds(61)), utility::string_t(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + } + + TEST_FIXTURE(container_test_base, container_lease_renew) + { + m_container.create(wa::storage::blob_container_public_access_type::off, wa::storage::blob_request_options(), m_context); + + auto lease_id = m_container.acquire_lease(wa::storage::lease_time(std::chrono::seconds(40)), utility::string_t(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + auto lease_condition = wa::storage::access_condition::generate_lease_condition(lease_id); + + std::this_thread::sleep_for(std::chrono::seconds(30)); + CHECK_THROW(m_container.renew_lease(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context), std::invalid_argument); + m_container.renew_lease(lease_condition, wa::storage::blob_request_options(), m_context); + + std::this_thread::sleep_for(std::chrono::seconds(30)); + check_lease_access(m_container, wa::storage::lease_state::leased, lease_id, false, false); + + std::this_thread::sleep_for(std::chrono::seconds(20)); + check_lease_access(m_container, wa::storage::lease_state::expired, lease_id, false, false); + m_container.renew_lease(lease_condition, wa::storage::blob_request_options(), m_context); + } + + TEST_FIXTURE(container_test_base, container_lease_change) + { + m_container.create(wa::storage::blob_container_public_access_type::off, wa::storage::blob_request_options(), m_context); + + auto lease_id = m_container.acquire_lease(wa::storage::lease_time(), utility::string_t(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + CHECK_THROW(m_container.change_lease(fake_guid, wa::storage::access_condition::generate_lease_condition(fake_guid), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + CHECK_THROW(m_container.change_lease(utility::string_t(), wa::storage::access_condition::generate_lease_condition(lease_id), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + + CHECK_THROW(m_container.change_lease(fake_guid, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context), std::invalid_argument); + auto new_lease_id = m_container.change_lease(fake_guid, wa::storage::access_condition::generate_lease_condition(lease_id), wa::storage::blob_request_options(), m_context); + CHECK_UTF8_EQUAL(fake_guid, new_lease_id); + check_lease_access(m_container, wa::storage::lease_state::leased, lease_id, true, false); + check_lease_access(m_container, wa::storage::lease_state::leased, fake_guid, false, true); + } + + TEST_FIXTURE(container_test_base, container_lease_release) + { + m_container.create(wa::storage::blob_container_public_access_type::off, wa::storage::blob_request_options(), m_context); + + auto lease_id = m_container.acquire_lease(wa::storage::lease_time(), utility::string_t(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_THROW(m_container.release_lease(wa::storage::access_condition::generate_lease_condition(fake_guid), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + CHECK_THROW(m_container.release_lease(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context), std::invalid_argument); + m_container.release_lease(wa::storage::access_condition::generate_lease_condition(lease_id), wa::storage::blob_request_options(), m_context); + check_lease_access(m_container, wa::storage::lease_state::available, lease_id, false, true); + } + + TEST_FIXTURE(container_test_base, container_lease_break_infinite_immediately) + { + m_container.create(wa::storage::blob_container_public_access_type::off, wa::storage::blob_request_options(), m_context); + + auto lease_id = m_container.acquire_lease(wa::storage::lease_time(), utility::string_t(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + m_container.break_lease(wa::storage::lease_break_period(), wa::storage::access_condition::generate_lease_condition(fake_guid), wa::storage::blob_request_options(), m_context); + check_lease_access(m_container, wa::storage::lease_state::broken, lease_id, false, true); + } + + TEST_FIXTURE(container_test_base, container_lease_break_infinite_period) + { + m_container.create(wa::storage::blob_container_public_access_type::off, wa::storage::blob_request_options(), m_context); + + auto lease_id = m_container.acquire_lease(wa::storage::lease_time(), utility::string_t(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + m_container.break_lease(wa::storage::lease_break_period(std::chrono::seconds(20)), wa::storage::access_condition::generate_lease_condition(fake_guid), wa::storage::blob_request_options(), m_context); + check_lease_access(m_container, wa::storage::lease_state::breaking, lease_id, false, false); + + std::this_thread::sleep_for(std::chrono::seconds(30)); + check_lease_access(m_container, wa::storage::lease_state::broken, lease_id, false, true); + } + + TEST_FIXTURE(container_test_base, container_lease_break_non_infinite_remaining) + { + m_container.create(wa::storage::blob_container_public_access_type::off, wa::storage::blob_request_options(), m_context); + + auto lease_id = m_container.acquire_lease(wa::storage::lease_time(std::chrono::seconds(60)), utility::string_t(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + m_container.break_lease(wa::storage::lease_break_period(), wa::storage::access_condition::generate_lease_condition(fake_guid), wa::storage::blob_request_options(), m_context); + check_lease_access(m_container, wa::storage::lease_state::breaking, lease_id, false, false); + + std::this_thread::sleep_for(std::chrono::seconds(80)); + check_lease_access(m_container, wa::storage::lease_state::broken, lease_id, false, true); + } + + TEST_FIXTURE(container_test_base, container_lease_break_non_infinite_period) + { + m_container.create(wa::storage::blob_container_public_access_type::off, wa::storage::blob_request_options(), m_context); + + auto lease_id = m_container.acquire_lease(wa::storage::lease_time(std::chrono::seconds(60)), utility::string_t(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + m_container.break_lease(wa::storage::lease_break_period(std::chrono::seconds(20)), wa::storage::access_condition::generate_lease_condition(fake_guid), wa::storage::blob_request_options(), m_context); + check_lease_access(m_container, wa::storage::lease_state::breaking, lease_id, false, false); + + std::this_thread::sleep_for(std::chrono::seconds(40)); + check_lease_access(m_container, wa::storage::lease_state::broken, lease_id, false, true); + } +} diff --git a/Microsoft.WindowsAzure.Storage/tests/blob_streams_test.cpp b/Microsoft.WindowsAzure.Storage/tests/blob_streams_test.cpp new file mode 100644 index 00000000..3169adef --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/blob_streams_test.cpp @@ -0,0 +1,408 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "blob_test_base.h" +#include "check_macros.h" + +size_t seek_read_and_compare(concurrency::streams::istream stream, std::vector buffer_to_compare, utility::size64_t offset, size_t count, size_t expected_read_count) +{ + std::vector buffer; + buffer.resize(count); + stream.seek(offset); + auto read_count = stream.streambuf().getn(buffer.data(), count).get(); + CHECK_EQUAL(expected_read_count, read_count); + CHECK_ARRAY_EQUAL(buffer_to_compare.data() + offset, buffer.data(), read_count); + return read_count; +} + +SUITE(Blob) +{ + TEST_FIXTURE(block_blob_test_base, blob_read_stream_download) + { + wa::storage::blob_request_options options; + options.set_stream_read_size_in_bytes(1 * 1024 * 1024); + options.set_use_transactional_md5(true); + + std::vector buffer; + buffer.resize(3 * 1024 * 1024); + fill_buffer_and_get_md5(buffer); + m_blob.upload_from_stream(concurrency::streams::bytestream::open_istream(buffer), wa::storage::access_condition(), options, m_context); + + concurrency::streams::container_buffer> output_buffer; + + auto stream = m_blob.open_read(wa::storage::access_condition(), options, m_context); + stream.read_to_end(output_buffer).wait(); + stream.close(); + + CHECK_EQUAL(buffer.size(), output_buffer.collection().size()); + CHECK_ARRAY_EQUAL(buffer, output_buffer.collection(), output_buffer.collection().size()); + } + + TEST_FIXTURE(block_blob_test_base, blob_read_stream_etag_lock) + { + wa::storage::blob_request_options options; + options.set_stream_read_size_in_bytes(1 * 1024 * 1024); + + std::vector buffer; + buffer.resize(2 * 1024 * 1024); + fill_buffer_and_get_md5(buffer); + m_blob.upload_from_stream(concurrency::streams::bytestream::open_istream(buffer), wa::storage::access_condition(), options, m_context); + + auto stream = m_blob.open_read(wa::storage::access_condition(), options, m_context); + m_blob.upload_metadata(wa::storage::access_condition(), options, m_context); + CHECK_THROW(stream.read().wait(), wa::storage::storage_exception); + stream.close(); + + auto condition = wa::storage::access_condition::generate_if_match_condition(U("*")); + stream = m_blob.open_read(condition, options, m_context); + m_blob.upload_metadata(wa::storage::access_condition(), options, m_context); + CHECK_THROW(stream.read().wait(), wa::storage::storage_exception); + stream.close(); + } + + TEST_FIXTURE(block_blob_test_base, blob_read_stream_seek) + { + wa::storage::blob_request_options options; + options.set_stream_read_size_in_bytes(2 * 1024 * 1024); + + std::vector buffer; + buffer.resize(3 * 1024 * 1024); + fill_buffer_and_get_md5(buffer); + m_blob.upload_from_stream(concurrency::streams::bytestream::open_istream(buffer), wa::storage::access_condition(), options, m_context); + + auto stream = m_blob.open_read(wa::storage::access_condition(), options, m_context); + CHECK(stream.can_seek()); + + // Because container create, blob upload, and HEAD are also in the request results, + // number of requests should start with 3. + size_t attempts = 3; + + size_t position = 0; + CHECK_EQUAL(position, stream.tell()); + position += seek_read_and_compare(stream, buffer, position, 1024, 1024); + attempts++; + CHECK_EQUAL(position, stream.tell()); + position += seek_read_and_compare(stream, buffer, position, 512, 512); + CHECK_EQUAL(position, stream.tell()); + position = buffer.size() - 128; + stream.seek(position); + CHECK_EQUAL(position, stream.tell()); + position += seek_read_and_compare(stream, buffer, position, 1024, 128); + attempts++; + CHECK_EQUAL(position, stream.tell()); + position = 4096; + stream.seek(position); + CHECK_EQUAL(position, stream.tell()); + position += seek_read_and_compare(stream, buffer, position, 1024, 1024); + attempts++; + CHECK_EQUAL(position, stream.tell()); + position += 4096; + stream.seek(position); + CHECK_EQUAL(position, stream.tell()); + position += seek_read_and_compare(stream, buffer, position, 1024, 1024); + CHECK_EQUAL(position, stream.tell()); + position -= 4096; + stream.seek(position); + CHECK_EQUAL(position, stream.tell()); + position += seek_read_and_compare(stream, buffer, position, 128, 128); + CHECK_EQUAL(position, stream.tell()); + position = 2 * 1024 * 1024 + 4096 - 512; + stream.seek(position); + CHECK_EQUAL(position, stream.tell()); + position += seek_read_and_compare(stream, buffer, position, 1024, 512); + attempts++; + CHECK_EQUAL(position, stream.tell()); + position += seek_read_and_compare(stream, buffer, position, 1024, 1024); + CHECK_EQUAL(position, stream.tell()); + position -= 1024; + stream.seek(position); + CHECK_EQUAL(position, stream.tell()); + position += seek_read_and_compare(stream, buffer, position, 2048, 2048); + CHECK_EQUAL(position, stream.tell()); + position = buffer.size() - 128; + stream.seek(position); + CHECK_EQUAL(position, stream.tell()); + position += seek_read_and_compare(stream, buffer, position, 1024, 128); + CHECK_EQUAL(position, stream.tell()); + + CHECK_EQUAL(attempts, m_context.request_results().size()); + } + + TEST_FIXTURE(block_blob_test_base, block_blob_write_stream_seek) + { + auto stream = m_blob.open_write(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK(!stream.can_seek()); + CHECK_EQUAL(concurrency::streams::ostream::traits::eof(), stream.seek(0)); + } + + TEST_FIXTURE(page_blob_test_base, page_blob_write_stream_seek_with_md5) + { + wa::storage::blob_request_options options; + options.set_store_blob_content_md5(true); + + auto stream = m_blob.open_write(16 * 1024, wa::storage::access_condition(), options, m_context); + CHECK(!stream.can_seek()); + CHECK_EQUAL(concurrency::streams::ostream::traits::eof(), stream.seek(0)); + } + + TEST_FIXTURE(page_blob_test_base, page_blob_write_stream_seek_without_md5) + { + std::vector buffer; + std::vector final_blob_contents; + final_blob_contents.resize(16 * 1024); + + wa::storage::blob_request_options options; + options.set_store_blob_content_md5(false); + + auto stream = m_blob.open_write(final_blob_contents.size(), wa::storage::access_condition(), options, m_context); + CHECK(stream.can_seek()); + + // Because container create and blob create are also in the request results, + // number of requests should start with 2. + size_t attempts = 2; + + buffer.resize(1024); + fill_buffer_and_get_md5(buffer); + stream.streambuf().putn(buffer.data(), buffer.size()).wait(); + + std::copy(buffer.begin(), buffer.end(), final_blob_contents.begin()); + + stream.seek(5 * 1024); + attempts++; + + fill_buffer_and_get_md5(buffer); + stream.streambuf().putn(buffer.data(), buffer.size()).wait(); + + std::copy(buffer.begin(), buffer.end(), final_blob_contents.begin() + 5 * 1024); + + fill_buffer_and_get_md5(buffer); + stream.streambuf().putn(buffer.data(), buffer.size()).wait(); + + std::copy(buffer.begin(), buffer.end(), final_blob_contents.begin() + 6 * 1024); + + stream.seek(512); + attempts++; + + fill_buffer_and_get_md5(buffer); + stream.streambuf().putn(buffer.data(), buffer.size()).wait(); + + std::copy(buffer.begin(), buffer.end(), final_blob_contents.begin() + 512); + + stream.close().wait(); + attempts++; + + CHECK_EQUAL(attempts, m_context.request_results().size()); + + concurrency::streams::container_buffer> downloaded_blob; + m_blob.download_to_stream(downloaded_blob.create_ostream(), wa::storage::access_condition(), options, m_context); + + CHECK_ARRAY_EQUAL(final_blob_contents, downloaded_blob.collection(), final_blob_contents.size()); + } + + TEST_FIXTURE(page_blob_test_base, existing_page_blob_write_stream) + { + CHECK_THROW(m_blob.open_write(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + + wa::storage::blob_request_options options; + options.set_store_blob_content_md5(true); + + std::vector buffer; + buffer.resize(16 * 1024); + fill_buffer_and_get_md5(buffer); + m_blob.upload_from_stream(concurrency::streams::bytestream::open_istream(buffer), wa::storage::access_condition(), options, m_context); + + CHECK_THROW(m_blob.open_write(wa::storage::access_condition(), options, m_context), std::logic_error); + + options.set_store_blob_content_md5(false); + auto stream = m_blob.open_write(wa::storage::access_condition(), options, m_context); + stream.seek(512); + stream.streambuf().putn(buffer.data(), 512).wait(); + stream.close().wait(); + + concurrency::streams::container_buffer> downloaded_blob; + CHECK_THROW(m_blob.download_to_stream(downloaded_blob.create_ostream(), wa::storage::access_condition(), options, m_context), wa::storage::storage_exception); + + downloaded_blob.seekpos(0, std::ios_base::out); + options.set_disable_content_md5_validation(true); + m_blob.download_to_stream(downloaded_blob.create_ostream(), wa::storage::access_condition(), options, m_context); + + CHECK_ARRAY_EQUAL(buffer.data(), downloaded_blob.collection().data(), 512); + CHECK_ARRAY_EQUAL(buffer.data(), downloaded_blob.collection().data() + 512, 512); + CHECK_ARRAY_EQUAL(buffer.data() + 1024, downloaded_blob.collection().data() + 1024, buffer.size() - 1024); + } + + TEST_FIXTURE(block_blob_test_base, block_blob_write_stream_access_condition) + { + m_blob.upload_block_list(std::vector(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + auto missing_blob = m_container.get_block_blob_reference(U("missing_blob1")); + CHECK_THROW(missing_blob.open_write(wa::storage::access_condition::generate_if_match_condition(m_blob.properties().etag()), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + + missing_blob = m_container.get_block_blob_reference(U("missing_blob2")); + missing_blob.open_write(wa::storage::access_condition::generate_if_none_match_condition(m_blob.properties().etag()), wa::storage::blob_request_options(), m_context).close().wait(); + + missing_blob = m_container.get_block_blob_reference(U("missing_blob3")); + missing_blob.open_write(wa::storage::access_condition::generate_if_none_match_condition(U("*")), wa::storage::blob_request_options(), m_context).close().wait(); + + missing_blob = m_container.get_block_blob_reference(U("missing_blob4")); + missing_blob.open_write(wa::storage::access_condition::generate_if_modified_since_condition(m_blob.properties().last_modified() + utility::datetime::from_minutes(1)), wa::storage::blob_request_options(), m_context).close().wait(); + + missing_blob = m_container.get_block_blob_reference(U("missing_blob5")); + missing_blob.open_write(wa::storage::access_condition::generate_if_not_modified_since_condition(m_blob.properties().last_modified() - utility::datetime::from_minutes(1)), wa::storage::blob_request_options(), m_context).close().wait(); + + m_blob.open_write(wa::storage::access_condition::generate_if_match_condition(m_blob.properties().etag()), wa::storage::blob_request_options(), m_context).close().wait(); + + CHECK_THROW(m_blob.open_write(wa::storage::access_condition::generate_if_match_condition(missing_blob.properties().etag()), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + + m_blob.open_write(wa::storage::access_condition::generate_if_none_match_condition(missing_blob.properties().etag()), wa::storage::blob_request_options(), m_context).close().wait(); + + CHECK_THROW(m_blob.open_write(wa::storage::access_condition::generate_if_none_match_condition(m_blob.properties().etag()), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + + auto stream = m_blob.open_write(wa::storage::access_condition::generate_if_none_match_condition(U("*")), wa::storage::blob_request_options(), m_context); + CHECK_THROW(stream.close().wait(), wa::storage::storage_exception); + + m_blob.open_write(wa::storage::access_condition::generate_if_modified_since_condition(m_blob.properties().last_modified() - utility::datetime::from_minutes(1)), wa::storage::blob_request_options(), m_context).close().wait(); + + CHECK_THROW(m_blob.open_write(wa::storage::access_condition::generate_if_modified_since_condition(m_blob.properties().last_modified() + utility::datetime::from_minutes(1)), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + + m_blob.open_write(wa::storage::access_condition::generate_if_not_modified_since_condition(m_blob.properties().last_modified() + utility::datetime::from_minutes(1)), wa::storage::blob_request_options(), m_context).close().wait(); + + CHECK_THROW(m_blob.open_write(wa::storage::access_condition::generate_if_not_modified_since_condition(m_blob.properties().last_modified() - utility::datetime::from_minutes(1)), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + + stream = m_blob.open_write(wa::storage::access_condition::generate_if_match_condition(m_blob.properties().etag()), wa::storage::blob_request_options(), m_context); + m_blob.upload_properties(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_THROW(stream.close().wait(), wa::storage::storage_exception); + + missing_blob = m_container.get_block_blob_reference(U("missing_blob6")); + stream = missing_blob.open_write(wa::storage::access_condition::generate_if_none_match_condition(U("*")), wa::storage::blob_request_options(), m_context); + missing_blob.upload_block_list(std::vector(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_THROW(stream.close().wait(), wa::storage::storage_exception); + + stream = m_blob.open_write(wa::storage::access_condition::generate_if_not_modified_since_condition(m_blob.properties().last_modified()), wa::storage::blob_request_options(), m_context); + std::this_thread::sleep_for(std::chrono::seconds(1)); + m_blob.upload_properties(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_THROW(stream.close().wait(), wa::storage::storage_exception); + } + + + TEST_FIXTURE(page_blob_test_base, page_blob_write_stream_access_condition) + { + m_blob.create(0, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + auto missing_blob = m_container.get_page_blob_reference(U("missing_blob1")); + CHECK_THROW(missing_blob.open_write(0, wa::storage::access_condition::generate_if_match_condition(m_blob.properties().etag()), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + + missing_blob = m_container.get_page_blob_reference(U("missing_blob2")); + missing_blob.open_write(0, wa::storage::access_condition::generate_if_none_match_condition(m_blob.properties().etag()), wa::storage::blob_request_options(), m_context).close().wait(); + + missing_blob = m_container.get_page_blob_reference(U("missing_blob3")); + missing_blob.open_write(0, wa::storage::access_condition::generate_if_none_match_condition(U("*")), wa::storage::blob_request_options(), m_context).close().wait(); + + missing_blob = m_container.get_page_blob_reference(U("missing_blob4")); + missing_blob.open_write(0, wa::storage::access_condition::generate_if_modified_since_condition(m_blob.properties().last_modified() + utility::datetime::from_minutes(1)), wa::storage::blob_request_options(), m_context).close().wait(); + + missing_blob = m_container.get_page_blob_reference(U("missing_blob5")); + missing_blob.open_write(0, wa::storage::access_condition::generate_if_not_modified_since_condition(m_blob.properties().last_modified() - utility::datetime::from_minutes(1)), wa::storage::blob_request_options(), m_context).close().wait(); + + m_blob.open_write(0, wa::storage::access_condition::generate_if_match_condition(m_blob.properties().etag()), wa::storage::blob_request_options(), m_context).close().wait(); + + CHECK_THROW(m_blob.open_write(0, wa::storage::access_condition::generate_if_match_condition(missing_blob.properties().etag()), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + + m_blob.open_write(0, wa::storage::access_condition::generate_if_none_match_condition(missing_blob.properties().etag()), wa::storage::blob_request_options(), m_context).close().wait(); + + CHECK_THROW(m_blob.open_write(0, wa::storage::access_condition::generate_if_none_match_condition(m_blob.properties().etag()), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + + CHECK_THROW(m_blob.open_write(0, wa::storage::access_condition::generate_if_none_match_condition(U("*")), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + + m_blob.open_write(0, wa::storage::access_condition::generate_if_modified_since_condition(m_blob.properties().last_modified() - utility::datetime::from_minutes(1)), wa::storage::blob_request_options(), m_context).close().wait(); + + CHECK_THROW(m_blob.open_write(0, wa::storage::access_condition::generate_if_modified_since_condition(m_blob.properties().last_modified() + utility::datetime::from_minutes(1)), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + + m_blob.open_write(0, wa::storage::access_condition::generate_if_not_modified_since_condition(m_blob.properties().last_modified() + utility::datetime::from_minutes(1)), wa::storage::blob_request_options(), m_context).close().wait(); + + CHECK_THROW(m_blob.open_write(0, wa::storage::access_condition::generate_if_not_modified_since_condition(m_blob.properties().last_modified() - utility::datetime::from_minutes(1)), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + } + + TEST_FIXTURE(block_blob_test_base, block_blob_write_stream_maximum_execution_time) + { + std::chrono::seconds duration(10); + + wa::storage::blob_request_options options; + options.set_maximum_execution_time(duration); + + auto stream = m_blob.open_write(wa::storage::access_condition(), options, m_context); + + std::this_thread::sleep_for(duration); + + stream.write(0).wait(); + stream.close().wait(); + } + + TEST_FIXTURE(page_blob_test_base, page_blob_write_stream_maximum_execution_time) + { + std::chrono::seconds duration(10); + + wa::storage::blob_request_options options; + options.set_maximum_execution_time(duration); + + auto stream = m_blob.open_write(512, wa::storage::access_condition(), options, m_context); + + std::this_thread::sleep_for(duration); + + std::vector buffer; + buffer.resize(512); + + stream.streambuf().putn(buffer.data(), buffer.size()).wait(); + stream.close().wait(); + } + + TEST_FIXTURE(page_blob_test_base, existing_page_blob_write_stream_maximum_execution_time) + { + std::chrono::seconds duration(10); + + wa::storage::blob_request_options options; + options.set_maximum_execution_time(duration); + + m_blob.create(512, wa::storage::access_condition(), options, m_context); + auto stream = m_blob.open_write(wa::storage::access_condition(), options, m_context); + + std::this_thread::sleep_for(duration); + + std::vector buffer; + buffer.resize(512); + + stream.streambuf().putn(buffer.data(), buffer.size()).wait(); + stream.close().wait(); + } + + TEST_FIXTURE(block_blob_test_base, blob_read_stream_maximum_execution_time) + { + std::chrono::seconds duration(10); + + wa::storage::blob_request_options options; + options.set_maximum_execution_time(duration); + + m_blob.upload_text(U("test"), wa::storage::access_condition(), options, m_context); + auto stream = m_blob.open_read(wa::storage::access_condition(), options, m_context); + + std::this_thread::sleep_for(duration); + + stream.read().wait(); + stream.close().wait(); + } +} diff --git a/Microsoft.WindowsAzure.Storage/tests/blob_test_base.cpp b/Microsoft.WindowsAzure.Storage/tests/blob_test_base.cpp new file mode 100644 index 00000000..48e5a046 --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/blob_test_base.cpp @@ -0,0 +1,140 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "blob_test_base.h" +#include "check_macros.h" + +#include "wascore/streams.h" + +utility::string_t blob_service_test_base::fill_buffer_and_get_md5(std::vector& buffer) +{ + return fill_buffer_and_get_md5(buffer, 0, buffer.size()); +} + +utility::string_t blob_service_test_base::fill_buffer_and_get_md5(std::vector& buffer, size_t offset, size_t count) +{ + std::generate_n(buffer.begin(), buffer.size(), [] () -> uint8_t + { + return std::rand() % UINT8_MAX; + }); + + wa::storage::core::hash_md5_streambuf md5; + md5.putn(buffer.data() + offset, count).wait(); + md5.close().wait(); + return utility::conversions::to_base64(md5.hash()); +} + +utility::string_t blob_service_test_base::get_random_container_name(size_t length) +{ + utility::string_t name; + name.resize(length); + std::generate_n(name.begin(), length, [] () -> utility::char_t + { + const utility::char_t possible_chars[] = { U("abcdefghijklmnopqrstuvwxyz1234567890") }; + return possible_chars[std::rand() % (sizeof(possible_chars) / sizeof(utility::char_t) - 1)]; + }); + + return utility::conversions::print_string(utility::datetime::utc_now().to_interval()) + name; +} + +void blob_service_test_base::check_parallelism(const wa::storage::operation_context& context, int expected_parallelism) +{ + typedef std::pair request; + + std::vector requests; + for (auto iter = context.request_results().begin(); iter != context.request_results().end(); ++iter) + { + requests.push_back(request(iter->start_time(), true)); + requests.push_back(request(iter->end_time(), false)); + } + + std::sort(requests.begin(), requests.end(), [] (const request& a, const request& b) -> bool + { + return a.first.to_interval() < b.first.to_interval(); + }); + + int current_count = 0; + int max_count = 0; + for (auto iter = requests.begin(); iter != requests.end(); ++iter) + { + if (iter->second) + { + current_count++; + } + else + { + current_count--; + } + + CHECK(current_count >= 0); + if (max_count < current_count) + { + max_count = current_count; + } + } + + CHECK_EQUAL(expected_parallelism, max_count); +} + +web::http::uri blob_service_test_base::defiddler(const web::http::uri& uri) +{ + if (uri.host() == U("ipv4.fiddler")) + { + web::http::uri_builder builder(uri); + builder.set_host(U("127.0.0.1")); + return builder.to_uri(); + } + + return uri; +} + +void blob_service_test_base::check_blob_equal(const wa::storage::cloud_blob& expected, const wa::storage::cloud_blob& actual) +{ + CHECK(expected.type() == actual.type()); + CHECK_UTF8_EQUAL(expected.uri().primary_uri().to_string(), actual.uri().primary_uri().to_string()); + CHECK_UTF8_EQUAL(expected.uri().secondary_uri().to_string(), actual.uri().secondary_uri().to_string()); + CHECK_EQUAL(expected.is_snapshot(), actual.is_snapshot()); + CHECK(expected.snapshot_time() == actual.snapshot_time()); + CHECK_UTF8_EQUAL(expected.snapshot_qualified_uri().primary_uri().to_string(), actual.snapshot_qualified_uri().primary_uri().to_string()); + CHECK_UTF8_EQUAL(expected.snapshot_qualified_uri().secondary_uri().to_string(), actual.snapshot_qualified_uri().secondary_uri().to_string()); + check_blob_copy_state_equal(expected.copy_state(), actual.copy_state()); + check_blob_properties_equal(expected.properties(), actual.properties()); +} + +void blob_service_test_base::check_blob_copy_state_equal(const wa::storage::copy_state& expected, const wa::storage::copy_state& actual) +{ + CHECK(expected.status() == actual.status()); + CHECK_EQUAL(expected.bytes_copied(), actual.bytes_copied()); + CHECK_EQUAL(expected.total_bytes(), actual.total_bytes()); + CHECK(expected.completion_time() == actual.completion_time()); + CHECK_UTF8_EQUAL(expected.copy_id(), actual.copy_id()); + CHECK_UTF8_EQUAL(expected.status_description(), actual.status_description()); + CHECK_UTF8_EQUAL(expected.source().to_string(), actual.source().to_string()); +} + +void blob_service_test_base::check_blob_properties_equal(const wa::storage::cloud_blob_properties& expected, const wa::storage::cloud_blob_properties& actual) +{ + CHECK_UTF8_EQUAL(expected.etag(), actual.etag()); + CHECK(expected.last_modified() == actual.last_modified()); + CHECK_UTF8_EQUAL(expected.cache_control(), actual.cache_control()); + CHECK_UTF8_EQUAL(expected.content_disposition(), actual.content_disposition()); + CHECK_UTF8_EQUAL(expected.content_encoding(), actual.content_encoding()); + CHECK_UTF8_EQUAL(expected.content_language(), actual.content_language()); + CHECK_UTF8_EQUAL(expected.content_md5(), actual.content_md5()); + CHECK_UTF8_EQUAL(expected.content_type(), actual.content_type()); +} diff --git a/Microsoft.WindowsAzure.Storage/tests/blob_test_base.h b/Microsoft.WindowsAzure.Storage/tests/blob_test_base.h new file mode 100644 index 00000000..47821e70 --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/blob_test_base.h @@ -0,0 +1,181 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#pragma once + +#include +#include "CurrentTest.h" + +#include "test_base.h" +#include "was/blob.h" + +const utility::string_t dummy_md5(U("MDAwMDAwMDA=")); + +class blob_service_test_base : public test_base +{ +public: + blob_service_test_base() + : m_client(test_config::instance().account().create_cloud_blob_client()) + { + } + + ~blob_service_test_base() + { + } + +protected: + + static web::http::uri defiddler(const web::http::uri& uri); + static utility::string_t fill_buffer_and_get_md5(std::vector& buffer); + static utility::string_t fill_buffer_and_get_md5(std::vector& buffer, size_t offset, size_t count); + static utility::string_t get_random_container_name(size_t length = 10); + static void check_parallelism(const wa::storage::operation_context& context, int expected_parallelism); + static void check_blob_equal(const wa::storage::cloud_blob& expected, const wa::storage::cloud_blob& actual); + static void check_blob_copy_state_equal(const wa::storage::copy_state& expected, const wa::storage::copy_state& actual); + static void check_blob_properties_equal(const wa::storage::cloud_blob_properties& expected, const wa::storage::cloud_blob_properties& actual); + + std::vector list_all_containers(const utility::string_t& prefix, const wa::storage::container_listing_includes& includes, int max_results, const wa::storage::blob_request_options& options); + std::vector list_all_blobs_from_client(const utility::string_t& prefix, const wa::storage::blob_listing_includes& includes, int max_results, const wa::storage::blob_request_options& options); + + wa::storage::cloud_blob_client m_client; +}; + +class blob_service_test_base_with_objects_to_delete : public blob_service_test_base +{ +public: + + blob_service_test_base_with_objects_to_delete() + { + } + + ~blob_service_test_base_with_objects_to_delete() + { + for (auto iter = m_blobs_to_delete.begin(); iter != m_blobs_to_delete.end(); ++iter) + { + try + { + iter->delete_blob(); + } + catch (const wa::storage::storage_exception&) + { + } + } + + for (auto iter = m_containers_to_delete.begin(); iter != m_containers_to_delete.end(); ++iter) + { + try + { + iter->delete_container(); + } + catch (const wa::storage::storage_exception&) + { + } + } + } + +protected: + + std::vector m_blobs_to_delete; + std::vector m_containers_to_delete; +}; + +class container_test_base : public blob_service_test_base +{ +public: + container_test_base() + { + m_container = m_client.get_container_reference(get_random_container_name()); + } + + ~container_test_base() + { + try + { + m_container.delete_container(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + } + catch (const wa::storage::storage_exception&) + { + } + } + +protected: + + void check_public_access(wa::storage::blob_container_public_access_type access); + std::vector list_all_blobs(const utility::string_t& prefix, const wa::storage::blob_listing_includes& includes, int max_results, const wa::storage::blob_request_options& options); + void check_lease_access(wa::storage::cloud_blob_container& container, wa::storage::lease_state state, const utility::string_t& lease_id, bool fake, bool allow_delete); + + wa::storage::cloud_blob_container m_container; +}; + +class blob_test_base : public container_test_base +{ +public: + blob_test_base() + { + m_container.create(wa::storage::blob_container_public_access_type::off, wa::storage::blob_request_options(), m_context); + } + + ~blob_test_base() + { + } + +protected: + + bool wait_for_copy(wa::storage::cloud_blob& blob); + static wa::storage::operation_context upload_and_download(wa::storage::cloud_blob& blob, size_t buffer_size, size_t buffer_offset, size_t blob_size, bool use_seekable_stream, const wa::storage::blob_request_options& options, size_t expected_request_count, bool expect_md5_header); + void check_access(const utility::string_t& sas_token, uint8_t permissions, const wa::storage::cloud_blob_shared_access_headers& headers, const wa::storage::cloud_blob& original_blob); + void check_lease_access(wa::storage::cloud_blob& blob, wa::storage::lease_state state, const utility::string_t& lease_id, bool fake); +}; + +class block_blob_test_base : public blob_test_base +{ +public: + block_blob_test_base() + { + m_blob = m_container.get_block_blob_reference(U("blockblob")); + } + + ~block_blob_test_base() + { + } + +protected: + + static utility::string_t get_block_id(uint16_t block_index); + void check_block_list_equal(const std::vector& committed_put_block_list, const std::vector& uncommitted_put_block_list); + + wa::storage::cloud_block_blob m_blob; +}; + +class page_blob_test_base : public blob_test_base +{ +public: + page_blob_test_base() + { + m_blob = m_container.get_page_blob_reference(U("pageblob")); + } + + ~page_blob_test_base() + { + } + +protected: + + void check_page_ranges_equal(const std::vector& page_ranges); + + wa::storage::cloud_page_blob m_blob; +}; diff --git a/Microsoft.WindowsAzure.Storage/tests/check_macros.h b/Microsoft.WindowsAzure.Storage/tests/check_macros.h new file mode 100644 index 00000000..d537145d --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/check_macros.h @@ -0,0 +1,20 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#pragma once + +#define CHECK_UTF8_EQUAL(expected, actual) CHECK_EQUAL(utility::conversions::to_utf8string(expected), utility::conversions::to_utf8string(actual)) diff --git a/Microsoft.WindowsAzure.Storage/tests/cloud_blob_client_test.cpp b/Microsoft.WindowsAzure.Storage/tests/cloud_blob_client_test.cpp new file mode 100644 index 00000000..0dae85f1 --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/cloud_blob_client_test.cpp @@ -0,0 +1,232 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "blob_test_base.h" +#include "check_macros.h" + +#pragma region Fixture + +std::vector blob_service_test_base::list_all_containers(const utility::string_t& prefix, const wa::storage::container_listing_includes& includes, int max_results, const wa::storage::blob_request_options& options) +{ + std::vector containers; + wa::storage::blob_continuation_token token; + + do + { + auto results = m_client.list_containers_segmented(prefix, includes, max_results, token, options, m_context); + + if (max_results > 0) + { + CHECK(results.results().size() <= static_cast(max_results)); + } + + std::copy(results.results().begin(), results.results().end(), std::back_inserter(containers)); + token = results.continuation_token(); + } while (!token.empty()); + + return containers; +} + +std::vector blob_service_test_base::list_all_blobs_from_client(const utility::string_t& prefix, const wa::storage::blob_listing_includes& includes, int max_results, const wa::storage::blob_request_options& options) +{ + std::vector blobs; + wa::storage::blob_continuation_token token; + + do + { + auto results = m_client.list_blobs_segmented(prefix, true, includes, max_results, token, options, m_context); + + if (max_results > 0) + { + CHECK(results.blobs().size() <= static_cast(max_results)); + } + + std::copy(results.blobs().begin(), results.blobs().end(), std::back_inserter(blobs)); + token = results.continuation_token(); + } while (!token.empty()); + + return blobs; +} + +#pragma endregion + +SUITE(Blob) +{ + TEST_FIXTURE(blob_service_test_base_with_objects_to_delete, list_containers_with_prefix) + { + auto prefix = get_random_container_name(); + + for (int i = 0; i < 1; i++) + { + auto index = utility::conversions::print_string(i); + auto container = m_client.get_container_reference(prefix + index); + m_containers_to_delete.push_back(container); + container.metadata()[U("index")] = index; + container.create(wa::storage::blob_container_public_access_type::off, wa::storage::blob_request_options(), m_context); + } + + std::vector containers(m_containers_to_delete); + + auto listing = list_all_containers(prefix, wa::storage::container_listing_includes::all(), 1, wa::storage::blob_request_options()); + for (auto listing_iter = listing.begin(); listing_iter != listing.end(); ++listing_iter) + { + bool found = false; + for (auto iter = containers.begin(); iter != containers.end(); ++iter) + { + if (iter->name() == listing_iter->name()) + { + auto index_str = listing_iter->metadata().find(U("index")); + CHECK(index_str != listing_iter->metadata().end()); + CHECK_UTF8_EQUAL(iter->name(), prefix + index_str->second); + containers.erase(iter); + found = true; + break; + } + } + + CHECK(found); + } + + CHECK(containers.empty()); + } + + TEST_FIXTURE(blob_service_test_base_with_objects_to_delete, list_containers) + { + auto prefix = get_random_container_name(); + + for (int i = 0; i < 1; i++) + { + auto index = utility::conversions::print_string(i); + auto container = m_client.get_container_reference(prefix + index); + m_containers_to_delete.push_back(container); + container.metadata()[U("index")] = index; + container.create(wa::storage::blob_container_public_access_type::off, wa::storage::blob_request_options(), m_context); + } + + std::vector containers(m_containers_to_delete); + + auto listing = list_all_containers(utility::string_t(), wa::storage::container_listing_includes::all(), 1, wa::storage::blob_request_options()); + for (auto listing_iter = listing.begin(); listing_iter != listing.end(); ++listing_iter) + { + for (auto iter = containers.begin(); iter != containers.end(); ++iter) + { + if (iter->name() == listing_iter->name()) + { + auto index_str = listing_iter->metadata().find(U("index")); + CHECK(index_str != listing_iter->metadata().end()); + CHECK_UTF8_EQUAL(iter->name(), prefix + index_str->second); + containers.erase(iter); + break; + } + } + } + + CHECK(containers.empty()); + } + + TEST_FIXTURE(blob_service_test_base_with_objects_to_delete, list_blobs_from_client_root) + { + auto root_container = m_client.get_root_container_reference(); + root_container.create_if_not_exists(); + + auto prefix = get_random_container_name(); + + for (int i = 0; i < 1; i++) + { + auto index = utility::conversions::print_string(i); + auto blob = root_container.get_block_blob_reference(prefix + index); + m_blobs_to_delete.push_back(blob); + blob.upload_text(U("test"), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + } + + std::vector blobs(m_blobs_to_delete); + + auto listing = list_all_blobs_from_client(prefix, wa::storage::blob_listing_includes(), 1, wa::storage::blob_request_options()); + for (auto listing_iter = listing.begin(); listing_iter != listing.end(); ++listing_iter) + { + for (auto iter = blobs.begin(); iter != blobs.end(); ++iter) + { + if (iter->name() == listing_iter->name()) + { + blobs.erase(iter); + break; + } + } + } + + CHECK(blobs.empty()); + } + + TEST_FIXTURE(blob_test_base, list_blobs_from_client) + { + std::vector blobs; + auto prefix = get_random_container_name(); + + for (int i = 0; i < 1; i++) + { + auto index = utility::conversions::print_string(i); + auto blob = m_container.get_block_blob_reference(prefix + index); + blob.upload_text(U("test"), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + blobs.push_back(blob); + } + + std::vector original_blobs(blobs); + + auto listing = list_all_blobs_from_client(m_container.name(), wa::storage::blob_listing_includes(), 1, wa::storage::blob_request_options()); + CHECK(listing.empty()); + + listing = list_all_blobs_from_client(m_container.name() + U("/"), wa::storage::blob_listing_includes(), 1, wa::storage::blob_request_options()); + for (auto listing_iter = listing.begin(); listing_iter != listing.end(); ++listing_iter) + { + for (auto iter = blobs.begin(); iter != blobs.end(); ++iter) + { + if (iter->name() == listing_iter->name()) + { + blobs.erase(iter); + break; + } + } + } + + CHECK(blobs.empty()); + + blobs = original_blobs; + + listing = list_all_blobs_from_client(m_container.name() + U("/") + prefix, wa::storage::blob_listing_includes(), 1, wa::storage::blob_request_options()); + for (auto listing_iter = listing.begin(); listing_iter != listing.end(); ++listing_iter) + { + for (auto iter = blobs.begin(); iter != blobs.end(); ++iter) + { + if (iter->name() == listing_iter->name()) + { + blobs.erase(iter); + break; + } + } + } + + CHECK(blobs.empty()); + } + + TEST_FIXTURE(test_base, blob_shared_key_lite) + { + auto client = test_config::instance().account().create_cloud_blob_client(); + client.set_authentication_scheme(wa::storage::authentication_scheme::shared_key_lite); + client.list_containers_segmented(utility::string_t(), wa::storage::container_listing_includes(), 1, wa::storage::blob_continuation_token(), wa::storage::blob_request_options(), m_context); + } +} diff --git a/Microsoft.WindowsAzure.Storage/tests/cloud_blob_container_test.cpp b/Microsoft.WindowsAzure.Storage/tests/cloud_blob_container_test.cpp new file mode 100644 index 00000000..7e6e8dc0 --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/cloud_blob_container_test.cpp @@ -0,0 +1,294 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "blob_test_base.h" +#include "check_macros.h" + +#pragma region Fixture + +void container_test_base::check_public_access(wa::storage::blob_container_public_access_type access) +{ + auto blob = m_container.get_block_blob_reference(U("blob")); + blob.upload_text(U("test"), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + wa::storage::cloud_blob_container public_container(m_container.uri()); + auto public_blob = public_container.get_blob_reference(blob.name()); + + if (access != wa::storage::blob_container_public_access_type::off) + { + public_blob.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + } + else + { + CHECK_THROW(public_blob.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + } + + if (access == wa::storage::blob_container_public_access_type::container) + { + public_container.list_blobs_segmented(utility::string_t(), true, wa::storage::blob_listing_includes::all(), 0, wa::storage::blob_continuation_token(), wa::storage::blob_request_options(), m_context); + public_container.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + } + else + { + CHECK_THROW(public_container.list_blobs_segmented(utility::string_t(), true, wa::storage::blob_listing_includes::all(), 0, wa::storage::blob_continuation_token(), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + CHECK_THROW(public_container.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + } + + auto perms = m_container.download_permissions(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK(access == perms.public_access()); +} + +std::vector container_test_base::list_all_blobs(const utility::string_t& prefix, const wa::storage::blob_listing_includes& includes, int max_results, const wa::storage::blob_request_options& options) +{ + std::vector blobs; + wa::storage::blob_continuation_token token; + + do + { + auto results = m_container.list_blobs_segmented(prefix, true, includes, max_results, token, options, m_context); + + if (max_results > 0) + { + CHECK(results.blobs().size() <= static_cast(max_results)); + } + + std::copy(results.blobs().begin(), results.blobs().end(), std::back_inserter(blobs)); + token = results.continuation_token(); + } while (!token.empty()); + + return blobs; +} + +#pragma endregion + +SUITE(Blob) +{ + TEST_FIXTURE(container_test_base, container_get_reference) + { + auto block_blob = m_container.get_block_blob_reference(U("blob1")); + CHECK_UTF8_EQUAL(m_container.uri().primary_uri().to_string(), block_blob.container().uri().primary_uri().to_string()); + CHECK_UTF8_EQUAL(m_container.uri().secondary_uri().to_string(), block_blob.container().uri().secondary_uri().to_string()); + + auto page_blob = m_container.get_page_blob_reference(U("blob2")); + CHECK_UTF8_EQUAL(m_container.uri().primary_uri().to_string(), page_blob.container().uri().primary_uri().to_string()); + CHECK_UTF8_EQUAL(m_container.uri().secondary_uri().to_string(), page_blob.container().uri().secondary_uri().to_string()); + + auto directory = m_container.get_directory_reference(U("dir")); + CHECK_UTF8_EQUAL(m_container.uri().primary_uri().to_string(), directory.container().uri().primary_uri().to_string()); + CHECK_UTF8_EQUAL(m_container.uri().secondary_uri().to_string(), directory.container().uri().secondary_uri().to_string()); + } + + TEST_FIXTURE(container_test_base, container_create_delete) + { + CHECK(!m_container.exists(wa::storage::blob_request_options(), m_context)); + CHECK(!m_container.delete_container_if_exists(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context)); + CHECK(m_container.create_if_not_exists(wa::storage::blob_container_public_access_type::off, wa::storage::blob_request_options(), m_context)); + CHECK(!m_container.create_if_not_exists(wa::storage::blob_container_public_access_type::off, wa::storage::blob_request_options(), m_context)); + CHECK(m_container.exists(wa::storage::blob_request_options(), m_context)); + CHECK(m_container.delete_container_if_exists(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context)); + CHECK(!m_container.delete_container_if_exists(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context)); + CHECK(!m_container.exists(wa::storage::blob_request_options(), m_context)); + } + + TEST_FIXTURE(container_test_base, container_create_public_off) + { + m_container.create(wa::storage::blob_container_public_access_type::off, wa::storage::blob_request_options(), m_context); + check_public_access(wa::storage::blob_container_public_access_type::off); + } + + TEST_FIXTURE(container_test_base, container_create_public_blob) + { + m_container.create(wa::storage::blob_container_public_access_type::blob, wa::storage::blob_request_options(), m_context); + check_public_access(wa::storage::blob_container_public_access_type::blob); + } + + TEST_FIXTURE(container_test_base, container_create_public_container) + { + m_container.create(wa::storage::blob_container_public_access_type::container, wa::storage::blob_request_options(), m_context); + check_public_access(wa::storage::blob_container_public_access_type::container); + } + + TEST_FIXTURE(container_test_base, container_set_public_access) + { + m_container.create(wa::storage::blob_container_public_access_type::off, wa::storage::blob_request_options(), m_context); + wa::storage::blob_container_permissions permissions; + + permissions.set_public_access(wa::storage::blob_container_public_access_type::container); + m_container.upload_permissions(permissions, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + check_public_access(wa::storage::blob_container_public_access_type::container); + + permissions.set_public_access(wa::storage::blob_container_public_access_type::blob); + m_container.upload_permissions(permissions, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + check_public_access(wa::storage::blob_container_public_access_type::blob); + + permissions.set_public_access(wa::storage::blob_container_public_access_type::off); + m_container.upload_permissions(permissions, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + check_public_access(wa::storage::blob_container_public_access_type::off); + } + + TEST_FIXTURE(container_test_base, container_metadata) + { + // Create with 2 pairs + m_container.metadata()[U("key1")] = U("value1"); + m_container.metadata()[U("key2")] = U("value2"); + m_container.create(wa::storage::blob_container_public_access_type::off, wa::storage::blob_request_options(), m_context); + + auto same_container = m_client.get_container_reference(m_container.name()); + CHECK(same_container.metadata().empty()); + same_container.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_EQUAL(2, same_container.metadata().size()); + CHECK_UTF8_EQUAL(U("value1"), same_container.metadata()[U("key1")]); + CHECK_UTF8_EQUAL(U("value2"), same_container.metadata()[U("key2")]); + + // Add 1 pair + same_container.metadata()[U("key3")] = U("value3"); + same_container.upload_metadata(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + m_container.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_EQUAL(3, same_container.metadata().size()); + CHECK_UTF8_EQUAL(U("value1"), m_container.metadata()[U("key1")]); + CHECK_UTF8_EQUAL(U("value2"), m_container.metadata()[U("key2")]); + CHECK_UTF8_EQUAL(U("value3"), m_container.metadata()[U("key3")]); + + // Overwrite with 1 pair + m_container.metadata().clear(); + m_container.metadata()[U("key4")] = U("value4"); + m_container.upload_metadata(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + same_container.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_EQUAL(1, same_container.metadata().size()); + CHECK_UTF8_EQUAL(U("value4"), same_container.metadata()[U("key4")]); + + // Clear all pairs + same_container.metadata().clear(); + same_container.upload_metadata(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + m_container.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK(m_container.metadata().empty()); + } + + TEST_FIXTURE(container_test_base, container_list_blobs) + { + m_container.create(wa::storage::blob_container_public_access_type::off, wa::storage::blob_request_options(), m_context); + std::map blobs; + + for (int i = 0; i < 4; i++) + { + auto index = utility::conversions::print_string(i); + auto blob = m_container.get_block_blob_reference(U("blockblob") + index); + blob.metadata()[U("index")] = index; + + std::vector buffer; + buffer.resize(i * 16 * 1024); + auto stream = concurrency::streams::container_stream>::open_istream(buffer); + blob.upload_from_stream(stream, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + blobs[blob.name()] = blob; + } + + for (int i = 0; i < 3; i++) + { + auto index = utility::conversions::print_string(i); + auto blob = m_container.get_page_blob_reference(U("pageblob") + index); + blob.metadata()[U("index")] = index; + + blob.create(i * 512, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + blobs[blob.name()] = blob; + } + + auto listing1 = list_all_blobs(utility::string_t(), wa::storage::blob_listing_includes::all(), 2, wa::storage::blob_request_options()); + for (auto iter = listing1.begin(); iter != listing1.end(); ++iter) + { + auto blob = blobs.find(iter->name()); + CHECK(blob != blobs.end()); + + CHECK_UTF8_EQUAL(blob->second.uri().primary_uri().to_string(), iter->uri().primary_uri().to_string()); + CHECK_UTF8_EQUAL(blob->second.uri().secondary_uri().to_string(), iter->uri().secondary_uri().to_string()); + + auto index_str = blob->second.metadata().find(U("index")); + CHECK(index_str != blob->second.metadata().end()); + auto index = utility::conversions::scan_string(index_str->second); + + switch (iter->type()) + { + case wa::storage::blob_type::block_blob: + CHECK_EQUAL(index * 16 * 1024, iter->properties().size()); + break; + + case wa::storage::blob_type::page_blob: + CHECK_EQUAL(index * 512, iter->properties().size()); + break; + + default: + CHECK(false); + break; + } + + blobs.erase(blob); + } + + CHECK_EQUAL(0, blobs.size()); + + auto listing2 = list_all_blobs(U("block"), wa::storage::blob_listing_includes(), 10, wa::storage::blob_request_options()); + CHECK_EQUAL(4, listing2.size()); + for (auto iter = listing2.begin(); iter != listing2.end(); ++iter) + { + CHECK(iter->metadata().empty()); + } + } + + TEST_FIXTURE(blob_test_base, container_stored_policy) + { + auto stored_permissions = m_container.download_permissions(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK(stored_permissions.policies().empty()); + + auto now = utility::datetime::utc_now(); + auto aligned_now = now - (now.to_interval() % (10 * 1000 * 1000)); + + wa::storage::blob_shared_access_policy policy; + policy.set_permissions(wa::storage::blob_shared_access_policy::permissions::write); + policy.set_start(aligned_now - utility::datetime::from_minutes(5)); + policy.set_expiry(aligned_now + utility::datetime::from_minutes(30)); + + wa::storage::blob_container_permissions permissions; + permissions.policies()[U("id1")] = policy; + m_container.upload_permissions(permissions, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + std::this_thread::sleep_for(std::chrono::seconds(30)); + + auto blob = m_container.get_block_blob_reference(U("blob")); + blob.upload_text(U("test"), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + auto sas_token = blob.get_shared_access_signature(wa::storage::blob_shared_access_policy(), U("id1")); + check_access(sas_token, wa::storage::blob_shared_access_policy::permissions::write, wa::storage::cloud_blob_shared_access_headers(), blob); + + stored_permissions = m_container.download_permissions(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_EQUAL(1, stored_permissions.policies().size()); + auto stored_policy = stored_permissions.policies().find(U("id1")); + CHECK(stored_policy != stored_permissions.policies().end()); + CHECK_EQUAL(policy.permission(), stored_policy->second.permission()); + CHECK_EQUAL(policy.start().to_interval(), stored_policy->second.start().to_interval()); + CHECK_EQUAL(policy.expiry().to_interval(), stored_policy->second.expiry().to_interval()); + + stored_permissions.policies().clear(); + m_container.upload_permissions(stored_permissions, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + std::this_thread::sleep_for(std::chrono::seconds(30)); + + check_access(sas_token, wa::storage::blob_shared_access_policy::permissions::none, wa::storage::cloud_blob_shared_access_headers(), blob); + + stored_permissions = m_container.download_permissions(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK(stored_permissions.policies().empty()); + } +} diff --git a/Microsoft.WindowsAzure.Storage/tests/cloud_blob_directory_test.cpp b/Microsoft.WindowsAzure.Storage/tests/cloud_blob_directory_test.cpp new file mode 100644 index 00000000..9db53ab1 --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/cloud_blob_directory_test.cpp @@ -0,0 +1,247 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "blob_test_base.h" +#include "check_macros.h" + +const utility::string_t delimiters[] = { U("$"), U("@"), U("-"), U("%"), U("/"), U("||") }; + +#pragma region Fixture + +void create_blob_tree(const wa::storage::cloud_blob_container& container, const std::vector& blobs, const utility::string_t& delimiter, wa::storage::operation_context context) +{ + for (auto iter = blobs.begin(); iter != blobs.end(); ++iter) + { + utility::string_t name(iter->name()); + std::vector splitted_name; + utility::string_t::size_type pos(0); + + pos -= delimiter.size(); + do + { + name = name.substr(pos + delimiter.size()); + pos = name.find(delimiter); + splitted_name.push_back(name.substr(0, pos)); + } while (pos != utility::string_t::npos); + + wa::storage::cloud_blob blob; + if (splitted_name.size() == 1) + { + blob = container.get_blob_reference(splitted_name.back()); + } + else + { + auto directory = container.get_directory_reference(splitted_name[0]); + for (size_t i = 1; i < splitted_name.size() - 1; i++) + { + directory = directory.get_subdirectory_reference(splitted_name[i]); + } + + blob = directory.get_blob_reference(splitted_name.back()); + } + + if (iter->type() == wa::storage::blob_type::page_blob) + { + wa::storage::cloud_page_blob page_blob(blob); + page_blob.create(0, wa::storage::access_condition(), wa::storage::blob_request_options(), context); + } + else + { + wa::storage::cloud_block_blob block_blob(blob); + block_blob.upload_block_list(std::vector(), wa::storage::access_condition(), wa::storage::blob_request_options(), context); + } + } +} + +int list_entire_blob_tree_helper(std::vector& blobs, const wa::storage::cloud_blob_directory& directory, const wa::storage::blob_listing_includes& includes, int max_results, const wa::storage::blob_request_options& options, wa::storage::operation_context context) +{ + wa::storage::blob_continuation_token token; + int max_depth = 0; + + do + { + auto results = directory.list_blobs_segmented(false, includes, max_results, token, options, context); + + if (max_results > 0) + { + CHECK((results.blobs().size() + results.directories().size()) <= static_cast(max_results)); + } + + std::copy(results.blobs().begin(), results.blobs().end(), std::back_inserter(blobs)); + + for (auto iter = results.directories().begin(); iter != results.directories().end(); ++iter) + { + int depth = list_entire_blob_tree_helper(blobs, *iter, includes, max_results, options, context); + if (depth > max_depth) + { + max_depth = depth; + } + } + + token = results.continuation_token(); + } while (!token.empty()); + + return max_depth + 1; +} + +std::vector list_entire_blob_tree(const wa::storage::cloud_blob_container& container, const wa::storage::blob_listing_includes& includes, int max_results, int& max_depth, const wa::storage::blob_request_options& options, wa::storage::operation_context context) +{ + std::vector blobs; + wa::storage::blob_continuation_token token; + max_depth = 0; + + do + { + auto results = container.list_blobs_segmented(utility::string_t(), false, includes, max_results, token, options, context); + + if (max_results > 0) + { + CHECK((results.blobs().size() + results.directories().size()) <= static_cast(max_results)); + } + + std::copy(results.blobs().begin(), results.blobs().end(), std::back_inserter(blobs)); + + for (auto iter = results.directories().begin(); iter != results.directories().end(); ++iter) + { + int depth = list_entire_blob_tree_helper(blobs, *iter, includes, max_results, options, context); + if (depth > max_depth) + { + max_depth = depth; + } + } + + token = results.continuation_token(); + } while (!token.empty()); + + return blobs; +} + +void check_parents(const wa::storage::cloud_blob_container& container, const utility::string_t& delimiter) +{ + auto dir1 = container.get_directory_reference(U("dir1")); + CHECK_UTF8_EQUAL(U("dir1") + delimiter, dir1.prefix()); + + auto dir2 = dir1.get_subdirectory_reference(U("dir2")); + CHECK_UTF8_EQUAL(U("dir1") + delimiter + U("dir2") + delimiter, dir2.prefix()); + + auto dir3 = dir2.get_subdirectory_reference(U("dir3")); + CHECK_UTF8_EQUAL(U("dir1") + delimiter + U("dir2") + delimiter + U("dir3") + delimiter, dir3.prefix()); + + auto blob = dir3.get_blob_reference(U("blob")); + CHECK_UTF8_EQUAL(U("dir1") + delimiter + U("dir2") + delimiter + U("dir3") + delimiter + U("blob"), blob.name()); + + auto block_blob = dir3.get_block_blob_reference(U("block_blob")); + CHECK(wa::storage::blob_type::block_blob == block_blob.type()); + CHECK_UTF8_EQUAL(U("dir1") + delimiter + U("dir2") + delimiter + U("dir3") + delimiter + U("block_blob"), block_blob.name()); + + auto page_blob = dir3.get_page_blob_reference(U("page_blob")); + CHECK(wa::storage::blob_type::page_blob == page_blob.type()); + CHECK_UTF8_EQUAL(U("dir1") + delimiter + U("dir2") + delimiter + U("dir3") + delimiter + U("page_blob"), page_blob.name()); + + auto blob_parent = blob.get_parent_reference(); + CHECK_UTF8_EQUAL(dir3.prefix(), blob_parent.prefix()); + + auto dir3_parent = blob_parent.get_parent_reference(); + CHECK_UTF8_EQUAL(dir2.prefix(), dir3_parent.prefix()); + + auto dir2_parent = dir3_parent.get_parent_reference(); + CHECK_UTF8_EQUAL(dir1.prefix(), dir2_parent.prefix()); + + auto dir1_parent = dir2_parent.get_parent_reference(); + CHECK(!dir1_parent.is_valid()); + + auto root_blob = container.get_blob_reference(U("blob")); + CHECK_UTF8_EQUAL(U("blob"), root_blob.name()); + + auto root_blob_parent = root_blob.get_parent_reference(); + CHECK(!root_blob_parent.is_valid()); +} + +#pragma endregion + +SUITE(Blob) +{ + TEST_FIXTURE(blob_test_base, directory_parent) + { + for (auto delimiter : delimiters) + { + m_client.set_directory_delimiter(delimiter); + auto container = m_client.get_container_reference(m_container.name()); + check_parents(container, delimiter); + } + } + + TEST_FIXTURE(blob_service_test_base, directory_parent_in_root_container) + { + for (auto delimiter : delimiters) + { + m_client.set_directory_delimiter(delimiter); + auto container = m_client.get_root_container_reference(); + check_parents(container, delimiter); + } + } + + TEST_FIXTURE(blob_test_base, directory_tree_listing) + { + for (auto delimiter : delimiters) + { + m_client.set_directory_delimiter(delimiter); + auto container = m_client.get_container_reference(m_container.name()); + + std::vector blobs; + blobs.push_back(container.get_block_blob_reference(U("block_blobs") + delimiter + U("dir1") + delimiter + U("dir2") + delimiter + U("blob1"))); + blobs.push_back(container.get_block_blob_reference(U("block_blobs") + delimiter + U("dir1") + delimiter + U("dir2") + delimiter + U("blob2"))); + blobs.push_back(container.get_block_blob_reference(U("block_blobs") + delimiter + U("dir1") + delimiter + U("blob3"))); + blobs.push_back(container.get_block_blob_reference(U("block_blobs") + delimiter + U("dir1") + delimiter + U("blob4"))); + blobs.push_back(container.get_block_blob_reference(U("block_blobs") + delimiter + U("blob5"))); + blobs.push_back(container.get_page_blob_reference(U("page_blobs") + delimiter + U("dir1") + delimiter + U("dir2") + delimiter + U("blob6"))); + blobs.push_back(container.get_page_blob_reference(U("page_blobs") + delimiter + U("blob7"))); + + create_blob_tree(container, blobs, delimiter, m_context); + + int depth; + auto results = list_entire_blob_tree(container, wa::storage::blob_listing_includes(), 2, depth, wa::storage::blob_request_options(), m_context); + CHECK_EQUAL(3, depth); + + for (auto iter = results.begin(); iter != results.end(); ++iter) + { + bool found = false; + for (auto blobs_iter = blobs.begin(); blobs_iter != blobs.end(); ++blobs_iter) + { + if (blobs_iter->name() == iter->name()) + { + CHECK(blobs_iter->type() == iter->type()); + blobs.erase(blobs_iter); + found = true; + break; + } + } + + CHECK(found); + + iter->delete_blob(wa::storage::delete_snapshots_option::none, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + } + + CHECK(blobs.empty()); + + wa::storage::blob_listing_includes includes; + includes.set_snapshots(true); + CHECK_THROW(list_entire_blob_tree(container, includes, 1, depth, wa::storage::blob_request_options(), m_context), std::invalid_argument); + } + } +} diff --git a/Microsoft.WindowsAzure.Storage/tests/cloud_blob_test.cpp b/Microsoft.WindowsAzure.Storage/tests/cloud_blob_test.cpp new file mode 100644 index 00000000..6485f680 --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/cloud_blob_test.cpp @@ -0,0 +1,586 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "blob_test_base.h" +#include "check_macros.h" + +#include "cpprest/producerconsumerstream.h" + +#pragma region Fixture + +bool blob_test_base::wait_for_copy(wa::storage::cloud_blob& blob) +{ + do + { + std::this_thread::sleep_for(std::chrono::seconds(1)); + blob.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + } while (blob.copy_state().status() == wa::storage::copy_status::pending); + + return blob.copy_state().status() == wa::storage::copy_status::success; +} + +wa::storage::operation_context blob_test_base::upload_and_download(wa::storage::cloud_blob& blob, size_t buffer_size, size_t buffer_offset, size_t blob_size, bool use_seekable_stream, const wa::storage::blob_request_options& options, size_t expected_request_count, bool expect_md5_header) +{ + wa::storage::operation_context context; + print_client_request_id(context, U("upload/download")); + + utility::string_t md5_header; + context.set_sending_request([&md5_header] (web::http::http_request& request, wa::storage::operation_context) + { + if (!request.headers().match(U("x-ms-blob-content-md5"), md5_header)) + { + md5_header.clear(); + } + }); + + std::vector buffer; + buffer.resize(buffer_size); + auto md5 = fill_buffer_and_get_md5(buffer, buffer_offset, blob_size == 0 ? buffer_size - buffer_offset : blob_size); + + concurrency::streams::istream stream; + if (use_seekable_stream) + { + stream = concurrency::streams::bytestream::open_istream(buffer); + stream.seek(buffer_offset); + } + else + { + concurrency::streams::producer_consumer_buffer pcbuffer; + pcbuffer.putn(buffer.data() + buffer_offset, buffer_size - buffer_offset); + pcbuffer.close(std::ios_base::out); + stream = pcbuffer.create_istream(); + } + + if (blob.type() == wa::storage::blob_type::block_blob) + { + wa::storage::cloud_block_blob block_blob(blob); + if (blob_size == 0) + { + block_blob.upload_from_stream(stream, wa::storage::access_condition(), options, context); + } + else + { + block_blob.upload_from_stream(stream, blob_size, wa::storage::access_condition(), options, context); + } + } + else if (blob.type() == wa::storage::blob_type::page_blob) + { + wa::storage::cloud_page_blob page_blob(blob); + if (blob_size == 0) + { + page_blob.upload_from_stream(stream, wa::storage::access_condition(), options, context); + } + else + { + page_blob.upload_from_stream(stream, blob_size, wa::storage::access_condition(), options, context); + } + } + + CHECK_UTF8_EQUAL(expect_md5_header ? md5 : utility::string_t(), md5_header); + CHECK_EQUAL(expected_request_count, context.request_results().size()); + + wa::storage::blob_request_options download_options(options); + download_options.set_use_transactional_md5(false); + + concurrency::streams::container_buffer> output_buffer; + blob.download_to_stream(output_buffer.create_ostream(), wa::storage::access_condition(), download_options, context); + CHECK_ARRAY_EQUAL(buffer.data() + buffer_offset, output_buffer.collection().data(), blob_size == 0 ? (buffer_size - buffer_offset) : blob_size); + + context.set_sending_request(std::function()); + return context; +} + +void blob_test_base::check_access(const utility::string_t& sas_token, uint8_t permissions, const wa::storage::cloud_blob_shared_access_headers& headers, const wa::storage::cloud_blob& original_blob) +{ + wa::storage::storage_credentials credentials; + if (!sas_token.empty()) + { + credentials = wa::storage::storage_credentials(sas_token); + } + + wa::storage::cloud_blob_container container(m_container.uri(), credentials); + wa::storage::cloud_blob blob = container.get_blob_reference(original_blob.name()); + + if (permissions & wa::storage::blob_shared_access_policy::permissions::list) + { + container.list_blobs_segmented(utility::string_t(), true, wa::storage::blob_listing_includes(), 0, wa::storage::blob_continuation_token(), wa::storage::blob_request_options(), m_context); + } + else + { + CHECK_THROW(container.list_blobs_segmented(utility::string_t(), true, wa::storage::blob_listing_includes(), 0, wa::storage::blob_continuation_token(), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + } + + if (permissions & wa::storage::blob_shared_access_policy::permissions::read) + { + blob.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + if (!headers.cache_control().empty()) + { + CHECK_UTF8_EQUAL(headers.cache_control(), blob.properties().cache_control()); + } + else + { + CHECK_UTF8_EQUAL(original_blob.properties().cache_control(), blob.properties().cache_control()); + } + + if (!headers.content_disposition().empty()) + { + CHECK_UTF8_EQUAL(headers.content_disposition(), blob.properties().content_disposition()); + } + else + { + CHECK_UTF8_EQUAL(original_blob.properties().content_disposition(), blob.properties().content_disposition()); + } + + if (!headers.content_encoding().empty()) + { + CHECK_UTF8_EQUAL(headers.content_encoding(), blob.properties().content_encoding()); + } + else + { + CHECK_UTF8_EQUAL(original_blob.properties().content_encoding(), blob.properties().content_encoding()); + } + + if (!headers.content_language().empty()) + { + CHECK_UTF8_EQUAL(headers.content_language(), blob.properties().content_language()); + } + else + { + CHECK_UTF8_EQUAL(original_blob.properties().content_language(), blob.properties().content_language()); + } + + if (!headers.content_type().empty()) + { + CHECK_UTF8_EQUAL(headers.content_type(), blob.properties().content_type()); + } + else + { + CHECK_UTF8_EQUAL(original_blob.properties().content_type(), blob.properties().content_type()); + } + } + else + { + CHECK_THROW(blob.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + } + + if (permissions & wa::storage::blob_shared_access_policy::permissions::write) + { + blob.upload_metadata(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + } + else + { + CHECK_THROW(blob.upload_metadata(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + } + + if (permissions & wa::storage::blob_shared_access_policy::permissions::del) + { + blob.delete_blob(wa::storage::delete_snapshots_option::none, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + } + else + { + CHECK_THROW(blob.delete_blob(wa::storage::delete_snapshots_option::none, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + } +} + +#pragma endregion + +SUITE(Blob) +{ + TEST_FIXTURE(blob_test_base, blob_delete) + { + auto blob = m_container.get_block_blob_reference(U("blockblob")); + + CHECK(!blob.delete_blob_if_exists(wa::storage::delete_snapshots_option::none, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context)); + CHECK_THROW(blob.delete_blob(wa::storage::delete_snapshots_option::none, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + + blob.upload_block_list(std::vector()); + + CHECK(blob.delete_blob_if_exists(wa::storage::delete_snapshots_option::none, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context)); + CHECK(!blob.delete_blob_if_exists(wa::storage::delete_snapshots_option::none, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context)); + } + + TEST_FIXTURE(blob_test_base, blob_exists) + { + auto blob = m_container.get_page_blob_reference(U("pageblob")); + + CHECK(!blob.exists(wa::storage::blob_request_options(), m_context)); + blob.create(1024, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + auto same_blob = m_container.get_page_blob_reference(U("pageblob")); + + CHECK(same_blob.exists(wa::storage::blob_request_options(), m_context)); + CHECK_EQUAL(1024, same_blob.properties().size()); + } + + TEST_FIXTURE(blob_test_base, blob_properties) + { + wa::storage::blob_request_options options; + options.set_disable_content_md5_validation(true); + + auto blob = m_container.get_page_blob_reference(U("pageblob")); + CHECK_THROW(blob.download_attributes(wa::storage::access_condition(), options, m_context), wa::storage::storage_exception); + + blob.create(1024, wa::storage::access_condition(), options, m_context); + CHECK_EQUAL(1024, blob.properties().size()); + CHECK(!blob.properties().etag().empty()); + CHECK((utility::datetime::utc_now() - blob.properties().last_modified()) < utility::datetime::from_minutes(5)); + CHECK(blob.properties().cache_control().empty()); + CHECK(blob.properties().content_disposition().empty()); + CHECK(blob.properties().content_encoding().empty()); + CHECK(blob.properties().content_language().empty()); + CHECK(blob.properties().content_md5().empty()); + CHECK(blob.properties().content_type().empty()); + CHECK(wa::storage::lease_status::unspecified == blob.properties().lease_status()); + + auto same_blob = m_container.get_page_blob_reference(blob.name()); + same_blob.download_attributes(wa::storage::access_condition(), options, m_context); + CHECK_EQUAL(1024, same_blob.properties().size()); + CHECK_UTF8_EQUAL(blob.properties().etag(), same_blob.properties().etag()); + CHECK(blob.properties().last_modified() == same_blob.properties().last_modified()); + CHECK(same_blob.properties().cache_control().empty()); + CHECK(same_blob.properties().content_disposition().empty()); + CHECK(same_blob.properties().content_encoding().empty()); + CHECK(same_blob.properties().content_language().empty()); + CHECK(same_blob.properties().content_md5().empty()); + CHECK_UTF8_EQUAL(U("application/octet-stream"), same_blob.properties().content_type()); + CHECK(wa::storage::lease_status::unlocked == same_blob.properties().lease_status()); + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + blob.properties().set_cache_control(U("no-transform")); + blob.properties().set_content_disposition(U("attachment")); + blob.properties().set_content_encoding(U("gzip")); + blob.properties().set_content_language(U("tr,en")); + blob.properties().set_content_md5(dummy_md5); + blob.properties().set_content_type(U("text/html")); + blob.upload_properties(wa::storage::access_condition(), options, m_context); + CHECK(blob.properties().etag() != same_blob.properties().etag()); + CHECK(blob.properties().last_modified().to_interval() > same_blob.properties().last_modified().to_interval()); + + same_blob.download_attributes(wa::storage::access_condition(), options, m_context); + check_blob_properties_equal(blob.properties(), same_blob.properties()); + + auto still_same_blob = m_container.get_page_blob_reference(blob.name()); + auto stream = concurrency::streams::container_stream>::open_ostream(); + still_same_blob.download_to_stream(stream, wa::storage::access_condition(), options, m_context); + check_blob_properties_equal(blob.properties(), still_same_blob.properties()); + + auto listing = list_all_blobs(utility::string_t(), wa::storage::blob_listing_includes::all(), 0, options); + check_blob_properties_equal(blob.properties(), listing.front().properties()); + } + + TEST_FIXTURE(blob_test_base, blob_type) + { + auto page_blob = m_container.get_page_blob_reference(U("pageblob")); + CHECK(wa::storage::blob_type::page_blob == page_blob.type()); + page_blob.create(0, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + auto block_blob = m_container.get_block_blob_reference(U("blockblob")); + CHECK(wa::storage::blob_type::block_blob == block_blob.type()); + block_blob.upload_text(utility::string_t(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + auto same_page_blob = m_container.get_blob_reference(U("pageblob")); + CHECK(wa::storage::blob_type::unspecified == same_page_blob.type()); + same_page_blob.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK(wa::storage::blob_type::page_blob == same_page_blob.type()); + + auto same_block_blob = m_container.get_blob_reference(U("blockblob")); + CHECK(wa::storage::blob_type::unspecified == same_block_blob.type()); + same_block_blob.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK(wa::storage::blob_type::block_blob == same_block_blob.type()); + + auto invalid_page_blob = m_container.get_page_blob_reference(U("blockblob")); + CHECK_THROW(invalid_page_blob.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + + auto invalid_block_blob = m_container.get_block_blob_reference(U("pageblob")); + CHECK_THROW(invalid_block_blob.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + } + + TEST_FIXTURE(blob_test_base, blob_metadata) + { + // Create with 2 pairs + auto blob = m_container.get_block_blob_reference(U("blockblob")); + blob.metadata()[U("key1")] = U("value1"); + blob.metadata()[U("key2")] = U("value2"); + blob.upload_text(utility::string_t()); + + auto same_blob = m_container.get_blob_reference(blob.name()); + CHECK(same_blob.metadata().empty()); + same_blob.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_EQUAL(2, same_blob.metadata().size()); + CHECK_UTF8_EQUAL(U("value1"), same_blob.metadata()[U("key1")]); + CHECK_UTF8_EQUAL(U("value2"), same_blob.metadata()[U("key2")]); + + // Add 1 pair + same_blob.metadata()[U("key3")] = U("value3"); + same_blob.upload_metadata(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + blob.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_EQUAL(3, same_blob.metadata().size()); + CHECK_UTF8_EQUAL(U("value1"), blob.metadata()[U("key1")]); + CHECK_UTF8_EQUAL(U("value2"), blob.metadata()[U("key2")]); + CHECK_UTF8_EQUAL(U("value3"), blob.metadata()[U("key3")]); + + // Overwrite with 1 pair + blob.metadata().clear(); + blob.metadata()[U("key4")] = U("value4"); + blob.upload_metadata(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + same_blob.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_EQUAL(1, same_blob.metadata().size()); + CHECK_UTF8_EQUAL(U("value4"), same_blob.metadata()[U("key4")]); + + // Clear all pairs + same_blob.metadata().clear(); + same_blob.upload_metadata(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + blob.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK(blob.metadata().empty()); + } + + TEST_FIXTURE(blob_test_base, blob_invalid_sas_and_snapshot) + { + wa::storage::blob_shared_access_policy policy; + policy.set_expiry(utility::datetime::utc_now() + utility::datetime::from_minutes(30)); + policy.set_permissions(wa::storage::blob_shared_access_policy::permissions::read); + auto sas_token = m_container.get_shared_access_signature(policy); + + web::http::uri_builder builder(m_container.uri().primary_uri()); + builder.append_path(U("/blob")); + wa::storage::cloud_blob blob1(wa::storage::storage_uri(builder.to_uri()), m_container.service_client().credentials()); + + builder.set_query(sas_token); + CHECK_THROW(wa::storage::cloud_blob(wa::storage::storage_uri(builder.to_uri()), m_container.service_client().credentials()), std::invalid_argument); + + utility::string_t snapshot_time(U("2013-08-15T11:22:33.1234567Z")); + utility::string_t invalid_snapshot_time(U("2013-08-15T12:22:33.1234567Z")); + builder.set_query(utility::string_t()); + wa::storage::cloud_blob blob2(wa::storage::storage_uri(builder.to_uri()), snapshot_time, m_container.service_client().credentials()); + + builder.append_query(U("snapshot"), invalid_snapshot_time); + wa::storage::cloud_blob blob3(wa::storage::storage_uri(builder.to_uri()), invalid_snapshot_time, m_container.service_client().credentials()); + CHECK_THROW(wa::storage::cloud_blob(wa::storage::storage_uri(builder.to_uri()), snapshot_time, m_container.service_client().credentials()), std::invalid_argument); + + auto sas_container = wa::storage::cloud_blob_container(m_container.uri(), wa::storage::storage_credentials(sas_token)); + CHECK_THROW(sas_container.get_shared_access_signature(policy), std::logic_error); + auto sas_blob = sas_container.get_blob_reference(U("blob")); + CHECK_THROW(sas_blob.get_shared_access_signature(policy), std::logic_error); + + auto anonymous_container = wa::storage::cloud_blob_container(m_container.uri()); + CHECK_THROW(anonymous_container.get_shared_access_signature(policy), std::logic_error); + auto anonymous_blob = anonymous_container.get_blob_reference(U("blob")); + CHECK_THROW(anonymous_blob.get_shared_access_signature(policy), std::logic_error); + } + + TEST_FIXTURE(blob_test_base, container_sas_combinations) + { + for (int i = 0; i < 16; i++) + { + auto permissions = static_cast(i); + + wa::storage::blob_shared_access_policy policy; + policy.set_permissions(permissions); + policy.set_start(utility::datetime::utc_now() - utility::datetime::from_minutes(5)); + policy.set_expiry(utility::datetime::utc_now() + utility::datetime::from_minutes(30)); + auto sas_token = m_container.get_shared_access_signature(policy); + + auto blob = m_container.get_block_blob_reference(U("blob") + utility::conversions::print_string(i)); + blob.properties().set_cache_control(U("no-transform")); + blob.properties().set_content_disposition(U("attachment")); + blob.properties().set_content_encoding(U("gzip")); + blob.properties().set_content_language(U("tr,en")); + blob.properties().set_content_type(U("text/html")); + blob.upload_text(U("test"), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + check_access(sas_token, permissions, wa::storage::cloud_blob_shared_access_headers(), blob); + } + } + + TEST_FIXTURE(blob_test_base, blob_sas_combinations) + { + for (int i = 0; i < 8; i++) + { + auto permissions = static_cast(i); + + wa::storage::blob_shared_access_policy policy; + policy.set_permissions(permissions); + policy.set_start(utility::datetime::utc_now() - utility::datetime::from_minutes(5)); + policy.set_expiry(utility::datetime::utc_now() + utility::datetime::from_minutes(30)); + + auto blob = m_container.get_block_blob_reference(U("blob") + utility::conversions::print_string(i)); + blob.properties().set_cache_control(U("no-transform")); + blob.properties().set_content_disposition(U("attachment")); + blob.properties().set_content_encoding(U("gzip")); + blob.properties().set_content_language(U("tr,en")); + blob.properties().set_content_type(U("text/html")); + blob.upload_text(U("test"), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + auto sas_token = blob.get_shared_access_signature(policy); + check_access(sas_token, permissions, wa::storage::cloud_blob_shared_access_headers(), blob); + } + } + + TEST_FIXTURE(blob_test_base, blob_sas_combinations_headers) + { + wa::storage::blob_shared_access_policy policy; + policy.set_permissions(wa::storage::blob_shared_access_policy::permissions::read); + policy.set_start(utility::datetime::utc_now() - utility::datetime::from_minutes(5)); + policy.set_expiry(utility::datetime::utc_now() + utility::datetime::from_minutes(30)); + + auto blob = m_container.get_block_blob_reference(U("blob")); + blob.properties().set_cache_control(U("no-transform")); + blob.properties().set_content_disposition(U("attachment")); + blob.properties().set_content_encoding(U("gzip")); + blob.properties().set_content_language(U("tr,en")); + blob.properties().set_content_type(U("text/html")); + blob.upload_text(U("test"), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + wa::storage::cloud_blob_shared_access_headers headers; + headers.set_cache_control(U("s-maxage")); + headers.set_content_disposition(U("inline")); + headers.set_content_encoding(U("chunked")); + headers.set_content_language(U("en")); + headers.set_content_type(U("plain/text")); + + auto sas_token = blob.get_shared_access_signature(policy, utility::string_t(), headers); + check_access(sas_token, wa::storage::blob_shared_access_policy::permissions::read, headers, blob); + } + + TEST_FIXTURE(blob_test_base, blob_sas_invalid_time) + { + wa::storage::blob_shared_access_policy policy; + policy.set_permissions(wa::storage::blob_shared_access_policy::permissions::read); + + auto blob = m_container.get_block_blob_reference(U("blob")); + blob.upload_text(U("test"), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + policy.set_start(utility::datetime::utc_now() + utility::datetime::from_minutes(5)); + policy.set_expiry(utility::datetime::utc_now() + utility::datetime::from_minutes(30)); + check_access(blob.get_shared_access_signature(policy), wa::storage::blob_shared_access_policy::permissions::none, wa::storage::cloud_blob_shared_access_headers(), blob); + + policy.set_start(utility::datetime::utc_now() - utility::datetime::from_minutes(10)); + policy.set_expiry(utility::datetime::utc_now() - utility::datetime::from_minutes(5)); + check_access(blob.get_shared_access_signature(policy), wa::storage::blob_shared_access_policy::permissions::none, wa::storage::cloud_blob_shared_access_headers(), blob); + } + + TEST_FIXTURE(block_blob_test_base, blob_snapshot) + { + m_blob.upload_text(U("1"), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK(!m_blob.is_snapshot()); + CHECK(m_blob.snapshot_time().empty()); + CHECK_UTF8_EQUAL(m_blob.snapshot_qualified_uri().primary_uri().to_string(), m_blob.uri().primary_uri().to_string()); + CHECK_UTF8_EQUAL(m_blob.snapshot_qualified_uri().secondary_uri().to_string(), m_blob.uri().secondary_uri().to_string()); + + auto snapshot1 = m_blob.create_snapshot(wa::storage::cloud_metadata(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK(wa::storage::blob_type::block_blob == snapshot1.type()); + CHECK_UTF8_EQUAL(m_blob.properties().etag(), snapshot1.properties().etag()); + CHECK(m_blob.properties().last_modified() == snapshot1.properties().last_modified()); + CHECK(snapshot1.is_snapshot()); + CHECK(!snapshot1.snapshot_time().empty()); + CHECK_UTF8_EQUAL(m_blob.uri().primary_uri().to_string(), snapshot1.uri().primary_uri().to_string()); + CHECK_UTF8_EQUAL(m_blob.uri().secondary_uri().to_string(), snapshot1.uri().secondary_uri().to_string()); + CHECK(m_blob.snapshot_qualified_uri().primary_uri() != snapshot1.snapshot_qualified_uri().primary_uri()); + CHECK(m_blob.snapshot_qualified_uri().secondary_uri() != snapshot1.snapshot_qualified_uri().secondary_uri()); + CHECK(snapshot1.snapshot_qualified_uri().primary_uri().query().find(U("snapshot")) != utility::string_t::npos); + CHECK(snapshot1.snapshot_qualified_uri().secondary_uri().query().find(U("snapshot")) != utility::string_t::npos); + + CHECK_THROW(snapshot1.upload_properties(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context), std::logic_error); + CHECK_THROW(snapshot1.upload_metadata(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context), std::logic_error); + CHECK_THROW(snapshot1.create_snapshot(wa::storage::cloud_metadata(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context), std::logic_error); + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + auto snapshot2 = m_blob.create_snapshot(wa::storage::cloud_metadata(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK(snapshot2.snapshot_time() > snapshot1.snapshot_time()); + + snapshot1.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + m_blob.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + check_blob_properties_equal(m_blob.properties(), snapshot1.properties()); + + web::http::uri snapshot1_primary_uri(m_blob.uri().primary_uri().to_string() + U("?snapshot=") + snapshot1.snapshot_time()); + web::http::uri snapshot1_secondary_uri(m_blob.uri().secondary_uri().to_string() + U("?snapshot=") + snapshot1.snapshot_time()); + wa::storage::cloud_blob snapshot1_clone(wa::storage::storage_uri(snapshot1_primary_uri, snapshot1_secondary_uri), m_blob.service_client().credentials()); + CHECK(snapshot1.snapshot_time() == snapshot1_clone.snapshot_time()); + snapshot1_clone.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + check_blob_properties_equal(snapshot1.properties(), snapshot1_clone.properties()); + + wa::storage::cloud_blob snapshot1_clone2(m_blob.uri(), snapshot1.snapshot_time(), m_blob.service_client().credentials()); + CHECK(snapshot1.snapshot_time() == snapshot1_clone2.snapshot_time()); + snapshot1_clone2.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + check_blob_properties_equal(snapshot1.properties(), snapshot1_clone2.properties()); + + m_blob.upload_text(U("2"), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_UTF8_EQUAL(U("1"), wa::storage::cloud_block_blob(snapshot1).download_text(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context)); + + auto snapshot1_copy = m_container.get_block_blob_reference(m_blob.name() + U("copy")); + snapshot1_copy.start_copy_from_blob(defiddler(snapshot1.snapshot_qualified_uri().primary_uri()), wa::storage::access_condition(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK(wait_for_copy(snapshot1_copy)); + CHECK_UTF8_EQUAL(U("1"), snapshot1_copy.download_text(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context)); + + auto blobs = list_all_blobs(utility::string_t(), wa::storage::blob_listing_includes::all(), 0, wa::storage::blob_request_options()); + CHECK_EQUAL(4, blobs.size()); + check_blob_equal(snapshot1, blobs[0]); + check_blob_equal(snapshot2, blobs[1]); + check_blob_equal(m_blob, blobs[2]); + check_blob_equal(snapshot1_copy, blobs[3]); + } + + TEST_FIXTURE(blob_test_base, blob_snapshot_delete) + { + auto delete_root_only_blob = m_container.get_block_blob_reference(U("delete_root_only_not_permitted")); + delete_root_only_blob.upload_text(U("1"), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + auto delete_root_only_snapshot = delete_root_only_blob.create_snapshot(wa::storage::cloud_metadata(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_THROW(delete_root_only_blob.delete_blob(wa::storage::delete_snapshots_option::none, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + CHECK(delete_root_only_blob.exists(wa::storage::blob_request_options(), m_context)); + CHECK(delete_root_only_snapshot.exists(wa::storage::blob_request_options(), m_context)); + + auto delete_snapshots_only_blob = m_container.get_block_blob_reference(U("delete_snapshots_only")); + delete_snapshots_only_blob.upload_text(U("1"), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + auto delete_snapshots_only_snapshot = delete_snapshots_only_blob.create_snapshot(wa::storage::cloud_metadata(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + delete_snapshots_only_blob.delete_blob(wa::storage::delete_snapshots_option::delete_snapshots_only, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK(delete_snapshots_only_blob.exists(wa::storage::blob_request_options(), m_context)); + CHECK(!delete_snapshots_only_snapshot.exists(wa::storage::blob_request_options(), m_context)); + + auto delete_everything_blob = m_container.get_block_blob_reference(U("delete_everything")); + delete_everything_blob.upload_text(U("1"), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + auto delete_everything_snapshot = delete_everything_blob.create_snapshot(wa::storage::cloud_metadata(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + delete_everything_blob.delete_blob(wa::storage::delete_snapshots_option::include_snapshots, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK(!delete_everything_blob.exists(wa::storage::blob_request_options(), m_context)); + CHECK(!delete_everything_snapshot.exists(wa::storage::blob_request_options(), m_context)); + + CHECK_THROW(delete_root_only_snapshot.delete_blob(wa::storage::delete_snapshots_option::include_snapshots, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context), std::invalid_argument); + CHECK_THROW(delete_root_only_snapshot.delete_blob(wa::storage::delete_snapshots_option::delete_snapshots_only, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context), std::invalid_argument); + delete_root_only_snapshot.delete_blob(wa::storage::delete_snapshots_option::none, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + } + + TEST_FIXTURE(blob_test_base, blob_copy) + { + auto blob = m_container.get_block_blob_reference(U("blob")); + blob.upload_text(U("1"), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + auto copy = m_container.get_block_blob_reference(U("copy")); + CHECK_THROW(copy.start_copy_from_blob(defiddler(blob.uri().primary_uri()), wa::storage::access_condition::generate_if_match_condition(U("\"0xFFFFFFFFFFFFFFF\"")), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + CHECK_EQUAL(web::http::status_codes::PreconditionFailed, m_context.request_results().back().http_status_code()); + auto copy_id = copy.start_copy_from_blob(defiddler(blob.uri().primary_uri()), wa::storage::access_condition::generate_if_match_condition(blob.properties().etag()), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK(wait_for_copy(copy)); + CHECK_THROW(copy.abort_copy(copy_id, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + CHECK_EQUAL(web::http::status_codes::Conflict, m_context.request_results().back().http_status_code()); + CHECK_THROW(copy.start_copy_from_blob(defiddler(blob.uri().primary_uri()), wa::storage::access_condition::generate_if_match_condition(blob.properties().etag()), wa::storage::access_condition::generate_if_match_condition(U("\"0xFFFFFFFFFFFFFFF\"")), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + CHECK_EQUAL(web::http::status_codes::PreconditionFailed, m_context.request_results().back().http_status_code()); + copy.start_copy_from_blob(defiddler(blob.uri().primary_uri()), wa::storage::access_condition::generate_if_match_condition(blob.properties().etag()), wa::storage::access_condition::generate_if_match_condition(copy.properties().etag()), wa::storage::blob_request_options(), m_context); + CHECK(wait_for_copy(copy)); + } +} diff --git a/Microsoft.WindowsAzure.Storage/tests/cloud_block_blob_test.cpp b/Microsoft.WindowsAzure.Storage/tests/cloud_block_blob_test.cpp new file mode 100644 index 00000000..2b70eb46 --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/cloud_block_blob_test.cpp @@ -0,0 +1,520 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "blob_test_base.h" +#include "check_macros.h" + +#pragma region Fixture + +utility::string_t block_blob_test_base::get_block_id(uint16_t block_index) +{ + return utility::conversions::to_base64(block_index); +} + +void block_blob_test_base::check_block_list_equal(const std::vector& committed_put_block_list, const std::vector& uncommitted_put_block_list) +{ + { + auto get_block_list = m_blob.download_block_list(wa::storage::block_listing_filter::committed, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_EQUAL(committed_put_block_list.size(), get_block_list.size()); + for (size_t i = 0; i < committed_put_block_list.size(); ++i) + { + CHECK_UTF8_EQUAL(committed_put_block_list[i].id(), get_block_list[i].id()); + CHECK(wa::storage::block_list_item::block_mode::committed == get_block_list[i].mode()); + } + } + + { + auto get_block_list = m_blob.download_block_list(wa::storage::block_listing_filter::uncommitted, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_EQUAL(uncommitted_put_block_list.size(), get_block_list.size()); + for (size_t i = 0; i < uncommitted_put_block_list.size(); ++i) + { + CHECK_UTF8_EQUAL(uncommitted_put_block_list[i].id(), get_block_list[i].id()); + CHECK(wa::storage::block_list_item::block_mode::uncommitted == get_block_list[i].mode()); + } + } + + { + auto get_block_list = m_blob.download_block_list(wa::storage::block_listing_filter::all, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_EQUAL(committed_put_block_list.size() + uncommitted_put_block_list.size(), get_block_list.size()); + for (size_t i = 0; i < committed_put_block_list.size(); ++i) + { + CHECK_UTF8_EQUAL(committed_put_block_list[i].id(), get_block_list[i].id()); + CHECK(wa::storage::block_list_item::block_mode::committed == get_block_list[i].mode()); + } + + for (size_t i = 0; i < uncommitted_put_block_list.size(); ++i) + { + CHECK_UTF8_EQUAL(uncommitted_put_block_list[i].id(), get_block_list[committed_put_block_list.size() + i].id()); + CHECK(wa::storage::block_list_item::block_mode::uncommitted == get_block_list[committed_put_block_list.size() + i].mode()); + } + } +} + +#pragma endregion + +SUITE(Blob) +{ + TEST_FIXTURE(block_blob_test_base, block_upload) + { + std::vector buffer; + buffer.resize(16 * 1024); + wa::storage::blob_request_options options; + std::vector uncommitted_blocks; + std::vector committed_blocks; + + utility::string_t md5_header; + m_context.set_sending_request([&md5_header] (web::http::http_request& request, wa::storage::operation_context) + { + if (!request.headers().match(web::http::header_names::content_md5, md5_header)) + { + md5_header.clear(); + } + }); + + options.set_use_transactional_md5(false); + for (int i = 0; i < 3; ++i) + { + fill_buffer_and_get_md5(buffer); + auto stream = concurrency::streams::bytestream::open_istream(buffer); + auto block_id = get_block_id(i); + uncommitted_blocks.push_back(wa::storage::block_list_item(block_id)); + m_blob.upload_block(block_id, stream, utility::string_t(), wa::storage::access_condition(), options, m_context); + CHECK_UTF8_EQUAL(utility::string_t(), md5_header); + } + + check_block_list_equal(committed_blocks, uncommitted_blocks); + std::copy(uncommitted_blocks.begin(), uncommitted_blocks.end(), std::back_inserter(committed_blocks)); + m_blob.upload_block_list(committed_blocks, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + uncommitted_blocks.clear(); + + options.set_use_transactional_md5(false); + for (int i = 3; i < 6; ++i) + { + auto md5 = fill_buffer_and_get_md5(buffer); + auto stream = concurrency::streams::bytestream::open_istream(buffer); + auto block_id = get_block_id(i); + uncommitted_blocks.push_back(wa::storage::block_list_item(block_id)); + m_blob.upload_block(block_id, stream, md5, wa::storage::access_condition(), options, m_context); + CHECK_UTF8_EQUAL(md5, md5_header); + } + + check_block_list_equal(committed_blocks, uncommitted_blocks); + std::copy(uncommitted_blocks.begin(), uncommitted_blocks.end(), std::back_inserter(committed_blocks)); + m_blob.upload_block_list(committed_blocks, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + uncommitted_blocks.clear(); + + options.set_use_transactional_md5(true); + for (int i = 6; i < 9; ++i) + { + auto md5 = fill_buffer_and_get_md5(buffer); + auto stream = concurrency::streams::bytestream::open_istream(buffer); + auto block_id = get_block_id(i); + uncommitted_blocks.push_back(wa::storage::block_list_item(block_id)); + m_blob.upload_block(block_id, stream, utility::string_t(), wa::storage::access_condition(), options, m_context); + CHECK_UTF8_EQUAL(md5, md5_header); + } + + check_block_list_equal(committed_blocks, uncommitted_blocks); + std::copy(uncommitted_blocks.begin(), uncommitted_blocks.end(), std::back_inserter(committed_blocks)); + m_blob.upload_block_list(committed_blocks, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + uncommitted_blocks.clear(); + + options.set_use_transactional_md5(true); + fill_buffer_and_get_md5(buffer); + auto stream = concurrency::streams::bytestream::open_istream(buffer); + CHECK_THROW(m_blob.upload_block(get_block_id(0), stream, dummy_md5, wa::storage::access_condition(), options, m_context), wa::storage::storage_exception); + CHECK_UTF8_EQUAL(dummy_md5, md5_header); + + check_block_list_equal(committed_blocks, uncommitted_blocks); + + m_context.set_sending_request(std::function()); + } + + TEST_FIXTURE(block_blob_test_base, block_blob_upload) + { + const size_t size = 6 * 1024 * 1024; + wa::storage::blob_request_options options; + + options.set_store_blob_content_md5(false); + check_parallelism(upload_and_download(m_blob, size, 0, 0, true, options, 1, false), 1); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_use_transactional_md5(true); + options.set_store_blob_content_md5(true); + check_parallelism(upload_and_download(m_blob, size, 0, 0, true, options, 1, true), 1); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_single_blob_upload_threshold_in_bytes(4 * 1024 * 1024); + check_parallelism(upload_and_download(m_blob, size, 0, 0, true, options, 3, true), 1); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_store_blob_content_md5(false); + check_parallelism(upload_and_download(m_blob, size, 0, 0, true, options, 3, false), 1); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_stream_write_size_in_bytes(1 * 1024 * 1024); + check_parallelism(upload_and_download(m_blob, size, 0, 0, true, options, 7, false), 1); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_use_transactional_md5(false); + options.set_single_blob_upload_threshold_in_bytes(6 * 1024 * 1024); + check_parallelism(upload_and_download(m_blob, size, 0, 0, true, options, 1, false), 1); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_parallelism_factor(4); + options.set_use_transactional_md5(true); + check_parallelism(upload_and_download(m_blob, size, 0, 0, true, options, 7, false), 4); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_parallelism_factor(8); + options.set_store_blob_content_md5(true); + check_parallelism(upload_and_download(m_blob, size, 0, 0, true, options, 7, true), 6); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + } + + TEST_FIXTURE(block_blob_test_base, block_blob_upload_with_nonseekable) + { + const size_t size = 6 * 1024 * 1024; + wa::storage::blob_request_options options; + options.set_use_transactional_md5(true); + + options.set_store_blob_content_md5(false); + check_parallelism(upload_and_download(m_blob, size, 0, 0, false, options, 3, false), 1); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_store_blob_content_md5(true); + check_parallelism(upload_and_download(m_blob, size, 0, 0, false, options, 3, true), 1); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_single_blob_upload_threshold_in_bytes(4 * 1024 * 1024); + check_parallelism(upload_and_download(m_blob, size, 0, 0, false, options, 3, true), 1); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_store_blob_content_md5(false); + check_parallelism(upload_and_download(m_blob, size, 0, 0, false, options, 3, false), 1); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_stream_write_size_in_bytes(1 * 1024 * 1024); + check_parallelism(upload_and_download(m_blob, size, 0, 0, false, options, 7, false), 1); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_single_blob_upload_threshold_in_bytes(6 * 1024 * 1024); + check_parallelism(upload_and_download(m_blob, size, 0, 0, false, options, 7, false), 1); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_parallelism_factor(4); + check_parallelism(upload_and_download(m_blob, size, 0, 0, false, options, 7, false), 4); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_parallelism_factor(8); + options.set_store_blob_content_md5(true); + check_parallelism(upload_and_download(m_blob, size, 0, 0, false, options, 7, true), 6); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + } + + TEST_FIXTURE(block_blob_test_base, block_blob_upload_with_size) + { + const size_t buffer_size = 6 * 1024 * 1024; + const size_t blob_size = 4 * 1024 * 1024; + wa::storage::blob_request_options options; + + const size_t buffer_offsets[2] = { 0, 1024 }; + for (auto buffer_offset : buffer_offsets) + { + options.set_stream_write_size_in_bytes(blob_size); + options.set_use_transactional_md5(false); + options.set_store_blob_content_md5(false); + options.set_parallelism_factor(1); + check_parallelism(upload_and_download(m_blob, buffer_size, buffer_offset, blob_size, true, options, 1, false), 1); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_use_transactional_md5(true); + options.set_store_blob_content_md5(true); + check_parallelism(upload_and_download(m_blob, buffer_size, buffer_offset, blob_size, true, options, 1, true), 1); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_stream_write_size_in_bytes(1 * 1024 * 1024); + options.set_store_blob_content_md5(false); + options.set_parallelism_factor(4); + check_parallelism(upload_and_download(m_blob, buffer_size, buffer_offset, blob_size, true, options, 5, false), 4); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_parallelism_factor(8); + options.set_store_blob_content_md5(true); + check_parallelism(upload_and_download(m_blob, buffer_size, buffer_offset, blob_size, true, options, 5, true), 4); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + } + } + + TEST_FIXTURE(block_blob_test_base, block_blob_upload_with_size_with_nonseekable) + { + const size_t buffer_size = 6 * 1024 * 1024; + const size_t blob_size = 4 * 1024 * 1024; + wa::storage::blob_request_options options; + + const size_t buffer_offsets[2] = { 0, 1024 }; + for (auto buffer_offset : buffer_offsets) + { + options.set_stream_write_size_in_bytes(blob_size); + options.set_use_transactional_md5(false); + options.set_store_blob_content_md5(false); + options.set_parallelism_factor(1); + check_parallelism(upload_and_download(m_blob, buffer_size, buffer_offset, blob_size, false, options, 1, false), 1); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_use_transactional_md5(true); + options.set_store_blob_content_md5(true); + check_parallelism(upload_and_download(m_blob, buffer_size, buffer_offset, blob_size, false, options, 1, true), 1); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_stream_write_size_in_bytes(1 * 1024 * 1024); + options.set_store_blob_content_md5(false); + options.set_parallelism_factor(4); + check_parallelism(upload_and_download(m_blob, buffer_size, buffer_offset, blob_size, false, options, 5, false), 4); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_parallelism_factor(8); + options.set_store_blob_content_md5(true); + check_parallelism(upload_and_download(m_blob, buffer_size, buffer_offset, blob_size, false, options, 5, true), 4); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + } + } + + TEST_FIXTURE(block_blob_test_base, block_blob_upload_with_invalid_size) + { + const size_t buffer_size = 2 * 1024 * 1024; + wa::storage::blob_request_options options; + options.set_store_blob_content_md5(false); + CHECK_THROW(upload_and_download(m_blob, buffer_size, 0, buffer_size + 1, true, options, 0, false), wa::storage::storage_exception); + CHECK_THROW(upload_and_download(m_blob, buffer_size, 0, buffer_size + 1, false, options, 0, false), std::invalid_argument); + CHECK_THROW(upload_and_download(m_blob, buffer_size, 1024, buffer_size - 1023, true, options, 0, false), wa::storage::storage_exception); + CHECK_THROW(upload_and_download(m_blob, buffer_size, 1024, buffer_size - 1023, false, options, 0, false), std::invalid_argument); + } + + TEST_FIXTURE(block_blob_test_base, block_blob_text_upload) + { + wa::storage::blob_request_options options; + options.set_store_blob_content_md5(true); + + utility::string_t text; + m_blob.upload_text(text, wa::storage::access_condition(), options, m_context); + CHECK(m_blob.download_text(wa::storage::access_condition(), options, m_context).empty()); + + text = U("test"); + m_blob.upload_text(text, wa::storage::access_condition(), options, m_context); + CHECK_UTF8_EQUAL(text, m_blob.download_text(wa::storage::access_condition(), options, m_context)); + + m_blob.properties().set_content_md5(dummy_md5); + m_blob.upload_properties(); + CHECK_THROW(m_blob.download_text(wa::storage::access_condition(), options, m_context), wa::storage::storage_exception); + } + + TEST_FIXTURE(block_blob_test_base, block_blob_constructor) + { + m_blob.upload_block_list(std::vector(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK(!m_blob.properties().etag().empty()); + + wa::storage::cloud_block_blob blob1(m_blob.uri()); + CHECK_UTF8_EQUAL(m_blob.name(), blob1.name()); + CHECK_UTF8_EQUAL(m_blob.uri().primary_uri().to_string(), blob1.uri().primary_uri().to_string()); + CHECK_UTF8_EQUAL(m_blob.uri().secondary_uri().to_string(), blob1.uri().secondary_uri().to_string()); + CHECK(blob1.properties().etag().empty()); + + wa::storage::cloud_blob blob2(m_blob); + CHECK_UTF8_EQUAL(m_blob.name(), blob2.name()); + CHECK_UTF8_EQUAL(m_blob.uri().primary_uri().to_string(), blob2.uri().primary_uri().to_string()); + CHECK_UTF8_EQUAL(m_blob.uri().secondary_uri().to_string(), blob2.uri().secondary_uri().to_string()); + CHECK_UTF8_EQUAL(m_blob.properties().etag(), blob2.properties().etag()); + + wa::storage::cloud_block_blob blob3(blob2); + CHECK_UTF8_EQUAL(m_blob.name(), blob3.name()); + CHECK_UTF8_EQUAL(m_blob.uri().primary_uri().to_string(), blob3.uri().primary_uri().to_string()); + CHECK_UTF8_EQUAL(m_blob.uri().secondary_uri().to_string(), blob3.uri().secondary_uri().to_string()); + CHECK_UTF8_EQUAL(m_blob.properties().etag(), blob2.properties().etag()); + } + + TEST_FIXTURE(block_blob_test_base, block_blob_block_list_with_metadata) + { + m_blob.metadata()[U("key1")] = U("value1"); + m_blob.metadata()[U("key2")] = U("value2"); + m_blob.upload_block_list(std::vector()); + + auto same_blob = m_container.get_block_blob_reference(m_blob.name()); + CHECK(same_blob.metadata().empty()); + same_blob.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_EQUAL(2, same_blob.metadata().size()); + CHECK_UTF8_EQUAL(U("value1"), same_blob.metadata()[U("key1")]); + CHECK_UTF8_EQUAL(U("value2"), same_blob.metadata()[U("key2")]); + } + + TEST_FIXTURE(block_blob_test_base, block_blob_put_blob_with_metadata) + { + m_blob.metadata()[U("key1")] = U("value1"); + m_blob.metadata()[U("key2")] = U("value2"); + m_blob.upload_text(utility::string_t(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + auto same_blob = m_container.get_block_blob_reference(m_blob.name()); + CHECK(same_blob.metadata().empty()); + same_blob.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_EQUAL(2, same_blob.metadata().size()); + CHECK_UTF8_EQUAL(U("value1"), same_blob.metadata()[U("key1")]); + CHECK_UTF8_EQUAL(U("value2"), same_blob.metadata()[U("key2")]); + } + + TEST_FIXTURE(block_blob_test_base, block_blob_upload_invalid_options) + { + wa::storage::blob_request_options options; + options.set_store_blob_content_md5(false); + options.set_use_transactional_md5(true); + + CHECK_THROW(m_blob.upload_text(utility::string_t(), wa::storage::access_condition(), options, m_context), std::invalid_argument); + } + + TEST_FIXTURE(block_blob_test_base, block_blob_snapshot_metadata) + { + m_blob.metadata()[U("key1")] = U("value1"); + m_blob.metadata()[U("key2")] = U("value2"); + m_blob.upload_text(U("1")); + + auto snapshot1 = m_blob.create_snapshot(wa::storage::cloud_metadata(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK(snapshot1.metadata().empty()); + snapshot1.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_EQUAL(2, snapshot1.metadata().size()); + CHECK_UTF8_EQUAL(U("value1"), snapshot1.metadata()[U("key1")]); + CHECK_UTF8_EQUAL(U("value2"), snapshot1.metadata()[U("key2")]); + + wa::storage::cloud_metadata snapshot_metadata; + snapshot_metadata[U("key3")] = U("value1"); + snapshot_metadata[U("key4")] = U("value2"); + auto snapshot2 = m_blob.create_snapshot(snapshot_metadata, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK(snapshot2.metadata().empty()); + snapshot2.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_EQUAL(2, snapshot1.metadata().size()); + CHECK_UTF8_EQUAL(U("value1"), snapshot2.metadata()[U("key3")]); + CHECK_UTF8_EQUAL(U("value2"), snapshot2.metadata()[U("key4")]); + } + + TEST_FIXTURE(block_blob_test_base, block_reordering) + { + m_blob.properties().set_content_type(U("text/plain; charset=utf-8")); + + std::vector blocks; + for (int i = 0; i < 10; i++) + { + auto id = get_block_id(i); + auto utf8_body = utility::conversions::to_utf8string(utility::conversions::print_string(i)); + auto length = utf8_body.size(); + auto stream = concurrency::streams::bytestream::open_istream(std::move(utf8_body)); + m_blob.upload_block(id, stream, utility::string_t(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + blocks.push_back(wa::storage::block_list_item(id)); + } + + m_blob.upload_block_list(blocks, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_UTF8_EQUAL(U("0123456789"), m_blob.download_text(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context)); + + blocks.erase(blocks.begin()); + m_blob.upload_block_list(blocks, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_UTF8_EQUAL(U("123456789"), m_blob.download_text(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context)); + + auto iter = blocks.begin(); + std::advance(iter, 3); + blocks.erase(iter); + m_blob.upload_block_list(blocks, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_UTF8_EQUAL(U("12356789"), m_blob.download_text(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context)); + + auto id = get_block_id(4); + auto utf8_body = utility::conversions::to_utf8string(utility::conversions::print_string(4)); + auto length = utf8_body.size(); + auto stream = concurrency::streams::bytestream::open_istream(std::move(utf8_body)); + m_blob.upload_block(id, stream, utility::string_t(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + blocks.insert(blocks.begin(), wa::storage::block_list_item(id)); + m_blob.upload_block_list(blocks, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_UTF8_EQUAL(U("412356789"), m_blob.download_text(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context)); + + blocks.push_back(wa::storage::block_list_item(id)); + m_blob.upload_block_list(blocks, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_UTF8_EQUAL(U("4123567894"), m_blob.download_text(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context)); + } + + TEST_FIXTURE(block_blob_test_base, list_uncommitted_blobs) + { + std::vector buffer; + buffer.resize(16 * 1024); + fill_buffer_and_get_md5(buffer); + auto stream = concurrency::streams::bytestream::open_istream(buffer); + auto ucblob = m_container.get_block_blob_reference(U("ucblob")); + ucblob.upload_block(get_block_id(0), stream, utility::string_t(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + m_blob.upload_text(utility::string_t(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + wa::storage::blob_listing_includes includes; + auto blobs = list_all_blobs(utility::string_t(), includes, 0, wa::storage::blob_request_options()); + CHECK_EQUAL(1, blobs.size()); + + includes.set_uncommitted_blobs(true); + blobs = list_all_blobs(utility::string_t(), includes, 0, wa::storage::blob_request_options()); + CHECK_EQUAL(2, blobs.size()); + } + + TEST_FIXTURE(block_blob_test_base, block_blob_upload_maximum_execution_time) + { + std::chrono::seconds duration(10); + + std::vector buffer; + buffer.resize(2 * 1024 * 1024); + + wa::storage::blob_request_options options; + options.set_maximum_execution_time(duration); + options.set_stream_write_size_in_bytes(buffer.size() / 2); + options.set_single_blob_upload_threshold_in_bytes(buffer.size() / 2); + + m_context.set_response_received([duration] (web::http::http_request&, const web::http::http_response&, wa::storage::operation_context) + { + std::this_thread::sleep_for(duration); + }); + + CHECK_THROW(m_blob.upload_from_stream(concurrency::streams::bytestream::open_istream(std::move(buffer)), wa::storage::access_condition(), options, m_context), wa::storage::storage_exception); + CHECK_EQUAL(2, m_context.request_results().size()); + + m_context.set_response_received(std::function()); + } +} diff --git a/Microsoft.WindowsAzure.Storage/tests/cloud_page_blob_test.cpp b/Microsoft.WindowsAzure.Storage/tests/cloud_page_blob_test.cpp new file mode 100644 index 00000000..3f6838aa --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/cloud_page_blob_test.cpp @@ -0,0 +1,409 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "blob_test_base.h" +#include "check_macros.h" + +#pragma region Fixture + +void page_blob_test_base::check_page_ranges_equal(const std::vector& page_ranges) +{ + auto downloaded_page_ranges = m_blob.download_page_ranges(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_EQUAL(page_ranges.size(), downloaded_page_ranges.size()); + for (size_t i = 0; i < page_ranges.size(); ++i) + { + CHECK_EQUAL(page_ranges[i].start_offset(), downloaded_page_ranges[i].start_offset()); + CHECK_EQUAL(page_ranges[i].end_offset(), downloaded_page_ranges[i].end_offset()); + } +} + +#pragma endregion + +SUITE(Blob) +{ + TEST_FIXTURE(page_blob_test_base, page_blob_create) + { + auto same_blob = m_container.get_page_blob_reference(m_blob.name()); + CHECK(!same_blob.exists(wa::storage::blob_request_options(), m_context)); + m_blob.create(1024, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_EQUAL(1024, m_blob.properties().size()); + CHECK_EQUAL(0, same_blob.properties().size()); + CHECK(same_blob.exists(wa::storage::blob_request_options(), m_context)); + CHECK_EQUAL(1024, same_blob.properties().size()); + m_blob.delete_blob(wa::storage::delete_snapshots_option::none, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK(!same_blob.exists(wa::storage::blob_request_options(), m_context)); + CHECK_EQUAL(1024, same_blob.properties().size()); + m_blob.create(2048, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_EQUAL(2048, m_blob.properties().size()); + CHECK_EQUAL(1024, same_blob.properties().size()); + CHECK(same_blob.exists(wa::storage::blob_request_options(), m_context)); + CHECK_EQUAL(2048, same_blob.properties().size()); + } + + TEST_FIXTURE(page_blob_test_base, page_blob_resize) + { + m_blob.properties().set_content_language(U("tr,en")); + m_blob.create(1024, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_EQUAL(1024, m_blob.properties().size()); + + m_blob.properties().set_content_language(U("en")); + m_blob.resize(2048, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_EQUAL(2048, m_blob.properties().size()); + + CHECK_UTF8_EQUAL(U("en"), m_blob.properties().content_language()); + m_blob.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_UTF8_EQUAL(U("tr,en"), m_blob.properties().content_language()); + } + + TEST_FIXTURE(page_blob_test_base, page_blob_sequence_number) + { + m_blob.properties().set_content_language(U("tr,en")); + m_blob.create(512, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_EQUAL(512, m_blob.properties().size()); + CHECK_EQUAL(0, m_blob.properties().page_blob_sequence_number()); + + m_blob.properties().set_content_language(U("en")); + m_blob.set_sequence_number(wa::storage::sequence_number::update(5), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_EQUAL(5, m_blob.properties().page_blob_sequence_number()); + + m_blob.set_sequence_number(wa::storage::sequence_number::maximum(7), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_EQUAL(7, m_blob.properties().page_blob_sequence_number()); + + m_blob.set_sequence_number(wa::storage::sequence_number::maximum(3), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_EQUAL(7, m_blob.properties().page_blob_sequence_number()); + + m_blob.set_sequence_number(wa::storage::sequence_number::increment(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_EQUAL(8, m_blob.properties().page_blob_sequence_number()); + + CHECK_UTF8_EQUAL(U("en"), m_blob.properties().content_language()); + m_blob.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_UTF8_EQUAL(U("tr,en"), m_blob.properties().content_language()); + CHECK_EQUAL(8, m_blob.properties().page_blob_sequence_number()); + + m_blob.clear_pages(0, 512, wa::storage::access_condition::generate_if_sequence_number_equal_condition(8), wa::storage::blob_request_options(), m_context); + m_blob.clear_pages(0, 512, wa::storage::access_condition::generate_if_sequence_number_less_than_or_equal_condition(8), wa::storage::blob_request_options(), m_context); + m_blob.clear_pages(0, 512, wa::storage::access_condition::generate_if_sequence_number_less_than_or_equal_condition(9), wa::storage::blob_request_options(), m_context); + m_blob.clear_pages(0, 512, wa::storage::access_condition::generate_if_sequence_number_less_than_condition(9), wa::storage::blob_request_options(), m_context); + + CHECK_THROW(m_blob.clear_pages(0, 512, wa::storage::access_condition::generate_if_sequence_number_equal_condition(7), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + CHECK_THROW(m_blob.clear_pages(0, 512, wa::storage::access_condition::generate_if_sequence_number_less_than_or_equal_condition(7), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + CHECK_THROW(m_blob.clear_pages(0, 512, wa::storage::access_condition::generate_if_sequence_number_less_than_or_equal_condition(7), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + CHECK_THROW(m_blob.clear_pages(0, 512, wa::storage::access_condition::generate_if_sequence_number_less_than_condition(7), wa::storage::blob_request_options(), m_context), wa::storage::storage_exception); + } + + TEST_FIXTURE(page_blob_test_base, page_upload) + { + std::vector buffer; + buffer.resize(512); + wa::storage::blob_request_options options; + std::vector pages; + + utility::string_t md5_header; + m_context.set_sending_request([&md5_header] (web::http::http_request& request, wa::storage::operation_context) + { + if (!request.headers().match(web::http::header_names::content_md5, md5_header)) + { + md5_header.clear(); + } + }); + + m_blob.create(1 * 1024 * 1024, wa::storage::access_condition(), options, m_context); + + options.set_use_transactional_md5(false); + for (int i = 0; i < 3; ++i) + { + fill_buffer_and_get_md5(buffer); + auto stream = concurrency::streams::bytestream::open_istream(buffer); + wa::storage::page_range range(i * 1024, i * 1024 + buffer.size() - 1); + pages.push_back(range); + m_blob.upload_pages(stream, range.start_offset(), utility::string_t(), wa::storage::access_condition(), options, m_context); + CHECK_UTF8_EQUAL(utility::string_t(), md5_header); + } + + check_page_ranges_equal(pages); + + options.set_use_transactional_md5(false); + for (int i = 3; i < 6; ++i) + { + auto md5 = fill_buffer_and_get_md5(buffer); + auto stream = concurrency::streams::bytestream::open_istream(buffer); + wa::storage::page_range range(i * 1536, i * 1536 + buffer.size() - 1); + pages.push_back(range); + m_blob.upload_pages(stream, range.start_offset(), md5, wa::storage::access_condition(), options, m_context); + CHECK_UTF8_EQUAL(md5, md5_header); + } + + check_page_ranges_equal(pages); + + options.set_use_transactional_md5(true); + for (int i = 6; i < 9; ++i) + { + auto md5 = fill_buffer_and_get_md5(buffer); + auto stream = concurrency::streams::bytestream::open_istream(buffer); + wa::storage::page_range range(i * 2048, i * 2048 + buffer.size() - 1); + pages.push_back(range); + m_blob.upload_pages(stream, range.start_offset(), utility::string_t(), wa::storage::access_condition(), options, m_context); + CHECK_UTF8_EQUAL(md5, md5_header); + } + + check_page_ranges_equal(pages); + + options.set_use_transactional_md5(true); + fill_buffer_and_get_md5(buffer); + auto stream = concurrency::streams::bytestream::open_istream(buffer); + CHECK_THROW(m_blob.upload_pages(stream, 0, dummy_md5, wa::storage::access_condition(), options, m_context), wa::storage::storage_exception); + CHECK_UTF8_EQUAL(dummy_md5, md5_header); + + check_page_ranges_equal(pages); + + m_context.set_sending_request(std::function()); + } + + TEST_FIXTURE(page_blob_test_base, page_clear) + { + std::vector buffer; + buffer.resize(16 * 1024); + wa::storage::blob_request_options options; + + fill_buffer_and_get_md5(buffer); + auto stream = concurrency::streams::bytestream::open_istream(buffer); + m_blob.upload_from_stream(stream, wa::storage::access_condition(), options, m_context); + + std::vector pages; + pages.push_back(wa::storage::page_range(0, 512 - 1)); + pages.push_back(wa::storage::page_range(512 * 2, 10 * 1024 - 1)); + pages.push_back(wa::storage::page_range(13 * 1024, buffer.size() - 1)); + + for (size_t i = 1; i < pages.size(); i++) + { + int64_t start_offset = pages[i - 1].end_offset() + 1; + int64_t length = pages[i].start_offset() - start_offset; + m_blob.clear_pages(start_offset, length, wa::storage::access_condition(), options, m_context); + } + + check_page_ranges_equal(pages); + } + + TEST_FIXTURE(page_blob_test_base, page_blob_upload) + { + const size_t size = 6 * 1024 * 1024; + wa::storage::blob_request_options options; + options.set_use_transactional_md5(true); + + check_parallelism(upload_and_download(m_blob, size, 0, 0, true, options, 3, false), 1); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_store_blob_content_md5(true); + check_parallelism(upload_and_download(m_blob, size, 0, 0, true, options, 4, true), 1); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_store_blob_content_md5(false); + options.set_stream_write_size_in_bytes(1 * 1024 * 1024); + check_parallelism(upload_and_download(m_blob, size, 0, 0, true, options, 7, false), 1); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_parallelism_factor(4); + check_parallelism(upload_and_download(m_blob, size, 0, 0, true, options, 7, false), 4); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_parallelism_factor(8); + options.set_store_blob_content_md5(true); + check_parallelism(upload_and_download(m_blob, size, 0, 0, true, options, 8, true), 6); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + } + + TEST_FIXTURE(page_blob_test_base, page_blob_upload_with_nonseekable) + { + const size_t size = 6 * 1024 * 1024; + wa::storage::blob_request_options options; + + CHECK_THROW(upload_and_download(m_blob, size, 0, 0, false, options, 3, false), std::logic_error); + } + + TEST_FIXTURE(page_blob_test_base, page_blob_upload_with_size) + { + const size_t buffer_size = 6 * 1024 * 1024; + const size_t blob_size = 4 * 1024 * 1024; + wa::storage::blob_request_options options; + options.set_use_transactional_md5(true); + + const size_t buffer_offsets[2] = { 0, 1024 }; + for (auto buffer_offset : buffer_offsets) + { + options.set_stream_write_size_in_bytes(blob_size); + options.set_store_blob_content_md5(false); + options.set_parallelism_factor(1); + check_parallelism(upload_and_download(m_blob, buffer_size, buffer_offset, blob_size, true, options, 2, false), 1); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_store_blob_content_md5(true); + check_parallelism(upload_and_download(m_blob, buffer_size, buffer_offset, blob_size, true, options, 3, true), 1); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_stream_write_size_in_bytes(1 * 1024 * 1024); + options.set_store_blob_content_md5(false); + options.set_parallelism_factor(4); + check_parallelism(upload_and_download(m_blob, buffer_size, buffer_offset, blob_size, true, options, 5, false), 4); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_parallelism_factor(8); + options.set_store_blob_content_md5(true); + check_parallelism(upload_and_download(m_blob, buffer_size, buffer_offset, blob_size, true, options, 6, true), 4); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + } + } + + TEST_FIXTURE(page_blob_test_base, page_blob_upload_with_size_with_nonseekable) + { + const size_t buffer_size = 6 * 1024 * 1024; + const size_t blob_size = 4 * 1024 * 1024; + wa::storage::blob_request_options options; + options.set_use_transactional_md5(true); + + const size_t buffer_offsets[2] = { 0, 1024 }; + for (auto buffer_offset : buffer_offsets) + { + options.set_stream_write_size_in_bytes(blob_size); + options.set_store_blob_content_md5(false); + options.set_parallelism_factor(1); + check_parallelism(upload_and_download(m_blob, buffer_size, buffer_offset, blob_size, false, options, 2, false), 1); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_store_blob_content_md5(true); + check_parallelism(upload_and_download(m_blob, buffer_size, buffer_offset, blob_size, false, options, 3, true), 1); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_stream_write_size_in_bytes(1 * 1024 * 1024); + options.set_store_blob_content_md5(false); + options.set_parallelism_factor(4); + check_parallelism(upload_and_download(m_blob, buffer_size, buffer_offset, blob_size, false, options, 5, false), 4); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + + options.set_parallelism_factor(8); + options.set_store_blob_content_md5(true); + check_parallelism(upload_and_download(m_blob, buffer_size, buffer_offset, blob_size, false, options, 6, true), 4); + m_blob.delete_blob(); + m_blob.properties().set_content_md5(utility::string_t()); + } + } + + TEST_FIXTURE(page_blob_test_base, page_blob_upload_with_invalid_size) + { + const size_t buffer_size = 2 * 1024 * 1024; + wa::storage::blob_request_options options; + CHECK_THROW(upload_and_download(m_blob, buffer_size, 0, buffer_size + 1, true, options, 0, false), wa::storage::storage_exception); + CHECK_THROW(upload_and_download(m_blob, buffer_size, 0, buffer_size + 1, false, options, 0, false), wa::storage::storage_exception); + CHECK_THROW(upload_and_download(m_blob, buffer_size, 1024, buffer_size - 1023, true, options, 0, false), wa::storage::storage_exception); + CHECK_THROW(upload_and_download(m_blob, buffer_size, 1024, buffer_size - 1023, false, options, 0, false), wa::storage::storage_exception); + } + + TEST_FIXTURE(page_blob_test_base, page_blob_constructor) + { + m_blob.create(0, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK(!m_blob.properties().etag().empty()); + + wa::storage::cloud_page_blob blob1(m_blob.uri()); + CHECK_UTF8_EQUAL(m_blob.name(), blob1.name()); + CHECK_UTF8_EQUAL(m_blob.uri().primary_uri().to_string(), blob1.uri().primary_uri().to_string()); + CHECK_UTF8_EQUAL(m_blob.uri().secondary_uri().to_string(), blob1.uri().secondary_uri().to_string()); + CHECK(blob1.properties().etag().empty()); + + wa::storage::cloud_blob blob2(m_blob); + CHECK_UTF8_EQUAL(m_blob.name(), blob2.name()); + CHECK_UTF8_EQUAL(m_blob.uri().primary_uri().to_string(), blob2.uri().primary_uri().to_string()); + CHECK_UTF8_EQUAL(m_blob.uri().secondary_uri().to_string(), blob2.uri().secondary_uri().to_string()); + CHECK_UTF8_EQUAL(m_blob.properties().etag(), blob2.properties().etag()); + + wa::storage::cloud_page_blob blob3(blob2); + CHECK_UTF8_EQUAL(m_blob.name(), blob3.name()); + CHECK_UTF8_EQUAL(m_blob.uri().primary_uri().to_string(), blob3.uri().primary_uri().to_string()); + CHECK_UTF8_EQUAL(m_blob.uri().secondary_uri().to_string(), blob3.uri().secondary_uri().to_string()); + CHECK_UTF8_EQUAL(m_blob.properties().etag(), blob2.properties().etag()); + } + + TEST_FIXTURE(page_blob_test_base, page_blob_create_with_metadata) + { + m_blob.metadata()[U("key1")] = U("value1"); + m_blob.metadata()[U("key2")] = U("value2"); + m_blob.create(0, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + auto same_blob = m_container.get_page_blob_reference(m_blob.name()); + CHECK(same_blob.metadata().empty()); + same_blob.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_EQUAL(2, same_blob.metadata().size()); + CHECK_UTF8_EQUAL(U("value1"), same_blob.metadata()[U("key1")]); + CHECK_UTF8_EQUAL(U("value2"), same_blob.metadata()[U("key2")]); + } + TEST_FIXTURE(page_blob_test_base, page_blob_snapshot_metadata) + { + m_blob.metadata()[U("key1")] = U("value1"); + m_blob.metadata()[U("key2")] = U("value2"); + m_blob.create(0, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + + auto snapshot1 = m_blob.create_snapshot(wa::storage::cloud_metadata(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK(snapshot1.metadata().empty()); + snapshot1.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_EQUAL(2, snapshot1.metadata().size()); + CHECK_UTF8_EQUAL(U("value1"), snapshot1.metadata()[U("key1")]); + CHECK_UTF8_EQUAL(U("value2"), snapshot1.metadata()[U("key2")]); + + wa::storage::cloud_metadata snapshot_metadata; + snapshot_metadata[U("key3")] = U("value1"); + snapshot_metadata[U("key4")] = U("value2"); + auto snapshot2 = m_blob.create_snapshot(snapshot_metadata, wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK(snapshot2.metadata().empty()); + snapshot2.download_attributes(wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + CHECK_EQUAL(2, snapshot1.metadata().size()); + CHECK_UTF8_EQUAL(U("value1"), snapshot2.metadata()[U("key3")]); + CHECK_UTF8_EQUAL(U("value2"), snapshot2.metadata()[U("key4")]); + } + + TEST_FIXTURE(page_blob_test_base, page_blob_upload_maximum_execution_time) + { + std::chrono::seconds duration(10); + + std::vector buffer; + buffer.resize(2 * 1024 * 1024); + + wa::storage::blob_request_options options; + options.set_maximum_execution_time(duration); + options.set_stream_write_size_in_bytes(buffer.size() / 2); + + m_context.set_response_received([duration] (web::http::http_request&, const web::http::http_response&, wa::storage::operation_context) + { + std::this_thread::sleep_for(duration); + }); + + CHECK_THROW(m_blob.upload_from_stream(concurrency::streams::bytestream::open_istream(std::move(buffer)), wa::storage::access_condition(), options, m_context), wa::storage::storage_exception); + CHECK_EQUAL(2, m_context.request_results().size()); + + m_context.set_response_received(std::function()); + } +} diff --git a/Microsoft.WindowsAzure.Storage/tests/cloud_queue_client_test.cpp b/Microsoft.WindowsAzure.Storage/tests/cloud_queue_client_test.cpp new file mode 100644 index 00000000..074e382f --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/cloud_queue_client_test.cpp @@ -0,0 +1,232 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "test_helper.h" +#include "was/queue.h" + +SUITE(QueueClient) +{ + TEST(QueueClient_Empty) + { + wa::storage::cloud_queue_client client; + + CHECK(client.base_uri().primary_uri().is_empty()); + CHECK(client.base_uri().secondary_uri().is_empty()); + CHECK(client.credentials().is_anonymous()); + } + + TEST(QueueClient_BaseUri) + { + wa::storage::storage_uri base_uri(web::http::uri(U("https://myaccount.queue.core.windows.net")), web::http::uri(U("https://myaccount-secondary.queue.core.windows.net"))); + + wa::storage::cloud_queue_client client(base_uri); + + CHECK(client.base_uri().primary_uri() == base_uri.primary_uri()); + CHECK(client.base_uri().secondary_uri() == base_uri.secondary_uri()); + CHECK(client.credentials().is_anonymous()); + } + + TEST(QueueClient_BaseUriAndCredentials) + { + wa::storage::storage_uri base_uri(web::http::uri(U("https://myaccount.queue.core.windows.net")), web::http::uri(U("https://myaccount-secondary.queue.core.windows.net"))); + wa::storage::storage_credentials credentials(U("devstoreaccount1"), U("Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==")); + wa::storage::queue_request_options default_request_options; + + wa::storage::cloud_queue_client client(base_uri, credentials); + + CHECK(client.base_uri().primary_uri() == base_uri.primary_uri()); + CHECK(client.base_uri().secondary_uri() == base_uri.secondary_uri()); + CHECK(client.credentials().is_shared_key()); + } + + TEST(QueueClient_BaseUriAndCredentialsAndDefaultRequestOptions) + { + wa::storage::storage_uri base_uri(web::http::uri(U("https://myaccount.queue.core.windows.net")), web::http::uri(U("https://myaccount-secondary.queue.core.windows.net"))); + wa::storage::storage_credentials credentials(U("devstoreaccount1"), U("Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==")); + wa::storage::queue_request_options default_request_options; + + wa::storage::cloud_queue_client client(base_uri, credentials, default_request_options); + + CHECK(client.base_uri().primary_uri() == base_uri.primary_uri()); + CHECK(client.base_uri().secondary_uri() == base_uri.secondary_uri()); + CHECK(client.credentials().is_shared_key()); + + default_request_options.set_location_mode(wa::storage::location_mode::secondary_only); + + client = wa::storage::cloud_queue_client(base_uri, credentials, default_request_options); + + CHECK(client.default_request_options().location_mode() == wa::storage::location_mode::secondary_only); + } + + TEST(ListQueues_Normal) + { + const int QUEUE_COUNT = 5; + + wa::storage::cloud_queue queues[QUEUE_COUNT]; + bool is_found[QUEUE_COUNT]; + for (int i = 0; i < QUEUE_COUNT; ++i) + { + wa::storage::cloud_queue queue = get_queue(); + + queue.metadata().insert(std::pair(U("aaa"), U("111"))); + queue.metadata().insert(std::pair(U("bbb"), U("222"))); + queue.upload_metadata(); + + queues[i] = queue; + is_found[i] = false; + } + + wa::storage::cloud_queue_client client = get_queue_client(); + + utility::string_t prefix; + bool get_metadata; + wa::storage::queue_request_options options; + wa::storage::operation_context context; + + { + prefix = object_name_prefix; + get_metadata = false; + + std::vector results = client.list_queues(prefix, get_metadata, options, context); + + CHECK(results.size() >= QUEUE_COUNT); + + for (std::vector::const_iterator itr = results.cbegin(); itr != results.cend(); ++itr) + { + wa::storage::cloud_queue queue = *itr; + + for (int i = 0; i < QUEUE_COUNT; ++i) + { + if (queue.name().compare(queues[i].name()) == 0) + { + CHECK(!queue.service_client().base_uri().primary_uri().is_empty()); + CHECK(queue.service_client().credentials().is_shared_key()); + CHECK(!queue.name().empty()); + CHECK(!queue.uri().primary_uri().is_empty()); + + CHECK(queue.metadata().empty()); + + is_found[i] = true; + } + } + } + } + + { + prefix = object_name_prefix; + get_metadata = true; + + std::vector results = client.list_queues(prefix, get_metadata, options, context); + + CHECK(results.size() >= QUEUE_COUNT); + + for (std::vector::const_iterator itr = results.cbegin(); itr != results.cend(); ++itr) + { + wa::storage::cloud_queue queue = *itr; + + for (int i = 0; i < QUEUE_COUNT; ++i) + { + if (queue.name().compare(queues[i].name()) == 0) + { + CHECK(!queue.service_client().base_uri().primary_uri().is_empty()); + CHECK(queue.service_client().credentials().is_shared_key()); + CHECK(!queue.name().empty()); + CHECK(!queue.uri().primary_uri().is_empty()); + + CHECK_EQUAL(2U, queue.metadata().size()); + CHECK(queue.metadata()[U("aaa")].compare(U("111")) == 0); + CHECK(queue.metadata()[U("bbb")].compare(U("222")) == 0); + + is_found[i] = true; + } + } + } + } + + for (int i = 0; i < QUEUE_COUNT; ++i) + { + queues[i].delete_queue(); + } + } + + TEST(ListQueues_Segmented) + { + const int QUEUE_COUNT = 5; + + wa::storage::cloud_queue queues[QUEUE_COUNT]; + bool is_found[QUEUE_COUNT]; + for (int i = 0; i < QUEUE_COUNT; ++i) + { + queues[i] = get_queue(); + is_found[i] = false; + } + + wa::storage::cloud_queue_client client = get_queue_client(); + + utility::string_t prefix; + bool get_metadata; + int max_results; + wa::storage::continuation_token continuation_token; + wa::storage::queue_request_options options; + wa::storage::operation_context context; + + prefix = object_name_prefix; + get_metadata = false; + max_results = 3; + + int segment_count = 0; + wa::storage::queue_result_segment result_segment; + do + { + result_segment = client.list_queues_segmented(prefix, get_metadata, max_results, continuation_token, options, context); + std::vector results = result_segment.results(); + + CHECK(results.size() >= 0); + CHECK((int)results.size() <= max_results); + + for (std::vector::const_iterator itr = results.cbegin(); itr != results.cend(); ++itr) + { + wa::storage::cloud_queue queue = *itr; + + for (int i = 0; i < QUEUE_COUNT; ++i) + { + if (queue.name().compare(queues[i].name()) == 0) + { + CHECK(!queue.service_client().base_uri().primary_uri().is_empty()); + CHECK(queue.service_client().credentials().is_shared_key()); + CHECK(!queue.name().empty()); + CHECK(!queue.uri().primary_uri().is_empty()); + + is_found[i] = true; + } + } + } + + ++segment_count; + continuation_token = result_segment.continuation_token(); + } + while (!continuation_token.empty()); + + CHECK(segment_count > 1); + + for (int i = 0; i < QUEUE_COUNT; ++i) + { + queues[i].delete_queue(); + } + } +} diff --git a/Microsoft.WindowsAzure.Storage/tests/cloud_queue_test.cpp b/Microsoft.WindowsAzure.Storage/tests/cloud_queue_test.cpp new file mode 100644 index 00000000..05f8ce5d --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/cloud_queue_test.cpp @@ -0,0 +1,721 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "test_helper.h" +#include "was/queue.h" + +SUITE(Queue) +{ + TEST(Queue_Empty) + { + wa::storage::cloud_queue queue; + + CHECK(queue.service_client().base_uri().primary_uri().is_empty()); + CHECK(queue.service_client().base_uri().secondary_uri().is_empty()); + CHECK(queue.service_client().credentials().is_anonymous()); + CHECK(queue.name().empty()); + CHECK(queue.uri().primary_uri().is_empty()); + CHECK(queue.uri().secondary_uri().is_empty()); + CHECK_EQUAL(-1, queue.approximate_message_count()); + } + + TEST(Queue_Uri) + { + wa::storage::storage_uri uri(web::http::uri(U("https://myaccount.queue.core.windows.net/myqueue")), web::http::uri(U("https://myaccount-secondary.queue.core.windows.net/myqueue"))); + + wa::storage::cloud_queue queue(uri); + + web::http::uri expected_primary_uri(U("https://myaccount.queue.core.windows.net")); + web::http::uri expected_secondary_uri(U("https://myaccount-secondary.queue.core.windows.net")); + + CHECK(queue.service_client().base_uri().primary_uri() == expected_primary_uri); + CHECK(queue.service_client().base_uri().secondary_uri() == expected_secondary_uri); + CHECK(queue.service_client().credentials().is_anonymous()); + CHECK(queue.name().compare(U("myqueue")) == 0); + CHECK(queue.uri().primary_uri() == uri.primary_uri()); + CHECK(queue.uri().secondary_uri() == uri.secondary_uri()); + CHECK_EQUAL(-1, queue.approximate_message_count()); + } + + TEST(Queue_UriAndCredentials) + { + wa::storage::storage_uri uri(web::http::uri(U("https://myaccount.queue.core.windows.net/myqueue")), web::http::uri(U("https://myaccount-secondary.queue.core.windows.net/myqueue"))); + wa::storage::storage_credentials credentials(U("devstoreaccount1"), U("Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==")); + + wa::storage::cloud_queue queue(uri, credentials); + + web::http::uri expected_primary_uri(U("https://myaccount.queue.core.windows.net")); + web::http::uri expected_secondary_uri(U("https://myaccount-secondary.queue.core.windows.net")); + + CHECK(queue.service_client().base_uri().primary_uri() == expected_primary_uri); + CHECK(queue.service_client().base_uri().secondary_uri() == expected_secondary_uri); + CHECK(queue.service_client().credentials().is_shared_key()); + CHECK(queue.name().compare(U("myqueue")) == 0); + CHECK(queue.uri().primary_uri() == uri.primary_uri()); + CHECK(queue.uri().secondary_uri() == uri.secondary_uri()); + CHECK_EQUAL(-1, queue.approximate_message_count()); + } + + + TEST(Message_Empty) + { + wa::storage::cloud_queue_message message; + + CHECK(message.content_as_string().empty()); + CHECK(message.content_as_binary().empty()); + CHECK(message.id().empty()); + CHECK(message.pop_receipt().empty()); + CHECK(!message.expiration_time().is_initialized()); + CHECK(!message.insertion_time().is_initialized()); + CHECK(!message.next_visibile_time().is_initialized()); + CHECK_EQUAL(0, message.dequeue_count()); + } + + TEST(Message_String) + { + utility::string_t content = get_random_string(); + + wa::storage::cloud_queue_message message(content); + + CHECK(content.compare(message.content_as_string()) == 0); + CHECK_THROW(message.content_as_binary(), std::runtime_error); + CHECK(message.id().empty()); + CHECK(message.pop_receipt().empty()); + CHECK(!message.expiration_time().is_initialized()); + CHECK(!message.insertion_time().is_initialized()); + CHECK(!message.next_visibile_time().is_initialized()); + CHECK_EQUAL(0, message.dequeue_count()); + + content = get_random_string(); + message.set_content(content); + + CHECK(content.compare(message.content_as_string()) == 0); + CHECK_THROW(message.content_as_binary(), std::runtime_error); + CHECK(message.id().empty()); + CHECK(message.pop_receipt().empty()); + CHECK(!message.expiration_time().is_initialized()); + CHECK(!message.insertion_time().is_initialized()); + CHECK(!message.next_visibile_time().is_initialized()); + CHECK_EQUAL(0, message.dequeue_count()); + } + + TEST(Message_Binary) + { + std::vector content = get_random_binary_data(); + + wa::storage::cloud_queue_message message(content); + + CHECK(message.content_as_string().size() >= content.size()); + CHECK_ARRAY_EQUAL(content, message.content_as_binary(), content.size()); + CHECK(message.id().empty()); + CHECK(message.pop_receipt().empty()); + CHECK(!message.expiration_time().is_initialized()); + CHECK(!message.insertion_time().is_initialized()); + CHECK(!message.next_visibile_time().is_initialized()); + CHECK_EQUAL(0, message.dequeue_count()); + + content = get_random_binary_data(); + message.set_content(content); + + CHECK(message.content_as_string().size() >= content.size()); + CHECK_ARRAY_EQUAL(content, message.content_as_binary(), content.size()); + CHECK(message.id().empty()); + CHECK(message.pop_receipt().empty()); + CHECK(!message.expiration_time().is_initialized()); + CHECK(!message.insertion_time().is_initialized()); + CHECK(!message.next_visibile_time().is_initialized()); + CHECK_EQUAL(0, message.dequeue_count()); + } + + TEST(Message_IdAndPopReceipt) + { + utility::string_t id = get_random_string(); + utility::string_t pop_receipt = get_random_string(); + + wa::storage::cloud_queue_message message(id, pop_receipt); + + CHECK(message.content_as_string().empty()); + CHECK(message.content_as_binary().empty()); + CHECK(id.compare(message.id()) == 0); + CHECK(pop_receipt.compare(message.pop_receipt()) == 0); + CHECK(!message.expiration_time().is_initialized()); + CHECK(!message.insertion_time().is_initialized()); + CHECK(!message.next_visibile_time().is_initialized()); + CHECK_EQUAL(0, message.dequeue_count()); + } + + TEST(QueueRequestOptions_Normal) + { + wa::storage::queue_request_options options; + + CHECK(options.location_mode() == wa::storage::location_mode::primary_only); + } + + TEST(Queue_CreateAndDelete) + { + utility::string_t queue_name = get_queue_name(); + wa::storage::cloud_queue_client client = get_queue_client(); + + wa::storage::cloud_queue queue = client.get_queue_reference(queue_name); + + { + wa::storage::queue_request_options options; + wa::storage::operation_context context; + + queue.create(options, context); + } + + { + wa::storage::queue_request_options options; + wa::storage::operation_context context; + + bool exists = queue.exists(options, context); + + CHECK(exists); + } + + { + wa::storage::queue_request_options options; + wa::storage::operation_context context; + + CHECK_THROW(queue.create(options, context), wa::storage::storage_exception); + } + + { + wa::storage::queue_request_options options; + wa::storage::operation_context context; + + queue.delete_queue(options, context); + } + + { + wa::storage::queue_request_options options; + wa::storage::operation_context context; + + bool exists = queue.exists(options, context); + + CHECK(!exists); + } + + std::this_thread::sleep_for(std::chrono::seconds(3LL)); + + { + wa::storage::queue_request_options options; + wa::storage::operation_context context; + + //CHECK_THROW(queue.delete_queue(options, context), wa::storage::storage_exception); + } + } + + TEST(Queue_CreateIfNotExistsAndDeleteIfExists) + { + utility::string_t queue_name = get_queue_name(); + wa::storage::cloud_queue_client client = get_queue_client(); + + wa::storage::cloud_queue queue = client.get_queue_reference(queue_name); + + { + wa::storage::queue_request_options options; + wa::storage::operation_context context; + + bool created = queue.create_if_not_exists(options, context); + + CHECK(created); + } + + { + wa::storage::queue_request_options options; + wa::storage::operation_context context; + + bool exists = queue.exists(options, context); + + CHECK(exists); + } + + { + wa::storage::queue_request_options options; + wa::storage::operation_context context; + + bool created = queue.create_if_not_exists(options, context); + + CHECK(!created); + } + + { + wa::storage::queue_request_options options; + wa::storage::operation_context context; + + bool deleted = queue.delete_queue_if_exists(options, context); + + CHECK(deleted); + } + + { + wa::storage::queue_request_options options; + wa::storage::operation_context context; + + bool exists = queue.exists(options, context); + + CHECK(!exists); + } + + { + wa::storage::queue_request_options options; + wa::storage::operation_context context; + + bool deleted = queue.delete_queue_if_exists(options, context); + + CHECK(!deleted); + } + } + + TEST(Queue_ApproximateMessageCount) + { + const int MESSAGE_COUNT = 3; + + wa::storage::cloud_queue queue = get_queue(); + + wa::storage::queue_request_options options; + wa::storage::operation_context context; + + CHECK_EQUAL(-1, queue.approximate_message_count()); + + queue.download_attributes(options, context); + + CHECK_EQUAL(0, queue.approximate_message_count()); + + for (int i = 0; i < MESSAGE_COUNT; ++i) + { + wa::storage::cloud_queue_message message(U("Hello World!")); + queue.add_message(message); + } + + CHECK_EQUAL(0, queue.approximate_message_count()); + + queue.download_attributes(options, context); + + CHECK_EQUAL(MESSAGE_COUNT, queue.approximate_message_count()); + + queue.clear(); + + CHECK_EQUAL(MESSAGE_COUNT, queue.approximate_message_count()); + + queue.download_attributes(options, context); + + CHECK_EQUAL(0, queue.approximate_message_count()); + + queue.delete_queue(); + } + + TEST(Queue_Metadata) + { + wa::storage::cloud_queue_client client = get_queue_client(); + utility::string_t queue_name = get_queue_name(); + wa::storage::cloud_queue queue1 = client.get_queue_reference(queue_name); + wa::storage::cloud_queue queue2 = client.get_queue_reference(queue_name); + queue1.create_if_not_exists(); + + wa::storage::queue_request_options options; + wa::storage::operation_context context; + + CHECK(queue1.metadata().empty()); + + queue1.download_attributes(options, context); + + CHECK(queue1.metadata().empty()); + + queue1.metadata().insert(std::pair(U("aaa"), U("111"))); + queue1.metadata().insert(std::pair(U("bbb"), U("222"))); + queue1.metadata().insert(std::pair(U("ccc"), U("333"))); + + queue1.upload_metadata(options, context); + + CHECK_EQUAL(3, queue1.metadata().size()); + CHECK(queue1.metadata()[U("aaa")].compare(U("111")) == 0); + CHECK(queue1.metadata()[U("bbb")].compare(U("222")) == 0); + CHECK(queue1.metadata()[U("ccc")].compare(U("333")) == 0); + + queue1.download_attributes(options, context); + + CHECK_EQUAL(3, queue1.metadata().size()); + CHECK(queue1.metadata()[U("aaa")].compare(U("111")) == 0); + CHECK(queue1.metadata()[U("bbb")].compare(U("222")) == 0); + CHECK(queue1.metadata()[U("ccc")].compare(U("333")) == 0); + + queue2.download_attributes(options, context); + + CHECK_EQUAL(3, queue2.metadata().size()); + CHECK(queue2.metadata()[U("aaa")].compare(U("111")) == 0); + CHECK(queue2.metadata()[U("bbb")].compare(U("222")) == 0); + CHECK(queue2.metadata()[U("ccc")].compare(U("333")) == 0); + + queue2.metadata().erase(U("bbb")); + + CHECK_EQUAL(2, queue2.metadata().size()); + CHECK(queue2.metadata()[U("aaa")].compare(U("111")) == 0); + CHECK(queue2.metadata()[U("ccc")].compare(U("333")) == 0); + + queue2.upload_metadata(options, context); + + CHECK_EQUAL(2, queue2.metadata().size()); + CHECK(queue2.metadata()[U("aaa")].compare(U("111")) == 0); + CHECK(queue2.metadata()[U("ccc")].compare(U("333")) == 0); + + queue1.download_attributes(options, context); + + CHECK_EQUAL(2, queue1.metadata().size()); + CHECK(queue1.metadata()[U("aaa")].compare(U("111")) == 0); + CHECK(queue1.metadata()[U("ccc")].compare(U("333")) == 0); + + queue1.delete_queue(); + } + + TEST(Queue_Messages) + { + wa::storage::cloud_queue queue = get_queue(); + + utility::string_t content1 = get_random_string(); + utility::string_t content2 = get_random_string(); + utility::string_t content3 = get_random_string(); + utility::string_t new_content; + + wa::storage::cloud_queue_message message1; + wa::storage::cloud_queue_message message2; + wa::storage::cloud_queue_message message3; + + wa::storage::queue_request_options options; + wa::storage::operation_context context; + + { + std::chrono::seconds time_to_live; + std::chrono::seconds initial_visibility_timeout; + + message1.set_content(content1); + time_to_live = std::chrono::seconds(15 * 60); + initial_visibility_timeout = std::chrono::seconds(0); + + queue.add_message(message1, time_to_live, initial_visibility_timeout, options, context); + + message2.set_content(content2); + time_to_live = std::chrono::seconds(15 * 60); + initial_visibility_timeout = std::chrono::seconds(0); + + queue.add_message(message2, time_to_live, initial_visibility_timeout, options, context); + + message3.set_content(content3); + time_to_live = std::chrono::seconds(15 * 60); + initial_visibility_timeout = std::chrono::seconds(0); + + queue.add_message(message3, time_to_live, initial_visibility_timeout, options, context); + } + + { + size_t message_count; + std::chrono::seconds visibility_timeout; + + message_count = 2U; + visibility_timeout = std::chrono::seconds(1); + + std::vector messages = queue.get_messages(message_count, visibility_timeout, options, context); + + CHECK_EQUAL(2U, messages.size()); + + message1 = messages[0]; + message2 = messages[1]; + + for (std::vector::const_iterator itr = messages.cbegin(); itr != messages.cend(); ++itr) + { + wa::storage::cloud_queue_message message = *itr; + + CHECK(message.content_as_string().compare(content1) == 0 || message.content_as_string().compare(content2) == 0 || message.content_as_string().compare(content3) == 0); + CHECK(!message.id().empty()); + CHECK(!message.pop_receipt().empty()); + CHECK(message.insertion_time().is_initialized()); + CHECK(message.expiration_time().is_initialized()); + CHECK(message.next_visibile_time().is_initialized()); + } + } + + std::this_thread::sleep_for(std::chrono::seconds(3LL)); + + { + utility::string_t old_pop_recepit = message1.pop_receipt(); + utility::datetime old_next_visible_time = message1.next_visibile_time(); + + std::chrono::seconds visibility_timeout; + bool update_content; + + visibility_timeout = std::chrono::seconds(15 * 60); + update_content = false; + + queue.update_message(message1, visibility_timeout, update_content, options, context); + + CHECK(message1.content_as_string().compare(content1) == 0 || message1.content_as_string().compare(content2) == 0 || message1.content_as_string().compare(content3) == 0); + CHECK(!message1.id().empty()); + CHECK(!message1.pop_receipt().empty()); + CHECK(message1.insertion_time().is_initialized()); + CHECK(message1.expiration_time().is_initialized()); + CHECK(message1.next_visibile_time().is_initialized()); + + CHECK(old_pop_recepit.compare(message1.pop_receipt()) != 0); + CHECK(old_next_visible_time != message1.next_visibile_time()); + } + + { + size_t message_count; + + message_count = 5U; + + std::vector messages = queue.peek_messages(message_count, options, context); + + CHECK_EQUAL(2U, messages.size()); + + for (std::vector::const_iterator itr = messages.cbegin(); itr != messages.cend(); ++itr) + { + wa::storage::cloud_queue_message message = *itr; + + CHECK(message.content_as_string().compare(content1) == 0 || message.content_as_string().compare(content2) == 0 || message.content_as_string().compare(content3) == 0); + CHECK(!message.id().empty()); + CHECK(message.pop_receipt().empty()); + CHECK(message.insertion_time().is_initialized()); + CHECK(message.expiration_time().is_initialized()); + CHECK(!message.next_visibile_time().is_initialized()); + } + } + + { + queue.delete_message(message2, options, context); + + CHECK(message2.content_as_string().compare(content1) == 0 || message2.content_as_string().compare(content2) == 0 || message2.content_as_string().compare(content3) == 0); + CHECK(!message2.id().empty()); + CHECK(!message2.pop_receipt().empty()); + CHECK(message2.insertion_time().is_initialized()); + CHECK(message2.expiration_time().is_initialized()); + CHECK(message2.next_visibile_time().is_initialized()); + } + + { + wa::storage::cloud_queue_message message = queue.peek_message(options, context); + + CHECK(message.content_as_string().compare(content1) == 0 || message.content_as_string().compare(content2) == 0 || message.content_as_string().compare(content3) == 0); + CHECK(!message.id().empty()); + CHECK(message.pop_receipt().empty()); + CHECK(message.insertion_time().is_initialized()); + CHECK(message.expiration_time().is_initialized()); + CHECK(!message.next_visibile_time().is_initialized()); + } + + { + std::chrono::seconds visibility_timeout; + + visibility_timeout = std::chrono::seconds(15 * 60); + + message3 = queue.get_message(visibility_timeout, options, context); + + CHECK(message3.content_as_string().compare(content1) == 0 || message3.content_as_string().compare(content2) == 0 || message3.content_as_string().compare(content3) == 0); + CHECK(!message3.id().empty()); + CHECK(!message3.pop_receipt().empty()); + CHECK(message3.insertion_time().is_initialized()); + CHECK(message3.expiration_time().is_initialized()); + CHECK(message3.next_visibile_time().is_initialized()); + } + + { + wa::storage::cloud_queue_message message = queue.peek_message(options, context); + + CHECK(message.content_as_string().empty()); + CHECK(message.id().empty()); + CHECK(message.pop_receipt().empty()); + CHECK(!message.insertion_time().is_initialized()); + CHECK(!message.expiration_time().is_initialized()); + CHECK(!message.next_visibile_time().is_initialized()); + } + + { + std::chrono::seconds visibility_timeout; + + visibility_timeout = std::chrono::seconds(1); + + wa::storage::cloud_queue_message message = queue.get_message(visibility_timeout, options, context); + + CHECK(message.content_as_string().empty()); + CHECK(message.id().empty()); + CHECK(message.pop_receipt().empty()); + CHECK(!message.insertion_time().is_initialized()); + CHECK(!message.expiration_time().is_initialized()); + CHECK(!message.next_visibile_time().is_initialized()); + } + + std::this_thread::sleep_for(std::chrono::seconds(3LL)); + + { + new_content = get_random_string(); + utility::string_t old_pop_recepit = message3.pop_receipt(); + utility::datetime old_next_visible_time = message3.next_visibile_time(); + + std::chrono::seconds visibility_timeout; + bool update_content; + + message3.set_content(new_content); + visibility_timeout = std::chrono::seconds(0); + update_content = true; + + queue.update_message(message3, visibility_timeout, update_content, options, context); + + CHECK(message3.content_as_string().compare(new_content) == 0); + CHECK(!message3.id().empty()); + CHECK(!message3.pop_receipt().empty()); + CHECK(message3.insertion_time().is_initialized()); + CHECK(message3.expiration_time().is_initialized()); + CHECK(message3.next_visibile_time().is_initialized()); + + CHECK(old_pop_recepit.compare(message3.pop_receipt()) != 0); + CHECK(old_next_visible_time != message3.next_visibile_time()); + } + + { + wa::storage::cloud_queue_message message = queue.peek_message(options, context); + + CHECK(message.content_as_string().compare(new_content) == 0); + CHECK(!message.id().empty()); + CHECK(message.pop_receipt().empty()); + CHECK(message.insertion_time().is_initialized()); + CHECK(message.expiration_time().is_initialized()); + CHECK(!message.next_visibile_time().is_initialized()); + } + + { + new_content = get_random_string(); + + std::chrono::seconds visibility_timeout; + bool update_content; + + message3.set_content(new_content); + visibility_timeout = std::chrono::seconds(0); + update_content = true; + + queue.update_message(message3, visibility_timeout, update_content, options, context); + + CHECK(message3.content_as_string().compare(new_content) == 0); + CHECK(!message3.id().empty()); + CHECK(!message3.pop_receipt().empty()); + CHECK(message3.insertion_time().is_initialized()); + CHECK(message3.expiration_time().is_initialized()); + CHECK(message3.next_visibile_time().is_initialized()); + } + + { + wa::storage::cloud_queue_message message = queue.peek_message(options, context); + + CHECK(message.content_as_string().compare(new_content) == 0); + CHECK(!message.id().empty()); + CHECK(message.pop_receipt().empty()); + CHECK(message.insertion_time().is_initialized()); + CHECK(message.expiration_time().is_initialized()); + CHECK(!message.next_visibile_time().is_initialized()); + } + + { + queue.clear(options, context); + } + + { + wa::storage::cloud_queue_message message = queue.peek_message(options, context); + + CHECK(message.content_as_string().empty()); + CHECK(message.id().empty()); + CHECK(message.pop_receipt().empty()); + CHECK(!message.insertion_time().is_initialized()); + CHECK(!message.expiration_time().is_initialized()); + CHECK(!message.next_visibile_time().is_initialized()); + } + + queue.delete_queue(); + } + + TEST(Queue_Permissions) + { + wa::storage::cloud_queue queue = get_queue(); + + utility::string_t policy_name1 = U("policy1"); + utility::string_t policy_name2 = U("policy2"); + + uint8_t permission1 = wa::storage::queue_shared_access_policy::permissions::read | wa::storage::queue_shared_access_policy::permissions::add; + uint8_t permission2 = wa::storage::queue_shared_access_policy::permissions::read | wa::storage::queue_shared_access_policy::permissions::update; + + wa::storage::queue_shared_access_policy policy1(utility::datetime::utc_now(), utility::datetime::utc_now(), permission1); + wa::storage::queue_shared_access_policy policy2(utility::datetime::utc_now(), utility::datetime::utc_now(), permission2); + + { + wa::storage::queue_request_options options; + wa::storage::operation_context context; + + wa::storage::queue_permissions permissions = queue.download_permissions(options, context); + + CHECK(permissions.policies().empty()); + } + + { + wa::storage::queue_permissions permissions; + wa::storage::queue_request_options options; + wa::storage::operation_context context; + + wa::storage::shared_access_policies policies; + policies.insert(std::pair(policy_name1, policy1)); + policies.insert(std::pair(policy_name2, policy2)); + + permissions.set_policies(policies); + queue.upload_permissions(permissions, options, context); + } + + { + wa::storage::queue_request_options options; + wa::storage::operation_context context; + + wa::storage::queue_permissions permissions = queue.download_permissions(options, context); + + CHECK_EQUAL(2U, permissions.policies().size()); + CHECK_EQUAL(permission1, permissions.policies()[policy_name1].permission()); + CHECK(permissions.policies()[policy_name1].start().is_initialized()); + CHECK(permissions.policies()[policy_name1].expiry().is_initialized()); + CHECK_EQUAL(permission2, permissions.policies()[policy_name2].permission()); + CHECK(permissions.policies()[policy_name2].start().is_initialized()); + CHECK(permissions.policies()[policy_name2].expiry().is_initialized()); + } + + { + wa::storage::queue_permissions permissions; + wa::storage::queue_request_options options; + wa::storage::operation_context context; + + wa::storage::shared_access_policies policies; + + permissions.set_policies(policies); + queue.upload_permissions(permissions, options, context); + } + + { + wa::storage::queue_request_options options; + wa::storage::operation_context context; + + wa::storage::queue_permissions permissions = queue.download_permissions(options, context); + + CHECK(permissions.policies().empty()); + } + } +} diff --git a/Microsoft.WindowsAzure.Storage/tests/cloud_storage_account_test.cpp b/Microsoft.WindowsAzure.Storage/tests/cloud_storage_account_test.cpp new file mode 100644 index 00000000..6942b2ac --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/cloud_storage_account_test.cpp @@ -0,0 +1,262 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#include "stdafx.h" + +#include "check_macros.h" +#include "was/storage_account.h" +#include "was/blob.h" +#include "was/queue.h" +#include "was/table.h" + +const utility::string_t test_uri(U("http://test/abc")); +const utility::string_t token(U("?sp=abcde&sig=1")); +const utility::string_t test_account_name(U("test")); +const utility::string_t test_account_key(U("Fby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==")); +const utility::string_t test_endpoint_suffix(U("fake.endpoint.suffix")); + +void check_credentials_equal(const wa::storage::storage_credentials& a, const wa::storage::storage_credentials& b) +{ + CHECK_EQUAL(a.is_anonymous(), b.is_anonymous()); + CHECK_EQUAL(a.is_sas(), b.is_sas()); + CHECK_EQUAL(a.is_shared_key(), b.is_shared_key()); + CHECK_UTF8_EQUAL(a.account_name(), b.account_name()); + CHECK_UTF8_EQUAL(utility::conversions::to_base64(a.account_key()), utility::conversions::to_base64(b.account_key())); +} + +void check_account_equal(wa::storage::cloud_storage_account a, wa::storage::cloud_storage_account b) +{ + CHECK_UTF8_EQUAL(a.blob_endpoint().primary_uri().to_string(), b.blob_endpoint().primary_uri().to_string()); + CHECK_UTF8_EQUAL(a.blob_endpoint().secondary_uri().to_string(), b.blob_endpoint().secondary_uri().to_string()); + CHECK_UTF8_EQUAL(a.queue_endpoint().primary_uri().to_string(), b.queue_endpoint().primary_uri().to_string()); + CHECK_UTF8_EQUAL(a.queue_endpoint().secondary_uri().to_string(), b.queue_endpoint().secondary_uri().to_string()); + CHECK_UTF8_EQUAL(a.table_endpoint().primary_uri().to_string(), b.table_endpoint().primary_uri().to_string()); + CHECK_UTF8_EQUAL(a.table_endpoint().secondary_uri().to_string(), b.table_endpoint().secondary_uri().to_string()); + CHECK_UTF8_EQUAL(a.to_string(), b.to_string(false)); + CHECK_UTF8_EQUAL(a.to_string(true), b.to_string(true)); + check_credentials_equal(a.credentials(), b.credentials()); +} + +SUITE(Core) +{ + TEST(storage_credentials_anonymous) + { + wa::storage::storage_credentials creds; + + CHECK_UTF8_EQUAL(utility::string_t(), creds.account_name()); + CHECK_EQUAL(true, creds.is_anonymous()); + CHECK_EQUAL(false, creds.is_sas()); + CHECK_EQUAL(false, creds.is_shared_key()); + + web::http::uri uri(test_uri); + CHECK_UTF8_EQUAL(test_uri, creds.transform_uri(uri).to_string()); + } + + TEST(storage_credentials_shared_key) + { + wa::storage::storage_credentials creds(test_account_name, test_account_key); + + CHECK_UTF8_EQUAL(test_account_name, creds.account_name()); + CHECK_EQUAL(false, creds.is_anonymous()); + CHECK_EQUAL(false, creds.is_sas()); + CHECK_EQUAL(true, creds.is_shared_key()); + + web::http::uri uri(test_uri); + CHECK_UTF8_EQUAL(test_uri, creds.transform_uri(uri).to_string()); + + CHECK_UTF8_EQUAL(test_account_key, utility::conversions::to_base64(creds.account_key())); + } + + TEST(storage_credentials_sas) + { + wa::storage::storage_credentials creds(token); + + CHECK_UTF8_EQUAL(utility::string_t(), creds.account_name()); + CHECK_EQUAL(false, creds.is_anonymous()); + CHECK_EQUAL(true, creds.is_sas()); + CHECK_EQUAL(false, creds.is_shared_key()); + + web::http::uri uri(test_uri); + CHECK_UTF8_EQUAL(test_uri + token, creds.transform_uri(uri).to_string()); + } + + TEST(storage_credentials_empty_key) + { + const utility::string_t defaults_connection_string(U("DefaultEndpointsProtocol=https;AccountName=") + test_account_name + U(";AccountKey=")); + + wa::storage::storage_credentials creds1(test_account_name, utility::string_t()); + CHECK_UTF8_EQUAL(test_account_name, creds1.account_name()); + CHECK_EQUAL(false, creds1.is_anonymous()); + CHECK_EQUAL(false, creds1.is_sas()); + CHECK_EQUAL(true, creds1.is_shared_key()); + CHECK_UTF8_EQUAL(utility::string_t(), utility::conversions::to_base64(creds1.account_key())); + + wa::storage::cloud_storage_account account1(creds1, true); + CHECK_UTF8_EQUAL(defaults_connection_string, account1.to_string(true)); + check_credentials_equal(creds1, account1.credentials()); + + auto account2 = wa::storage::cloud_storage_account::parse(defaults_connection_string); + CHECK_UTF8_EQUAL(defaults_connection_string, account2.to_string(true)); + check_credentials_equal(creds1, account2.credentials()); + } + + TEST(cloud_storage_account_devstore) + { + auto account = wa::storage::cloud_storage_account::development_storage_account(); + CHECK_UTF8_EQUAL(U("http://127.0.0.1:10000/devstoreaccount1"), account.blob_endpoint().primary_uri().to_string()); + CHECK_UTF8_EQUAL(U("http://127.0.0.1:10001/devstoreaccount1"), account.queue_endpoint().primary_uri().to_string()); + CHECK_UTF8_EQUAL(U("http://127.0.0.1:10002/devstoreaccount1"), account.table_endpoint().primary_uri().to_string()); + CHECK(account.blob_endpoint().secondary_uri().is_empty()); + CHECK(account.queue_endpoint().secondary_uri().is_empty()); + CHECK(account.table_endpoint().secondary_uri().is_empty()); + } + + TEST(cloud_storage_account_default_http) + { + wa::storage::storage_credentials creds(test_account_name, test_account_key); + wa::storage::cloud_storage_account account(creds, false); + + CHECK_UTF8_EQUAL(U("http://") + test_account_name + U(".blob.core.windows.net/"), account.blob_endpoint().primary_uri().to_string()); + CHECK_UTF8_EQUAL(U("http://") + test_account_name + U(".queue.core.windows.net/"), account.queue_endpoint().primary_uri().to_string()); + CHECK_UTF8_EQUAL(U("http://") + test_account_name + U(".table.core.windows.net/"), account.table_endpoint().primary_uri().to_string()); + CHECK_UTF8_EQUAL(U("http://") + test_account_name + U("-secondary.blob.core.windows.net/"), account.blob_endpoint().secondary_uri().to_string()); + CHECK_UTF8_EQUAL(U("http://") + test_account_name + U("-secondary.queue.core.windows.net/"), account.queue_endpoint().secondary_uri().to_string()); + CHECK_UTF8_EQUAL(U("http://") + test_account_name + U("-secondary.table.core.windows.net/"), account.table_endpoint().secondary_uri().to_string()); + + auto account2 = wa::storage::cloud_storage_account::parse(account.to_string(true)); + check_account_equal(account, account2); + } + + TEST(cloud_storage_account_default_https) + { + wa::storage::storage_credentials creds(test_account_name, test_account_key); + wa::storage::cloud_storage_account account(creds, true); + + CHECK_UTF8_EQUAL(U("https://") + test_account_name + U(".blob.core.windows.net/"), account.blob_endpoint().primary_uri().to_string()); + CHECK_UTF8_EQUAL(U("https://") + test_account_name + U(".queue.core.windows.net/"), account.queue_endpoint().primary_uri().to_string()); + CHECK_UTF8_EQUAL(U("https://") + test_account_name + U(".table.core.windows.net/"), account.table_endpoint().primary_uri().to_string()); + CHECK_UTF8_EQUAL(U("https://") + test_account_name + U("-secondary.blob.core.windows.net/"), account.blob_endpoint().secondary_uri().to_string()); + CHECK_UTF8_EQUAL(U("https://") + test_account_name + U("-secondary.queue.core.windows.net/"), account.queue_endpoint().secondary_uri().to_string()); + CHECK_UTF8_EQUAL(U("https://") + test_account_name + U("-secondary.table.core.windows.net/"), account.table_endpoint().secondary_uri().to_string()); + + auto account2 = wa::storage::cloud_storage_account::parse(account.to_string(true)); + check_account_equal(account, account2); + } + + TEST(cloud_storage_account_endpoint_suffix_http) + { + utility::ostringstream_t str; + str << U("DefaultEndpointsProtocol=http;AccountName=") << test_account_name << ";AccountKey=" << test_account_key << ";EndpointSuffix=" << test_endpoint_suffix; + auto account = wa::storage::cloud_storage_account::parse(str.str()); + + CHECK_UTF8_EQUAL(U("http://") + test_account_name + U(".blob.") + test_endpoint_suffix + U("/"), account.blob_endpoint().primary_uri().to_string()); + CHECK_UTF8_EQUAL(U("http://") + test_account_name + U(".queue.") + test_endpoint_suffix + U("/"), account.queue_endpoint().primary_uri().to_string()); + CHECK_UTF8_EQUAL(U("http://") + test_account_name + U(".table.") + test_endpoint_suffix + U("/"), account.table_endpoint().primary_uri().to_string()); + CHECK_UTF8_EQUAL(U("http://") + test_account_name + U("-secondary.blob.") + test_endpoint_suffix + U("/"), account.blob_endpoint().secondary_uri().to_string()); + CHECK_UTF8_EQUAL(U("http://") + test_account_name + U("-secondary.queue.") + test_endpoint_suffix + U("/"), account.queue_endpoint().secondary_uri().to_string()); + CHECK_UTF8_EQUAL(U("http://") + test_account_name + U("-secondary.table.") + test_endpoint_suffix + U("/"), account.table_endpoint().secondary_uri().to_string()); + } + + TEST(cloud_storage_account_endpoint_suffix_https) + { + utility::ostringstream_t str; + str << U("DefaultEndpointsProtocol=https;AccountName=") << test_account_name << ";AccountKey=" << test_account_key << ";EndpointSuffix=" << test_endpoint_suffix; + auto account = wa::storage::cloud_storage_account::parse(str.str()); + + CHECK_UTF8_EQUAL(U("https://") + test_account_name + U(".blob.") + test_endpoint_suffix + U("/"), account.blob_endpoint().primary_uri().to_string()); + CHECK_UTF8_EQUAL(U("https://") + test_account_name + U(".queue.") + test_endpoint_suffix + U("/"), account.queue_endpoint().primary_uri().to_string()); + CHECK_UTF8_EQUAL(U("https://") + test_account_name + U(".table.") + test_endpoint_suffix + U("/"), account.table_endpoint().primary_uri().to_string()); + CHECK_UTF8_EQUAL(U("https://") + test_account_name + U("-secondary.blob.") + test_endpoint_suffix + U("/"), account.blob_endpoint().secondary_uri().to_string()); + CHECK_UTF8_EQUAL(U("https://") + test_account_name + U("-secondary.queue.") + test_endpoint_suffix + U("/"), account.queue_endpoint().secondary_uri().to_string()); + CHECK_UTF8_EQUAL(U("https://") + test_account_name + U("-secondary.table.") + test_endpoint_suffix + U("/"), account.table_endpoint().secondary_uri().to_string()); + } + + void check_string_roundtrip(const utility::string_t& connection_string) + { + auto account = wa::storage::cloud_storage_account::parse(connection_string); + CHECK_UTF8_EQUAL(connection_string, account.to_string(true)); + check_account_equal(account, wa::storage::cloud_storage_account::parse(account.to_string(true))); + } + + TEST(cloud_storage_account_string_roundtrip) + { + check_string_roundtrip(U("UseDevelopmentStorage=true")); + check_string_roundtrip(U("DevelopmentStorageProxyUri=http://ipv4.fiddler/;UseDevelopmentStorage=true")); + check_string_roundtrip(U("DefaultEndpointsProtocol=http;AccountName=test;AccountKey=abc=")); + check_string_roundtrip(U("DefaultEndpointsProtocol=http;EndpointSuffix=test.suffix;AccountName=test;AccountKey=abc=")); + check_string_roundtrip(U("DefaultEndpointsProtocol=http;EndpointSuffix=test.suffix;QueueEndpoint=https://alternate.queue.endpoint/;AccountName=test;AccountKey=abc=")); + check_string_roundtrip(U("DefaultEndpointsProtocol=http;QueueEndpoint=https://alternate.queue.endpoint/;AccountName=test;AccountKey=abc=")); + check_string_roundtrip(U("BlobEndpoint=https://alternate.blob.endpoint/;AccountName=test;AccountKey=abc=")); + check_string_roundtrip(U("QueueEndpoint=https://alternate.queue.endpoint/;AccountName=test;AccountKey=abc=")); + check_string_roundtrip(U("TableEndpoint=https://alternate.table.endpoint/;AccountName=test;AccountKey=abc=")); + check_string_roundtrip(U("BlobEndpoint=https://alternate.blob.endpoint/")); + check_string_roundtrip(U("QueueEndpoint=https://alternate.queue.endpoint/")); + check_string_roundtrip(U("TableEndpoint=https://alternate.table.endpoint/")); + check_string_roundtrip(U("BlobEndpoint=https://alternate.blob.endpoint/;SharedAccessSignature=?abc=def")); + check_string_roundtrip(U("QueueEndpoint=https://alternate.queue.endpoint/;SharedAccessSignature=?abc=def")); + check_string_roundtrip(U("TableEndpoint=https://alternate.table.endpoint/;SharedAccessSignature=?abc=def")); + } + + TEST(cloud_storage_account_string_empty_values) + { + auto account = wa::storage::cloud_storage_account::parse(U(";BlobEndpoint=http://blobs/;;AccountName=test;;AccountKey=abc=;")); + CHECK_UTF8_EQUAL(U("BlobEndpoint=http://blobs/;AccountName=test;AccountKey=abc="), account.to_string(true)); + } + + TEST(cloud_storage_account_clients) + { + wa::storage::storage_credentials creds(test_account_name, test_account_key); + wa::storage::cloud_storage_account account(creds, false); + + auto blob_client = account.create_cloud_blob_client(); + CHECK_UTF8_EQUAL(account.blob_endpoint().primary_uri().to_string(), blob_client.base_uri().primary_uri().to_string()); + CHECK_UTF8_EQUAL(account.blob_endpoint().secondary_uri().to_string(), blob_client.base_uri().secondary_uri().to_string()); + + auto queue_client = account.create_cloud_queue_client(); + CHECK_UTF8_EQUAL(account.queue_endpoint().primary_uri().to_string(), queue_client.base_uri().primary_uri().to_string()); + CHECK_UTF8_EQUAL(account.queue_endpoint().secondary_uri().to_string(), queue_client.base_uri().secondary_uri().to_string()); + + auto table_client = account.create_cloud_table_client(); + CHECK_UTF8_EQUAL(account.table_endpoint().primary_uri().to_string(), table_client.base_uri().primary_uri().to_string()); + CHECK_UTF8_EQUAL(account.table_endpoint().secondary_uri().to_string(), table_client.base_uri().secondary_uri().to_string()); + } + + TEST(cloud_storage_account_incorrect_devstore) + { + CHECK_THROW(wa::storage::cloud_storage_account::parse(U("UseDevelopmentStorage=false")), std::invalid_argument); + CHECK_THROW(wa::storage::cloud_storage_account::parse(U("UseDevelopmentStorage=true;AccountName=devstoreaccount1")), std::invalid_argument); + CHECK_THROW(wa::storage::cloud_storage_account::parse(U("UseDevelopmentStorage=true;BlobEndpoint=http://127.0.0.1:1000/devstoreaccount1")), std::invalid_argument); + } + + TEST(cloud_storage_account_blob_endpoint) + { + auto account = wa::storage::cloud_storage_account::parse(U("DefaultEndpointsProtocol=http;BlobEndpoint=http://customdomain.com/;AccountName=asdf;AccountKey=abc=")); + CHECK_UTF8_EQUAL(U("http://customdomain.com/"), account.blob_endpoint().primary_uri().to_string()); + CHECK(account.blob_endpoint().secondary_uri().is_empty()); + } + + TEST(cloud_storage_account_devstore_proxy) + { + auto account = wa::storage::cloud_storage_account::parse(U("UseDevelopmentStorage=true;DevelopmentStorageProxyUri=http://ipv4.fiddler")); + CHECK_UTF8_EQUAL(U("http://ipv4.fiddler:10000/devstoreaccount1"), account.blob_endpoint().primary_uri().to_string()); + CHECK_UTF8_EQUAL(U("http://ipv4.fiddler:10001/devstoreaccount1"), account.queue_endpoint().primary_uri().to_string()); + CHECK_UTF8_EQUAL(U("http://ipv4.fiddler:10002/devstoreaccount1"), account.table_endpoint().primary_uri().to_string()); + CHECK(account.blob_endpoint().secondary_uri().is_empty()); + CHECK(account.queue_endpoint().secondary_uri().is_empty()); + CHECK(account.table_endpoint().secondary_uri().is_empty()); + } +} diff --git a/Microsoft.WindowsAzure.Storage/tests/cloud_table_client_test.cpp b/Microsoft.WindowsAzure.Storage/tests/cloud_table_client_test.cpp new file mode 100644 index 00000000..a3640b91 --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/cloud_table_client_test.cpp @@ -0,0 +1,191 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "test_helper.h" +#include "was/table.h" + +SUITE(TableClient) +{ + TEST(TableClient_Empty) + { + wa::storage::cloud_table_client client; + + CHECK(client.base_uri().primary_uri().is_empty()); + CHECK(client.base_uri().secondary_uri().is_empty()); + CHECK(client.credentials().is_anonymous()); + CHECK(client.default_request_options().payload_format() == wa::storage::table_payload_format::json); + } + + TEST(TableClient_BaseUri) + { + wa::storage::storage_uri base_uri(web::http::uri(U("https://myaccount.table.core.windows.net")), web::http::uri(U("https://myaccount-secondary.table.core.windows.net"))); + + wa::storage::cloud_table_client client(base_uri); + + CHECK(client.base_uri().primary_uri() == base_uri.primary_uri()); + CHECK(client.base_uri().secondary_uri() == base_uri.secondary_uri()); + CHECK(client.credentials().is_anonymous()); + CHECK(client.default_request_options().payload_format() == wa::storage::table_payload_format::json); + } + + TEST(TableClient_BaseUriAndCredentials) + { + wa::storage::storage_uri base_uri(web::http::uri(U("https://myaccount.table.core.windows.net")), web::http::uri(U("https://myaccount-secondary.table.core.windows.net"))); + wa::storage::storage_credentials credentials(U("devstoreaccount1"), U("Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==")); + wa::storage::table_request_options default_request_options; + + wa::storage::cloud_table_client client(base_uri, credentials); + + CHECK(client.base_uri().primary_uri() == base_uri.primary_uri()); + CHECK(client.base_uri().secondary_uri() == base_uri.secondary_uri()); + CHECK(client.credentials().is_shared_key()); + CHECK(client.default_request_options().payload_format() == wa::storage::table_payload_format::json); + } + + TEST(TableClient_BaseUriAndCredentialsAndDefaultRequestOptions) + { + wa::storage::storage_uri base_uri(web::http::uri(U("https://myaccount.table.core.windows.net")), web::http::uri(U("https://myaccount-secondary.table.core.windows.net"))); + wa::storage::storage_credentials credentials(U("devstoreaccount1"), U("Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==")); + wa::storage::table_request_options default_request_options; + + wa::storage::cloud_table_client client(base_uri, credentials, default_request_options); + + CHECK(client.base_uri().primary_uri() == base_uri.primary_uri()); + CHECK(client.base_uri().secondary_uri() == base_uri.secondary_uri()); + CHECK(client.credentials().is_shared_key()); + CHECK(client.default_request_options().payload_format() == wa::storage::table_payload_format::json); + + default_request_options.set_payload_format(wa::storage::table_payload_format::json_no_metadata); + + client = wa::storage::cloud_table_client(base_uri, credentials, default_request_options); + + CHECK(client.default_request_options().payload_format() == wa::storage::table_payload_format::json_no_metadata); + } + + TEST(ListTables_Normal) + { + const int TABLE_COUNT = 5; + + wa::storage::cloud_table tables[TABLE_COUNT]; + bool is_found[TABLE_COUNT]; + for (int i = 0; i < TABLE_COUNT; ++i) + { + tables[i] = get_table(); + is_found[i] = false; + } + + wa::storage::cloud_table_client client = get_table_client(); + + utility::string_t prefix; + wa::storage::table_request_options options; + wa::storage::operation_context context; + + prefix = object_name_prefix; + + std::vector results = client.list_tables(prefix, options, context); + + CHECK(results.size() >= TABLE_COUNT); + + for (std::vector::const_iterator itr = results.cbegin(); itr != results.cend(); ++itr) + { + wa::storage::cloud_table table = *itr; + + for (int i = 0; i < TABLE_COUNT; ++i) + { + if (table.name().compare(tables[i].name()) == 0) + { + CHECK(!table.service_client().base_uri().primary_uri().is_empty()); + CHECK(table.service_client().credentials().is_shared_key()); + CHECK(!table.name().empty()); + CHECK(!table.uri().primary_uri().is_empty()); + + is_found[i] = true; + } + } + } + + for (int i = 0; i < TABLE_COUNT; ++i) + { + tables[i].delete_table(); + } + } + + TEST(ListTables_Segmented) + { + const int TABLE_COUNT = 5; + + wa::storage::cloud_table tables[TABLE_COUNT]; + bool is_found[TABLE_COUNT]; + for (int i = 0; i < TABLE_COUNT; ++i) + { + tables[i] = get_table(); + is_found[i] = false; + } + + wa::storage::cloud_table_client client = get_table_client(); + + utility::string_t prefix; + int max_results; + wa::storage::continuation_token continuation_token; + wa::storage::table_request_options options; + wa::storage::operation_context context; + + prefix = object_name_prefix; + max_results = 3; + + int segment_count = 0; + wa::storage::table_result_segment result_segment; + do + { + result_segment = client.list_tables_segmented(prefix, max_results, continuation_token, options, context); + std::vector results = result_segment.results(); + + CHECK(results.size() >= 0); + CHECK((int)results.size() <= max_results); + + for (std::vector::const_iterator itr = results.cbegin(); itr != results.cend(); ++itr) + { + wa::storage::cloud_table table = *itr; + + for (int i = 0; i < TABLE_COUNT; ++i) + { + if (table.name().compare(tables[i].name()) == 0) + { + CHECK(!table.service_client().base_uri().primary_uri().is_empty()); + CHECK(table.service_client().credentials().is_shared_key()); + CHECK(!table.name().empty()); + CHECK(!table.uri().primary_uri().is_empty()); + + is_found[i] = true; + } + } + } + + ++segment_count; + continuation_token = result_segment.continuation_token(); + } + while (!continuation_token.empty()); + + CHECK(segment_count > 1); + + for (int i = 0; i < TABLE_COUNT; ++i) + { + tables[i].delete_table(); + } + } +} diff --git a/Microsoft.WindowsAzure.Storage/tests/cloud_table_test.cpp b/Microsoft.WindowsAzure.Storage/tests/cloud_table_test.cpp new file mode 100644 index 00000000..4683e99d --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/cloud_table_test.cpp @@ -0,0 +1,2729 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "test_helper.h" +#include "was/table.h" + +SUITE(Table) +{ + /* + utility::string_t property_null(bool is_null) + { + if (is_null) + { + return utility::string_t(U("null, ")); + } + + return utility::string_t(); + } + + utility::string_t property_type_name(edm_type property_type) + { + switch (property_type) + { + case edm_type::binary: + return utility::string_t(U("binary")); + + case edm_type::boolean: + return utility::string_t(U("boolean")); + + case edm_type::datetime: + return utility::string_t(U("datetime")); + + case edm_type::double_floating_point: + return utility::string_t(U("double")); + + case edm_type::guid: + return utility::string_t(U("guid")); + + case edm_type::int32: + return utility::string_t(U("int32")); + + case edm_type::int64: + return utility::string_t(U("int64")); + + case edm_type::string: + return utility::string_t(U("string")); + + } + + return utility::string_t(U("Unknown")); + } + + utility::string_t random_string() + { + const int SIZE = 10; + utility::string_t result; + result.reserve(SIZE); + for (int i = 0; i < SIZE; ++i) + { + result.push_back(U('0') + rand() % SIZE); + } + return result; + } + + void print(const table_entity& entity) + { + utility::stringstream_t log; + log << U("PK: ") << entity.partition_key() << U(", ") << U("FK: ") << entity.row_key() << U(", ") << U("TS: ") << entity.timestamp().to_string(); + + unordered_map properties = entity.properties(); + for (unordered_map::const_iterator propertyIterator = properties.cbegin(); propertyIterator != properties.cend(); ++propertyIterator) + { + utility::string_t property_name = propertyIterator->first; + entity_property property = propertyIterator->second; + log << U(", ") << property_name << U(" (") << property_null(property.is_null()) << property_type_name(property.property_type()) << U("): ") << property.str(); + + if (property.property_type() == edm_type::double_floating_point) + { + double x = property.double_value(); + } + } + + log << endl; + ucout << log.str(); + } + + void print(const table_result& result) + { + utility::stringstream_t log; + log << U("Status: ") << result.http_status_code() << U("ETag: ") << result.etag() << endl; + ucout << log.str(); + + print(result.entity()); + } + */ + + /* + utility::uuid_t get_guid() const + { + UUID new_id; + UuidCreate(&new_id); + + utility::uuid_t result; + memcpy(result.data, &new_id, 16); + + return result; + } + */ + + TEST(Table_Empty) + { + wa::storage::cloud_table table; + + CHECK(table.service_client().base_uri().primary_uri().is_empty()); + CHECK(table.service_client().base_uri().secondary_uri().is_empty()); + CHECK(table.service_client().credentials().is_anonymous()); + CHECK(table.name().empty()); + CHECK(table.uri().primary_uri().is_empty()); + CHECK(table.uri().secondary_uri().is_empty()); + } + + TEST(Table_Uri) + { + wa::storage::storage_uri uri(web::http::uri(U("https://myaccount.table.core.windows.net/mytable")), web::http::uri(U("https://myaccount-secondary.table.core.windows.net/mytable"))); + + wa::storage::cloud_table table(uri); + + web::http::uri expected_primary_uri(U("https://myaccount.table.core.windows.net")); + web::http::uri expected_secondary_uri(U("https://myaccount-secondary.table.core.windows.net")); + + CHECK(table.service_client().base_uri().primary_uri() == expected_primary_uri); + CHECK(table.service_client().base_uri().secondary_uri() == expected_secondary_uri); + CHECK(table.service_client().credentials().is_anonymous()); + CHECK(table.name().compare(U("mytable")) == 0); + CHECK(table.uri().primary_uri() == uri.primary_uri()); + CHECK(table.uri().secondary_uri() == uri.secondary_uri()); + } + + TEST(Table_UriAndCredentials) + { + wa::storage::storage_uri uri(web::http::uri(U("https://myaccount.table.core.windows.net/mytable")), web::http::uri(U("https://myaccount-secondary.table.core.windows.net/mytable"))); + wa::storage::storage_credentials credentials(U("devstoreaccount1"), U("Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==")); + + wa::storage::cloud_table table(uri, credentials); + + web::http::uri expected_primary_uri(U("https://myaccount.table.core.windows.net")); + web::http::uri expected_secondary_uri(U("https://myaccount-secondary.table.core.windows.net")); + + CHECK(table.service_client().base_uri().primary_uri() == expected_primary_uri); + CHECK(table.service_client().base_uri().secondary_uri() == expected_secondary_uri); + CHECK(table.service_client().credentials().is_shared_key()); + CHECK(table.name().compare(U("mytable")) == 0); + CHECK(table.uri().primary_uri() == uri.primary_uri()); + CHECK(table.uri().secondary_uri() == uri.secondary_uri()); + } + + /* + TEST(Table_ClientAndUri) + { + wa::storage::storage_uri base_uri(web::http::uri(U("https://myaccount.table.core.windows.net")), web::http::uri(U("https://myaccount-secondary.table.core.windows.net"))); + wa::storage::storage_credentials credentials(U("devstoreaccount1"), U("Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==")); + wa::storage::cloud_table_client client(base_uri, credentials); + utility::string_t table_name(U("mytable")); + wa::storage::storage_uri uri(web::http::uri(U("https://myaccount.table.core.windows.net/mytable")), web::http::uri(U("https://myaccount-secondary.table.core.windows.net/mytable"))); + + wa::storage::cloud_table table(client, table_name, uri); + + CHECK(table.service_client().base_uri().primary_uri() == base_uri.primary_uri()); + CHECK(table.service_client().base_uri().secondary_uri() == base_uri.secondary_uri()); + CHECK(table.service_client().credentials().is_shared_key()); + CHECK(table.name().compare(U("mytable")) == 0); + CHECK(table.uri().primary_uri() == uri.primary_uri()); + CHECK(table.uri().secondary_uri() == uri.secondary_uri()); + } + */ + + TEST(EntityProperty_Binary) + { + std::vector value = get_random_binary_data(); + wa::storage::entity_property property(value); + + CHECK(property.property_type() == wa::storage::edm_type::binary); + CHECK(!property.is_null()); + CHECK_ARRAY_EQUAL(value, property.binary_value(), value.size()); + + CHECK_THROW(property.boolean_value(), std::runtime_error); + CHECK_THROW(property.datetime_value(), std::runtime_error); + CHECK_THROW(property.double_value(), std::runtime_error); + CHECK_THROW(property.guid_value(), std::runtime_error); + CHECK_THROW(property.int32_value(), std::runtime_error); + CHECK_THROW(property.int64_value(), std::runtime_error); + CHECK_THROW(property.string_value(), std::runtime_error); + + CHECK(property.str().size() > 0); + + value = get_random_binary_data(); + property.set_value(value); + + CHECK(property.property_type() == wa::storage::edm_type::binary); + CHECK(!property.is_null()); + CHECK_ARRAY_EQUAL(value, property.binary_value(), value.size()); + + CHECK_THROW(property.boolean_value(), std::runtime_error); + CHECK_THROW(property.datetime_value(), std::runtime_error); + CHECK_THROW(property.double_value(), std::runtime_error); + CHECK_THROW(property.guid_value(), std::runtime_error); + CHECK_THROW(property.int32_value(), std::runtime_error); + CHECK_THROW(property.int64_value(), std::runtime_error); + CHECK_THROW(property.string_value(), std::runtime_error); + + CHECK(property.str().size() > 0); + } + + TEST(EntityProperty_Boolean) + { + bool value = get_random_boolean(); + wa::storage::entity_property property(value); + + CHECK(property.property_type() == wa::storage::edm_type::boolean); + CHECK(!property.is_null()); + CHECK(property.boolean_value() == value); + + CHECK_THROW(property.binary_value(), std::runtime_error); + CHECK_THROW(property.datetime_value(), std::runtime_error); + CHECK_THROW(property.double_value(), std::runtime_error); + CHECK_THROW(property.guid_value(), std::runtime_error); + CHECK_THROW(property.int32_value(), std::runtime_error); + CHECK_THROW(property.int64_value(), std::runtime_error); + CHECK_THROW(property.string_value(), std::runtime_error); + + CHECK(property.str().size() > 0); + + value = get_random_boolean(); + property.set_value(value); + + CHECK(property.property_type() == wa::storage::edm_type::boolean); + CHECK(!property.is_null()); + CHECK(property.boolean_value() == value); + + CHECK_THROW(property.binary_value(), std::runtime_error); + CHECK_THROW(property.datetime_value(), std::runtime_error); + CHECK_THROW(property.double_value(), std::runtime_error); + CHECK_THROW(property.guid_value(), std::runtime_error); + CHECK_THROW(property.int32_value(), std::runtime_error); + CHECK_THROW(property.int64_value(), std::runtime_error); + CHECK_THROW(property.string_value(), std::runtime_error); + + CHECK(property.str().size() > 0); + } + + TEST(EntityProperty_DateTime) + { + utility::datetime value = get_random_datetime(); + wa::storage::entity_property property(value); + + CHECK(property.property_type() == wa::storage::edm_type::datetime); + CHECK(!property.is_null()); + CHECK_CLOSE(value.to_interval(), property.datetime_value().to_interval(), 10000000); + + CHECK_THROW(property.binary_value(), std::runtime_error); + CHECK_THROW(property.boolean_value(), std::runtime_error); + CHECK_THROW(property.double_value(), std::runtime_error); + CHECK_THROW(property.guid_value(), std::runtime_error); + CHECK_THROW(property.int32_value(), std::runtime_error); + CHECK_THROW(property.int64_value(), std::runtime_error); + CHECK_THROW(property.string_value(), std::runtime_error); + + CHECK(property.str().size() > 0); + + value = get_random_datetime(); + property.set_value(value); + + CHECK(property.property_type() == wa::storage::edm_type::datetime); + CHECK(!property.is_null()); + CHECK_CLOSE(value.to_interval(), property.datetime_value().to_interval(), 10000000); + + CHECK_THROW(property.binary_value(), std::runtime_error); + CHECK_THROW(property.boolean_value(), std::runtime_error); + CHECK_THROW(property.double_value(), std::runtime_error); + CHECK_THROW(property.guid_value(), std::runtime_error); + CHECK_THROW(property.int32_value(), std::runtime_error); + CHECK_THROW(property.int64_value(), std::runtime_error); + CHECK_THROW(property.string_value(), std::runtime_error); + + CHECK(property.str().size() > 0); + } + + TEST(EntityProperty_Double) + { + double value = get_random_double(); + wa::storage::entity_property property(value); + + CHECK(property.property_type() == wa::storage::edm_type::double_floating_point); + CHECK(!property.is_null()); + CHECK(property.double_value() == value); + + CHECK_THROW(property.binary_value(), std::runtime_error); + CHECK_THROW(property.boolean_value(), std::runtime_error); + CHECK_THROW(property.datetime_value(), std::runtime_error); + CHECK_THROW(property.guid_value(), std::runtime_error); + CHECK_THROW(property.int32_value(), std::runtime_error); + CHECK_THROW(property.int64_value(), std::runtime_error); + CHECK_THROW(property.string_value(), std::runtime_error); + + CHECK(property.str().size() > 0); + + value = get_random_double(); + property.set_value(value); + + CHECK(property.property_type() == wa::storage::edm_type::double_floating_point); + CHECK(!property.is_null()); + CHECK(property.double_value() == value); + + CHECK_THROW(property.binary_value(), std::runtime_error); + CHECK_THROW(property.boolean_value(), std::runtime_error); + CHECK_THROW(property.datetime_value(), std::runtime_error); + CHECK_THROW(property.guid_value(), std::runtime_error); + CHECK_THROW(property.int32_value(), std::runtime_error); + CHECK_THROW(property.int64_value(), std::runtime_error); + CHECK_THROW(property.string_value(), std::runtime_error); + + CHECK(property.str().size() > 0); + + value = std::numeric_limits::quiet_NaN(); + property.set_value(value); + + CHECK(property.property_type() == wa::storage::edm_type::double_floating_point); + CHECK(!property.is_null()); + CHECK(property.double_value() != property.double_value()); // Only NaN is defined to not equal itself + CHECK(property.str().size() > 0); + + value = std::numeric_limits::infinity(); + property.set_value(value); + + CHECK(property.property_type() == wa::storage::edm_type::double_floating_point); + CHECK(!property.is_null()); + CHECK(property.double_value() == value); + CHECK(property.str().size() > 0); + + value = -std::numeric_limits::infinity(); + property.set_value(value); + + CHECK(property.property_type() == wa::storage::edm_type::double_floating_point); + CHECK(!property.is_null()); + CHECK(property.double_value() == value); + CHECK(property.str().size() > 0); + } + + TEST(EntityProperty_Guid) + { + utility::uuid value = get_random_guid(); + wa::storage::entity_property property(value); + + CHECK(property.property_type() == wa::storage::edm_type::guid); + CHECK(!property.is_null()); + CHECK(property.guid_value() == value); + + CHECK_THROW(property.binary_value(), std::runtime_error); + CHECK_THROW(property.boolean_value(), std::runtime_error); + CHECK_THROW(property.datetime_value(), std::runtime_error); + CHECK_THROW(property.double_value(), std::runtime_error); + CHECK_THROW(property.int32_value(), std::runtime_error); + CHECK_THROW(property.int64_value(), std::runtime_error); + CHECK_THROW(property.string_value(), std::runtime_error); + + CHECK(property.str().size() > 0); + + value = get_random_guid(); + property.set_value(value); + + CHECK(property.property_type() == wa::storage::edm_type::guid); + CHECK(!property.is_null()); + CHECK(property.guid_value() == value); + + CHECK_THROW(property.binary_value(), std::runtime_error); + CHECK_THROW(property.boolean_value(), std::runtime_error); + CHECK_THROW(property.datetime_value(), std::runtime_error); + CHECK_THROW(property.double_value(), std::runtime_error); + CHECK_THROW(property.int32_value(), std::runtime_error); + CHECK_THROW(property.int64_value(), std::runtime_error); + CHECK_THROW(property.string_value(), std::runtime_error); + + CHECK(property.str().size() > 0); + } + + TEST(EntityProperty_Int32) + { + int32_t value = get_random_int32(); + wa::storage::entity_property property(value); + + CHECK(property.property_type() == wa::storage::edm_type::int32); + CHECK(!property.is_null()); + CHECK(property.int32_value() == value); + + CHECK_THROW(property.binary_value(), std::runtime_error); + CHECK_THROW(property.boolean_value(), std::runtime_error); + CHECK_THROW(property.datetime_value(), std::runtime_error); + CHECK_THROW(property.double_value(), std::runtime_error); + CHECK_THROW(property.guid_value(), std::runtime_error); + CHECK_THROW(property.int64_value(), std::runtime_error); + CHECK_THROW(property.string_value(), std::runtime_error); + + CHECK(property.str().size() > 0); + + value = get_random_int32(); + property.set_value(value); + + CHECK(property.property_type() == wa::storage::edm_type::int32); + CHECK(!property.is_null()); + CHECK(property.int32_value() == value); + + CHECK_THROW(property.binary_value(), std::runtime_error); + CHECK_THROW(property.boolean_value(), std::runtime_error); + CHECK_THROW(property.datetime_value(), std::runtime_error); + CHECK_THROW(property.double_value(), std::runtime_error); + CHECK_THROW(property.guid_value(), std::runtime_error); + CHECK_THROW(property.int64_value(), std::runtime_error); + CHECK_THROW(property.string_value(), std::runtime_error); + + CHECK(property.str().size() > 0); + } + + TEST(EntityProperty_Int64) + { + int64_t value = get_random_int64(); + wa::storage::entity_property property(value); + + CHECK(property.property_type() == wa::storage::edm_type::int64); + CHECK(!property.is_null()); + CHECK(property.int64_value() == value); + + CHECK_THROW(property.binary_value(), std::runtime_error); + CHECK_THROW(property.boolean_value(), std::runtime_error); + CHECK_THROW(property.datetime_value(), std::runtime_error); + CHECK_THROW(property.double_value(), std::runtime_error); + CHECK_THROW(property.guid_value(), std::runtime_error); + CHECK_THROW(property.int32_value(), std::runtime_error); + CHECK_THROW(property.string_value(), std::runtime_error); + + CHECK(property.str().size() > 0); + + value = get_random_int64(); + property.set_value(value); + + CHECK(property.property_type() == wa::storage::edm_type::int64); + CHECK(!property.is_null()); + CHECK(property.int64_value() == value); + + CHECK_THROW(property.binary_value(), std::runtime_error); + CHECK_THROW(property.boolean_value(), std::runtime_error); + CHECK_THROW(property.datetime_value(), std::runtime_error); + CHECK_THROW(property.double_value(), std::runtime_error); + CHECK_THROW(property.guid_value(), std::runtime_error); + CHECK_THROW(property.int32_value(), std::runtime_error); + CHECK_THROW(property.string_value(), std::runtime_error); + + CHECK(property.str().size() > 0); + } + + TEST(EntityProperty_String) + { + utility::string_t value = get_random_string(); + wa::storage::entity_property property(value); + + CHECK(property.property_type() == wa::storage::edm_type::string); + CHECK(!property.is_null()); + CHECK(property.string_value().compare(value) == 0); + + CHECK_THROW(property.binary_value(), std::runtime_error); + CHECK_THROW(property.boolean_value(), std::runtime_error); + CHECK_THROW(property.datetime_value(), std::runtime_error); + CHECK_THROW(property.double_value(), std::runtime_error); + CHECK_THROW(property.guid_value(), std::runtime_error); + CHECK_THROW(property.int32_value(), std::runtime_error); + CHECK_THROW(property.int64_value(), std::runtime_error); + + CHECK(property.str().size() > 0); + + value = get_random_string(); + property.set_value(value); + + CHECK(property.property_type() == wa::storage::edm_type::string); + CHECK(!property.is_null()); + CHECK(property.string_value().compare(value) == 0); + + CHECK_THROW(property.binary_value(), std::runtime_error); + CHECK_THROW(property.boolean_value(), std::runtime_error); + CHECK_THROW(property.datetime_value(), std::runtime_error); + CHECK_THROW(property.double_value(), std::runtime_error); + CHECK_THROW(property.guid_value(), std::runtime_error); + CHECK_THROW(property.int32_value(), std::runtime_error); + CHECK_THROW(property.int64_value(), std::runtime_error); + + CHECK(property.str().size() > 0); + } + + TEST(EntityProperty_Null) + { + wa::storage::entity_property property; + + CHECK(property.property_type() == wa::storage::edm_type::string); + CHECK(property.is_null()); + CHECK(property.string_value().empty()); + + CHECK_THROW(property.binary_value(), std::runtime_error); + CHECK_THROW(property.boolean_value(), std::runtime_error); + CHECK_THROW(property.datetime_value(), std::runtime_error); + CHECK_THROW(property.double_value(), std::runtime_error); + CHECK_THROW(property.guid_value(), std::runtime_error); + CHECK_THROW(property.int32_value(), std::runtime_error); + CHECK_THROW(property.int64_value(), std::runtime_error); + + property.set_is_null(false); + + CHECK(property.property_type() == wa::storage::edm_type::string); + CHECK(!property.is_null()); + CHECK(property.string_value().empty()); + + CHECK_THROW(property.binary_value(), std::runtime_error); + CHECK_THROW(property.boolean_value(), std::runtime_error); + CHECK_THROW(property.datetime_value(), std::runtime_error); + CHECK_THROW(property.double_value(), std::runtime_error); + CHECK_THROW(property.guid_value(), std::runtime_error); + CHECK_THROW(property.int32_value(), std::runtime_error); + CHECK_THROW(property.int64_value(), std::runtime_error); + + property.set_is_null(true); + + CHECK(property.property_type() == wa::storage::edm_type::string); + CHECK(property.is_null()); + CHECK(property.string_value().empty()); + + CHECK_THROW(property.binary_value(), std::runtime_error); + CHECK_THROW(property.boolean_value(), std::runtime_error); + CHECK_THROW(property.datetime_value(), std::runtime_error); + CHECK_THROW(property.double_value(), std::runtime_error); + CHECK_THROW(property.guid_value(), std::runtime_error); + CHECK_THROW(property.int32_value(), std::runtime_error); + CHECK_THROW(property.int64_value(), std::runtime_error); + + int32_t value = get_random_int32(); + property.set_value(value); + + CHECK(property.property_type() == wa::storage::edm_type::int32); + CHECK(!property.is_null()); + CHECK(property.int32_value() == value); + + CHECK_THROW(property.binary_value(), std::runtime_error); + CHECK_THROW(property.boolean_value(), std::runtime_error); + CHECK_THROW(property.datetime_value(), std::runtime_error); + CHECK_THROW(property.double_value(), std::runtime_error); + CHECK_THROW(property.guid_value(), std::runtime_error); + CHECK_THROW(property.int64_value(), std::runtime_error); + CHECK_THROW(property.string_value(), std::runtime_error); + + property.set_is_null(true); + + CHECK(property.property_type() == wa::storage::edm_type::int32); + CHECK(property.is_null()); + + CHECK_THROW(property.binary_value(), std::runtime_error); + CHECK_THROW(property.boolean_value(), std::runtime_error); + CHECK_THROW(property.datetime_value(), std::runtime_error); + CHECK_THROW(property.double_value(), std::runtime_error); + CHECK_THROW(property.guid_value(), std::runtime_error); + CHECK_THROW(property.int64_value(), std::runtime_error); + CHECK_THROW(property.string_value(), std::runtime_error); + CHECK_THROW(property.string_value(), std::runtime_error); + } + + TEST(Entity_PartitionKeyAndRowKey) + { + utility::string_t partition_key = get_random_string(); + utility::string_t row_key = get_random_string(); + + wa::storage::table_entity entity(partition_key, row_key); + + CHECK(entity.partition_key().compare(partition_key) == 0); + CHECK(entity.row_key().compare(row_key) == 0); + CHECK(entity.etag().empty()); + CHECK(entity.properties().empty()); + CHECK(!entity.timestamp().is_initialized()); + + partition_key = get_random_string(); + entity.set_partition_key(partition_key); + + CHECK(entity.partition_key().compare(partition_key) == 0); + + row_key = get_random_string(); + entity.set_row_key(row_key); + + CHECK(entity.row_key().compare(row_key) == 0); + + utility::string_t etag = get_random_string(); + entity.set_etag(etag); + + CHECK(entity.etag().compare(etag) == 0); + + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyA"), wa::storage::entity_property(get_random_boolean()))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyG"), wa::storage::entity_property(get_random_binary_data()))); + + CHECK(entity.properties().size() == 2U); + } + + TEST(Entity_PartitionKeyAndRowKeyAndETagAndProperties) + { + utility::string_t partition_key = get_random_string(); + utility::string_t row_key = get_random_string(); + utility::string_t etag = get_random_string(); + wa::storage::table_entity::properties_type properties; + + properties.reserve(3); + properties.insert(wa::storage::table_entity::property_type(U("PropertyD"), wa::storage::entity_property(get_random_double()))); + properties.insert(wa::storage::table_entity::property_type(U("PropertyE"), wa::storage::entity_property(get_random_string()))); + properties.insert(wa::storage::table_entity::property_type(U("PropertyH"), wa::storage::entity_property(get_random_guid()))); + + wa::storage::table_entity entity(partition_key, row_key, etag, properties); + + CHECK(entity.partition_key().compare(partition_key) == 0); + CHECK(entity.row_key().compare(row_key) == 0); + CHECK(entity.etag().compare(etag) == 0); + CHECK(entity.properties().size() == 3U); + CHECK(!entity.timestamp().is_initialized()); + + partition_key = get_random_string(); + entity.set_partition_key(partition_key); + + CHECK(entity.partition_key().compare(partition_key) == 0); + + row_key = get_random_string(); + entity.set_row_key(row_key); + + CHECK(entity.row_key().compare(row_key) == 0); + + etag = get_random_string(); + entity.set_etag(etag); + + CHECK(entity.etag().compare(etag) == 0); + + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyA"), wa::storage::entity_property(get_random_boolean()))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyG"), wa::storage::entity_property(get_random_binary_data()))); + + CHECK(entity.properties().size() == 5U); + } + + TEST(Operation_Delete) + { + utility::string_t partition_key = get_random_string(); + utility::string_t row_key = get_random_string(); + wa::storage::table_entity entity(partition_key, row_key); + + wa::storage::table_operation operation = wa::storage::table_operation::delete_entity(entity); + + CHECK(operation.entity().partition_key().compare(partition_key) == 0); + CHECK(operation.entity().row_key().compare(row_key) == 0); + CHECK(operation.entity().timestamp() == entity.timestamp()); + CHECK(operation.entity().etag().compare(entity.etag()) == 0); + CHECK(operation.operation_type() == wa::storage::table_operation_type::delete_operation); + } + + TEST(Operation_Insert) + { + utility::string_t partition_key = get_random_string(); + utility::string_t row_key = get_random_string(); + wa::storage::table_entity entity(partition_key, row_key); + + wa::storage::table_operation operation = wa::storage::table_operation::insert_entity(entity); + + CHECK(operation.entity().partition_key().compare(partition_key) == 0); + CHECK(operation.entity().row_key().compare(row_key) == 0); + CHECK(operation.entity().timestamp() == entity.timestamp()); + CHECK(operation.entity().etag().compare(entity.etag()) == 0); + CHECK(operation.operation_type() == wa::storage::table_operation_type::insert_operation); + } + + TEST(Operation_InsertOrMerge) + { + utility::string_t partition_key = get_random_string(); + utility::string_t row_key = get_random_string(); + wa::storage::table_entity entity(partition_key, row_key); + + wa::storage::table_operation operation = wa::storage::table_operation::insert_or_merge_entity(entity); + + CHECK(operation.entity().partition_key().compare(partition_key) == 0); + CHECK(operation.entity().row_key().compare(row_key) == 0); + CHECK(operation.entity().timestamp() == entity.timestamp()); + CHECK(operation.entity().etag().compare(entity.etag()) == 0); + CHECK(operation.operation_type() == wa::storage::table_operation_type::insert_or_merge_operation); + } + + TEST(Operation_InsertOrReplace) + { + utility::string_t partition_key = get_random_string(); + utility::string_t row_key = get_random_string(); + wa::storage::table_entity entity(partition_key, row_key); + + wa::storage::table_operation operation = wa::storage::table_operation::insert_or_replace_entity(entity); + + CHECK(operation.entity().partition_key().compare(partition_key) == 0); + CHECK(operation.entity().row_key().compare(row_key) == 0); + CHECK(operation.entity().timestamp() == entity.timestamp()); + CHECK(operation.entity().etag().compare(entity.etag()) == 0); + CHECK(operation.operation_type() == wa::storage::table_operation_type::insert_or_replace_operation); + } + + TEST(Operation_Merge) + { + utility::string_t partition_key = get_random_string(); + utility::string_t row_key = get_random_string(); + wa::storage::table_entity entity(partition_key, row_key); + + wa::storage::table_operation operation = wa::storage::table_operation::merge_entity(entity); + + CHECK(operation.entity().partition_key().compare(partition_key) == 0); + CHECK(operation.entity().row_key().compare(row_key) == 0); + CHECK(operation.entity().timestamp() == entity.timestamp()); + CHECK(operation.entity().etag().compare(entity.etag()) == 0); + CHECK(operation.operation_type() == wa::storage::table_operation_type::merge_operation); + } + + TEST(Operation_Replace) + { + utility::string_t partition_key = get_random_string(); + utility::string_t row_key = get_random_string(); + wa::storage::table_entity entity(partition_key, row_key); + + wa::storage::table_operation operation = wa::storage::table_operation::replace_entity(entity); + + CHECK(operation.entity().partition_key().compare(partition_key) == 0); + CHECK(operation.entity().row_key().compare(row_key) == 0); + CHECK(operation.entity().timestamp() == entity.timestamp()); + CHECK(operation.entity().etag().compare(entity.etag()) == 0); + CHECK(operation.operation_type() == wa::storage::table_operation_type::replace_operation); + } + + TEST(Operation_Retrieve) + { + utility::string_t partition_key = get_random_string(); + utility::string_t row_key = get_random_string(); + + wa::storage::table_operation operation = wa::storage::table_operation::retrieve_entity(partition_key, row_key); + + CHECK(operation.entity().partition_key().compare(partition_key) == 0); + CHECK(operation.entity().row_key().compare(row_key) == 0); + CHECK(!operation.entity().timestamp().is_initialized()); + CHECK(operation.entity().etag().empty()); + CHECK(operation.operation_type() == wa::storage::table_operation_type::retrieve_operation); + } + + TEST(Query_Normal) + { + wa::storage::table_query query; + + CHECK_EQUAL(-1, query.take_count()); + CHECK(query.filter_string().empty()); + CHECK(query.select_columns().empty()); + + int take_count = 25; + query.set_take_count(take_count); + + CHECK_EQUAL(take_count, query.take_count()); + + utility::string_t filter_string(U("PartitionKey eq 'AAA' and RowKey lt 'BBB' and Timestamp ge datetime'2013-09-01T00:00:00Z'")); + query.set_filter_string(filter_string); + + CHECK(query.filter_string().compare(filter_string) == 0); + + std::vector select_columns; + select_columns.reserve(3); + select_columns.push_back(U("PropertyA")); + select_columns.push_back(U("PropertyB")); + select_columns.push_back(U("PropertyC")); + query.set_select_columns(select_columns); + + CHECK_EQUAL(select_columns.size(), query.select_columns().size()); + CHECK(query.select_columns()[0].compare(U("PropertyA")) == 0); + CHECK(query.select_columns()[1].compare(U("PropertyB")) == 0); + CHECK(query.select_columns()[2].compare(U("PropertyC")) == 0); + } + + TEST(TableRequestOptions_Normal) + { + wa::storage::table_request_options options; + + CHECK(options.payload_format() == wa::storage::table_payload_format::json); + + wa::storage::table_payload_format payload_format = wa::storage::table_payload_format::json_no_metadata; + options.set_payload_format(payload_format); + + CHECK(options.payload_format() == payload_format); + } + + TEST(Table_CreateAndDelete) + { + utility::string_t table_name = get_table_name(); + wa::storage::cloud_table_client client = get_table_client(); + + wa::storage::cloud_table table = client.get_table_reference(table_name); + + { + wa::storage::table_request_options options; + wa::storage::operation_context context; + + table.create(options, context); + } + + { + wa::storage::table_request_options options; + wa::storage::operation_context context; + + bool exists = table.exists(options, context); + + CHECK(exists); + } + + { + wa::storage::table_request_options options; + wa::storage::operation_context context; + + CHECK_THROW(table.create(options, context), wa::storage::storage_exception); + } + + { + wa::storage::table_request_options options; + wa::storage::operation_context context; + + table.delete_table(options, context); + } + + { + wa::storage::table_request_options options; + wa::storage::operation_context context; + + bool exists = table.exists(options, context); + + CHECK(!exists); + } + + { + wa::storage::table_request_options options; + wa::storage::operation_context context; + + CHECK_THROW(table.delete_table(options, context), wa::storage::storage_exception); + } + } + + TEST(Table_CreateIfNotExistsAndDeleteIfExists) + { + utility::string_t table_name = get_table_name(); + wa::storage::cloud_table_client client = get_table_client(); + + wa::storage::cloud_table table = client.get_table_reference(table_name); + + { + wa::storage::table_request_options options; + wa::storage::operation_context context; + + bool created = table.create_if_not_exists(options, context); + + CHECK(created); + } + + { + wa::storage::table_request_options options; + wa::storage::operation_context context; + + bool exists = table.exists(options, context); + + CHECK(exists); + } + + { + wa::storage::table_request_options options; + wa::storage::operation_context context; + + bool created = table.create_if_not_exists(options, context); + + CHECK(!created); + } + + { + wa::storage::table_request_options options; + wa::storage::operation_context context; + + bool deleted = table.delete_table_if_exists(options, context); + + CHECK(deleted); + } + + { + wa::storage::table_request_options options; + wa::storage::operation_context context; + + bool exists = table.exists(options, context); + + CHECK(!exists); + } + + { + wa::storage::table_request_options options; + wa::storage::operation_context context; + + bool deleted = table.delete_table_if_exists(options, context); + + CHECK(!deleted); + } + } + + /* + class foo : public web::json::details::_Number + { + public: + foo() + : web::json::details::_Number(123.4567890123456789) + { + } + + std::basic_string bar() + { + std::basic_string stream; + format(stream); + return stream; + } + }; + + TEST(FooTest) + { + foo x; + x.bar(); + } + */ + + TEST(EntityOperation_InsertAndDelete) + { + wa::storage::cloud_table table = get_table(); + + utility::string_t partition_key = get_random_string(); + utility::string_t row_key = get_random_string(); + + bool boolean_value = get_random_boolean(); + int32_t int32_value = get_random_int32(); + int64_t int64_value = get_random_int64(); + double double_value = get_random_double(); + utility::string_t string_value = get_random_string(); + utility::datetime datetime_value = get_random_datetime(); + std::vector binary_value = get_random_binary_data(); + utility::uuid guid_value = get_random_guid(); + + { + wa::storage::table_entity entity(partition_key, row_key); + + entity.properties().reserve(8); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyA"), wa::storage::entity_property(boolean_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyB"), wa::storage::entity_property(int32_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyC"), wa::storage::entity_property(int64_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyD"), wa::storage::entity_property(double_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyE"), wa::storage::entity_property(string_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyF"), wa::storage::entity_property(datetime_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyG"), wa::storage::entity_property(binary_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyH"), wa::storage::entity_property(guid_value))); + + wa::storage::table_operation operation = wa::storage::table_operation::insert_entity(entity); + wa::storage::table_request_options options; + wa::storage::operation_context context; + + wa::storage::table_result result = table.execute(operation, options, context); + + CHECK(result.entity().partition_key().empty()); + CHECK(result.entity().row_key().empty()); + CHECK(!result.entity().timestamp().is_initialized()); + CHECK(result.entity().etag().empty()); + CHECK_EQUAL(204, result.http_status_code()); + CHECK(!result.etag().empty()); + } + + { + wa::storage::table_operation operation = wa::storage::table_operation::retrieve_entity(partition_key, row_key); + wa::storage::table_request_options options; + wa::storage::operation_context context; + + wa::storage::table_result result = table.execute(operation, options, context); + + CHECK(result.entity().partition_key().compare(partition_key) == 0); + CHECK(result.entity().row_key().compare(row_key) == 0); + CHECK(result.entity().timestamp().is_initialized()); + CHECK(result.entity().etag().empty()); + CHECK_EQUAL(200, result.http_status_code()); + CHECK(!result.etag().empty()); + + CHECK_EQUAL(8, result.entity().properties().size()); + CHECK(result.entity().properties().find(U("PropertyA")) != result.entity().properties().cend()); + CHECK_EQUAL(boolean_value, result.entity().properties().find(U("PropertyA"))->second.boolean_value()); + CHECK(result.entity().properties().find(U("PropertyB")) != result.entity().properties().cend()); + CHECK_EQUAL(int32_value, result.entity().properties().find(U("PropertyB"))->second.int32_value()); + CHECK(result.entity().properties().find(U("PropertyC")) != result.entity().properties().cend()); + CHECK_EQUAL(int64_value, result.entity().properties().find(U("PropertyC"))->second.int64_value()); + CHECK(result.entity().properties().find(U("PropertyD")) != result.entity().properties().cend()); + CHECK_EQUAL(double_value, result.entity().properties().find(U("PropertyD"))->second.double_value()); + CHECK(result.entity().properties().find(U("PropertyE")) != result.entity().properties().cend()); + CHECK(result.entity().properties().find(U("PropertyE"))->second.string_value().compare(string_value) == 0); + CHECK(result.entity().properties().find(U("PropertyF")) != result.entity().properties().cend()); + CHECK(result.entity().properties().find(U("PropertyF"))->second.datetime_value() == datetime_value); + CHECK(result.entity().properties().find(U("PropertyG")) != result.entity().properties().cend()); + CHECK_ARRAY_EQUAL(binary_value, result.entity().properties().find(U("PropertyG"))->second.binary_value(), binary_value.size()); + CHECK(result.entity().properties().find(U("PropertyH")) != result.entity().properties().cend()); + CHECK(utility::uuid_equal(result.entity().properties().find(U("PropertyH"))->second.guid_value(), guid_value)); + } + + { + wa::storage::table_entity entity(partition_key, row_key); + + entity.properties().reserve(8); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyA"), wa::storage::entity_property(boolean_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyB"), wa::storage::entity_property(int32_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyC"), wa::storage::entity_property(int64_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyD"), wa::storage::entity_property(double_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyE"), wa::storage::entity_property(string_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyF"), wa::storage::entity_property(datetime_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyG"), wa::storage::entity_property(binary_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyH"), wa::storage::entity_property(guid_value))); + + wa::storage::table_operation operation = wa::storage::table_operation::insert_entity(entity); + wa::storage::table_request_options options; + wa::storage::operation_context context; + + CHECK_THROW(table.execute(operation, options, context), wa::storage::storage_exception); + } + + { + //utility::string_t etag(U("*")); + + wa::storage::table_entity entity(partition_key, row_key); + //entity.set_etag(etag); + + wa::storage::table_operation operation = wa::storage::table_operation::delete_entity(entity); + wa::storage::table_request_options options; + wa::storage::operation_context context; + + wa::storage::table_result result = table.execute(operation, options, context); + + CHECK(result.entity().partition_key().empty()); + CHECK(result.entity().row_key().empty()); + CHECK(!result.entity().timestamp().is_initialized()); + CHECK(result.entity().etag().empty()); + CHECK_EQUAL(204, result.http_status_code()); + CHECK(result.etag().empty()); + } + + { + wa::storage::table_operation operation = wa::storage::table_operation::retrieve_entity(partition_key, row_key); + wa::storage::table_request_options options; + wa::storage::operation_context context; + + wa::storage::table_result result = table.execute(operation, options, context); + + CHECK(result.entity().partition_key().empty()); + CHECK(result.entity().row_key().empty()); + CHECK(!result.entity().timestamp().is_initialized()); + CHECK(result.entity().etag().empty()); + CHECK_EQUAL(404, result.http_status_code()); + CHECK(result.etag().empty()); + } + + { + wa::storage::table_entity entity(partition_key, row_key); + + wa::storage::table_operation operation = wa::storage::table_operation::delete_entity(entity); + wa::storage::table_request_options options; + wa::storage::operation_context context; + + CHECK_THROW(table.execute(operation, options, context), wa::storage::storage_exception); + } + + table.delete_table(); + } + + TEST(EntityOperation_InsertAndMerge) + { + wa::storage::cloud_table table = get_table(); + + utility::string_t partition_key = get_random_string(); + utility::string_t row_key = get_random_string(); + + bool boolean_value = get_random_boolean(); + int32_t int32_value = get_random_int32(); + int64_t int64_value = get_random_int64(); + double double_value = get_random_double(); + utility::string_t string_value = get_random_string(); + utility::datetime datetime_value = get_random_datetime(); + std::vector binary_value = get_random_binary_data(); + utility::uuid guid_value = get_random_guid(); + + { + wa::storage::table_entity entity(partition_key, row_key); + + entity.properties().reserve(8); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyA"), wa::storage::entity_property(boolean_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyB"), wa::storage::entity_property(int32_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyC"), wa::storage::entity_property(int64_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyD"), wa::storage::entity_property(double_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyE"), wa::storage::entity_property(string_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyF"), wa::storage::entity_property(datetime_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyG"), wa::storage::entity_property(binary_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyH"), wa::storage::entity_property(guid_value))); + + wa::storage::table_operation operation = wa::storage::table_operation::insert_or_merge_entity(entity); + wa::storage::table_request_options options; + wa::storage::operation_context context; + + wa::storage::table_result result = table.execute(operation, options, context); + + CHECK(result.entity().partition_key().empty()); + CHECK(result.entity().row_key().empty()); + CHECK(!result.entity().timestamp().is_initialized()); + CHECK(result.entity().etag().empty()); + CHECK_EQUAL(204, result.http_status_code()); + CHECK(!result.etag().empty()); + } + + { + wa::storage::table_operation operation = wa::storage::table_operation::retrieve_entity(partition_key, row_key); + wa::storage::table_request_options options; + wa::storage::operation_context context; + + wa::storage::table_result result = table.execute(operation, options, context); + + CHECK(result.entity().partition_key().compare(partition_key) == 0); + CHECK(result.entity().row_key().compare(row_key) == 0); + CHECK(result.entity().timestamp().is_initialized()); + CHECK(result.entity().etag().empty()); + CHECK_EQUAL(200, result.http_status_code()); + CHECK(!result.etag().empty()); + + CHECK_EQUAL(8, result.entity().properties().size()); + CHECK(result.entity().properties().find(U("PropertyA")) != result.entity().properties().cend()); + CHECK_EQUAL(boolean_value, result.entity().properties().find(U("PropertyA"))->second.boolean_value()); + CHECK(result.entity().properties().find(U("PropertyB")) != result.entity().properties().cend()); + CHECK_EQUAL(int32_value, result.entity().properties().find(U("PropertyB"))->second.int32_value()); + CHECK(result.entity().properties().find(U("PropertyC")) != result.entity().properties().cend()); + CHECK_EQUAL(int64_value, result.entity().properties().find(U("PropertyC"))->second.int64_value()); + CHECK(result.entity().properties().find(U("PropertyD")) != result.entity().properties().cend()); + CHECK_EQUAL(double_value, result.entity().properties().find(U("PropertyD"))->second.double_value()); + CHECK(result.entity().properties().find(U("PropertyE")) != result.entity().properties().cend()); + CHECK(result.entity().properties().find(U("PropertyE"))->second.string_value().compare(string_value) == 0); + CHECK(result.entity().properties().find(U("PropertyF")) != result.entity().properties().cend()); + CHECK(result.entity().properties().find(U("PropertyF"))->second.datetime_value() == datetime_value); + CHECK(result.entity().properties().find(U("PropertyG")) != result.entity().properties().cend()); + CHECK_ARRAY_EQUAL(binary_value, result.entity().properties().find(U("PropertyG"))->second.binary_value(), binary_value.size()); + CHECK(result.entity().properties().find(U("PropertyH")) != result.entity().properties().cend()); + CHECK(utility::uuid_equal(result.entity().properties().find(U("PropertyH"))->second.guid_value(), guid_value)); + } + + int64_value = get_random_int64(); + double_value = get_random_double(); + string_value = get_random_string(); + datetime_value = get_random_datetime(); + binary_value = get_random_binary_data(); + guid_value = get_random_guid(); + + int32_t int32_value2 = get_random_int32(); + int32_t int32_value3 = get_random_int32(); + + { + wa::storage::table_entity entity(partition_key, row_key); + + entity.properties().reserve(8); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyC"), wa::storage::entity_property(int64_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyD"), wa::storage::entity_property(double_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyE"), wa::storage::entity_property(string_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyF"), wa::storage::entity_property(datetime_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyG"), wa::storage::entity_property(binary_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyH"), wa::storage::entity_property(guid_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyI"), wa::storage::entity_property(int32_value2))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyJ"), wa::storage::entity_property(int32_value3))); + + wa::storage::table_operation operation = wa::storage::table_operation::insert_or_merge_entity(entity); + wa::storage::table_request_options options; + wa::storage::operation_context context; + + wa::storage::table_result result = table.execute(operation, options, context); + + CHECK(result.entity().partition_key().empty()); + CHECK(result.entity().row_key().empty()); + CHECK(!result.entity().timestamp().is_initialized()); + CHECK(result.entity().etag().empty()); + CHECK_EQUAL(204, result.http_status_code()); + CHECK(!result.etag().empty()); + } + + { + wa::storage::table_operation operation = wa::storage::table_operation::retrieve_entity(partition_key, row_key); + wa::storage::table_request_options options; + wa::storage::operation_context context; + + wa::storage::table_result result = table.execute(operation, options, context); + + CHECK(result.entity().partition_key().compare(partition_key) == 0); + CHECK(result.entity().row_key().compare(row_key) == 0); + CHECK(result.entity().timestamp().is_initialized()); + CHECK(result.entity().etag().empty()); + CHECK_EQUAL(200, result.http_status_code()); + CHECK(!result.etag().empty()); + + CHECK_EQUAL(10, result.entity().properties().size()); + CHECK(result.entity().properties().find(U("PropertyA")) != result.entity().properties().cend()); + CHECK_EQUAL(boolean_value, result.entity().properties().find(U("PropertyA"))->second.boolean_value()); + CHECK(result.entity().properties().find(U("PropertyB")) != result.entity().properties().cend()); + CHECK_EQUAL(int32_value, result.entity().properties().find(U("PropertyB"))->second.int32_value()); + CHECK(result.entity().properties().find(U("PropertyC")) != result.entity().properties().cend()); + CHECK_EQUAL(int64_value, result.entity().properties().find(U("PropertyC"))->second.int64_value()); + CHECK(result.entity().properties().find(U("PropertyD")) != result.entity().properties().cend()); + CHECK_EQUAL(double_value, result.entity().properties().find(U("PropertyD"))->second.double_value()); + CHECK(result.entity().properties().find(U("PropertyE")) != result.entity().properties().cend()); + CHECK(result.entity().properties().find(U("PropertyE"))->second.string_value().compare(string_value) == 0); + CHECK(result.entity().properties().find(U("PropertyF")) != result.entity().properties().cend()); + CHECK(result.entity().properties().find(U("PropertyF"))->second.datetime_value() == datetime_value); + CHECK(result.entity().properties().find(U("PropertyG")) != result.entity().properties().cend()); + CHECK_ARRAY_EQUAL(binary_value, result.entity().properties().find(U("PropertyG"))->second.binary_value(), binary_value.size()); + CHECK(result.entity().properties().find(U("PropertyH")) != result.entity().properties().cend()); + CHECK(utility::uuid_equal(result.entity().properties().find(U("PropertyH"))->second.guid_value(), guid_value)); + CHECK(result.entity().properties().find(U("PropertyI")) != result.entity().properties().cend()); + CHECK_EQUAL(int32_value2, result.entity().properties().find(U("PropertyI"))->second.int32_value()); + CHECK(result.entity().properties().find(U("PropertyJ")) != result.entity().properties().cend()); + CHECK_EQUAL(int32_value3, result.entity().properties().find(U("PropertyJ"))->second.int32_value()); + } + + int64_value = get_random_int64(); + double_value = get_random_double(); + string_value = get_random_string(); + datetime_value = get_random_datetime(); + binary_value = get_random_binary_data(); + guid_value = get_random_guid(); + + int32_t int32_value4 = get_random_int32(); + int32_t int32_value5 = get_random_int32(); + + { + wa::storage::table_entity entity(partition_key, row_key); + + entity.properties().reserve(8); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyC"), wa::storage::entity_property(int64_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyD"), wa::storage::entity_property(double_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyE"), wa::storage::entity_property(string_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyF"), wa::storage::entity_property(datetime_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyG"), wa::storage::entity_property(binary_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyH"), wa::storage::entity_property(guid_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyK"), wa::storage::entity_property(int32_value4))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyL"), wa::storage::entity_property(int32_value5))); + + wa::storage::table_operation operation = wa::storage::table_operation::merge_entity(entity); + wa::storage::table_request_options options; + wa::storage::operation_context context; + + wa::storage::table_result result = table.execute(operation, options, context); + + CHECK(result.entity().partition_key().empty()); + CHECK(result.entity().row_key().empty()); + CHECK(!result.entity().timestamp().is_initialized()); + CHECK(result.entity().etag().empty()); + CHECK_EQUAL(204, result.http_status_code()); + CHECK(!result.etag().empty()); + } + + { + wa::storage::table_operation operation = wa::storage::table_operation::retrieve_entity(partition_key, row_key); + wa::storage::table_request_options options; + wa::storage::operation_context context; + + wa::storage::table_result result = table.execute(operation, options, context); + + CHECK(result.entity().partition_key().compare(partition_key) == 0); + CHECK(result.entity().row_key().compare(row_key) == 0); + CHECK(result.entity().timestamp().is_initialized()); + CHECK(result.entity().etag().empty()); + CHECK_EQUAL(200, result.http_status_code()); + CHECK(!result.etag().empty()); + + CHECK_EQUAL(12, result.entity().properties().size()); + CHECK(result.entity().properties().find(U("PropertyA")) != result.entity().properties().cend()); + CHECK_EQUAL(boolean_value, result.entity().properties().find(U("PropertyA"))->second.boolean_value()); + CHECK(result.entity().properties().find(U("PropertyB")) != result.entity().properties().cend()); + CHECK_EQUAL(int32_value, result.entity().properties().find(U("PropertyB"))->second.int32_value()); + CHECK(result.entity().properties().find(U("PropertyC")) != result.entity().properties().cend()); + CHECK_EQUAL(int64_value, result.entity().properties().find(U("PropertyC"))->second.int64_value()); + CHECK(result.entity().properties().find(U("PropertyD")) != result.entity().properties().cend()); + CHECK_EQUAL(double_value, result.entity().properties().find(U("PropertyD"))->second.double_value()); + CHECK(result.entity().properties().find(U("PropertyE")) != result.entity().properties().cend()); + CHECK(result.entity().properties().find(U("PropertyE"))->second.string_value().compare(string_value) == 0); + CHECK(result.entity().properties().find(U("PropertyF")) != result.entity().properties().cend()); + CHECK(result.entity().properties().find(U("PropertyF"))->second.datetime_value() == datetime_value); + CHECK(result.entity().properties().find(U("PropertyG")) != result.entity().properties().cend()); + CHECK_ARRAY_EQUAL(binary_value, result.entity().properties().find(U("PropertyG"))->second.binary_value(), binary_value.size()); + CHECK(result.entity().properties().find(U("PropertyH")) != result.entity().properties().cend()); + CHECK(utility::uuid_equal(result.entity().properties().find(U("PropertyH"))->second.guid_value(), guid_value)); + CHECK(result.entity().properties().find(U("PropertyI")) != result.entity().properties().cend()); + CHECK_EQUAL(int32_value2, result.entity().properties().find(U("PropertyI"))->second.int32_value()); + CHECK(result.entity().properties().find(U("PropertyJ")) != result.entity().properties().cend()); + CHECK_EQUAL(int32_value3, result.entity().properties().find(U("PropertyJ"))->second.int32_value()); + CHECK(result.entity().properties().find(U("PropertyK")) != result.entity().properties().cend()); + CHECK_EQUAL(int32_value4, result.entity().properties().find(U("PropertyK"))->second.int32_value()); + CHECK(result.entity().properties().find(U("PropertyL")) != result.entity().properties().cend()); + CHECK_EQUAL(int32_value5, result.entity().properties().find(U("PropertyL"))->second.int32_value()); + } + + { + wa::storage::table_entity entity(partition_key, row_key); + + wa::storage::table_operation operation = wa::storage::table_operation::delete_entity(entity); + wa::storage::table_request_options options; + wa::storage::operation_context context; + + wa::storage::table_result result = table.execute(operation, options, context); + + CHECK(result.entity().partition_key().empty()); + CHECK(result.entity().row_key().empty()); + CHECK(!result.entity().timestamp().is_initialized()); + CHECK(result.entity().etag().empty()); + CHECK_EQUAL(204, result.http_status_code()); + CHECK(result.etag().empty()); + } + + int64_value = get_random_int64(); + double_value = get_random_double(); + string_value = get_random_string(); + datetime_value = get_random_datetime(); + binary_value = get_random_binary_data(); + guid_value = get_random_guid(); + + int32_t int32_value6 = get_random_int32(); + int32_t int32_value7 = get_random_int32(); + + { + wa::storage::table_entity entity(partition_key, row_key); + + entity.properties().reserve(8); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyC"), wa::storage::entity_property(int64_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyD"), wa::storage::entity_property(double_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyE"), wa::storage::entity_property(string_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyF"), wa::storage::entity_property(datetime_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyG"), wa::storage::entity_property(binary_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyH"), wa::storage::entity_property(guid_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyM"), wa::storage::entity_property(int32_value6))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyN"), wa::storage::entity_property(int32_value7))); + + wa::storage::table_operation operation = wa::storage::table_operation::merge_entity(entity); + wa::storage::table_request_options options; + wa::storage::operation_context context; + + CHECK_THROW(table.execute(operation, options, context), wa::storage::storage_exception); + } + + table.delete_table(); + } + + TEST(EntityOperation_InsertAndReplace) + { + wa::storage::cloud_table table = get_table(); + + utility::string_t partition_key = get_random_string(); + utility::string_t row_key = get_random_string(); + + bool boolean_value = get_random_boolean(); + int32_t int32_value = get_random_int32(); + int64_t int64_value = get_random_int64(); + double double_value = get_random_double(); + utility::string_t string_value = get_random_string(); + utility::datetime datetime_value = get_random_datetime(); + std::vector binary_value = get_random_binary_data(); + utility::uuid guid_value = get_random_guid(); + + { + wa::storage::table_entity entity(partition_key, row_key); + + entity.properties().reserve(8); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyA"), wa::storage::entity_property(boolean_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyB"), wa::storage::entity_property(int32_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyC"), wa::storage::entity_property(int64_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyD"), wa::storage::entity_property(double_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyE"), wa::storage::entity_property(string_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyF"), wa::storage::entity_property(datetime_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyG"), wa::storage::entity_property(binary_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyH"), wa::storage::entity_property(guid_value))); + + wa::storage::table_operation operation = wa::storage::table_operation::insert_or_replace_entity(entity); + wa::storage::table_request_options options; + wa::storage::operation_context context; + + wa::storage::table_result result = table.execute(operation, options, context); + + CHECK(result.entity().partition_key().empty()); + CHECK(result.entity().row_key().empty()); + CHECK(!result.entity().timestamp().is_initialized()); + CHECK(result.entity().etag().empty()); + CHECK_EQUAL(204, result.http_status_code()); + CHECK(!result.etag().empty()); + } + + { + wa::storage::table_operation operation = wa::storage::table_operation::retrieve_entity(partition_key, row_key); + wa::storage::table_request_options options; + wa::storage::operation_context context; + + wa::storage::table_result result = table.execute(operation, options, context); + + CHECK(result.entity().partition_key().compare(partition_key) == 0); + CHECK(result.entity().row_key().compare(row_key) == 0); + CHECK(result.entity().timestamp().is_initialized()); + CHECK(result.entity().etag().empty()); + CHECK_EQUAL(200, result.http_status_code()); + CHECK(!result.etag().empty()); + + CHECK_EQUAL(8, result.entity().properties().size()); + CHECK(result.entity().properties().find(U("PropertyA")) != result.entity().properties().cend()); + CHECK_EQUAL(boolean_value, result.entity().properties().find(U("PropertyA"))->second.boolean_value()); + CHECK(result.entity().properties().find(U("PropertyB")) != result.entity().properties().cend()); + CHECK_EQUAL(int32_value, result.entity().properties().find(U("PropertyB"))->second.int32_value()); + CHECK(result.entity().properties().find(U("PropertyC")) != result.entity().properties().cend()); + CHECK_EQUAL(int64_value, result.entity().properties().find(U("PropertyC"))->second.int64_value()); + CHECK(result.entity().properties().find(U("PropertyD")) != result.entity().properties().cend()); + CHECK_EQUAL(double_value, result.entity().properties().find(U("PropertyD"))->second.double_value()); + CHECK(result.entity().properties().find(U("PropertyE")) != result.entity().properties().cend()); + CHECK(result.entity().properties().find(U("PropertyE"))->second.string_value().compare(string_value) == 0); + CHECK(result.entity().properties().find(U("PropertyF")) != result.entity().properties().cend()); + CHECK(result.entity().properties().find(U("PropertyF"))->second.datetime_value() == datetime_value); + CHECK(result.entity().properties().find(U("PropertyG")) != result.entity().properties().cend()); + CHECK_ARRAY_EQUAL(binary_value, result.entity().properties().find(U("PropertyG"))->second.binary_value(), binary_value.size()); + CHECK(result.entity().properties().find(U("PropertyH")) != result.entity().properties().cend()); + CHECK(utility::uuid_equal(result.entity().properties().find(U("PropertyH"))->second.guid_value(), guid_value)); + } + + int64_value = get_random_int64(); + double_value = get_random_double(); + string_value = get_random_string(); + datetime_value = get_random_datetime(); + binary_value = get_random_binary_data(); + guid_value = get_random_guid(); + + int32_t int32_value2 = get_random_int32(); + int32_t int32_value3 = get_random_int32(); + + { + wa::storage::table_entity entity(partition_key, row_key); + + entity.properties().reserve(8); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyC"), wa::storage::entity_property(int64_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyD"), wa::storage::entity_property(double_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyE"), wa::storage::entity_property(string_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyF"), wa::storage::entity_property(datetime_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyG"), wa::storage::entity_property(binary_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyH"), wa::storage::entity_property(guid_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyI"), wa::storage::entity_property(int32_value2))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyJ"), wa::storage::entity_property(int32_value3))); + + wa::storage::table_operation operation = wa::storage::table_operation::insert_or_replace_entity(entity); + wa::storage::table_request_options options; + wa::storage::operation_context context; + + wa::storage::table_result result = table.execute(operation, options, context); + + CHECK(result.entity().partition_key().empty()); + CHECK(result.entity().row_key().empty()); + CHECK(!result.entity().timestamp().is_initialized()); + CHECK(result.entity().etag().empty()); + CHECK_EQUAL(204, result.http_status_code()); + CHECK(!result.etag().empty()); + } + + { + wa::storage::table_operation operation = wa::storage::table_operation::retrieve_entity(partition_key, row_key); + wa::storage::table_request_options options; + wa::storage::operation_context context; + + wa::storage::table_result result = table.execute(operation, options, context); + + CHECK(result.entity().partition_key().compare(partition_key) == 0); + CHECK(result.entity().row_key().compare(row_key) == 0); + CHECK(result.entity().timestamp().is_initialized()); + CHECK(result.entity().etag().empty()); + CHECK_EQUAL(200, result.http_status_code()); + CHECK(!result.etag().empty()); + + CHECK_EQUAL(8, result.entity().properties().size()); + CHECK(result.entity().properties().find(U("PropertyC")) != result.entity().properties().cend()); + CHECK_EQUAL(int64_value, result.entity().properties().find(U("PropertyC"))->second.int64_value()); + CHECK(result.entity().properties().find(U("PropertyD")) != result.entity().properties().cend()); + CHECK_EQUAL(double_value, result.entity().properties().find(U("PropertyD"))->second.double_value()); + CHECK(result.entity().properties().find(U("PropertyE")) != result.entity().properties().cend()); + CHECK(result.entity().properties().find(U("PropertyE"))->second.string_value().compare(string_value) == 0); + CHECK(result.entity().properties().find(U("PropertyF")) != result.entity().properties().cend()); + CHECK(result.entity().properties().find(U("PropertyF"))->second.datetime_value() == datetime_value); + CHECK(result.entity().properties().find(U("PropertyG")) != result.entity().properties().cend()); + CHECK_ARRAY_EQUAL(binary_value, result.entity().properties().find(U("PropertyG"))->second.binary_value(), binary_value.size()); + CHECK(result.entity().properties().find(U("PropertyH")) != result.entity().properties().cend()); + CHECK(utility::uuid_equal(result.entity().properties().find(U("PropertyH"))->second.guid_value(), guid_value)); + CHECK(result.entity().properties().find(U("PropertyI")) != result.entity().properties().cend()); + CHECK_EQUAL(int32_value2, result.entity().properties().find(U("PropertyI"))->second.int32_value()); + CHECK(result.entity().properties().find(U("PropertyJ")) != result.entity().properties().cend()); + CHECK_EQUAL(int32_value3, result.entity().properties().find(U("PropertyJ"))->second.int32_value()); + } + + int64_value = get_random_int64(); + double_value = get_random_double(); + string_value = get_random_string(); + datetime_value = get_random_datetime(); + binary_value = get_random_binary_data(); + guid_value = get_random_guid(); + + int32_t int32_value4 = get_random_int32(); + int32_t int32_value5 = get_random_int32(); + + { + wa::storage::table_entity entity(partition_key, row_key); + + entity.properties().reserve(8); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyC"), wa::storage::entity_property(int64_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyD"), wa::storage::entity_property(double_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyE"), wa::storage::entity_property(string_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyF"), wa::storage::entity_property(datetime_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyG"), wa::storage::entity_property(binary_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyH"), wa::storage::entity_property(guid_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyK"), wa::storage::entity_property(int32_value4))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyL"), wa::storage::entity_property(int32_value5))); + + wa::storage::table_operation operation = wa::storage::table_operation::replace_entity(entity); + wa::storage::table_request_options options; + wa::storage::operation_context context; + + wa::storage::table_result result = table.execute(operation, options, context); + + CHECK(result.entity().partition_key().empty()); + CHECK(result.entity().row_key().empty()); + CHECK(!result.entity().timestamp().is_initialized()); + CHECK(result.entity().etag().empty()); + CHECK_EQUAL(204, result.http_status_code()); + CHECK(!result.etag().empty()); + } + + { + wa::storage::table_operation operation = wa::storage::table_operation::retrieve_entity(partition_key, row_key); + wa::storage::table_request_options options; + wa::storage::operation_context context; + + wa::storage::table_result result = table.execute(operation, options, context); + + CHECK(result.entity().partition_key().compare(partition_key) == 0); + CHECK(result.entity().row_key().compare(row_key) == 0); + CHECK(result.entity().timestamp().is_initialized()); + CHECK(result.entity().etag().empty()); + CHECK_EQUAL(200, result.http_status_code()); + CHECK(!result.etag().empty()); + + CHECK_EQUAL(8, result.entity().properties().size()); + CHECK(result.entity().properties().find(U("PropertyC")) != result.entity().properties().cend()); + CHECK_EQUAL(int64_value, result.entity().properties().find(U("PropertyC"))->second.int64_value()); + CHECK(result.entity().properties().find(U("PropertyD")) != result.entity().properties().cend()); + CHECK_EQUAL(double_value, result.entity().properties().find(U("PropertyD"))->second.double_value()); + CHECK(result.entity().properties().find(U("PropertyE")) != result.entity().properties().cend()); + CHECK(result.entity().properties().find(U("PropertyE"))->second.string_value().compare(string_value) == 0); + CHECK(result.entity().properties().find(U("PropertyF")) != result.entity().properties().cend()); + CHECK(result.entity().properties().find(U("PropertyF"))->second.datetime_value() == datetime_value); + CHECK(result.entity().properties().find(U("PropertyG")) != result.entity().properties().cend()); + CHECK_ARRAY_EQUAL(binary_value, result.entity().properties().find(U("PropertyG"))->second.binary_value(), binary_value.size()); + CHECK(result.entity().properties().find(U("PropertyH")) != result.entity().properties().cend()); + CHECK(utility::uuid_equal(result.entity().properties().find(U("PropertyH"))->second.guid_value(), guid_value)); + CHECK(result.entity().properties().find(U("PropertyK")) != result.entity().properties().cend()); + CHECK_EQUAL(int32_value4, result.entity().properties().find(U("PropertyK"))->second.int32_value()); + CHECK(result.entity().properties().find(U("PropertyL")) != result.entity().properties().cend()); + CHECK_EQUAL(int32_value5, result.entity().properties().find(U("PropertyL"))->second.int32_value()); + } + + { + wa::storage::table_entity entity(partition_key, row_key); + + wa::storage::table_operation operation = wa::storage::table_operation::delete_entity(entity); + wa::storage::table_request_options options; + wa::storage::operation_context context; + + wa::storage::table_result result = table.execute(operation, options, context); + + CHECK(result.entity().partition_key().empty()); + CHECK(result.entity().row_key().empty()); + CHECK(!result.entity().timestamp().is_initialized()); + CHECK(result.entity().etag().empty()); + CHECK_EQUAL(204, result.http_status_code()); + CHECK(result.etag().empty()); + } + + int64_value = get_random_int64(); + double_value = get_random_double(); + string_value = get_random_string(); + datetime_value = get_random_datetime(); + binary_value = get_random_binary_data(); + guid_value = get_random_guid(); + + int32_t int32_value6 = get_random_int32(); + int32_t int32_value7 = get_random_int32(); + + { + wa::storage::table_entity entity(partition_key, row_key); + + entity.properties().reserve(8); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyC"), wa::storage::entity_property(int64_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyD"), wa::storage::entity_property(double_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyE"), wa::storage::entity_property(string_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyF"), wa::storage::entity_property(datetime_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyG"), wa::storage::entity_property(binary_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyH"), wa::storage::entity_property(guid_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyM"), wa::storage::entity_property(int32_value6))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyN"), wa::storage::entity_property(int32_value7))); + + wa::storage::table_operation operation = wa::storage::table_operation::replace_entity(entity); + wa::storage::table_request_options options; + wa::storage::operation_context context; + + CHECK_THROW(table.execute(operation, options, context), wa::storage::storage_exception); + } + + table.delete_table(); + } + + TEST(EntityOperation_DoubleSpecialValues) + { + wa::storage::cloud_table table = get_table(); + + utility::string_t partition_key = get_random_string(); + utility::string_t row_key = get_random_string(); + + double nan_value = std::numeric_limits::quiet_NaN(); + double infinity_value = std::numeric_limits::infinity(); + double negative_infinity_value = -std::numeric_limits::infinity(); + double negative_zero = -0.0; + double round_number = 123.0; + + { + wa::storage::table_entity entity(partition_key, row_key); + + entity.properties().reserve(4); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyA"), wa::storage::entity_property(nan_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyB"), wa::storage::entity_property(infinity_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyC"), wa::storage::entity_property(negative_infinity_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyD"), wa::storage::entity_property(negative_zero))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyE"), wa::storage::entity_property(round_number))); + + wa::storage::table_operation operation = wa::storage::table_operation::insert_entity(entity); + wa::storage::table_request_options options; + wa::storage::operation_context context; + + wa::storage::table_result result = table.execute(operation, options, context); + + CHECK(result.entity().partition_key().empty()); + CHECK(result.entity().row_key().empty()); + CHECK(!result.entity().timestamp().is_initialized()); + CHECK(result.entity().etag().empty()); + CHECK_EQUAL(204, result.http_status_code()); + CHECK(!result.etag().empty()); + } + + { + wa::storage::table_operation operation = wa::storage::table_operation::retrieve_entity(partition_key, row_key); + wa::storage::table_request_options options; + wa::storage::operation_context context; + + wa::storage::table_result result = table.execute(operation, options, context); + + CHECK(result.entity().partition_key().compare(partition_key) == 0); + CHECK(result.entity().row_key().compare(row_key) == 0); + CHECK(result.entity().timestamp().is_initialized()); + CHECK(result.entity().etag().empty()); + CHECK_EQUAL(200, result.http_status_code()); + CHECK(!result.etag().empty()); + + CHECK_EQUAL(5, result.entity().properties().size()); + CHECK(result.entity().properties().find(U("PropertyA")) != result.entity().properties().cend()); + CHECK(result.entity().properties().find(U("PropertyA"))->second.double_value() != result.entity().properties().find(U("PropertyA"))->second.double_value()); // Only NaN is defined to not equal itself + CHECK(result.entity().properties().find(U("PropertyB")) != result.entity().properties().cend()); + CHECK_EQUAL(infinity_value, result.entity().properties().find(U("PropertyB"))->second.double_value()); + CHECK(result.entity().properties().find(U("PropertyC")) != result.entity().properties().cend()); + CHECK_EQUAL(negative_infinity_value, result.entity().properties().find(U("PropertyC"))->second.double_value()); + + // TODO: Handle -0.0 correctly (also investigate that the service and other client libraries can handle -0.0) + /* + CHECK(result.entity().properties().find(U("PropertyD")) != result.entity().properties().cend()); + CHECK_EQUAL(0.0, result.entity().properties().find(U("PropertyD"))->second.double_value()); + CHECK_EQUAL(negative_infinity_value, 1.0 / result.entity().properties().find(U("PropertyD"))->second.double_value()); // 1.0 / -0.0 == -Infinity + */ + + CHECK(result.entity().properties().find(U("PropertyE")) != result.entity().properties().cend()); + CHECK_EQUAL(round_number, result.entity().properties().find(U("PropertyE"))->second.double_value()); + } + + { + wa::storage::table_entity entity(partition_key, row_key); + + wa::storage::table_operation operation = wa::storage::table_operation::delete_entity(entity); + wa::storage::table_request_options options; + wa::storage::operation_context context; + + wa::storage::table_result result = table.execute(operation, options, context); + + CHECK(result.entity().partition_key().empty()); + CHECK(result.entity().row_key().empty()); + CHECK(!result.entity().timestamp().is_initialized()); + CHECK(result.entity().etag().empty()); + CHECK_EQUAL(204, result.http_status_code()); + CHECK(result.etag().empty()); + } + + table.delete_table(); + } + + TEST(EntityBatch_Normal) + { + const int BATCH_SIZE = 3; + + wa::storage::cloud_table table = get_table(); + + utility::string_t partition_key = get_random_string(); + utility::string_t row_keys[BATCH_SIZE]; + + for (int i = 0; i < BATCH_SIZE; ++i) + { + row_keys[i] = get_random_string(); + } + + int32_t int32_value = get_random_int32(); + utility::string_t string_value = get_random_string(); + + { + wa::storage::table_batch_operation operation; + wa::storage::table_request_options options; + wa::storage::operation_context context; + + for (int i = 0; i < BATCH_SIZE; ++i) + { + wa::storage::table_entity entity(partition_key, row_keys[i]); + + entity.properties().reserve(2); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyA"), wa::storage::entity_property(int32_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyB"), wa::storage::entity_property(string_value))); + + operation.insert_entity(entity); + } + + std::vector results = table.execute_batch(operation, options, context); + + CHECK_EQUAL(BATCH_SIZE, results.size()); + + for (int i = 0; i < BATCH_SIZE; ++i) + { + /* + CHECK(operation.operations()[i].entity().partition_key().compare(results[i].entity().partition_key()) == 0); + CHECK(operation.operations()[i].entity().row_key().compare(results[i].entity().row_key()) == 0); + */ + CHECK(results[i].entity().partition_key().empty()); + CHECK(results[i].entity().row_key().empty()); + CHECK(!results[i].entity().timestamp().is_initialized()); + CHECK(results[i].entity().etag().empty()); + CHECK_EQUAL(0U, results[i].entity().properties().size()); + CHECK_EQUAL(204, results[i].http_status_code()); + CHECK(!results[i].etag().empty()); + } + } + + { + wa::storage::table_query query; + wa::storage::table_request_options options; + wa::storage::operation_context context; + + utility::string_t filter_string = wa::storage::table_query::generate_filter_condition(U("PartitionKey"), wa::storage::query_comparison_operator::equal, partition_key); + query.set_filter_string(filter_string); + + std::vector results = table.execute_query(query, options, context); + + CHECK_EQUAL(BATCH_SIZE, results.size()); + + for (int i = 0; i < BATCH_SIZE; ++i) + { + CHECK_EQUAL(2, results[i].properties().size()); + CHECK_EQUAL(int32_value, results[i].properties()[U("PropertyA")].int32_value()); + CHECK(string_value.compare(results[i].properties()[U("PropertyB")].string_value()) == 0); + } + } + + int32_t int32_value2 = get_random_int32(); + utility::string_t string_value2 = get_random_string(); + + { + wa::storage::table_batch_operation operation; + wa::storage::table_request_options options; + wa::storage::operation_context context; + + for (int i = 0; i < BATCH_SIZE; ++i) + { + wa::storage::table_entity entity(partition_key, row_keys[i]); + + entity.properties().reserve(3); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyA"), wa::storage::entity_property(int32_value2))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyB"), wa::storage::entity_property(string_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyC"), wa::storage::entity_property(string_value2))); + + operation.insert_or_merge_entity(entity); + } + + std::vector results = table.execute_batch(operation, options, context); + + CHECK_EQUAL(BATCH_SIZE, results.size()); + + for (int i = 0; i < BATCH_SIZE; ++i) + { + CHECK(results[i].entity().partition_key().empty()); + CHECK(results[i].entity().row_key().empty()); + CHECK(!results[i].entity().timestamp().is_initialized()); + CHECK(results[i].entity().etag().empty()); + CHECK_EQUAL(0U, results[i].entity().properties().size()); + CHECK_EQUAL(204, results[i].http_status_code()); + CHECK(!results[i].etag().empty()); + } + } + + { + wa::storage::table_query query; + wa::storage::table_request_options options; + wa::storage::operation_context context; + + utility::string_t filter_string = wa::storage::table_query::generate_filter_condition(U("PartitionKey"), wa::storage::query_comparison_operator::equal, partition_key); + query.set_filter_string(filter_string); + + std::vector results = table.execute_query(query, options, context); + + CHECK_EQUAL(BATCH_SIZE, results.size()); + + for (int i = 0; i < BATCH_SIZE; ++i) + { + CHECK_EQUAL(3, results[i].properties().size()); + CHECK_EQUAL(int32_value2, results[i].properties()[U("PropertyA")].int32_value()); + CHECK(string_value.compare(results[i].properties()[U("PropertyB")].string_value()) == 0); + CHECK(string_value2.compare(results[i].properties()[U("PropertyC")].string_value()) == 0); + } + } + + int32_t int32_value3 = get_random_int32(); + utility::string_t string_value3 = get_random_string(); + + { + wa::storage::table_batch_operation operation; + wa::storage::table_request_options options; + wa::storage::operation_context context; + + for (int i = 0; i < BATCH_SIZE; ++i) + { + wa::storage::table_entity entity(partition_key, row_keys[i]); + + entity.properties().reserve(3); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyA"), wa::storage::entity_property(int32_value3))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyB"), wa::storage::entity_property(string_value))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyD"), wa::storage::entity_property(string_value3))); + + operation.insert_or_replace_entity(entity); + } + + std::vector results = table.execute_batch(operation, options, context); + + CHECK_EQUAL(BATCH_SIZE, results.size()); + + for (int i = 0; i < BATCH_SIZE; ++i) + { + CHECK(results[i].entity().partition_key().empty()); + CHECK(results[i].entity().row_key().empty()); + CHECK(!results[i].entity().timestamp().is_initialized()); + CHECK(results[i].entity().etag().empty()); + CHECK_EQUAL(0U, results[i].entity().properties().size()); + CHECK_EQUAL(204, results[i].http_status_code()); + CHECK(!results[i].etag().empty()); + } + } + + { + wa::storage::table_query query; + wa::storage::table_request_options options; + wa::storage::operation_context context; + + utility::string_t filter_string = wa::storage::table_query::generate_filter_condition(U("PartitionKey"), wa::storage::query_comparison_operator::equal, partition_key); + query.set_filter_string(filter_string); + + std::vector results = table.execute_query(query, options, context); + + CHECK_EQUAL(BATCH_SIZE, results.size()); + + for (int i = 0; i < BATCH_SIZE; ++i) + { + CHECK_EQUAL(3, results[i].properties().size()); + CHECK_EQUAL(int32_value3, results[i].properties()[U("PropertyA")].int32_value()); + CHECK(string_value.compare(results[i].properties()[U("PropertyB")].string_value()) == 0); + CHECK(string_value3.compare(results[i].properties()[U("PropertyD")].string_value()) == 0); + } + } + + int32_t int32_value4 = get_random_int32(); + utility::string_t string_value4 = get_random_string(); + + { + wa::storage::table_batch_operation operation; + wa::storage::table_request_options options; + wa::storage::operation_context context; + + for (int i = 0; i < BATCH_SIZE; ++i) + { + wa::storage::table_entity entity(partition_key, row_keys[i]); + + entity.properties().reserve(2); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyB"), wa::storage::entity_property(string_value4))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyE"), wa::storage::entity_property(int32_value4))); + + operation.replace_entity(entity); + } + + std::vector results = table.execute_batch(operation, options, context); + + CHECK_EQUAL(BATCH_SIZE, results.size()); + + for (int i = 0; i < BATCH_SIZE; ++i) + { + CHECK(results[i].entity().partition_key().empty()); + CHECK(results[i].entity().row_key().empty()); + CHECK(!results[i].entity().timestamp().is_initialized()); + CHECK(results[i].entity().etag().empty()); + CHECK_EQUAL(0U, results[i].entity().properties().size()); + CHECK_EQUAL(204, results[i].http_status_code()); + CHECK(!results[i].etag().empty()); + } + } + + { + wa::storage::table_query query; + wa::storage::table_request_options options; + wa::storage::operation_context context; + + utility::string_t filter_string = wa::storage::table_query::generate_filter_condition(U("PartitionKey"), wa::storage::query_comparison_operator::equal, partition_key); + query.set_filter_string(filter_string); + + std::vector results = table.execute_query(query, options, context); + + CHECK_EQUAL(BATCH_SIZE, results.size()); + + for (int i = 0; i < BATCH_SIZE; ++i) + { + CHECK_EQUAL(2, results[i].properties().size()); + CHECK(string_value4.compare(results[i].properties()[U("PropertyB")].string_value()) == 0); + CHECK_EQUAL(int32_value4, results[i].properties()[U("PropertyE")].int32_value()); + } + } + + int32_t int32_value5 = get_random_int32(); + utility::string_t string_value5 = get_random_string(); + + { + wa::storage::table_batch_operation operation; + wa::storage::table_request_options options; + wa::storage::operation_context context; + + for (int i = 0; i < BATCH_SIZE; ++i) + { + wa::storage::table_entity entity(partition_key, row_keys[i]); + + entity.properties().reserve(2); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyE"), wa::storage::entity_property(int32_value5))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyF"), wa::storage::entity_property(string_value5))); + + operation.merge_entity(entity); + } + + std::vector results = table.execute_batch(operation, options, context); + + CHECK_EQUAL(BATCH_SIZE, results.size()); + + for (int i = 0; i < BATCH_SIZE; ++i) + { + CHECK(results[i].entity().partition_key().empty()); + CHECK(results[i].entity().row_key().empty()); + CHECK(!results[i].entity().timestamp().is_initialized()); + CHECK(results[i].entity().etag().empty()); + CHECK_EQUAL(0U, results[i].entity().properties().size()); + CHECK_EQUAL(204, results[i].http_status_code()); + CHECK(!results[i].etag().empty()); + } + } + + { + wa::storage::table_query query; + wa::storage::table_request_options options; + wa::storage::operation_context context; + + utility::string_t filter_string = wa::storage::table_query::generate_filter_condition(U("PartitionKey"), wa::storage::query_comparison_operator::equal, partition_key); + query.set_filter_string(filter_string); + + std::vector results = table.execute_query(query, options, context); + + CHECK_EQUAL(BATCH_SIZE, results.size()); + + for (int i = 0; i < BATCH_SIZE; ++i) + { + CHECK_EQUAL(3, results[i].properties().size()); + CHECK(string_value4.compare(results[i].properties()[U("PropertyB")].string_value()) == 0); + CHECK_EQUAL(int32_value5, results[i].properties()[U("PropertyE")].int32_value()); + CHECK(string_value5.compare(results[i].properties()[U("PropertyF")].string_value()) == 0); + } + } + + { + for (int i = 0; i < BATCH_SIZE; ++i) + { + wa::storage::table_batch_operation operation; + wa::storage::table_request_options options; + wa::storage::operation_context context; + + operation.retrieve_entity(partition_key, row_keys[i]); + + std::vector results = table.execute_batch(operation, options, context); + + CHECK_EQUAL(1, results.size()); + + CHECK(operation.operations()[0].entity().partition_key().compare(results[0].entity().partition_key()) == 0); + CHECK(operation.operations()[0].entity().row_key().compare(results[0].entity().row_key()) == 0); + CHECK(!results[0].entity().etag().empty()); + CHECK(results[0].entity().timestamp().is_initialized()); + CHECK_EQUAL(3U, results[0].entity().properties().size()); + CHECK_EQUAL(200, results[0].http_status_code()); + CHECK(!results[0].etag().empty()); + } + } + + { + wa::storage::table_batch_operation operation; + wa::storage::table_request_options options; + wa::storage::operation_context context; + + for (int i = 0; i < BATCH_SIZE; ++i) + { + wa::storage::table_entity entity(partition_key, row_keys[i]); + + operation.delete_entity(entity); + } + + std::vector results = table.execute_batch(operation, options, context); + + CHECK_EQUAL(BATCH_SIZE, results.size()); + + for (int i = 0; i < BATCH_SIZE; ++i) + { + CHECK(results[i].entity().partition_key().empty()); + CHECK(results[i].entity().row_key().empty()); + CHECK(!results[i].entity().timestamp().is_initialized()); + CHECK(results[i].entity().etag().empty()); + CHECK_EQUAL(0U, results[i].entity().properties().size()); + CHECK_EQUAL(204, results[i].http_status_code()); + CHECK(results[i].etag().empty()); + } + } + + { + for (int i = 0; i < BATCH_SIZE; ++i) + { + wa::storage::table_batch_operation operation; + wa::storage::table_request_options options; + wa::storage::operation_context context; + + operation.retrieve_entity(partition_key, row_keys[i]); + + std::vector results = table.execute_batch(operation, options, context); + + CHECK_EQUAL(1, results.size()); + + CHECK(results[0].entity().partition_key().empty()); + CHECK(results[0].entity().row_key().empty()); + CHECK(results[0].entity().etag().empty()); + CHECK(!results[0].entity().timestamp().is_initialized()); + CHECK_EQUAL(0U, results[0].entity().properties().size()); + CHECK_EQUAL(404, results[0].http_status_code()); + CHECK(results[0].etag().empty()); + } + } + + table.delete_table(); + } + + TEST(EntityBatch_PartitionKeyMismatch) + { + wa::storage::cloud_table table = get_table(false); + + utility::string_t partition_key1 = get_random_string(); + utility::string_t row_key1 = get_random_string(); + utility::string_t partition_key2 = get_random_string(); + utility::string_t row_key2 = get_random_string(); + + wa::storage::table_batch_operation operation; + wa::storage::table_request_options options; + wa::storage::operation_context context; + + wa::storage::table_entity entity1(partition_key1, row_key1); + operation.insert_entity(entity1); + + wa::storage::table_entity entity2(partition_key2, row_key2); + operation.insert_entity(entity2); + + CHECK_THROW(table.execute_batch(operation, options, context), std::invalid_argument); + } + + TEST(EntityBatch_MultipleRetrieve) + { + wa::storage::cloud_table table = get_table(false); + + utility::string_t partition_key = get_random_string(); + utility::string_t row_key1 = get_random_string(); + utility::string_t row_key2 = get_random_string(); + + wa::storage::table_batch_operation operation; + wa::storage::table_request_options options; + wa::storage::operation_context context; + + operation.retrieve_entity(partition_key, row_key1); + operation.retrieve_entity(partition_key, row_key2); + + CHECK_THROW(table.execute_batch(operation, options, context), std::invalid_argument); + } + + TEST(EntityBatch_RetrieveMixture) + { + wa::storage::cloud_table table = get_table(false); + + utility::string_t partition_key = get_random_string(); + utility::string_t row_key1 = get_random_string(); + utility::string_t row_key2 = get_random_string(); + + wa::storage::table_batch_operation operation; + wa::storage::table_request_options options; + wa::storage::operation_context context; + + operation.retrieve_entity(partition_key, row_key1); + + wa::storage::table_entity entity2(partition_key, row_key2); + operation.insert_entity(entity2); + + CHECK_THROW(table.execute_batch(operation, options, context), std::invalid_argument); + } + + TEST(EntityQuery_Normal) + { + wa::storage::cloud_table table = get_table(); + + utility::string_t partition_key1 = get_random_string(); + utility::string_t partition_key2 = get_random_string(); + + { + for (int partition = 1; partition <= 2; ++partition) + { + utility::string_t partition_key = partition == 1 ? partition_key1 : partition_key2; + + for (int row1 = 0; row1 < 26; ++row1) + { + wa::storage::table_batch_operation operation; + + for (int row2 = 0; row2 < 26; ++row2) + { + utility::string_t row_key = get_string('a' + row1, 'a' + row2); + + wa::storage::table_entity entity(partition_key, row_key); + + entity.properties().reserve(8); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyA"), wa::storage::entity_property(get_random_boolean()))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyB"), wa::storage::entity_property(get_random_int32()))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyC"), wa::storage::entity_property(get_random_int64()))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyD"), wa::storage::entity_property(get_random_double()))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyE"), wa::storage::entity_property(get_random_string()))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyF"), wa::storage::entity_property(get_random_datetime()))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyG"), wa::storage::entity_property(get_random_binary_data()))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyH"), wa::storage::entity_property(get_random_guid()))); + + operation.insert_entity(entity); + } + + std::vector results = table.execute_batch(operation); + + for (std::vector::const_iterator itr = results.cbegin(); itr != results.cend(); ++itr) + { + wa::storage::table_result result = *itr; + + CHECK(result.entity().partition_key().empty()); + CHECK(result.entity().row_key().empty()); + CHECK(!result.entity().timestamp().is_initialized()); + CHECK(result.entity().etag().empty()); + CHECK_EQUAL(204, result.http_status_code()); + CHECK(!result.etag().empty()); + } + } + } + } + + { + wa::storage::table_query query; + wa::storage::table_request_options options; + wa::storage::operation_context context; + + int take_count = 10; + query.set_take_count(take_count); + + CHECK_EQUAL(take_count, query.take_count()); + + utility::string_t filter_string = wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::generate_filter_condition(U("PartitionKey"), wa::storage::query_comparison_operator::equal, partition_key1), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("RowKey"), wa::storage::query_comparison_operator::greater_than_or_equal, U("k"))), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("RowKey"), wa::storage::query_comparison_operator::less_than, U("n"))), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("Timestamp"), wa::storage::query_comparison_operator::greater_than_or_equal, utility::datetime::from_string(U("2013-09-01T00:00:00Z"), utility::datetime::ISO_8601))), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("PropertyA"), wa::storage::query_comparison_operator::not_equal, false)), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("PropertyB"), wa::storage::query_comparison_operator::not_equal, 1234567890)), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("PropertyC"), wa::storage::query_comparison_operator::not_equal, 1234567890123456789LL)), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("PropertyD"), wa::storage::query_comparison_operator::not_equal, 9.1234567890123456789)), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("PropertyE"), wa::storage::query_comparison_operator::not_equal, U("ABCDE12345"))), + wa::storage::query_logical_operator::and, + // TODO: Add fractional seconds + wa::storage::table_query::generate_filter_condition(U("PropertyF"), wa::storage::query_comparison_operator::not_equal, utility::datetime::from_string(U("2013-01-02T03:04:05Z"), utility::datetime::date_format::ISO_8601))), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("PropertyG"), wa::storage::query_comparison_operator::not_equal, std::vector(10, 'X'))), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("PropertyH"), wa::storage::query_comparison_operator::not_equal, utility::string_to_uuid(U("12345678-abcd-efab-cdef-1234567890ab")))); + query.set_filter_string(filter_string); + + utility::string_t expected_filter_string = utility::string_t(U("(((((((((((PartitionKey eq '")) + partition_key1 + utility::string_t(U("') and (RowKey ge 'k')) and (RowKey lt 'n')) and (Timestamp ge datetime'2013-09-01T00:00:00Z')) and (PropertyA ne false)) and (PropertyB ne 1234567890)) and (PropertyC ne 1234567890123456789L)) and (PropertyD ne 9.1234567890123461)) and (PropertyE ne 'ABCDE12345')) and (PropertyF ne datetime'2013-01-02T03:04:05Z')) and (PropertyG ne X'58585858585858585858')) and (PropertyH ne guid'12345678-abcd-efab-cdef-1234567890ab')")); + CHECK(filter_string.compare(expected_filter_string) == 0); + + std::vector select_columns; + select_columns.reserve(9); + select_columns.push_back(U("PropertyA")); + select_columns.push_back(U("PropertyB")); + select_columns.push_back(U("PropertyC")); + select_columns.push_back(U("PropertyD")); + select_columns.push_back(U("PropertyE")); + select_columns.push_back(U("PropertyF")); + select_columns.push_back(U("PropertyG")); + select_columns.push_back(U("PropertyH")); + select_columns.push_back(U("PropertyX")); + query.set_select_columns(select_columns); + + options.set_payload_format(wa::storage::table_payload_format::json_full_metadata); + + std::vector results = table.execute_query(query, options, context); + + CHECK(results.size() > 0); + CHECK((int)results.size() > take_count); + + for (std::vector::const_iterator entity_iterator = results.cbegin(); entity_iterator != results.cend(); ++entity_iterator) + { + wa::storage::table_entity entity = *entity_iterator; + + CHECK(!entity.partition_key().empty()); + CHECK(!entity.row_key().empty()); + CHECK(entity.timestamp().is_initialized()); + CHECK(!entity.etag().empty()); + + wa::storage::table_entity::properties_type properties = entity.properties(); + + CHECK_EQUAL(9, properties.size()); + + for (wa::storage::table_entity::properties_type::const_iterator propertyIterator = properties.cbegin(); propertyIterator != properties.cend(); ++propertyIterator) + { + utility::string_t property_name = propertyIterator->first; + wa::storage::entity_property property = propertyIterator->second; + + CHECK(!property_name.empty()); + CHECK(property.is_null() || !property.str().empty()); + } + } + } + + { + wa::storage::table_query query; + wa::storage::table_request_options options; + wa::storage::operation_context context; + + int take_count = 10; + query.set_take_count(take_count); + + CHECK_EQUAL(take_count, query.take_count()); + + utility::string_t filter_string = wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::generate_filter_condition(U("PartitionKey"), wa::storage::query_comparison_operator::equal, partition_key1), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("RowKey"), wa::storage::query_comparison_operator::greater_than_or_equal, U("k"))), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("RowKey"), wa::storage::query_comparison_operator::less_than, U("n"))), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("Timestamp"), wa::storage::query_comparison_operator::greater_than_or_equal, utility::datetime::from_string(U("2013-09-01T00:00:00Z"), utility::datetime::ISO_8601))), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("PropertyA"), wa::storage::query_comparison_operator::not_equal, false)), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("PropertyB"), wa::storage::query_comparison_operator::not_equal, 1234567890)), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("PropertyC"), wa::storage::query_comparison_operator::not_equal, 1234567890123456789LL)), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("PropertyD"), wa::storage::query_comparison_operator::not_equal, 9.1234567890123456789)), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("PropertyE"), wa::storage::query_comparison_operator::not_equal, U("ABCDE12345"))), + wa::storage::query_logical_operator::and, + // TODO: Add fractional seconds + wa::storage::table_query::generate_filter_condition(U("PropertyF"), wa::storage::query_comparison_operator::not_equal, utility::datetime::from_string(U("2013-01-02T03:04:05Z"), utility::datetime::date_format::ISO_8601))), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("PropertyG"), wa::storage::query_comparison_operator::not_equal, std::vector(10, 'X'))), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("PropertyH"), wa::storage::query_comparison_operator::not_equal, utility::string_to_uuid(U("12345678-abcd-efab-cdef-1234567890ab")))); + query.set_filter_string(filter_string); + + utility::string_t expected_filter_string = utility::string_t(U("(((((((((((PartitionKey eq '")) + partition_key1 + utility::string_t(U("') and (RowKey ge 'k')) and (RowKey lt 'n')) and (Timestamp ge datetime'2013-09-01T00:00:00Z')) and (PropertyA ne false)) and (PropertyB ne 1234567890)) and (PropertyC ne 1234567890123456789L)) and (PropertyD ne 9.1234567890123461)) and (PropertyE ne 'ABCDE12345')) and (PropertyF ne datetime'2013-01-02T03:04:05Z')) and (PropertyG ne X'58585858585858585858')) and (PropertyH ne guid'12345678-abcd-efab-cdef-1234567890ab')")); + CHECK(filter_string.compare(expected_filter_string) == 0); + + std::vector select_columns; + select_columns.reserve(9); + select_columns.push_back(U("PropertyA")); + select_columns.push_back(U("PropertyB")); + select_columns.push_back(U("PropertyC")); + select_columns.push_back(U("PropertyD")); + select_columns.push_back(U("PropertyE")); + select_columns.push_back(U("PropertyF")); + select_columns.push_back(U("PropertyG")); + select_columns.push_back(U("PropertyH")); + select_columns.push_back(U("PropertyX")); + query.set_select_columns(select_columns); + + options.set_payload_format(wa::storage::table_payload_format::json_full_metadata); + + std::vector results = table.execute_query(query, options, context); + + CHECK(results.size() > 0); + + for (std::vector::const_iterator entity_iterator = results.cbegin(); entity_iterator != results.cend(); ++entity_iterator) + { + wa::storage::table_entity entity = *entity_iterator; + + CHECK(!entity.partition_key().empty()); + CHECK(!entity.row_key().empty()); + CHECK(entity.timestamp().is_initialized()); + CHECK(!entity.etag().empty()); + + wa::storage::table_entity::properties_type properties = entity.properties(); + + CHECK_EQUAL(9, properties.size()); + + for (wa::storage::table_entity::properties_type::const_iterator propertyIterator = properties.cbegin(); propertyIterator != properties.cend(); ++propertyIterator) + { + utility::string_t property_name = propertyIterator->first; + wa::storage::entity_property property = propertyIterator->second; + + CHECK(!property_name.empty()); + CHECK(property.is_null() || !property.str().empty()); + } + } + } + + table.delete_table(); + } + + TEST(EntityQuery_Segmented) + { + wa::storage::cloud_table table = get_table(); + + utility::string_t partition_key1 = get_random_string(); + utility::string_t partition_key2 = get_random_string(); + + { + for (int partition = 1; partition <= 2; ++partition) + { + utility::string_t partition_key = partition == 1 ? partition_key1 : partition_key2; + + for (int row1 = 0; row1 < 26; ++row1) + { + wa::storage::table_batch_operation operation; + + for (int row2 = 0; row2 < 26; ++row2) + { + utility::string_t row_key = get_string('a' + row1, 'a' + row2); + + wa::storage::table_entity entity(partition_key, row_key); + + entity.properties().reserve(8); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyA"), wa::storage::entity_property(get_random_boolean()))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyB"), wa::storage::entity_property(get_random_int32()))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyC"), wa::storage::entity_property(get_random_int64()))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyD"), wa::storage::entity_property(get_random_double()))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyE"), wa::storage::entity_property(get_random_string()))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyF"), wa::storage::entity_property(get_random_datetime()))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyG"), wa::storage::entity_property(get_random_binary_data()))); + entity.properties().insert(wa::storage::table_entity::property_type(U("PropertyH"), wa::storage::entity_property(get_random_guid()))); + + operation.insert_entity(entity); + } + + std::vector results = table.execute_batch(operation); + + for (std::vector::const_iterator itr = results.cbegin(); itr != results.cend(); ++itr) + { + wa::storage::table_result result = *itr; + + CHECK(result.entity().partition_key().empty()); + CHECK(result.entity().row_key().empty()); + CHECK(!result.entity().timestamp().is_initialized()); + CHECK(result.entity().etag().empty()); + CHECK_EQUAL(204, result.http_status_code()); + CHECK(!result.etag().empty()); + } + } + } + } + + { + wa::storage::table_query query; + wa::storage::table_request_options options; + wa::storage::operation_context context; + + int take_count = 10; + query.set_take_count(take_count); + + CHECK_EQUAL(take_count, query.take_count()); + + utility::string_t filter_string = wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::generate_filter_condition(U("PartitionKey"), wa::storage::query_comparison_operator::equal, partition_key1), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("RowKey"), wa::storage::query_comparison_operator::greater_than_or_equal, U("k"))), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("RowKey"), wa::storage::query_comparison_operator::less_than, U("n"))), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("Timestamp"), wa::storage::query_comparison_operator::greater_than_or_equal, utility::datetime::from_string(U("2013-09-01T00:00:00Z"), utility::datetime::ISO_8601))), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("PropertyA"), wa::storage::query_comparison_operator::not_equal, false)), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("PropertyB"), wa::storage::query_comparison_operator::not_equal, 1234567890)), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("PropertyC"), wa::storage::query_comparison_operator::not_equal, 1234567890123456789LL)), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("PropertyD"), wa::storage::query_comparison_operator::not_equal, 9.1234567890123456789)), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("PropertyE"), wa::storage::query_comparison_operator::not_equal, U("ABCDE12345"))), + wa::storage::query_logical_operator::and, + // TODO: Add fractional seconds + wa::storage::table_query::generate_filter_condition(U("PropertyF"), wa::storage::query_comparison_operator::not_equal, utility::datetime::from_string(U("2013-01-02T03:04:05Z"), utility::datetime::date_format::ISO_8601))), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("PropertyG"), wa::storage::query_comparison_operator::not_equal, std::vector(10, 'X'))), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("PropertyH"), wa::storage::query_comparison_operator::not_equal, utility::string_to_uuid(U("12345678-abcd-efab-cdef-1234567890ab")))); + query.set_filter_string(filter_string); + + utility::string_t expected_filter_string = utility::string_t(U("(((((((((((PartitionKey eq '")) + partition_key1 + utility::string_t(U("') and (RowKey ge 'k')) and (RowKey lt 'n')) and (Timestamp ge datetime'2013-09-01T00:00:00Z')) and (PropertyA ne false)) and (PropertyB ne 1234567890)) and (PropertyC ne 1234567890123456789L)) and (PropertyD ne 9.1234567890123461)) and (PropertyE ne 'ABCDE12345')) and (PropertyF ne datetime'2013-01-02T03:04:05Z')) and (PropertyG ne X'58585858585858585858')) and (PropertyH ne guid'12345678-abcd-efab-cdef-1234567890ab')")); + CHECK(filter_string.compare(expected_filter_string) == 0); + + std::vector select_columns; + select_columns.reserve(9); + select_columns.push_back(U("PropertyA")); + select_columns.push_back(U("PropertyB")); + select_columns.push_back(U("PropertyC")); + select_columns.push_back(U("PropertyD")); + select_columns.push_back(U("PropertyE")); + select_columns.push_back(U("PropertyF")); + select_columns.push_back(U("PropertyG")); + select_columns.push_back(U("PropertyH")); + select_columns.push_back(U("PropertyX")); + query.set_select_columns(select_columns); + + options.set_payload_format(wa::storage::table_payload_format::json_full_metadata); + + std::vector results = table.execute_query(query, options, context); + + CHECK(results.size() > 0); + CHECK((int)results.size() > take_count); + + for (std::vector::const_iterator entity_iterator = results.cbegin(); entity_iterator != results.cend(); ++entity_iterator) + { + wa::storage::table_entity entity = *entity_iterator; + + CHECK(!entity.partition_key().empty()); + CHECK(!entity.row_key().empty()); + CHECK(entity.timestamp().is_initialized()); + CHECK(!entity.etag().empty()); + + wa::storage::table_entity::properties_type properties = entity.properties(); + + CHECK_EQUAL(9, properties.size()); + + for (wa::storage::table_entity::properties_type::const_iterator propertyIterator = properties.cbegin(); propertyIterator != properties.cend(); ++propertyIterator) + { + utility::string_t property_name = propertyIterator->first; + wa::storage::entity_property property = propertyIterator->second; + + CHECK(!property_name.empty()); + CHECK(property.is_null() || !property.str().empty()); + } + } + } + + { + wa::storage::table_query query; + wa::storage::continuation_token continuation_token; + wa::storage::table_request_options options; + wa::storage::operation_context context; + + int take_count = 10; + query.set_take_count(take_count); + + CHECK_EQUAL(take_count, query.take_count()); + + utility::string_t filter_string = wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::combine_filter_conditions( + wa::storage::table_query::generate_filter_condition(U("PartitionKey"), wa::storage::query_comparison_operator::equal, partition_key1), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("RowKey"), wa::storage::query_comparison_operator::greater_than_or_equal, U("k"))), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("RowKey"), wa::storage::query_comparison_operator::less_than, U("n"))), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("Timestamp"), wa::storage::query_comparison_operator::greater_than_or_equal, utility::datetime::from_string(U("2013-09-01T00:00:00Z"), utility::datetime::ISO_8601))), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("PropertyA"), wa::storage::query_comparison_operator::not_equal, false)), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("PropertyB"), wa::storage::query_comparison_operator::not_equal, 1234567890)), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("PropertyC"), wa::storage::query_comparison_operator::not_equal, 1234567890123456789LL)), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("PropertyD"), wa::storage::query_comparison_operator::not_equal, 9.1234567890123456789)), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("PropertyE"), wa::storage::query_comparison_operator::not_equal, U("ABCDE12345"))), + wa::storage::query_logical_operator::and, + // TODO: Add fractional seconds + wa::storage::table_query::generate_filter_condition(U("PropertyF"), wa::storage::query_comparison_operator::not_equal, utility::datetime::from_string(U("2013-01-02T03:04:05Z"), utility::datetime::date_format::ISO_8601))), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("PropertyG"), wa::storage::query_comparison_operator::not_equal, std::vector(10, 'X'))), + wa::storage::query_logical_operator::and, + wa::storage::table_query::generate_filter_condition(U("PropertyH"), wa::storage::query_comparison_operator::not_equal, utility::string_to_uuid(U("12345678-abcd-efab-cdef-1234567890ab")))); + query.set_filter_string(filter_string); + + utility::string_t expected_filter_string = utility::string_t(U("(((((((((((PartitionKey eq '")) + partition_key1 + utility::string_t(U("') and (RowKey ge 'k')) and (RowKey lt 'n')) and (Timestamp ge datetime'2013-09-01T00:00:00Z')) and (PropertyA ne false)) and (PropertyB ne 1234567890)) and (PropertyC ne 1234567890123456789L)) and (PropertyD ne 9.1234567890123461)) and (PropertyE ne 'ABCDE12345')) and (PropertyF ne datetime'2013-01-02T03:04:05Z')) and (PropertyG ne X'58585858585858585858')) and (PropertyH ne guid'12345678-abcd-efab-cdef-1234567890ab')")); + CHECK(filter_string.compare(expected_filter_string) == 0); + + std::vector select_columns; + select_columns.reserve(9); + select_columns.push_back(U("PropertyA")); + select_columns.push_back(U("PropertyB")); + select_columns.push_back(U("PropertyC")); + select_columns.push_back(U("PropertyD")); + select_columns.push_back(U("PropertyE")); + select_columns.push_back(U("PropertyF")); + select_columns.push_back(U("PropertyG")); + select_columns.push_back(U("PropertyH")); + select_columns.push_back(U("PropertyX")); + query.set_select_columns(select_columns); + + options.set_payload_format(wa::storage::table_payload_format::json_full_metadata); + + int segment_count = 0; + wa::storage::table_query_segment query_segment; + do + { + query_segment = table.execute_query_segmented(query, continuation_token, options, context); + std::vector results = query_segment.results(); + + CHECK(results.size() > 0); + CHECK((int)results.size() <= take_count); + + for (std::vector::const_iterator entity_iterator = results.cbegin(); entity_iterator != results.cend(); ++entity_iterator) + { + wa::storage::table_entity entity = *entity_iterator; + + CHECK(!entity.partition_key().empty()); + CHECK(!entity.row_key().empty()); + CHECK(entity.timestamp().is_initialized()); + CHECK(!entity.etag().empty()); + + wa::storage::table_entity::properties_type properties = entity.properties(); + + CHECK_EQUAL(9, properties.size()); + + for (wa::storage::table_entity::properties_type::const_iterator propertyIterator = properties.cbegin(); propertyIterator != properties.cend(); ++propertyIterator) + { + utility::string_t property_name = propertyIterator->first; + wa::storage::entity_property property = propertyIterator->second; + + CHECK(!property_name.empty()); + CHECK(property.is_null() || !property.str().empty()); + } + } + + ++segment_count; + continuation_token = query_segment.continuation_token(); + } + while (!continuation_token.empty()); + + CHECK(segment_count > 1); + } + + table.delete_table(); + } + + TEST(Table_Permissions) + { + wa::storage::cloud_table table = get_table(); + + utility::string_t policy_name1 = U("policy1"); + utility::string_t policy_name2 = U("policy2"); + + uint8_t permission1 = wa::storage::table_shared_access_policy::permissions::read | wa::storage::table_shared_access_policy::permissions::add; + uint8_t permission2 = wa::storage::table_shared_access_policy::permissions::read | wa::storage::table_shared_access_policy::permissions::update; + + wa::storage::table_shared_access_policy policy1(utility::datetime::utc_now(), utility::datetime::utc_now(), permission1); + wa::storage::table_shared_access_policy policy2(utility::datetime::utc_now(), utility::datetime::utc_now(), permission2); + + { + wa::storage::table_request_options options; + wa::storage::operation_context context; + + wa::storage::table_permissions permissions = table.download_permissions(options, context); + + CHECK(permissions.policies().empty()); + } + + { + wa::storage::table_permissions permissions; + wa::storage::table_request_options options; + wa::storage::operation_context context; + + wa::storage::shared_access_policies policies; + policies.insert(std::pair(policy_name1, policy1)); + policies.insert(std::pair(policy_name2, policy2)); + + permissions.set_policies(policies); + table.upload_permissions(permissions, options, context); + } + + { + wa::storage::table_request_options options; + wa::storage::operation_context context; + + wa::storage::table_permissions permissions = table.download_permissions(options, context); + + CHECK_EQUAL(2U, permissions.policies().size()); + CHECK_EQUAL(permission1, permissions.policies()[policy_name1].permission()); + CHECK(permissions.policies()[policy_name1].start().is_initialized()); + CHECK(permissions.policies()[policy_name1].expiry().is_initialized()); + CHECK_EQUAL(permission2, permissions.policies()[policy_name2].permission()); + CHECK(permissions.policies()[policy_name2].start().is_initialized()); + CHECK(permissions.policies()[policy_name2].expiry().is_initialized()); + } + + { + wa::storage::table_permissions permissions; + wa::storage::table_request_options options; + wa::storage::operation_context context; + + wa::storage::shared_access_policies policies; + + permissions.set_policies(policies); + table.upload_permissions(permissions, options, context); + } + + { + wa::storage::table_request_options options; + wa::storage::operation_context context; + + wa::storage::table_permissions permissions = table.download_permissions(options, context); + + CHECK(permissions.policies().empty()); + } + } +} diff --git a/Microsoft.WindowsAzure.Storage/tests/executor_test.cpp b/Microsoft.WindowsAzure.Storage/tests/executor_test.cpp new file mode 100644 index 00000000..21704733 --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/executor_test.cpp @@ -0,0 +1,70 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "blob_test_base.h" +#include "check_macros.h" + +SUITE(Core) +{ + TEST(operation_context) + { + auto client = test_config::instance().account().create_cloud_blob_client(); + + utility::string_t client_request_id; + utility::string_t service_request_id; + utility::string_t test_key; + auto start_time = utility::datetime::utc_now(); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + + wa::storage::operation_context context; + context.set_client_request_id(U("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee")); + context.user_headers().add(U("x-ms-test-key"), U("test-value")); + context.set_sending_request([&client_request_id, &test_key] (web::http::http_request& request, wa::storage::operation_context context) mutable + { + client_request_id = request.headers().find(U("x-ms-client-request-id"))->second; + test_key = request.headers().find(U("x-ms-test-key"))->second; + }); + context.set_response_received([&service_request_id] (web::http::http_request& request, const web::http::http_response& response, wa::storage::operation_context context) mutable + { + service_request_id = response.headers().find(U("x-ms-request-id"))->second; + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + }); + + auto container = client.get_container_reference(U("this-container-does-not-exist")); + container.exists(wa::storage::blob_request_options(), context); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + auto end_time = utility::datetime::utc_now(); + + CHECK_EQUAL(1, context.request_results().size()); + auto result = context.request_results().front(); + + CHECK(result.is_response_available()); + CHECK_UTF8_EQUAL(U("aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"), client_request_id); + CHECK_UTF8_EQUAL(service_request_id, result.service_request_id()); + CHECK_EQUAL(web::http::status_codes::NotFound, result.http_status_code()); + CHECK(start_time.to_interval() < result.start_time().to_interval()); + CHECK(end_time.to_interval() > result.end_time().to_interval()); + CHECK(result.end_time().to_interval() > result.start_time().to_interval()); + } + + TEST(storage_uri) + { + CHECK_THROW(wa::storage::storage_uri(U("http://www.microsoft.com/test1"), U("http://www.microsoft.com/test2")), std::invalid_argument); + } +} diff --git a/Microsoft.WindowsAzure.Storage/tests/main.cpp b/Microsoft.WindowsAzure.Storage/tests/main.cpp new file mode 100644 index 00000000..39b5908c --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/main.cpp @@ -0,0 +1,59 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#include "stdafx.h" + +#include "was/blob.h" + +int run_tests(const char* suite_name, const char* test_name) +{ + UnitTest::TestReporterStdout reporter; + UnitTest::TestRunner runner(reporter); + return runner.RunTestsIf(UnitTest::Test::GetTestList(), suite_name, [test_name] (UnitTest::Test* test) -> bool + { + return (test_name == NULL) || (!strcmp(test_name, test->m_details.testName)); + }, 0); +} + +int main(int argc, const char* argv[]) +{ + wa::storage::operation_context::set_default_log_level(wa::storage::client_log_level::log_level_verbose); + + if (argc == 1) + { + return run_tests(NULL, NULL); + } + + int failure_count = 0; + for (int i = 1; i < argc; ++i) + { + std::string arg(argv[i]); + auto colon = arg.find(':'); + if (colon == std::string::npos) + { + failure_count += run_tests(argv[i], NULL); + } + else + { + auto suite_name = arg.substr(0, colon); + auto test_name = arg.substr(colon + 1); + failure_count += run_tests(suite_name.c_str(), test_name.c_str()); + } + } + + return failure_count; +} diff --git a/Microsoft.WindowsAzure.Storage/tests/packages.config b/Microsoft.WindowsAzure.Storage/tests/packages.config new file mode 100644 index 00000000..38fd4cb4 --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Microsoft.WindowsAzure.Storage/tests/read_from_secondary_test.cpp b/Microsoft.WindowsAzure.Storage/tests/read_from_secondary_test.cpp new file mode 100644 index 00000000..aa9c03a9 --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/read_from_secondary_test.cpp @@ -0,0 +1,270 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#include "stdafx.h" + +#include "check_macros.h" +#include "blob_test_base.h" + +class basic_always_retry_policy : public wa::storage::basic_retry_policy +{ +public: + + basic_always_retry_policy(std::vector expected_retry_context_list, std::vector retry_info_list) + : wa::storage::basic_retry_policy(), m_current_retry_count(0), + m_expected_retry_context_list(std::move(expected_retry_context_list)), + m_retry_info_list(std::move(retry_info_list)) + { + CHECK_EQUAL(m_expected_retry_context_list.size(), m_retry_info_list.size() + 1); + } + + wa::storage::retry_info evaluate(const wa::storage::retry_context& retry_context, wa::storage::operation_context context) override + { + CHECK_EQUAL(m_current_retry_count++, retry_context.current_retry_count()); + CHECK(retry_context.current_retry_count() < static_cast(m_expected_retry_context_list.size())); + CHECK(retry_context.current_location_mode() == m_expected_retry_context_list[retry_context.current_retry_count()].current_location_mode()); + CHECK(retry_context.next_location() == m_expected_retry_context_list[retry_context.current_retry_count()].next_location()); + + if (static_cast(retry_context.current_retry_count()) < m_retry_info_list.size()) + { + return m_retry_info_list[retry_context.current_retry_count()]; + } + else + { + return wa::storage::retry_info(); + } + } + + wa::storage::retry_policy clone() const override + { + return wa::storage::retry_policy(std::make_shared(m_expected_retry_context_list, m_retry_info_list)); + } + +private: + + std::vector m_expected_retry_context_list; + std::vector m_retry_info_list; + int m_current_retry_count; +}; + +class multi_location_test_helper +{ +public: + + multi_location_test_helper(const wa::storage::storage_uri& uri, wa::storage::storage_location initial_location, wa::storage::operation_context context, const std::vector& expected_retry_context_list, const std::vector& retry_info_list) + : m_uri(uri), m_initial_location(initial_location), m_retry_info_list(retry_info_list), + m_policy(std::make_shared(expected_retry_context_list, retry_info_list)), + m_request_counter(0), m_error(false), m_context(context), m_context_results_offset(context.request_results().size()) + { + m_context.set_sending_request([this] (web::http::http_request& request, wa::storage::operation_context) + { + if (!m_error) + { + wa::storage::storage_location location(m_request_counter == 0 ? m_initial_location : m_retry_info_list[m_request_counter - 1].target_location()); + auto uri = m_uri.get_location_uri(location); + if (!request.request_uri().has_same_authority(uri)) + { + m_error = true; + } + } + + m_request_counter++; + }); + } + + ~multi_location_test_helper() + { + m_context.set_sending_request(std::function()); + + CHECK(!m_error); + CHECK(m_initial_location == m_context.request_results()[m_context_results_offset].target_location()); + CHECK_EQUAL(m_retry_info_list.size() + 1, m_context.request_results().size() - m_context_results_offset); + for (size_t i = 0; i < m_retry_info_list.size(); ++i) + { + CHECK(m_retry_info_list[i].target_location() == m_context.request_results()[m_context_results_offset + i + 1].target_location()); + + // This check assumes that datetime::to_interval() returns the time in microseconds/10 + std::chrono::microseconds interval((m_context.request_results()[m_context_results_offset + i + 1].start_time().to_interval() - m_context.request_results()[m_context_results_offset + i].end_time().to_interval()) / 10); + CHECK(m_retry_info_list[i].retry_interval() < interval); + } + } + + wa::storage::retry_policy policy() const + { + return m_policy; + } + +private: + + std::vector m_retry_info_list; + wa::storage::storage_uri m_uri; + wa::storage::storage_location m_initial_location; + wa::storage::retry_policy m_policy; + wa::storage::operation_context m_context; + int m_context_results_offset; + int m_request_counter; + bool m_error; +}; + +void test_container_download_attributes(wa::storage::cloud_blob_container& container, wa::storage::location_mode mode, wa::storage::storage_location initial_location, wa::storage::operation_context context, const std::vector& expected_retry_context_list, const std::vector& retry_info_list) +{ + multi_location_test_helper helper(container.service_client().base_uri(), initial_location, context, expected_retry_context_list, retry_info_list); + wa::storage::blob_request_options options; + options.set_location_mode(mode); + options.set_retry_policy(helper.policy()); + CHECK_THROW(container.download_attributes(wa::storage::access_condition(), options, context), wa::storage::storage_exception); +} + +static wa::storage::retry_info create_fake_retry_info(wa::storage::storage_location target_location, wa::storage::location_mode updated_mode, std::chrono::milliseconds interval) +{ + wa::storage::retry_context dummy_context(0, wa::storage::request_result(), target_location, updated_mode); + wa::storage::retry_info fake_info(dummy_context); + fake_info.set_retry_interval(interval); + return fake_info; +} + +void add_updated_location_mode_list(std::vector& expected_retry_context_list, std::vector& retry_info_list) +{ + retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::secondary_only, std::chrono::seconds(4))); + expected_retry_context_list.push_back(wa::storage::retry_context(0, wa::storage::request_result(), wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_only)); + retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::primary_only, std::chrono::seconds(1))); + expected_retry_context_list.push_back(wa::storage::retry_context(0, wa::storage::request_result(), wa::storage::storage_location::primary, wa::storage::location_mode::primary_only)); + retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::primary_then_secondary, std::chrono::seconds(1))); + expected_retry_context_list.push_back(wa::storage::retry_context(0, wa::storage::request_result(), wa::storage::storage_location::primary, wa::storage::location_mode::primary_then_secondary)); + retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_then_primary, std::chrono::seconds(1))); + expected_retry_context_list.push_back(wa::storage::retry_context(0, wa::storage::request_result(), wa::storage::storage_location::primary, wa::storage::location_mode::secondary_then_primary)); +} + +void test_multi_location_retries(std::function&, const std::vector&)> test) +{ + { + std::vector expected_retry_context_list; + std::vector retry_info_list; + expected_retry_context_list.push_back(wa::storage::retry_context(0, wa::storage::request_result(), wa::storage::storage_location::primary, wa::storage::location_mode::primary_only)); + test(wa::storage::location_mode::primary_only, wa::storage::storage_location::primary, expected_retry_context_list, retry_info_list); + } + + { + std::vector expected_retry_context_list; + std::vector retry_info_list; + expected_retry_context_list.push_back(wa::storage::retry_context(0, wa::storage::request_result(), wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_only)); + test(wa::storage::location_mode::secondary_only, wa::storage::storage_location::secondary, expected_retry_context_list, retry_info_list); + } + + { + std::vector expected_retry_context_list; + std::vector retry_info_list; + expected_retry_context_list.push_back(wa::storage::retry_context(0, wa::storage::request_result(), wa::storage::storage_location::secondary, wa::storage::location_mode::primary_then_secondary)); + test(wa::storage::location_mode::primary_then_secondary, wa::storage::storage_location::primary, expected_retry_context_list, retry_info_list); + } + + { + std::vector expected_retry_context_list; + std::vector retry_info_list; + expected_retry_context_list.push_back(wa::storage::retry_context(0, wa::storage::request_result(), wa::storage::storage_location::primary, wa::storage::location_mode::secondary_then_primary)); + test(wa::storage::location_mode::secondary_then_primary, wa::storage::storage_location::secondary, expected_retry_context_list, retry_info_list); + } + + { + std::vector expected_retry_context_list; + std::vector retry_info_list; + expected_retry_context_list.push_back(wa::storage::retry_context(0, wa::storage::request_result(), wa::storage::storage_location::primary, wa::storage::location_mode::primary_only)); + retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::primary_only, std::chrono::seconds(6))); + expected_retry_context_list.push_back(wa::storage::retry_context(0, wa::storage::request_result(), wa::storage::storage_location::primary, wa::storage::location_mode::primary_only)); + retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::primary_only, std::chrono::seconds(1))); + expected_retry_context_list.push_back(wa::storage::retry_context(0, wa::storage::request_result(), wa::storage::storage_location::primary, wa::storage::location_mode::primary_only)); + add_updated_location_mode_list(expected_retry_context_list, retry_info_list); + test(wa::storage::location_mode::primary_only, wa::storage::storage_location::primary, expected_retry_context_list, retry_info_list); + } + + { + std::vector expected_retry_context_list; + std::vector retry_info_list; + expected_retry_context_list.push_back(wa::storage::retry_context(0, wa::storage::request_result(), wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_only)); + retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_only, std::chrono::seconds(6))); + expected_retry_context_list.push_back(wa::storage::retry_context(0, wa::storage::request_result(), wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_only)); + retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_only, std::chrono::seconds(1))); + expected_retry_context_list.push_back(wa::storage::retry_context(0, wa::storage::request_result(), wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_only)); + add_updated_location_mode_list(expected_retry_context_list, retry_info_list); + test(wa::storage::location_mode::secondary_only, wa::storage::storage_location::secondary, expected_retry_context_list, retry_info_list); + } + + { + std::vector expected_retry_context_list; + std::vector retry_info_list; + expected_retry_context_list.push_back(wa::storage::retry_context(0, wa::storage::request_result(), wa::storage::storage_location::secondary, wa::storage::location_mode::primary_then_secondary)); + retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::primary_then_secondary, std::chrono::seconds(6))); + expected_retry_context_list.push_back(wa::storage::retry_context(0, wa::storage::request_result(), wa::storage::storage_location::primary, wa::storage::location_mode::primary_then_secondary)); + retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::primary_then_secondary, std::chrono::seconds(1))); + expected_retry_context_list.push_back(wa::storage::retry_context(0, wa::storage::request_result(), wa::storage::storage_location::secondary, wa::storage::location_mode::primary_then_secondary)); + add_updated_location_mode_list(expected_retry_context_list, retry_info_list); + test(wa::storage::location_mode::primary_then_secondary, wa::storage::storage_location::primary, expected_retry_context_list, retry_info_list); + } + + { + std::vector expected_retry_context_list; + std::vector retry_info_list; + expected_retry_context_list.push_back(wa::storage::retry_context(0, wa::storage::request_result(), wa::storage::storage_location::primary, wa::storage::location_mode::secondary_then_primary)); + retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::secondary_then_primary, std::chrono::seconds(6))); + expected_retry_context_list.push_back(wa::storage::retry_context(0, wa::storage::request_result(), wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_then_primary)); + retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_then_primary, std::chrono::seconds(1))); + expected_retry_context_list.push_back(wa::storage::retry_context(0, wa::storage::request_result(), wa::storage::storage_location::primary, wa::storage::location_mode::secondary_then_primary)); + add_updated_location_mode_list(expected_retry_context_list, retry_info_list); + test(wa::storage::location_mode::secondary_then_primary, wa::storage::storage_location::secondary, expected_retry_context_list, retry_info_list); + } +} + +SUITE(Core) +{ + TEST_FIXTURE(container_test_base, blob_multi_location_retries) + { + test_multi_location_retries(std::bind(test_container_download_attributes, m_container, std::placeholders::_1, std::placeholders::_2, m_context, std::placeholders::_3, std::placeholders::_4)); + } + + TEST_FIXTURE(blob_test_base, location_lock) + { + for (int i = 0; i < 2; i++) + { + auto index = utility::conversions::print_string(i); + auto blob = m_container.get_block_blob_reference(U("blockblob") + index); + + blob.upload_text(blob.name(), wa::storage::access_condition(), wa::storage::blob_request_options(), m_context); + } + + wa::storage::blob_request_options options; + options.set_retry_policy(wa::storage::no_retry_policy()); + + options.set_location_mode(wa::storage::location_mode::primary_only); + auto results = m_container.list_blobs_segmented(utility::string_t(), true, wa::storage::blob_listing_includes(), 1, wa::storage::blob_continuation_token(), options, m_context); + CHECK_EQUAL(4, m_context.request_results().size()); + + auto token = results.continuation_token(); + options.set_location_mode(wa::storage::location_mode::secondary_only); + CHECK_THROW(m_container.list_blobs_segmented(utility::string_t(), true, wa::storage::blob_listing_includes(), 1, token, options, m_context), wa::storage::storage_exception); + CHECK_EQUAL(4, m_context.request_results().size()); + + auto container = m_client.get_container_reference(m_container.name() + U("-missing")); + + token.set_target_location(wa::storage::storage_location::secondary); + CHECK_THROW(container.list_blobs_segmented(utility::string_t(), true, wa::storage::blob_listing_includes(), 1, token, options, m_context), wa::storage::storage_exception); + CHECK_EQUAL(5, m_context.request_results().size()); + + options.set_location_mode(wa::storage::location_mode::primary_only); + CHECK_THROW(container.list_blobs_segmented(utility::string_t(), true, wa::storage::blob_listing_includes(), 1, token, options, m_context), wa::storage::storage_exception); + CHECK_EQUAL(5, m_context.request_results().size()); + } +} diff --git a/Microsoft.WindowsAzure.Storage/tests/retry_policy_test.cpp b/Microsoft.WindowsAzure.Storage/tests/retry_policy_test.cpp new file mode 100644 index 00000000..54eca6c5 --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/retry_policy_test.cpp @@ -0,0 +1,330 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "test_base.h" +#include "check_macros.h" + +wa::storage::storage_location get_initial_location(wa::storage::location_mode mode) +{ + switch (mode) + { + case wa::storage::location_mode::primary_only: + case wa::storage::location_mode::primary_then_secondary: + return wa::storage::storage_location::primary; + + case wa::storage::location_mode::secondary_only: + case wa::storage::location_mode::secondary_then_primary: + return wa::storage::storage_location::secondary; + + default: + throw std::invalid_argument("mode"); + } +} + +wa::storage::storage_location get_next_location(wa::storage::location_mode mode, wa::storage::storage_location current_location) +{ + switch (mode) + { + case wa::storage::location_mode::primary_only: + return wa::storage::storage_location::primary; + + case wa::storage::location_mode::secondary_only: + return wa::storage::storage_location::secondary; + + case wa::storage::location_mode::primary_then_secondary: + case wa::storage::location_mode::secondary_then_primary: + switch (current_location) + { + case wa::storage::storage_location::primary: + return wa::storage::storage_location::secondary; + + case wa::storage::storage_location::secondary: + return wa::storage::storage_location::primary; + + default: + throw std::invalid_argument("current_location"); + } + + default: + throw std::invalid_argument("mode"); + } +} + +void verify_retry_results(wa::storage::retry_policy policy, web::http::status_code primary_status_code, web::http::status_code secondary_status_code, wa::storage::location_mode mode, std::function allowed_delta, std::vector expected_retry_info_list) +{ + auto initial_location = get_initial_location(mode); + auto next_location = get_next_location(mode, initial_location); + + wa::storage::operation_context op_context; + wa::storage::request_result result(utility::datetime::utc_now(), + initial_location, + web::http::http_response(initial_location == wa::storage::storage_location::secondary ? secondary_status_code : primary_status_code), + false); + + int retry_count = 0; + for (auto iter = expected_retry_info_list.cbegin(); iter != expected_retry_info_list.cend(); ++iter) + { + auto retry_info = policy.evaluate(wa::storage::retry_context(retry_count++, result, next_location, mode), op_context); + + CHECK(retry_info.should_retry()); + CHECK(iter->target_location() == retry_info.target_location()); + CHECK(iter->updated_location_mode() == retry_info.updated_location_mode()); + CHECK_CLOSE(iter->retry_interval().count(), retry_info.retry_interval().count(), allowed_delta(retry_count).count()); + + std::this_thread::sleep_for(retry_info.retry_interval()); + + result = wa::storage::request_result(utility::datetime::utc_now(), + retry_info.target_location(), + web::http::http_response(retry_info.target_location() == wa::storage::storage_location::secondary ? secondary_status_code : primary_status_code), + false); + mode = retry_info.updated_location_mode(); + next_location = get_next_location(mode, next_location); + } + + auto retry_info = policy.evaluate(wa::storage::retry_context(retry_count++, result, next_location, mode), op_context); + CHECK(!retry_info.should_retry()); +} + +static wa::storage::retry_info create_fake_retry_info(wa::storage::storage_location target_location, wa::storage::location_mode updated_mode, std::chrono::milliseconds interval) +{ + wa::storage::retry_context dummy_context(0, wa::storage::request_result(), target_location, updated_mode); + wa::storage::retry_info fake_info(dummy_context); + fake_info.set_retry_interval(interval); + return fake_info; +} + +SUITE(Core) +{ + TEST(no_retry_results) + { + auto allowed_delta = [] (int retry_count) -> std::chrono::milliseconds + { + return std::chrono::milliseconds(); + }; + + // Both locations return InternalError + + { + wa::storage::no_retry_policy policy; + std::vector expected_retry_info_list; + verify_retry_results(policy, web::http::status_codes::InternalError, web::http::status_codes::InternalError, wa::storage::location_mode::primary_only, allowed_delta, expected_retry_info_list); + } + + { + wa::storage::no_retry_policy policy; + std::vector expected_retry_info_list; + verify_retry_results(policy, web::http::status_codes::InternalError, web::http::status_codes::InternalError, wa::storage::location_mode::secondary_only, allowed_delta, expected_retry_info_list); + } + + { + wa::storage::no_retry_policy policy; + std::vector expected_retry_info_list; + verify_retry_results(policy, web::http::status_codes::InternalError, web::http::status_codes::InternalError, wa::storage::location_mode::primary_then_secondary, allowed_delta, expected_retry_info_list); + } + + { + wa::storage::no_retry_policy policy; + std::vector expected_retry_info_list; + verify_retry_results(policy, web::http::status_codes::InternalError, web::http::status_codes::InternalError, wa::storage::location_mode::secondary_then_primary, allowed_delta, expected_retry_info_list); + } + + // Primary location returns InternalError, while secondary location returns NotFound + + { + wa::storage::no_retry_policy policy; + std::vector expected_retry_info_list; + verify_retry_results(policy, web::http::status_codes::InternalError, web::http::status_codes::NotFound, wa::storage::location_mode::secondary_only, allowed_delta, expected_retry_info_list); + } + + { + wa::storage::no_retry_policy policy; + std::vector expected_retry_info_list; + verify_retry_results(policy, web::http::status_codes::InternalError, web::http::status_codes::NotFound, wa::storage::location_mode::primary_then_secondary, allowed_delta, expected_retry_info_list); + } + + { + wa::storage::no_retry_policy policy; + std::vector expected_retry_info_list; + verify_retry_results(policy, web::http::status_codes::InternalError, web::http::status_codes::NotFound, wa::storage::location_mode::secondary_then_primary, allowed_delta, expected_retry_info_list); + } + } + + TEST(exponential_retry_results) + { + auto allowed_delta = [] (int retry_count) -> std::chrono::milliseconds + { + return std::chrono::milliseconds(static_cast(1000 * ((std::pow(2, retry_count) - 1) * 0.2 + 0.1))); + }; + + // Both locations return InternalError + + { + wa::storage::exponential_retry_policy policy(std::chrono::seconds(1), 4); + std::vector expected_retry_info_list; + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::primary_only, std::chrono::seconds(3))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::primary_only, std::chrono::seconds(4))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::primary_only, std::chrono::seconds(6))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::primary_only, std::chrono::seconds(10))); + verify_retry_results(policy, web::http::status_codes::InternalError, web::http::status_codes::InternalError, wa::storage::location_mode::primary_only, allowed_delta, expected_retry_info_list); + } + + { + wa::storage::exponential_retry_policy policy(std::chrono::seconds(1), 4); + std::vector expected_retry_info_list; + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_only, std::chrono::seconds(3))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_only, std::chrono::seconds(4))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_only, std::chrono::seconds(6))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_only, std::chrono::seconds(10))); + verify_retry_results(policy, web::http::status_codes::InternalError, web::http::status_codes::InternalError, wa::storage::location_mode::secondary_only, allowed_delta, expected_retry_info_list); + } + + { + wa::storage::exponential_retry_policy policy(std::chrono::seconds(1), 4); + std::vector expected_retry_info_list; + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::primary_then_secondary, std::chrono::seconds(0))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::primary_then_secondary, std::chrono::seconds(4))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::primary_then_secondary, std::chrono::seconds(2))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::primary_then_secondary, std::chrono::seconds(8))); + verify_retry_results(policy, web::http::status_codes::InternalError, web::http::status_codes::InternalError, wa::storage::location_mode::primary_then_secondary, allowed_delta, expected_retry_info_list); + } + + { + wa::storage::exponential_retry_policy policy(std::chrono::seconds(1), 4); + std::vector expected_retry_info_list; + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::secondary_then_primary, std::chrono::seconds(0))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_then_primary, std::chrono::seconds(4))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::secondary_then_primary, std::chrono::seconds(2))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_then_primary, std::chrono::seconds(8))); + verify_retry_results(policy, web::http::status_codes::InternalError, web::http::status_codes::InternalError, wa::storage::location_mode::secondary_then_primary, allowed_delta, expected_retry_info_list); + } + + // Primary location returns InternalError, while secondary location returns NotFound + + { + wa::storage::exponential_retry_policy policy(std::chrono::seconds(1), 4); + std::vector expected_retry_info_list; + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_only, std::chrono::seconds(3))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_only, std::chrono::seconds(4))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_only, std::chrono::seconds(6))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_only, std::chrono::seconds(10))); + verify_retry_results(policy, web::http::status_codes::InternalError, web::http::status_codes::NotFound, wa::storage::location_mode::secondary_only, allowed_delta, expected_retry_info_list); + } + + { + wa::storage::exponential_retry_policy policy(std::chrono::seconds(1), 4); + std::vector expected_retry_info_list; + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::primary_then_secondary, std::chrono::seconds(0))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::primary_only, std::chrono::seconds(4))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::primary_only, std::chrono::seconds(6))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::primary_only, std::chrono::seconds(10))); + verify_retry_results(policy, web::http::status_codes::InternalError, web::http::status_codes::NotFound, wa::storage::location_mode::primary_then_secondary, allowed_delta, expected_retry_info_list); + } + + { + wa::storage::exponential_retry_policy policy(std::chrono::seconds(1), 4); + std::vector expected_retry_info_list; + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::primary_only, std::chrono::seconds(0))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::primary_only, std::chrono::seconds(4))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::primary_only, std::chrono::seconds(6))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::primary_only, std::chrono::seconds(10))); + verify_retry_results(policy, web::http::status_codes::InternalError, web::http::status_codes::NotFound, wa::storage::location_mode::secondary_then_primary, allowed_delta, expected_retry_info_list); + } + } + + TEST(linear_retry_results) + { + auto allowed_delta = [] (int retry_count) -> std::chrono::milliseconds + { + return std::chrono::milliseconds(100); + }; + + // Both locations return InternalError + + { + wa::storage::linear_retry_policy policy(std::chrono::seconds(2), 4); + std::vector expected_retry_info_list; + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::primary_only, std::chrono::seconds(2))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::primary_only, std::chrono::seconds(2))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::primary_only, std::chrono::seconds(2))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::primary_only, std::chrono::seconds(2))); + verify_retry_results(policy, web::http::status_codes::InternalError, web::http::status_codes::InternalError, wa::storage::location_mode::primary_only, allowed_delta, expected_retry_info_list); + } + + { + wa::storage::linear_retry_policy policy(std::chrono::seconds(2), 4); + std::vector expected_retry_info_list; + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_only, std::chrono::seconds(2))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_only, std::chrono::seconds(2))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_only, std::chrono::seconds(2))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_only, std::chrono::seconds(2))); + verify_retry_results(policy, web::http::status_codes::InternalError, web::http::status_codes::InternalError, wa::storage::location_mode::secondary_only, allowed_delta, expected_retry_info_list); + } + + { + wa::storage::linear_retry_policy policy(std::chrono::seconds(2), 4); + std::vector expected_retry_info_list; + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::primary_then_secondary, std::chrono::seconds(0))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::primary_then_secondary, std::chrono::seconds(2))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::primary_then_secondary, std::chrono::seconds(0))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::primary_then_secondary, std::chrono::seconds(2))); + verify_retry_results(policy, web::http::status_codes::InternalError, web::http::status_codes::InternalError, wa::storage::location_mode::primary_then_secondary, allowed_delta, expected_retry_info_list); + } + + { + wa::storage::linear_retry_policy policy(std::chrono::seconds(2), 4); + std::vector expected_retry_info_list; + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::secondary_then_primary, std::chrono::seconds(0))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_then_primary, std::chrono::seconds(2))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::secondary_then_primary, std::chrono::seconds(0))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_then_primary, std::chrono::seconds(2))); + verify_retry_results(policy, web::http::status_codes::InternalError, web::http::status_codes::InternalError, wa::storage::location_mode::secondary_then_primary, allowed_delta, expected_retry_info_list); + } + + // Primary location returns InternalError, while secondary location returns NotFound + + { + wa::storage::linear_retry_policy policy(std::chrono::seconds(2), 4); + std::vector expected_retry_info_list; + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_only, std::chrono::seconds(2))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_only, std::chrono::seconds(2))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_only, std::chrono::seconds(2))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::secondary_only, std::chrono::seconds(2))); + verify_retry_results(policy, web::http::status_codes::InternalError, web::http::status_codes::NotFound, wa::storage::location_mode::secondary_only, allowed_delta, expected_retry_info_list); + } + + { + wa::storage::linear_retry_policy policy(std::chrono::seconds(2), 4); + std::vector expected_retry_info_list; + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::secondary, wa::storage::location_mode::primary_then_secondary, std::chrono::seconds(0))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::primary_only, std::chrono::seconds(2))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::primary_only, std::chrono::seconds(2))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::primary_only, std::chrono::seconds(2))); + verify_retry_results(policy, web::http::status_codes::InternalError, web::http::status_codes::NotFound, wa::storage::location_mode::primary_then_secondary, allowed_delta, expected_retry_info_list); + } + + { + wa::storage::linear_retry_policy policy(std::chrono::seconds(2), 4); + std::vector expected_retry_info_list; + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::primary_only, std::chrono::seconds(0))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::primary_only, std::chrono::seconds(2))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::primary_only, std::chrono::seconds(2))); + expected_retry_info_list.push_back(create_fake_retry_info(wa::storage::storage_location::primary, wa::storage::location_mode::primary_only, std::chrono::seconds(2))); + verify_retry_results(policy, web::http::status_codes::InternalError, web::http::status_codes::NotFound, wa::storage::location_mode::secondary_then_primary, allowed_delta, expected_retry_info_list); + } + } +} diff --git a/Microsoft.WindowsAzure.Storage/tests/service_properties_test.cpp b/Microsoft.WindowsAzure.Storage/tests/service_properties_test.cpp new file mode 100644 index 00000000..b20bf9c6 --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/service_properties_test.cpp @@ -0,0 +1,225 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "test_base.h" +#include "check_macros.h" + +#include "was/blob.h" +#include "was/queue.h" +#include "was/table.h" + +void add_metrics_1(wa::storage::service_properties::metrics_properties& metrics) +{ + metrics = wa::storage::service_properties::metrics_properties(); + metrics.set_version(U("1.0")); + metrics.set_enabled(true); + metrics.set_include_apis(false); + metrics.set_retention_policy_enabled(true); + metrics.set_retention_days(5); +} + +void add_metrics_2(wa::storage::service_properties::metrics_properties& metrics) +{ + metrics = wa::storage::service_properties::metrics_properties(); + metrics.set_version(U("1.0")); + metrics.set_enabled(true); + metrics.set_include_apis(true); + metrics.set_retention_policy_enabled(false); +} + +void add_logging_1(wa::storage::service_properties::logging_properties& logging) +{ + logging = wa::storage::service_properties::logging_properties(); + logging.set_version(U("1.0")); + logging.set_read_enabled(true); + logging.set_retention_policy_enabled(true); + logging.set_retention_days(20); +} + +void add_logging_2(wa::storage::service_properties::logging_properties& logging) +{ + logging = wa::storage::service_properties::logging_properties(); + logging.set_version(U("1.0")); + logging.set_write_enabled(true); + logging.set_delete_enabled(true); + logging.set_retention_policy_enabled(false); +} + +void add_cors_rule_1(std::vector& cors_rules) +{ + wa::storage::service_properties::cors_rule rule; + rule.allowed_headers().push_back(U("x-ms-meta-data*")); + rule.allowed_headers().push_back(U("x-ms-meta-target*")); + rule.allowed_origins().push_back(U("www.ab.com")); + rule.allowed_origins().push_back(U("www.bc.com")); + rule.allowed_methods().push_back(web::http::methods::GET); + rule.allowed_methods().push_back(web::http::methods::PUT); + rule.exposed_headers().push_back(U("x-ms-meta-source*")); + rule.exposed_headers().push_back(U("x-ms-meta-test*")); + rule.set_max_age(std::chrono::seconds(5)); + cors_rules.push_back(rule); +} + +void add_cors_rule_2(std::vector& cors_rules) +{ + wa::storage::service_properties::cors_rule rule; + rule.allowed_headers().push_back(U("x-ms-meta-ab*")); + rule.allowed_origins().push_back(U("*")); + rule.allowed_methods().push_back(web::http::methods::HEAD); + rule.exposed_headers().push_back(U("x-ms-meta-abc*")); + rule.set_max_age(std::chrono::seconds(25)); + cors_rules.push_back(rule); +} + +void check_service_properties(const wa::storage::service_properties& a, const wa::storage::service_properties& b) +{ + CHECK_UTF8_EQUAL(a.logging().version(), b.logging().version()); + CHECK_EQUAL(a.logging().delete_enabled(), b.logging().delete_enabled()); + CHECK_EQUAL(a.logging().read_enabled(), b.logging().read_enabled()); + CHECK_EQUAL(a.logging().write_enabled(), b.logging().write_enabled()); + CHECK_EQUAL(a.logging().retention_policy_enabled(), b.logging().retention_policy_enabled()); + CHECK_EQUAL(a.logging().retention_days(), b.logging().retention_days()); + + CHECK_UTF8_EQUAL(a.hour_metrics().version(), b.hour_metrics().version()); + CHECK_EQUAL(a.hour_metrics().enabled(), b.hour_metrics().enabled()); + CHECK_EQUAL(a.hour_metrics().include_apis(), b.hour_metrics().include_apis()); + CHECK_EQUAL(a.hour_metrics().retention_policy_enabled(), b.hour_metrics().retention_policy_enabled()); + CHECK_EQUAL(a.hour_metrics().retention_days(), b.hour_metrics().retention_days()); + + CHECK_UTF8_EQUAL(a.minute_metrics().version(), b.minute_metrics().version()); + CHECK_EQUAL(a.minute_metrics().enabled(), b.minute_metrics().enabled()); + CHECK_EQUAL(a.minute_metrics().include_apis(), b.minute_metrics().include_apis()); + CHECK_EQUAL(a.minute_metrics().retention_policy_enabled(), b.minute_metrics().retention_policy_enabled()); + CHECK_EQUAL(a.minute_metrics().retention_days(), b.minute_metrics().retention_days()); + + auto a_iter = a.cors().cbegin(); + auto b_iter = b.cors().cbegin(); + for (; a_iter != a.cors().cend() && b_iter != b.cors().cend(); ++a_iter, ++b_iter) + { + CHECK(std::equal(a_iter->allowed_headers().cbegin(), a_iter->allowed_headers().cend(), b_iter->allowed_headers().cbegin())); + CHECK(std::equal(a_iter->allowed_methods().cbegin(), a_iter->allowed_methods().cend(), b_iter->allowed_methods().cbegin())); + CHECK(std::equal(a_iter->allowed_origins().cbegin(), a_iter->allowed_origins().cend(), b_iter->allowed_origins().cbegin())); + CHECK(std::equal(a_iter->exposed_headers().cbegin(), a_iter->exposed_headers().cend(), b_iter->exposed_headers().cbegin())); + CHECK_EQUAL(a_iter->max_age().count(), b_iter->max_age().count()); + } + + CHECK(a_iter == a.cors().cend()); + CHECK(b_iter == b.cors().cend()); + + CHECK_UTF8_EQUAL(a.default_service_version(), b.default_service_version()); +} + +template +void test_service_properties(const Client& client, const Options& options, wa::storage::operation_context context, bool default_version_supported) +{ + wa::storage::service_properties props; + add_logging_1(props.logging()); + add_metrics_1(props.hour_metrics()); + add_metrics_2(props.minute_metrics()); + add_cors_rule_1(props.cors()); + add_cors_rule_2(props.cors()); + + wa::storage::service_properties temp_props; + add_logging_2(temp_props.logging()); + add_metrics_2(temp_props.hour_metrics()); + add_metrics_1(temp_props.minute_metrics()); + add_cors_rule_2(temp_props.cors()); + + client.upload_service_properties(props, wa::storage::service_properties_includes::all(), options, context); + check_service_properties(props, client.download_service_properties(options, context)); + + { + wa::storage::service_properties_includes includes; + includes.set_logging(true); + client.upload_service_properties(temp_props, includes, options, context); + add_logging_2(props.logging()); + check_service_properties(props, client.download_service_properties(options, context)); + add_logging_1(temp_props.logging()); + } + + { + wa::storage::service_properties_includes includes; + includes.set_hour_metrics(true); + client.upload_service_properties(temp_props, includes, options, context); + add_metrics_2(props.hour_metrics()); + check_service_properties(props, client.download_service_properties(options, context)); + add_metrics_1(temp_props.hour_metrics()); + } + + { + wa::storage::service_properties_includes includes; + includes.set_minute_metrics(true); + client.upload_service_properties(temp_props, includes, options, context); + add_metrics_1(props.minute_metrics()); + check_service_properties(props, client.download_service_properties(options, context)); + add_metrics_2(temp_props.minute_metrics()); + } + + { + wa::storage::service_properties_includes includes; + includes.set_cors(true); + client.upload_service_properties(temp_props, includes, options, context); + props.cors().erase(props.cors().begin()); + check_service_properties(props, client.download_service_properties(options, context)); + temp_props.cors().clear(); + } + + { + wa::storage::service_properties_includes includes; + includes.set_hour_metrics(true); + includes.set_minute_metrics(true); + includes.set_cors(true); + client.upload_service_properties(temp_props, includes, options, context); + add_metrics_1(props.hour_metrics()); + add_metrics_2(props.minute_metrics()); + props.cors().clear(); + check_service_properties(props, client.download_service_properties(options, context)); + } + + props.set_default_service_version(U("2013-08-15")); + if (default_version_supported) + { + client.upload_service_properties(props, wa::storage::service_properties_includes::all(), options, context); + check_service_properties(props, client.download_service_properties(options, context)); + } + else + { + CHECK_THROW(client.upload_service_properties(props, wa::storage::service_properties_includes::all(), options, context), wa::storage::storage_exception); + } +} + +SUITE(Client) +{ + TEST_FIXTURE(test_base, blob_service_properties) + { + auto client = test_config::instance().account().create_cloud_blob_client(); + test_service_properties(client, wa::storage::blob_request_options(), m_context, true); + } + + TEST_FIXTURE(test_base, queue_service_properties) + { + auto client = test_config::instance().account().create_cloud_queue_client(); + test_service_properties(client, wa::storage::queue_request_options(), m_context, false); + } + + TEST_FIXTURE(test_base, table_service_properties) + { + auto client = test_config::instance().account().create_cloud_table_client(); + test_service_properties(client, wa::storage::table_request_options(), m_context, false); + } +} diff --git a/Microsoft.WindowsAzure.Storage/tests/stdafx.cpp b/Microsoft.WindowsAzure.Storage/tests/stdafx.cpp new file mode 100644 index 00000000..09ac5930 --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/stdafx.cpp @@ -0,0 +1,22 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +// stdafx.cpp : source file that includes just the standard includes +// Microsoft.WindowsAzure.Storage.UnitTests.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" diff --git a/Microsoft.WindowsAzure.Storage/tests/stdafx.h b/Microsoft.WindowsAzure.Storage/tests/stdafx.h new file mode 100644 index 00000000..d8909620 --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/stdafx.h @@ -0,0 +1,34 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +// This is required to support enough number of arguments in VC11, especially for std::bind +#define _VARIADIC_MAX 8 + +#include "UnitTest++.h" +#include "TestReporterStdout.h" + +#include +#include diff --git a/Microsoft.WindowsAzure.Storage/tests/targetver.h b/Microsoft.WindowsAzure.Storage/tests/targetver.h new file mode 100644 index 00000000..9a67c656 --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/targetver.h @@ -0,0 +1,25 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/Microsoft.WindowsAzure.Storage/tests/test_base.cpp b/Microsoft.WindowsAzure.Storage/tests/test_base.cpp new file mode 100644 index 00000000..77d95376 --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/test_base.cpp @@ -0,0 +1,62 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#include "stdafx.h" + +#include "test_base.h" +#include "cpprest/json.h" + +test_config::test_config() +{ + utility::ifstream_t config_file; + config_file.open("test_configurations.json"); + + web::json::value config; + config_file >> config; + + auto target_name = config[U("target")].as_string(); + auto tenants = config[U("tenants")]; + + for (auto iter = tenants.cbegin(); iter != tenants.cend(); ++iter) + { + if (iter->second[U("name")].as_string() == target_name) + { + if (!iter->second.has_field(U("connection_string"))) + { + wa::storage::storage_credentials credentials(iter->second[U("account_name")].as_string(), iter->second[U("account_key")].as_string()); + wa::storage::storage_uri blob_uri(iter->second[U("blob_primary_endpoint")].as_string(), iter->second[U("blob_secondary_endpoint")].as_string()); + wa::storage::storage_uri queue_uri(iter->second[U("queue_primary_endpoint")].as_string(), iter->second[U("queue_secondary_endpoint")].as_string()); + wa::storage::storage_uri table_uri(iter->second[U("table_primary_endpoint")].as_string(), iter->second[U("table_secondary_endpoint")].as_string()); + m_account = wa::storage::cloud_storage_account(credentials, blob_uri, queue_uri, table_uri); + } + else + { + auto connection_string = iter->second[U("connection_string")]; + m_account = wa::storage::cloud_storage_account::parse(connection_string.as_string()); + } + + break; + } + } +} + +void test_base::print_client_request_id(const wa::storage::operation_context& context, const utility::string_t& purpose) +{ + std::string suite_name(UnitTest::CurrentTest::Details()->suiteName); + std::string test_name(UnitTest::CurrentTest::Details()->testName); + ucout << utility::conversions::to_string_t(suite_name) << U("::") << utility::conversions::to_string_t(test_name) << U(": ") << purpose << U(" client request ID: ") << context.client_request_id() << std::endl; +} diff --git a/Microsoft.WindowsAzure.Storage/tests/test_base.h b/Microsoft.WindowsAzure.Storage/tests/test_base.h new file mode 100644 index 00000000..675e611b --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/test_base.h @@ -0,0 +1,63 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#pragma once + +#include "was/common.h" +#include "was/storage_account.h" + +class test_config +{ +public: + + static const test_config& instance() + { + static test_config singleton_instance; + return singleton_instance; + } + + const wa::storage::cloud_storage_account& account() const + { + return m_account; + } + +private: + + test_config(); + + wa::storage::cloud_storage_account m_account; +}; + +class test_base +{ +public: + + test_base() + { + print_client_request_id(m_context, U("test fixture")); + } + + ~test_base() + { + } + +protected: + + static void print_client_request_id(const wa::storage::operation_context& context, const utility::string_t& purpose); + + wa::storage::operation_context m_context; +}; diff --git a/Microsoft.WindowsAzure.Storage/tests/test_configurations.json b/Microsoft.WindowsAzure.Storage/tests/test_configurations.json new file mode 100644 index 00000000..a7c1173f --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/test_configurations.json @@ -0,0 +1,15 @@ +{ + "target": "production", + "tenants": [ + { + "name": "devstore", + "type": "devstore", + "connection_string": "UseDevelopmentStorage=true" + }, + { + "name": "production", + "type": "cloud", + "connection_string": "DefaultEndpointsProtocol=https;AccountName=myaccountname;AccountKey=myaccountkey" + } + ] +} \ No newline at end of file diff --git a/Microsoft.WindowsAzure.Storage/tests/test_helper.cpp b/Microsoft.WindowsAzure.Storage/tests/test_helper.cpp new file mode 100644 index 00000000..db37368e --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/test_helper.cpp @@ -0,0 +1,166 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#include "stdafx.h" +#include "test_helper.h" +#include "test_base.h" + +utility::string_t object_name_prefix = utility::string_t(U("nativeclientlibraryunittest")); +utility::string_t table_type_name = utility::string_t(U("table")); +utility::string_t queue_type_name = utility::string_t(U("queue")); +bool is_random_initialized = false; + +/* +utility::string_t get_partition_key(int partition_number) +{ + utility::ostringstream_t result; + result << partition_number; + return result.str(); +} +*/ + +utility::string_t get_string(utility::char_t value1, utility::char_t value2) +{ + utility::ostringstream_t result; + result << value1 << value2; + return result.str(); +} + +void initialize_random() +{ + if (!is_random_initialized) + { + srand((unsigned int)time(NULL)); + is_random_initialized = true; + } +} + +bool get_random_boolean() +{ + initialize_random(); + return (rand() & 0x1) == 0; +} + +int32_t get_random_int32() +{ + initialize_random(); + return (int32_t)rand() << 16 | (int32_t)rand(); +} + +int64_t get_random_int64() +{ + initialize_random(); + return (int64_t)rand() << 48 | (int64_t)rand() << 32 | (int64_t)rand() << 16 | (int64_t)rand(); +} + +double get_random_double() +{ + initialize_random(); + return (double)rand() / RAND_MAX; +} + +utility::string_t get_random_string() +{ + initialize_random(); + const int SIZE = 10; + utility::string_t result; + result.reserve(SIZE); + for (int i = 0; i < SIZE; ++i) + { + result.push_back(U('0') + rand() % 10); + } + return result; +} + +utility::datetime get_random_datetime() +{ + initialize_random(); + return utility::datetime::utc_now() + rand(); +} + +std::vector get_random_binary_data() +{ + initialize_random(); + const int SIZE = 100; + std::vector result; + result.reserve(SIZE); + for (int i = 0; i < SIZE; ++i) + { + result.push_back(rand() % 256); + } + return result; +} + +utility::uuid get_random_guid() +{ + return utility::new_uuid(); +} + +utility::string_t get_object_name(const utility::string_t& object_type_name) +{ + utility::string_t object_name; + object_name.reserve(37U + object_type_name.size()); + object_name.append(object_name_prefix); + object_name.append(object_type_name); + object_name.append(get_random_string()); + return object_name; +} + +wa::storage::cloud_table_client get_table_client() +{ + return test_config::instance().account().create_cloud_table_client(); +} + +utility::string_t get_table_name() +{ + return get_object_name(table_type_name); +} + +wa::storage::cloud_table get_table(bool create) +{ + wa::storage::cloud_table_client client = get_table_client(); + utility::string_t table_name = get_table_name(); + wa::storage::cloud_table table = client.get_table_reference(table_name); + if (create) + { + table.create_if_not_exists(); + } + return table; +} + +wa::storage::cloud_queue_client get_queue_client() +{ + return test_config::instance().account().create_cloud_queue_client(); +} + +utility::string_t get_queue_name() +{ + return get_object_name(queue_type_name); +} + +wa::storage::cloud_queue get_queue(bool create) +{ + wa::storage::cloud_queue_client client = get_queue_client(); + utility::string_t queue_name = get_queue_name(); + wa::storage::cloud_queue queue = client.get_queue_reference(queue_name); + if (create) + { + queue.create_if_not_exists(); + } + return queue; +} + diff --git a/Microsoft.WindowsAzure.Storage/tests/test_helper.h b/Microsoft.WindowsAzure.Storage/tests/test_helper.h new file mode 100644 index 00000000..dc53b21a --- /dev/null +++ b/Microsoft.WindowsAzure.Storage/tests/test_helper.h @@ -0,0 +1,40 @@ +// ----------------------------------------------------------------------------------------- +// +// Copyright 2013 Microsoft Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------------------- + +#pragma once +#include "was/table.h" +#include "was/queue.h" + +extern utility::string_t object_name_prefix; + +utility::string_t get_string(utility::char_t value1, utility::char_t value2); +void initialize_random(); +bool get_random_boolean(); +int32_t get_random_int32(); +int64_t get_random_int64(); +double get_random_double(); +utility::string_t get_random_string(); +utility::datetime get_random_datetime(); +std::vector get_random_binary_data(); +utility::uuid get_random_guid(); +wa::storage::storage_credentials get_credentials(); +wa::storage::cloud_table_client get_table_client(); +utility::string_t get_table_name(); +wa::storage::cloud_table get_table(bool create = true); +wa::storage::cloud_queue_client get_queue_client(); +utility::string_t get_queue_name(); +wa::storage::cloud_queue get_queue(bool create = true);