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