/*
* Copyright © 2013. Palomino Labs (http://palominolabs.com)
*
* 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.palominolabs.crm.sf.soap;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.palominolabs.crm.sf.core.Id;
import com.palominolabs.crm.sf.core.SObject;
import com.palominolabs.crm.sf.soap.jaxwsstub.partner.ExceptionCode;
import com.palominolabs.crm.sf.soap.jaxwsstub.partner.StatusCodeType;
import com.palominolabs.crm.sf.soap.jaxwsstub.partner.UnexpectedErrorFault_Exception;
import com.palominolabs.crm.sf.testutil.SObjectUtil;
import com.palominolabs.crm.sf.testutil.TestFixtureUtils;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.Duration;
import org.joda.time.ReadableDuration;
import org.junit.Before;
import org.junit.Test;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static com.google.common.collect.Lists.newArrayList;
import static com.palominolabs.crm.sf.soap.TestConnectionUtils.getConnectionBundle;
import static com.palominolabs.crm.sf.testutil.ConnectionTestSfUserProps.getPropVal;
import static com.palominolabs.testutil.BooleanAssert.assertBooleanEquals;
import static com.palominolabs.testutil.CollectionAssert.assertSetEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@SuppressWarnings("ConstantConditions")
public class PartnerConnectionImplTest {
private static final String USER =
getPropVal("com.palominolabs.test.crm.sf.conn.org2MainUser.sfLogin");
private static final String PASSWORD =
getPropVal("com.palominolabs.test.crm.sf.conn.org2MainUser.sfPassword");
static final String TEST_PARTNER_KEY = "testPartnerKey";
static final int MAX_API_CALLS = 4;
private PartnerConnectionImpl conn;
private ConnectionBundleImpl bundle;
@Before
public void setUp() throws SecurityException, IllegalArgumentException, NoSuchFieldException {
this.bundle = getConnectionBundle(USER, PASSWORD);
this.conn = (PartnerConnectionImpl) bundle.getPartnerConnection();
// com.sun.xml.ws.transport.http.client.HttpTransportPipe.dump = true;
}
@Test
public void testBadLogin() {
final PartnerConnection badConn = getConnectionBundle(USER, PASSWORD + "x").getPartnerConnection();
try {
badConn.getServerTimestamp();
fail();
} catch (ApiException e) {
assertEquals("Bad credentials for user '" + USER + "'", e.getMessage());
assertEquals(USER, e.getUsername());
assertEquals(ExceptionCode.INVALID___LOGIN, e.getApiFaultCode());
assertEquals("Invalid username, password, security token; or user locked out.", e.getApiFaultMessage());
//noinspection ConstantConditions
assertEquals(e.getClass().getName() + ": " + e.getMessage() + " {username='" + e.getUsername() +
"', faultCode=" + e.getApiFaultCode().value() + ", faultMessage='" + e.getApiFaultMessage() +
"', row=null, column=null}", e.toString());
}
}
@Test
public void testCount() throws ApiException {
assertEquals(12, this.conn.count("Account"));
}
@Test
public void testCountWithConstraint() throws ApiException, InterruptedException {
assertEquals(8, this.conn.count("Account", "AnnualRevenue > 100000"));
}
@Test
public void testCountAll() throws ApiException {
// no deleted records, so same as count
assertEquals(12, this.conn.countAll("Account"));
}
@Test
public void testCountAllWithConstraint() throws ApiException {
// no deleted records, so same as count
assertEquals(8, this.conn.countAll("Account", "AnnualRevenue > 100000"));
}
@Test
public void testCreateWithNoFields() throws ApiException {
int origCount = this.conn.count("Contact");
List<SObject> sObjs = new ArrayList<SObject>();
SObject sObj = PartnerSObjectImpl.getNew("Contact");
sObjs.add(sObj);
List<SaveResult> results = this.conn.create(sObjs);
assertEquals(1, results.size());
SaveResult result = results.get(0);
assertFalse(result.isSuccess());
assertNull(result.getId());
assertEquals(1, result.getErrors().size());
PartnerApiError error = result.getErrors().get(0);
assertEquals(StatusCodeType.REQUIRED___FIELD___MISSING, error.getStatusCode());
assertEquals("Required fields are missing: [LastName]", error.getMessage());
assertEquals(1, error.getFields().size());
assertEquals("LastName", error.getFields().get(0));
int newCount = this.conn.count("Contact");
assertEquals(origCount, newCount);
}
@Test
public void testCreate_WithInvalidFields_IncludesRowColumnAndCorrectlyFormattedMessage() throws ApiException {
String key = "gecko";
String value = "shirt";
List<SObject> sObjs = new ArrayList<SObject>();
SObject sObj = PartnerSObjectImpl.getNew("Contact");
sObj.setField(key, value);
sObjs.add(sObj);
try {
this.conn.create(sObjs);
fail();
} catch (ApiException e) {
// This seems wrong, and is stupid, but it is right
assertEquals(-1, e.getApiFaultColumn().intValue());
assertEquals(-1, e.getApiFaultRow().intValue());
// If this fails, the w2l-uploader might break because we depend on the format of this message
// So, look at the SalseforceAttemptCallable
assertEquals(
"No such column 'gecko' on entity 'Contact'. If you are attempting to use a custom field, be sure " +
"to append the '__c' after the custom field name. Please reference your WSDL or the " +
"describe call for the appropriate names.", e.getApiFaultMessage());
}
}
@Test
public void testDeleteBadId() throws ApiException {
List<Id> idList = new ArrayList<Id>();
Id id = new Id("00Q7zzz000Kj4Jn");
idList.add(id);
List<DeleteResult> results = this.conn.delete(idList);
assertEquals(1, results.size());
DeleteResult res = results.get(0);
assertFalse(res.isSuccess());
assertNull(res.getId());
assertEquals(1, res.getErrors().size());
PartnerApiError error = res.getErrors().get(0);
assertEquals(StatusCodeType.MALFORMED___ID, error.getStatusCode());
assertEquals("bad id " + id.toString(), error.getMessage());
assertEquals(0, error.getFields().size());
}
@Test
public void testDescribeGlobal() throws ApiException {
DescribeGlobalResult actual = this.conn.describeGlobal();
DescribeGlobalResult expected =
(DescribeGlobalResult) TestFixtureUtils
.loadFixtures("/sObjectFixtures/ConnectionTests/describeGlobalResult.xml");
assertEquals(expected.getEncoding(), actual.getEncoding());
assertEquals(expected.getMaxBatchSize(), actual.getMaxBatchSize());
assertSetEquals(expected.getSObjectNames(), actual.getSObjectNames());
for (int i = 0; i < expected.getSObjectTypes().size(); i++) {
final GlobalSObjectDescription exType = expected.getSObjectTypes().get(i);
final GlobalSObjectDescription acType = actual.getSObjectTypes().get(i);
String message = "exType: " + exType.getLabel() + "; acType: " + acType.getLabel();
assertEquals(message, exType.getKeyPrefix(), acType.getKeyPrefix());
assertEquals(message, exType.getLabel(), acType.getLabel());
assertEquals(message, exType.getLabelPlural(), acType.getLabelPlural());
assertEquals(message, exType.getName(), acType.getName());
assertBooleanEquals(message, exType.isActivateable(), acType.isActivateable());
assertBooleanEquals(message, exType.isCreateable(), acType.isCreateable());
assertBooleanEquals(message, exType.isCustom(), acType.isCustom());
assertBooleanEquals(message, exType.isCustomSetting(), acType.isCustomSetting());
assertBooleanEquals(message, exType.isDeletable(), acType.isDeletable());
assertBooleanEquals(message, exType.isDeprecatedAndHidden(), acType.isDeprecatedAndHidden());
assertBooleanEquals(message, exType.isFeedEnabled(), acType.isFeedEnabled());
assertBooleanEquals(message, exType.isLayoutable(), acType.isLayoutable());
assertBooleanEquals(message, exType.isMergeable(), acType.isMergeable());
assertBooleanEquals(message, exType.isQueryable(), acType.isQueryable());
assertBooleanEquals(message, exType.isReplicateable(), acType.isReplicateable());
assertBooleanEquals(message, exType.isRetrieveable(), acType.isRetrieveable());
assertBooleanEquals(message, exType.isSearchable(), acType.isSearchable());
assertBooleanEquals(message, exType.isTriggerable(), acType.isTriggerable());
assertBooleanEquals(message, exType.isUndeletable(), acType.isUndeletable());
assertBooleanEquals(message, exType.isUpdateable(), acType.isUpdateable());
}
}
@SuppressWarnings("ReuseOfLocalVariable")
@Test
public void testCreateThenDeleteThenEmptyRecycleBin() throws ApiException {
// get a count before creation
String objName = "Task";
String objField = "Priority";
int origCount = this.conn.count(objName);
Id createdId = this.createTask();
// make sure # of contacts was incremented
int newCount = this.conn.count(objName);
assertEquals(origCount + 1, newCount);
// query to check the last name
PartnerQueryResult qResult = this.conn
.query("SELECT " + objField + " FROM " + objName + " WHERE Id ='" + createdId.toString() + "'");
assertEquals(1, qResult.getSObjects().size());
assertEquals("High", qResult.getSObjects().get(0).getField(objField));
// tidy up by deleting the contact
this.deleteId(createdId);
// check that count went back down
newCount = this.conn.count(objName);
assertEquals(origCount, newCount);
// shouldn't find the object now
qResult = this.conn
.query("SELECT " + objField + " FROM " + objName + " WHERE Id ='" + createdId.toString() + "'");
assertEquals(0, qResult.getSObjects().size());
// but should be able to query-all it
qResult = this.conn.queryAll("SELECT Id FROM " + objName + " WHERE Id ='" + createdId.toString() + "'");
assertEquals(1, qResult.getSObjects().size());
assertEquals(createdId, qResult.getSObjects().get(0).getId());
// empty the recycle bin
List<Id> idsToEmptyFromRecycleBin = new ArrayList<Id>();
idsToEmptyFromRecycleBin.add(createdId);
List<EmptyRecycleBinResult> emptyBinResults = this.conn.emptyRecycleBin(idsToEmptyFromRecycleBin);
assertEquals(1, emptyBinResults.size());
assertTrue(emptyBinResults.get(0).isSuccess());
}
@Test
public void testServerTimestamp() throws ApiException {
DateTime serverTime = this.conn.getServerTimestamp();
DateTime now = new DateTime(DateTimeZone.UTC);
ReadableDuration diff = new Duration(serverTime, now);
// their time is less than 10s from ours
assertTrue("Server timestamp > 10000 millis off from ours", Math.abs(diff.getMillis()) < 10000);
}
@Test
public void testGetUserInfo() throws ApiException {
UserInfo userInfo = this.conn.getUserInfo();
assertEquals("$", userInfo.getCurrencySymbol());
assertEquals("00D50000000Ixbm", userInfo.getOrganizationId().toString());
assertEquals("Team Lazer Beez", userInfo.getOrganizationName());
assertEquals("USD", userInfo.getOrgDefaultCurrencyIsoCode());
assertEquals("00e5000000185Q9", userInfo.getProfileId().toString());
assertNull(userInfo.getRoleId());
assertNull(userInfo.getUserDefaultCurrencyIsoCode());
assertEquals(USER, userInfo.getUserEmail());
assertEquals("sftestorg3 mpierce", userInfo.getUserFullName());
assertEquals("00550000001gvBO", userInfo.getUserId().toString());
assertEquals("en_US", userInfo.getUserLanguage());
assertEquals("en_US", userInfo.getUserLocale());
assertEquals(USER, userInfo.getUserName());
assertEquals("America/Los_Angeles", userInfo.getUserTimeZone());
assertEquals("Standard", userInfo.getUserType());
assertEquals("Theme3", userInfo.getUserUiSkin());
assertFalse(userInfo.isAccessibilityMode());
assertFalse(userInfo.isOrganizationMultiCurrency());
assertFalse(userInfo.isOrgDisallowHtmlAttachments());
assertFalse(userInfo.isOrgHasPersonAccounts());
assertEquals(5242880, userInfo.getOrgAttachmentFileSizeLimit());
assertEquals(7200, userInfo.getSessionSecondsValid());
}
@Test
public void testQuery() throws ApiException {
PartnerQueryResult result =
this.conn.query("SELECT Id,Name,Description FROM Product2 WHERE Id = '01t50000001L5cT'");
assertNull(result.getQueryLocator());
assertTrue(result.isDone());
assertEquals(1, result.getSObjects().size());
SObject object = result.getSObjects().get(0);
//noinspection ConstantConditions
assertEquals("01t50000001L5cT", object.getId().toString());
assertEquals("GenWatt Diesel 200kW", object.getField("Name"));
assertNull(object.getField("Description"));
assertEquals(2, object.getAllFields().size());
assertEquals("Product2", object.getType());
assertTrue(object.isFieldSet("Description"));
assertFalse(object.isFieldSet("Foo"));
}
@Test
public void testQuerySite() throws ApiException {
PartnerQueryResult query = this.conn.query("SELECT Id FROM Site");
assertEquals(1, query.getTotalSize());
assertEquals(new Id("0DM50000000PBBU"), query.getSObjects().get(0).getId());
}
@Test
@SuppressWarnings("unchecked")
public void testQueryWithSubquery() throws ApiException {
PartnerQueryResult relQR = conn.query(
"SELECT Id, Name, AnnualRevenue, (SELECT Id, FirstName, Email FROM Contacts), " +
" (Select Id, Subject from Tasks), (Select Id, Subject from Cases) FROM Account WHERE Id='0015000000WWD7b'");
assertTrue(relQR.isDone());
assertNull(relQR.getQueryLocator());
assertEquals(1, relQR.getTotalSize());
PartnerSObject account = relQR.getSObjects().get(0);
assertEquals(2, account.getRelationshipQueryResults().size());
PartnerQueryResult subqueryResult = account.getRelationshipQueryResults().get("Contacts");
assertTrue(subqueryResult.isDone());
assertNull(subqueryResult.getQueryLocator());
assertEquals(2, subqueryResult.getTotalSize());
List<PartnerSObject> expectedContacts =
(List<PartnerSObject>) TestFixtureUtils
.loadFixtures("/sObjectFixtures/ConnectionTests/subqueryContacts.xml");
List<PartnerSObject> actualContacts = subqueryResult.getSObjects();
assertEquals(expectedContacts.size(), actualContacts.size());
Map<Id, PartnerSObject> actualMap = SObjectUtil.mapifySObjects(actualContacts);
Map<Id, PartnerSObject> expectedMap = SObjectUtil.mapifySObjects(expectedContacts);
for (Map.Entry<Id, PartnerSObject> idSObjectEntry : expectedMap.entrySet()) {
SObject actual = actualMap.get(idSObjectEntry.getKey());
SObject expected = idSObjectEntry.getValue();
assertNotNull("Object for id " + idSObjectEntry.getKey(), actual);
// jax-ws muffles the duplicate Id field row, whereas the by hand parsing does not
Map<String, String> actualFields = actual.getAllFields();
actualFields.remove("Id");
assertEquals(expected.getAllFields(), actualFields);
assertEquals(expected.getType(), actual.getType());
}
// There are no tasks for this account, and unfortunately SF exposes this as a null field called Tasks.
assertFalse(account.getRelationshipQueryResults().containsKey("Tasks"));
// there should be 3 cases
PartnerQueryResult cases = account.getRelationshipQueryResults().get("Cases");
assertNotNull(cases);
assertTrue(cases.isDone());
assertNull(cases.getQueryLocator());
assertEquals(3, cases.getTotalSize());
Collection<String> subjects =
Collections2.transform(cases.getSObjects(), new Function<PartnerSObject, String>() {
@Nullable
@Override
public String apply(@Nullable PartnerSObject partnerSObject) {
return partnerSObject.getField("Subject");
}
});
assertEquals(newArrayList("Maintenance guidelines for generator unclear",
"Electronic panel fitting loose", "Frequent mechanical breakdown"), newArrayList(subjects));
}
@Test
public void testQueryWithDotRelationship() throws ApiException {
PartnerQueryResult result = conn.query("SELECT Id, Name, Owner.Name, Owner.Id FROM Account WHERE Id='0015000000WWD7b'");
PartnerSObject account = result.getSObjects().get(0);
assertEquals(1, account.getRelationshipSubObjects().size());
assertEquals("United Oil & Gas, Singapore", account.getField("Name"));
assertEquals(new Id("0015000000WWD7b"), account.getId());
PartnerSObject owner = account.getRelationshipSubObjects().get("Owner");
assertNotNull(owner);
assertEquals("User", owner.getType());
assertEquals("sftestorg3 mpierce", owner.getField("Name"));
assertEquals(new Id("00550000001gvBO"), owner.getId());
}
@Test
public void testQueryBadColumn() {
String queryStr = "SELECT Asdf FROM Contact";
try {
this.conn.query(queryStr);
fail();
} catch (ApiException e) {
assertEquals("Invalid field", e.getMessage());
assertEquals(ExceptionCode.INVALID___FIELD, e.getApiFaultCode());
assertEquals("\n" + "SELECT Asdf FROM Contact\n" + " ^\n" + "ERROR at Row:1:Column:8\n" +
"No such column 'Asdf' on entity 'Contact'. If you are attempting to " +
"use a custom field, be sure to append the '__c' after the custom " +
"field name. Please reference your WSDL or the describe call for " + "the appropriate names.",
e.getApiFaultMessage());
}
}
@Test
public void testQueryBadSOQLSyntax() {
String queryStr = "SELECT Id FROM Contact WHERE";
try {
this.conn.query(queryStr);
fail();
} catch (ApiException e) {
assertEquals("Malformed query", e.getMessage());
assertEquals(ExceptionCode.MALFORMED___QUERY, e.getApiFaultCode());
assertEquals("\n" +
"SELECT Id FROM Contact WHERE\n" +
" ^\n" +
"ERROR at Row:1:Column:28\n" +
"unexpected token: '<EOF>'", e.getApiFaultMessage());
}
}
@Test
public void testQueryShortId() {
String queryStr = "SELECT Id FROM Contact WHERE Id = 'ZZZ'";
try {
this.conn.query(queryStr);
fail();
} catch (ApiException e) {
assertEquals("Unexpected error", e.getMessage());
assertEquals(ExceptionCode.INVALID___QUERY___FILTER___OPERATOR, e.getApiFaultCode());
assertEquals("\n" + "SELECT Id FROM Contact WHERE Id = 'ZZZ'\n" + " ^\n" +
"ERROR at Row:1:Column:30\n" + "invalid ID field: ZZZ", e.getApiFaultMessage());
}
}
@Test
public void testQueryDoneInOneStep() throws ApiException {
// Unfortunately we don't have an org handy with tons of contacts, so for now just test
// queries that don't need a queryMore
PartnerQueryResult result = this.conn.query("SELECT Id FROM Contact LIMIT 1");
assertEquals(1, result.getSObjects().size());
assertTrue(result.isDone());
}
@Test
public void testQueryMoreBadQueryLocator() {
try {
this.conn.queryMore(new PartnerQueryLocator("wrong"));
fail();
} catch (ApiException e) {
assertEquals("Invalid query locator", e.getMessage());
assertEquals(ExceptionCode.INVALID___QUERY___LOCATOR, e.getApiFaultCode());
assertEquals("invalid query locator", e.getApiFaultMessage());
}
}
@Test
public void testQueryWithoutGettingIdColumn() throws ApiException {
PartnerQueryResult result = this.conn.query("SELECT FirstName FROM Contact LIMIT 1");
List<PartnerSObject> sObjects = result.getSObjects();
assertEquals(1, sObjects.size());
final SObject sObj = sObjects.get(0);
assertEquals("Rose", sObj.getField("FirstName"));
assertNull(sObj.getId());
assertTrue(result.isDone());
}
@Test
public void testRetrieve() throws ApiException {
List<Id> ids = new ArrayList<Id>();
ids.add(new Id("0035000000km1oh"));
List<SObject> results = this.conn.retrieve("Contact", ids, Arrays.asList("FirstName", "LastName"));
assertEquals(1, results.size());
SObject contact = results.get(0);
assertEquals("0035000000km1oh", contact.getId().toString());
assertEquals("Rose", contact.getField("FirstName"));
assertEquals("Gonzalez", contact.getField("LastName"));
assertEquals(2, contact.getAllFields().size());
assertEquals("Contact", contact.getType());
}
@Test
public void testRetrieveEmptyIdList() throws ApiException {
List<Id> ids = new ArrayList<Id>();
List<SObject> results = this.conn.retrieve("Contact", ids, Arrays.asList("FirstName", "LastName"));
assertEquals(0, results.size());
}
@SuppressWarnings("ReuseOfLocalVariable")
@Test
public void testRetrieveExtended() throws ApiException {
List<Id> ids = new ArrayList<Id>();
ids.add(new Id("0035000000km1oh"));
ids.add(new Id("0035000000km1oi"));
ids.add(new Id("0035000000km1oj"));
ids.add(new Id("0035000000km1ok"));
ids.add(new Id("0035000000km1ol"));
List<String> fields = new ArrayList<String>();
fields.add("FirstName");
fields.add("LastName");
fields.add("Department");
fields.add("Email");
fields.add("Title");
Map<Id, SObject> results = this.conn.retrieveExtended("Contact", ids, fields, 12);
assertEquals(5, results.size());
SObject s = results.get(ids.get(0));
assertEquals("Rose", s.getField("FirstName"));
assertEquals("Gonzalez", s.getField("LastName"));
assertEquals("Procurement", s.getField("Department"));
assertEquals("rose@edge.com", s.getField("Email"));
assertEquals("SVP, Procurement", s.getField("Title"));
s = results.get(ids.get(1));
assertEquals("Sean", s.getField("FirstName"));
assertEquals("Forbes", s.getField("LastName"));
assertEquals("Finance", s.getField("Department"));
assertEquals("sean@edge.com", s.getField("Email"));
assertEquals("CFO", s.getField("Title"));
s = results.get(ids.get(2));
assertEquals("Jack", s.getField("FirstName"));
assertEquals("Rogers", s.getField("LastName"));
assertNull(s.getField("Department"));
assertEquals("jrogers@burlington.com", s.getField("Email"));
assertEquals("VP, Facilities", s.getField("Title"));
s = results.get(ids.get(3));
assertEquals("Pat", s.getField("FirstName"));
assertEquals("Stumuller", s.getField("LastName"));
assertEquals("Finance", s.getField("Department"));
assertEquals("pat@pyramid.net", s.getField("Email"));
assertEquals("SVP, Administration and Finance", s.getField("Title"));
s = results.get(ids.get(4));
assertEquals("Andy", s.getField("FirstName"));
assertEquals("Young", s.getField("LastName"));
assertEquals("Internal Operations", s.getField("Department"));
assertEquals("a_young@dickenson.com", s.getField("Email"));
assertEquals("SVP, Operations", s.getField("Title"));
}
@Test
public void testRetrieveExtendedRethrowsRetrieveException() {
List<Id> ids = new ArrayList<Id>();
ids.add(new Id("0034000000QnQVPAA3"));
List<String> fields = new ArrayList<String>();
fields.add("BadFieldName");
try {
this.conn.retrieveExtended("Contact", ids, fields, 12);
fail();
} catch (ApiException e) {
assertEquals("Couldn't retrieve a field name chunk", e.getMessage());
Throwable t = e.getCause();
assertTrue(t instanceof ApiException);
ApiException inner = (ApiException) t;
assertEquals("Invalid field", inner.getMessage());
assertEquals(ExceptionCode.INVALID___FIELD, inner.getApiFaultCode());
assertEquals("\nSELECT BadFieldName FROM Contact\n ^\n" + "ERROR at Row:1:Column:8\n" +
"No such column 'BadFieldName' on entity 'Contact'. " +
"If you are attempting to use a custom field, be sure to " +
"append the '__c' after the custom field name. Please reference " +
"your WSDL or the describe call for the appropriate names.", inner.getApiFaultMessage());
assertNotSame(inner, e);
}
}
@SuppressWarnings("ReuseOfLocalVariable")
@Test
public void testUpdate() throws ApiException {
List<PartnerSObject> initialQueryResults = this.conn.query("Select Name, Id from Opportunity").getSObjects();
assertTrue(initialQueryResults.size() > 1);
SObject sObj = initialQueryResults.get(0);
Id id = sObj.getId();
// get the LastName of the first id, add xyz to it, then remove it
String origName = sObj.getField("Name");
sObj.setField("Name", origName + "xyz");
List<SObject> updateList = new ArrayList<SObject>();
updateList.add(sObj);
List<SaveResult> updateResults = this.conn.update(updateList);
assertEquals(1, updateList.size());
SaveResult result = updateResults.get(0);
assertTrue(result.isSuccess());
assertEquals(0, result.getErrors().size());
assertEquals(id, result.getId());
List<PartnerSObject> postUpdateQueryResults =
this.conn.query("Select Name, Id from Opportunity WHERE Id = '" + id.toString() + "'").getSObjects();
assertEquals(1, postUpdateQueryResults.size());
SObject postUpdateSObj = postUpdateQueryResults.get(0);
assertEquals(origName + "xyz", postUpdateSObj.getField("Name"));
postUpdateSObj.setField("Name", origName);
updateList = new ArrayList<SObject>();
updateList.add(postUpdateSObj);
this.conn.update(updateList);
List<PartnerSObject> post2ndUpdateQueryResults =
this.conn.query("Select Name, Id from Opportunity WHERE Id = '" + id.toString() + "'").getSObjects();
assertEquals(1, post2ndUpdateQueryResults.size());
SObject post2ndUpdateSObj = post2ndUpdateQueryResults.get(0);
assertEquals(origName, post2ndUpdateSObj.getField("Name"));
}
@Test
public void testUpdateFailsValidation() throws ApiException {
List<PartnerSObject> initialQueryResults =
this.conn.query("Select Name, Id from Opportunity LIMIT 1").getSObjects();
assertEquals(1, initialQueryResults.size());
SObject sObj = initialQueryResults.get(0);
// get the Name of the first id, add xyz to it, then remove it
String origName = sObj.getField("Name");
sObj.setField("Name", "Invalid-" + origName);
List<SObject> updateList = new ArrayList<SObject>();
updateList.add(sObj);
List<SaveResult> updateResults = this.conn.update(updateList);
assertEquals(1, updateList.size());
SaveResult result = updateResults.get(0);
assertFalse(result.isSuccess());
assertNull(result.getId());
assertEquals(1, result.getErrors().size());
PartnerApiError e = result.getErrors().get(0);
assertEquals("Name can't start with Invalid-", e.getMessage());
assertEquals(StatusCodeType.FIELD___CUSTOM___VALIDATION___EXCEPTION, e.getStatusCode());
assertEquals(0, e.getFields().size());
}
@Test
public void testUpdateWithInvalidXmlInFieldName() {
SObject sObj = PartnerSObjectImpl.getNew("Opportunity");
sObj.setField("<&", "valueForBadField");
List<SObject> updateList = new ArrayList<SObject>();
updateList.add(sObj);
try {
this.conn.update(updateList);
fail();
} catch (ApiException e) {
assertEquals("Couldn't create DOM nodes for field name <<&> and value <valueForBadField>",
e.getCause().getMessage());
}
}
@SuppressWarnings("ReuseOfLocalVariable")
@Test
public void testUpdateWithInvalidXmlInValue() throws ApiException {
List<PartnerSObject> initialQueryResults =
this.conn.query("Select Name, Id from Opportunity LIMIT 1").getSObjects();
assertEquals(1, initialQueryResults.size());
SObject sObj = initialQueryResults.get(0);
Id id = sObj.getId();
// add a nasty string to the end of the name
String origName = sObj.getField("Name");
String badFieldSuffix = "'<&>\"";
sObj.setField("Name", origName + badFieldSuffix);
List<SObject> updateList = new ArrayList<SObject>();
updateList.add(sObj);
List<SaveResult> updateResults = this.conn.update(updateList);
assertEquals(1, updateList.size());
// make sure the update succeeded
SaveResult result = updateResults.get(0);
assertTrue(result.toString(), result.isSuccess());
assertEquals(0, result.getErrors().size());
assertEquals(id, result.getId());
// check that a query returns the new value
List<PartnerSObject> postUpdateQueryResults =
this.conn.query("Select Name, Id from Opportunity WHERE Id = '" + id.toString() + "'").getSObjects();
assertEquals(1, postUpdateQueryResults.size());
SObject postUpdateSObj = postUpdateQueryResults.get(0);
assertEquals(origName + badFieldSuffix, postUpdateSObj.getField("Name"));
// set the name back to the original
postUpdateSObj.setField("Name", origName);
updateList = new ArrayList<SObject>();
updateList.add(postUpdateSObj);
this.conn.update(updateList);
List<PartnerSObject> post2ndUpdateQueryResults =
this.conn.query("Select Name, Id from Opportunity WHERE Id = '" + id.toString() + "'").getSObjects();
assertEquals(1, post2ndUpdateQueryResults.size());
SObject post2ndUpdateSObj = post2ndUpdateQueryResults.get(0);
assertEquals(origName, post2ndUpdateSObj.getField("Name"));
}
@Test
public void testUpdateWithBadId() throws ApiException {
// get some data
List<PartnerSObject> initialQueryResults = this.conn.query("Select LastName, Id from Contact").getSObjects();
assertTrue(initialQueryResults.size() > 1);
SObject sObj = initialQueryResults.get(0);
Id badId = new Id("00370XX000Y3zjW");
SObject badSObj = PartnerSObjectImpl.getNewWithId("Contact", badId);
badSObj.setAllFields(sObj.getAllFields());
List<SObject> sObjsToUpdate = new ArrayList<SObject>();
sObjsToUpdate.add(badSObj);
List<SaveResult> results = this.conn.update(sObjsToUpdate);
assertEquals(1, results.size());
SaveResult res = results.get(0);
assertFalse(res.isSuccess());
assertNull(res.getId());
assertEquals(1, res.getErrors().size());
PartnerApiError error = res.getErrors().get(0);
assertEquals("invalid cross reference id", error.getMessage());
assertEquals(StatusCodeType.INVALID___CROSS___REFERENCE___KEY, error.getStatusCode());
assertEquals(0, error.getFields().size());
}
@Test
public void testUpdateWithFieldsToNull() throws ApiException {
List<Id> idList = Collections.singletonList(new Id("0065000000FgGSp"));
List<SObject> sObjs = this.conn.retrieve("Opportunity", idList, Arrays.asList("Amount", "Id"));
SObject sObj = sObjs.get(0);
String fname = "Amount";
String origValue = "350000.0";
assertEquals(origValue, sObj.getField(fname));
try {
// null it
SObject nullBirthday = PartnerSObjectImpl.getNewWithId("Opportunity", sObj.getId());
nullBirthday.setField(fname, null);
List<SaveResult> resultList = this.conn.update(Collections.singletonList(nullBirthday));
assertTrue(resultList.get(0).isSuccess());
List<SObject> sObjsWithNulledBirthday =
this.conn.retrieve("Opportunity", idList, Arrays.asList("Amount", "Id"));
SObject sObjWithNullField = sObjsWithNulledBirthday.get(0);
assertTrue(sObjWithNullField.isFieldSet(fname));
assertNull(sObjWithNullField.getField(fname));
} finally {
sObj.setField(fname, origValue);
List<SaveResult> resultList = this.conn.update(sObjs);
assertTrue(resultList.get(0).isSuccess());
List<SObject> sObjsWithBirthday = this.conn.retrieve("Opportunity", idList, Arrays.asList("Amount", "Id"));
SObject sObjWithBirthdayAgain = sObjsWithBirthday.get(0);
assertEquals(origValue, sObjWithBirthdayAgain.getField(fname));
}
}
@Test
public void testGetConnInternalInfo() throws ApiException {
assertEquals(USER, this.conn.getUsername());
}
@Test
public void testReconfigureWithInvalidCredsRethrowsConnectionException_KeepsRetryingWithBadCredentials()
throws ApiException, UnexpectedErrorFault_Exception, IllegalAccessException, NoSuchFieldException {
this.conn.getServerTimestamp();
this.bundle.updateCredentials(USER, PASSWORD + "x", MAX_API_CALLS);
for (int i = 0; i < 3; i++) {
try {
this.conn.getServerTimestamp();
fail();
} catch (ApiException e) {
assertEquals("Bad credentials for user '" + USER + "'", e.getMessage());
assertEquals(ExceptionCode.INVALID___LOGIN, e.getApiFault().getFaultCode());
}
}
}
@Test
public void testReconfigureThenInvalidOperationThrowsExceptionFromRetryOfInvalidOp()
throws UnexpectedErrorFault_Exception, IllegalAccessException, ApiException, NoSuchFieldException {
this.conn.getServerTimestamp();
this.conn.logout();
try {
this.conn.query("SELECT Id FROM no_such_object");
fail();
} catch (ApiException e) {
assertEquals("Invalid SObject", e.getMessage());
assertEquals(ExceptionCode.INVALID___TYPE, e.getApiFaultCode());
assertEquals(
"\n" + "SELECT Id FROM no_such_object\n" + " ^\n" + "ERROR at Row:1:Column:16\n" +
"sObject type 'no_such_object' is not supported. " +
"If you are attempting to use a custom object, be sure to append the '__c' after" +
" the entity name. Please reference your WSDL or the describe call for" +
" the appropriate names.", e.getApiFaultMessage());
}
}
@Test
public void testReconfigureCanFailSeveralTimesThenSucceedWithNewCredentials()
throws UnexpectedErrorFault_Exception, IllegalAccessException, ApiException, NoSuchFieldException {
this.conn.getServerTimestamp();
this.bundle.updateCredentials(USER, PASSWORD + "x", MAX_API_CALLS);
for (int i = 0; i < 3; i++) {
try {
this.conn.getServerTimestamp();
fail();
} catch (ApiException e) {
assertEquals("Bad credentials for user '" + USER + "'", e.getMessage());
assertEquals(ExceptionCode.INVALID___LOGIN, e.getApiFault().getFaultCode());
}
}
this.bundle.updateCredentials(USER, PASSWORD, MAX_API_CALLS);
this.conn.getServerTimestamp();
}
@Test
public void testCreateWithHiddenFields_ReturnsUnsuccessfulSaveResult() throws ApiException {
ConnectionBundle connectionBundle = getConnectionBundle(
getPropVal("com.palominolabs.test.w2l.sf.upload.limitedVisibilityUser.sfLogin"),
getPropVal("com.palominolabs.test.w2l.sf.upload.limitedVisibilityUser.sfPassword"));
PartnerConnection partnerConnection = connectionBundle.getPartnerConnection();
String hiddenField = "HiddenToPeon__c";
SObject sObj = PartnerSObjectImpl.getNew("Lead");
sObj.setField("LastName", "I have a last name");
sObj.setField("Company", "I have a job, dude");
// This field is hidden to the logged in user
sObj.setField(hiddenField, "You can't see me");
List<SaveResult> resultList = partnerConnection.create(Collections.singletonList(sObj));
final SaveResult saveResult = resultList.get(0);
boolean isSuccess = saveResult.isSuccess();
try {
assertFalse(isSuccess);
PartnerApiError error = saveResult.getErrors().get(0);
assertEquals(StatusCodeType.INVALID___FIELD___FOR___INSERT___UPDATE, error.getStatusCode());
assertEquals(1, error.getFields().size());
assertEquals(hiddenField, error.getFields().get(0));
} finally {
// Clean it up
if (isSuccess) {
List<Id> idList = Collections.singletonList(resultList.get(0).getId());
partnerConnection.delete(idList);
partnerConnection.emptyRecycleBin(idList);
}
}
}
/**
* Create an Division object, with assertions on the result
*
* @return the id of the created Division
*
* @throws ApiException on error
*/
private Id createTask() throws ApiException {
List<SObject> sObjs = new ArrayList<SObject>();
SObject sObj = PartnerSObjectImpl.getNew("Task");
sObj.setField("Priority", "High");
sObj.setField("Status", "In Progress");
sObjs.add(sObj);
List<SaveResult> results = this.conn.create(sObjs);
// check save result
assertEquals(1, results.size());
SaveResult result = results.get(0);
assertTrue("result succeeded", result.isSuccess());
assertNotNull(result.getId());
Id createdId = result.getId();
assertEquals(0, result.getErrors().size());
return createdId;
}
/**
* Delete one id, with assertions on the result
*
* @param id the id to delete
*
* @throws ApiException on error
*/
private void deleteId(Id id) throws ApiException {
List<Id> idsToDelete = new ArrayList<Id>();
idsToDelete.add(id);
List<DeleteResult> deleteResults = this.conn.delete(idsToDelete);
assertEquals(1, deleteResults.size());
assertTrue(deleteResults.get(0).isSuccess());
assertEquals(id, deleteResults.get(0).getId());
assertEquals(0, deleteResults.get(0).getErrors().size());
}
static void logout(PartnerConnection c) throws ApiException {
((PartnerConnectionImpl) c).logout();
}
}