/*
* 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.sling.launchpad.webapp.integrationtest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.sling.commons.json.JSONException;
import org.apache.sling.commons.testing.integration.HttpTestBase;
/** Test the {link JsonQueryServlet) functionality.
* We don't need to test the repository query feature, just
* make sure that the query servlet parameters are interpreted correctly.
*/
public class JsonQueryServletTest extends HttpTestBase {
private String testFolderUrl;
private final String testPath = "/" + getClass().getSimpleName() + "_" + System.currentTimeMillis();
private final static String counterCode = "out.print(data.length);";
@Override
protected void setUp() throws Exception {
super.setUp();
testFolderUrl = testClient.createNode(HTTP_BASE_URL + testPath, null);
// create subfolders A..E with subnodes 0..4, each contains its name as a text property
for(char folder = 'A'; folder <= 'E'; folder++) {
final Map<String, String> props = new HashMap<String, String> ();
props.put("creator", getClass().getSimpleName());
props.put("date", "2008-04-13T17:55:00");
props.put("date@TypeHint", "Date");
props.put("text", "folder " + folder);
final String subfolderUrl = testClient.createNode(testFolderUrl + "/folder" + folder, props);
for(int i=0; i < 5; i++) {
props.put("text", "folder " + folder + " node "+ i);
testClient.createNode(subfolderUrl + "/node" + i, props);
}
}
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
if(testFolderUrl != null) {
testClient.delete(WEBDAV_BASE_URL + testPath);
}
}
private void assertCount(int expectedCount, String statement, String queryType, int offset, int rows)
throws IOException {
assertCount(expectedCount, statement, queryType, offset, rows, false);
}
private void assertCount(int expectedCount, String statement, String queryType, int offset, int rows,
boolean tidy)
throws IOException {
final List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new NameValuePair("statement", statement));
if(queryType != null) {
params.add(new NameValuePair("queryType", queryType));
}
if(offset > 0) {
params.add(new NameValuePair("offset", String.valueOf(offset)));
}
if(rows > 0) {
params.add(new NameValuePair("rows", String.valueOf(rows)));
}
final String json = getContent(testFolderUrl + ".query" + (tidy ? ".tidy" : "") + ".json", CONTENT_TYPE_JSON, params);
assertJavascript(
expectedCount + ".0",
json,
counterCode,
"statement=" + statement + ", queryType=" + queryType
);
}
public void testFolderQuery() throws IOException {
assertCount(1, "/" + testPath + "/folderC", "xpath", 0, 0);
}
public void testSubFolderQuery() throws IOException {
assertCount(5, "/" + testPath + "/folderA/*", "xpath", 0, 0);
}
public void testDefaultQueryType() throws IOException {
assertCount(5, "/" + testPath + "/folderE/*", null, 0, 0);
}
public void testSql() throws IOException {
final String query = "select * from nt:unstructured where jcr:path like '" + testPath + "/folderB/%'";
assertCount(5, query, "sql", 0, 0);
}
public void testSql2() throws IOException {
final String query = "select * from [nt:unstructured] where ISDESCENDANTNODE('" + testPath + "/folderB')";
assertCount(5, query, "JCR-SQL2", 0, 0);
}
public void testOffset() throws IOException {
assertCount(3, "/" + testPath + "/folderC/*", "xpath", 2, 0);
}
public void testRows() throws IOException {
assertCount(2, "/" + testPath + "/folderC/*", "xpath", 0, 2);
}
public void testPropertyParam() throws IOException {
final String url = testFolderUrl + ".query.json";
final String statement = "/" + testPath + "/folderB/node3";
final List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new NameValuePair("statement", statement));
String json = getContent(url, CONTENT_TYPE_JSON, params);
assertJavascript("1.0", json, counterCode);
assertJavascript("ok", json, "if(!data[0].text) out.print('ok')");
assertJavascript("ok", json, "if(!data[0].creator) out.print('ok')");
params.add(new NameValuePair("property", "text"));
json = getContent(url, CONTENT_TYPE_JSON, params);
assertJavascript("1.0", json, counterCode);
assertJavascript("ok", json, "if(data[0].text) out.print('ok')");
assertJavascript("folder B node 3", json, "out.print(data[0].text)");
assertJavascript("ok", json, "if(!data[0].creator) out.print('ok')");
params.add(new NameValuePair("property", "creator"));
json = getContent(url, CONTENT_TYPE_JSON, params);
assertJavascript("1.0", json, counterCode);
assertJavascript("ok", json, "if(data[0].text) out.print('ok')");
assertJavascript("folder B node 3", json, "out.print(data[0].text)");
assertJavascript("ok", json, "if(data[0].creator) out.print('ok')");
assertJavascript(getClass().getSimpleName(), json, "out.print(data[0].creator)");
params.add(new NameValuePair("property", "date"));
json = getContent(url, CONTENT_TYPE_JSON, params);
assertJavascript("1.0", json, counterCode);
assertJavascript("ok", json, "if(data[0].date) out.print('ok')");
assertJavascript("Sun", json, "out.print(data[0].date.substring(0,3))");
}
/**
* Test for SLING-1632: tidy rendering of query results
*/
public void testTidyResultFormat() throws IOException, JSONException {
boolean tidy = true;
//query should function the same when the output is tidy'ed.
String statement = "/" + testPath + "/folderA/*";
String queryType = "xpath";
assertCount(5, statement, queryType, 0, 0, tidy);
final List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new NameValuePair("statement", statement));
params.add(new NameValuePair("queryType", queryType));
final String json = getContent(testFolderUrl + ".query.json", CONTENT_TYPE_JSON, params);
final String tidyJson = getContent(testFolderUrl + ".query.tidy.json", CONTENT_TYPE_JSON, params);
//tidy json text should have whitespace that makes it not be equivalent to the untidy version
assertNotSame(json, tidyJson);
int noTidyCount = countOccurences(json, '\n');
int tidyCount = countOccurences(tidyJson, '\n');
int delta = tidyCount - noTidyCount;
// tidy output contains at least 25 additional EOL chars
int min = 25;
assertTrue("The .tidy selector should add at least 25 EOL chars to json output (delta=" + delta + ")", delta > min);
}
protected static int countOccurences(String str, char toCount) {
int result = 0;
for(char c : str.toCharArray()) {
if(c == toCount) {
result++;
}
}
return result;
}
}