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 */
016package org.kuali.student.mock.mojo;
017
018import org.kuali.student.contract.model.MessageStructure;
019import org.kuali.student.contract.model.Service;
020import org.kuali.student.contract.model.ServiceContractModel;
021import org.kuali.student.contract.model.ServiceMethod;
022import org.kuali.student.contract.model.ServiceMethodError;
023import org.kuali.student.contract.model.ServiceMethodParameter;
024import org.kuali.student.contract.model.util.ServicesFilter;
025import org.kuali.student.contract.writer.service.GetterSetterNameCalculator;
026import org.slf4j.Logger;
027import org.slf4j.LoggerFactory;
028
029import javax.management.OperationsException;
030
031import java.util.ArrayList;
032import 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 */
044public 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}