TEST_FIXTURE(page_blob_test_base, page_blob_sequence_number)
    {
        m_blob.properties().set_content_language(U("tr,en"));
        m_blob.create(512, 0, azure::storage::access_condition(), azure::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(azure::storage::sequence_number::update(5), azure::storage::access_condition(), azure::storage::blob_request_options(), m_context);
        CHECK_EQUAL(5, m_blob.properties().page_blob_sequence_number());

        m_blob.set_sequence_number(azure::storage::sequence_number::maximum(7), azure::storage::access_condition(), azure::storage::blob_request_options(), m_context);
        CHECK_EQUAL(7, m_blob.properties().page_blob_sequence_number());

        m_blob.set_sequence_number(azure::storage::sequence_number::maximum(3), azure::storage::access_condition(), azure::storage::blob_request_options(), m_context);
        CHECK_EQUAL(7, m_blob.properties().page_blob_sequence_number());

        m_blob.set_sequence_number(azure::storage::sequence_number::increment(), azure::storage::access_condition(), azure::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(azure::storage::access_condition(), azure::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, azure::storage::access_condition::generate_if_sequence_number_equal_condition(8), azure::storage::blob_request_options(), m_context);
        m_blob.clear_pages(0, 512, azure::storage::access_condition::generate_if_sequence_number_less_than_or_equal_condition(8), azure::storage::blob_request_options(), m_context);
        m_blob.clear_pages(0, 512, azure::storage::access_condition::generate_if_sequence_number_less_than_or_equal_condition(9), azure::storage::blob_request_options(), m_context);
        m_blob.clear_pages(0, 512, azure::storage::access_condition::generate_if_sequence_number_less_than_condition(9), azure::storage::blob_request_options(), m_context);

        CHECK_THROW(m_blob.clear_pages(0, 512, azure::storage::access_condition::generate_if_sequence_number_equal_condition(7), azure::storage::blob_request_options(), m_context), azure::storage::storage_exception);
        CHECK_THROW(m_blob.clear_pages(0, 512, azure::storage::access_condition::generate_if_sequence_number_less_than_or_equal_condition(7), azure::storage::blob_request_options(), m_context), azure::storage::storage_exception);
        CHECK_THROW(m_blob.clear_pages(0, 512, azure::storage::access_condition::generate_if_sequence_number_less_than_or_equal_condition(7), azure::storage::blob_request_options(), m_context), azure::storage::storage_exception);
        CHECK_THROW(m_blob.clear_pages(0, 512, azure::storage::access_condition::generate_if_sequence_number_less_than_condition(7), azure::storage::blob_request_options(), m_context), azure::storage::storage_exception);
    }
void blob_service_test_base::check_blob_copy_state_equal(const azure::storage::copy_state& expected, const azure::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_equal(const azure::storage::cloud_blob& expected, const azure::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());
}
    TEST_FIXTURE(append_blob_test_base, append_blob_create_with_metadata)
    {
        m_blob.metadata()[U("key1")] = U("value1");
        m_blob.metadata()[U("key2")] = U("value2");
        m_blob.create_or_replace(azure::storage::access_condition(), azure::storage::blob_request_options(), m_context);

        auto same_blob = m_container.get_append_blob_reference(m_blob.name());
        CHECK(same_blob.metadata().empty());
        same_blob.download_attributes(azure::storage::access_condition(), azure::storage::blob_request_options(), m_context);
        CHECK_EQUAL(2U, 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(file_directory_test_base, directory_get_parent_directory_ref)
    {
        m_directory.create_if_not_exists(azure::storage::file_access_condition(), azure::storage::file_request_options(), m_context);
        auto directory_name = _XPLATSTR("directory");
        auto directory = m_directory.get_subdirectory_reference(directory_name);
        auto parent_directory = directory.get_parent_directory_reference();

        CHECK_UTF8_EQUAL(directory_name, directory.name());
        CHECK(directory.get_parent_share_reference().is_valid());
        check_equal(m_share, directory.get_parent_share_reference());

        check_equal(m_directory, parent_directory);

        CHECK(!directory.uri().primary_uri().is_empty());
        CHECK(directory.metadata().empty());
        CHECK(directory.properties().etag().empty());
        CHECK(!directory.properties().last_modified().is_initialized());

        CHECK(!parent_directory.uri().primary_uri().is_empty());
        CHECK(parent_directory.metadata().empty());
        CHECK(parent_directory.properties().etag().empty());
        CHECK(!parent_directory.properties().last_modified().is_initialized());

        // Check if get_root_directory_refence works for root directory's sub-directory.
        auto root_direcotry = m_share.get_root_directory_reference();
        directory_name = _XPLATSTR("directory");
        directory = root_direcotry.get_subdirectory_reference(directory_name);
        parent_directory = directory.get_parent_directory_reference();

        CHECK_UTF8_EQUAL(directory_name, directory.name());
        CHECK(directory.get_parent_share_reference().is_valid());
        check_equal(m_share, directory.get_parent_share_reference());

        check_equal(root_direcotry, parent_directory);

        CHECK(!directory.uri().primary_uri().is_empty());
        CHECK(directory.metadata().empty());
        CHECK(directory.properties().etag().empty());
        CHECK(!directory.properties().last_modified().is_initialized());

        CHECK(!parent_directory.uri().primary_uri().is_empty());
        CHECK(parent_directory.metadata().empty());
        CHECK(parent_directory.properties().etag().empty());
        CHECK(!parent_directory.properties().last_modified().is_initialized());

        // Check if get_root_directory_refence works for root directory.
        root_direcotry = m_share.get_root_directory_reference();
        directory = root_direcotry.get_parent_directory_reference();

        check_equal(root_direcotry, parent_directory);
    }
    TEST_FIXTURE(page_blob_test_base, page_blob_resize)
    {
        m_blob.properties().set_content_language(U("tr,en"));
        m_blob.create(1024, 0, azure::storage::access_condition(), azure::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, azure::storage::access_condition(), azure::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(azure::storage::access_condition(), azure::storage::blob_request_options(), m_context);
        CHECK_UTF8_EQUAL(U("tr,en"), m_blob.properties().content_language());
    }
    TEST_FIXTURE(append_blob_test_base, append_blob_snapshot_metadata)
    {
        m_blob.metadata()[U("key1")] = U("value1");
        m_blob.metadata()[U("key2")] = U("value2");
        m_blob.create_or_replace(azure::storage::access_condition(), azure::storage::blob_request_options(), m_context);

        auto snapshot1 = m_blob.create_snapshot(azure::storage::cloud_metadata(), azure::storage::access_condition(), azure::storage::blob_request_options(), m_context);
        CHECK_EQUAL(2U, snapshot1.metadata().size());
        CHECK_UTF8_EQUAL(U("value1"), snapshot1.metadata()[U("key1")]);
        CHECK_UTF8_EQUAL(U("value2"), snapshot1.metadata()[U("key2")]);

        azure::storage::cloud_append_blob snapshot1_clone(snapshot1.uri(), snapshot1.snapshot_time(), snapshot1.service_client().credentials());
        CHECK(snapshot1_clone.metadata().empty());
        snapshot1_clone.download_attributes(azure::storage::access_condition(), azure::storage::blob_request_options(), m_context);
        CHECK_EQUAL(2U, snapshot1_clone.metadata().size());
        CHECK_UTF8_EQUAL(U("value1"), snapshot1_clone.metadata()[U("key1")]);
        CHECK_UTF8_EQUAL(U("value2"), snapshot1_clone.metadata()[U("key2")]);

        azure::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, azure::storage::access_condition(), azure::storage::blob_request_options(), m_context);
        CHECK_EQUAL(2U, snapshot1.metadata().size());
        CHECK_UTF8_EQUAL(U("value1"), snapshot2.metadata()[U("key3")]);
        CHECK_UTF8_EQUAL(U("value2"), snapshot2.metadata()[U("key4")]);

        azure::storage::cloud_append_blob snapshot2_clone(snapshot2.uri(), snapshot2.snapshot_time(), snapshot2.service_client().credentials());
        CHECK(snapshot2_clone.metadata().empty());
        snapshot2_clone.download_attributes(azure::storage::access_condition(), azure::storage::blob_request_options(), m_context);
        CHECK_EQUAL(2U, snapshot2_clone.metadata().size());
        CHECK_UTF8_EQUAL(U("value1"), snapshot2_clone.metadata()[U("key3")]);
        CHECK_UTF8_EQUAL(U("value2"), snapshot2_clone.metadata()[U("key4")]);
    }
    TEST_FIXTURE(file_directory_test_base, directory_get_file_ref)
    {
        m_directory.create_if_not_exists(azure::storage::file_access_condition(), azure::storage::file_request_options(), m_context);
        auto file_name = _XPLATSTR("file");
        auto file = m_directory.get_file_reference(file_name);

        CHECK_UTF8_EQUAL(file_name, file.name());
        CHECK(file.get_parent_share_reference().is_valid());
        check_equal(m_share, file.get_parent_share_reference());

        check_equal(m_directory, file.get_parent_directory_reference());

        CHECK(!file.uri().primary_uri().is_empty());
        CHECK(file.metadata().empty());
        CHECK(file.properties().etag().empty());
        CHECK(file.properties().cache_control().empty());
        CHECK(file.properties().content_disposition().empty());
        CHECK(file.properties().content_encoding().empty());
        CHECK(file.properties().content_language().empty());
        CHECK(file.properties().content_md5().empty());
        CHECK(file.properties().content_type().empty());
        CHECK(file.properties().type().empty());
        CHECK_EQUAL(file.properties().length(), 0);

        CHECK(!file.properties().last_modified().is_initialized());

        // In root directory
        file = m_share.get_root_directory_reference().get_file_reference(file_name);

        CHECK_UTF8_EQUAL(file_name, file.name());
        CHECK(file.get_parent_share_reference().is_valid());
        check_equal(m_share, file.get_parent_share_reference());

        check_equal(m_share.get_root_directory_reference(), file.get_parent_directory_reference());

        CHECK(!file.uri().primary_uri().is_empty());
        CHECK(file.metadata().empty());
        CHECK(file.properties().etag().empty());
        CHECK(file.properties().cache_control().empty());
        CHECK(file.properties().content_disposition().empty());
        CHECK(file.properties().content_encoding().empty());
        CHECK(file.properties().content_language().empty());
        CHECK(file.properties().content_md5().empty());
        CHECK(file.properties().content_type().empty());
        CHECK(file.properties().type().empty());
        CHECK_EQUAL(file.properties().length(), 0);

        CHECK(!file.properties().last_modified().is_initialized());
    }
void blob_service_test_base_with_objects_to_delete::check_container_list(const std::vector<azure::storage::cloud_blob_container>& list, const utility::string_t& prefix, bool check_found)
{
    auto container_list_sorted = std::is_sorted(list.cbegin(), list.cend(), [](const azure::storage::cloud_blob_container& a, const azure::storage::cloud_blob_container& b)
    {
        return a.name() < b.name();
    });
    CHECK(container_list_sorted);

    std::vector<azure::storage::cloud_blob_container> containers(m_containers_to_delete);

    for (auto list_iter = list.begin(); list_iter != list.end(); ++list_iter)
    {
        bool found = false;
        for (auto iter = containers.begin(); iter != containers.end(); ++iter)
        {
            if (iter->name() == list_iter->name())
            {
                auto index_str = list_iter->metadata().find(U("index"));
                CHECK(index_str != list_iter->metadata().end());
                CHECK_UTF8_EQUAL(iter->name(), prefix + index_str->second);
                containers.erase(iter);
                found = true;
                break;
            }
        }
        if (check_found)
        {
            CHECK(found);
        }
    }
    CHECK(containers.empty());
}
void check_service_properties(const azure::storage::service_properties& a, const azure::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());
    if (a.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());
    if (a.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());

    // TODO: Is the following check valid?
    //CHECK_UTF8_EQUAL(a.default_service_version(), b.default_service_version());
}
    TEST_FIXTURE(file_directory_test_base, directory_metadata)
    {
        auto value1 = this->get_random_string();
        auto value2 = this->get_random_string();
        auto value3 = this->get_random_string();
        auto value4 = this->get_random_string();

        // create 2 pairs
        m_directory.metadata()[_XPLATSTR("key1")] = value1;
        m_directory.metadata()[_XPLATSTR("key2")] = value2;
        m_directory.create_if_not_exists(azure::storage::file_access_condition(), azure::storage::file_request_options(), m_context);
        CHECK_UTF8_EQUAL(m_directory.metadata()[_XPLATSTR("key1")], value1);
        CHECK_UTF8_EQUAL(m_directory.metadata()[_XPLATSTR("key2")], value2);

        auto same_directory = m_directory.get_parent_share_reference().get_directory_reference(m_directory.name());
        CHECK(same_directory.metadata().empty());
        same_directory.download_attributes();
        CHECK_EQUAL(2U, same_directory.metadata().size());
        CHECK_UTF8_EQUAL(value1, same_directory.metadata()[_XPLATSTR("key1")]);
        CHECK_UTF8_EQUAL(value2, same_directory.metadata()[_XPLATSTR("key2")]);

        // add 1 pair
        m_directory.metadata()[_XPLATSTR("key3")] = value3;
        m_directory.upload_metadata(azure::storage::file_access_condition(), azure::storage::file_request_options(), m_context);

        same_directory.metadata().clear();
        CHECK(same_directory.metadata().empty());
        same_directory.download_attributes();
        CHECK_EQUAL(3U, same_directory.metadata().size());
        CHECK_UTF8_EQUAL(value1, same_directory.metadata()[_XPLATSTR("key1")]);
        CHECK_UTF8_EQUAL(value2, same_directory.metadata()[_XPLATSTR("key2")]);
        CHECK_UTF8_EQUAL(value3, same_directory.metadata()[_XPLATSTR("key3")]);

        // overwrite with 1 pair
        m_directory.metadata().clear();
        m_directory.metadata()[_XPLATSTR("key4")] = value4;
        m_directory.upload_metadata(azure::storage::file_access_condition(), azure::storage::file_request_options(), m_context);

        same_directory.metadata().clear();
        CHECK(same_directory.metadata().empty());
        same_directory.download_attributes();
        CHECK_EQUAL(1U, same_directory.metadata().size());
        CHECK_UTF8_EQUAL(value4, same_directory.metadata()[_XPLATSTR("key4")]);

        // clear metadata
        m_directory.metadata().clear();
        m_directory.upload_metadata(azure::storage::file_access_condition(), azure::storage::file_request_options(), m_context);

        same_directory.metadata().clear();
        CHECK(same_directory.metadata().empty());
        same_directory.download_attributes();
        CHECK_EQUAL(0U, same_directory.metadata().size());
    }
void blob_service_test_base::check_blob_properties_equal(const azure::storage::cloud_blob_properties& expected, const azure::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());
}
    TEST_FIXTURE(container_test_base, container_get_reference)
    {
        auto block_blob = m_container.get_block_blob_reference(_XPLATSTR("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(_XPLATSTR("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 append_blob = m_container.get_append_blob_reference(_XPLATSTR("blob3"));
        CHECK_UTF8_EQUAL(m_container.uri().primary_uri().to_string(), append_blob.container().uri().primary_uri().to_string());
        CHECK_UTF8_EQUAL(m_container.uri().secondary_uri().to_string(), append_blob.container().uri().secondary_uri().to_string());

        auto directory = m_container.get_directory_reference(_XPLATSTR("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(page_blob_test_base, page_blob_file_upload)
    {
        azure::storage::blob_request_options options;
        options.set_store_blob_content_md5(true);

        utility::string_t md5_header;
        m_context.set_sending_request([&md5_header] (web::http::http_request& request, azure::storage::operation_context)
        {
            if (!request.headers().match(U("x-ms-blob-content-md5"), md5_header))
            {
                md5_header.clear();
            }
        });

        temp_file invalid_file(1000);
        CHECK_THROW(m_blob.upload_from_file(invalid_file.path(), 0, azure::storage::access_condition(), options, m_context), azure::storage::storage_exception);

        temp_file file(1024);
        m_blob.upload_from_file(file.path(), 0, azure::storage::access_condition(), options, m_context);
        CHECK_UTF8_EQUAL(file.content_md5(), md5_header);

        m_context.set_sending_request(std::function<void(web::http::http_request &, azure::storage::operation_context)>());

        temp_file file2(0);
        m_blob.download_to_file(file2.path(), azure::storage::access_condition(), options, m_context);

        concurrency::streams::container_buffer<std::vector<uint8_t>> original_file_buffer;
        auto original_file = concurrency::streams::file_stream<uint8_t>::open_istream(file.path()).get();
        original_file.read_to_end(original_file_buffer).wait();
        original_file.close().wait();

        concurrency::streams::container_buffer<std::vector<uint8_t>> downloaded_file_buffer;
        auto downloaded_file = concurrency::streams::file_stream<uint8_t>::open_istream(file2.path()).get();
        downloaded_file.read_to_end(downloaded_file_buffer).wait();
        downloaded_file.close().wait();

        CHECK_EQUAL(original_file_buffer.collection().size(), downloaded_file_buffer.collection().size());
        CHECK_ARRAY_EQUAL(original_file_buffer.collection(), downloaded_file_buffer.collection(), (int)downloaded_file_buffer.collection().size());

        m_blob.properties().set_content_md5(dummy_md5);
        m_blob.upload_properties();
        options.set_retry_policy(azure::storage::no_retry_policy());
        CHECK_THROW(m_blob.download_to_file(file2.path(), azure::storage::access_condition(), options, m_context), azure::storage::storage_exception);
    }
    TEST_FIXTURE(block_blob_test_base, storage_extended_error_verify_xml_with_details)
    {
        const size_t buffer_size = 8 * 1024;
        std::vector<uint8_t> buffer;
        buffer.resize(buffer_size);
        auto md5 = fill_buffer_and_get_md5(buffer);

        auto stream = concurrency::streams::bytestream::open_istream(buffer);
        m_blob.upload_block(get_block_id(0), stream, md5);

        try
        {
            stream.seek(1024, std::ios_base::beg);
            m_blob.upload_block(get_block_id(1), stream, md5);
            CHECK(false);
        }
        catch (azure::storage::storage_exception& ex)
        {
            CHECK_UTF8_EQUAL(U("Md5Mismatch"), ex.result().extended_error().code());
            CHECK(!ex.result().extended_error().message().empty());
            CHECK(ex.result().extended_error().details().size() > 0);
        }
    }
    TEST_FIXTURE(container_test_base, container_metadata)
    {
        // Create with 2 pairs
        m_container.metadata()[_XPLATSTR("key1")] = _XPLATSTR("value1");
        m_container.metadata()[_XPLATSTR("key2")] = _XPLATSTR("value2");
        m_container.create(azure::storage::blob_container_public_access_type::off, azure::storage::blob_request_options(), m_context);
        check_container_no_stale_property(m_container);

        auto same_container = m_client.get_container_reference(m_container.name());
        CHECK(same_container.metadata().empty());
        same_container.download_attributes(azure::storage::access_condition(), azure::storage::blob_request_options(), m_context);
        CHECK_EQUAL(2U, same_container.metadata().size());
        CHECK_UTF8_EQUAL(_XPLATSTR("value1"), same_container.metadata()[_XPLATSTR("key1")]);
        CHECK_UTF8_EQUAL(_XPLATSTR("value2"), same_container.metadata()[_XPLATSTR("key2")]);

        // Add 1 pair
        same_container.metadata()[_XPLATSTR("key3")] = _XPLATSTR("value3");
        same_container.upload_metadata(azure::storage::access_condition(), azure::storage::blob_request_options(), m_context);
        m_container.download_attributes(azure::storage::access_condition(), azure::storage::blob_request_options(), m_context);
        CHECK_EQUAL(3U, same_container.metadata().size());
        CHECK_UTF8_EQUAL(_XPLATSTR("value1"), m_container.metadata()[_XPLATSTR("key1")]);
        CHECK_UTF8_EQUAL(_XPLATSTR("value2"), m_container.metadata()[_XPLATSTR("key2")]);
        CHECK_UTF8_EQUAL(_XPLATSTR("value3"), m_container.metadata()[_XPLATSTR("key3")]);

        // Overwrite with 1 pair
        m_container.metadata().clear();
        m_container.metadata()[_XPLATSTR("key4")] = _XPLATSTR("value4");
        m_container.upload_metadata(azure::storage::access_condition(), azure::storage::blob_request_options(), m_context);
        same_container.download_attributes(azure::storage::access_condition(), azure::storage::blob_request_options(), m_context);
        CHECK_EQUAL(1U, same_container.metadata().size());
        CHECK_UTF8_EQUAL(_XPLATSTR("value4"), same_container.metadata()[_XPLATSTR("key4")]);

        // Clear all pairs
        same_container.metadata().clear();
        same_container.upload_metadata(azure::storage::access_condition(), azure::storage::blob_request_options(), m_context);
        m_container.download_attributes(azure::storage::access_condition(), azure::storage::blob_request_options(), m_context);
        CHECK(m_container.metadata().empty());
    }
    TEST_FIXTURE(container_test_base, container_list_blobs)
    {
        m_container.create(azure::storage::blob_container_public_access_type::off, azure::storage::blob_request_options(), m_context);
        check_container_no_stale_property(m_container);
        std::map<utility::string_t, azure::storage::cloud_blob> blobs;

        for (int i = 0; i < 4; i++)
        {
            auto index = utility::conversions::print_string(i);
            auto blob = m_container.get_block_blob_reference(_XPLATSTR("blockblob") + index);
            blob.metadata()[_XPLATSTR("index")] = index;
            
            std::vector<uint8_t> buffer;
            buffer.resize(i * 16 * 1024);
            auto stream = concurrency::streams::container_stream<std::vector<uint8_t>>::open_istream(buffer);
            blob.upload_from_stream(stream, azure::storage::access_condition(), azure::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(_XPLATSTR("pageblob") + index);
            blob.metadata()[_XPLATSTR("index")] = index;
            
            blob.create(i * 512, 0, azure::storage::access_condition(), azure::storage::blob_request_options(), m_context);
            check_container_no_stale_property(m_container);
            blobs[blob.name()] = blob;
        }

        for (int i = 0; i < 3; i++)
        {
            auto index = utility::conversions::print_string(i);
            auto blob = m_container.get_append_blob_reference(_XPLATSTR("appendblob") + index);
            blob.metadata()[_XPLATSTR("index")] = index;

            blob.create_or_replace(azure::storage::access_condition(), azure::storage::blob_request_options(), m_context);
            check_container_no_stale_property(m_container);

            std::vector<uint8_t> buffer;
            buffer.resize((i + 1) * 8 * 1024);
            fill_buffer_and_get_md5(buffer);
            auto stream = concurrency::streams::container_stream<std::vector<uint8_t>>::open_istream(buffer);
            blob.append_block(stream, utility::string_t(), azure::storage::access_condition(), azure::storage::blob_request_options(), m_context);
            blobs[blob.name()] = blob;
        }

        auto listing1 = list_all_blobs(utility::string_t(), azure::storage::blob_listing_details::all, 0, azure::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(_XPLATSTR("index"));
            CHECK(index_str != blob->second.metadata().end());
            auto index = utility::conversions::scan_string<int>(index_str->second);

            switch (iter->type())
            {
            case azure::storage::blob_type::block_blob:
                CHECK_EQUAL(index * 16 * 1024, iter->properties().size());
                break;

            case azure::storage::blob_type::page_blob:
                CHECK_EQUAL(index * 512, iter->properties().size());
                break;

            case azure::storage::blob_type::append_blob:
                CHECK_EQUAL((index + 1) * 8 * 1024, iter->properties().size());
                break;

            default:
                CHECK(false);
                break;
            }

            blobs.erase(blob);
        }

        CHECK_EQUAL(0U, blobs.size());

        auto listing2 = list_all_blobs(_XPLATSTR("block"), azure::storage::blob_listing_details::none, 10, azure::storage::blob_request_options());
        CHECK_EQUAL(4U, listing2.size());
        for (auto iter = listing2.begin(); iter != listing2.end(); ++iter)
        {
            CHECK(iter->metadata().empty());
        }
    }
    TEST_FIXTURE(append_blob_test_base, append_blob_append)
    {
        const size_t file_buffer_size = 24 * 1024 * 1024 + 6;
        std::vector<uint8_t> file_buffer;
        file_buffer.resize(file_buffer_size);

        azure::storage::blob_request_options options;
        options.set_use_transactional_md5(false);

        utility::string_t md5_header;
        m_context.set_sending_request([&md5_header](web::http::http_request& request, azure::storage::operation_context)
        {
            if (!request.headers().match(web::http::header_names::content_md5, md5_header))
            {
                md5_header.clear();
            }
        });

        m_blob.create_or_replace(azure::storage::access_condition(), options, m_context);

        int block_count = 0;

        // append stream (4M, 4M)
        const size_t buffer_offsets1[2] = { 0, 4 * 1024 * 1024};
        for (uint16_t i = 0; i < 2; ++i)
        {
            std::vector<uint8_t> buffer;
            buffer.resize(4 * 1024 * 1024);
            fill_buffer_and_get_md5(buffer);
            std::copy(buffer.begin(), buffer.end(), file_buffer.begin() + buffer_offsets1[i]);

            auto stream = concurrency::streams::bytestream::open_istream(buffer);
            azure::storage::access_condition condition = azure::storage::access_condition::generate_if_append_position_equal_condition(buffer_offsets1[i]);
            m_blob.append_from_stream(stream, condition, options, m_context);
            CHECK_UTF8_EQUAL(utility::string_t(), md5_header);

            block_count++;
            CHECK_EQUAL(block_count, m_blob.properties().append_blob_committed_block_count());
        }

        // append stream with length (2M, 2M, 2M)
        const size_t buffer_offsets2[3] = { 8 * 1024 * 1024,  10 * 1024 * 1024, 12 * 1024 * 1024 };
        for (uint16_t i = 0; i < 3; ++i)
        {
            std::vector<uint8_t> buffer;
            buffer.resize(4 * 1024 * 1024);
            fill_buffer_and_get_md5(buffer);
            std::copy(buffer.begin(), buffer.begin() + 2 * 1024 * 1024, file_buffer.begin() + buffer_offsets2[i]);

            auto stream = concurrency::streams::bytestream::open_istream(buffer);
            m_blob.append_from_stream(stream, 2 * 1024 * 1024, azure::storage::access_condition(), options, m_context);
            CHECK_UTF8_EQUAL(utility::string_t(), md5_header);

            block_count++;
            CHECK_EQUAL(block_count, m_blob.properties().append_blob_committed_block_count());
        }

        // append file (5M, 5M)
        const size_t buffer_offsets3[2] = { 14 * 1024 * 1024, 19 * 1024 * 1024 };
        for (uint16_t i = 0; i < 2; ++i)
        {
            std::vector<uint8_t> buffer;
            buffer.resize(5 * 1024 * 1024);
            fill_buffer_and_get_md5(buffer);
            std::copy(buffer.begin(), buffer.end(), file_buffer.begin() + buffer_offsets3[i]);

            // create a temporary test file
            utility::string_t tmp_file_path = get_random_container_name(8);
            auto stream = concurrency::streams::file_stream<uint8_t>::open_ostream(tmp_file_path).get();
            stream.streambuf().putn_nocopy(buffer.data(), buffer.size()).wait();
            stream.close().wait();

            // append from file
            m_blob.append_from_file(tmp_file_path, azure::storage::access_condition(), options, m_context);

            // remote the temporary test file
            std::remove(utility::conversions::to_utf8string(tmp_file_path).c_str());

            block_count  += 2;
            CHECK_EQUAL(block_count, m_blob.properties().append_blob_committed_block_count());
        }

        // append text (1, 5)
        const size_t buffer_offsets4[2] = { 24 * 1024 * 1024, 24 * 1024 * 1024 + 1};
        {
            utility::string_t text1 = U("1");
            std::string text1_copy = utility::conversions::to_utf8string(text1);
            std::copy(text1_copy.begin(), text1_copy.end(), file_buffer.begin() + buffer_offsets4[0]);
            m_blob.append_text(text1);
            block_count++;
            CHECK_EQUAL(block_count, m_blob.properties().append_blob_committed_block_count());

            utility::string_t text2 = U("test2");
            std::string text2_copy = utility::conversions::to_utf8string(text2);
            std::copy(text2_copy.begin(), text2_copy.end(), file_buffer.begin() + buffer_offsets4[1]);
            m_blob.append_text(text2);
            block_count++;
            CHECK_EQUAL(block_count, m_blob.properties().append_blob_committed_block_count());
        }

        // download the blob
        concurrency::streams::container_buffer<std::vector<uint8_t>> downloaded_blob;
        m_blob.download_to_stream(downloaded_blob.create_ostream(), azure::storage::access_condition(), options, m_context);

        CHECK_ARRAY_EQUAL(file_buffer, downloaded_blob.collection(), (int)file_buffer.size());

        m_blob.delete_blob();

        m_context.set_sending_request(std::function<void(web::http::http_request &, azure::storage::operation_context)>());
    }
    TEST_FIXTURE(append_blob_test_base, append_block)
    {
        const size_t buffer_size = 16 * 1024;
        std::vector<uint8_t> buffer;
        buffer.resize(buffer_size);
        azure::storage::blob_request_options options;

        utility::string_t md5_header;
        m_context.set_sending_request([&md5_header](web::http::http_request& request, azure::storage::operation_context)
        {
            if (!request.headers().match(web::http::header_names::content_md5, md5_header))
            {
                md5_header.clear();
            }
        });

        m_blob.create_or_replace(azure::storage::access_condition(), options, m_context);

        options.set_use_transactional_md5(false);
        for (uint16_t i = 0; i < 3; ++i)
        {
            fill_buffer_and_get_md5(buffer);
            auto stream = concurrency::streams::bytestream::open_istream(buffer);
            int64_t offset = m_blob.append_block(stream, utility::string_t(), azure::storage::access_condition(), options, m_context);
            CHECK_UTF8_EQUAL(utility::string_t(), md5_header);
            CHECK_EQUAL(i * buffer_size, offset);
            CHECK_EQUAL(i + 1, m_blob.properties().append_blob_committed_block_count());
        }

        options.set_use_transactional_md5(false);
        for (uint16_t i = 3; i < 6; ++i)
        {
            auto md5 = fill_buffer_and_get_md5(buffer);
            auto stream = concurrency::streams::bytestream::open_istream(buffer);
            int64_t offset = m_blob.append_block(stream, md5, azure::storage::access_condition(), options, m_context);
            CHECK_UTF8_EQUAL(md5, md5_header);
            CHECK_EQUAL(i * buffer_size, offset);
            CHECK_EQUAL(i + 1, m_blob.properties().append_blob_committed_block_count());
        }

        options.set_use_transactional_md5(true);
        for (uint16_t i = 6; i < 9; ++i)
        {
            auto md5 = fill_buffer_and_get_md5(buffer);
            auto stream = concurrency::streams::bytestream::open_istream(buffer);
            int64_t offset = m_blob.append_block(stream, utility::string_t(), azure::storage::access_condition(), options, m_context);
            CHECK_UTF8_EQUAL(md5, md5_header);
            CHECK_EQUAL(i * buffer_size, offset);
            CHECK_EQUAL(i + 1, m_blob.properties().append_blob_committed_block_count());
        }

        // block stream with length = 0
        options.set_use_transactional_md5(true);
        fill_buffer_and_get_md5(buffer);
        auto stream1 = concurrency::streams::bytestream::open_istream(buffer);
        stream1.seek(buffer.size());
        CHECK_THROW(m_blob.append_block(stream1, utility::string_t(), azure::storage::access_condition(), options, m_context), azure::storage::storage_exception);

        options.set_use_transactional_md5(true);
        fill_buffer_and_get_md5(buffer);
        auto stream = concurrency::streams::bytestream::open_istream(buffer);
        CHECK_THROW(m_blob.append_block(stream, dummy_md5, azure::storage::access_condition(), options, m_context), azure::storage::storage_exception);
        CHECK_UTF8_EQUAL(dummy_md5, md5_header);

        m_context.set_sending_request(std::function<void(web::http::http_request &, azure::storage::operation_context)>());
    }
    TEST_FIXTURE(append_blob_test_base, append_blob_constructor)
    {
        m_blob.create_or_replace(azure::storage::access_condition(), azure::storage::blob_request_options(), m_context);
        CHECK(!m_blob.properties().etag().empty());

        azure::storage::cloud_append_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());

        azure::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());

        azure::storage::cloud_append_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_upload)
    {
        std::vector<uint8_t> buffer;
        buffer.resize(512);
        azure::storage::blob_request_options options;
        std::vector<azure::storage::page_range> pages;

        utility::string_t md5_header;
        m_context.set_sending_request([&md5_header] (web::http::http_request& request, azure::storage::operation_context)
        {
            if (!request.headers().match(web::http::header_names::content_md5, md5_header))
            {
                md5_header.clear();
            }
        });

        m_blob.create(12 * 1024 * 1024, 0, azure::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);
            azure::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(), azure::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);
            azure::storage::page_range range(i * 1536, i * 1536 + buffer.size() - 1);
            pages.push_back(range);
            m_blob.upload_pages(stream, range.start_offset(), md5, azure::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);
            azure::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(), azure::storage::access_condition(), options, m_context);
            CHECK_UTF8_EQUAL(md5, md5_header);
        }

        options.set_use_transactional_md5(false);
        {
            // upload a page range of max_block_size
            std::vector<uint8_t> big_buffer;
            big_buffer.resize(azure::storage::protocol::max_block_size);
            auto md5 = fill_buffer_and_get_md5(big_buffer);
            auto stream = concurrency::streams::bytestream::open_istream(big_buffer);
            azure::storage::page_range range(4 * 1024 * 1024, 4 * 1024 * 1024 + azure::storage::protocol::max_block_size - 1);
            pages.push_back(range);
            m_blob.upload_pages(stream, range.start_offset(), md5, azure::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, azure::storage::access_condition(), options, m_context), azure::storage::storage_exception);
        CHECK_UTF8_EQUAL(dummy_md5, md5_header);

        // trying upload page ranges bigger than max_block_size
        {
            buffer.resize(azure::storage::protocol::max_block_size + 1);
            fill_buffer_and_get_md5(buffer);

            azure::storage::page_range range(8 * 1024 * 1024, 8 * 1024 * 1024 + azure::storage::protocol::max_block_size -1);
            auto stream = concurrency::streams::bytestream::open_istream(buffer);
            CHECK_THROW(m_blob.upload_pages(stream, range.start_offset(), utility::string_t(), azure::storage::access_condition(), options, m_context), std::invalid_argument);
        }

        check_page_ranges_equal(pages);

        m_context.set_sending_request(std::function<void(web::http::http_request &, azure::storage::operation_context)>());
    }