Pull up changes from the CONNECTORS-856 branch

git-svn-id: https://svn.apache.org/repos/asf/manifoldcf/branches/CONNECTORS-856-2@1613081 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/build.xml b/build.xml
index c762675..5f44256 100644
--- a/build.xml
+++ b/build.xml
@@ -1201,6 +1201,72 @@
             <param name="artifact-type" value="jar"/>
         </antcall>
     </target>
+
+    <target name="download-mock">
+        <mkdir dir="lib"/>
+        <property name="powermock-version" value="1.5.2"/>
+        <property name="powermock-package" value="org/powermock"/>
+        <antcall target="download-via-maven"><param name="target" value="lib"/>
+            <param name="project-path" value="${powermock-package}"/>
+            <param name="artifact-version" value="${powermock-version}"/>
+            <param name="artifact-name" value="powermock-module-junit4"/>
+            <param name="artifact-type" value="jar"/>
+        </antcall>
+        <antcall target="download-via-maven"><param name="target" value="lib"/>
+            <param name="project-path" value="${powermock-package}"/>
+            <param name="artifact-version" value="${powermock-version}"/>
+            <param name="artifact-name" value="powermock-api-mockito"/>
+            <param name="artifact-type" value="jar"/>
+        </antcall>
+        <antcall target="download-via-maven"><param name="target" value="lib"/>
+            <param name="project-path" value="${powermock-package}"/>
+            <param name="artifact-version" value="${powermock-version}"/>
+            <param name="artifact-name" value="powermock-api-support"/>
+            <param name="artifact-type" value="jar"/>
+        </antcall>
+        <antcall target="download-via-maven"><param name="target" value="lib"/>
+            <param name="project-path" value="${powermock-package}"/>
+            <param name="artifact-version" value="${powermock-version}"/>
+            <param name="artifact-name" value="powermock-core"/>
+            <param name="artifact-type" value="jar"/>
+        </antcall>
+        <antcall target="download-via-maven"><param name="target" value="lib"/>
+            <param name="project-path" value="${powermock-package}"/>
+            <param name="artifact-version" value="${powermock-version}"/>
+            <param name="artifact-name" value="powermock-reflect"/>
+            <param name="artifact-type" value="jar"/>
+        </antcall>
+        <antcall target="download-via-maven"><param name="target" value="lib"/>
+            <param name="project-path" value="${powermock-package}"/>
+            <param name="artifact-version" value="${powermock-version}"/>
+            <param name="artifact-name" value="powermock-module-junit4-common"/>
+            <param name="artifact-type" value="jar"/>
+        </antcall>
+        <antcall target="download-via-maven"><param name="target" value="lib"/>
+            <param name="project-path" value="org/mockito"/>
+            <param name="artifact-version" value="1.9.5"/>
+            <param name="artifact-name" value="mockito-all"/>
+            <param name="artifact-type" value="jar"/>
+        </antcall>
+        <antcall target="download-via-maven"><param name="target" value="lib"/>
+            <param name="project-path" value="org/noggit"/>
+            <param name="artifact-version" value="0.5"/>
+            <param name="artifact-name" value="noggit"/>
+            <param name="artifact-type" value="jar"/>
+        </antcall>
+        <antcall target="download-via-maven"><param name="target" value="lib"/>
+            <param name="project-path" value="org/javassist"/>
+            <param name="artifact-version" value="3.18.0-GA"/>
+            <param name="artifact-name" value="javassist"/>
+            <param name="artifact-type" value="jar"/>
+        </antcall>
+        <antcall target="download-via-maven"><param name="target" value="lib"/>
+            <param name="project-path" value="org/objenesis"/>
+            <param name="artifact-version" value="1.2"/>
+            <param name="artifact-name" value="objenesis"/>
+            <param name="artifact-type" value="jar"/>
+        </antcall>
+    </target>
     
     <target name="download-junit">
         <mkdir dir="lib"/>
@@ -1211,7 +1277,7 @@
             <param name="artifact-type" value="jar"/>
         </antcall>
     </target>
-    
+
     <target name="download-log4j">
         <mkdir dir="lib"/>
         <antcall target="download-via-maven"><param name="target" value="lib"/>
@@ -1802,7 +1868,7 @@
         </antcall>
     </target>
 	
-    <target name="make-core-deps" depends="download-mongo-java-driver,download-jira-client,download-google-api-client,download-dropbox-client,download-solrj,download-zookeeper,download-httpcomponents,download-json,download-hsqldb,download-xerces,download-commons,download-elasticsearch-plugin,download-solr-plugins,download-sharepoint-plugins,download-jstl,download-xmlgraphics-commons,download-wstx-asl,download-xmlsec,download-xml-apis,download-wss4j,download-velocity,download-streambuffer,download-stax,download-servlet-api,download-xml-resolver,download-osgi,download-opensaml,download-mimepull,download-mail,download-log4j,download-junit,download-jaxws,download-glassfish,download-jaxb,download-tomcat,download-h2,download-h2-support,download-geronimo-specs,download-fop,download-derby,download-postgresql,download-axis,download-saaj,download-wsdl4j,download-castor,download-jetty,download-slf4j,download-xalan,download-activation,download-avalon-framework,download-poi,download-chemistry,download-ecj,download-hadoop,download-protobuf,download-tika,download-jackson">
+    <target name="make-core-deps" depends="download-mongo-java-driver,download-jira-client,download-google-api-client,download-dropbox-client,download-solrj,download-zookeeper,download-httpcomponents,download-json,download-hsqldb,download-xerces,download-commons,download-elasticsearch-plugin,download-solr-plugins,download-sharepoint-plugins,download-jstl,download-xmlgraphics-commons,download-wstx-asl,download-xmlsec,download-xml-apis,download-wss4j,download-velocity,download-streambuffer,download-stax,download-servlet-api,download-xml-resolver,download-osgi,download-opensaml,download-mimepull,download-mail,download-log4j,download-junit,download-mock,download-jaxws,download-glassfish,download-jaxb,download-tomcat,download-h2,download-h2-support,download-geronimo-specs,download-fop,download-derby,download-postgresql,download-axis,download-saaj,download-wsdl4j,download-castor,download-jetty,download-slf4j,download-xalan,download-activation,download-avalon-framework,download-poi,download-chemistry,download-ecj,download-hadoop,download-protobuf,download-tika,download-jackson">
         <copy todir="lib">
             <fileset dir="lib-license" includes="*.txt"/>
         </copy>
diff --git a/connectors/solr/connector/src/test/java/org/apache/manifoldcf/agents/output/solr/HttpPosterTest.java b/connectors/solr/connector/src/test/java/org/apache/manifoldcf/agents/output/solr/HttpPosterTest.java
new file mode 100644
index 0000000..1206ae7
--- /dev/null
+++ b/connectors/solr/connector/src/test/java/org/apache/manifoldcf/agents/output/solr/HttpPosterTest.java
@@ -0,0 +1,221 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.manifoldcf.agents.output.solr;
+
+import junit.framework.TestCase;
+import org.apache.log4j.Logger;
+import org.apache.manifoldcf.agents.interfaces.IOutputAddActivity;
+import org.apache.manifoldcf.agents.interfaces.RepositoryDocument;
+import org.apache.manifoldcf.agents.system.Logging;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.when;
+import static org.powermock.api.mockito.PowerMockito.*;
+
+/**
+ * @Author: Alessandro Benedetti Date: 19/12/2013
+ */
+
+@RunWith( PowerMockRunner.class )
+@PrepareForTest( { HttpPoster.class } )
+public class HttpPosterTest
+  extends TestCase
+{
+
+
+  HttpPoster httpPosterToTest;
+
+  RepositoryDocument document;
+
+  IOutputAddActivity act;
+
+  @Override
+  public void setUp()
+    throws Exception
+  {
+
+    httpPosterToTest = spy(new HttpPoster( "localhost", "collection1", 5000, 500, "update", "removePath", "statusPath",
+                 "allowAttributeName", "denyAttributeName", "idAttributeName",
+                 "modifiedDateAttributeName", "createdDateAttributeName", "indexedDateAttributeName",
+                 "fileNameAttributeName", "mimeTypeAttributeName", new Long( 5000 ), "true"));
+  }
+
+  /**
+   * Verify  the IndexPost Method create the correct IngestThread class
+   * @throws Exception
+   */
+  public void testIndexPost()
+    throws Exception
+  {
+    String[] shareAcls = new String[]{ "shareAcl1", "shareAcl2" };
+    String[] shareDenyAcls = new String[]{ "denyShareAcl1", "denyShareAcl2" };
+    String[] acls = new String[]{ "acl1", "acl2" };
+    String[] denyAcls = new String[]{ "denyAcl1", "denyAcl2" };
+
+    Logging.ingest = mock( Logger.class );
+    when( Logging.ingest.isDebugEnabled() ).thenReturn( false );
+    initRepositoryDocumentMock( shareAcls, shareDenyAcls, acls, denyAcls );
+
+    act = mock( IOutputAddActivity.class );
+    when(act.qualifyAccessToken( eq( "AuthorityString"), anyString() )).thenAnswer(new Answer<String>() {
+      @Override
+      public String answer(InvocationOnMock invocation) throws Throwable {
+        Object[] args = invocation.getArguments();
+        return (String) args[1];
+      }
+    });
+
+    Map<String, List<String>> sourceTargets = this.getMappingsMap();
+    Map<String, List<String>> streamParam = this.getArgumentsMap();
+
+    String commitWithin = "true";
+    HttpPoster.IngestThread mockIngestionThread=mock(HttpPoster.IngestThread.class);
+    Mockito.doThrow(new RuntimeException()).when( mockIngestionThread ).run();
+
+    whenNew( HttpPoster.IngestThread.class).withArguments("Document Id", document, streamParam, true, sourceTargets,shareAcls,
+      shareDenyAcls, acls, denyAcls,commitWithin).thenReturn(mockIngestionThread);
+    httpPosterToTest.indexPost("Document Id", document, streamParam,
+      sourceTargets,true, "AuthorityString",act);
+
+    verifyNew(HttpPoster.IngestThread.class).withArguments("Document Id", document, streamParam, true, sourceTargets, shareAcls,
+      shareDenyAcls, acls, denyAcls, commitWithin );
+  }
+
+  private void initRepositoryDocumentMock( String[] shareAcls, String[] shareDenyAcls, String[] acls,
+    String[] denyAcls )
+    throws IOException
+  {
+    document = mock( RepositoryDocument.class );
+    List<String> fields = getFields();
+    Iterator<String> fieldsIterator = fields.iterator();
+    when( document.getFields() ).thenReturn( fieldsIterator );
+    when( document.getFieldAsStrings( "cm:description" ) ).thenReturn( new String[]{ "description" } );
+    when( document.getFieldAsStrings( "cm:name" ) ).thenReturn( new String[]{ "name" } );
+    when( document.getFieldAsStrings( "cm:title" ) ).thenReturn( new String[]{ "title" } );
+    when( document.getFieldAsStrings( "extraMetadata1" ) ).thenReturn( new String[]{ "value1" } );
+    when( document.getFieldAsStrings( "extraMetadata2" ) ).thenReturn( new String[]{ "value2" } );
+    when( document.getFieldAsStrings( "extraMetadata3" ) ).thenReturn( new String[]{ "value3" } );
+    when( document.getACL()).thenReturn(acls);
+    when( document.getShareACL()).thenReturn(shareAcls);
+    when( document.getShareDenyACL()).thenReturn( shareDenyAcls );
+    when( document.getDenyACL()).thenReturn(denyAcls);
+  }
+
+  /**
+   * inits the expected solr params for both the tests, in the first one we expect the extra params not to be present
+   * because they are not in the mappings
+   *
+   * @param test2
+   * @return
+   * @throws java.io.UnsupportedEncodingException
+   */
+  private ModifiableSolrParams initExpectedSolrParams( Boolean test2 )
+    throws UnsupportedEncodingException
+  {
+    ModifiableSolrParams expectedParams = new ModifiableSolrParams();
+    expectedParams.add( "literal.idAttributeName", "document id" );
+    expectedParams.add( "literal.allowAttributeNameshare", "shareAcl1" );
+    expectedParams.add( "literal.allowAttributeNameshare", "shareAcl2" );
+    expectedParams.add( "literal.denyAttributeNameshare", "denyShareAcl1" );
+    expectedParams.add( "literal.denyAttributeNameshare", "denyShareAcl2" );
+    expectedParams.add( "literal.allowAttributeNamedocument", "acl1" );
+    expectedParams.add( "literal.allowAttributeNamedocument", "acl2" );
+    expectedParams.add( "literal.denyAttributeNamedocument", "denyAcl1" );
+    expectedParams.add( "literal.denyAttributeNamedocument", "denyAcl2" );
+    expectedParams.add( "stream.type", "text/plain" );
+    expectedParams.add( "literal.cm_description_s", "description" );
+    expectedParams.add( "literal.cm_title_s", "title" );
+    expectedParams.add( "literal.cm_name_s", "name" );
+    if ( test2 )
+    {
+      expectedParams.add( "literal.extraMetadata1", "value1" );
+      expectedParams.add( "literal.extraMetadata2", "value2" );
+      expectedParams.add( "literal.extraMetadata3", "value3" );
+    }
+    expectedParams.add( "commitWithin", "true" );
+    return expectedParams;
+  }
+
+  /**
+   * return a list of example metadata fields present in a mock document
+   *
+   * @return
+   */
+  private List<String> getFields()
+  {
+    List<String> fields = new ArrayList<String>();
+    fields.add( "cm:description" );
+    fields.add( "cm:title" );
+    fields.add( "cm:name" );
+    fields.add( "extraMetadata1" );
+    fields.add( "extraMetadata2" );
+    fields.add( "extraMetadata3" );
+    return fields;
+  }
+
+  /**
+   * returns a testing mapping map
+   *
+   * @return
+   */
+  private Map<String, List<String>> getMappingsMap()
+  {
+    Map<String, List<String>> sourceTargets = new HashMap<String, List<String>>();
+    List<String> firstList = new ArrayList<String>();
+    firstList.add( "cm_description_s" );
+    List<String> secondList = new ArrayList<String>();
+    secondList.add( "cm_name_s" );
+    List<String> thirdList = new ArrayList<String>();
+    thirdList.add( "cm_title_s" );
+
+    sourceTargets.put( "cm:description", firstList );
+    sourceTargets.put( "cm:name", secondList );
+    sourceTargets.put( "cm:title", thirdList );
+    return sourceTargets;
+  }
+
+  /**
+   * returns a testing argument map
+   *
+   * @return
+   */
+  private Map<String, List<String>> getArgumentsMap()
+  {
+    Map<String, List<String>> sourceTargets = new HashMap<String, List<String>>();
+    List<String> firstList = new ArrayList<String>();
+    firstList.add( "text/plain" );
+    sourceTargets.put( "stream.type", firstList );
+    return sourceTargets;
+  }
+}
diff --git a/connectors/solr/connector/src/test/java/org/apache/manifoldcf/agents/output/solr/IngestThreadTest.java b/connectors/solr/connector/src/test/java/org/apache/manifoldcf/agents/output/solr/IngestThreadTest.java
new file mode 100644
index 0000000..14ca690
--- /dev/null
+++ b/connectors/solr/connector/src/test/java/org/apache/manifoldcf/agents/output/solr/IngestThreadTest.java
@@ -0,0 +1,255 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.manifoldcf.agents.output.solr;
+
+import junit.framework.TestCase;
+import org.apache.log4j.Logger;
+import org.apache.manifoldcf.agents.interfaces.IOutputAddActivity;
+import org.apache.manifoldcf.agents.interfaces.RepositoryDocument;
+import org.apache.manifoldcf.agents.system.Logging;
+import org.apache.solr.client.solrj.SolrServer;
+import org.apache.solr.client.solrj.request.ContentStreamUpdateRequest;
+import org.apache.solr.client.solrj.response.UpdateResponse;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.junit.runner.RunWith;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.when;
+import static org.powermock.api.mockito.PowerMockito.doCallRealMethod;
+import static org.powermock.api.mockito.PowerMockito.mock;
+import static org.powermock.api.mockito.PowerMockito.spy;
+import static org.powermock.api.mockito.PowerMockito.*;
+
+/**
+ * @Author: Alessandro Benedetti Date: 19/12/2013
+ */
+@RunWith( PowerMockRunner.class )
+@PrepareForTest( { HttpPoster.IngestThread.class } )
+public class IngestThreadTest
+  extends TestCase
+{
+
+  HttpPoster.IngestThread ingestThreadToTestKeepAllMetadata;
+  HttpPoster.IngestThread ingestThreadToTest;
+
+  HttpPoster poster;
+
+  RepositoryDocument document;
+
+  IOutputAddActivity act;
+
+  @Override
+  public void setUp()
+    throws Exception
+  {
+
+    poster =new HttpPoster( "zkHost", "collection1", 5000, 500, "update", "removePath", "statusPath",
+                 "allowAttributeName", "denyAttributeName", "idAttributeName",
+                 "modifiedDateAttributeName", "createdDateAttributeName", "indexedDateAttributeName",
+                 "fileNameAttributeName", "mimeTypeAttributeName", new Long( 5000 ), "true" );
+
+    Logging.ingest = mock( Logger.class );
+    when( Logging.ingest.isDebugEnabled() ).thenReturn( false );
+    document = mock( RepositoryDocument.class );
+    List<String> fields = getFields();
+    Iterator<String> fieldsIterator = fields.iterator();
+    when( document.getFields() ).thenReturn( fieldsIterator );
+    when( document.getFieldAsStrings( "cm:description" ) ).thenReturn( new String[]{ "description" } );
+    when( document.getFieldAsStrings( "cm:name" ) ).thenReturn( new String[]{ "name" } );
+    when( document.getFieldAsStrings( "cm:title" ) ).thenReturn( new String[]{ "title" } );
+    when( document.getFieldAsStrings( "extraMetadata1" ) ).thenReturn( new String[]{ "value1" } );
+    when( document.getFieldAsStrings( "extraMetadata2" ) ).thenReturn( new String[]{ "value2" } );
+    when( document.getFieldAsStrings( "extraMetadata3" ) ).thenReturn( new String[]{ "value3" } );
+
+    act = mock( IOutputAddActivity.class );
+
+    Map<String, List<String>> sourceTargets = this.getMappingsMap();
+    Map<String, List<String>> streamParam = this.getArgumentsMap();
+    String[] shareAcls = new String[]{ "shareAcl1", "shareAcl2" };
+    String[] shareDenyAcls = new String[]{ "denyShareAcl1", "denyShareAcl2" };
+    String[] acls = new String[]{ "acl1", "acl2" };
+    String[] denyAcls = new String[]{ "denyAcl1", "denyAcl2" };
+    Map<String,String[]> aclsMap = new HashMap<String,String[]>();
+    Map<String,String[]> denyAclsMap = new HashMap<String,String[]>();
+    aclsMap.put("share",shareAcls);
+    aclsMap.put("document",acls);
+    denyAclsMap.put("share",shareDenyAcls);
+    denyAclsMap.put("document",denyAcls);
+    String commitWithin = "true";
+
+    ingestThreadToTestKeepAllMetadata = spy(
+      poster.new IngestThread( "document id", document, streamParam, true, sourceTargets,
+                   aclsMap, denyAclsMap, commitWithin ) );
+    ingestThreadToTest = spy(
+      poster.new IngestThread( "document id", document, streamParam, false, sourceTargets,
+                   aclsMap, denyAclsMap, commitWithin ) );
+
+  }
+
+
+  public void testIndexPostNotKeepMetadata()
+    throws Exception
+  {
+    this.indexPostNotKeepMetadata( ingestThreadToTestKeepAllMetadata, true );
+    this.indexPostNotKeepMetadata( ingestThreadToTest, false );
+
+  }
+
+  /**
+   * Verify the behaviour, when the keep variable is set to true, we want to send to Solr all the metadata fields
+   *
+   * @param keep
+   * @throws Exception
+   */
+  private void indexPostNotKeepMetadata( HttpPoster.IngestThread it, boolean keep )
+    throws Exception
+  {
+    ContentStreamUpdateRequest contentStreamUpdateRequest = mock( ContentStreamUpdateRequest.class );
+    whenNew( ContentStreamUpdateRequest.class ).withArguments( anyString() ).thenReturn(
+      contentStreamUpdateRequest );
+    UpdateResponse mockResponse = mock( UpdateResponse.class );
+    when( contentStreamUpdateRequest.process( any( SolrServer.class ) ) ).thenReturn( mockResponse );
+    doCallRealMethod().when( contentStreamUpdateRequest ).setParams( any( ModifiableSolrParams.class ) );
+    doCallRealMethod().when( contentStreamUpdateRequest ).getParams();
+    it.run();
+    verifyNew( ContentStreamUpdateRequest.class, atLeastOnce() ).withArguments( anyString() );
+
+    ModifiableSolrParams expectedParams = initExpectedSolrParams( keep );
+    assertEqualsModifiableSolrParams( expectedParams, contentStreamUpdateRequest.getParams() );
+    verify( contentStreamUpdateRequest ).process( any( SolrServer.class ) );
+
+  }
+
+  /**
+   * asserts that 2 ModifiableSolrParams are equals
+   *
+   * @param expected
+   * @param actual
+   * @return
+   */
+  private boolean assertEqualsModifiableSolrParams( ModifiableSolrParams expected, ModifiableSolrParams actual )
+  {
+    Set<String> expectedParameterNames = expected.getParameterNames();
+    Set<String> actualParameterNames = actual.getParameterNames();
+    int expectedSize = expectedParameterNames.size();
+    assertEquals( expectedSize, actualParameterNames.size() );
+    for ( String parameterName : expectedParameterNames )
+    {
+      assertEquals( expected.get( parameterName ), actual.get( parameterName ) );
+    }
+    return true;
+  }
+
+  /**
+   * inits the expected solr params for both the tests, in the first one we expect the extra params not to be present
+   * because they are not in the mappings
+   *
+   * @param test2
+   * @return
+   * @throws UnsupportedEncodingException
+   */
+  private ModifiableSolrParams initExpectedSolrParams( boolean test2 )
+    throws UnsupportedEncodingException
+  {
+    ModifiableSolrParams expectedParams = new ModifiableSolrParams();
+    expectedParams.add( "literal.idAttributeName", "document id" );
+    expectedParams.add( "literal.allowAttributeNameshare", "shareAcl1" );
+    expectedParams.add( "literal.allowAttributeNameshare", "shareAcl2" );
+    expectedParams.add( "literal.denyAttributeNameshare", "denyShareAcl1" );
+    expectedParams.add( "literal.denyAttributeNameshare", "denyShareAcl2" );
+    expectedParams.add( "literal.allowAttributeNamedocument", "acl1" );
+    expectedParams.add( "literal.allowAttributeNamedocument", "acl2" );
+    expectedParams.add( "literal.denyAttributeNamedocument", "denyAcl1" );
+    expectedParams.add( "literal.denyAttributeNamedocument", "denyAcl2" );
+    expectedParams.add( "stream.type", "text/plain" );
+    expectedParams.add( "literal.cm_description_s", "description" );
+    expectedParams.add( "literal.cm_title_s", "title" );
+    expectedParams.add( "literal.cm_name_s", "name" );
+    if ( test2 )
+    {
+      expectedParams.add( "literal.extraMetadata1", "value1" );
+      expectedParams.add( "literal.extraMetadata2", "value2" );
+      expectedParams.add( "literal.extraMetadata3", "value3" );
+    }
+    expectedParams.add( "commitWithin", "true" );
+    return expectedParams;
+  }
+
+  /**
+   * return a list of example metadata fields present in a mock document
+   *
+   * @return
+   */
+  private List<String> getFields()
+  {
+    List<String> fields = new ArrayList<String>();
+    fields.add( "cm:description" );
+    fields.add( "cm:title" );
+    fields.add( "cm:name" );
+    fields.add( "extraMetadata1" );
+    fields.add( "extraMetadata2" );
+    fields.add( "extraMetadata3" );
+    return fields;
+  }
+
+  /**
+   * returns a testing mapping map
+   *
+   * @return
+   */
+  private Map<String, List<String>> getMappingsMap()
+  {
+    Map<String, List<String>> sourceTargets = new HashMap<String, List<String>>();
+    List<String> firstList = new ArrayList<String>();
+    firstList.add( "cm_description_s" );
+    List<String> secondList = new ArrayList<String>();
+    secondList.add( "cm_name_s" );
+    List<String> thirdList = new ArrayList<String>();
+    thirdList.add( "cm_title_s" );
+
+    sourceTargets.put( "cm:description", firstList );
+    sourceTargets.put( "cm:name", secondList );
+    sourceTargets.put( "cm:title", thirdList );
+    return sourceTargets;
+  }
+
+  /**
+   * returns a testing argument map
+   *
+   * @return
+   */
+  private Map<String, List<String>> getArgumentsMap()
+  {
+    Map<String, List<String>> sourceTargets = new HashMap<String, List<String>>();
+    List<String> firstList = new ArrayList<String>();
+    firstList.add( "text/plain" );
+    sourceTargets.put( "stream.type", firstList );
+    return sourceTargets;
+  }
+}
diff --git a/connectors/solr/connector/src/test/java/org/apache/manifoldcf/agents/output/solr/SolrConnectorTest.java b/connectors/solr/connector/src/test/java/org/apache/manifoldcf/agents/output/solr/SolrConnectorTest.java
new file mode 100644
index 0000000..5c5b8e2
--- /dev/null
+++ b/connectors/solr/connector/src/test/java/org/apache/manifoldcf/agents/output/solr/SolrConnectorTest.java
@@ -0,0 +1,102 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.manifoldcf.agents.output.solr;
+
+import junit.framework.TestCase;
+import org.apache.manifoldcf.agents.interfaces.IOutputAddActivity;
+import org.apache.manifoldcf.agents.interfaces.RepositoryDocument;
+import org.apache.manifoldcf.agents.interfaces.ServiceInterruption;
+import org.apache.manifoldcf.core.interfaces.ManifoldCFException;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.powermock.api.mockito.PowerMockito.mock;
+import static org.powermock.api.mockito.PowerMockito.spy;
+
+/**
+ * @Author: Alessandro Benedetti
+ * Date: 18/12/2013
+ */
+public class SolrConnectorTest extends TestCase{
+
+  SolrConnector connectorToTest;
+  HttpPoster poster;
+  RepositoryDocument document;
+  IOutputAddActivity act;
+
+  @Override
+  public void setUp() throws Exception {
+    connectorToTest=spy(new SolrConnector());
+    poster=mock(HttpPoster.class);
+    document=mock(RepositoryDocument.class);
+    act=mock(IOutputAddActivity.class);
+    when(poster.indexPost(anyString(),eq(document),anyMap(),anyMap(),anyBoolean(),anyString(),eq(act))).thenReturn(true);
+
+  }
+
+  /**
+   * Test the AddOrReplaceDocument with an example test string in input
+   * @throws ManifoldCFException
+   * @throws ServiceInterruption
+   */
+  public void testAddOrReplaceDocument() throws ManifoldCFException, ServiceInterruption {
+    Map<String, List<String>> expectedSourceTargets = getMappingsMap();
+    Map<String,List<String>> expectedStreamParams = getStreamTypeMap();
+
+    connectorToTest.poster=poster;
+    String outputDescription = "1+stream.type=text/plain=+3+cm:description=cm_description_s=+cm:name=cm_name_s=+cm:title=cm_title_s=+1+keepAllMetadata=true=+";
+    connectorToTest.addOrReplaceDocument("Document Id", outputDescription,document,"",act);
+    verify(poster).indexPost(eq("Document Id"),eq(document),eq(expectedStreamParams),eq(expectedSourceTargets),eq(true),eq(""),eq(act));
+  }
+
+  /**
+   * returns the expected mappings map for the input string in test
+   * @return
+   */
+  private Map<String, List<String>> getMappingsMap() {
+    Map<String, List<String>> sourceTargets = new HashMap<String, List<String>>();
+    List<String> firstList=new ArrayList<String>();
+    firstList.add("cm_description_s");
+    List<String> secondList=new ArrayList<String>();
+    secondList.add("cm_name_s");
+    List<String> thirdList=new ArrayList<String>();
+    thirdList.add("cm_title_s");
+
+    sourceTargets.put("cm:description",firstList);
+    sourceTargets.put("cm:name",secondList);
+    sourceTargets.put("cm:title",thirdList);
+    return sourceTargets;
+  }
+
+  /**
+   * returns the expected mappings map for the input string in test
+   * @return
+   */
+  private Map<String, List<String>> getStreamTypeMap() {
+    Map<String, List<String>> sourceTargets = new HashMap<String, List<String>>();
+    List<String> firstList=new ArrayList<String>();
+    firstList.add("text/plain");
+    sourceTargets.put("stream.type",firstList);
+    return sourceTargets;
+  }
+}
diff --git a/connectors/solr/pom.xml b/connectors/solr/pom.xml
index 60b6e08..decb607 100644
--- a/connectors/solr/pom.xml
+++ b/connectors/solr/pom.xml
@@ -230,6 +230,18 @@
       <version>${junit.version}</version>
       <scope>test</scope>
     </dependency>
+      <dependency>
+          <groupId>org.powermock</groupId>
+          <artifactId>powermock-module-junit4</artifactId>
+          <version>${powerMock.version}</version>
+          <scope>test</scope>
+      </dependency>
+      <dependency>
+          <groupId>org.powermock</groupId>
+          <artifactId>powermock-api-mockito</artifactId>
+          <version>${powerMock.version}</version>
+          <scope>test</scope>
+      </dependency>
     <dependency>
       <groupId>${project.groupId}</groupId>
       <artifactId>mcf-core</artifactId>
diff --git a/framework/build.xml b/framework/build.xml
index 3e43dc5..631d34c 100644
--- a/framework/build.xml
+++ b/framework/build.xml
@@ -59,7 +59,6 @@
             <include name="slf4j*.jar"/>
             <include name="jsp-2.1*.jar"/>
             <include name="jsp-api*.jar"/>
-            <include name="junit*.jar"/>
             <include name="log4j*.jar"/>
             <include name="serializer*.jar"/>
             <include name="servlet-api*.jar"/>
@@ -82,6 +81,22 @@
         </fileset>
 
     </path>
+
+    <path id="framework-test-classpath">
+        <path refid="framework-classpath"/>
+        <fileset dir="../lib">
+            <include name="junit*.jar"/>
+            <include name="powermock-module-junit4*.jar"/>
+            <include name="powermock-core*.jar"/>
+            <include name="powermock-reflect*.jar"/>
+            <include name="powermock-api-support*.jar"/>
+            <include name="powermock-api-mockito*.jar"/>
+            <include name="mockito-all*.jar"/>
+            <include name="noggit*.jar"/>
+            <include name="javassist*.jar"/>
+            <include name="objenesis*.jar"/>
+        </fileset>
+    </path>
     
     <target name="doc">
         <mkdir dir="dist/doc"/>
@@ -1535,7 +1550,7 @@
         <mkdir dir="build/core-tests/classes"/>
         <javac srcdir="core/src/test/java" destdir="build/core-tests/classes" deprecation="true" target="1.7" source="1.7" debug="true" encoding="utf-8" debuglevel="lines,vars,source">
             <classpath>
-                <path refid="framework-classpath"/>
+                <path refid="framework-test-classpath"/>
                 <pathelement location="build/core/classes"/>
             </classpath>
         </javac>
@@ -1545,7 +1560,7 @@
         <mkdir dir="build/agents-tests/classes"/>
         <javac srcdir="agents/src/test/java" destdir="build/agents-tests/classes" deprecation="true" target="1.7" source="1.7" debug="true" encoding="utf-8" debuglevel="lines,vars,source">
             <classpath>
-                <path refid="framework-classpath"/>
+                <path refid="framework-test-classpath"/>
                 <pathelement location="build/core/classes"/>
                 <pathelement location="build/core-tests/classes"/>
                 <pathelement location="build/agents/classes"/>
@@ -1557,7 +1572,7 @@
         <mkdir dir="build/pull-agent-tests/classes"/>
         <javac srcdir="pull-agent/src/test/java" destdir="build/pull-agent-tests/classes" deprecation="true" target="1.7" source="1.7" debug="true" encoding="utf-8" debuglevel="lines,vars,source">
             <classpath>
-                <path refid="framework-classpath"/>
+                <path refid="framework-test-classpath"/>
                 <pathelement location="build/core/classes"/>
                 <pathelement location="build/core-tests/classes"/>
                 <pathelement location="build/agents/classes"/>
@@ -1571,7 +1586,7 @@
         <mkdir dir="build/script-engine-tests/classes"/>
         <javac srcdir="script-engine/src/test/java" destdir="build/script-engine-tests/classes" deprecation="true" target="1.7" source="1.7" debug="true" encoding="utf-8" debuglevel="lines,vars,source">
             <classpath>
-                <path refid="framework-classpath"/>
+                <path refid="framework-test-classpath"/>
                 <pathelement location="build/core/classes"/>
                 <pathelement location="build/script-engine/classes"/>
             </classpath>
@@ -1611,6 +1626,15 @@
             <fileset dir="../lib">
                 <include name="junit*.jar"/>
                 <include name="juli*.jar"/>
+                <include name="powermock-module-junit4*.jar"/>
+                <include name="powermock-core*.jar"/>
+                <include name="powermock-reflect*.jar"/>
+                <include name="powermock-api-support*.jar"/>
+                <include name="powermock-api-mockito*.jar"/>
+                <include name="mockito-all*.jar"/>
+                <include name="noggit*.jar"/>
+                <include name="javassist*.jar"/>
+                <include name="objenesis*.jar"/>
             </fileset>
             <fileset dir="build/test-jar">
                 <include name="mcf-core-tests.jar"/>
@@ -1627,7 +1651,7 @@
         <mkdir dir="test-output"/>
         <junit fork="true" maxmemory="128m" dir="test-output" outputtoformatters="true" showoutput="true" haltonfailure="true">
             <classpath>
-                <path refid="framework-classpath"/>
+                <path refid="framework-test-classpath"/>
                 <pathelement location="build/core/classes"/>
                 <pathelement location="build/core-tests/classes"/>
             </classpath>
@@ -1646,7 +1670,7 @@
         <mkdir dir="test-output"/>
         <junit fork="true" maxmemory="128m" dir="test-output" outputtoformatters="true" showoutput="true" haltonfailure="true">
             <classpath>
-                <path refid="framework-classpath"/>
+                <path refid="framework-test-classpath"/>
                 <pathelement location="build/core/classes"/>
                 <pathelement location="build/core-tests/classes"/>
                 <pathelement location="build/agents/classes"/>
@@ -1666,7 +1690,7 @@
         <mkdir dir="test-output"/>
         <junit fork="true" maxmemory="128m" dir="test-output" outputtoformatters="true" showoutput="true" haltonfailure="true">
             <classpath>
-                <path refid="framework-classpath"/>
+                <path refid="framework-test-classpath"/>
                 <pathelement location="build/core/classes"/>
                 <pathelement location="build/script-engine/classes"/>
                 <pathelement location="build/script-engine-tests/classes"/>
@@ -1683,7 +1707,7 @@
         <mkdir dir="test-output"/>
         <junit fork="true" maxmemory="128m" dir="test-output" outputtoformatters="true" showoutput="true" haltonfailure="true">
             <classpath>
-                <path refid="framework-classpath"/>
+                <path refid="framework-test-classpath"/>
                 <fileset dir="build/test-jar">
                     <include name="mcf-core-tests.jar"/>
                 </fileset>
@@ -1701,7 +1725,7 @@
         <mkdir dir="test-derby-output"/>
         <junit fork="true" maxmemory="128m" dir="test-derby-output" outputtoformatters="true" showoutput="true" haltonfailure="true">
             <classpath>
-                <path refid="framework-classpath"/>
+                <path refid="framework-test-classpath"/>
                 <pathelement location="build/core/classes"/>
                 <pathelement location="build/ui-core/classes"/>
                 <pathelement location="build/agents/classes"/>
@@ -1724,7 +1748,7 @@
         <mkdir dir="test-postgresql-output"/>
         <junit fork="true" maxmemory="128m" dir="test-postgresql-output" outputtoformatters="true" showoutput="true" haltonfailure="true">
             <classpath>
-                <path refid="framework-classpath"/>
+                <path refid="framework-test-classpath"/>
                 <pathelement location="build/core/classes"/>
                 <pathelement location="build/ui-core/classes"/>
                 <pathelement location="build/agents/classes"/>
@@ -1747,7 +1771,7 @@
         <mkdir dir="test-mysql-output"/>
         <junit fork="true" maxmemory="128m" dir="test-mysql-output" outputtoformatters="true" showoutput="true" haltonfailure="true">
             <classpath>
-                <path refid="framework-classpath"/>
+                <path refid="framework-test-classpath"/>
                 <pathelement location="build/core/classes"/>
                 <pathelement location="build/ui-core/classes"/>
                 <pathelement location="build/agents/classes"/>
@@ -1770,7 +1794,7 @@
         <mkdir dir="test-HSQLDB-output"/>
         <junit fork="true" maxmemory="128m" dir="test-HSQLDB-output" outputtoformatters="true" showoutput="true" haltonfailure="true">
             <classpath>
-                <path refid="framework-classpath"/>
+                <path refid="framework-test-classpath"/>
                 <pathelement location="build/core/classes"/>
                 <pathelement location="build/ui-core/classes"/>
                 <pathelement location="build/agents/classes"/>
@@ -1793,7 +1817,7 @@
         <mkdir dir="test-HSQLDBext-output"/>
         <junit fork="true" maxmemory="128m" dir="test-HSQLDBext-output" outputtoformatters="true" showoutput="true" haltonfailure="true">
             <classpath>
-                <path refid="framework-classpath"/>
+                <path refid="framework-test-classpath"/>
                 <pathelement location="build/core/classes"/>
                 <pathelement location="build/ui-core/classes"/>
                 <pathelement location="build/agents/classes"/>
diff --git a/framework/buildfiles/connector-build.xml b/framework/buildfiles/connector-build.xml
index 3ba2cc4..8bf78aa 100644
--- a/framework/buildfiles/connector-build.xml
+++ b/framework/buildfiles/connector-build.xml
@@ -130,7 +130,6 @@
             <include name="slf4j*.jar"/>
             <include name="jsp-2.1*.jar"/>
             <include name="jsp-api*.jar"/>
-            <include name="junit*.jar"/>
             <include name="log4j*.jar"/>
             <include name="serializer*.jar"/>
             <include name="servlet-api*.jar"/>
diff --git a/lib-license/LICENSE.txt b/lib-license/LICENSE.txt
index 6b54daf..9dad7ff 100644
--- a/lib-license/LICENSE.txt
+++ b/lib-license/LICENSE.txt
@@ -327,10 +327,29 @@
 This product includes a tagsoup.jar.
 License: Apache 2 (http://home.ccil.org/~cowan/XML/tagsoup/)
 
+This product includes a mockito-all.jar.
+License: MIT (http://code.google.com/p/mockito/wiki/License)
+
 This product may include pdf files that embed IPA-licensed fonts.
 License: IPA Font License Agreement v1.0 (http://ossipedia.ipa.go.jp/ipafont/index.html#LicenseEng)
 
 ==========================================================================
+The following license applies to mockito-all.jar:
+--------------------------------------------------------------------------
+
+
+The MIT License
+
+Copyright (c) 2007 Mockito contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+==========================================================================
 The following license applies to json.jar, which is generated from the json
 sources from http://www.json.org/java/index.html
 --------------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index b7e6450..d64e8ed 100644
--- a/pom.xml
+++ b/pom.xml
@@ -38,6 +38,7 @@
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <!-- dependency versions -->
     <junit.version>4.8.2</junit.version>
+    <powerMock.version>1.5.2</powerMock.version>
     <postgresql.version>9.1-901.jdbc4</postgresql.version>
     <mysql.version>5.1.18</mysql.version>
     <hsqldb.version>2.3.1</hsqldb.version>