]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
8830fdd63c4e3cb7ad5ea4aec4660c28ccdd83c2
[tmp/jakarta-migration.git] /
1 package org.collectionspace.services.IntegrationTests.xmlreplay;\r
2 \r
3 import org.apache.commons.cli.*;\r
4 \r
5 import org.apache.commons.io.FileUtils;\r
6 import org.apache.commons.jexl2.JexlContext;\r
7 import org.apache.commons.jexl2.JexlEngine;\r
8 import org.apache.commons.jexl2.MapContext;\r
9 import org.dom4j.*;\r
10 import org.dom4j.io.SAXReader;\r
11 import org.jdom.input.SAXBuilder;\r
12 import org.xml.sax.InputSource;\r
13 \r
14 \r
15 import org.jdom.Document;\r
16 import org.jdom.Element;\r
17 import org.jdom.JDOMException;\r
18 \r
19 import java.io.*;\r
20 import java.util.*;\r
21 \r
22 /**  This class is used to replay a request to the Services layer, by sending the XML payload\r
23  *   in an appropriate Multipart request.\r
24  *   See example usage in calling class XmlReplayTest in services/IntegrationTests, and also in main() in this class.\r
25  *   @author Laramie Crocker\r
26  */\r
27 public class XmlReplay {\r
28 \r
29     public XmlReplay(String basedir){\r
30         this.basedir = basedir;\r
31         this.serviceResultsMap = createResultsMap();\r
32     }\r
33 \r
34     public static final String DEFAULT_CONTROL = "xml-replay-control.xml";\r
35     public static final String DEFAULT_MASTER_CONTROL = "xml-replay-master.xml";\r
36     public static final String DEFAULT_DEV_MASTER_CONTROL = "dev-master.xml";\r
37 \r
38     private String basedir = ".";  //set from constructor.\r
39     public String getBaseDir(){\r
40         return basedir;\r
41     }\r
42     \r
43     private String controlFileName = DEFAULT_CONTROL;\r
44     public String getControlFileName() {\r
45         return controlFileName;\r
46     }\r
47     public void setControlFileName(String controlFileName) {\r
48         this.controlFileName = controlFileName;\r
49     }\r
50 \r
51     private String protoHostPort = "";\r
52     public String getProtoHostPort() {\r
53         return protoHostPort;\r
54     }\r
55     public void setProtoHostPort(String protoHostPort) {\r
56         this.protoHostPort = protoHostPort;\r
57     }\r
58 \r
59     private boolean autoDeletePOSTS = true;\r
60     public boolean isAutoDeletePOSTS() {\r
61         return autoDeletePOSTS;\r
62     }\r
63     public void setAutoDeletePOSTS(boolean autoDeletePOSTS) {\r
64         this.autoDeletePOSTS = autoDeletePOSTS;\r
65     }\r
66 \r
67     private Dump dump;\r
68     public Dump getDump() {\r
69         return dump;\r
70     }\r
71     public void setDump(Dump dump) {\r
72         this.dump = dump;\r
73     }\r
74 \r
75     AuthsMap defaultAuthsMap;\r
76     public AuthsMap getDefaultAuthsMap(){\r
77         return defaultAuthsMap;\r
78     }\r
79     public void setDefaultAuthsMap(AuthsMap authsMap){\r
80         defaultAuthsMap = authsMap;\r
81     }\r
82 \r
83     private Map<String, ServiceResult> serviceResultsMap;\r
84     public Map<String, ServiceResult> getServiceResultsMap(){\r
85         return serviceResultsMap;\r
86     }\r
87     public static Map<String, ServiceResult> createResultsMap(){\r
88         return new HashMap<String, ServiceResult>();\r
89     }\r
90 \r
91 \r
92     public String toString(){\r
93         return "XmlReplay{"+this.basedir+", "+this.defaultAuthsMap+", "+this.dump+'}';\r
94     }\r
95 \r
96     // ============== METHODS ===========================================================\r
97 \r
98     public org.dom4j.Document openMasterConfigFile(String masterFilename) throws FileNotFoundException {\r
99         org.dom4j.Document document = getDocument(Tools.glue(basedir, "/", masterFilename)); //will check full path first, then checks relative to PWD.\r
100         if (document == null){\r
101             throw new FileNotFoundException("XmlReplay master control file ("+masterFilename+") not found in basedir: "+basedir+". Exiting test.");\r
102         }\r
103         return document;\r
104     }\r
105 \r
106     /** specify the master config file, relative to getBaseDir(), but ignore any tests or testGroups in the master.\r
107      *  @return a Document object, which you don't need to use: all options will be stored in XmlReplay instance.\r
108      */\r
109     public org.dom4j.Document readOptionsFromMasterConfigFile(String masterFilename) throws FileNotFoundException {\r
110         org.dom4j.Document document = openMasterConfigFile(masterFilename);\r
111         protoHostPort = document.selectSingleNode("/xmlReplayMaster/protoHostPort").getText().trim();\r
112         AuthsMap authsMap = readAuths(document);\r
113         setDefaultAuthsMap(authsMap);\r
114         Dump dump = XmlReplay.readDumpOptions(document);\r
115         setDump(dump);\r
116         return document;\r
117     }\r
118 \r
119     public List<List<ServiceResult>> runMaster(String masterFilename) throws Exception {\r
120         return runMaster(masterFilename, true);\r
121     }\r
122 \r
123     /** Creates new instances of XmlReplay, one for each controlFile specified in the master,\r
124      *  and setting defaults from this instance, but not sharing ServiceResult objects or maps. */\r
125     public List<List<ServiceResult>> runMaster(String masterFilename, boolean readOptionsFromMaster) throws Exception {\r
126         List<List<ServiceResult>> list = new ArrayList<List<ServiceResult>>();\r
127         org.dom4j.Document document;\r
128         if (readOptionsFromMaster){\r
129             document = readOptionsFromMasterConfigFile(masterFilename);\r
130         } else {\r
131             document = openMasterConfigFile(masterFilename);\r
132         }\r
133         String controlFile, testGroup, test;\r
134         List<Node> runNodes;\r
135         runNodes = document.selectNodes("/xmlReplayMaster/run");\r
136         for (Node runNode : runNodes) {\r
137             controlFile = runNode.valueOf("@controlFile");\r
138             testGroup = runNode.valueOf("@testGroup");\r
139             test = runNode.valueOf("@test"); //may be empty\r
140 \r
141             //Create a new instance and clone only config values, not any results maps.\r
142             XmlReplay replay = new XmlReplay(basedir);\r
143             replay.setControlFileName(controlFile);\r
144             replay.setProtoHostPort(protoHostPort);\r
145             replay.setAutoDeletePOSTS(isAutoDeletePOSTS());\r
146             replay.setDump(dump);\r
147             replay.setDefaultAuthsMap(getDefaultAuthsMap());\r
148 \r
149             //Now run *that* instance.\r
150             List<ServiceResult> results = replay.runTests(testGroup, test);\r
151             list.add(results);\r
152         }\r
153         return list;\r
154     }\r
155 \r
156     /** Use this if you wish to named tests within a testGroup, otherwise call runTestGroup(). */\r
157     public List<ServiceResult>  runTests(String testGroupID, String testID) throws Exception {\r
158         List<ServiceResult> result = runXmlReplayFile(this.basedir,\r
159                                 this.controlFileName,\r
160                                 testGroupID,\r
161                                 testID,\r
162                                 this.serviceResultsMap,\r
163                                 this.autoDeletePOSTS,\r
164                                 dump,\r
165                                 this.protoHostPort,\r
166                                 this.defaultAuthsMap);\r
167         return result;\r
168     }\r
169 \r
170     /** Use this if you wish to specify just ONE test to run within a testGroup, otherwise call runTestGroup(). */\r
171     public ServiceResult  runTest(String testGroupID, String testID) throws Exception {\r
172         List<ServiceResult> result = runXmlReplayFile(this.basedir,\r
173                                 this.controlFileName,\r
174                                 testGroupID,\r
175                                 testID,\r
176                                 this.serviceResultsMap,\r
177                                 this.autoDeletePOSTS,\r
178                                 dump,\r
179                                 this.protoHostPort,\r
180                                 this.defaultAuthsMap);\r
181         if (result.size()>1){\r
182             throw new IndexOutOfBoundsException("Multiple ("+result.size()+") tests with ID='"+testID+"' were found within test group '"+testGroupID+"', but there should only be one test per ID attribute.");\r
183         }\r
184         return result.get(0);\r
185     }\r
186 \r
187     /** Use this if you wish to run all tests within a testGroup.*/\r
188     public List<ServiceResult> runTestGroup(String testGroupID) throws Exception {\r
189         //NOTE: calling runTest with empty testID runs all tests in a test group, but don't expose this fact.\r
190         // Expose this method (runTestGroup) instead.\r
191         return runTests(testGroupID, "");\r
192     }\r
193 \r
194     public List<ServiceResult>  autoDelete(String logName){\r
195         return autoDelete(this.serviceResultsMap, logName);\r
196     }\r
197 \r
198     /** Use this method to clean up resources created on the server that returned CSIDs, if you have\r
199      *  specified autoDeletePOSTS==false, which means you are managing the cleanup yourself.\r
200      * @param serviceResultsMap a Map of ServiceResult objects, which will contain ServiceResult.deleteURL.\r
201      * @return a List<String> of debug info about which URLs could not be deleted.\r
202      */\r
203     public static List<ServiceResult> autoDelete(Map<String, ServiceResult> serviceResultsMap, String logName){\r
204         List<ServiceResult> results = new ArrayList<ServiceResult>();\r
205         for (ServiceResult pr : serviceResultsMap.values()){\r
206             try {\r
207                 ServiceResult deleteResult = XmlReplayTransport.doDELETE(pr.deleteURL, pr.auth, pr.testID, "[autodelete:"+logName+"]");\r
208                 results.add(deleteResult);\r
209             } catch (Throwable t){\r
210                 String s = (pr!=null) ? "ERROR while cleaning up ServiceResult map: "+pr+" for "+pr.deleteURL+" :: "+t\r
211                                       : "ERROR while cleaning up ServiceResult map (null ServiceResult): "+t;\r
212                 System.err.println(s);\r
213                 ServiceResult errorResult = new ServiceResult();\r
214                 errorResult.fullURL = pr.fullURL;\r
215                 errorResult.testGroupID = pr.testGroupID;\r
216                 errorResult.fromTestID = pr.fromTestID;\r
217                 errorResult.error = s;\r
218                 results.add(errorResult);\r
219             }\r
220         }\r
221         return results;\r
222     }\r
223 \r
224     public static class AuthsMap {\r
225         Map<String,String> map;\r
226         String defaultID="";\r
227         public String getDefaultAuth(){\r
228             return map.get(defaultID);\r
229         }\r
230         public String toString(){\r
231             return "AuthsMap: {default='"+defaultID+"'; "+map.keySet()+'}';\r
232         }\r
233     }\r
234 \r
235     public static AuthsMap readAuths(org.dom4j.Document document){\r
236     Map<String, String> map = new HashMap<String, String>();\r
237         List<Node> authNodes = document.selectNodes("//auths/auth");\r
238         for (Node auth : authNodes) {\r
239             map.put(auth.valueOf("@ID"), auth.getStringValue());\r
240         }\r
241         AuthsMap authsMap = new AuthsMap();\r
242         Node auths = document.selectSingleNode("//auths");\r
243         String defaultID = "";\r
244         if (auths != null){\r
245             defaultID = auths.valueOf("@default");\r
246         }\r
247         authsMap.map = map;\r
248         authsMap.defaultID = defaultID;\r
249         return authsMap;\r
250     }\r
251 \r
252     public static class Dump {\r
253         public boolean payloads = false;\r
254         //public static final ServiceResult.DUMP_OPTIONS dumpServiceResultOptions = ServiceResult.DUMP_OPTIONS;\r
255         public ServiceResult.DUMP_OPTIONS dumpServiceResult = ServiceResult.DUMP_OPTIONS.minimal;\r
256         public String toString(){\r
257             return "payloads: "+payloads+" dumpServiceResult: "+dumpServiceResult;\r
258         }\r
259     }\r
260 \r
261     public static Dump getDumpConfig(){\r
262         return new Dump();\r
263     }\r
264 \r
265     public static Dump readDumpOptions(org.dom4j.Document document){\r
266         Dump dump = getDumpConfig();\r
267         Node dumpNode = document.selectSingleNode("//dump");\r
268         if (dumpNode != null){\r
269             dump.payloads = Tools.isTrue(dumpNode.valueOf("@payloads"));\r
270             String dumpServiceResultStr = dumpNode.valueOf("@dumpServiceResult");\r
271             if (Tools.notEmpty(dumpServiceResultStr)){\r
272                 dump.dumpServiceResult = ServiceResult.DUMP_OPTIONS.valueOf(dumpServiceResultStr);\r
273             }\r
274         }\r
275         return dump;\r
276     }\r
277 \r
278     private static class PartsStruct {\r
279         public List<String> partsList = new ArrayList<String>();\r
280         public List<String> filesList = new ArrayList<String>();\r
281         boolean bDoingSinglePartPayload = false;\r
282         String singlePartPayloadFilename = "";\r
283         String overrideTestID = "";\r
284         public static PartsStruct readParts(Node testNode, final String testID, String xmlReplayBaseDir){\r
285             PartsStruct result = new PartsStruct();\r
286             result.singlePartPayloadFilename = testNode.valueOf("filename");\r
287             String singlePartPayloadFilename = testNode.valueOf("filename");\r
288             if (Tools.notEmpty(singlePartPayloadFilename)){\r
289                 result.bDoingSinglePartPayload = true;\r
290                 result.singlePartPayloadFilename = xmlReplayBaseDir + '/' + singlePartPayloadFilename;\r
291             } else {\r
292                 result.bDoingSinglePartPayload = false;\r
293                 List<Node> parts = testNode.selectNodes("parts/part");\r
294                 if (parts == null || parts.size()==0){\r
295                     //path is just /testGroup/test/part/\r
296                     String commonPartName = testNode.valueOf("part/label");\r
297                     String testfile = testNode.valueOf("part/filename");\r
298                     String fullTestFilename = xmlReplayBaseDir + '/' + testfile;\r
299                     if ( Tools.isEmpty(testID) ){\r
300                         result.overrideTestID = testfile; //It is legal to have a missing ID attribute, and rely on a unique filename.\r
301                     }\r
302                     result.partsList.add(commonPartName);\r
303                     result.filesList.add(fullTestFilename);\r
304                 } else {\r
305                     // path is /testGroup/test/parts/part/\r
306                     for (Node part : parts){\r
307                         String commonPartName = part.valueOf("label");\r
308                         String filename = part.valueOf("filename");\r
309                         String fullTestFilename = xmlReplayBaseDir + '/' + filename;\r
310                         if ( Tools.isEmpty(testID) ){  //if testID is empty, we'll use the *first*  filename as ID.\r
311                             result.overrideTestID = filename; //It is legal to have a missing ID attribute, and rely on a unique filename.\r
312                         }\r
313                         result.partsList.add(commonPartName);\r
314                         result.filesList.add(fullTestFilename);\r
315                     }\r
316                 }\r
317             }\r
318             return result;\r
319         }\r
320     }\r
321 \r
322     private static String fixupFullURL(String fullURL, String protoHostPort, String uri){\r
323         if ( ! uri.startsWith(protoHostPort)){\r
324             fullURL = Tools.glue(protoHostPort, "/", uri);\r
325         } else {\r
326             fullURL = uri;\r
327         }\r
328         return fullURL;\r
329     }\r
330 \r
331     private static String fromTestID(String fullURL, Node testNode, Map<String, ServiceResult> serviceResultsMap){\r
332         String fromTestID = testNode.valueOf("fromTestID");\r
333         if (Tools.notEmpty(fromTestID)){\r
334             ServiceResult getPR = serviceResultsMap.get(fromTestID);\r
335             if (getPR != null){\r
336                 fullURL = Tools.glue(fullURL, "/", getPR.location);\r
337             }\r
338         }\r
339         return fullURL;\r
340     }\r
341 \r
342     private static String CSIDfromTestID(Node testNode, Map<String, ServiceResult> serviceResultsMap){\r
343         String result = "";\r
344         String fromTestID = testNode.valueOf("fromTestID");\r
345         if (Tools.notEmpty(fromTestID)){\r
346             ServiceResult getPR = serviceResultsMap.get(fromTestID);\r
347             if (getPR != null){\r
348                 result = getPR.location;\r
349             }\r
350         }\r
351         return result;\r
352     }\r
353 \r
354     public static org.dom4j.Document getDocument(String xmlFileName) {\r
355         org.dom4j.Document document = null;\r
356         SAXReader reader = new SAXReader();\r
357         try {\r
358             document = reader.read(xmlFileName);\r
359         } catch (DocumentException e) {\r
360             System.out.println("ERROR reading document: "+e);\r
361             e.printStackTrace();\r
362         }\r
363         return document;\r
364     }\r
365     protected static String validateResponseSinglePayload(ServiceResult serviceResult,\r
366                                                  Map<String, ServiceResult> serviceResultsMap,\r
367                                                  PartsStruct expectedResponseParts,\r
368                                                  XmlReplayEval evalStruct)\r
369     throws Exception {\r
370         String OK = "";\r
371         byte[] b = FileUtils.readFileToByteArray(new File(expectedResponseParts.singlePartPayloadFilename));\r
372         String expectedPartContent = new String(b);\r
373         expectedPartContent = evalStruct.eval(expectedPartContent, serviceResultsMap, evalStruct.jexl, evalStruct.jc);\r
374         String label = "NOLABEL";\r
375         String leftID  = "{from expected part, label:"+label+" filename: "+expectedResponseParts.singlePartPayloadFilename+"}";\r
376         String rightID = "{from server, label:"+label\r
377                             +" fromTestID: "+serviceResult.fromTestID\r
378                             +" URL: "+serviceResult.fullURL\r
379                             +"}";\r
380         TreeWalkResults list =\r
381             XmlCompareJdom.compareParts(expectedPartContent,\r
382                                         leftID,\r
383                                         serviceResult.result,\r
384                                         rightID);\r
385         serviceResult.addPartSummary(label, list);\r
386         return OK;\r
387     }\r
388 \r
389     protected static String validateResponse(ServiceResult serviceResult,\r
390                                              Map<String, ServiceResult> serviceResultsMap,\r
391                                              PartsStruct expectedResponseParts,\r
392                                              XmlReplayEval evalStruct){\r
393         String OK = "";\r
394         if (expectedResponseParts == null) return OK;\r
395         if (serviceResult == null) return OK;\r
396         if (serviceResult.result.length() == 0) return OK;\r
397         String responseDump = serviceResult.result;\r
398         try {\r
399             if (expectedResponseParts.bDoingSinglePartPayload){\r
400                 return validateResponseSinglePayload(serviceResult, serviceResultsMap, expectedResponseParts, evalStruct);\r
401             }\r
402             //System.out.println("responseDump: "+responseDump);\r
403             PayloadLogger.HttpTraffic traffic = PayloadLogger.readPayloads(responseDump, serviceResult.boundary, serviceResult.contentLength);\r
404 \r
405             for (int i=0; i<expectedResponseParts.partsList.size(); i++){\r
406                 String fileName = expectedResponseParts.filesList.get(i);\r
407                 String label = expectedResponseParts.partsList.get(i);\r
408                 byte[] b = FileUtils.readFileToByteArray(new File(fileName));\r
409                 String expectedPartContent = new String(b);\r
410                 expectedPartContent = evalStruct.eval(expectedPartContent, serviceResultsMap, evalStruct.jexl, evalStruct.jc);\r
411                 System.out.println("expected: "+label+ " content ==>\r\n"+expectedPartContent);\r
412                 PayloadLogger.Part partFromServer = traffic.getPart(label);\r
413                 String partFromServerContent = "";\r
414                 if (partFromServer != null){\r
415                     partFromServerContent = partFromServer.getContent();\r
416                 } else {\r
417                     partFromServerContent = "";\r
418                 }\r
419                 //if (partFromServer!=null) {\r
420                     //System.out.println("====part content from server.   label-->"+label+"<-- \r\npart-->"+partFromServerContent+"<--");\r
421 \r
422                     String leftID  = "{from expected part, label:"+label+" filename: "+fileName+"}";\r
423                     String rightID = "{from server, label:"+label\r
424                                         //+" testGroupID: "+serviceResult.testGroupID\r
425                                         +" fromTestID: "+serviceResult.fromTestID\r
426                                         +" URL: "+serviceResult.fullURL\r
427                                         +"}";\r
428                     TreeWalkResults list =\r
429                         XmlCompareJdom.compareParts(expectedPartContent,\r
430                                                     leftID,\r
431                                                     partFromServerContent,\r
432                                                     rightID);\r
433                     //if (list.getMismatchCount()>0){\r
434                         serviceResult.addPartSummary(label, list);\r
435                     //}\r
436                 //}\r
437             }\r
438         } catch (Exception e){\r
439             String err = "ERROR in XmlReplay.validateResponse() : "+e; \r
440             //System.out.println(err);\r
441             return err  ;\r
442         }\r
443         return OK;\r
444     }\r
445 \r
446 \r
447     //================= runXmlReplayFile ======================================================\r
448 \r
449     public static List<ServiceResult> runXmlReplayFile(String xmlReplayBaseDir,\r
450                                           String controlFileName,\r
451                                           String testGroupID,\r
452                                           String oneTestID,\r
453                                           Map<String, ServiceResult> serviceResultsMap,\r
454                                           boolean param_autoDeletePOSTS,\r
455                                           Dump dump,\r
456                                           String protoHostPortParam,\r
457                                           AuthsMap defaultAuths)\r
458                                           throws Exception {\r
459         //Internally, we maintain two collections of ServiceResult:\r
460         //  the first is the return value of this method.\r
461         //  the second is the serviceResultsMap, which is used for keeping track of CSIDs created by POSTs, for later reference by DELETE, etc.\r
462         List<ServiceResult> results = new ArrayList<ServiceResult>();\r
463 \r
464         String controlFile = Tools.glue(xmlReplayBaseDir, "/", controlFileName);\r
465         org.dom4j.Document document;\r
466         document = getDocument(controlFile); //will check full path first, then checks relative to PWD.\r
467         if (document==null){\r
468             throw new FileNotFoundException("XmlReplay control file ("+controlFileName+") not found in basedir: "+xmlReplayBaseDir+" Exiting test.");\r
469         }\r
470         String protoHostPort;\r
471         if (Tools.isEmpty(protoHostPortParam)){\r
472             protoHostPort = document.selectSingleNode("/xmlReplay/protoHostPort").getText().trim();\r
473             System.out.println("DEPRECATED: Using protoHostPort ('"+protoHostPort+"') from xmlReplay file ('"+controlFile+"'), not master.");\r
474         } else {\r
475             protoHostPort = protoHostPortParam;\r
476         }\r
477         if (Tools.isEmpty(protoHostPort)){\r
478             throw new Exception("XmlReplay control file must have a protoHostPort element");\r
479         }\r
480 \r
481         String authsMapINFO;\r
482         AuthsMap authsMap = readAuths(document);\r
483         if (authsMap.map.size()==0){\r
484             authsMap = defaultAuths;\r
485             authsMapINFO = "Using defaultAuths from master file: "+defaultAuths;\r
486         } else {\r
487             authsMapINFO = "Using AuthsMap from control file: "+authsMap;\r
488         }\r
489         System.out.println("XmlReplay running:"\r
490                           +"\r\n   controlFile: "+ (new File(controlFile).getCanonicalPath())\r
491                           +"\r\n   protoHostPort: "+protoHostPort\r
492                           +"\r\n   testGroup: "+testGroupID\r
493                           + (Tools.notEmpty(oneTestID) ? "\r\n   oneTestID: "+oneTestID : "")\r
494                           +"\r\n   AuthsMap: "+authsMapINFO\r
495                           +"\r\n   param_autoDeletePOSTS: "+param_autoDeletePOSTS\r
496                           +"\r\n   Dump info: "+dump\r
497                           +"\r\n");\r
498 \r
499         String autoDeletePOSTS = "";\r
500         List<Node> testgroupNodes;\r
501         if (Tools.notEmpty(testGroupID)){\r
502             testgroupNodes = document.selectNodes("//testGroup[@ID='"+testGroupID+"']");\r
503         } else {\r
504             testgroupNodes = document.selectNodes("//testGroup");\r
505         }\r
506 \r
507         JexlEngine jexl = new JexlEngine();   // Used for expression language expansion from uri field.\r
508         XmlReplayEval evalStruct = new XmlReplayEval();\r
509         evalStruct.serviceResultsMap = serviceResultsMap;\r
510         evalStruct.jexl = jexl;\r
511 \r
512         for (Node testgroup : testgroupNodes) {\r
513             JexlContext jc = new MapContext();  //Get a new JexlContext for each test group.\r
514             evalStruct.jc = jc;\r
515 \r
516             autoDeletePOSTS = testgroup.valueOf("@autoDeletePOSTS");\r
517             List<Node> tests;\r
518             if (Tools.notEmpty(oneTestID)){\r
519                 tests = testgroup.selectNodes("test[@ID='"+oneTestID+"']");\r
520             } else {\r
521                 tests = testgroup.selectNodes("test");\r
522             }\r
523             String authForTest = "";\r
524             int testElementIndex = -1;\r
525 \r
526             for (Node testNode : tests) {\r
527                 try {\r
528                     testElementIndex++;\r
529                     String testID = testNode.valueOf("@ID");\r
530                     String testIDLabel = Tools.notEmpty(testID) ? (testGroupID+'.'+testID) : (testGroupID+'.'+testElementIndex);\r
531                     String method = testNode.valueOf("method");\r
532                     String uri = testNode.valueOf("uri");\r
533                     String fullURL = Tools.glue(protoHostPort, "/", uri);\r
534                     String initURI = uri;\r
535 \r
536                     String authIDForTest = testNode.valueOf("@auth");\r
537                     String currentAuthForTest = authsMap.map.get(authIDForTest);\r
538                     if (Tools.notEmpty(currentAuthForTest)){\r
539                         authForTest = currentAuthForTest; //else just run with current from last loop;\r
540                     }\r
541                     if (Tools.isEmpty(authForTest)){\r
542                         authForTest = defaultAuths.getDefaultAuth();\r
543                     }\r
544 \r
545                     if (uri.indexOf("$")>-1){\r
546                         uri = evalStruct.eval(uri, serviceResultsMap, jexl, jc);\r
547                     }\r
548                     fullURL = fixupFullURL(fullURL, protoHostPort, uri);\r
549 \r
550                     List<Integer> expectedCodes = new ArrayList<Integer>();\r
551                     String expectedCodesStr = testNode.valueOf("expectedCodes");\r
552                     if (Tools.notEmpty(expectedCodesStr)){\r
553                          String[] codesArray = expectedCodesStr.split(",");\r
554                          for (String code : codesArray){\r
555                              expectedCodes.add(new Integer(code.trim()));\r
556                          }\r
557                     }\r
558 \r
559                     Node responseNode = testNode.selectSingleNode("response");\r
560                     PartsStruct expectedResponseParts = null;\r
561                     if (responseNode!=null){\r
562                         expectedResponseParts = PartsStruct.readParts(responseNode, testID, xmlReplayBaseDir);\r
563                         //System.out.println("reponse parts: >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"+expectedResponseParts);\r
564                     }\r
565 \r
566                     ServiceResult serviceResult;\r
567                     boolean isPOST = method.equalsIgnoreCase("POST");\r
568                     boolean isPUT =  method.equalsIgnoreCase("PUT");\r
569                     if ( isPOST || isPUT ) {\r
570                         PartsStruct parts = PartsStruct.readParts(testNode, testID, xmlReplayBaseDir);\r
571                         if (Tools.notEmpty(parts.overrideTestID)) {\r
572                             testID = parts.overrideTestID;\r
573                         }\r
574                         if (isPOST){\r
575                             String csid = CSIDfromTestID(testNode, serviceResultsMap);\r
576                             if (Tools.notEmpty(csid)) uri = Tools.glue(uri, "/", csid+"/items/");\r
577                         } else if (isPUT) {\r
578                             uri = fromTestID(uri, testNode, serviceResultsMap);\r
579                         }\r
580                         if (parts.bDoingSinglePartPayload){\r
581                             serviceResult = XmlReplayTransport.doPOST_PUTFromXML(parts.singlePartPayloadFilename, protoHostPort, uri, method, XmlReplayTransport.APPLICATION_XML, evalStruct, authForTest, testIDLabel);\r
582                         } else {\r
583                             serviceResult = XmlReplayTransport.doPOST_PUTFromXML_Multipart(parts.filesList, parts.partsList, protoHostPort, uri, method, evalStruct, authForTest, testIDLabel);\r
584                         }\r
585                         results.add(serviceResult);\r
586                         if (isPOST){\r
587                             serviceResultsMap.put(testID, serviceResult);      //PUTs do not return a Location, so don't add PUTs to serviceResultsMap.\r
588                         }\r
589                         fullURL = fixupFullURL(fullURL, protoHostPort, uri);\r
590                     } else if (method.equalsIgnoreCase("DELETE")){\r
591                         String fromTestID = testNode.valueOf("fromTestID");\r
592                         ServiceResult pr = serviceResultsMap.get(fromTestID);\r
593                         if (pr!=null){\r
594                             serviceResult = XmlReplayTransport.doDELETE(pr.deleteURL, authForTest, testIDLabel, fromTestID);\r
595                             serviceResult.fromTestID = fromTestID;\r
596                             if (expectedCodes.size()>0){\r
597                                 serviceResult.expectedCodes = expectedCodes;\r
598                             }\r
599                             results.add(serviceResult);\r
600                             if (serviceResult.gotExpectedResult()){  //gotExpectedResult depends on serviceResult.expectedCodes.\r
601                                 serviceResultsMap.remove(fromTestID);\r
602                             }\r
603                         } else {\r
604                             if (Tools.notEmpty(fromTestID)){\r
605                                 serviceResult = new ServiceResult();\r
606                                 serviceResult.responseCode = 0;\r
607                                 serviceResult.error = "ID not found in element fromTestID: "+fromTestID;\r
608                                 System.err.println("****\r\nServiceResult: "+serviceResult.error+". SKIPPING TEST. Full URL: "+fullURL);\r
609                             } else {\r
610                                 serviceResult = XmlReplayTransport.doDELETE(fullURL, authForTest, testID, fromTestID);\r
611                             }\r
612                             serviceResult.fromTestID = fromTestID;\r
613                             results.add(serviceResult);\r
614                         }\r
615                     } else if (method.equalsIgnoreCase("GET")){\r
616                         fullURL = fromTestID(fullURL, testNode, serviceResultsMap);\r
617                         serviceResult = XmlReplayTransport.doGET(fullURL, authForTest, testIDLabel);\r
618                         results.add(serviceResult);\r
619                     } else if (method.equalsIgnoreCase("LIST")){\r
620                         fullURL = fixupFullURL(fullURL, protoHostPort, uri);\r
621                         String listQueryParams = ""; //TODO: empty for now, later may pick up from XML control file.\r
622                         serviceResult = XmlReplayTransport.doLIST(fullURL, listQueryParams, authForTest, testIDLabel);\r
623                         results.add(serviceResult);\r
624                     } else {\r
625                         throw new Exception("HTTP method not supported by XmlReplay: "+method);\r
626                     }\r
627 \r
628                     serviceResult.testID = testID;\r
629                     serviceResult.fullURL = fullURL;\r
630                     serviceResult.auth = authForTest;\r
631                     serviceResult.method = method;\r
632                     if (expectedCodes.size()>0){\r
633                         serviceResult.expectedCodes = expectedCodes;\r
634                     }\r
635                     if (Tools.isEmpty(serviceResult.testID)) serviceResult.testID = testIDLabel;\r
636                     if (Tools.isEmpty(serviceResult.testGroupID)) serviceResult.testGroupID = testGroupID;\r
637 \r
638                     Node expectedLevel = testNode.selectSingleNode("response/expected");\r
639                     if (expectedLevel!=null){\r
640                         String level = expectedLevel.valueOf("@level");\r
641                         serviceResult.payloadStrictness = level;\r
642                     }\r
643 \r
644                     String vError = validateResponse(serviceResult, serviceResultsMap, expectedResponseParts, evalStruct);\r
645                     if (Tools.notEmpty(vError)){\r
646                         serviceResult.error = vError;\r
647                         serviceResult.failureReason = " : VALIDATION ERROR; ";\r
648                     }\r
649 \r
650                     String serviceResultRow = serviceResult.dump(dump.dumpServiceResult);\r
651                     String leader = (dump.dumpServiceResult == ServiceResult.DUMP_OPTIONS.detailed) ? "XmlReplay:"+testIDLabel+": ": "";\r
652                     System.out.println(leader+serviceResultRow+"\r\n");\r
653                     if (dump.payloads) System.out.println(serviceResult.result);\r
654                 } catch (Throwable t) {\r
655                     System.out.println("ERROR: XmlReplay experienced an error in a test node: "+testNode+" Throwable: "+t);\r
656                 }\r
657             }\r
658             if (Tools.isTrue(autoDeletePOSTS)&&param_autoDeletePOSTS){\r
659                 autoDelete(serviceResultsMap, "default");\r
660             }\r
661         }\r
662         return results;\r
663     }\r
664 \r
665 \r
666     //======================== MAIN ===================================================================\r
667 \r
668     private static Options createOptions() {\r
669         Options options = new Options();\r
670         options.addOption("xmlReplayBaseDir", true, "default/basedir");\r
671         return options;\r
672     }\r
673 \r
674     public static String usage(){\r
675         String result = "org.collectionspace.services.IntegrationTests.xmlreplay.XmlReplay {args}\r\n"\r
676                         +"  -xmlReplayBaseDir <dir> \r\n"\r
677                         +" You may also override these with system args, e.g.: \r\n"\r
678                         +"   -DxmlReplayBaseDir=/path/to/dir \r\n"\r
679                         +" These may also be passed in via the POM.\r\n"\r
680                         +" You can also set these system args, e.g.: \r\n"\r
681                         +"  -DtestGroupID=<oneID> \r\n"\r
682                         +"  -DtestID=<one TestGroup ID>"\r
683                         +"  -DautoDeletePOSTS=<true|false> \r\n"\r
684                         +"    (note: -DautoDeletePOSTS won't force deletion if set to false in control file.";\r
685         return result;\r
686     }\r
687 \r
688     private static String opt(CommandLine line, String option){\r
689         String result;\r
690         String fromProps = System.getProperty(option);\r
691         if (Tools.notEmpty(fromProps)){\r
692             return fromProps;\r
693         }\r
694         result = line.getOptionValue(option);\r
695         if (result == null){\r
696             result = "";\r
697         }\r
698         return result;\r
699     }\r
700 \r
701     public static void main(String[]args) throws Exception {\r
702         Options options = createOptions();\r
703         //System.out.println("System CLASSPATH: "+prop.getProperty("java.class.path", null));\r
704         CommandLineParser parser = new GnuParser();\r
705         try {\r
706             // parse the command line arguments\r
707             CommandLine line = parser.parse(options, args);\r
708 \r
709             String xmlReplayBaseDir = opt(line, "xmlReplayBaseDir");\r
710             String testGroupID      = opt(line, "testGroupID");\r
711             String testID           = opt(line, "testID");\r
712             String autoDeletePOSTS  = opt(line, "autoDeletePOSTS");\r
713             String dumpResults      = opt(line, "dumpResults");\r
714             String controlFilename   = opt(line, "controlFilename");\r
715             String xmlReplayMaster  = opt(line, "xmlReplayMaster");\r
716 \r
717             xmlReplayBaseDir = Tools.fixFilename(xmlReplayBaseDir);\r
718             controlFilename = Tools.fixFilename(controlFilename);\r
719 \r
720             boolean bAutoDeletePOSTS = true;\r
721             if (Tools.notEmpty(autoDeletePOSTS)) {\r
722                 bAutoDeletePOSTS = Tools.isTrue(autoDeletePOSTS);\r
723             }\r
724             boolean bDumpResults = false;\r
725             if (Tools.notEmpty(dumpResults)) {\r
726                 bDumpResults = Tools.isTrue(autoDeletePOSTS);\r
727             }\r
728             if (Tools.isEmpty(xmlReplayBaseDir)){\r
729                 System.err.println("ERROR: xmlReplayBaseDir was not specified.");\r
730                 return;\r
731             }\r
732             File f = new File(Tools.glue(xmlReplayBaseDir, "/", controlFilename));\r
733             if (Tools.isEmpty(xmlReplayMaster) && !f.exists()){\r
734                 System.err.println("Control file not found: "+f.getCanonicalPath());\r
735                 return;\r
736             }\r
737             File fMaster = new File(Tools.glue(xmlReplayBaseDir, "/", xmlReplayMaster));\r
738             if (Tools.notEmpty(xmlReplayMaster)  && !fMaster.exists()){\r
739                 System.err.println("Master file not found: "+fMaster.getCanonicalPath());\r
740                 return;\r
741             }\r
742 \r
743             String xmlReplayBaseDirResolved = (new File(xmlReplayBaseDir)).getCanonicalPath();\r
744             System.out.println("XmlReplay ::"\r
745                             + "\r\n    xmlReplayBaseDir: "+xmlReplayBaseDir\r
746                             + "\r\n    xmlReplayBaseDir(resolved): "+xmlReplayBaseDirResolved\r
747                             + "\r\n    controlFilename: "+controlFilename\r
748                             + "\r\n    xmlReplayMaster: "+xmlReplayMaster\r
749                             + "\r\n    testGroupID: "+testGroupID\r
750                             + "\r\n    testID: "+testID\r
751                             + "\r\n    autoDeletePOSTS: "+bAutoDeletePOSTS\r
752                             + (Tools.notEmpty(xmlReplayMaster)\r
753                                        ? ("\r\n    will use master file: "+fMaster.getCanonicalPath())\r
754                                        : ("\r\n    will use control file: "+f.getCanonicalPath()) )\r
755                              );\r
756             \r
757             if (Tools.notEmpty(xmlReplayMaster)){\r
758                 if (Tools.notEmpty(controlFilename)){\r
759                     System.out.println("WARN: controlFilename: "+controlFilename+" will not be used because master was specified.  Running master: "+xmlReplayMaster);\r
760                 }\r
761                 XmlReplay replay = new XmlReplay(xmlReplayBaseDirResolved);\r
762                 replay.readOptionsFromMasterConfigFile(xmlReplayMaster);\r
763                 replay.setAutoDeletePOSTS(bAutoDeletePOSTS);\r
764                 Dump dumpFromMaster = replay.getDump();\r
765                 dumpFromMaster.payloads = Tools.isTrue(dumpResults);\r
766                 replay.setDump(dumpFromMaster);\r
767                 replay.runMaster(xmlReplayMaster, false); //false, because we already just read the options, and override a few.\r
768             } else {\r
769                 Dump dump = getDumpConfig();\r
770                 dump.payloads = Tools.isTrue(dumpResults);\r
771                 runXmlReplayFile(xmlReplayBaseDirResolved, controlFilename, testGroupID, testID, createResultsMap(), bAutoDeletePOSTS, dump, "", null);\r
772             }\r
773         } catch (ParseException exp) {\r
774             // oops, something went wrong\r
775             System.err.println("Cmd-line parsing failed.  Reason: " + exp.getMessage());\r
776             System.err.println(usage());\r
777         } catch (Exception e) {\r
778             System.out.println("Error : " + e.getMessage());\r
779             e.printStackTrace();\r
780         }\r
781     }\r
782 \r
783 }\r