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