/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.test.capedwarf.search.test;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.google.appengine.api.search.Document;
import com.google.appengine.api.search.Field;
import com.google.appengine.api.search.GetIndexesRequest;
import com.google.appengine.api.search.GetRequest;
import com.google.appengine.api.search.GetResponse;
import com.google.appengine.api.search.Index;
import com.google.appengine.api.search.IndexSpec;
import com.google.appengine.api.search.PutResponse;
import com.google.appengine.api.search.Query;
import com.google.appengine.api.search.QueryOptions;
import com.google.appengine.api.search.Results;
import com.google.appengine.api.search.ScoredDocument;
import com.google.appengine.api.search.SearchService;
import com.google.appengine.api.search.SearchServiceConfig;
import com.google.appengine.api.search.SearchServiceFactory;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.jboss.test.capedwarf.common.test.TestBase;
import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
* @author <a href="mailto:mluksa@redhat.com">Marko Luksa</a>
*/
@RunWith(Arquillian.class)
public abstract class SearchTestBase extends TestBase {
public static final String FOO_NAMESPACE = "fooNamespace";
public static final String BAR_NAMESPACE = "barNamespace";
protected SearchService service;
@Deployment
public static WebArchive getDeployment() {
return getCapedwarfDeployment().addClass(SearchTestBase.class);
}
protected void clear() {
removeAllDocumentsFrom(service);
removeAllDocumentsFrom(getSearchService(FOO_NAMESPACE));
removeAllDocumentsFrom(getSearchService(BAR_NAMESPACE));
}
private void removeAllDocumentsFrom(SearchService service) {
GetResponse<Index> response = service.getIndexes(GetIndexesRequest.newBuilder());
for (Index index : response.getResults()) {
GetResponse<Document> documents = index.getRange(GetRequest.newBuilder());
for (Document document : documents.getResults()) {
index.delete(document.getId());
}
}
}
@Before
public void setUp() throws Exception {
service = SearchServiceFactory.getSearchService();
clear();
}
@After
public void tearDown() throws Exception {
clear();
}
protected static void assertDocumentContainsField(Document doc, String fieldName, Field.FieldType fieldType, Object fieldValue) {
for (Field field : doc.getFields(fieldName)) {
if (field.getType().equals(fieldType)) {
Object storedValue = getFieldValue(field, fieldType);
if (storedValue.equals(fieldValue)) {
return;
}
}
}
fail("Document does not contain field " + fieldName + " of type " + fieldType + " with value " + fieldValue);
}
protected static Object getFieldValue(Field field, Field.FieldType fieldType) {
switch (fieldType) {
case TEXT:
return field.getText();
case ATOM:
return field.getAtom();
default:
throw new InternalError("Not implemented");
}
}
protected static void assertListContainsDocumentsWithIds(Collection<String> documentIds, List<Document> documents) {
HashSet<String> ids = new HashSet<String>(documentIds);
for (Document document : documents) {
boolean removed = ids.remove(document.getId());
if (!removed) {
fail("list contained document with unexpected id: " + document.getId());
}
}
if (!ids.isEmpty()) {
fail("list did not contain the following ids: " + ids);
}
}
protected Document newEmptyDocument() {
return Document.newBuilder().build();
}
protected Document newEmptyDocument(String id) {
return newDocument(id);
}
protected Document newDocument(Field.Builder... fields) {
return newDocument(null, fields);
}
protected Document newDocument(String id, Field.Builder... fields) {
Document.Builder builder = Document.newBuilder();
if (id != null) {
builder.setId(id);
}
for (Field.Builder field : fields) {
builder.addField(field);
}
return builder.build();
}
protected Field.Builder newField(String fieldName) {
return Field.newBuilder().setName(fieldName);
}
protected Index getTestIndex() {
return getIndex("testIndex");
}
protected Index getIndexInNamespace(String name, String namespace) {
IndexSpec indexSpec = getIndexSpec(name);
return getSearchService(namespace).getIndex(indexSpec);
}
protected Index getIndex(String name) {
IndexSpec indexSpec = getIndexSpec(name);
return service.getIndex(indexSpec);
}
protected IndexSpec getIndexSpec(String name) {
return IndexSpec.newBuilder().setName(name).build();
}
protected GetRequest defaultListRequest() {
return GetRequest.newBuilder().build();
}
protected Document addAndRetrieve(Document doc) {
Index index = getTestIndex();
PutResponse addResponse = index.put(doc);
String docId = addResponse.getIds().get(0);
List<Document> results = getAllDocumentsIn(index);
assertEquals(1, results.size());
Document retrievedDoc = results.get(0);
assertEquals(docId, retrievedDoc.getId());
return retrievedDoc;
}
protected int numberOfDocumentsIn(Index index) {
return getAllDocumentsIn(index).size();
}
protected List<Document> getAllDocumentsIn(Index index) {
if (isRunningInsideGaeDevServer()) {
fixListDocumentsBugWhenInvokedOnEmptyIndex(index);
}
return index.getRange(defaultListRequest()).getResults();
}
/**
* On GAE development server, calling index.listDocuments() throws a ListException (and logs a FileNotFoundException) if
* the index hadn't had anything added to it prior to calling listDocuments().
*
* We work around this bug by storing and immediately removing a document.
*/
private void fixListDocumentsBugWhenInvokedOnEmptyIndex(Index index) {
index.put(newEmptyDocument("indexInitializer"));
index.delete("indexInitializer");
}
protected void createEmptyDocuments(int number, Index index) {
for (int i=0; i<number; i++) {
index.put(newEmptyDocument());
}
}
protected Index createIndex(String indexName) {
Index index = getIndex(indexName);
index.put(newEmptyDocument());
return index;
}
protected Index createIndexInNamespace(String indexName, String namespace) {
Index index = getIndexInNamespace(indexName, namespace);
index.put(newEmptyDocument());
return index;
}
protected void assertSearchYields(Index index, String queryString, String... documentIds) {
assertSearchYields(index, queryString, null, documentIds.length, documentIds);
}
protected void assertSearchYields(Index index, String queryString, QueryOptions options, int expectedNumberFound, String... documentIds) {
Results<ScoredDocument> results = getResults(index, queryString, options);
Collection<ScoredDocument> scoredDocuments = results.getResults();
assertEquals("number of found documents", expectedNumberFound, results.getNumberFound());
assertEquals("number of returned documents", documentIds.length, results.getNumberReturned());
assertEquals("actual number of ScoredDcuments", documentIds.length, results.getResults().size());
Set<String> expectedDocumentIds = new HashSet<String>(Arrays.asList(documentIds));
for (ScoredDocument scoredDocument : scoredDocuments) {
boolean wasContained = expectedDocumentIds.remove(scoredDocument.getId());
if (!wasContained) {
fail("Search \"" + queryString + "\" yielded unexpected document id: " + scoredDocument.getId());
}
}
}
protected Results<ScoredDocument> getResults(Index index, String queryString, QueryOptions options) {
Query query = Query.newBuilder().setOptions(options).build(queryString);
return index.search(query);
}
protected SearchService getSearchService(String namespace) {
return SearchServiceFactory.getSearchService(SearchServiceConfig.newBuilder().setNamespace(namespace).build());
}
}