1 // Copyright 2020 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "google/cloud/storage/internal/grpc_client.h"
16 #include "google/cloud/storage/oauth2/google_credentials.h"
17 #include "google/cloud/testing_util/assert_ok.h"
18 #include "google/cloud/testing_util/is_proto_equal.h"
19 #include "google/cloud/testing_util/scoped_environment.h"
20 #include <google/protobuf/text_format.h>
21 #include <gmock/gmock.h>
22
23 namespace google {
24 namespace cloud {
25 namespace storage {
26 inline namespace STORAGE_CLIENT_NS {
27 namespace internal {
28 namespace {
29
30 namespace storage_proto = ::google::storage::v1;
31 using ::google::cloud::testing_util::IsProtoEqual;
32
TEST(GrpcClientObjectRequest,InsertObjectMediaRequestSimple)33 TEST(GrpcClientObjectRequest, InsertObjectMediaRequestSimple) {
34 storage_proto::InsertObjectRequest expected;
35 EXPECT_TRUE(google::protobuf::TextFormat::ParseFromString(
36 R"pb(
37 insert_object_spec: {
38 resource: { bucket: "test-bucket-name" name: "test-object-name" }
39 }
40 # See hash_validator_test.cc for how these magic numbers are obtained.
41 object_checksums: {
42 crc32c { value: 576848900 }
43 md5_hash: "9e107d9d372bb6826bd81d3542a419d6"
44 }
45 )pb",
46 &expected));
47
48 InsertObjectMediaRequest request(
49 "test-bucket-name", "test-object-name",
50 "The quick brown fox jumps over the lazy dog");
51 auto actual = GrpcClient::ToProto(request);
52 EXPECT_THAT(actual, IsProtoEqual(expected));
53 }
54
TEST(GrpcClientObjectRequest,InsertObjectMediaRequestAllOptions)55 TEST(GrpcClientObjectRequest, InsertObjectMediaRequestAllOptions) {
56 storage_proto::InsertObjectRequest expected;
57 EXPECT_TRUE(google::protobuf::TextFormat::ParseFromString(
58 R"pb(
59 insert_object_spec: {
60 resource: {
61 bucket: "test-bucket-name"
62 name: "test-object-name"
63 content_type: "test-content-type"
64 content_encoding: "test-content-encoding"
65 crc32c: { value: 576848900 }
66 # See hash_validator_test.cc for how it was obtained.
67 md5_hash: "nhB9nTcrtoJr2B01QqQZ1g=="
68 kms_key_name: "test-kms-key-name"
69 }
70 predefined_acl: OBJECT_ACL_PRIVATE
71 if_generation_match: { value: 0 }
72 if_generation_not_match: { value: 7 }
73 if_metageneration_match: { value: 42 }
74 if_metageneration_not_match: { value: 84 }
75 projection: FULL
76 }
77 common_object_request_params: {
78 encryption_algorithm: "AES256"
79 # to get the key value use:
80 # /bin/echo -n "01234567" | openssl base64
81 # to get the key hash use (note this command goes over two lines):
82 # /bin/echo -n "01234567" | sha256sum | awk '{printf("%s", $1);}' |
83 # xxd -r -p | openssl base64
84 encryption_key: "MDEyMzQ1Njc="
85 encryption_key_sha256: "kkWSubED8U+DP6r7Z/SAaR8BmIqkV8AGF2n1jNRzEbw="
86 }
87 common_request_params: {
88 user_project: "test-user-project"
89 quota_user: "test-quota-user"
90 }
91 # See hash_validator_test.cc for how these magic numbers are obtained.
92 object_checksums: {
93 crc32c { value: 576848900 }
94 md5_hash: "9e107d9d372bb6826bd81d3542a419d6"
95 }
96 )pb",
97 &expected));
98
99 auto constexpr kContents = "The quick brown fox jumps over the lazy dog";
100
101 InsertObjectMediaRequest request("test-bucket-name", "test-object-name",
102 kContents);
103 request.set_multiple_options(
104 ContentType("test-content-type"),
105 ContentEncoding("test-content-encoding"),
106 Crc32cChecksumValue(ComputeCrc32cChecksum(kContents)),
107 MD5HashValue(ComputeMD5Hash(kContents)), PredefinedAcl("private"),
108 IfGenerationMatch(0), IfGenerationNotMatch(7), IfMetagenerationMatch(42),
109 IfMetagenerationNotMatch(84), Projection::Full(),
110 UserProject("test-user-project"), QuotaUser("test-quota-user"),
111 UserIp("test-user-ip"), EncryptionKey::FromBinaryKey("01234567"),
112 KmsKeyName("test-kms-key-name"));
113
114 auto actual = GrpcClient::ToProto(request);
115 EXPECT_THAT(actual, IsProtoEqual(expected));
116 }
117
TEST(GrpcClientObjectRequest,InsertObjectMediaRequestWithObjectMetadata)118 TEST(GrpcClientObjectRequest, InsertObjectMediaRequestWithObjectMetadata) {
119 storage_proto::InsertObjectRequest expected;
120 EXPECT_TRUE(google::protobuf::TextFormat::ParseFromString(
121 R"pb(
122 insert_object_spec: {
123 resource: {
124 bucket: "test-bucket-name"
125 name: "test-object-name"
126 acl: { role: "test-role1" entity: "test-entity1" }
127 acl: { role: "test-role2" entity: "test-entity2" }
128 cache_control: "test-cache-control"
129 content_disposition: "test-content-disposition"
130 content_encoding: "test-content-encoding"
131 content_language: "test-content-language"
132 content_type: "test-content-type"
133 event_based_hold: { value: true }
134 metadata: { key: "test-key-1" value: "test-value-1" }
135 metadata: { key: "test-key-2" value: "test-value-2" }
136 storage_class: "test-storage-class"
137 temporary_hold: true
138 }
139 }
140 # See hash_validator_test.cc for how these magic numbers are obtained.
141 object_checksums: {
142 crc32c { value: 576848900 }
143 md5_hash: "9e107d9d372bb6826bd81d3542a419d6"
144 }
145 )pb",
146 &expected));
147
148 auto constexpr kContents = "The quick brown fox jumps over the lazy dog";
149
150 std::vector<ObjectAccessControl> acls{
151 ObjectAccessControl().set_role("test-role1").set_entity("test-entity1"),
152 ObjectAccessControl().set_role("test-role2").set_entity("test-entity2")};
153
154 InsertObjectMediaRequest request("test-bucket-name", "test-object-name",
155 kContents);
156 request.set_multiple_options(WithObjectMetadata(
157 ObjectMetadata()
158 .set_acl(acls)
159 .set_cache_control("test-cache-control")
160 .set_content_disposition("test-content-disposition")
161 .set_content_encoding("test-content-encoding")
162 .set_content_language("test-content-language")
163 .set_content_type("test-content-type")
164 .set_event_based_hold(true)
165 .upsert_metadata("test-key-1", "test-value-1")
166 .upsert_metadata("test-key-2", "test-value-2")
167 .set_storage_class("test-storage-class")
168 .set_temporary_hold(true)));
169
170 auto actual = GrpcClient::ToProto(request);
171 EXPECT_THAT(actual, IsProtoEqual(expected));
172 }
173
TEST(GrpcClientObjectRequest,DeleteObjectRequestSimple)174 TEST(GrpcClientObjectRequest, DeleteObjectRequestSimple) {
175 storage_proto::DeleteObjectRequest expected;
176 EXPECT_TRUE(google::protobuf::TextFormat::ParseFromString(R"""(
177 bucket: "test-bucket-name"
178 object: "test-object-name"
179 )""",
180 &expected));
181
182 DeleteObjectRequest request("test-bucket-name", "test-object-name");
183
184 auto actual = GrpcClient::ToProto(request);
185 EXPECT_THAT(actual, IsProtoEqual(expected));
186 }
187
TEST(GrpcClientObjectRequest,DeleteObjectRequestAllFields)188 TEST(GrpcClientObjectRequest, DeleteObjectRequestAllFields) {
189 storage_proto::DeleteObjectRequest expected;
190 EXPECT_TRUE(google::protobuf::TextFormat::ParseFromString(R"""(
191 bucket: "test-bucket-name"
192 object: "test-object-name"
193 generation: 1234
194 if_generation_match: { value: 2345 }
195 if_generation_not_match: { value: 3456 }
196 if_metageneration_match: { value: 42 }
197 if_metageneration_not_match: { value: 7 }
198 common_request_params: {
199 quota_user: "test-quota-user"
200 user_project: "test-user-project"
201 }
202 )""",
203 &expected));
204
205 DeleteObjectRequest request("test-bucket-name", "test-object-name");
206 request.set_multiple_options(
207 Generation(1234), IfGenerationMatch(2345), IfGenerationNotMatch(3456),
208 IfMetagenerationMatch(42), IfMetagenerationNotMatch(7),
209 UserProject("test-user-project"), QuotaUser("test-quota-user"),
210 UserIp("test-user-ip"));
211
212 auto actual = GrpcClient::ToProto(request);
213 EXPECT_THAT(actual, IsProtoEqual(expected));
214 }
215
TEST(GrpcClientObjectRequest,ResumableUploadRequestSimple)216 TEST(GrpcClientObjectRequest, ResumableUploadRequestSimple) {
217 google::storage::v1::StartResumableWriteRequest expected;
218 EXPECT_TRUE(google::protobuf::TextFormat::ParseFromString(R"""(
219 insert_object_spec: {
220 resource: {
221 name: "test-object"
222 bucket: "test-bucket"
223 }
224 })""",
225 &expected));
226
227 ResumableUploadRequest req("test-bucket", "test-object");
228
229 auto actual = GrpcClient::ToProto(req);
230 EXPECT_THAT(actual, IsProtoEqual(expected));
231 }
232
TEST(GrpcClientObjectRequest,ResumableUploadRequestAllFields)233 TEST(GrpcClientObjectRequest, ResumableUploadRequestAllFields) {
234 google::storage::v1::StartResumableWriteRequest expected;
235 EXPECT_TRUE(google::protobuf::TextFormat::ParseFromString(R"""(
236 insert_object_spec: {
237 resource: {
238 name: "test-object"
239 bucket: "test-bucket"
240 content_encoding: "test-content-encoding"
241 content_type: "test-content-type"
242 crc32c: { value: 576848900 }
243 md5_hash: "nhB9nTcrtoJr2B01QqQZ1g=="
244 kms_key_name: "test-kms-key-name"
245 }
246 predefined_acl: OBJECT_ACL_PRIVATE
247 if_generation_match: { value: 0 }
248 if_generation_not_match: { value: 7 }
249 if_metageneration_match: { value: 42 }
250 if_metageneration_not_match: { value: 84 }
251 projection: FULL
252 }
253 common_request_params: {
254 user_project: "test-user-project"
255 quota_user: "test-quota-user"
256 }
257
258 common_object_request_params: {
259 encryption_algorithm: "AES256"
260 # to get the key value use:
261 # /bin/echo -n "01234567" | openssl base64
262 # to get the key hash use (note this command goes over two lines):
263 # /bin/echo -n "01234567" | sha256sum | awk '{printf("%s", $1);}' |
264 # xxd -r -p | openssl base64
265 encryption_key: "MDEyMzQ1Njc="
266 encryption_key_sha256: "kkWSubED8U+DP6r7Z/SAaR8BmIqkV8AGF2n1jNRzEbw="
267 })""",
268 &expected));
269
270 ResumableUploadRequest req("test-bucket", "test-object");
271 req.set_multiple_options(
272 ContentType("test-content-type"),
273 ContentEncoding("test-content-encoding"),
274 Crc32cChecksumValue(
275 ComputeCrc32cChecksum("The quick brown fox jumps over the lazy dog")),
276 MD5HashValue(
277 ComputeMD5Hash("The quick brown fox jumps over the lazy dog")),
278 PredefinedAcl("private"), IfGenerationMatch(0), IfGenerationNotMatch(7),
279 IfMetagenerationMatch(42), IfMetagenerationNotMatch(84),
280 Projection::Full(), UserProject("test-user-project"),
281 QuotaUser("test-quota-user"), UserIp("test-user-ip"),
282 EncryptionKey::FromBinaryKey("01234567"),
283 KmsKeyName("test-kms-key-name"));
284
285 auto actual = GrpcClient::ToProto(req);
286 EXPECT_THAT(actual, IsProtoEqual(expected));
287 }
288
TEST(GrpcClientObjectRequest,ResumableUploadRequestWithObjectMetadataFields)289 TEST(GrpcClientObjectRequest, ResumableUploadRequestWithObjectMetadataFields) {
290 google::storage::v1::StartResumableWriteRequest expected;
291 EXPECT_TRUE(google::protobuf::TextFormat::ParseFromString(R"""(
292 insert_object_spec: {
293 resource: {
294 name: "test-object"
295 bucket: "test-bucket"
296 content_encoding: "test-content-encoding"
297 content_disposition: "test-content-disposition"
298 cache_control: "test-cache-control"
299 content_language: "test-content-language"
300 content_type: "test-content-type"
301 storage_class: "REGIONAL"
302 event_based_hold: { value: true }
303 metadata: { key: "test-metadata-key1" value: "test-value1" }
304 metadata: { key: "test-metadata-key2" value: "test-value2" }
305 temporary_hold: true
306 acl: { role: "test-role1" entity: "test-entity1" }
307 acl: { role: "test-role2" entity: "test-entity2" }
308 }
309 })""",
310 &expected));
311
312 ResumableUploadRequest req("test-bucket", "test-object");
313 std::vector<ObjectAccessControl> acls{
314 ObjectAccessControl().set_role("test-role1").set_entity("test-entity1"),
315 ObjectAccessControl().set_role("test-role2").set_entity("test-entity2")};
316 req.set_multiple_options(WithObjectMetadata(
317 ObjectMetadata()
318 .set_storage_class(storage_class::Regional())
319 .set_content_encoding("test-content-encoding")
320 .set_content_disposition("test-content-disposition")
321 .set_cache_control("test-cache-control")
322 .set_content_language("test-content-language")
323 .set_content_type("test-content-type")
324 .set_event_based_hold(true)
325 .upsert_metadata("test-metadata-key1", "test-value1")
326 .upsert_metadata("test-metadata-key2", "test-value2")
327 .set_storage_class(storage_class::Regional())
328 .set_temporary_hold(true)
329 .set_acl(std::move(acls))));
330
331 auto actual = GrpcClient::ToProto(req);
332 EXPECT_THAT(actual, IsProtoEqual(expected));
333 }
334
TEST(GrpcClientObjectRequest,QueryResumableUploadRequestSimple)335 TEST(GrpcClientObjectRequest, QueryResumableUploadRequestSimple) {
336 google::storage::v1::QueryWriteStatusRequest expected;
337 EXPECT_TRUE(google::protobuf::TextFormat::ParseFromString(
338 R"pb(
339 upload_id: "test-upload-id"
340 )pb",
341 &expected));
342
343 QueryResumableUploadRequest req("test-upload-id");
344
345 auto actual = GrpcClient::ToProto(req);
346 EXPECT_THAT(actual, IsProtoEqual(expected));
347 }
348
TEST(GrpcClientObjectRequest,ReadObjectRangeRequestSimple)349 TEST(GrpcClientObjectRequest, ReadObjectRangeRequestSimple) {
350 google::storage::v1::GetObjectMediaRequest expected;
351 EXPECT_TRUE(google::protobuf::TextFormat::ParseFromString(
352 R"pb(
353 bucket: "test-bucket" object: "test-object"
354 )pb",
355 &expected));
356
357 ReadObjectRangeRequest req("test-bucket", "test-object");
358
359 auto const actual = GrpcClient::ToProto(req);
360 EXPECT_THAT(actual, IsProtoEqual(expected));
361 }
362
TEST(GrpcClientObjectRequest,ReadObjectRangeRequestAllFields)363 TEST(GrpcClientObjectRequest, ReadObjectRangeRequestAllFields) {
364 google::storage::v1::GetObjectMediaRequest expected;
365 EXPECT_TRUE(google::protobuf::TextFormat::ParseFromString(
366 R"pb(
367 bucket: "test-bucket"
368 object: "test-object"
369 generation: 7
370 read_offset: 2000
371 read_limit: 1000
372 if_generation_match: { value: 1 }
373 if_generation_not_match: { value: 2 }
374 if_metageneration_match: { value: 3 }
375 if_metageneration_not_match: { value: 4 }
376 common_request_params: {
377 user_project: "test-user-project"
378 quota_user: "test-quota-user"
379 }
380 common_object_request_params: {
381 encryption_algorithm: "AES256"
382 # to get the key value use:
383 # /bin/echo -n "01234567" | openssl base64
384 # to get the key hash use (note this command goes over two lines):
385 # /bin/echo -n "01234567" | sha256sum | awk '{printf("%s", $1);}' |
386 # xxd -r -p | openssl base64
387 encryption_key: "MDEyMzQ1Njc="
388 encryption_key_sha256: "kkWSubED8U+DP6r7Z/SAaR8BmIqkV8AGF2n1jNRzEbw="
389 }
390 )pb",
391 &expected));
392
393 ReadObjectRangeRequest req("test-bucket", "test-object");
394 req.set_multiple_options(
395 Generation(7), ReadFromOffset(2000), ReadRange(1000, 3000),
396 IfGenerationMatch(1), IfGenerationNotMatch(2), IfMetagenerationMatch(3),
397 IfMetagenerationNotMatch(4), UserProject("test-user-project"),
398 UserProject("test-user-project"), QuotaUser("test-quota-user"),
399 UserIp("test-user-ip"), EncryptionKey::FromBinaryKey("01234567"));
400
401 auto const actual = GrpcClient::ToProto(req);
402 EXPECT_THAT(actual, IsProtoEqual(expected));
403 }
404
TEST(GrpcClientObjectRequest,ReadObjectRangeRequestReadLast)405 TEST(GrpcClientObjectRequest, ReadObjectRangeRequestReadLast) {
406 google::storage::v1::GetObjectMediaRequest expected;
407 EXPECT_TRUE(google::protobuf::TextFormat::ParseFromString(
408 R"pb(
409 bucket: "test-bucket" object: "test-object" read_offset: -2000
410 )pb",
411 &expected));
412
413 ReadObjectRangeRequest req("test-bucket", "test-object");
414 req.set_multiple_options(ReadLast(2000));
415
416 auto const actual = GrpcClient::ToProto(req);
417 EXPECT_THAT(actual, IsProtoEqual(expected));
418 }
419
TEST(GrpcClientObjectRequest,ReadObjectRangeRequestReadLastZero)420 TEST(GrpcClientObjectRequest, ReadObjectRangeRequestReadLastZero) {
421 google::storage::v1::GetObjectMediaRequest expected;
422 EXPECT_TRUE(google::protobuf::TextFormat::ParseFromString(
423 R"pb(
424 bucket: "test-bucket" object: "test-object"
425 )pb",
426 &expected));
427
428 ReadObjectRangeRequest req("test-bucket", "test-object");
429 req.set_multiple_options(ReadLast(0));
430
431 auto const actual = GrpcClient::ToProto(req);
432 EXPECT_THAT(actual, IsProtoEqual(expected));
433
434 testing_util::ScopedEnvironment grpc_endpoint{
435 "GOOGLE_CLOUD_CPP_STORAGE_GRPC_ENDPOINT", "locahost:1"};
436 GrpcClient client{ClientOptions{oauth2::CreateAnonymousCredentials()}};
437 StatusOr<std::unique_ptr<ObjectReadSource>> reader = client.ReadObject(req);
438 EXPECT_EQ(reader.status().code(), StatusCode::kOutOfRange);
439 }
440
441 } // namespace
442 } // namespace internal
443 } // namespace STORAGE_CLIENT_NS
444 } // namespace storage
445 } // namespace cloud
446 } // namespace google
447