|
13 | 13 | // limitations under the License. |
14 | 14 |
|
15 | 15 | #include "google/cloud/storage/internal/rest/stub.h" |
| 16 | +#include "google/cloud/storage/internal/hash_function.h" |
16 | 17 | #include "google/cloud/storage/testing/canonical_errors.h" |
17 | 18 | #include "google/cloud/internal/api_client_header.h" |
18 | 19 | #include "google/cloud/testing_util/mock_rest_client.h" |
@@ -45,6 +46,29 @@ using ::testing::Pair; |
45 | 46 | using ::testing::ResultOf; |
46 | 47 | using ::testing::Return; |
47 | 48 |
|
| 49 | +class NoOpHashFunction : public HashFunction { |
| 50 | + public: |
| 51 | + std::string Name() const override { return "NoOp"; } |
| 52 | + void Update(absl::string_view b) override { Cormorant(b); } |
| 53 | + Status Update(std::int64_t o, absl::string_view b) override { |
| 54 | + Cormorant(o, b); |
| 55 | + return Status{}; |
| 56 | + } |
| 57 | + Status Update(std::int64_t o, absl::string_view b, std::uint32_t c) override { |
| 58 | + Cormorant(o, b, c); |
| 59 | + return Status{}; |
| 60 | + } |
| 61 | + Status Update(std::int64_t o, absl::Cord const& b, std::uint32_t c) override { |
| 62 | + Cormorant(o, b, c); |
| 63 | + return Status{}; |
| 64 | + } |
| 65 | + HashValues Finish() override { return {}; } |
| 66 | + |
| 67 | + private: |
| 68 | + template <typename... Args> |
| 69 | + void Cormorant(Args const&...) {} |
| 70 | +}; |
| 71 | + |
48 | 72 | TEST(RestStubTest, ResolveStorageAuthorityProdEndpoint) { |
49 | 73 | auto options = |
50 | 74 | Options{}.set<RestEndpointOption>("https://storage.googleapis.com"); |
@@ -921,6 +945,92 @@ TEST(RestStubTest, DeleteNotification) { |
921 | 945 | StatusIs(PermanentError().code(), PermanentError().message())); |
922 | 946 | } |
923 | 947 |
|
| 948 | +TEST(RestStubTest, UploadChunkLastChunkWithCrc32c) { |
| 949 | + auto mock = std::make_shared<MockRestClient>(); |
| 950 | + EXPECT_CALL( |
| 951 | + *mock, |
| 952 | + Put(ExpectedContext(), |
| 953 | + ResultOf( |
| 954 | + "request headers contain x-goog-hash with crc32c", |
| 955 | + [](RestRequest const& r) { return r.headers(); }, |
| 956 | + Contains(Pair("x-goog-hash", ElementsAre("crc32c=test-crc32c")))), |
| 957 | + ExpectedPayload())) |
| 958 | + .WillOnce(Return(PermanentError())); |
| 959 | + auto tested = std::make_unique<RestStub>(Options{}, mock, mock); |
| 960 | + auto context = TestContext(); |
| 961 | + auto status = tested->UploadChunk( |
| 962 | + context, TestOptions(), |
| 963 | + UploadChunkRequest("test-url", 0, {}, |
| 964 | + std::make_shared<NoOpHashFunction>(), |
| 965 | + {"test-crc32c", ""})); |
| 966 | + EXPECT_THAT(status, |
| 967 | + StatusIs(PermanentError().code(), PermanentError().message())); |
| 968 | +} |
| 969 | + |
| 970 | +TEST(RestStubTest, UploadChunkLastChunkWithMd5) { |
| 971 | + auto mock = std::make_shared<MockRestClient>(); |
| 972 | + EXPECT_CALL( |
| 973 | + *mock, |
| 974 | + Put(ExpectedContext(), |
| 975 | + ResultOf( |
| 976 | + "request headers contain x-goog-hash with md5", |
| 977 | + [](RestRequest const& r) { return r.headers(); }, |
| 978 | + Contains(Pair("x-goog-hash", ElementsAre("md5=test-md5")))), |
| 979 | + ExpectedPayload())) |
| 980 | + .WillOnce(Return(PermanentError())); |
| 981 | + auto tested = std::make_unique<RestStub>(Options{}, mock, mock); |
| 982 | + auto context = TestContext(); |
| 983 | + auto status = tested->UploadChunk( |
| 984 | + context, TestOptions(), |
| 985 | + UploadChunkRequest("test-url", 0, {}, |
| 986 | + std::make_shared<NoOpHashFunction>(), |
| 987 | + {"", "test-md5"})); |
| 988 | + EXPECT_THAT(status, |
| 989 | + StatusIs(PermanentError().code(), PermanentError().message())); |
| 990 | +} |
| 991 | + |
| 992 | +TEST(RestStubTest, UploadChunkLastChunkWithBoth) { |
| 993 | + auto mock = std::make_shared<MockRestClient>(); |
| 994 | + EXPECT_CALL( |
| 995 | + *mock, |
| 996 | + Put(ExpectedContext(), |
| 997 | + ResultOf( |
| 998 | + "request headers contain x-goog-hash with crc32c and md5", |
| 999 | + [](RestRequest const& r) { return r.headers(); }, |
| 1000 | + Contains(Pair("x-goog-hash", ElementsAre("crc32c=test-crc32c", |
| 1001 | + "md5=test-md5")))), |
| 1002 | + ExpectedPayload())) |
| 1003 | + .WillOnce(Return(PermanentError())); |
| 1004 | + auto tested = std::make_unique<RestStub>(Options{}, mock, mock); |
| 1005 | + auto context = TestContext(); |
| 1006 | + auto status = tested->UploadChunk( |
| 1007 | + context, TestOptions(), |
| 1008 | + UploadChunkRequest("test-url", 0, {}, |
| 1009 | + std::make_shared<NoOpHashFunction>(), |
| 1010 | + {"test-crc32c", "test-md5"})); |
| 1011 | + EXPECT_THAT(status, |
| 1012 | + StatusIs(PermanentError().code(), PermanentError().message())); |
| 1013 | +} |
| 1014 | + |
| 1015 | +TEST(RestStubTest, UploadChunkIntermediate) { |
| 1016 | + auto mock = std::make_shared<MockRestClient>(); |
| 1017 | + EXPECT_CALL(*mock, Put(ExpectedContext(), |
| 1018 | + ResultOf( |
| 1019 | + "request headers do not contain x-goog-hash", |
| 1020 | + [](RestRequest const& r) { return r.headers(); }, |
| 1021 | + Not(Contains(Pair("x-goog-hash", _)))), |
| 1022 | + ExpectedPayload())) |
| 1023 | + .WillOnce(Return(PermanentError())); |
| 1024 | + auto tested = std::make_unique<RestStub>(Options{}, mock, mock); |
| 1025 | + auto context = TestContext(); |
| 1026 | + auto status = tested->UploadChunk( |
| 1027 | + context, TestOptions(), |
| 1028 | + UploadChunkRequest("test-url", 0, {}, |
| 1029 | + std::make_shared<NoOpHashFunction>())); |
| 1030 | + EXPECT_THAT(status, |
| 1031 | + StatusIs(PermanentError().code(), PermanentError().message())); |
| 1032 | +} |
| 1033 | + |
924 | 1034 | } // namespace |
925 | 1035 | } // namespace internal |
926 | 1036 | GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END |
|
0 commit comments