View Javadoc

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