-
Notifications
You must be signed in to change notification settings - Fork 74
Expand file tree
/
Copy pathDatabaseClientPropertySourceTest.java
More file actions
249 lines (204 loc) · 8.86 KB
/
Copy pathDatabaseClientPropertySourceTest.java
File metadata and controls
249 lines (204 loc) · 8.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
/*
* Copyright (c) 2010-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
*/
package com.marklogic.client.impl;
import com.marklogic.client.DatabaseClient;
import com.marklogic.client.DatabaseClientBuilder;
import com.marklogic.client.DatabaseClientFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import java.util.HashMap;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
/**
* Intent of this test is to cover code that cannot be covered by DatabaseClientBuilderTest.
*/
public class DatabaseClientPropertySourceTest {
private Map<String, Object> props = new HashMap<>();
private DatabaseClientFactory.Bean bean;
private static final String PREFIX = DatabaseClientBuilder.PREFIX;
private static final String VERIFIER_PROPERTY = PREFIX + "sslHostnameVerifier";
private static final String CONNECTION_TYPE_PROPERTY = PREFIX + "connectionType";
@BeforeEach
void beforeEach() {
props.put(PREFIX + "authType", "digest");
props.put(PREFIX + "username", "someuser");
props.put(PREFIX + "password", "someword");
}
@Test
void anyHostnameVerifier() {
props.put(VERIFIER_PROPERTY, "any");
bean = buildBean();
assertEquals(DatabaseClientFactory.SSLHostnameVerifier.ANY, bean.getSecurityContext().getSSLHostnameVerifier());
}
@Test
void commonHostnameVerifier() {
props.put(VERIFIER_PROPERTY, "COMmon");
bean = buildBean();
assertEquals(DatabaseClientFactory.SSLHostnameVerifier.COMMON, bean.getSecurityContext().getSSLHostnameVerifier());
}
@Test
void strictHostnameVerifier() {
props.put(VERIFIER_PROPERTY, "STRICT");
bean = buildBean();
assertEquals(DatabaseClientFactory.SSLHostnameVerifier.STRICT, bean.getSecurityContext().getSSLHostnameVerifier());
}
@Test
void gatewayConnectionType() {
props.put(CONNECTION_TYPE_PROPERTY, "gateway");
bean = buildBean();
assertEquals(DatabaseClient.ConnectionType.GATEWAY, bean.getConnectionType());
props.put(CONNECTION_TYPE_PROPERTY, "GATEWAY");
bean = buildBean();
assertEquals(DatabaseClient.ConnectionType.GATEWAY, bean.getConnectionType());
}
@Test
void stringPort() {
props.put(PREFIX + "port", "8000");
bean = buildBean();
assertEquals(8000, bean.getPort());
}
@Test
void cloudAuthWithNoSslInputs() {
props.put(PREFIX + "authType", "cloud");
props.put(PREFIX + "cloud.apiKey", "abc123");
props.put(PREFIX + "basePath", "/my/path");
bean = buildBean();
assertEquals("/my/path", bean.getBasePath());
assertTrue(bean.getSecurityContext() instanceof DatabaseClientFactory.ProgressDataCloudAuthContext);
DatabaseClientFactory.ProgressDataCloudAuthContext context = (DatabaseClientFactory.ProgressDataCloudAuthContext) bean.getSecurityContext();
assertEquals("abc123", context.getApiKey());
assertNotNull(context.getSSLContext(), "If cloud is chosen with no SSL protocol or context, the default JVM " +
"SSLContext should be used");
assertNotNull(context.getSSLContext().getSocketFactory(), "The default JVM SSLContext should already be " +
"initialized and thus it should be possible to get a socket factory from it");
assertNotNull(context.getTrustManager(), "If cloud is chosen with no SSL protocol or context, the default JVM " +
"trust manager should be used");
}
@Test
void cloudWithNonNumericDuration() {
props.put(PREFIX + "authType", "cloud");
props.put(PREFIX + "cloud.apiKey", "abc123");
props.put(PREFIX + "basePath", "/my/path");
props.put(PREFIX + "cloud.tokenDuration", "abc");
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> buildBean());
assertEquals("Cloud token duration must be numeric", ex.getMessage());
}
@Test
void disableGzippedResponses() {
final String prop = PREFIX + "disableGzippedResponses";
props.put(PREFIX + prop, "true");
// Won't throw an error, but we can't verify the results because the list of configurators in
// DatabaseClientFactory is private.
buildBean();
// Verifying this doesn't throw an error either; the impl should be using Boolean.parseBoolean which only cares
// if the value equals 'true'.
props.put(prop, "123");
buildBean();
}
@Test
void connectionString() {
useConnectionString("user:password@localhost:8000");
DatabaseClientFactory.Bean bean = buildBean();
assertEquals("localhost", bean.getHost());
assertEquals(8000, bean.getPort());
assertNull(bean.getDatabase());
DatabaseClientFactory.DigestAuthContext context = (DatabaseClientFactory.DigestAuthContext) bean.getSecurityContext();
assertEquals("user", context.getUser());
assertEquals("password", context.getPassword());
}
@Test
void connectionStringWithDatabase() {
useConnectionString("user:password@localhost:8000/Documents");
DatabaseClientFactory.Bean bean = buildBean();
assertEquals("localhost", bean.getHost());
assertEquals(8000, bean.getPort());
assertEquals("Documents", bean.getDatabase());
DatabaseClientFactory.DigestAuthContext context = (DatabaseClientFactory.DigestAuthContext) bean.getSecurityContext();
assertEquals("user", context.getUser());
assertEquals("password", context.getPassword());
}
@Test
void connectionStringWithSeparateDatabase() {
useConnectionString("user:password@localhost:8000");
props.put(PREFIX + "database", "SomeDatabase");
DatabaseClientFactory.Bean bean = buildBean();
assertEquals("localhost", bean.getHost());
assertEquals(8000, bean.getPort());
assertEquals("SomeDatabase", bean.getDatabase());
}
@Test
void usernameAndPasswordBothRequireDecoding() {
useConnectionString("test-user%40:sp%40r%3Ak@localhost:8000/Documents");
DatabaseClientFactory.Bean bean = buildBean();
assertEquals("localhost", bean.getHost());
assertEquals(8000, bean.getPort());
assertEquals("Documents", bean.getDatabase());
DatabaseClientFactory.DigestAuthContext context = (DatabaseClientFactory.DigestAuthContext) bean.getSecurityContext();
assertEquals("test-user@", context.getUser());
assertEquals("sp@r:k", context.getPassword(), "Verifies that the user must encode username and password " +
"values that contain ':' or '@'. The builder is then expected to decode them into the correct values.");
}
@ParameterizedTest
@ValueSource(strings = {
"user@host@port",
"user@host:port",
"user:password@host",
"user:password:something@host:port",
"user:password@host:port:something"
})
void invalidConnectionString(String connectionString) {
useConnectionString(connectionString);
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> buildBean());
assertEquals("Invalid value for connection string; must be username:password@host:port/optionalDatabaseName",
ex.getMessage());
}
@Test
void nonNumericPortInConnectionString() {
useConnectionString("user:password@host:nonNumericPort");
IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, () -> buildBean());
assertEquals("Invalid value for connection string; port must be numeric, but was 'nonNumericPort'", ex.getMessage());
}
@Test
void hostTakesPrecedence() {
props.put(PREFIX + "host", "somehost");
props.put(PREFIX + "connectionString", "user:password@localhost:8000/Documents");
DatabaseClientFactory.Bean bean = buildBean();
assertEquals("somehost", bean.getHost(), "This allows a user to use a connection string as a starting " +
"point, and then override the host for a direct connection to a particular host in a cluster. This " +
"capability is used by our Spark connector to support direct connections to multiple hosts.");
}
private void useConnectionString(String connectionString) {
props.put(PREFIX + "connectionString", connectionString);
}
private DatabaseClientFactory.Bean buildBean() {
DatabaseClientPropertySource source = new DatabaseClientPropertySource(propertyName -> props.get(propertyName));
return source.newClientBean();
}
@Test
void readAndWriteTimeoutsFromPropertySource() {
props.put(PREFIX + "readTimeoutMillis", 5000L);
props.put(PREFIX + "writeTimeoutMillis", 10000L);
DatabaseClientFactory.Bean bean = buildBean();
assertEquals(5000L, bean.getReadTimeoutMillis());
assertEquals(10000L, bean.getWriteTimeoutMillis());
}
@Test
void readAndWriteTimeoutsFromPropertySourceAsStrings() {
props.put(PREFIX + "readTimeoutMillis", "15000");
props.put(PREFIX + "writeTimeoutMillis", "30000");
DatabaseClientFactory.Bean bean = buildBean();
assertEquals(15000L, bean.getReadTimeoutMillis());
assertEquals(30000L, bean.getWriteTimeoutMillis());
}
@Test
void readAndWriteTimeoutsFromPropertySourceAsIntegers() {
props.put(PREFIX + "readTimeoutMillis", 5000); // Integer, not Long
props.put(PREFIX + "writeTimeoutMillis", 10000);
DatabaseClientFactory.Bean bean = buildBean();
assertEquals(5000L, bean.getReadTimeoutMillis());
assertEquals(10000L, bean.getWriteTimeoutMillis());
}
}