Skip to content

Commit 0dd3d97

Browse files
committed
Merge pull request #272 from mziccard/add-remotegcshelper-test
Add RemoteGcsHelper unit tests
2 parents 99a4091 + 862ea6a commit 0dd3d97

2 files changed

Lines changed: 202 additions & 67 deletions

File tree

gcloud-java-storage/src/main/java/com/google/gcloud/storage/testing/RemoteGcsHelper.java

Lines changed: 33 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,17 @@
1616

1717
package com.google.gcloud.storage.testing;
1818

19-
import com.google.common.collect.ImmutableMap;
2019
import com.google.gcloud.AuthCredentials;
2120
import com.google.gcloud.storage.BlobInfo;
2221
import com.google.gcloud.RetryParams;
2322
import com.google.gcloud.storage.Storage;
2423
import com.google.gcloud.storage.StorageException;
2524
import com.google.gcloud.storage.StorageOptions;
26-
import com.google.gcloud.storage.testing.RemoteGcsHelper.Option.KeyFromClasspath;
2725

2826
import java.io.FileInputStream;
2927
import java.io.FileNotFoundException;
3028
import java.io.InputStream;
3129
import java.io.IOException;
32-
import java.util.Map;
3330
import java.util.UUID;
3431
import java.util.concurrent.Callable;
3532
import java.util.concurrent.ExecutionException;
@@ -96,35 +93,19 @@ public static String generateBucketName() {
9693
}
9794

9895
/**
99-
* Creates a {@code RemoteGcsHelper} object for the given project id and JSON key path.
96+
* Creates a {@code RemoteGcsHelper} object for the given project id and JSON key input stream.
10097
*
10198
* @param projectId id of the project to be used for running the tests
102-
* @param keyPath path to the JSON key to be used for running the tests
103-
* @param options creation options
99+
* @param keyStream input stream for a JSON key
104100
* @return A {@code RemoteGcsHelper} object for the provided options.
105-
* @throws com.google.gcloud.storage.testing.RemoteGcsHelper.GcsHelperException if the file pointed by
106-
* {@code keyPath} does not exist
101+
* @throws com.google.gcloud.storage.testing.RemoteGcsHelper.GcsHelperException if
102+
* {@code keyStream} is not a valid JSON key stream
107103
*/
108-
public static RemoteGcsHelper create(String projectId, String keyPath, Option... options)
104+
public static RemoteGcsHelper create(String projectId, InputStream keyStream)
109105
throws GcsHelperException {
110-
boolean keyFromClassPath = false;
111-
Map<Class<? extends Option>, Option> optionsMap = Option.asImmutableMap(options);
112-
if (optionsMap.containsKey(KeyFromClasspath.class)) {
113-
keyFromClassPath =
114-
((KeyFromClasspath) optionsMap.get(KeyFromClasspath.class)).keyFromClasspath();
115-
}
116106
try {
117-
InputStream keyFileStream;
118-
if (keyFromClassPath) {
119-
keyFileStream = RemoteGcsHelper.class.getResourceAsStream(keyPath);
120-
if (keyFileStream == null) {
121-
throw new FileNotFoundException(keyPath + " not found in classpath");
122-
}
123-
} else {
124-
keyFileStream = new FileInputStream(keyPath);
125-
}
126107
StorageOptions storageOptions = StorageOptions.builder()
127-
.authCredentials(AuthCredentials.createForJson(keyFileStream))
108+
.authCredentials(AuthCredentials.createForJson(keyStream))
128109
.projectId(projectId)
129110
.retryParams(RetryParams.builder()
130111
.retryMaxAttempts(10)
@@ -137,6 +118,28 @@ public static RemoteGcsHelper create(String projectId, String keyPath, Option...
137118
.readTimeout(60000)
138119
.build();
139120
return new RemoteGcsHelper(storageOptions);
121+
} catch (IOException ex) {
122+
if (log.isLoggable(Level.WARNING)) {
123+
log.log(Level.WARNING, ex.getMessage());
124+
}
125+
throw GcsHelperException.translate(ex);
126+
}
127+
}
128+
129+
/**
130+
* Creates a {@code RemoteGcsHelper} object for the given project id and JSON key path.
131+
*
132+
* @param projectId id of the project to be used for running the tests
133+
* @param keyPath path to the JSON key to be used for running the tests
134+
* @return A {@code RemoteGcsHelper} object for the provided options.
135+
* @throws com.google.gcloud.storage.testing.RemoteGcsHelper.GcsHelperException if the file
136+
* pointed by {@code keyPath} does not exist
137+
*/
138+
public static RemoteGcsHelper create(String projectId, String keyPath)
139+
throws GcsHelperException {
140+
try {
141+
InputStream keyFileStream = new FileInputStream(keyPath);
142+
return create(projectId, keyFileStream);
140143
} catch (FileNotFoundException ex) {
141144
if (log.isLoggable(Level.WARNING)) {
142145
log.log(Level.WARNING, ex.getMessage());
@@ -154,13 +157,12 @@ public static RemoteGcsHelper create(String projectId, String keyPath, Option...
154157
* Creates a {@code RemoteGcsHelper} object. Project id and path to JSON key are read from two
155158
* environment variables: {@code GCLOUD_TESTS_PROJECT_ID} and {@code GCLOUD_TESTS_KEY}.
156159
*
157-
* @param options creation options
158160
* @return A {@code RemoteGcsHelper} object for the provided options.
159-
* @throws com.google.gcloud.storage.testing.RemoteGcsHelper.GcsHelperException if environment variables
160-
* {@code GCLOUD_TESTS_PROJECT_ID} and {@code GCLOUD_TESTS_KEY} are not set or if the file
161-
* pointed by {@code GCLOUD_TESTS_KEY} does not exist
161+
* @throws com.google.gcloud.storage.testing.RemoteGcsHelper.GcsHelperException if environment
162+
* variables {@code GCLOUD_TESTS_PROJECT_ID} and {@code GCLOUD_TESTS_KEY} are not set or if
163+
* the file pointed by {@code GCLOUD_TESTS_KEY} does not exist
162164
*/
163-
public static RemoteGcsHelper create(Option... options) throws GcsHelperException {
165+
public static RemoteGcsHelper create() throws GcsHelperException {
164166
String projectId = System.getenv(PROJECT_ID_ENV_VAR);
165167
String keyPath = System.getenv(PRIVATE_KEY_ENV_VAR);
166168
if (projectId == null) {
@@ -177,7 +179,7 @@ public static RemoteGcsHelper create(Option... options) throws GcsHelperExceptio
177179
}
178180
throw new GcsHelperException(message);
179181
}
180-
return create(projectId, keyPath, options);
182+
return create(projectId, keyPath);
181183
}
182184

183185
private static class DeleteBucketTask implements Callable<Boolean> {
@@ -210,42 +212,6 @@ public Boolean call() throws Exception {
210212
}
211213
}
212214

213-
public static abstract class Option implements java.io.Serializable {
214-
215-
private static final long serialVersionUID = 8849118657896662369L;
216-
217-
public static final class KeyFromClasspath extends Option {
218-
219-
private static final long serialVersionUID = -5506049413185246821L;
220-
221-
private final boolean keyFromClasspath;
222-
223-
public KeyFromClasspath(boolean keyFromClasspath) {
224-
this.keyFromClasspath = keyFromClasspath;
225-
}
226-
227-
public boolean keyFromClasspath() {
228-
return keyFromClasspath;
229-
}
230-
}
231-
232-
Option() {
233-
// package protected
234-
}
235-
236-
public static KeyFromClasspath keyFromClassPath() {
237-
return new KeyFromClasspath(true);
238-
}
239-
240-
static Map<Class<? extends Option>, Option> asImmutableMap(Option... options) {
241-
ImmutableMap.Builder<Class<? extends Option>, Option> builder = ImmutableMap.builder();
242-
for (Option option : options) {
243-
builder.put(option.getClass(), option);
244-
}
245-
return builder.build();
246-
}
247-
}
248-
249215
public static class GcsHelperException extends RuntimeException {
250216

251217
private static final long serialVersionUID = -7756074894502258736L;
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/*
2+
* Copyright 2015 Google Inc. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.gcloud.storage;
18+
19+
import static org.junit.Assert.assertEquals;
20+
import static org.junit.Assert.assertTrue;
21+
22+
import com.google.common.collect.ImmutableList;
23+
import com.google.gcloud.storage.testing.RemoteGcsHelper;
24+
25+
import java.io.ByteArrayInputStream;
26+
import java.io.InputStream;
27+
import java.nio.file.Files;
28+
import java.nio.file.Paths;
29+
import java.util.Iterator;
30+
import java.util.List;
31+
import java.util.UUID;
32+
import java.util.concurrent.ExecutionException;
33+
import java.util.concurrent.TimeUnit;
34+
35+
import org.easymock.EasyMock;
36+
import org.junit.BeforeClass;
37+
import org.junit.Rule;
38+
import org.junit.Test;
39+
import org.junit.rules.ExpectedException;
40+
41+
public class RemoteGcsHelperTest {
42+
43+
private static final String BUCKET_NAME = "bucket-name";
44+
private static final String PROJECT_ID = "project-id";
45+
private static final String JSON_KEY = "{\n"
46+
+ " \"private_key_id\": \"somekeyid\",\n"
47+
+ " \"private_key\": \"-----BEGIN PRIVATE KEY-----\\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggS"
48+
+ "kAgEAAoIBAQC+K2hSuFpAdrJI\\nnCgcDz2M7t7bjdlsadsasad+fvRSW6TjNQZ3p5LLQY1kSZRqBqylRkzteMOyHg"
49+
+ "aR\\n0Pmxh3ILCND5men43j3h4eDbrhQBuxfEMalkG92sL+PNQSETY2tnvXryOvmBRwa/\\nQP/9dJfIkIDJ9Fw9N4"
50+
+ "Bhhhp6mCcRpdQjV38H7JsyJ7lih/oNjECgYAt\\nknddadwkwewcVxHFhcZJO+XWf6ofLUXpRwiTZakGMn8EE1uVa2"
51+
+ "LgczOjwWHGi99MFjxSer5m9\\n1tCa3/KEGKiS/YL71JvjwX3mb+cewlkcmweBKZHM2JPTk0ZednFSpVZMtycjkbLa"
52+
+ "\\ndYOS8V85AgMBewECggEBAKksaldajfDZDV6nGqbFjMiizAKJolr/M3OQw16K6o3/\\n0S31xIe3sSlgW0+UbYlF"
53+
+ "4U8KifhManD1apVSC3csafaspP4RZUHFhtBywLO9pR5c\\nr6S5aLp+gPWFyIp1pfXbWGvc5VY/v9x7ya1VEa6rXvL"
54+
+ "sKupSeWAW4tMj3eo/64ge\\nsdaceaLYw52KeBYiT6+vpsnYrEkAHO1fF/LavbLLOFJmFTMxmsNaG0tuiJHgjshB\\"
55+
+ "n82DpMCbXG9YcCgI/DbzuIjsdj2JC1cascSP//3PmefWysucBQe7Jryb6NQtASmnv\\nCdDw/0jmZTEjpe4S1lxfHp"
56+
+ "lAhHFtdgYTvyYtaLZiVVkCgYEA8eVpof2rceecw/I6\\n5ng1q3Hl2usdWV/4mZMvR0fOemacLLfocX6IYxT1zA1FF"
57+
+ "JlbXSRsJMf/Qq39mOR2\\nSpW+hr4jCoHeRVYLgsbggtrevGmILAlNoqCMpGZ6vDmJpq6ECV9olliDvpPgWOP+\\nm"
58+
+ "YPDreFBGxWvQrADNbRt2dmGsrsCgYEAyUHqB2wvJHFqdmeBsaacewzV8x9WgmeX\\ngUIi9REwXlGDW0Mz50dxpxcK"
59+
+ "CAYn65+7TCnY5O/jmL0VRxU1J2mSWyWTo1C+17L0\\n3fUqjxL1pkefwecxwecvC+gFFYdJ4CQ/MHHXU81Lwl1iWdF"
60+
+ "Cd2UoGddYaOF+KNeM\\nHC7cmqra+JsCgYEAlUNywzq8nUg7282E+uICfCB0LfwejuymR93CtsFgb7cRd6ak\\nECR"
61+
+ "8FGfCpH8ruWJINllbQfcHVCX47ndLZwqv3oVFKh6pAS/vVI4dpOepP8++7y1u\\ncoOvtreXCX6XqfrWDtKIvv0vjl"
62+
+ "HBhhhp6mCcRpdQjV38H7JsyJ7lih/oNjECgYAt\\nkndj5uNl5SiuVxHFhcZJO+XWf6ofLUregtevZakGMn8EE1uVa"
63+
+ "2AY7eafmoU/nZPT\\n00YB0TBATdCbn/nBSuKDESkhSg9s2GEKQZG5hBmL5uCMfo09z3SfxZIhJdlerreP\\nJ7gSi"
64+
+ "dI12N+EZxYd4xIJh/HFDgp7RRO87f+WJkofMQKBgGTnClK1VMaCRbJZPriw\\nEfeFCoOX75MxKwXs6xgrw4W//AYG"
65+
+ "GUjDt83lD6AZP6tws7gJ2IwY/qP7+lyhjEqN\\nHtfPZRGFkGZsdaksdlaksd323423d+15/UvrlRSFPNj1tWQmNKk"
66+
+ "XyRDW4IG1Oa2p\\nrALStNBx5Y9t0/LQnFI4w3aG\\n-----END PRIVATE KEY-----\\n\",\n"
67+
+ " \"client_email\": \"[email protected]\",\n"
68+
+ " \"client_id\": \"someclientid.apps.googleusercontent.com\",\n"
69+
+ " \"type\": \"service_account\"\n"
70+
+ "}";
71+
private static final InputStream JSON_KEY_STREAM = new ByteArrayInputStream(JSON_KEY.getBytes());
72+
private static final List<BlobInfo> BLOB_LIST = ImmutableList.of(
73+
BlobInfo.builder(BUCKET_NAME, "n1").build(),
74+
BlobInfo.builder(BUCKET_NAME, "n2").build());
75+
private static final StorageException RETRYABLE_EXCEPTION = new StorageException(409, "", true);
76+
private static final StorageException FATAL_EXCEPTION = new StorageException(500, "", false);
77+
private static final ListResult<BlobInfo> BLOB_LIST_RESULT = new ListResult<BlobInfo>() {
78+
79+
@Override
80+
public String nextPageCursor() {
81+
return "listResult";
82+
}
83+
84+
@Override
85+
public ListResult<BlobInfo> nextPage() {
86+
return null;
87+
}
88+
89+
@Override
90+
public Iterator<BlobInfo> iterator() {
91+
return BLOB_LIST.iterator();
92+
}
93+
};
94+
private static String KEY_PATH = "/does/not/exist/key." + UUID.randomUUID().toString() + ".json";
95+
96+
@Rule
97+
public ExpectedException thrown = ExpectedException.none();
98+
99+
@BeforeClass
100+
public static void beforeClass() {
101+
while (Files.exists(Paths.get(JSON_KEY))) {
102+
KEY_PATH = "/does/not/exist/key." + UUID.randomUUID().toString() + ".json";
103+
}
104+
}
105+
106+
@Test
107+
public void testForceDelete() throws InterruptedException, ExecutionException {
108+
Storage storageMock = EasyMock.createMock(Storage.class);
109+
EasyMock.expect(storageMock.list(BUCKET_NAME)).andReturn(BLOB_LIST_RESULT);
110+
for (BlobInfo info : BLOB_LIST) {
111+
EasyMock.expect(storageMock.delete(BUCKET_NAME, info.name())).andReturn(true);
112+
}
113+
EasyMock.expect(storageMock.delete(BUCKET_NAME)).andReturn(true);
114+
EasyMock.replay(storageMock);
115+
assertTrue(RemoteGcsHelper.forceDelete(storageMock, BUCKET_NAME, 5, TimeUnit.SECONDS));
116+
EasyMock.verify(storageMock);
117+
}
118+
119+
@Test
120+
public void testForceDeleteTimeout() throws InterruptedException, ExecutionException {
121+
Storage storageMock = EasyMock.createMock(Storage.class);
122+
EasyMock.expect(storageMock.list(BUCKET_NAME)).andReturn(BLOB_LIST_RESULT).anyTimes();
123+
for (BlobInfo info : BLOB_LIST) {
124+
EasyMock.expect(storageMock.delete(BUCKET_NAME, info.name())).andReturn(true).anyTimes();
125+
}
126+
EasyMock.expect(storageMock.delete(BUCKET_NAME)).andThrow(RETRYABLE_EXCEPTION).anyTimes();
127+
EasyMock.replay(storageMock);
128+
assertTrue(!RemoteGcsHelper.forceDelete(storageMock, BUCKET_NAME, 50, TimeUnit.MICROSECONDS));
129+
EasyMock.verify(storageMock);
130+
}
131+
132+
@Test
133+
public void testForceDeleteFail() throws InterruptedException, ExecutionException {
134+
Storage storageMock = EasyMock.createMock(Storage.class);
135+
EasyMock.expect(storageMock.list(BUCKET_NAME)).andReturn(BLOB_LIST_RESULT);
136+
for (BlobInfo info : BLOB_LIST) {
137+
EasyMock.expect(storageMock.delete(BUCKET_NAME, info.name())).andReturn(true);
138+
}
139+
EasyMock.expect(storageMock.delete(BUCKET_NAME)).andThrow(FATAL_EXCEPTION);
140+
EasyMock.replay(storageMock);
141+
thrown.expect(ExecutionException.class);
142+
try {
143+
RemoteGcsHelper.forceDelete(storageMock, BUCKET_NAME, 5, TimeUnit.SECONDS);
144+
} finally {
145+
EasyMock.verify(storageMock);
146+
}
147+
}
148+
149+
@Test
150+
public void testCreateFromStream() {
151+
RemoteGcsHelper helper = RemoteGcsHelper.create(PROJECT_ID, JSON_KEY_STREAM);
152+
StorageOptions options = helper.options();
153+
assertEquals(PROJECT_ID, options.projectId());
154+
assertEquals(60000, options.connectTimeout());
155+
assertEquals(60000, options.readTimeout());
156+
assertEquals(10, options.retryParams().getRetryMaxAttempts());
157+
assertEquals(6, options.retryParams().getRetryMinAttempts());
158+
assertEquals(30000, options.retryParams().getMaxRetryDelayMillis());
159+
assertEquals(120000, options.retryParams().getTotalRetryPeriodMillis());
160+
assertEquals(250, options.retryParams().getInitialRetryDelayMillis());
161+
}
162+
163+
@Test
164+
public void testCreateNoKey() {
165+
thrown.expect(RemoteGcsHelper.GcsHelperException.class);
166+
thrown.expectMessage(KEY_PATH + " (No such file or directory)");
167+
RemoteGcsHelper.create(PROJECT_ID, KEY_PATH);
168+
}
169+
}

0 commit comments

Comments
 (0)