001    /**
002     * Copyright 2004-2013 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.student.mock.mojo;
017    
018    import org.kuali.student.contract.model.MessageStructure;
019    import org.kuali.student.contract.model.Service;
020    import org.kuali.student.contract.model.ServiceContractModel;
021    import org.kuali.student.contract.model.ServiceMethod;
022    import org.kuali.student.contract.model.ServiceMethodError;
023    import org.kuali.student.contract.model.ServiceMethodParameter;
024    import org.kuali.student.contract.model.util.ServicesFilter;
025    import org.kuali.student.contract.writer.service.GetterSetterNameCalculator;
026    import org.slf4j.Logger;
027    import org.slf4j.LoggerFactory;
028    
029    import javax.management.OperationsException;
030    
031    import java.util.ArrayList;
032    import java.util.List;
033    
034    /**
035     * This class will generate the base class that does CRUD tests as
036     * part of the Conformance Tests for services. The generated class will
037     * need to be extended (and abstract methods filled in) to be complete.
038     * This class is meant to be generated again and again, and developers
039     * should customize tests in the extended class by extending or overwriting
040     * methods of this class as needed.
041     *
042     * @author Mezba Mahtab (mezba.mahtab@utoronto.ca)
043     */
044    public class ConformanceTestBaseCrudClassServiceWriter extends MockImplServiceWriter {
045        
046        private static final Logger log = LoggerFactory.getLogger(ConformanceTestExtendedCrudClassServiceWriter.class);
047    
048        /////////////////////////
049        // CONSTANTS
050        /////////////////////////
051    
052        public static final String ROOT_PACKAGE = "org.kuali.student";
053        protected static final String H1_COMMENT_CHAR = "=";
054        protected static final int H1_COMMENT_MARK_LENGTH = 20;
055    
056        ////////////////////////////
057        // Data Variables
058        ////////////////////////////
059    
060        private ServicesFilter filter;
061    
062        List<String> dtoObjectNamesWithCrud = null; // a list of all the DTOs managed by this class that has CRUDs
063    
064        protected List<String> getDtoObjectNamesWithCrud() {
065            return dtoObjectNamesWithCrud;
066        }
067    
068        ////////////////////////////
069        // CONSTRUCTOR
070        ////////////////////////////
071    
072        public ConformanceTestBaseCrudClassServiceWriter(ServiceContractModel model,
073                                                         String directory,
074                                                         String rootPackage,
075                                                         String servKey,
076                                                         List<ServiceMethod> methods,
077                                                         boolean isR1) {
078            super(model, directory, rootPackage, servKey, methods, isR1, calcPackage(servKey, rootPackage), calcClassName(servKey));
079            dtoObjectNamesWithCrud = calcNamesOfDTOsWithCrudManagedByService();
080        }
081    
082        public ConformanceTestBaseCrudClassServiceWriter(ServiceContractModel model,
083                                                         String directory,
084                                                         String rootPackage,
085                                                         String servKey,
086                                                         List<ServiceMethod> methods,
087                                                         boolean isR1,
088                                                         String packageName,
089                                                         String className) {
090            super(model, directory, rootPackage, servKey, methods, isR1, packageName, className);
091            dtoObjectNamesWithCrud = calcNamesOfDTOsWithCrudManagedByService();
092        }
093    
094        //////////////////////////
095        // FUNCTIONALS
096        //////////////////////////
097    
098        public static String calcPackage(String servKey, String rootPackage) {
099            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    }