/*
* Copyright 2015 MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mongodb.client;
import com.mongodb.AuthenticationMechanism;
import com.mongodb.ConnectionString;
import com.mongodb.MongoCredential;
import junit.framework.TestCase;
import org.bson.BsonDocument;
import org.bson.BsonValue;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import util.JsonPoweredTestHelper;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
// See https://github.com/mongodb/specifications/tree/master/source/crud/tests
@RunWith(Parameterized.class)
public class ConnectionStringTest extends TestCase {
private final String filename;
private final String description;
private final String input;
private final BsonDocument definition;
public ConnectionStringTest(final String filename, final String description, final String input,
final BsonDocument definition) {
this.filename = filename;
this.description = description;
this.input = input;
this.definition = definition;
}
@Test
public void shouldPassAllOutcomes() {
if (filename.equals("invalid-uris.json")) {
testInvalidUris();
} else if (filename.equals("valid-auth.json")) {
testValidAuth();
} else if (filename.equals("valid-host_identifiers.json")) {
testValidHostIdentifiers();
} else if (filename.equals("valid-options.json")) {
testValidOptions();
} else if (filename.equals("valid-unix_socket-absolute.json")) {
testInvalidUris(); // Sockets aren't supported
} else if (filename.equals("valid-unix_socket-relative.json")) {
testInvalidUris(); // Sockets aren't supported
} else if (filename.equals("valid-warnings.json")) {
testValidHostIdentifiers();
if (!definition.get("options").isNull()) {
testValidOptions();
}
} else {
throw new IllegalArgumentException("Unsupported file: " + filename);
}
}
@Parameterized.Parameters(name = "{1}")
public static Collection<Object[]> data() throws URISyntaxException, IOException {
List<Object[]> data = new ArrayList<Object[]>();
for (File file : JsonPoweredTestHelper.getTestFiles("/connection-string")) {
BsonDocument testDocument = JsonPoweredTestHelper.getTestDocument(file);
for (BsonValue test : testDocument.getArray("tests")) {
data.add(new Object[]{file.getName(), test.asDocument().getString("description").getValue(),
test.asDocument().getString("uri").getValue(), test.asDocument()});
}
}
return data;
}
private void testInvalidUris() {
Throwable expectedError = null;
try {
new ConnectionString(input);
} catch (Throwable t) {
expectedError = t;
}
assertTrue(String.format("Connection string '%s' should have throw an exception", input),
expectedError instanceof IllegalArgumentException);
}
private void testValidHostIdentifiers() {
ConnectionString connectionString = null;
try {
connectionString = new ConnectionString(input);
} catch (Throwable t) {
assertTrue(String.format("Connection string '%s' should not have throw an exception: %s", input, t.toString()), false);
}
assertExpectedHosts(connectionString.getHosts());
}
private void testValidOptions() {
ConnectionString connectionString = null;
try {
connectionString = new ConnectionString(input);
} catch (Throwable t) {
assertTrue(String.format("Connection string '%s' should not have throw an exception: %s", input, t.toString()), false);
}
for (Map.Entry<String, BsonValue> option : definition.getDocument("options").entrySet()) {
if (option.getKey().equals("authmechanism")) {
String expected = option.getValue().asString().getValue();
String actual = connectionString.getCredentialList().get(0).getAuthenticationMechanism().getMechanismName();
assertEquals(expected, actual);
} else if (option.getKey().equals("replicaset")) {
String expected = option.getValue().asString().getValue();
assertEquals(expected, connectionString.getRequiredReplicaSetName());
} else if (option.getKey().equals("wtimeoutms")) {
int expected = option.getValue().asInt32().getValue();
assertEquals(expected, connectionString.getWriteConcern().getWTimeout(TimeUnit.MILLISECONDS).intValue());
} else {
assertTrue(String.format("Unsupported option '%s' in '%s'", option.getKey(), input), false);
}
}
}
private void testValidAuth() {
ConnectionString connectionString = null;
try {
connectionString = new ConnectionString(input);
} catch (Throwable t) {
if (description.contains("without password")) {
// We don't allow null passwords without setting the authentication mechanism.
return;
} else {
assertTrue(String.format("Connection string '%s' should not have throw an exception: %s", input, t.toString()), false);
}
}
assertString("auth.db", getAuthDB(connectionString));
assertString("auth.username", connectionString.getUsername());
// Passwords for certain auth mechanisms are ignored.
String password = null;
if (connectionString.getPassword() != null) {
password = new String(connectionString.getPassword());
}
List<MongoCredential> credentials = connectionString.getCredentialList();
if (credentials.size() > 0) {
AuthenticationMechanism mechanism = credentials.get(0).getAuthenticationMechanism();
if (mechanism == null) {
assertString("auth.password", password);
} else {
switch (mechanism) {
case PLAIN:
case MONGODB_CR:
case SCRAM_SHA_1:
assertString("auth.password", password);
break;
default:
// Ignore the password field.
}
}
} else {
assertString("auth.password", password);
}
}
private void assertString(final String key, final String actual) {
BsonValue expected = definition;
if (key.contains(".")) {
for (String subKey : key.split("\\.")) {
expected = expected.asDocument().get(subKey);
}
} else {
expected = expected.asDocument().get(key);
}
if (expected.isNull()) {
assertTrue(String.format("%s should be null", key), actual == null);
} else if (expected.isString()) {
String expectedString = expected.asString().getValue();
assertTrue(String.format("%s should be %s but was %s", key, actual, expectedString), actual.equals(expectedString));
} else {
assertTrue(String.format("%s should be %s but was %s", key, actual, expected), false);
}
}
private void assertExpectedHosts(final List<String> hosts) {
List<String> cleanedHosts = new ArrayList<String>();
for (String host : hosts) {
if (host.startsWith("[")) {
int idx = host.indexOf("]");
cleanedHosts.add(host.substring(1, idx) + host.substring(idx + 1));
} else {
cleanedHosts.add(host);
}
}
List<String> expectedHosts = new ArrayList<String>();
for (BsonValue rawHost : definition.getArray("hosts")) {
BsonDocument hostDoc = rawHost.asDocument();
String host = hostDoc.getString("host").getValue();
String port = "";
if (!hostDoc.get("port").isNull()) {
port = ":" + hostDoc.getInt32("port").getValue();
}
expectedHosts.add(host + port);
}
assertEquals(expectedHosts, cleanedHosts);
}
private String getAuthDB(final ConnectionString connectionString) {
if (connectionString.getCollection() != null) {
return connectionString.getDatabase() + "." + connectionString.getCollection();
}
return connectionString.getDatabase();
}
}