<artifactId>commons-jexl</artifactId>\r
<version>2.0.1</version>\r
</dependency>\r
+ <dependency>\r
+ <groupId>jdom</groupId>\r
+ <artifactId>jdom</artifactId>\r
+ <version>1.0</version>\r
+ <scope>provided</scope>\r
+ </dependency>\r
</dependencies>\r
\r
<build>\r
--- /dev/null
+/**\r
+ * This document is a part of the source code and related artifacts\r
+ * for CollectionSpace, an open source collections management system\r
+ * for museums and related institutions:\r
+ *\r
+ * http://www.collectionspace.org\r
+ * http://wiki.collectionspace.org\r
+ *\r
+ * Copyright (c) 2009 Regents of the University of California\r
+ *\r
+ * Licensed under the Educational Community License (ECL), Version 2.0.\r
+ * You may not use this file except in compliance with this License.\r
+ *\r
+ * You may obtain a copy of the ECL 2.0 License at\r
+ * https://source.collectionspace.org/collection-space/LICENSE.txt\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package org.collectionspace.services.IntegrationTests.xmlreplay;\r
+\r
+import java.io.*;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+public class PayloadLogger{\r
+\r
+ public static void saveReport(){\r
+ reporter.writeTable();\r
+ }\r
+\r
+ private static Reporter reporter = new Reporter();\r
+\r
+ private static volatile int ID = 1000;\r
+ public String getID(){\r
+ return ""+ID;\r
+ }\r
+\r
+ private static String DD = "--";\r
+ private static String LABEL="LABEL: ";\r
+\r
+ static class Reporter {\r
+ public Reporter(){\r
+ table.append("<html><body><table border='1'>\r\n");\r
+ }\r
+ public static final String START = "<tr><td>";\r
+ public static final String SEP = "</td><td>";\r
+ public static final String END = "</td></tr>";\r
+\r
+ public void writeTable(){\r
+ table.append("</table></body></html>");\r
+ saveFile("./xml/", "results.html", table.toString());\r
+ }\r
+\r
+ private StringBuffer table = new StringBuffer();\r
+\r
+ public synchronized void finished(HttpTraffic traffic){\r
+ String direction = traffic.isRequest ?\r
+ "<span style='background-color: lightgreen;'>==></span>"\r
+ :\r
+ " <==";\r
+ table.append(START)\r
+ .append(direction)\r
+ .append(SEP)\r
+ .append(traffic==null ? "null" : traffic.toRow(SEP, this))\r
+ .append(END);\r
+ }\r
+\r
+ public synchronized void finished(List<HttpTraffic> trafficList){\r
+ for (HttpTraffic traffic: trafficList){\r
+ finished(traffic);\r
+ }\r
+ }\r
+\r
+ public static String link(String desc, String url){\r
+ return "<a href='"+url+"'>"+desc+"</a>";\r
+ }\r
+ public static final String newline = "<br />\r\n";\r
+\r
+ }\r
+\r
+\r
+ static class HttpTraffic {\r
+ public HttpTraffic(){\r
+ payloads = new ArrayList<Part>();\r
+ }\r
+ public List<Part> payloads;\r
+ public String method = "";\r
+ public String url = "";\r
+ public String queryParams = "";\r
+ public String message = "";\r
+ public int responseCode = 0;\r
+ public String boundary = "";\r
+ public String location = "";\r
+ public long contentLength = -1;\r
+ public boolean isRequest = true;\r
+ public boolean extra = false;\r
+ public String ID = "0";\r
+ public int nexti = 0;\r
+\r
+ public Part getPart(String label){\r
+ for (Part part : payloads){\r
+ if (part.label.equalsIgnoreCase(label)){\r
+ return part;\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+\r
+ public String toRow(String sep, Reporter reporter){\r
+ StringBuffer b = new StringBuffer();\r
+ for (Part part : payloads){\r
+ String name = part.label;\r
+ if (part.filename.length()==0){\r
+ continue;\r
+ }\r
+ if (name.trim().length()<=0){\r
+ name = ID;\r
+ }\r
+ name = name+" ("+part.filetype+')';\r
+ String link = reporter.link(name, part.filename);\r
+ b.append(link)\r
+ .append(reporter.newline);\r
+ }\r
+ String parts = b.toString();\r
+ if (isRequest){\r
+ return ID+sep\r
+ +method+sep\r
+ +url+sep\r
+ +queryParams+sep\r
+ +sep\r
+ +parts;\r
+ } else {\r
+ return ID+sep\r
+ +responseCode+sep\r
+ +message+sep\r
+ +location+sep\r
+ +contentLength+sep\r
+ +url\r
+ +parts;\r
+ }\r
+ }\r
+ }\r
+\r
+ static class Part {\r
+ public Part(String boundary){\r
+ this.boundary = boundary;\r
+ }\r
+ public boolean isMultipart(){\r
+ return boundary.length()>0;\r
+ }\r
+ public String toString(){\r
+ return "Part:"+label+";";\r
+ }\r
+ public String filename = "";\r
+ public String filetype = "";\r
+ public String boundary;\r
+ public StringBuffer buffer = new StringBuffer();\r
+ public String getContent(){\r
+ return buffer.toString();\r
+ }\r
+ public String label = "";\r
+ public int readPart(String[]lines, int i){\r
+ String line = "";\r
+ boolean readingPartHeaders = true;\r
+ while(readingPartHeaders){\r
+ line = killTrailingWS(lines[i]);\r
+ if (line.toUpperCase().startsWith(LABEL)){\r
+ this.label = line.substring(LABEL.length()).trim();\r
+ } else if (line.trim().length()==0){\r
+ readingPartHeaders = false;\r
+ }\r
+ i++;\r
+ }\r
+ while (i<lines.length){\r
+ line = lines[i];\r
+ if (line.startsWith(DD+boundary)){\r
+ return i; \r
+ }\r
+ this.buffer.append(line).append("\r\n"); //todo: maybe don't add CRLF on last line.\r
+ i++;\r
+ }\r
+ return i; \r
+ }\r
+ public int readRemaining(String [] lines, int i, long contentLength){\r
+ String line;\r
+ int bytesRead=0;\r
+ while (i<lines.length && bytesRead<contentLength){\r
+ line = killTrailingWS(lines[i]);\r
+ if (line.startsWith("HTTP/1.1")){\r
+ return i;\r
+ }\r
+ int read = line.length();\r
+ bytesRead += read;\r
+ buffer.append(line).append("\r\n"); //todo: maybe don't add CRLF on last line.\r
+ i++;\r
+ }\r
+ return i;\r
+ }\r
+ }\r
+\r
+ public static String parseBoundary(String headerLine) {\r
+ if (Tools.isEmpty(headerLine)) {\r
+ return "";\r
+ }\r
+ String lineUP = headerLine.toUpperCase();\r
+ String boundary = "";\r
+ if (lineUP.startsWith("CONTENT-TYPE:")) {\r
+ String[] boundaryTokens = headerLine.split("boundary=");\r
+ if (boundaryTokens.length == 2) {\r
+ boundary = killTrailingWS(boundaryTokens[1]);\r
+\r
+ } else if (boundaryTokens.length > 2) {\r
+ System.err.println("WARNING: too many tokens after boundary= on Content-Type: header line: " + headerLine);\r
+ }\r
+ }\r
+ return boundary;\r
+ }\r
+\r
+ /** places the boundary on the HttpTraffic in parameter object if boundary found in header "Content-Type:".\r
+ * @return the index of the NEXT line the caller should read. */\r
+ protected static int readHeaders(HttpTraffic traffic, String[]lines, int i){\r
+ int lineCount = lines.length;\r
+ String line, lineUP;\r
+ // Now read headers until we are ready for payload or parts.\r
+ while (i<lineCount){\r
+ line = lines[i];\r
+ if (line.trim().length()==0){ //blank line seen: end of headers.\r
+ i++;\r
+ break;\r
+ } else { //still reading outer headers.\r
+ lineUP = line.toUpperCase().trim();\r
+ if (lineUP.startsWith("CONTENT-TYPE:")){\r
+ String[] boundaryTokens = line.split("boundary=");\r
+ if (boundaryTokens.length == 2){\r
+ traffic.boundary = killTrailingWS(boundaryTokens[1]);\r
+\r
+ } else if (boundaryTokens.length > 2){\r
+ System.err.println("WARNING: too many tokens after boundary= on Content-Type: header line: "+line);\r
+ }\r
+ } else if (lineUP.startsWith("LOCATION: ")){\r
+ traffic.location = killTrailingWS(line.substring("LOCATION: ".length()));\r
+ } else if (lineUP.startsWith("CONTENT-LENGTH: ")){\r
+ traffic.contentLength = Integer.parseInt(killTrailingWS(line.substring("CONTENT-LENGTH: ".length())));\r
+ }\r
+ i++;\r
+ }\r
+ }\r
+ return i;\r
+ }\r
+\r
+\r
+ // 0 1 2 3\r
+ // a b c \r\r
+\r
+ private static String killTrailingWS(String s){\r
+ int i = s.length();\r
+ while (i>0){\r
+ char c = s.charAt(i-1);\r
+ if (c=='\r' || c=='\n' || c==' '){\r
+ i--;\r
+ continue;\r
+ } else {\r
+ break;\r
+ }\r
+ }\r
+ return s.substring(0, i);\r
+ }\r
+\r
+ public static HttpTraffic readPayloads(String fullPayload, String boundary, long contentLength){\r
+ HttpTraffic traffic = new HttpTraffic();\r
+ traffic.contentLength = contentLength;\r
+ traffic.boundary = boundary;\r
+ String [] lines = fullPayload.split("\\n", -1);\r
+ readPayloads(traffic, lines, 0);\r
+ return traffic;\r
+ }\r
+\r
+ protected static int readPayloads(HttpTraffic traffic, String[]lines, int i){\r
+ if (traffic.boundary.length()<=0){ //END of headers, and no boundary, so read remaining and return.\r
+ if (traffic.contentLength == 0){\r
+ return i;\r
+ }\r
+ Part part = new Part("");\r
+ traffic.payloads.add(part);\r
+ i = part.readRemaining(lines, i, traffic.contentLength);\r
+ return i;\r
+ }\r
+ int lineCount = lines.length;\r
+ String line;\r
+ while (i<lineCount){\r
+ //rest of message is payloads.\r
+ line = lines[i];\r
+\r
+ if (line.startsWith( DD + traffic.boundary + DD )){ //this is the ending boundary.\r
+ //close and accept payload chunk.\r
+ i++; //bump past last boundary. There might be more traffic after this.\r
+ return i;\r
+ } else if (line.startsWith(DD + traffic.boundary)){ //this is a first or middle boundary, but not last boundary.\r
+ i++; //bump past boundary\r
+ //begin payload chunk\r
+ Part part = new Part(traffic.boundary);\r
+ traffic.payloads.add(part);\r
+ i = part.readPart(lines, i);\r
+ } else {\r
+ return i;\r
+ //if (line.trim().length()>0){\r
+ // System.err.println("********** Skipping line: "+line); //either parser error, or something is outside of a boundary.\r
+ //}\r
+ //i++;\r
+ }\r
+ }\r
+ return i;\r
+ }\r
+\r
+ \r
+ private HttpTraffic parseForward(String forward, int nexti){\r
+ HttpTraffic forwardTraffic = new HttpTraffic();\r
+ forwardTraffic.isRequest = true;\r
+ forwardTraffic.ID = getID();\r
+ //String[] lines = forward.split("\\r\\n", -1);\r
+ String[] lines = forward.split("\\n", -1);\r
+ int lineCount = lines.length;\r
+ String line;\r
+ int i = nexti;\r
+\r
+ // Read the first line, and figure out if GET, POST, etc., and the URI\r
+ line = lines[i];\r
+ while (line.trim().length()==0){\r
+ i++;\r
+ if (i>=lineCount-1){\r
+ return null;\r
+ }\r
+ line = lines[i];\r
+ }\r
+ String[] tokens = line.split(" ", -1);\r
+ forwardTraffic.method = tokens[0];\r
+ String urlString = tokens[1];\r
+ String[] urlHalves = urlString.split("\\?", -1); //look for a query string of the form /foo/bar?param=baz and break on question mark.\r
+ forwardTraffic.url = urlHalves[0];\r
+ if (urlHalves.length > 1){\r
+ forwardTraffic.queryParams = urlHalves[1];\r
+ }\r
+ i++;\r
+\r
+ //if (forwardTraffic.method.equals("GET")|| forwardTraffic.method.equals("DELETE")){\r
+ // return forwardTraffic;\r
+ //}\r
+ // Now read headers until we are ready for payload or parts.\r
+ i = readHeaders(forwardTraffic, lines, i);\r
+\r
+ /*\r
+ if ( (i<lines.length-1) && (forwardTraffic.contentLength<=0) ) { //0 means a 0 was seen, -1 means no header was seen, as will be the case in GET or DELETE.\r
+\r
+ //there are more lines, but content-length header was zero,\r
+ // this means we are getting keep-alive bunches of DELETEs or OKs back.\r
+ System.err.println("###### extra requests in this one."+getID());\r
+ String filename = getID()+'_'+forwardTraffic.method+'_'+forwardTraffic.url.replaceAll("/", "_")+".requests";\r
+ saveFile("./xml", filename, forward);\r
+ return forwardTraffic;\r
+ }\r
+ */\r
+\r
+ // We are past headers now. The rest of message is payloads.\r
+ i = readPayloads(forwardTraffic, lines, i); //messes with forwardTraffic and places parts in it.\r
+ forwardTraffic.nexti = i;\r
+ return forwardTraffic;\r
+ }\r
+\r
+ private HttpTraffic parseReverse(String reverse, int nexti){\r
+ HttpTraffic reverseTraffic = new HttpTraffic();\r
+ reverseTraffic.isRequest = false;\r
+ reverseTraffic.ID = getID();\r
+ //String[] lines = reverse.split("\\r\\n", -1);\r
+ String[] lines = reverse.split("\\n", -1);\r
+ int lineCount = lines.length;\r
+ String line;\r
+ int i = nexti;\r
+ if (i>=lineCount){\r
+ return null;\r
+ }\r
+ line = lines[i];\r
+ \r
+ // Read the first line, and figure out response code, message.\r
+ while (i<lineCount){\r
+ if (line.startsWith("HTTP/1.1")){\r
+ break;\r
+ }\r
+ i++;\r
+ line = lines[i];\r
+ }\r
+ String[] tokens = line.split(" ", 3);\r
+ String HTTP11 = tokens[0];\r
+ reverseTraffic.responseCode = Integer.parseInt(tokens[1]);\r
+ reverseTraffic.message = killTrailingWS(tokens[2]);\r
+ i++; // done reading first line. Bump past first line.\r
+\r
+ //if (forwardResult.message.equals("OK")){\r
+ // return forwardResult;\r
+ //}\r
+\r
+ // Now read headers until we are ready for payload or parts.\r
+ i = readHeaders(reverseTraffic, lines, i);\r
+\r
+ /*\r
+ if ( (i<lines.length-1) && (reverseTraffic.contentLength==0) ) {\r
+ //there are more lines, but content-length header was zero,\r
+ // this means we are getting keep-alive bunches of DELETEs or OKs back.\r
+ System.err.println("###### extra responses in this one."+id);\r
+ String filename = getID()+".reponses";\r
+ saveFile("./xml", filename, reverse);\r
+ reverseTraffic.extra = true;\r
+ return reverseTraffic;\r
+ }\r
+ */\r
+ // We are past headers now. The rest of message is payloads.\r
+ i = readPayloads(reverseTraffic, lines, i); //messes with forwardResult and places parts in it.\r
+ reverseTraffic.nexti = i;\r
+ if (i>=lineCount){\r
+ reverseTraffic.nexti = -1;\r
+ }\r
+ return reverseTraffic;\r
+ }\r
+\r
+ private List<HttpTraffic> handleTcpDump(String dump){\r
+ int i = 0;\r
+ int trafficID = 0;\r
+ List<HttpTraffic> trafficList = new ArrayList<HttpTraffic>();\r
+ while (i>-1){\r
+ trafficID++;\r
+ HttpTraffic forward = parseForward(dump, i);\r
+ if (forward==null) break;\r
+ i = forward.nexti;\r
+ forward.ID = ""+trafficID;\r
+ if (forward.payloads.size()>0){\r
+ saveForwardFiles(forward);\r
+ }\r
+ trafficList.add(forward);\r
+\r
+ HttpTraffic reverse = parseReverse(dump, i);\r
+ if (reverse==null) break;\r
+ reverse.ID = ""+trafficID;\r
+ i = reverse.nexti;\r
+ if (reverse.payloads.size()>0){\r
+ saveReverseFiles(reverse);\r
+ }\r
+ trafficList.add(reverse);\r
+ }\r
+ return trafficList;\r
+ }\r
+\r
+ public static File saveFile(String dir, String relativeName, String content){\r
+ File result = null;\r
+ PrintWriter writer;\r
+ try{\r
+ result = new File(dir, relativeName);\r
+ writer = new PrintWriter(new FileOutputStream(result));\r
+ }catch (Exception e){\r
+ System.out.println("Can't write to file in saveFile: " + relativeName + " \r\n" + e);\r
+ return null;\r
+ }\r
+ writer.write(content);\r
+ writer.close();\r
+ return result;\r
+ }\r
+ \r
+ private void saveForwardFiles(HttpTraffic fr){\r
+ for (Part part : fr.payloads){\r
+ String body = part.buffer.toString();\r
+ if (body.trim().length()==0){\r
+ continue;\r
+ }\r
+ String filename = fr.ID+'_'+fr.method+'_'+fr.url.replaceAll("/", "_")+'_'+part.label+".xml";\r
+ filename = filename.replaceAll("/", "_");\r
+ System.out.println("trying to save file: "+filename+" :: "+fr);\r
+ part.filename = filename;\r
+ saveFile("./xml", filename, body);\r
+ }\r
+ }\r
+ \r
+ private void saveReverseFiles(HttpTraffic fr){\r
+ for (Part part : fr.payloads){\r
+ String body = part.buffer.toString();\r
+ if (body.trim().length()==0){\r
+ continue;\r
+ }\r
+ String filename = fr.ID+'_'+fr.method+'_'+fr.url.replaceAll("/", "_");\r
+ if (part.label.length()==0){\r
+ if (body.trim().startsWith("<?xml")){\r
+ filename = filename + "_res.xml";\r
+ part.filetype = "xml";\r
+ } else {\r
+ filename = filename + "_res.txt";\r
+ part.filetype = "txt";\r
+ }\r
+ } else {\r
+ filename = filename + '_'+part.label+"_res.xml";\r
+ part.filetype = "xml";\r
+ }\r
+ filename = filename.replaceAll("/", "_");\r
+ System.out.println("trying to save file: "+filename+" :: "+fr);\r
+ part.filename = filename;\r
+ saveFile("./xml", filename, body);\r
+ }\r
+ }\r
+\r
+ public static String readFile(String dir, String relPath) throws Exception{\r
+ File theFile = new File(dir, relPath);\r
+ FileInputStream fis = new FileInputStream(theFile);\r
+ byte[] theData = new byte[(int) theFile.length()];\r
+ // need to check the number of bytes read here\r
+ int howmany = fis.read(theData);\r
+ fis.close();\r
+ return new String(theData);\r
+ }\r
+\r
+ public static List<HttpTraffic> process(String httpSessionTraffic){\r
+ PayloadLogger pll = new PayloadLogger();\r
+ List<HttpTraffic> trafficList = pll.handleTcpDump(httpSessionTraffic);\r
+ return trafficList;\r
+ }\r
+\r
+ public static void main(String[]args) throws Exception {\r
+ String dump = readFile(".", args[0]);\r
+ PayloadLogger pll = new PayloadLogger();\r
+ List<HttpTraffic> trafficList = pll.handleTcpDump(dump);\r
+ reporter.finished(trafficList);\r
+ saveReport();\r
+ }\r
+\r
+ \r
+}
\ No newline at end of file
\r
package org.collectionspace.services.IntegrationTests.xmlreplay;\r
\r
+import org.apache.commons.httpclient.Header;\r
+\r
import java.util.ArrayList;\r
+import java.util.HashMap;\r
import java.util.List;\r
+import java.util.Map;\r
\r
/**\r
* User: laramie\r
public String error = "";\r
public String fromTestID = "";\r
public String auth = "";\r
+ public String boundary = "";\r
+ public String payloadStrictness = "";\r
+ public long contentLength = 0;\r
+ public String failureReason = "";\r
+ public Header[] responseHeaders = new Header[0];\r
public List<Integer> expectedCodes = new ArrayList<Integer>();\r
+ private Map<String, TreeWalkResults> partSummaries = new HashMap<String, TreeWalkResults>();\r
+ public void addPartSummary(String label, TreeWalkResults list){\r
+ partSummaries.put(label, list);\r
+ }\r
+ public String partsSummary(boolean detailed){\r
+ StringBuffer buf = new StringBuffer();\r
+ if (!isDomWalkOK()){\r
+ if (detailed) buf.append("\r\nDOM CHECK FAILED:\r\n");\r
+ else buf.append("; DOM CHECK FAILED:");\r
+ }\r
+ for (Map.Entry<String,TreeWalkResults> entry : partSummaries.entrySet()) {\r
+ String key = entry.getKey();\r
+ TreeWalkResults value = entry.getValue();\r
+ buf.append(" label:"+key+": ");\r
+ if (detailed){\r
+ buf.append("\r\n");\r
+ buf.append(value.fullSummary());\r
+ } else {\r
+ buf.append(value.miniSummary());\r
+ }\r
+\r
+ }\r
+ return buf.toString();\r
+ }\r
public boolean codeInSuccessRange(int code){\r
if (0<=code && code<200){\r
return false;\r
}\r
return true;\r
}\r
+\r
+ public boolean isDomWalkOK(){\r
+ if (Tools.isEmpty(payloadStrictness)){\r
+ return true;\r
+ }\r
+ PAYLOAD_STRICTNESS strictness = PAYLOAD_STRICTNESS.valueOf(payloadStrictness);\r
+ for (Map.Entry<String,TreeWalkResults> entry : partSummaries.entrySet()) {\r
+ String key = entry.getKey();\r
+ TreeWalkResults value = entry.getValue();\r
+ if (value.hasDocErrors()){\r
+ failureReason = " : DOM DOC_ERROR; ";\r
+ return false;\r
+ }\r
+ switch (strictness){\r
+ case STRICT:\r
+ if (!value.isStrictMatch()) {\r
+ failureReason = " : DOM NOT STRICT; ";\r
+ return false;\r
+ }\r
+ break;\r
+ case ADDOK:\r
+ if (value.countFor(TreeWalkResults.TreeWalkEntry.STATUS.TEXT_DIFFERENT)>0) {\r
+ failureReason = " : DOM TEXT_DIFFERENT; ";\r
+ return false;\r
+ }\r
+ if (value.countFor(TreeWalkResults.TreeWalkEntry.STATUS.R_MISSING)>0){\r
+ failureReason = " : DOM R_MISSING; ";\r
+ return false;\r
+ }\r
+ break;\r
+ case TEXT:\r
+ if (value.countFor(TreeWalkResults.TreeWalkEntry.STATUS.TEXT_DIFFERENT)>0) {\r
+ failureReason = " : DOM TEXT_DIFFERENT; ";\r
+ return false;\r
+ }\r
+ break;\r
+ case TREE:\r
+ if (!value.treesMatch()) {\r
+ failureReason = " : DOM TREE MISMATCH; ";\r
+ return false;\r
+ }\r
+ break;\r
+ case ZERO:\r
+ break;\r
+ }\r
+ }\r
+ return true;\r
+ }\r
+\r
public boolean gotExpectedResult(){\r
+ if (Tools.notEmpty(failureReason)){\r
+ return false;\r
+ }\r
for (Integer oneExpected : expectedCodes){\r
if (responseCode == oneExpected){\r
- return true;\r
+ return isDomWalkOK();\r
}\r
}\r
- if (expectedCodes.size()>0 && codeInSuccessRange(responseCode)){ //none found, but result expected.\r
+ if ( expectedCodes.size()>0 && codeInSuccessRange(responseCode)){ //none found, but result expected.\r
for (Integer oneExpected : expectedCodes){\r
if ( ! codeInSuccessRange(oneExpected)){\r
- return false;\r
+ return isDomWalkOK();\r
}\r
}\r
}\r
- return codeInSuccessRange(responseCode);\r
+ boolean ok = codeInSuccessRange(responseCode);\r
+ if (ok) {\r
+ return isDomWalkOK();\r
+ }\r
+ failureReason = " : STATUS CODE UNEXPECTED; ";\r
+ return false;\r
}\r
+\r
//public static final String[] DUMP_OPTIONS = {"minimal", "detailed", "full"};\r
public static enum DUMP_OPTIONS {minimal, detailed, full};\r
\r
+ public static enum PAYLOAD_STRICTNESS {ZERO, ADDOK, TREE, TEXT, STRICT};\r
+\r
public String toString(){\r
return detail(true);\r
\r
}\r
public String detail(boolean includePayloads){\r
- return "{ServiceResult: "\r
- + ( Tools.notEmpty(testID) ? " testID:"+testID : "" )\r
- + ( Tools.notEmpty(testGroupID) ? "; testGroupID:"+testGroupID : "" )\r
- + ( Tools.notEmpty(fromTestID) ? "; fromTestID:"+fromTestID : "" )\r
+ return "{"\r
+ + ( gotExpectedResult() ? "SUCCESS" : "FAILURE" )\r
+ + failureReason\r
+"; "+method\r
+"; "+responseCode\r
+ + ( (expectedCodes.size()>0) ? "; expectedCodes:"+expectedCodes : "" )\r
+ + ( Tools.notEmpty(testID) ? "; testID:"+testID : "" )\r
+ + ( Tools.notEmpty(testGroupID) ? "; testGroupID:"+testGroupID : "" )\r
+ + ( Tools.notEmpty(fromTestID) ? "; fromTestID:"+fromTestID : "" )\r
+ ( Tools.notEmpty(responseMessage) ? "; msg:"+responseMessage : "" )\r
+"; URL:"+fullURL\r
+"; auth: "+auth\r
+ ( Tools.notEmpty(deleteURL) ? "; deleteURL:"+deleteURL : "" )\r
+ ( Tools.notEmpty(location) ? "; location.CSID:"+location : "" )\r
+ ( Tools.notEmpty(error) ? "; ERROR:"+error : "" )\r
- + ( (expectedCodes.size()>0) ? "; expectedCodes:"+expectedCodes : "" )\r
+ "; gotExpected:"+gotExpectedResult()\r
+ ( includePayloads && Tools.notEmpty(result) ? "; result:"+result : "" )\r
+ + ( partsSummary(true))\r
+"}";\r
}\r
public String minimal(){\r
return "{"\r
+ ( gotExpectedResult() ? "SUCCESS" : "FAILURE" )\r
-\r
+ + failureReason\r
+ ( Tools.notEmpty(testID) ? "; "+testID : "" )\r
+"; "+method\r
+"; "+responseCode\r
+"; URL:"+fullURL\r
+"; auth: "+auth\r
+ ( Tools.notEmpty(error) ? "; ERROR:"+error : "" )\r
+ + ( partsSummary(false))\r
+"}";\r
}\r
public String dump(ServiceResult.DUMP_OPTIONS opt){\r
--- /dev/null
+/**\r
+ * This document is a part of the source code and related artifacts\r
+ * for CollectionSpace, an open source collections management system\r
+ * for museums and related institutions:\r
+ *\r
+ * http://www.collectionspace.org\r
+ * http://wiki.collectionspace.org\r
+ *\r
+ * Copyright (c) 2009 Regents of the University of California\r
+ *\r
+ * Licensed under the Educational Community License (ECL), Version 2.0.\r
+ * You may not use this file except in compliance with this License.\r
+ *\r
+ * You may obtain a copy of the ECL 2.0 License at\r
+ * https://source.collectionspace.org/collection-space/LICENSE.txt\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+package org.collectionspace.services.IntegrationTests.xmlreplay;\r
+\r
+import java.util.ArrayList;\r
+\r
+/**\r
+ * User: laramie\r
+ * $LastChangedRevision: $\r
+ * $LastChangedDate: $\r
+ */\r
+public class TreeWalkResults extends ArrayList<TreeWalkResults.TreeWalkEntry> {\r
+\r
+ public static class TreeWalkEntry {\r
+ public String lpath = "";\r
+ public String rpath = "";\r
+ public String ltextTrimmed = "";\r
+ public String rtextTrimmed = "";\r
+ public String message = "";\r
+ public String errmessage = "";\r
+ public static enum STATUS {INFO, MATCHED, R_MISSING, R_ADDED, DOC_ERROR, TEXT_DIFFERENT};\r
+ public STATUS status;\r
+ public String toString(){\r
+ return\r
+ "{"\r
+ +status.name()\r
+ +(Tools.notEmpty(lpath) ? ", L.path:"+lpath : "")\r
+ +(Tools.notEmpty(rpath) ? ", R.path:"+rpath : "")\r
+ +(Tools.notEmpty(message) ? ", message:"+message : "")\r
+ +((status != STATUS.MATCHED) && Tools.notEmpty(ltextTrimmed) ? ",\r\n L.trimmed:"+ltextTrimmed : "")\r
+ +((status != STATUS.MATCHED) && Tools.notEmpty(rtextTrimmed) ? ",\r\n R.trimmed:"+rtextTrimmed : "")\r
+ +"}";\r
+\r
+ }\r
+ }\r
+\r
+ public boolean hasDocErrors(){\r
+ for (TreeWalkEntry entry : this){\r
+ if (entry.status == TreeWalkEntry.STATUS.DOC_ERROR){\r
+ return true;\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
+ public String getErrorMessages(){\r
+ StringBuffer buf = new StringBuffer();\r
+ boolean first = true;\r
+ for (TreeWalkEntry entry : this){\r
+ if ( Tools.notEmpty(entry.errmessage)){\r
+ if (first) {\r
+ buf.append(",errors:");\r
+ } else {\r
+ buf.append(',');\r
+ }\r
+ buf.append('\''+entry.errmessage+"\'");\r
+ first = false;\r
+ }\r
+ }\r
+ return buf.toString();\r
+ }\r
+\r
+\r
+\r
+ public boolean isStrictMatch(){\r
+ for (TreeWalkEntry entry : this){\r
+ if (entry.status == TreeWalkEntry.STATUS.DOC_ERROR){\r
+ return false;\r
+ }\r
+ if ( !( entry.status == TreeWalkEntry.STATUS.MATCHED\r
+ || entry.status == TreeWalkEntry.STATUS.INFO)){\r
+ return false;\r
+ }\r
+ }\r
+ return true;\r
+ }\r
+ public int getMismatchCount(){\r
+ int c = 0;\r
+ for (TreeWalkEntry entry : this){\r
+ if ( entry.status == TreeWalkEntry.STATUS.DOC_ERROR\r
+ || entry.status != TreeWalkEntry.STATUS.MATCHED\r
+ || entry.status != TreeWalkEntry.STATUS.INFO){\r
+ c++;\r
+ }\r
+ }\r
+ return c;\r
+ }\r
+ /** For our purposes, trees match if they have the same element tree structure - no checking is done for text node changes. */\r
+ public boolean treesMatch(){\r
+ for (TreeWalkEntry entry : this){\r
+ if (entry.status == TreeWalkEntry.STATUS.DOC_ERROR\r
+ || entry.status == TreeWalkEntry.STATUS.R_MISSING\r
+ || entry.status == TreeWalkEntry.STATUS.R_ADDED ){\r
+ return false;\r
+ }\r
+ }\r
+ return true;\r
+ }\r
+\r
+ public int countFor(TreeWalkEntry.STATUS status){\r
+ int count = 0;\r
+ for (TreeWalkEntry entry : this){\r
+ if (entry.status.equals(status)){\r
+ count++;\r
+ }\r
+ }\r
+ return count;\r
+ }\r
+\r
+ public String miniSummary(){\r
+ //MATCHED, INFO, R_MISSING, R_ADDED, TEXT_DIFFERENT};\r
+ StringBuffer buf = new StringBuffer();\r
+ buf.append("{");\r
+ boolean nextline = false;\r
+ for (TreeWalkEntry.STATUS st : TreeWalkEntry.STATUS.values()){\r
+ if (nextline) buf.append(',');\r
+ buf.append(st.name()+':'+countFor(st));\r
+ nextline = true;\r
+ }\r
+ buf.append(getErrorMessages());\r
+ buf.append("}");\r
+ return buf.toString();\r
+ }\r
+\r
+ public String fullSummary(){\r
+ StringBuffer buf = new StringBuffer();\r
+ for (TreeWalkResults.TreeWalkEntry entry : this){\r
+ buf.append(entry.toString()).append("\r\n");\r
+ }\r
+ return buf.toString();\r
+ }\r
+\r
+\r
+ public String leftID;\r
+ public String rightID;\r
+}
\ No newline at end of file
--- /dev/null
+/**\r
+ * This document is a part of the source code and related artifacts\r
+ * for CollectionSpace, an open source collections management system\r
+ * for museums and related institutions:\r
+ *\r
+ * http://www.collectionspace.org\r
+ * http://wiki.collectionspace.org\r
+ *\r
+ * Copyright (c) 2009 Regents of the University of California\r
+ *\r
+ * Licensed under the Educational Community License (ECL), Version 2.0.\r
+ * You may not use this file except in compliance with this License.\r
+ *\r
+ * You may obtain a copy of the ECL 2.0 License at\r
+ * https://source.collectionspace.org/collection-space/LICENSE.txt\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.collectionspace.services.IntegrationTests.xmlreplay;\r
+\r
+import org.jdom.Document;\r
+import org.jdom.Element;\r
+import org.jdom.JDOMException;\r
+import org.jdom.input.SAXBuilder;\r
+import org.jaxen.XPath;\r
+import org.jaxen.jdom.JDOMXPath;\r
+\r
+\r
+import java.io.IOException;\r
+import java.io.StringReader;\r
+import java.util.ArrayList;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+\r
+import org.collectionspace.services.IntegrationTests.xmlreplay.TreeWalkResults.TreeWalkEntry;\r
+\r
+/**\r
+ * User: laramie\r
+ * $LastChangedRevision: $\r
+ * $LastChangedDate: $\r
+ */\r
+public class XmlCompareJdom {\r
+\r
+private static final String DEFAULT_SAX_DRIVER_CLASS = "org.apache.xerces.parsers.SAXParser";\r
+\r
+ public static org.jdom.Document getDocumentFromContent(String source) throws IOException, JDOMException {\r
+ org.jdom.Document doc;\r
+ SAXBuilder builder;\r
+ builder = new SAXBuilder();\r
+ builder.setValidation(false); //has no effect, I think.\r
+ doc = builder.build(new StringReader(source));\r
+ return doc;\r
+ }\r
+\r
+ public static TreeWalkResults compareParts(String expectedContent, String leftID, String actualPartContent, String rightID){\r
+ TreeWalkResults list = new TreeWalkResults();\r
+ try {\r
+\r
+ list.leftID = leftID;\r
+ list.rightID = rightID;\r
+ TreeWalkResults.TreeWalkEntry infoentry = new TreeWalkResults.TreeWalkEntry();\r
+ infoentry.status = TreeWalkResults.TreeWalkEntry.STATUS.INFO;\r
+ infoentry.message = "\r\n LEFT file: "+leftID+"\r\n RIGHT file: "+rightID;\r
+ list.add(infoentry);\r
+ if (Tools.isEmpty(expectedContent)){\r
+ TreeWalkEntry entry = new TreeWalkEntry();\r
+ entry.status = TreeWalkEntry.STATUS.DOC_ERROR;\r
+ entry.errmessage = "L dom was empty.";\r
+ list.add(entry);\r
+ } else if (Tools.isEmpty(actualPartContent)){\r
+ TreeWalkEntry entry = new TreeWalkEntry();\r
+ entry.errmessage = "R dom was empty.";\r
+ entry.status = TreeWalkEntry.STATUS.DOC_ERROR;\r
+ list.add(entry);\r
+ } else {\r
+ Document expected = getDocumentFromContent(expectedContent);\r
+ Document actual = getDocumentFromContent(actualPartContent);\r
+ treeWalk(expected, actual, list);\r
+ }\r
+ } catch (Throwable t){\r
+ String msg = "ERROR in XmlReplay.compareParts(): "+t;\r
+ System.out.println(msg);\r
+ TreeWalkEntry entry = new TreeWalkEntry();\r
+ entry.status = TreeWalkEntry.STATUS.DOC_ERROR;\r
+ entry.errmessage = msg;\r
+ list.add(entry);\r
+ }\r
+ return list;\r
+ }\r
+\r
+ public static List select(Element element, String xpathExpression) throws Exception {\r
+ XPath xpath = new JDOMXPath(xpathExpression);\r
+ return xpath.selectNodes(element);\r
+ }\r
+\r
+ public static Object selectSingleNode(Element element, String xpathExpression) throws Exception {\r
+ XPath xpath = new JDOMXPath(xpathExpression);\r
+ return xpath.selectSingleNode(element);\r
+ }\r
+\r
+\r
+\r
+\r
+ public static boolean treeWalk(Document left, Document right, TreeWalkResults list) throws Exception {\r
+ boolean res = treeWalk(left.getRootElement(), right.getRootElement(), "/", list);\r
+ return res;\r
+ }\r
+\r
+ public static boolean treeWalk(Element left, Element right, String parentPath, TreeWalkResults msgList) throws Exception {\r
+ String SPACE = " ";\r
+ if (left == null && right == null){\r
+ return true;\r
+ }\r
+ if (left == null){\r
+ return false;\r
+ }\r
+ if (right == null){\r
+ return false;\r
+ }\r
+ List l = left.getChildren();\r
+ Map foundRightMap = new HashMap();\r
+ boolean result = true;\r
+ for (Object o : l) {\r
+ if (!(o instanceof Element)){\r
+ continue;\r
+ }\r
+ Element leftChild = (Element)o;\r
+ String leftChildName = leftChild.getName();\r
+ if (Tools.isEmpty(leftChildName)){\r
+ continue;\r
+ }\r
+ String leftChildPath = Tools.glue(parentPath, "/", leftChildName);\r
+ Element rightChild = (Element)selectSingleNode(right,leftChildName);\r
+ if (rightChild == null){\r
+ TreeWalkEntry entry = new TreeWalkEntry();\r
+ entry.lpath = leftChildPath;\r
+ entry.status = TreeWalkEntry.STATUS.R_MISSING;\r
+ msgList.add(entry);\r
+ continue;\r
+ }\r
+ foundRightMap.put(leftChildName, "OK");\r
+ String leftChildTextTrim = leftChild.getText().trim();\r
+ String rightChildTextTrim = rightChild.getText().trim();\r
+ TreeWalkEntry entry = new TreeWalkEntry();\r
+ entry.ltextTrimmed = leftChildTextTrim;\r
+ entry.rtextTrimmed = rightChildTextTrim;\r
+ entry.lpath = leftChildPath;\r
+ entry.rpath = leftChildPath; //same\r
+\r
+ if (leftChildTextTrim.equals(rightChildTextTrim)){\r
+ entry.status = TreeWalkEntry.STATUS.MATCHED;\r
+ msgList.add(entry);\r
+ } else {\r
+ entry.status = TreeWalkEntry.STATUS.TEXT_DIFFERENT;\r
+ msgList.add(entry);\r
+ }\r
+\r
+ //============ DIVE !! =====================================================\r
+ result = result && treeWalk( leftChild, rightChild, leftChildPath, msgList);\r
+ }\r
+ for (Object r : right.getChildren()){\r
+ if (!(r instanceof Element)){\r
+ continue;\r
+ }\r
+ Element rightChild = (Element)r;\r
+ String rname = rightChild.getName();\r
+ if (null==foundRightMap.get(rname)){\r
+ String rightChildPath = Tools.glue(parentPath, "/", rname);\r
+\r
+ TreeWalkEntry entry = new TreeWalkEntry();\r
+ entry.rpath = rightChildPath;\r
+ entry.status = TreeWalkEntry.STATUS.R_ADDED;\r
+ msgList.add(entry);\r
+ }\r
+ }\r
+ return true;\r
+ }\r
+ \r
+}\r
\r
import org.apache.commons.cli.*;\r
\r
+import org.apache.commons.io.FileUtils;\r
import org.apache.commons.jexl2.JexlContext;\r
import org.apache.commons.jexl2.JexlEngine;\r
import org.apache.commons.jexl2.MapContext;\r
-import org.dom4j.Document;\r
-import org.dom4j.DocumentException;\r
-import org.dom4j.Node;\r
+import org.dom4j.*;\r
import org.dom4j.io.SAXReader;\r
+import org.jdom.input.SAXBuilder;\r
+import org.xml.sax.InputSource;\r
+\r
+\r
+import org.jdom.Document;\r
+import org.jdom.Element;\r
+import org.jdom.JDOMException;\r
\r
import java.io.*;\r
import java.util.*;\r
\r
// ============== METHODS ===========================================================\r
\r
- public Document openMasterConfigFile(String masterFilename) throws FileNotFoundException {\r
- Document document = getDocument(Tools.glue(basedir, "/", masterFilename)); //will check full path first, then checks relative to PWD.\r
+ public org.dom4j.Document openMasterConfigFile(String masterFilename) throws FileNotFoundException {\r
+ org.dom4j.Document document = getDocument(Tools.glue(basedir, "/", masterFilename)); //will check full path first, then checks relative to PWD.\r
if (document == null){\r
throw new FileNotFoundException("XmlReplay master control file ("+masterFilename+") not found in basedir: "+basedir+". Exiting test.");\r
}\r
/** specify the master config file, relative to getBaseDir(), but ignore any tests or testGroups in the master.\r
* @return a Document object, which you don't need to use: all options will be stored in XmlReplay instance.\r
*/\r
- public Document readOptionsFromMasterConfigFile(String masterFilename) throws FileNotFoundException {\r
- Document document = openMasterConfigFile(masterFilename);\r
+ public org.dom4j.Document readOptionsFromMasterConfigFile(String masterFilename) throws FileNotFoundException {\r
+ org.dom4j.Document document = openMasterConfigFile(masterFilename);\r
protoHostPort = document.selectSingleNode("/xmlReplayMaster/protoHostPort").getText().trim();\r
AuthsMap authsMap = readAuths(document);\r
setDefaultAuthsMap(authsMap);\r
* and setting defaults from this instance, but not sharing ServiceResult objects or maps. */\r
public List<List<ServiceResult>> runMaster(String masterFilename, boolean readOptionsFromMaster) throws Exception {\r
List<List<ServiceResult>> list = new ArrayList<List<ServiceResult>>();\r
- Document document;\r
+ org.dom4j.Document document;\r
if (readOptionsFromMaster){\r
document = readOptionsFromMasterConfigFile(masterFilename);\r
} else {\r
}\r
}\r
\r
- public static AuthsMap readAuths(Document document){\r
+ public static AuthsMap readAuths(org.dom4j.Document document){\r
Map<String, String> map = new HashMap<String, String>();\r
List<Node> authNodes = document.selectNodes("//auths/auth");\r
for (Node auth : authNodes) {\r
return new Dump();\r
}\r
\r
- public static Dump readDumpOptions(Document document){\r
+ public static Dump readDumpOptions(org.dom4j.Document document){\r
Dump dump = getDumpConfig();\r
Node dumpNode = document.selectSingleNode("//dump");\r
if (dumpNode != null){\r
} else {\r
result.bDoingSinglePartPayload = false;\r
List<Node> parts = testNode.selectNodes("parts/part");\r
- if (parts == null || parts.size()==0){ //path is just /testGroup/test/part/\r
+ if (parts == null || parts.size()==0){\r
+ //path is just /testGroup/test/part/\r
String commonPartName = testNode.valueOf("part/label");\r
String testfile = testNode.valueOf("part/filename");\r
String fullTestFilename = xmlReplayBaseDir + '/' + testfile;\r
}\r
result.partsList.add(commonPartName);\r
result.filesList.add(fullTestFilename);\r
- } else { // path is /testGroup/test/parts/part/\r
+ } else {\r
+ // path is /testGroup/test/parts/part/\r
for (Node part : parts){\r
String commonPartName = part.valueOf("label");\r
String filename = part.valueOf("filename");\r
return result;\r
}\r
\r
-\r
public static org.dom4j.Document getDocument(String xmlFileName) {\r
- Document document = null;\r
+ org.dom4j.Document document = null;\r
SAXReader reader = new SAXReader();\r
try {\r
document = reader.read(xmlFileName);\r
return document;\r
}\r
\r
+ protected static String validateResponse(ServiceResult serviceResult,\r
+ Map<String, ServiceResult> serviceResultsMap,\r
+ PartsStruct expectedResponseParts){\r
+ String OK = "";\r
+ if (expectedResponseParts == null) return OK;\r
+ if (serviceResult == null) return OK;\r
+ if (serviceResult.result.length() == 0) return OK;\r
+ String responseDump = serviceResult.result;\r
+ //System.out.println("responseDump: "+responseDump);\r
+ PayloadLogger.HttpTraffic traffic = PayloadLogger.readPayloads(responseDump, serviceResult.boundary, serviceResult.contentLength);\r
+ try {\r
+ for (int i=0; i<expectedResponseParts.partsList.size(); i++){\r
+ String fileName = expectedResponseParts.filesList.get(i);\r
+ String label = expectedResponseParts.partsList.get(i);\r
+ byte[] b = FileUtils.readFileToByteArray(new File(fileName));\r
+ String expectedPartContent = new String(b);\r
+ System.out.println("expected: "+label+ " content ==>\r\n"+expectedPartContent);\r
+ PayloadLogger.Part partFromServer = traffic.getPart(label);\r
+ String partFromServerContent = "";\r
+ if (partFromServer != null){\r
+ partFromServerContent = partFromServer.getContent();\r
+ } else {\r
+ partFromServerContent = "";\r
+ }\r
+ //if (partFromServer!=null) {\r
+ //System.out.println("====part content from server. label-->"+label+"<-- \r\npart-->"+partFromServerContent+"<--");\r
+\r
+ String leftID = "{from expected part, label:"+label+" filename: "+fileName+"}";\r
+ String rightID = "{from server, label:"+label\r
+ //+" testGroupID: "+serviceResult.testGroupID\r
+ +" fromTestID: "+serviceResult.fromTestID\r
+ +" URL: "+serviceResult.fullURL\r
+ +"}";\r
+ TreeWalkResults list =\r
+ XmlCompareJdom.compareParts(expectedPartContent,\r
+ leftID,\r
+ partFromServerContent,\r
+ rightID);\r
+ //if (list.getMismatchCount()>0){\r
+ serviceResult.addPartSummary(label, list);\r
+ //}\r
+ //}\r
+ }\r
+ } catch (Exception e){\r
+ String err = "ERROR in XmlReplay.validateResponse() : "+e; \r
+ //System.out.println(err);\r
+ return err ;\r
+ }\r
+ return OK;\r
+ }\r
+\r
\r
//================= runXmlReplayFile ======================================================\r
\r
List<ServiceResult> results = new ArrayList<ServiceResult>();\r
\r
String controlFile = Tools.glue(xmlReplayBaseDir, "/", controlFileName);\r
- Document document;\r
+ org.dom4j.Document document;\r
document = getDocument(controlFile); //will check full path first, then checks relative to PWD.\r
if (document==null){\r
throw new FileNotFoundException("XmlReplay control file ("+controlFileName+") not found in basedir: "+xmlReplayBaseDir+" Exiting test.");\r
}\r
}\r
\r
+ Node responseNode = testNode.selectSingleNode("response");\r
+ PartsStruct expectedResponseParts = null;\r
+ if (responseNode!=null){\r
+ expectedResponseParts = PartsStruct.readParts(responseNode, testID, xmlReplayBaseDir);\r
+ //System.out.println("reponse parts: >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"+expectedResponseParts);\r
+ }\r
+\r
ServiceResult serviceResult;\r
boolean isPOST = method.equalsIgnoreCase("POST");\r
boolean isPUT = method.equalsIgnoreCase("PUT");\r
if (pr!=null){\r
serviceResult = XmlReplayTransport.doDELETE(pr.deleteURL, authForTest, testIDLabel, fromTestID);\r
serviceResult.fromTestID = fromTestID;\r
+ if (expectedCodes.size()>0){\r
+ serviceResult.expectedCodes = expectedCodes;\r
+ }\r
results.add(serviceResult);\r
- if (serviceResult.gotExpectedResult()){\r
+ if (serviceResult.gotExpectedResult()){ //gotExpectedResult depends on serviceResult.expectedCodes.\r
serviceResultsMap.remove(fromTestID);\r
}\r
} else {\r
if (Tools.isEmpty(serviceResult.testID)) serviceResult.testID = testIDLabel;\r
if (Tools.isEmpty(serviceResult.testGroupID)) serviceResult.testGroupID = testGroupID;\r
\r
+ Node expectedLevel = testNode.selectSingleNode("response/expected");\r
+ if (expectedLevel!=null){\r
+ String level = expectedLevel.valueOf("@level");\r
+ serviceResult.payloadStrictness = level;\r
+ }\r
+\r
+ String vError = validateResponse(serviceResult, serviceResultsMap, expectedResponseParts);\r
+ if (Tools.notEmpty(vError)){\r
+ serviceResult.error = vError;\r
+ serviceResult.failureReason = " : VALIDATION ERROR; ";\r
+ }\r
+\r
String serviceResultRow = serviceResult.dump(dump.dumpServiceResult);\r
String leader = (dump.dumpServiceResult == ServiceResult.DUMP_OPTIONS.detailed) ? "XmlReplay:"+testIDLabel+": ": "";\r
System.out.println(leader+serviceResultRow+"\r\n");\r
buff.append(ROWSTARTRED+serviceResult.minimal()+ROWENDRED);\r
}\r
}\r
-\r
}\r
buff.append(TBLEND);\r
summary.table = buff.toString();\r
\r
package org.collectionspace.services.IntegrationTests.xmlreplay;\r
\r
+import org.apache.commons.httpclient.Header;\r
import org.apache.commons.httpclient.HttpClient;\r
import org.apache.commons.httpclient.methods.DeleteMethod;\r
import org.apache.commons.httpclient.methods.GetMethod;\r
import java.io.OutputStreamWriter;\r
import java.net.HttpURLConnection;\r
import java.net.URL;\r
+import java.util.Arrays;\r
import java.util.List;\r
import java.util.Map;\r
\r
\r
int statusCode1 = client.executeMethod(getMethod);\r
pr.responseCode = statusCode1;\r
+ pr.fromTestID = fromTestID;\r
pr.method = "GET";\r
try {\r
pr.result = getMethod.getResponseBodyAsString();\r
pr.responseMessage = getMethod.getStatusText();\r
+ Header[] headers = getMethod.getResponseHeaders();\r
+ pr.responseHeaders = Arrays.copyOf(headers, headers.length);\r
+ Header hdr = getMethod.getResponseHeader("CONTENT-TYPE");\r
+ if (hdr!=null){\r
+ String hdrStr = hdr.toExternalForm();\r
+ pr.boundary = PayloadLogger.parseBoundary(hdrStr);\r
+ }\r
+ pr.contentLength = getMethod.getResponseContentLength();\r
} catch (Throwable t){\r
//System.err.println("ERROR getting content from response: "+t);\r
pr.error = t.toString();\r
ServiceResult pr = new ServiceResult();\r
pr.method = "DELETE";\r
pr.fullURL = urlString;\r
+ pr.fromTestID = fromTestID;\r
if (Tools.isEmpty(urlString)){\r
pr.error = "url was empty. Check the result for fromTestID: "+fromTestID+". currentTest: "+testID;\r
return pr;\r
}\r
String msg = sb.toString();\r
result.result = msg;\r
+ result.boundary = PayloadLogger.parseBoundary(conn.getHeaderField("CONTENT-TYPE"));\r
+\r
rd.close();\r
} catch (Throwable t){\r
//System.err.println("ERROR getting content from response: "+t);\r
--- /dev/null
+/**\r
+ * This document is a part of the source code and related artifacts\r
+ * for CollectionSpace, an open source collections management system\r
+ * for museums and related institutions:\r
+ *\r
+ * http://www.collectionspace.org\r
+ * http://wiki.collectionspace.org\r
+ *\r
+ * Copyright (c) 2009 Regents of the University of California\r
+ *\r
+ * Licensed under the Educational Community License (ECL), Version 2.0.\r
+ * You may not use this file except in compliance with this License.\r
+ *\r
+ * You may obtain a copy of the ECL 2.0 License at\r
+ * https://source.collectionspace.org/collection-space/LICENSE.txt\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package org.collectionspace.services.IntegrationTests.test;\r
+\r
+import org.collectionspace.services.IntegrationTests.xmlreplay.TreeWalkResults;\r
+import org.collectionspace.services.IntegrationTests.xmlreplay.XmlCompareJdom;\r
+import org.testng.Assert;\r
+import org.testng.annotations.Test;\r
+\r
+/**\r
+ * User: laramie\r
+ * $LastChangedRevision: $\r
+ * $LastChangedDate: $\r
+ */\r
+public class XmlCompareJdomTest {\r
+\r
+\r
+ private void testBanner(String msg){\r
+ String BANNER ="-------------------------------------------------------";\r
+ System.out.println(BANNER+"\r\n"+this.getClass().getName()+"\r\n"+msg+"\r\n"+BANNER);\r
+ }\r
+ private void printTreeWalkResults(TreeWalkResults list){\r
+ for (TreeWalkResults.TreeWalkEntry entry : list){\r
+ System.out.println(entry.toString());\r
+ }\r
+ }\r
+\r
+ private void assertTreeWalkResults(TreeWalkResults results,\r
+ int addedRight,\r
+ int missingRight,\r
+ int textMismatches,\r
+ boolean strictMatch,\r
+ boolean treesMatch){\r
+ System.out.println("assertTreeWalkResults: ");\r
+\r
+ int addedr = results.countFor(TreeWalkResults.TreeWalkEntry.STATUS.R_ADDED);\r
+ int missingr = results.countFor(TreeWalkResults.TreeWalkEntry.STATUS.R_MISSING);\r
+ int tdiff = results.countFor(TreeWalkResults.TreeWalkEntry.STATUS.TEXT_DIFFERENT);\r
+ int badCount = results.getMismatchCount();\r
+ boolean strict = results.isStrictMatch();\r
+ boolean treeOK = results.treesMatch();\r
+\r
+ String expected = " expected: addedRight:"+addedRight+",missingRight:"+missingRight+",textMismatches:"+textMismatches\r
+ +",strictMatch:"+strictMatch+",treesMatch:"+treesMatch;\r
+\r
+ String actual = " actual: addedRight:"+addedr+",missingRight:"+missingr+",textMismatches:"+tdiff\r
+ +",strictMatch:"+strict+",treesMatch:"+treeOK;\r
+\r
+ String exp_act = expected +"\r\n"+actual+"\r\n";\r
+ System.out.print(exp_act);\r
+\r
+ printTreeWalkResults(results);\r
+\r
+\r
+ boolean done = false;\r
+ try {\r
+ Assert.assertEquals(addedr, addedRight, "assertTreeWalkResults:R_ADDED mismatch."+exp_act);\r
+\r
+ Assert.assertEquals(missingr, missingRight, "assertTreeWalkResults:R_MISSING mismatch."+exp_act);\r
+\r
+ Assert.assertEquals(tdiff, textMismatches, "assertTreeWalkResults:TEXT_DIFFERENT mismatch."+exp_act);\r
+\r
+\r
+ Assert.assertTrue((strict==strictMatch), "assertTreeWalkResults:strictMatch mismatch."+exp_act);\r
+\r
+ Assert.assertTrue((treeOK==treesMatch), "assertTreeWalkResults:treesMatch mismatch."+exp_act);\r
+\r
+ System.out.println("SUCCESS: assertTreeWalkResults done.\r\n");\r
+ done = true;\r
+ } finally {\r
+ if (!done) System.out.println("FAILURE: assertTreeWalkResults failed an assertion. See surefire report.\r\n");\r
+ }\r
+ }\r
+\r
+ @Test\r
+ public void testXmlCompareJdom(){\r
+ testBanner("testXmlCompareJdom");\r
+ TreeWalkResults results =\r
+ XmlCompareJdom.compareParts(expectedPartContent,\r
+ "expected",\r
+ partFromServer,\r
+ "from-server");\r
+ assertTreeWalkResults(results,0,0,0,true,true);\r
+ // addedRight,missingRight,textMismatches,strictMatch,treesMatch\r
+ }\r
+\r
+ @Test\r
+ public void testTextContentDifferent(){\r
+ testBanner("testTextContentDifferent");\r
+ TreeWalkResults results =\r
+ XmlCompareJdom.compareParts(expectedPartContent,\r
+ "expected",\r
+ srvHEAD+srvEN2+srvDEPOSITOR+srvFOOT,\r
+ "from-server");\r
+ assertTreeWalkResults(results,0,0,1,false,true);\r
+ // addedRight,missingRight,textMismatches,strictMatch,treesMatch\r
+ }\r
+\r
+\r
+ @Test\r
+ public void testAddedR(){\r
+ testBanner("testAddedR");\r
+ TreeWalkResults results =\r
+ XmlCompareJdom.compareParts(expectedPartContent,\r
+ "expected",\r
+ srvHEAD+srvEN+exNEWTREE+srvDEPOSITOR+exNEW+srvFOOT,\r
+ "from-server");\r
+ assertTreeWalkResults(results,2,0,0,false,false);\r
+ // addedRight,missingRight,textMismatches,strictMatch,treesMatch\r
+\r
+ }\r
+\r
+ @Test\r
+ public void testAddedL(){\r
+ testBanner("testAddedL");\r
+ TreeWalkResults results =\r
+ XmlCompareJdom.compareParts(exHEAD + exEN_WCH + exNEWTREE + exDEP + exNEW + exFOOT,\r
+ "expected",\r
+ partFromServer,\r
+ "from-server");\r
+ assertTreeWalkResults(results,0,3,0,false,false);\r
+ // addedRight,missingRight,textMismatches,strictMatch,treesMatch\r
+ }\r
+\r
+ @Test\r
+ public void testChildrenReordered(){\r
+ testBanner("testChildrenReordered");\r
+ TreeWalkResults results =\r
+ XmlCompareJdom.compareParts(exHEAD + exDEP + exEN + exFOOT,\r
+ "expected",\r
+ partFromServer,\r
+ "from-server");\r
+ assertTreeWalkResults(results,0,0,0,true,true);\r
+ // addedRight,missingRight,textMismatches,strictMatch,treesMatch\r
+ }\r
+\r
+\r
+ // ============ expected part, will be used as LEFT tree ==========================================================\r
+ private static String exHEAD ="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"\r
+ +"<ns2:objectexit_common \r\n"\r
+ +" xmlns:ns2=\"http://collectionspace.org/services/objectexit\" \r\n"\r
+ +" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" \r\n"\r
+ +" xsi:schemaLocation=\"http://collectionspace.org/services/objectexit http://services.collectionspace.org/objectexit/objectexit_common.xsd\">\r\n";\r
+ private static String exEN =" <exitNumber>objectexitNumber-1290026472360</exitNumber>\r\n";\r
+ private static String exEN_WCH =" <exitNumber>objectexitNumber-1290026472360\r\n"\r
+ +" <enChild>\r\n"\r
+ +" enChild content\r\n"\r
+ +" </enChild>\r\n"\r
+ +" </exitNumber>\r\n";\r
+ private static String exNEWTREE =" <first>\r\n"\r
+ +" <second>\r\n"\r
+ +" second content\r\n"\r
+ +" </second>\r\n"\r
+ +" </first>\r\n";\r
+ private static String exDEP =" <depositor>urn:cspace:org.collectionspace.demo:orgauthority:name(TestOrgAuth):organization:name(Northern Climes Museum)'Northern Climes Museum'</depositor>\r\n";\r
+ private static String exNEW =" <newField>objectexitNumber-1290026472360</newField>\r\n";\r
+ private static String exFOOT ="</ns2:objectexit_common>";\r
+\r
+ private static String expectedPartContent = exHEAD + exEN + exDEP + exFOOT;\r
+\r
+\r
+ // ============ from-server part, will be used as RIGHT tree ==========================================================\r
+\r
+ private static String srvHEAD = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"\r
+ +"<ns2:objectexit_common xmlns:ns2=\"http://collectionspace.org/services/objectexit\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://collectionspace.org/services/objectexit http://services.collectionspace.org/objectexit/objectexit_common.xsd\">\r\n";\r
+ private static String srvEN = "<exitNumber>objectexitNumber-1290026472360</exitNumber>\r\n";\r
+ private static String srvEN2 = "<exitNumber>objectexitNumber-9999999999999</exitNumber>\r\n";\r
+ private static String srvDEPOSITOR = "<depositor>urn:cspace:org.collectionspace.demo:orgauthority:name(TestOrgAuth):organization:name(Northern Climes Museum)'Northern Climes Museum'</depositor>\r\n";\r
+ private static String srvFOOT = "</ns2:objectexit_common>\r\n";\r
+\r
+ private static String partFromServer = srvHEAD+srvEN+srvDEPOSITOR+srvFOOT;\r
+\r
+\r
+\r
+}\r
\r
\r
<run controlFile="objectexit/object-exit.xml" testGroup="CRUDL" />\r
+ <run controlFile="objectexit/object-exit.xml" testGroup="domwalk" />\r
\r
\r
</xmlReplayMaster>\r
<auths>\r
<!-- IMPORTANT: THESE ARE STICKY :: THEY STICK AROUND UNTIL RESET, IN EXEC ORDER OF THIS FILE. -->\r
<auth ID="admin@collectionspace.org">YWRtaW5AY29sbGVjdGlvbnNwYWNlLm9yZzpBZG1pbmlzdHJhdG9y</auth>\r
+ <auth ID="testAdministator">YWRtaW5AY29sbGVjdGlvbnNwYWNlLm9yZzpBZG1pbmlzdHJhdG9y</auth>\r
</auths>\r
\r
+ \r
+ \r
+ <testGroup ID="domwalk" autoDeletePOSTS="true">\r
+ <test ID="oe1" auth="test">\r
+ <method>POST</method>\r
+ <uri>/cspace-services/objectexit/</uri>\r
+ <part>\r
+ <label>objectexit_common</label>\r
+ <filename>objectexit/oe1.xml</filename>\r
+ </part>\r
+ </test>\r
+ <test ID="oe2">\r
+ <method>GET</method>\r
+ <uri>/cspace-services/objectexit/${oe1.CSID}</uri>\r
+ <response>\r
+ <expected level="TEXT" />\r
+ <part>\r
+ <label>objectexit_common</label>\r
+ <filename>objectexit/res/oe2.res.xml</filename>\r
+ </part>\r
+ </response>\r
+ </test>\r
+ </testGroup>\r
+ \r
<!-- ================================================================================ -->\r
\r
<testGroup ID="CRUDL" autoDeletePOSTS="true">\r
<test ID="oe2">\r
<method>GET</method>\r
<uri>/cspace-services/objectexit/${oe1.CSID}</uri>\r
- <reponse>oe2.res.xml</reponse>\r
+ <response>\r
+ <part>\r
+ <label>objectexit_common</label>\r
+ <filename>objectexit/res/oe2.res.xml</filename>\r
+ </part>\r
+ </response>\r
</test>\r
<test ID="oe3">\r
<method>POST</method>\r
xmlns:ns2="http://collectionspace.org/services/objectexit"\r
xmlns:ns3="http://collectionspace.org/services/jaxb">\r
<depositor>urn:cspace:org.collectionspace.demo:orgauthority:name(TestOrgAuth):organization:name(Northern Climes Museum)'Northern Climes Museum'</depositor>\r
- <exitNumber>objectexitNumber-1290024274266</exitNumber>\r
+ <exitNumber>objectexitNumber-1290026472360</exitNumber>\r
</ns2:objectexit_common>\r
\r
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns2:objectexit_common_list xmlns:ns2="http://collectionspace.org/services/objectexit" xmlns:ns3="http://collectionspace.org/services/jaxb"><pageNum>0</pageNum><pageSize>40</pageSize><itemsInPage>4</itemsInPage><totalItems>4</totalItems><fieldsReturned>currentOwner|depositor|exitDate|exitMethod|exitNote|exitNumber|exitReason|packingNote|uri|csid</fieldsReturned><objectexit_list_item><exitNumber>objectexitNumber-1290026473391</exitNumber><uri>/objectexit/c5844272-0da5-4c1f-b361</uri><csid>c5844272-0da5-4c1f-b361</csid></objectexit_list_item><objectexit_list_item><exitNumber>objectexitNumber-1290026473626</exitNumber><uri>/objectexit/204b5375-5639-4dc3-8bbd</uri><csid>204b5375-5639-4dc3-8bbd</csid></objectexit_list_item><objectexit_list_item><exitNumber>objectexitNumber-1290026473860</exitNumber><uri>/objectexit/70499f68-7eba-4e63-a0ec</uri><csid>70499f68-7eba-4e63-a0ec</csid></objectexit_list_item><objectexit_list_item><exitNumber>objectexitNumber-1290026472360</exitNumber><uri>/objectexit/6f7a1e3e-5821-4ef2-bfcf</uri><csid>6f7a1e3e-5821-4ef2-bfcf</csid></objectexit_list_item></ns2:objectexit_common_list>\r
-\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<ns2:personauthorities_common xmlns:ns2="http://collectionspace.org/services/person" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://collectionspace.org/services/person http://services.collectionspace.org/person/personauthorities_common.xsd">\r
+<shortIdentifier>ObjectexitPersonAuth</shortIdentifier>\r
+<refName>urn:cspace:org.collectionspace.demo:personauthority:name(ObjectexitPersonAuth)'ObjectexitPersonAuth'</refName>\r
+<csid>${oe9.CSID}</csid>\r
+<displayName>ObjectexitPersonAuth</displayName>\r
+<vocabType>PersonAuthority</vocabType>\r
+</ns2:personauthorities_common>\r
+\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<ns2:persons_common xmlns:ns2="http://collectionspace.org/services/person" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://collectionspace.org/services/person http://services.collectionspace.org/person/persons_common.xsd">\r
+<surName>Owner</surName>\r
+<nationalities/>\r
+<shortIdentifier>owenCurOwner</shortIdentifier>\r
+<shortDisplayName>Owen the Cur Owner</shortDisplayName>\r
+<schoolsOrStyles/>\r
+<foreName>Owen the Cur</foreName>\r
+<groups/>\r
+<occupations/>\r
+<displayNameComputed>true</displayNameComputed>\r
+<csid>${oe11.CSID}</csid>\r
+<shortDisplayNameComputed>true</shortDisplayNameComputed>\r
+<inAuthority>${oe9.CSID}</inAuthority>\r
+<refName>urn:cspace:org.collectionspace.demo:personauthority:name(ObjectexitPersonAuth)'ObjectexitPersonAuth':person:name(owenCurOwner)</refName>\r
+<displayName>Owen the Cur Owner</displayName>\r
+</ns2:persons_common>\r
+\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<ns2:persons_common xmlns:ns2="http://collectionspace.org/services/person" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://collectionspace.org/services/person http://services.collectionspace.org/person/persons_common.xsd">\r
+<surName>Depositor</surName>\r
+<nationalities/>\r
+<shortIdentifier>davenportDepositor</shortIdentifier>\r
+<shortDisplayName>Davenport Depositor</shortDisplayName>\r
+<schoolsOrStyles/>\r
+<foreName>Davenport</foreName>\r
+<groups/>\r
+<occupations/>\r
+<displayNameComputed>true</displayNameComputed>\r
+<csid>${oe13.CSID}</csid>\r
+<shortDisplayNameComputed>true</shortDisplayNameComputed>\r
+<inAuthority>${oe9.CSID}</inAuthority>\r
+<refName>urn:cspace:org.collectionspace.demo:personauthority:name(ObjectexitPersonAuth)'ObjectexitPersonAuth':person:name(davenportDepositor)</refName>\r
+<displayName>Davenport Depositor</displayName>\r
+</ns2:persons_common>\r
+\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<ns2:objectexit_common xmlns:ns2="http://collectionspace.org/services/objectexit" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://collectionspace.org/services/objectexit http://services.collectionspace.org/objectexit/objectexit_common.xsd">\r
+<exitDate>exitDate-1290026474563</exitDate>\r
+<exitNumber>exitNumber-1290026474563</exitNumber>\r
+<depositor>urn:cspace:org.collectionspace.demo:personauthority:name(ObjectexitPersonAuth)'ObjectexitPersonAuth':person:name(davenportDepositor)</depositor>\r
+</ns2:objectexit_common>\r
+\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns3:authority-ref-list xmlns:ns2="http://collectionspace.org/services/jaxb" xmlns:ns3="http://collectionspace.org/services/common/authorityref"><pageNum>0</pageNum><pageSize>40</pageSize><itemsInPage>1</itemsInPage><totalItems>1</totalItems><authority-ref-item><sourceField>objectexit_common:depositor</sourceField><refName>urn:cspace:org.collectionspace.demo:personauthority:name(ObjectexitPersonAuth)'ObjectexitPersonAuth':person:name(davenportDepositor)</refName><authDisplayName>ObjectexitPersonAuth</authDisplayName><uri>/personauthorities/urn:cspace:name(ObjectexitPersonAuth)/items/urn:cspace:name(davenportDepositor)</uri></authority-ref-item></ns3:authority-ref-list>\r
+\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<ns2:objectexit_common \r
+ xmlns:ns2="http://collectionspace.org/services/objectexit" \r
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" \r
+ xsi:schemaLocation="http://collectionspace.org/services/objectexit http://services.collectionspace.org/objectexit/objectexit_common.xsd">\r
+<exitNumber>objectexitNumber-1290026472360\r
+<second>\r
+ second content\r
+ </second>\r
+</exitNumber>\r
+<first>\r
+ <second>\r
+ second content\r
+ </second>\r
+</first>\r
+\r
+<depositor>urn:cspace:org.collectionspace.demo:orgauthority:name(TestOrgAuth):organization:name(Northern Climes Museum)'Northern Climes Museum'</depositor>\r
+<newField>objectexitNumber-1290026472360</newField>\r
+</ns2:objectexit_common>\r
+\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<ns2:objectexit_common xmlns:ns2="http://collectionspace.org/services/objectexit" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://collectionspace.org/services/objectexit http://services.collectionspace.org/objectexit/objectexit_common.xsd">\r
+<exitNumber>updated-objectexitNumber-1290026472360</exitNumber>\r
+<depositor>urn:cspace:org.collectionspace.demo:orgauthority:name(TestOrgAuth):organization:name(Northern Climes Museum)'Northern Climes Museum'</depositor>\r
+</ns2:objectexit_common>\r
+\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns2:objectexit_common_list xmlns:ns2="http://collectionspace.org/services/objectexit" xmlns:ns3="http://collectionspace.org/services/jaxb"><pageNum>0</pageNum><pageSize>1</pageSize><itemsInPage>1</itemsInPage><totalItems>3</totalItems><fieldsReturned>currentOwner|depositor|exitDate|exitMethod|exitNote|exitNumber|exitReason|packingNote|uri|csid</fieldsReturned><objectexit_list_item><exitNumber>objectexitNumber-1290026473391</exitNumber><uri>/objectexit/${oe3.CSID}</uri><csid>${oe3.CSID}</csid></objectexit_list_item></ns2:objectexit_common_list>\r
+\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns2:objectexit_common_list xmlns:ns2="http://collectionspace.org/services/objectexit" xmlns:ns3="http://collectionspace.org/services/jaxb"><pageNum>0</pageNum><pageSize>1</pageSize><itemsInPage>1</itemsInPage><totalItems>3</totalItems><fieldsReturned>currentOwner|depositor|exitDate|exitMethod|exitNote|exitNumber|exitReason|packingNote|uri|csid</fieldsReturned><objectexit_list_item><exitNumber>objectexitNumber-1290026473391</exitNumber><uri>/objectexit/${oe3.CSID}</uri><csid>${oe3.CSID}</csid></objectexit_list_item></ns2:objectexit_common_list>\r
+\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns2:objectexit_common_list xmlns:ns2="http://collectionspace.org/services/objectexit" xmlns:ns3="http://collectionspace.org/services/jaxb"><pageNum>1</pageNum><pageSize>1</pageSize><itemsInPage>1</itemsInPage><totalItems>3</totalItems><fieldsReturned>currentOwner|depositor|exitDate|exitMethod|exitNote|exitNumber|exitReason|packingNote|uri|csid</fieldsReturned><objectexit_list_item><exitNumber>objectexitNumber-1290026473626</exitNumber><uri>/objectexit/${oe4.CSID}</uri><csid>${oe4.CSID}</csid></objectexit_list_item></ns2:objectexit_common_list>\r
+\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns2:objectexit_common_list xmlns:ns2="http://collectionspace.org/services/objectexit" xmlns:ns3="http://collectionspace.org/services/jaxb"><pageNum>2</pageNum><pageSize>1</pageSize><itemsInPage>1</itemsInPage><totalItems>3</totalItems><fieldsReturned>currentOwner|depositor|exitDate|exitMethod|exitNote|exitNumber|exitReason|packingNote|uri|csid</fieldsReturned><objectexit_list_item><exitNumber>objectexitNumber-1290026473860</exitNumber><uri>/objectexit/${oe5.CSID}</uri><csid>${oe5.CSID}</csid></objectexit_list_item></ns2:objectexit_common_list>\r
+\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\r
+<ns2:objectexit_common_list xmlns:ns2="http://collectionspace.org/services/objectexit" \r
+ xmlns:ns3="http://collectionspace.org/services/jaxb">\r
+ <pageNum>0</pageNum><pageSize>40</pageSize><itemsInPage>4</itemsInPage><totalItems>4</totalItems><fieldsReturned>currentOwner|depositor|exitDate|exitMethod|exitNote|exitNumber|exitReason|packingNote|uri|csid</fieldsReturned><objectexit_list_item><exitNumber>objectexitNumber-1290026473391</exitNumber><uri>/objectexit/${oe3.CSID}</uri><csid>${oe3.CSID}</csid></objectexit_list_item><objectexit_list_item><exitNumber>objectexitNumber-1290026473626</exitNumber><uri>/objectexit/${oe4.CSID}</uri><csid>${oe4.CSID}</csid></objectexit_list_item><objectexit_list_item><exitNumber>objectexitNumber-1290026473860</exitNumber><uri>/objectexit/${oe5.CSID}</uri><csid>${oe5.CSID}</csid></objectexit_list_item><objectexit_list_item><exitNumber>objectexitNumber-1290026472360</exitNumber><uri>/objectexit/${oe1.CSID}</uri><csid>${oe1.CSID}</csid></objectexit_list_item></ns2:objectexit_common_list>\r
+\r
</part>\r
</test>\r
<test ID="dimensionBigbird_PUT_AfterPermrolesDeleted">\r
- <expectedCodes>403, 404,405</expectedCodes><!-- Expected failure because dimensionBigbird_POST_AfterPermrolesDeleted failed.-->\r
+ <expectedCodes>403,404,405</expectedCodes><!-- Expected failure because dimensionBigbird_POST_AfterPermrolesDeleted failed.-->\r
<method>PUT</method>\r
<uri>/cspace-services/dimensions/${dimensionBigbird_POST_AfterPermrolesDeleted.CSID}</uri>\r
<part>\r
-->\r
\r
<run controlFile="./security.xml" testGroup="securityRemovingRoleperms" />\r
+ <run controlFile="objectexit/object-exit.xml" testGroup="CRUDL" />\r
+ <run controlFile="objectexit/object-exit.xml" testGroup="domwalk" />\r
\r
<!--<run controlFile="./organization.xml" testGroup="organization" />-->\r
\r