View Javadoc
1   /**
2    * Copyright 2004-2014 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         String testerName = null;
320         String keyOrId = null;
321         if (finder.findMessageStructure(dtoObjectName + "Info", "id") != null) {
322             keyOrId = "Id";
323             if (finder.findMessageStructure(dtoObjectName + "Info", "name") != null) {
324                 testerName = "IdEntityTester";
325             } else if (finder.findMessageStructure(dtoObjectName + "Info", "effectiveDate") != null) {
326                 testerName = "RelationshipTester";
327             } else {
328                 testerName = "IdNamelessEntityTester";
329                 // should also detect relationships
330             }
331         } else if (finder.findMessageStructure(dtoObjectName + "Info", "key") != null) {
332             keyOrId = "Key";
333             testerName = "KeyEntityTester";
334         }
335 
336         // indentPrintln("if (actual.getClass().isAssignableFrom(IdEntityInfo.class))");
337         if (testerName !=null) {
338             // openBrace();
339             // incrementIndent();
340             indentPrintln("assertNotNull(actual.get" + keyOrId + "());");
341             indentPrintln("new " + testerName + "().check(expected, actual);");
342             // decrementIndent();
343             // closeBrace();
344         }
345 
346         indentPrintln("");
347         indentPrintln("// METHOD TO TEST DTO FIELDS HERE FOR TEST CREATE");
348         indentPrintln("testCrud" + dtoObjectName + "_testDTOFieldsForTestCreateUpdate (expected, actual);");
349         indentPrintln("");
350 
351         indentPrintln("new AttributeTester().check(expected.getAttributes(), actual.getAttributes());");
352         indentPrintln("new MetaTester().checkAfterCreate(actual.getMeta());");
353         indentPrintln("");
354 
355         indentPrintDecoratedComment("test read");
356         indentPrintln("expected = actual;");
357         indentPrintln("actual = " + getMethodCallAsString ("get" + dtoObjectName, "null; // TODO INSERT CODE TO GET actual HERE BY CALLING SERVICE OP", MethodType.GET_BY_ID, dtoObjectName, "actual"));
358 
359         if (testerName !=null) {
360             indentPrintln("assertEquals(expected.get" + keyOrId + "(), actual.get" + keyOrId + "());");
361             indentPrintln("new " + testerName + "().check(expected, actual);");
362         }
363 
364         indentPrintln("");
365 
366         indentPrintln("// INSERT CODE FOR TESTING MORE DTO FIELDS HERE");
367         indentPrintln("testCrud" + dtoObjectName + "_testDTOFieldsForTestCreateUpdate (expected, actual);");
368         indentPrintln("");
369 
370         indentPrintln("new AttributeTester().check(expected.getAttributes(), actual.getAttributes());");
371         indentPrintln("new MetaTester().checkAfterGet(expected.getMeta(), actual.getMeta());");
372         indentPrintln("");
373     }
374 
375     /**
376      * Write the 'test update' portion.
377      */
378     public void writeTestUpdate (String dtoObjectName, List<MessageStructure> messageStructures) {
379         indentPrintDecoratedComment("test update");
380         indentPrintln(dtoObjectName + "Info original = new " + dtoObjectName + "Info (actual);");
381         indentPrintln("expected = new " + dtoObjectName + "Info (actual);");
382         
383         // indentPrintln("expected.setName(expected.getName() + \" updated\");");
384         // indentPrintln("expected.setDescr(new RichTextHelper().fromPlain(expected.getDescr().getPlain() + \"_Updated\"));");
385         indentPrintln("");
386         // indentPrintln("if (expected.getClass().isAssignableFrom(TypeStateEntityInfo.class))");
387         if (finder.findMessageStructure(dtoObjectName + "Info", "stateKey")!=null) {
388             // openBrace();
389             // incrementIndent();
390             // indentPrintln("TypeStateEntityInfo expectedTS = (TypeStateEntityInfo) expected;");
391             indentPrintln("expected.setStateKey(expected.getState() + \"_Updated\");");
392             // decrementIndent();
393             // closeBrace();
394         }
395         indentPrintln("");
396         indentPrintln("// METHOD TO INSERT CODE TO UPDATE DTO FIELDS HERE");
397         indentPrintln("testCrud" + dtoObjectName + "_setDTOFieldsForTestUpdate (expected);");
398         indentPrintln("");
399         indentPrintln("new AttributeTester().delete1Update1Add1ForUpdate(expected.getAttributes());");
400 
401         indentPrintln("// code to update");
402         indentPrintln("actual = " + getMethodCallAsString ("update" + dtoObjectName, "= null; // TODO INSERT CODE TO CALL UPDATE SERVICE OP HERE", MethodType.UPDATE, dtoObjectName, "expected"));
403         indentPrintln("");
404         
405         String testerName = null;
406         String keyOrId = null;
407         if (finder.findMessageStructure(dtoObjectName + "Info", "id") != null) {
408             keyOrId = "Id";
409             if (finder.findMessageStructure(dtoObjectName + "Info", "name") != null) {
410                 testerName = "IdEntityTester";
411             } else if (finder.findMessageStructure(dtoObjectName + "Info", "effectiveDate") != null) {
412                 testerName = "RelationshipTester";
413             } else {
414                 testerName = "IdNamelessEntityTester";
415             }
416         } else if (finder.findMessageStructure(dtoObjectName + "Info", "key") != null) {
417             keyOrId = "Key";
418             testerName = "KeyEntityTester";
419         }
420         if (testerName !=null) {
421             indentPrintln("assertEquals(expected.get" + keyOrId + "(), actual.get" + keyOrId + "());");
422             indentPrintln("new " + testerName + "().check(expected, actual);");
423         }
424         indentPrintln("");
425         indentPrintln("// METHOD TO INSERT CODE FOR TESTING DTO FIELDS HERE");
426         indentPrintln("testCrud" + dtoObjectName + "_testDTOFieldsForTestCreateUpdate (expected, actual);");
427         indentPrintln("");
428         indentPrintln("new AttributeTester().check(expected.getAttributes(), actual.getAttributes());");
429         indentPrintln("new MetaTester().checkAfterUpdate(expected.getMeta(), actual.getMeta());");
430         indentPrintln("");
431         
432         indentPrintln("// Test that VersionMissmatchException's are being detected");
433         indentPrintln("boolean exception = false;");        
434         indentPrintln("try {");
435         indent(getOut(), ' ');
436         
437         indentPrintln(getMethodCallAsString ("update" + dtoObjectName, "= null; // TODO INSERT CODE TO CALL UPDATE SERVICE OP HERE", MethodType.UPDATE, dtoObjectName, "original"));
438         indentPrintln("}");
439         indentPrintln("catch (VersionMismatchException e) { "); 
440         indent(getOut(), ' ');
441         indentPrint("exception = true;");
442         indentPrintln("}");
443         indentPrintln("");
444         
445         indentPrintln("Assert.assertTrue(\"VersionMissmatchException was not detected!\", exception);");
446         indentPrintln("");
447         
448     }
449 
450     /**
451      * Write the 'read after update' portion.
452      */
453     public void writeTestReadAfterUpdate (String dtoObjectName, List<MessageStructure> messageStructures) {
454         indentPrintDecoratedComment("test read after update");
455         indentPrintln("");
456         indentPrintln("expected = actual;");
457 
458         String testerName = null;
459         String keyOrId = null;
460         if (finder.findMessageStructure(dtoObjectName + "Info", "id") != null) {
461             keyOrId = "Id";
462             if (finder.findMessageStructure(dtoObjectName + "Info", "name") != null) {
463                 testerName = "IdEntityTester";
464             } else if (finder.findMessageStructure(dtoObjectName + "Info", "effectiveDate") != null) {
465                 testerName = "RelationshipTester";
466             } else {
467                 testerName = "IdNamelessEntityTester";
468             }
469         } else if (finder.findMessageStructure(dtoObjectName + "Info", "key") != null) {
470             keyOrId = "Key";
471             testerName = "KeyEntityTester";
472         }
473         
474         indentPrintln("// code to get actual");
475         indentPrintln("actual = " + getMethodCallAsString ("get" + dtoObjectName, "null; // TODO INSERT CODE TO GET actual HERE BY CALLING SERVICE OP", MethodType.GET_BY_ID, dtoObjectName, "actual"));
476         indentPrintln("");
477         
478         
479         if (testerName !=null) {
480             indentPrintln("assertEquals(expected.get" + keyOrId + "(), actual.get" + keyOrId + "());");
481             indentPrintln("new " + testerName + "().check(expected, actual);");
482         }
483         indentPrintln("");
484         indentPrintln("// INSERT METHOD CODE FOR TESTING DTO FIELDS HERE");
485         indentPrintln("testCrud" + dtoObjectName + "_testDTOFieldsForTestReadAfterUpdate (expected, actual);");
486         indentPrintln("");
487         indentPrintln("new AttributeTester().check(expected.getAttributes(), actual.getAttributes());");
488         indentPrintln("new MetaTester().checkAfterGet(expected.getMeta(), actual.getMeta());");
489         indentPrintln("");
490         indentPrintln(dtoObjectName + "Info alphaDTO = actual;");
491         indentPrintln("");
492         indentPrintln("// create a 2nd DTO");
493         indentPrintln(dtoObjectName + "Info betaDTO = new " + dtoObjectName + "Info ();");
494         // indentPrintln("betaDTO.setName(\"Beta entity name\");");
495         // indentPrintln("betaDTO.setDescr(new RichTextHelper().fromPlain(\"Beta entity description\"));");
496         indentPrintln("");
497         indentPrintln("// METHOD TO INSERT CODE TO SET MORE DTO FIELDS HERE");
498         indentPrintln("testCrud" + dtoObjectName + "_setDTOFieldsForTestReadAfterUpdate (betaDTO);");
499         indentPrintln("");
500         // indentPrintln("if (betaDTO.getClass().isAssignableFrom(TypeStateEntityInfo.class))");
501         if (finder.findMessageStructure(dtoObjectName + "Info", "typeKey")!=null) {
502             // openBrace();
503             // incrementIndent();
504             //indentPrintln("TypeStateEntityInfo betaDTOTS = (TypeStateEntityInfo) betaDTO;");
505             indentPrintln("betaDTO.setTypeKey(\"typeKeyBeta\");");
506             // decrementIndent();
507             // closeBrace();
508         }
509         if (finder.findMessageStructure(dtoObjectName + "Info", "stateKey")!=null) {
510             indentPrintln("betaDTO.setStateKey(\"stateKeyBeta\");");
511         }
512         indentPrintln("betaDTO = " + getMethodCallAsString("create" + dtoObjectName, "null; // TODO INSERT CODE TO CREATE betaDTO", MethodType.CREATE, dtoObjectName, "betaDTO"));
513 
514         indentPrintln("");
515         indentPrintDecoratedComment("test bulk get with no ids supplied");
516         indentPrintln("");
517         indentPrintln("List<String> " + initLower(dtoObjectName) + "Ids = new ArrayList<String>();");
518 
519         indentPrintln("// code to get DTO by Ids");
520         indentPrintln("List<" + dtoObjectName + "Info> records = " + getMethodCallAsString ("get" + dtoObjectName + "sByIds", "null; // TODO INSERT CODE TO GET DTO BY IDS", MethodType.GET_BY_IDS, dtoObjectName));
521         indentPrintln("");
522 
523         indentPrintln("assertEquals(" + initLower(dtoObjectName) + "Ids.size(), records.size());");
524         indentPrintln("assertEquals(0, " + initLower(dtoObjectName) + "Ids.size());");
525         indentPrintln("");
526         indentPrintDecoratedComment("test bulk get");
527         indentPrintln(initLower(dtoObjectName) + "Ids = new ArrayList<String>();");
528         indentPrintln(initLower(dtoObjectName) + "Ids.add(alphaDTO.getId());");
529         indentPrintln(initLower(dtoObjectName) + "Ids.add(betaDTO.getId());");
530 
531         indentPrintln("// code to get DTO by Ids");
532         indentPrintln("records = " + getMethodCallAsString ("get" + dtoObjectName + "sByIds", "null; // TODO INSERT CODE TO GET DTO BY IDS", MethodType.GET_BY_IDS, dtoObjectName));
533         indentPrintln("");
534 
535         indentPrintln("assertEquals(" + initLower(dtoObjectName) + "Ids.size(), records.size());");
536         indentPrintln("for (" + dtoObjectName + "Info record : records)");
537         openBrace();
538         incrementIndent();
539         indentPrintln("if (!" + initLower(dtoObjectName) + "Ids.remove(record.getId()))");
540         openBrace();
541         incrementIndent();
542         indentPrintln("fail(record.getId());");
543         decrementIndent();
544         closeBrace();
545         decrementIndent();
546         closeBrace();
547         indentPrintln("assertEquals(0, " + initLower(dtoObjectName) + "Ids.size());");
548         indentPrintln("");
549         indentPrintDecoratedComment("test get by type");
550 
551         indentPrintln("// code to get by specific type \"typeKey01\" ");
552         indentPrintln(initLower(dtoObjectName) + "Ids = testService.get" + dtoObjectName + "IdsByType (\"typeKey_Updated\", contextInfo);");
553         // indentPrintln(initLower(dtoObjectName) + "Ids = " + getMethodCallAsString ("get" + dtoObjectName + "IdsByType", "// INSERT CODE TO GET BY SPECIFIC TYPE \"typeKey01\" HERE", MethodType.GET_IDS_BY_TYPE));
554         indentPrintln("");
555 
556         indentPrintln("assertEquals(1, " + initLower(dtoObjectName) + "Ids.size());");
557         indentPrintln("assertEquals(alphaDTO.getId(), " + initLower(dtoObjectName) + "Ids.get(0));");
558         indentPrintln("");
559         indentPrintln("// test get by other type");
560 
561         indentPrintln("// code to get by specific type \"typeKeyBeta\" ");
562         indentPrintln(initLower(dtoObjectName) + "Ids = testService.get" + dtoObjectName + "IdsByType (\"typeKeyBeta\", contextInfo);");
563         // indentPrintln(initLower(dtoObjectName) + "Ids = " + getMethodCallAsString ("get" + dtoObjectName + "IdsByType", "// INSERT CODE TO GET BY SPECIFIC TYPE \"typeKeyBeta\" HERE", MethodType.GET_IDS_BY_TYPE));
564         indentPrintln("");
565 
566         indentPrintln("assertEquals(1, " + initLower(dtoObjectName) + "Ids.size());");
567         indentPrintln("assertEquals(betaDTO.getId(), " + initLower(dtoObjectName) + "Ids.get(0));");
568         indentPrintln("");
569     }
570 
571     /**
572      * Write the 'delete' portion.
573      */
574     public void writeTestDelete (String dtoObjectName, List<MessageStructure> messageStructures) {
575         indentPrintDecoratedComment("test delete");
576         indentPrintln("");
577 
578         indentPrintln("StatusInfo status = " + getMethodCallAsString ("delete" + dtoObjectName, "null; // TODO INSERT CODE TO DELETE RECORD", MethodType.DELETE, dtoObjectName, "actual"));
579         indentPrintln("");
580 
581         indentPrintln("assertNotNull(status);");
582         indentPrintln("assertTrue(status.getIsSuccess());");
583         indentPrintln("try");
584         openBrace();
585         incrementIndent();
586         indentPrintln(dtoObjectName + "Info record = " + getMethodCallAsString ("get" + dtoObjectName, "null; // TODO INSERT CODE TO RETRIEVE RECENTLY DELETED RECORD", MethodType.GET_BY_ID, dtoObjectName, "actual"));
587         indentPrintln("fail(\"Did not receive DoesNotExistException when attempting to get already-deleted entity\");");
588         decrementIndent();
589         closeBrace();
590         indentPrintln("catch (DoesNotExistException dnee)");
591         openBrace();
592         incrementIndent();
593         indentPrintln("// expected");
594         decrementIndent();
595         closeBrace();
596         indentPrintln("");
597     }
598 
599     /**
600      * Writes out a decorated comment.
601      */
602     public void indentPrintDecoratedComment (String label) {
603         indentPrintDecoratedComment(label, "-", 37);
604 /*
605         indentPrintln("// -------------------------------------");
606         indentPrintln("// " + label);
607         indentPrintln("// -------------------------------------");
608 */
609     }
610 
611     /**
612      * Writes out a decorated comment, with the decoration string passed in.
613      */
614     public void indentPrintDecoratedComment (String label, String decorChar, int decorLength) {
615         String decorPattern = "";
616         for (int i=0; i<decorLength; i++) { decorPattern += decorChar; }
617         indentPrintln("// " + decorPattern);
618         indentPrintln("// " + label);
619         indentPrintln("// " + decorPattern);
620     }
621 
622     /**
623      * Gets the method with the name.
624      */
625     private ServiceMethod getServiceMethod (String methodName) throws OperationsException {
626         for (ServiceMethod method : methods) {
627             if (method.getName().equals(methodName)) return method;
628         }
629         throw new OperationsException("Method " + methodName + " not found!");
630     }
631 
632     /**
633      * Gets the string to print to call a method with the given name.
634      * @param dtoObjectName 
635      */
636     private String getMethodCallAsString (String builtUpMethodName, String errorCode, MethodType methodType, String dtoObjectName) {
637         return getMethodCallAsString (builtUpMethodName, errorCode, methodType, dtoObjectName, null);
638     }
639 
640     /**
641      * Gets the string to print to call a method with the given name.
642      */
643     private String getMethodCallAsString (String builtUpMethodName, String errorCode, MethodType methodType, String dtoName, String dtoNameReplacement) {
644         try {
645             ServiceMethod method = getServiceMethod(builtUpMethodName);
646             String methodCallStr = "";
647             methodCallStr += method.getName() + " (";
648             String comma = " ";
649             for (ServiceMethodParameter param : method.getParameters()) {
650                 methodCallStr += comma;
651                 if (dtoName!=null && dtoNameReplacement!=null) {
652                     if ((dtoName + "Info").toLowerCase().equals(param.getName().toLowerCase())) {
653                         methodCallStr += dtoNameReplacement;
654                     }
655                     else if (param.getName().endsWith("TypeKey")) {
656                         methodCallStr += dtoNameReplacement + ".getTypeKey()";
657                     }
658                     else if (param.getName().endsWith("Id")) {
659                         methodCallStr += dtoNameReplacement + ".getId()";
660                     }
661                     else {
662                         methodCallStr += param.getName();
663                     }
664                 } else {
665                     methodCallStr += param.getName();
666                 }
667                 comma = ", ";
668             }
669             methodCallStr += ");";
670             return "testService." + methodCallStr;
671         } catch (Exception e) {
672             
673             if (dtoName != null)
674                 log.error("dtoName = " + dtoName + ", methodName = " + builtUpMethodName + ", errorCode = " + errorCode); 
675             else
676                 log.error("dtoName = unknown, methodName = " + builtUpMethodName + ", errorCode = " + errorCode);
677             
678             return errorCode;
679         }
680 
681     }
682 
683     /**
684      * Writes the section to set fields specific to this dto for testCreate section.
685      */
686     public void writetestCrudXXX_setDTOFieldsForTestCreate(String dtoObjectName, List<MessageStructure> messageStructures) {
687         indentPrintln("/*");
688         incrementIndent();
689         indentPrintln("A method to set the fields for a " + dtoObjectName  + " in a 'test create' section prior to calling the 'create' operation.");
690         decrementIndent();
691         indentPrintln("*/");
692         indentPrintln("public abstract void testCrud" + dtoObjectName + "_setDTOFieldsForTestCreate(" + dtoObjectName + "Info expected);");
693         indentPrintln("");
694     }
695 
696     /**
697      * Writes the section to test fields specific to this dto for testCreate and testUpdate sections.
698      */
699     public void writetestCrudXXX_testDTOFieldsForTestCreateUpdate(String dtoObjectName, List<MessageStructure> messageStructures) {
700         indentPrintln("/*");
701         incrementIndent();
702         indentPrintln("A method to test the fields for a " + dtoObjectName  + ". This is called after:");
703         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");
704         indentPrintln("- reading a DTO after creating it, and actual is the read DTO, and expected is the dto that was created");
705         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");
706         decrementIndent();
707         indentPrintln("*/");
708         indentPrintln("public abstract void testCrud" + dtoObjectName + "_testDTOFieldsForTestCreateUpdate(" + dtoObjectName + "Info expected, " + dtoObjectName + "Info actual);");
709         indentPrintln("");
710     }
711 
712 
713     /**
714      * Writes the section to set fields specific to this dto for testUpdate sections.
715      */
716     public void writetestCrudXXX_setDTOFieldsForTestUpdate(String dtoObjectName, List<MessageStructure> messageStructures) {
717         indentPrintln("/*");
718         incrementIndent();
719         indentPrintln("A method to set the fields for a " + dtoObjectName + " in a 'test update' section prior to calling the 'update' operation.");
720         decrementIndent();
721         indentPrintln("*/");
722         indentPrintln("public abstract void testCrud" + dtoObjectName + "_setDTOFieldsForTestUpdate(" + dtoObjectName + "Info expected);");
723         indentPrintln("");
724     }
725 
726     /**
727      * Writes the section to test fields specific to this dto for testReadAfterUpdate sections.
728      */
729     public void writetestCrudXXX_testDTOFieldsForTestReadAfterUpdate(String dtoObjectName, List<MessageStructure> messageStructures) {
730         indentPrintln("/*");
731         incrementIndent();
732         indentPrintln("A method to test the fields for a " + dtoObjectName  + " after an update operation, followed by a read operation,");
733         indentPrintln("where actual is the DTO returned by the read operation, and expected is the dto returned by the update operation.");
734         decrementIndent();
735         indentPrintln("*/");
736         indentPrintln("public abstract void testCrud" + dtoObjectName + "_testDTOFieldsForTestReadAfterUpdate(" + dtoObjectName + "Info expected, " + dtoObjectName + "Info actual);");
737         indentPrintln("");
738     }
739 
740     /**
741      * Writes the section to set fields specific to this dto for testReadAfterUpdate sections.
742      */
743     public void writetestCrudXXX_setDTOFieldsForTestReadAfterUpdate(String dtoObjectName, List<MessageStructure> messageStructures) {
744         indentPrintln("/*");
745         incrementIndent();
746         indentPrintln("A method to set the fields for a " + dtoObjectName  + " in the 'test read after update' section.");
747         indentPrintln("This dto is another (second) dto object being created for other tests.");
748         decrementIndent();
749         indentPrintln("*/");
750         indentPrintln("public abstract void testCrud" + dtoObjectName + "_setDTOFieldsForTestReadAfterUpdate(" + dtoObjectName + "Info expected);");
751         indentPrintln("");
752     }
753 
754     /**
755      * Given a method type, returns true if this method is tested as part of CRUD operations
756      * tested by the base test conformance class.
757      */
758         protected boolean isServiceMethodTestedAsPartofCrudInBaseConformanceTest (ServiceMethod method) {
759             MethodType methodType = calcMethodType(method);
760             if ((MethodType.CREATE.equals(methodType))
761                 || (MethodType.UPDATE.equals(methodType))
762                 || (MethodType.DELETE.equals(methodType))
763                 || (MethodType.GET_BY_ID.equals(methodType))
764                 || (MethodType.GET_BY_IDS.equals(methodType))
765                 || (MethodType.GET_IDS_BY_TYPE.equals(methodType))
766                 || (MethodType.GET_TYPE.equals(methodType))
767                 || (MethodType.GET_TYPES.equals(methodType))
768                 || (MethodType.GET_IDS_BY_TYPE.equals(methodType))
769                 ) {
770             return true;
771         } else {
772             return false;
773         }
774 
775     }
776 
777     /**
778      * Gets a list of all the DTO names that are part of this service.
779      */
780     protected List<String> calcNamesOfDTOsWithCrudManagedByService() {
781         List<String> dtoObjectNames = new ArrayList<String>();
782         for (ServiceMethod method: methods) {
783             // I am assuming all the DTOs will have a createXXX method.
784             if (MethodType.CREATE.equals (calcMethodType(method))) {
785                 String objectName = calcObjectName(method);
786                 // check if this is a valid info object
787                 if (finder.findXmlType(objectName + "Info")!=null) {
788                     dtoObjectNames.add(objectName);
789                 }
790 /*
791                 for (XmlType xmlType: model.getXmlTypes()) {
792                     if (xmlType.getName().equals(objectName + "Info")) {
793                         dtoObjectNames.add(objectName);
794                         break;
795                     }
796                 }
797 */
798             }
799         }
800         return dtoObjectNames;
801     }
802 
803     /**
804      * Does the importsAdd for all files required for testing
805      */
806     protected void doTestImportsAdd() {
807         // kuali imports
808         importsAdd("org.kuali.student.common.test.util.IdEntityTester");
809         importsAdd("org.kuali.student.common.test.util.KeyEntityTester");
810         importsAdd("org.kuali.student.r2.common.dto.ContextInfo");
811         importsAdd("org.kuali.student.r2.common.dto.IdEntityInfo");
812         importsAdd("org.kuali.student.r2.common.dto.StatusInfo");
813         importsAdd("org.kuali.student.r2.common.dto.TypeStateEntityInfo");
814         importsAdd("org.kuali.student.r2.common.util.RichTextHelper");
815         importsAdd("org.kuali.student.r2.core.organization.service.impl.lib.AttributeTester");
816         importsAdd("org.kuali.student.r2.core.organization.service.impl.lib.MetaTester");
817 
818         // import all the dto
819         // for each DTO, write the testCRUD
820         for (String dtoObjectName : dtoObjectNamesWithCrud) {
821             try { importsAdd(finder.findXmlType(dtoObjectName + "Info").getJavaPackage() + "." + dtoObjectName + "Info"); }
822             catch (Exception ignored) {}
823         }
824 
825         // exceptions
826         importsAdd("org.kuali.student.r2.common.exceptions.DataValidationErrorException");
827         importsAdd("org.kuali.student.r2.common.exceptions.DependentObjectsExistException");
828         importsAdd("org.kuali.student.r2.common.exceptions.DoesNotExistException");
829         importsAdd("org.kuali.student.r2.common.exceptions.InvalidParameterException");
830         importsAdd("org.kuali.student.r2.common.exceptions.MissingParameterException");
831         importsAdd("org.kuali.student.r2.common.exceptions.OperationFailedException");
832         importsAdd("org.kuali.student.r2.common.exceptions.PermissionDeniedException");
833         importsAdd("org.kuali.student.r2.common.exceptions.ReadOnlyException");
834         importsAdd("org.kuali.student.r2.common.exceptions.VersionMismatchException");
835 
836         // java imports
837         importsAdd("org.springframework.test.context.ContextConfiguration");
838         importsAdd("org.springframework.test.context.junit4.SpringJUnit4ClassRunner");
839         importsAdd("org.junit.Assert");
840         importsAdd("org.junit.Before");
841         importsAdd("org.junit.Test");
842         importsAdd("org.junit.runner.RunWith");
843         importsAdd("static org.junit.Assert.assertEquals");
844         importsAdd("static org.junit.Assert.assertFalse");
845         importsAdd("static org.junit.Assert.assertNotNull");
846         importsAdd("static org.junit.Assert.assertNull");
847         importsAdd("static org.junit.Assert.assertTrue");
848         importsAdd("static org.junit.Assert.fail");
849         importsAdd("javax.annotation.Resource");
850         importsAdd("java.util.ArrayList");
851         importsAdd("java.util.List");
852     }
853 }