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