001    /**
002     * Copyright 2004-2014 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            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    }