View Javadoc

1   /**
2    * Copyright 2013 The Kuali Foundation Licensed under the
3    * Educational Community License, Version 2.0 (the "License"); you may
4    * not use this file except in compliance with the License. You may
5    * obtain a copy of the License at
6    *
7    * http://www.osedu.org/licenses/ECL-2.0
8    *
9    * Unless required by applicable law or agreed to in writing,
10   * software distributed under the License is distributed on an "AS IS"
11   * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12   * or implied. See the License for the specific language governing
13   * permissions and limitations under the License.
14   *
15   * Created by Mezba Mahtab (mezba.mahtab@utoronto.ca) on 3/8/13
16   */
17  package org.kuali.student.mock.mojo;
18  
19  import org.kuali.student.contract.model.MessageStructure;
20  import org.kuali.student.contract.model.Service;
21  import org.kuali.student.contract.model.ServiceContractModel;
22  import org.kuali.student.contract.model.ServiceMethod;
23  import org.kuali.student.contract.model.ServiceMethodError;
24  import org.kuali.student.contract.model.ServiceMethodParameter;
25  import org.kuali.student.contract.model.util.ServicesFilter;
26  import org.kuali.student.contract.writer.service.GetterSetterNameCalculator;
27  import org.slf4j.Logger;
28  import org.slf4j.LoggerFactory;
29  
30  import javax.management.OperationsException;
31  
32  import java.util.ArrayList;
33  import java.util.List;
34  
35  /**
36   * This class will generate the base class that does CRUD tests as
37   * part of the Conformance Tests for services. The generated class will
38   * need to be extended (and abstract methods filled in) to be complete.
39   * This class is meant to be generated again and again, and developers
40   * should customize tests in the extended class by extending or overwriting
41   * methods of this class as needed.
42   *
43   * @author Mezba Mahtab (mezba.mahtab@utoronto.ca)
44   */
45  public class ConformanceTestBaseCrudClassServiceWriter extends MockImplServiceWriter {
46      
47      private static final Logger log = LoggerFactory.getLogger(ConformanceTestExtendedCrudClassServiceWriter.class);
48  
49      /////////////////////////
50      // CONSTANTS
51      /////////////////////////
52  
53      public static final String ROOT_PACKAGE = "org.kuali.student";
54      protected static final String H1_COMMENT_CHAR = "=";
55      protected static final int H1_COMMENT_MARK_LENGTH = 20;
56  
57      ////////////////////////////
58      // Data Variables
59      ////////////////////////////
60  
61      private ServicesFilter filter;
62  
63      List<String> dtoObjectNamesWithCrud = null; // a list of all the DTOs managed by this class that has CRUDs
64  
65      protected List<String> getDtoObjectNamesWithCrud() {
66          return dtoObjectNamesWithCrud;
67      }
68  
69      ////////////////////////////
70      // CONSTRUCTOR
71      ////////////////////////////
72  
73      public ConformanceTestBaseCrudClassServiceWriter(ServiceContractModel model,
74                                                       String directory,
75                                                       String rootPackage,
76                                                       String servKey,
77                                                       List<ServiceMethod> methods,
78                                                       boolean isR1) {
79          super(model, directory, rootPackage, servKey, methods, isR1, calcPackage(servKey, rootPackage), calcClassName(servKey));
80          dtoObjectNamesWithCrud = calcNamesOfDTOsWithCrudManagedByService();
81      }
82  
83      public ConformanceTestBaseCrudClassServiceWriter(ServiceContractModel model,
84                                                       String directory,
85                                                       String rootPackage,
86                                                       String servKey,
87                                                       List<ServiceMethod> methods,
88                                                       boolean isR1,
89                                                       String packageName,
90                                                       String className) {
91          super(model, directory, rootPackage, servKey, methods, isR1, packageName, className);
92          dtoObjectNamesWithCrud = calcNamesOfDTOsWithCrudManagedByService();
93      }
94  
95      //////////////////////////
96      // FUNCTIONALS
97      //////////////////////////
98  
99      public static String calcPackage(String servKey, String rootPackage) {
100         String pack = rootPackage + ".";
101         pack = pack + "service.test";
102         return pack;
103     }
104 
105     /**
106      * Given the service key (name), returns a calculated class name for the conformance tester.
107      */
108     public static String calcClassName(String servKey) {
109         return "Test" + GetterSetterNameCalculator.calcInitUpper(fixServKey(servKey) + "ServiceImplConformanceBaseCrud");
110     }
111 
112     public static List<Service> filterServices(ServiceContractModel model, ServicesFilter filter) {
113         if (filter == null) {
114             return model.getServices();
115         }
116         return filter.filter(model.getServices());
117     }
118 
119 
120     /**
121      * Write out the entire file
122      */
123     public void write() {
124         // begin file
125         indentPrintln("@RunWith(SpringJUnit4ClassRunner.class)");
126         indentPrintln("@ContextConfiguration(locations = {\"classpath:" + servKey + "-test-with-mock-context.xml\"})");
127         indentPrint("public abstract class " + calcClassName(servKey) + " ");
128         // println(" implements " + calcServiceInterfaceClassName(servKey));
129         Service serv = finder.findService(servKey);
130         setPackageName(serv.getImplProject() + ".impl"); // change the package name
131         importsAdd(serv.getImplProject() + "." + serv.getName()); // import for the service
132 
133         doTestImportsAdd();
134 
135         // begin main class
136         openBrace();
137 
138         indentPrintln("");
139         indentPrintDecoratedComment("SETUP", H1_COMMENT_CHAR, H1_COMMENT_MARK_LENGTH);
140         indentPrintln("");
141 
142         // test service setup
143         indentPrintln("@Resource");
144         indentPrintln("public " + calcServiceInterfaceClassName(servKey) + " testService;");
145         indentPrintln("public " + calcServiceInterfaceClassName(servKey) + " get" + calcServiceInterfaceClassName(servKey) + "() { return testService; }");
146         indentPrintln("public void set" + calcServiceInterfaceClassName(servKey) + "(" + calcServiceInterfaceClassName(servKey) + " service) { testService = service; }");
147         indentPrintln("");
148 
149         // context info setup
150         indentPrintln("public ContextInfo contextInfo = null;");
151         indentPrintln("public static String principalId = \"123\";");
152         indentPrintln("");
153         indentPrintln("@Before");
154         indentPrintln("public void setUp()");
155         openBrace();
156         indentPrintln("principalId = \"123\";");
157         indentPrintln("contextInfo = new ContextInfo();");
158         indentPrintln("contextInfo.setPrincipalId(principalId);");
159         closeBrace();
160         indentPrintln("");
161 
162         // testing starts
163         indentPrintDecoratedComment("TESTING", H1_COMMENT_CHAR, H1_COMMENT_MARK_LENGTH);
164         indentPrintln("");
165 /*
166                 new ArrayList<String>();
167         for (ServiceMethod method: methods) {
168             // I am assuming all the DTOs will have a createXXX method.
169             if (MethodType.CREATE.equals (calcMethodType(method))) {
170                 String objectName = calcObjectName(method);
171                 dtoObjectNames.add(objectName);
172             }
173         }
174 */
175         // for each DTO, write the testCRUD
176         for (String dtoObjectName : dtoObjectNamesWithCrud) {
177             writeTestCrud(dtoObjectName);
178             indentPrintln("");
179         }
180 
181         // print out list of service operations that were tested
182         indentPrintDecoratedComment("SERVICE OPS TESTED IN BASE TEST CLASS", H1_COMMENT_CHAR, H1_COMMENT_MARK_LENGTH*2);
183         indentPrintln("");
184 
185         // separate out crud and non-crud methods
186         List<ServiceMethod> methodsTestedAsCrud = new ArrayList<ServiceMethod>();
187         List<ServiceMethod> methodsNotTested = new ArrayList<ServiceMethod>();
188 
189         for (ServiceMethod method : methods) {
190             if (isServiceMethodTestedAsPartofCrudInBaseConformanceTest (method)) {
191                 methodsTestedAsCrud.add(method);
192             } else {
193                 methodsNotTested.add(method);
194             }
195         }
196 
197         // print out crud methods
198         indentPrintln("/*");
199         incrementIndent();
200         indentPrintln("The following methods are tested as part of CRUD operations for this service's DTOs:");
201         incrementIndent();
202         for (ServiceMethod method: methodsTestedAsCrud) {
203             indentPrintln(method.getName());
204         }
205         decrementIndent();
206         decrementIndent();
207         indentPrintln("*/");
208         indentPrintln("");
209 
210         // print out list of service operations that are not tested as abstract test methods
211         indentPrintDecoratedComment("SERVICE OPS NOT TESTED IN BASE TEST CLASS", H1_COMMENT_CHAR, H1_COMMENT_MARK_LENGTH*2);
212         indentPrintln("");
213 
214         // for each method, create an abstract method to test that and print it out
215         for (ServiceMethod method: methodsNotTested) {
216             indentPrintln("/* Method Name: " + method.getName() + " */");
217             indentPrintln("@Test");
218             indentPrintln("public abstract void test_" + method.getName() + "() ");
219             if (method.getErrors().size()>0) {
220                 indentPrint("throws ");
221                 String comma = "";
222                 for (ServiceMethodError error: method.getErrors()) {
223                     indentPrint(comma + error.getClassName().trim());
224                     comma = ",";
225                 }
226             }
227             indentPrintln(";");
228             indentPrintln("");
229         }
230 
231         // end file print out
232         closeBrace ();
233         println ("");
234 
235         // close and print file
236         this.writeJavaClassAndImportsOutToFile();
237         this.getOut().close();
238     }
239 
240     /**
241      * Write the CRUD test methods
242      */
243     public void writeTestCrud (String dtoObjectName) {
244 
245         // get the message structures of the dto
246         List<MessageStructure> messageStructures = finder.findMessageStructures(dtoObjectName + "Info");
247 
248         // start method open signature
249         indentPrintln("// ****************************************************");
250         indentPrintln("//           " + dtoObjectName + "Info");
251         indentPrintln("// ****************************************************");
252         indentPrintln("@Test");
253         indentPrintln("public void testCrud" + dtoObjectName + "() ");
254         incrementIndent();
255         indentPrintln("throws DataValidationErrorException,");
256         incrementIndent();
257         indentPrintln("DoesNotExistException,");
258         indentPrintln("InvalidParameterException,");
259         indentPrintln("MissingParameterException,");
260         indentPrintln("OperationFailedException,");
261         indentPrintln("PermissionDeniedException,");
262         indentPrintln("ReadOnlyException,");
263         indentPrintln("VersionMismatchException,");
264         indentPrintln("DependentObjectsExistException");
265         decrementIndent();
266         decrementIndent();
267         openBrace();
268         // end method open signature
269 
270         // write the test portions
271         incrementIndent();
272         writeTestCreate(dtoObjectName, messageStructures);
273         writeTestUpdate(dtoObjectName, messageStructures);
274         writeTestReadAfterUpdate(dtoObjectName, messageStructures);
275         writeTestDelete(dtoObjectName, messageStructures);
276         decrementIndent();
277 
278         // end method
279         closeBrace();
280         indentPrintln("");
281 
282         // methods that will be overwritten
283         writetestCrudXXX_setDTOFieldsForTestCreate(dtoObjectName, messageStructures);
284         writetestCrudXXX_testDTOFieldsForTestCreateUpdate(dtoObjectName, messageStructures);
285         writetestCrudXXX_setDTOFieldsForTestUpdate(dtoObjectName, messageStructures);
286         writetestCrudXXX_testDTOFieldsForTestReadAfterUpdate(dtoObjectName, messageStructures);
287         writetestCrudXXX_setDTOFieldsForTestReadAfterUpdate(dtoObjectName, messageStructures);
288     }
289 
290     /**
291      * Write the 'test create' portion.
292      */
293     public void writeTestCreate (String dtoObjectName, List<MessageStructure> messageStructures) {
294         indentPrintDecoratedComment("test create");
295         indentPrintln(dtoObjectName + "Info expected = new " + dtoObjectName + "Info ();");
296         // indentPrintln("expected.setName(\"Name01\");");
297         // indentPrintln("expected.setDescr(new RichTextHelper().fromPlain(\"Description01\"));");
298         indentPrintln("");
299         indentPrintln("// METHOD TO SET DTO FIELDS HERE FOR TEST CREATE");
300         indentPrintln("testCrud" + dtoObjectName + "_setDTOFieldsForTestCreate (expected);");
301         indentPrintln("");
302 
303 /*
304         indentPrintln("if (expected.getClass().isAssignableFrom(TypeStateEntityInfo.class))");
305         openBrace();
306         incrementIndent();
307         // indentPrintln("TypeStateEntityInfo expectedTS = (TypeStateEntityInfo) expected;");
308         indentPrintln("expected.setTypeKey(\"typeKey01\");");
309         indentPrintln("expected.setStateKey(\"stateKey01\");");
310         decrementIndent();
311         closeBrace();
312 */
313         indentPrintln("new AttributeTester().add2ForCreate(expected.getAttributes());");
314 
315         indentPrintln("");
316         indentPrintln("// code to create actual");
317         indentPrintln(dtoObjectName + "Info actual = " + getMethodCallAsString ("create" + dtoObjectName, "= null; // TODO INSERT CODE TO CREATE actual HERE", MethodType.CREATE, dtoObjectName, "expected"));
318         indentPrintln("");
319 
320         // indentPrintln("if (actual.getClass().isAssignableFrom(IdEntityInfo.class))");
321         if (finder.findMessageStructure(dtoObjectName + "Info", "id")!=null) {
322             // openBrace();
323             // incrementIndent();
324             indentPrintln("assertNotNull(actual.getId());");
325             indentPrintln("new IdEntityTester().check(expected, actual);");
326             // decrementIndent();
327             // closeBrace();
328         }
329         else if (finder.findMessageStructure(dtoObjectName + "Info", "key")!=null) {
330             indentPrintln("assertNotNull(actual.getKey());");
331             indentPrintln("new KeyEntityTester().check(expected, actual);");
332         }
333 
334         indentPrintln("");
335         indentPrintln("// METHOD TO TEST DTO FIELDS HERE FOR TEST CREATE");
336         indentPrintln("testCrud" + dtoObjectName + "_testDTOFieldsForTestCreateUpdate (expected, actual);");
337         indentPrintln("");
338 
339         indentPrintln("new AttributeTester().check(expected.getAttributes(), actual.getAttributes());");
340         indentPrintln("new MetaTester().checkAfterCreate(actual.getMeta());");
341         indentPrintln("");
342 
343         indentPrintDecoratedComment("test read");
344         indentPrintln("expected = actual;");
345         indentPrintln("actual = " + getMethodCallAsString ("get" + dtoObjectName, "null; // TODO INSERT CODE TO GET actual HERE BY CALLING SERVICE OP", MethodType.GET_BY_ID, dtoObjectName, "actual"));
346         if (finder.findMessageStructure(dtoObjectName + "Info", "id")!=null) {
347             indentPrintln("assertEquals(expected.getId(), actual.getId());");
348             indentPrintln("new IdEntityTester().check(expected, actual);");
349         }
350         else if (finder.findMessageStructure(dtoObjectName + "Info", "key")!=null) {
351             indentPrintln("assertEquals(expected.getKey(), actual.getKey());");
352             indentPrintln("new KeyEntityTester().check(expected, actual);");
353         }
354 
355 
356         indentPrintln("");
357 
358         indentPrintln("// INSERT CODE FOR TESTING MORE DTO FIELDS HERE");
359         indentPrintln("testCrud" + dtoObjectName + "_testDTOFieldsForTestCreateUpdate (expected, actual);");
360         indentPrintln("");
361 
362         indentPrintln("new AttributeTester().check(expected.getAttributes(), actual.getAttributes());");
363         indentPrintln("new MetaTester().checkAfterGet(expected.getMeta(), actual.getMeta());");
364         indentPrintln("");
365     }
366 
367     /**
368      * Write the 'test update' portion.
369      */
370     public void writeTestUpdate (String dtoObjectName, List<MessageStructure> messageStructures) {
371         indentPrintDecoratedComment("test update");
372         indentPrintln(dtoObjectName + "Info original = new " + dtoObjectName + "Info (actual);");
373         indentPrintln("expected = new " + dtoObjectName + "Info (actual);");
374         
375         // indentPrintln("expected.setName(expected.getName() + \" updated\");");
376         // indentPrintln("expected.setDescr(new RichTextHelper().fromPlain(expected.getDescr().getPlain() + \"_Updated\"));");
377         indentPrintln("");
378         // indentPrintln("if (expected.getClass().isAssignableFrom(TypeStateEntityInfo.class))");
379         if (finder.findMessageStructure(dtoObjectName + "Info", "stateKey")!=null) {
380             // openBrace();
381             // incrementIndent();
382             // indentPrintln("TypeStateEntityInfo expectedTS = (TypeStateEntityInfo) expected;");
383             indentPrintln("expected.setStateKey(expected.getState() + \"_Updated\");");
384             // decrementIndent();
385             // closeBrace();
386         }
387         indentPrintln("");
388         indentPrintln("// METHOD TO INSERT CODE TO UPDATE DTO FIELDS HERE");
389         indentPrintln("testCrud" + dtoObjectName + "_setDTOFieldsForTestUpdate (expected);");
390         indentPrintln("");
391         indentPrintln("new AttributeTester().delete1Update1Add1ForUpdate(expected.getAttributes());");
392 
393         indentPrintln("// code to update");
394         indentPrintln("actual = " + getMethodCallAsString ("update" + dtoObjectName, "= null; // TODO INSERT CODE TO CALL UPDATE SERVICE OP HERE", MethodType.UPDATE, dtoObjectName, "expected"));
395         indentPrintln("");
396         
397         if (finder.findMessageStructure(dtoObjectName + "Info", "id")!=null) {
398             indentPrintln("assertEquals(expected.getId(), actual.getId());");
399             indentPrintln("new IdEntityTester().check(expected, actual);");
400         }
401         else if (finder.findMessageStructure(dtoObjectName + "Info", "key")!=null) {
402             indentPrintln("assertEquals(expected.getKey(), actual.getKey());");
403             indentPrintln("new KeyEntityTester().check(expected, actual);");
404         }
405         indentPrintln("");
406         indentPrintln("// METHOD TO INSERT CODE FOR TESTING DTO FIELDS HERE");
407         indentPrintln("testCrud" + dtoObjectName + "_testDTOFieldsForTestCreateUpdate (expected, actual);");
408         indentPrintln("");
409         indentPrintln("new AttributeTester().check(expected.getAttributes(), actual.getAttributes());");
410         indentPrintln("new MetaTester().checkAfterUpdate(expected.getMeta(), actual.getMeta());");
411         indentPrintln("");
412         
413         indentPrintln("// Test that VersionMissmatchException's are being detected");
414         indentPrintln("boolean exception = false;");        
415         indentPrintln("try {");
416         indent(getOut(), ' ');
417         
418         indentPrintln(getMethodCallAsString ("update" + dtoObjectName, "= null; // TODO INSERT CODE TO CALL UPDATE SERVICE OP HERE", MethodType.UPDATE, dtoObjectName, "original"));
419         indentPrintln("}");
420         indentPrintln("catch (VersionMismatchException e) { "); 
421         indent(getOut(), ' ');
422         indentPrint("exception = true;");
423         indentPrintln("}");
424         indentPrintln("");
425         
426         indentPrintln("Assert.assertTrue(\"VersionMissmatchException was not detected!\", exception);");
427         indentPrintln("");
428         
429     }
430 
431     /**
432      * Write the 'read after update' portion.
433      */
434     public void writeTestReadAfterUpdate (String dtoObjectName, List<MessageStructure> messageStructures) {
435         indentPrintDecoratedComment("test read after update");
436         indentPrintln("");
437         indentPrintln("expected = actual;");
438 
439         indentPrintln("// code to get actual");
440         indentPrintln("actual = " + getMethodCallAsString ("get" + dtoObjectName, "null; // TODO INSERT CODE TO GET actual HERE BY CALLING SERVICE OP", MethodType.GET_BY_ID, dtoObjectName, "actual"));
441         indentPrintln("");
442         if (finder.findMessageStructure(dtoObjectName + "Info", "id")!=null) {
443             indentPrintln("assertEquals(expected.getId(), actual.getId());");
444             indentPrintln("new IdEntityTester().check(expected, actual);");
445         }
446         else if (finder.findMessageStructure(dtoObjectName + "Info", "key")!=null) {
447             indentPrintln("assertEquals(expected.getKey(), actual.getKey());");
448             indentPrintln("new KeyEntityTester().check(expected, actual);");
449         }
450         indentPrintln("");
451         indentPrintln("// INSERT METHOD CODE FOR TESTING DTO FIELDS HERE");
452         indentPrintln("testCrud" + dtoObjectName + "_testDTOFieldsForTestReadAfterUpdate (expected, actual);");
453         indentPrintln("");
454         indentPrintln("new AttributeTester().check(expected.getAttributes(), actual.getAttributes());");
455         indentPrintln("new MetaTester().checkAfterGet(expected.getMeta(), actual.getMeta());");
456         indentPrintln("");
457         indentPrintln(dtoObjectName + "Info alphaDTO = actual;");
458         indentPrintln("");
459         indentPrintln("// create a 2nd DTO");
460         indentPrintln(dtoObjectName + "Info betaDTO = new " + dtoObjectName + "Info ();");
461         // indentPrintln("betaDTO.setName(\"Beta entity name\");");
462         // indentPrintln("betaDTO.setDescr(new RichTextHelper().fromPlain(\"Beta entity description\"));");
463         indentPrintln("");
464         indentPrintln("// METHOD TO INSERT CODE TO SET MORE DTO FIELDS HERE");
465         indentPrintln("testCrud" + dtoObjectName + "_setDTOFieldsForTestReadAfterUpdate (betaDTO);");
466         indentPrintln("");
467         // indentPrintln("if (betaDTO.getClass().isAssignableFrom(TypeStateEntityInfo.class))");
468         if (finder.findMessageStructure(dtoObjectName + "Info", "typeKey")!=null) {
469             // openBrace();
470             // incrementIndent();
471             //indentPrintln("TypeStateEntityInfo betaDTOTS = (TypeStateEntityInfo) betaDTO;");
472             indentPrintln("betaDTO.setTypeKey(\"typeKeyBeta\");");
473             // decrementIndent();
474             // closeBrace();
475         }
476         if (finder.findMessageStructure(dtoObjectName + "Info", "stateKey")!=null) {
477             indentPrintln("betaDTO.setStateKey(\"stateKeyBeta\");");
478         }
479         indentPrintln("betaDTO = " + getMethodCallAsString("create" + dtoObjectName, "null; // TODO INSERT CODE TO CREATE betaDTO", MethodType.CREATE, dtoObjectName, "betaDTO"));
480 
481         indentPrintln("");
482         indentPrintDecoratedComment("test bulk get with no ids supplied");
483         indentPrintln("");
484         indentPrintln("List<String> " + initLower(dtoObjectName) + "Ids = new ArrayList<String>();");
485 
486         indentPrintln("// code to get DTO by Ids");
487         indentPrintln("List<" + dtoObjectName + "Info> records = " + getMethodCallAsString ("get" + dtoObjectName + "sByIds", "null; // TODO INSERT CODE TO GET DTO BY IDS", MethodType.GET_BY_IDS, dtoObjectName));
488         indentPrintln("");
489 
490         indentPrintln("assertEquals(" + initLower(dtoObjectName) + "Ids.size(), records.size());");
491         indentPrintln("assertEquals(0, " + initLower(dtoObjectName) + "Ids.size());");
492         indentPrintln("");
493         indentPrintDecoratedComment("test bulk get");
494         indentPrintln(initLower(dtoObjectName) + "Ids = new ArrayList<String>();");
495         indentPrintln(initLower(dtoObjectName) + "Ids.add(alphaDTO.getId());");
496         indentPrintln(initLower(dtoObjectName) + "Ids.add(betaDTO.getId());");
497 
498         indentPrintln("// code to get DTO by Ids");
499         indentPrintln("records = " + getMethodCallAsString ("get" + dtoObjectName + "sByIds", "null; // TODO INSERT CODE TO GET DTO BY IDS", MethodType.GET_BY_IDS, dtoObjectName));
500         indentPrintln("");
501 
502         indentPrintln("assertEquals(" + initLower(dtoObjectName) + "Ids.size(), records.size());");
503         indentPrintln("for (" + dtoObjectName + "Info record : records)");
504         openBrace();
505         incrementIndent();
506         indentPrintln("if (!" + initLower(dtoObjectName) + "Ids.remove(record.getId()))");
507         openBrace();
508         incrementIndent();
509         indentPrintln("fail(record.getId());");
510         decrementIndent();
511         closeBrace();
512         decrementIndent();
513         closeBrace();
514         indentPrintln("assertEquals(0, " + initLower(dtoObjectName) + "Ids.size());");
515         indentPrintln("");
516         indentPrintDecoratedComment("test get by type");
517 
518         indentPrintln("// code to get by specific type \"typeKey01\" ");
519         indentPrintln(initLower(dtoObjectName) + "Ids = testService.get" + dtoObjectName + "IdsByType (\"typeKey_Updated\", contextInfo);");
520         // indentPrintln(initLower(dtoObjectName) + "Ids = " + getMethodCallAsString ("get" + dtoObjectName + "IdsByType", "// INSERT CODE TO GET BY SPECIFIC TYPE \"typeKey01\" HERE", MethodType.GET_IDS_BY_TYPE));
521         indentPrintln("");
522 
523         indentPrintln("assertEquals(1, " + initLower(dtoObjectName) + "Ids.size());");
524         indentPrintln("assertEquals(alphaDTO.getId(), " + initLower(dtoObjectName) + "Ids.get(0));");
525         indentPrintln("");
526         indentPrintln("// test get by other type");
527 
528         indentPrintln("// code to get by specific type \"typeKeyBeta\" ");
529         indentPrintln(initLower(dtoObjectName) + "Ids = testService.get" + dtoObjectName + "IdsByType (\"typeKeyBeta\", contextInfo);");
530         // indentPrintln(initLower(dtoObjectName) + "Ids = " + getMethodCallAsString ("get" + dtoObjectName + "IdsByType", "// INSERT CODE TO GET BY SPECIFIC TYPE \"typeKeyBeta\" HERE", MethodType.GET_IDS_BY_TYPE));
531         indentPrintln("");
532 
533         indentPrintln("assertEquals(1, " + initLower(dtoObjectName) + "Ids.size());");
534         indentPrintln("assertEquals(betaDTO.getId(), " + initLower(dtoObjectName) + "Ids.get(0));");
535         indentPrintln("");
536     }
537 
538     /**
539      * Write the 'delete' portion.
540      */
541     public void writeTestDelete (String dtoObjectName, List<MessageStructure> messageStructures) {
542         indentPrintDecoratedComment("test delete");
543         indentPrintln("");
544 
545         indentPrintln("StatusInfo status = " + getMethodCallAsString ("delete" + dtoObjectName, "null; // TODO INSERT CODE TO DELETE RECORD", MethodType.DELETE, dtoObjectName, "actual"));
546         indentPrintln("");
547 
548         indentPrintln("assertNotNull(status);");
549         indentPrintln("assertTrue(status.getIsSuccess());");
550         indentPrintln("try");
551         openBrace();
552         incrementIndent();
553         indentPrintln(dtoObjectName + "Info record = " + getMethodCallAsString ("get" + dtoObjectName, "null; // TODO INSERT CODE TO RETRIEVE RECENTLY DELETED RECORD", MethodType.GET_BY_ID, dtoObjectName, "actual"));
554         indentPrintln("fail(\"Did not receive DoesNotExistException when attempting to get already-deleted entity\");");
555         decrementIndent();
556         closeBrace();
557         indentPrintln("catch (DoesNotExistException dnee)");
558         openBrace();
559         incrementIndent();
560         indentPrintln("// expected");
561         decrementIndent();
562         closeBrace();
563         indentPrintln("");
564     }
565 
566     /**
567      * Writes out a decorated comment.
568      */
569     public void indentPrintDecoratedComment (String label) {
570         indentPrintDecoratedComment(label, "-", 37);
571 /*
572         indentPrintln("// -------------------------------------");
573         indentPrintln("// " + label);
574         indentPrintln("// -------------------------------------");
575 */
576     }
577 
578     /**
579      * Writes out a decorated comment, with the decoration string passed in.
580      */
581     public void indentPrintDecoratedComment (String label, String decorChar, int decorLength) {
582         String decorPattern = "";
583         for (int i=0; i<decorLength; i++) { decorPattern += decorChar; }
584         indentPrintln("// " + decorPattern);
585         indentPrintln("// " + label);
586         indentPrintln("// " + decorPattern);
587     }
588 
589     /**
590      * Gets the method with the name.
591      */
592     private ServiceMethod getServiceMethod (String methodName) throws OperationsException {
593         for (ServiceMethod method : methods) {
594             if (method.getName().equals(methodName)) return method;
595         }
596         throw new OperationsException("Method " + methodName + " not found!");
597     }
598 
599     /**
600      * Gets the string to print to call a method with the given name.
601      * @param dtoObjectName 
602      */
603     private String getMethodCallAsString (String builtUpMethodName, String errorCode, MethodType methodType, String dtoObjectName) {
604         return getMethodCallAsString (builtUpMethodName, errorCode, methodType, dtoObjectName, null);
605     }
606 
607     /**
608      * Gets the string to print to call a method with the given name.
609      */
610     private String getMethodCallAsString (String builtUpMethodName, String errorCode, MethodType methodType, String dtoName, String dtoNameReplacement) {
611         try {
612             ServiceMethod method = getServiceMethod(builtUpMethodName);
613             String methodCallStr = "";
614             methodCallStr += method.getName() + " (";
615             String comma = " ";
616             for (ServiceMethodParameter param : method.getParameters()) {
617                 methodCallStr += comma;
618                 if (dtoName!=null && dtoNameReplacement!=null) {
619                     if ((dtoName + "Info").toLowerCase().equals(param.getName().toLowerCase())) {
620                         methodCallStr += dtoNameReplacement;
621                     }
622                     else if (param.getName().endsWith("TypeKey")) {
623                         methodCallStr += dtoNameReplacement + ".getTypeKey()";
624                     }
625                     else if (param.getName().endsWith("Id")) {
626                         methodCallStr += dtoNameReplacement + ".getId()";
627                     }
628                     else {
629                         methodCallStr += param.getName();
630                     }
631                 } else {
632                     methodCallStr += param.getName();
633                 }
634                 comma = ", ";
635             }
636             methodCallStr += ");";
637             return "testService." + methodCallStr;
638         } catch (Exception e) {
639             
640             if (dtoName != null)
641                 log.error("dtoName = " + dtoName + ", methodName = " + builtUpMethodName + ", errorCode = " + errorCode); 
642             else
643                 log.error("dtoName = unknown, methodName = " + builtUpMethodName + ", errorCode = " + errorCode);
644             
645             return errorCode;
646         }
647 
648     }
649 
650     /**
651      * Writes the section to set fields specific to this dto for testCreate section.
652      */
653     public void writetestCrudXXX_setDTOFieldsForTestCreate(String dtoObjectName, List<MessageStructure> messageStructures) {
654         indentPrintln("/*");
655         incrementIndent();
656         indentPrintln("A method to set the fields for a " + dtoObjectName  + " in a 'test create' section prior to calling the 'create' operation.");
657         decrementIndent();
658         indentPrintln("*/");
659         indentPrintln("public abstract void testCrud" + dtoObjectName + "_setDTOFieldsForTestCreate(" + dtoObjectName + "Info expected);");
660         indentPrintln("");
661     }
662 
663     /**
664      * Writes the section to test fields specific to this dto for testCreate and testUpdate sections.
665      */
666     public void writetestCrudXXX_testDTOFieldsForTestCreateUpdate(String dtoObjectName, List<MessageStructure> messageStructures) {
667         indentPrintln("/*");
668         incrementIndent();
669         indentPrintln("A method to test the fields for a " + dtoObjectName  + ". This is called after:");
670         indentPrintln("- creating a DTO, where actual is the DTO returned by the create operation, and expected is the dto passed in to the create operation");
671         indentPrintln("- reading a DTO after creating it, and actual is the read DTO, and expected is the dto that was created");
672         indentPrintln("- updating a DTO, where actual is DTO returned by the update operation, and expected is the dto that was passed in to the update operation");
673         decrementIndent();
674         indentPrintln("*/");
675         indentPrintln("public abstract void testCrud" + dtoObjectName + "_testDTOFieldsForTestCreateUpdate(" + dtoObjectName + "Info expected, " + dtoObjectName + "Info actual);");
676         indentPrintln("");
677     }
678 
679 
680     /**
681      * Writes the section to set fields specific to this dto for testUpdate sections.
682      */
683     public void writetestCrudXXX_setDTOFieldsForTestUpdate(String dtoObjectName, List<MessageStructure> messageStructures) {
684         indentPrintln("/*");
685         incrementIndent();
686         indentPrintln("A method to set the fields for a " + dtoObjectName + " in a 'test update' section prior to calling the 'update' operation.");
687         decrementIndent();
688         indentPrintln("*/");
689         indentPrintln("public abstract void testCrud" + dtoObjectName + "_setDTOFieldsForTestUpdate(" + dtoObjectName + "Info expected);");
690         indentPrintln("");
691     }
692 
693     /**
694      * Writes the section to test fields specific to this dto for testReadAfterUpdate sections.
695      */
696     public void writetestCrudXXX_testDTOFieldsForTestReadAfterUpdate(String dtoObjectName, List<MessageStructure> messageStructures) {
697         indentPrintln("/*");
698         incrementIndent();
699         indentPrintln("A method to test the fields for a " + dtoObjectName  + " after an update operation, followed by a read operation,");
700         indentPrintln("where actual is the DTO returned by the read operation, and expected is the dto returned by the update operation.");
701         decrementIndent();
702         indentPrintln("*/");
703         indentPrintln("public abstract void testCrud" + dtoObjectName + "_testDTOFieldsForTestReadAfterUpdate(" + dtoObjectName + "Info expected, " + dtoObjectName + "Info actual);");
704         indentPrintln("");
705     }
706 
707     /**
708      * Writes the section to set fields specific to this dto for testReadAfterUpdate sections.
709      */
710     public void writetestCrudXXX_setDTOFieldsForTestReadAfterUpdate(String dtoObjectName, List<MessageStructure> messageStructures) {
711         indentPrintln("/*");
712         incrementIndent();
713         indentPrintln("A method to set the fields for a " + dtoObjectName  + " in the 'test read after update' section.");
714         indentPrintln("This dto is another (second) dto object being created for other tests.");
715         decrementIndent();
716         indentPrintln("*/");
717         indentPrintln("public abstract void testCrud" + dtoObjectName + "_setDTOFieldsForTestReadAfterUpdate(" + dtoObjectName + "Info expected);");
718         indentPrintln("");
719     }
720 
721     /**
722      * Given a method type, returns true if this method is tested as part of CRUD operations
723      * tested by the base test conformance class.
724      */
725         protected boolean isServiceMethodTestedAsPartofCrudInBaseConformanceTest (ServiceMethod method) {
726             MethodType methodType = calcMethodType(method);
727             if ((MethodType.CREATE.equals(methodType))
728                 || (MethodType.UPDATE.equals(methodType))
729                 || (MethodType.DELETE.equals(methodType))
730                 || (MethodType.GET_BY_ID.equals(methodType))
731                 || (MethodType.GET_BY_IDS.equals(methodType))
732                 || (MethodType.GET_IDS_BY_TYPE.equals(methodType))
733                 || (MethodType.GET_TYPE.equals(methodType))
734                 || (MethodType.GET_TYPES.equals(methodType))
735                 || (MethodType.GET_IDS_BY_TYPE.equals(methodType))
736                 ) {
737             return true;
738         } else {
739             return false;
740         }
741 
742     }
743 
744     /**
745      * Gets a list of all the DTO names that are part of this service.
746      */
747     protected List<String> calcNamesOfDTOsWithCrudManagedByService() {
748         List<String> dtoObjectNames = new ArrayList<String>();
749         for (ServiceMethod method: methods) {
750             // I am assuming all the DTOs will have a createXXX method.
751             if (MethodType.CREATE.equals (calcMethodType(method))) {
752                 String objectName = calcObjectName(method);
753                 // check if this is a valid info object
754                 if (finder.findXmlType(objectName + "Info")!=null) {
755                     dtoObjectNames.add(objectName);
756                 }
757 /*
758                 for (XmlType xmlType: model.getXmlTypes()) {
759                     if (xmlType.getName().equals(objectName + "Info")) {
760                         dtoObjectNames.add(objectName);
761                         break;
762                     }
763                 }
764 */
765             }
766         }
767         return dtoObjectNames;
768     }
769 
770     /**
771      * Does the importsAdd for all files required for testing
772      */
773     protected void doTestImportsAdd() {
774         // kuali imports
775         importsAdd("org.kuali.student.common.test.util.IdEntityTester");
776         importsAdd("org.kuali.student.common.test.util.KeyEntityTester");
777         importsAdd("org.kuali.student.r2.common.dto.ContextInfo");
778         importsAdd("org.kuali.student.r2.common.dto.IdEntityInfo");
779         importsAdd("org.kuali.student.r2.common.dto.StatusInfo");
780         importsAdd("org.kuali.student.r2.common.dto.TypeStateEntityInfo");
781         importsAdd("org.kuali.student.r2.common.util.RichTextHelper");
782         importsAdd("org.kuali.student.r2.core.organization.service.impl.lib.AttributeTester");
783         importsAdd("org.kuali.student.r2.core.organization.service.impl.lib.MetaTester");
784 
785         // import all the dto
786         // for each DTO, write the testCRUD
787         for (String dtoObjectName : dtoObjectNamesWithCrud) {
788             try { importsAdd(finder.findXmlType(dtoObjectName + "Info").getJavaPackage() + "." + dtoObjectName + "Info"); }
789             catch (Exception ignored) {}
790         }
791 
792         // exceptions
793         importsAdd("org.kuali.student.r2.common.exceptions.DataValidationErrorException");
794         importsAdd("org.kuali.student.r2.common.exceptions.DependentObjectsExistException");
795         importsAdd("org.kuali.student.r2.common.exceptions.DoesNotExistException");
796         importsAdd("org.kuali.student.r2.common.exceptions.InvalidParameterException");
797         importsAdd("org.kuali.student.r2.common.exceptions.MissingParameterException");
798         importsAdd("org.kuali.student.r2.common.exceptions.OperationFailedException");
799         importsAdd("org.kuali.student.r2.common.exceptions.PermissionDeniedException");
800         importsAdd("org.kuali.student.r2.common.exceptions.ReadOnlyException");
801         importsAdd("org.kuali.student.r2.common.exceptions.VersionMismatchException");
802 
803         // java imports
804         importsAdd("org.springframework.test.context.ContextConfiguration");
805         importsAdd("org.springframework.test.context.junit4.SpringJUnit4ClassRunner");
806         importsAdd("org.junit.Assert");
807         importsAdd("org.junit.Before");
808         importsAdd("org.junit.Test");
809         importsAdd("org.junit.runner.RunWith");
810         importsAdd("static org.junit.Assert.assertEquals");
811         importsAdd("static org.junit.Assert.assertFalse");
812         importsAdd("static org.junit.Assert.assertNotNull");
813         importsAdd("static org.junit.Assert.assertNull");
814         importsAdd("static org.junit.Assert.assertTrue");
815         importsAdd("static org.junit.Assert.fail");
816         importsAdd("javax.annotation.Resource");
817         importsAdd("java.util.ArrayList");
818         importsAdd("java.util.List");
819     }
820 }