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 Charles on 5/6/13
016 */
017package org.kuali.student.enrollment.class2.courseoffering.service.impl;
018
019import org.kuali.rice.core.api.criteria.PredicateFactory;
020import org.kuali.rice.core.api.criteria.QueryByCriteria;
021import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
022import org.kuali.rice.krad.uif.service.impl.ViewHelperServiceImpl;
023import org.kuali.student.enrollment.class2.courseoffering.dto.RGStateWrapper;
024import org.kuali.student.enrollment.class2.courseoffering.dto.StatePropagationWrapper;
025import org.kuali.student.enrollment.class2.courseoffering.form.TestStatePropagationForm;
026import org.kuali.student.enrollment.class2.courseoffering.service.TestStatePropagationViewHelperService;
027import org.kuali.student.enrollment.class2.courseoffering.service.exception.AssertException;
028import org.kuali.student.enrollment.class2.courseoffering.service.exception.PseudoUnitTestException;
029import org.kuali.student.enrollment.class2.courseoffering.service.util.AFUTTypeEnum;
030import org.kuali.student.enrollment.class2.courseoffering.service.util.AoStateTransitionRefSolution;
031import org.kuali.student.enrollment.class2.courseoffering.service.util.PseudoUnitTestStateTransitionGrid;
032import org.kuali.student.enrollment.class2.courseoffering.service.util.RegGroupStateResult;
033import org.kuali.student.enrollment.class2.courseoffering.service.util.TransitionGridYesNoEnum;
034import org.kuali.student.enrollment.class2.courseoffering.util.CourseOfferingConstants;
035import org.kuali.student.enrollment.class2.courseoffering.util.CourseOfferingManagementUtil;
036import org.kuali.student.enrollment.class2.courseofferingset.service.facade.RolloverAssist;
037import org.kuali.student.enrollment.class2.courseofferingset.util.CourseOfferingSetUtil;
038import org.kuali.student.enrollment.courseoffering.dto.ActivityOfferingInfo;
039import org.kuali.student.enrollment.courseoffering.dto.CourseOfferingInfo;
040import org.kuali.student.enrollment.courseoffering.dto.FormatOfferingInfo;
041import org.kuali.student.enrollment.courseoffering.dto.RegistrationGroupInfo;
042import org.kuali.student.enrollment.courseofferingset.dto.SocInfo;
043import org.kuali.student.enrollment.courseofferingset.dto.SocRolloverResultInfo;
044import org.kuali.student.enrollment.courseofferingset.dto.SocRolloverResultItemInfo;
045import org.kuali.student.enrollment.lui.dto.LuiInfo;
046import org.kuali.student.enrollment.lui.dto.LuiLuiRelationInfo;
047import org.kuali.student.poc.eventproc.KSEventProcessorImpl;
048import org.kuali.student.poc.eventproc.event.KSEvent;
049import org.kuali.student.poc.eventproc.event.KSEventFactory;
050import org.kuali.student.r2.common.dto.ContextInfo;
051import org.kuali.student.r2.common.dto.RichTextInfo;
052import org.kuali.student.r2.common.dto.StatusInfo;
053import org.kuali.student.r2.common.exceptions.DataValidationErrorException;
054import org.kuali.student.r2.common.exceptions.DoesNotExistException;
055import org.kuali.student.r2.common.exceptions.InvalidParameterException;
056import org.kuali.student.r2.common.exceptions.MissingParameterException;
057import org.kuali.student.r2.common.exceptions.OperationFailedException;
058import org.kuali.student.r2.common.exceptions.PermissionDeniedException;
059import org.kuali.student.r2.common.exceptions.ReadOnlyException;
060import org.kuali.student.r2.common.exceptions.VersionMismatchException;
061import org.kuali.student.common.util.security.ContextUtils;
062import org.kuali.student.r2.common.util.constants.CourseOfferingServiceConstants;
063import org.kuali.student.r2.common.util.constants.CourseOfferingSetServiceConstants;
064import org.kuali.student.r2.common.util.constants.LuiServiceConstants;
065import org.kuali.student.r2.core.acal.dto.TermInfo;
066import org.kuali.student.r2.core.acal.service.facade.AcademicCalendarServiceFacade;
067import org.kuali.student.r2.core.atp.dto.AtpInfo;
068import org.kuali.student.r2.core.atp.service.AtpService;
069import org.kuali.student.r2.core.constants.AtpServiceConstants;
070import org.kuali.student.r2.core.scheduling.dto.ScheduleRequestSetInfo;
071
072import javax.xml.namespace.QName;
073import java.util.ArrayList;
074import java.util.Date;
075import java.util.HashMap;
076import java.util.HashSet;
077import java.util.List;
078import java.util.Map;
079import java.util.Random;
080import java.util.Set;
081
082/**
083 * View helper service impl for running AFUTs for state propagation.
084 *
085 * @author Kuali Student Team
086 */
087public class TestStatePropagationViewHelperServiceImpl extends ViewHelperServiceImpl implements TestStatePropagationViewHelperService {
088
089    // List of objects used in test
090    private SocInfo socInfo;
091    private CourseOfferingInfo courseOfferingInfo;
092    private List<FormatOfferingInfo> foInfos;
093    private RegistrationGroupInfo rgInfo;
094    private List<RegistrationGroupInfo> rgInfos;
095    private List<ActivityOfferingInfo> aoInfos; // Only for a single RG in first FO
096    private String primaryAoId;
097    private String secondaryAoId;
098    private String secondAoState = LuiServiceConstants.LUI_AO_STATE_DRAFT_KEY;
099    private TermInfo targetTerm;
100    private TermInfo subtermOne;
101    private TermInfo subtermTwo;
102
103    // Constants
104    public static final String SAMPLE_TERM = "200008";
105    public static final String SAMPLE_ROLLOVER_TERM = "200108";
106    public static final String COURSE_OFFERING_KEY = "courseOfferingKey";
107    private ContextInfo CONTEXT;
108
109    // Soc states
110    public static final List<String> SOC_STATES_ORDERED;
111    static {
112        SOC_STATES_ORDERED = new ArrayList<String>();
113        SOC_STATES_ORDERED.add(CourseOfferingSetServiceConstants.DRAFT_SOC_STATE_KEY);
114        SOC_STATES_ORDERED.add(CourseOfferingSetServiceConstants.OPEN_SOC_STATE_KEY);
115        SOC_STATES_ORDERED.add(CourseOfferingSetServiceConstants.LOCKED_SOC_STATE_KEY);
116        SOC_STATES_ORDERED.add(CourseOfferingSetServiceConstants.FINALEDITS_SOC_STATE_KEY);
117        SOC_STATES_ORDERED.add(CourseOfferingSetServiceConstants.PUBLISHING_SOC_STATE_KEY);
118        SOC_STATES_ORDERED.add(CourseOfferingSetServiceConstants.PUBLISHED_SOC_STATE_KEY);
119    }
120
121    public static final List<String> FO_STATES_ORDERED;
122    static {
123        FO_STATES_ORDERED = new ArrayList<String>();
124        FO_STATES_ORDERED.add(LuiServiceConstants.LUI_FO_STATE_DRAFT_KEY);
125        FO_STATES_ORDERED.add(LuiServiceConstants.LUI_FO_STATE_PLANNED_KEY);
126        // can only get to this state if SOC state is publishing/published
127        FO_STATES_ORDERED.add(LuiServiceConstants.LUI_FO_STATE_OFFERED_KEY);
128    }
129
130    public static final List<String> CO_STATES_ORDERED;
131    static {
132        CO_STATES_ORDERED = new ArrayList<String>();
133        CO_STATES_ORDERED.add(LuiServiceConstants.LUI_CO_STATE_DRAFT_KEY);
134        CO_STATES_ORDERED.add(LuiServiceConstants.LUI_CO_STATE_PLANNED_KEY);
135        // can only get to this state if SOC state is publishing/published
136        CO_STATES_ORDERED.add(LuiServiceConstants.LUI_CO_STATE_OFFERED_KEY);
137    }
138
139    /**
140     * What the FO state should be given two of the AO states are initially in draft and the "second" AO is in
141     * possibly a different state.
142     */
143    public static final Map<String, String> SECOND_AO_STATE_TO_FO_STATE;
144    static {
145        SECOND_AO_STATE_TO_FO_STATE = new HashMap<String, String>();
146        SECOND_AO_STATE_TO_FO_STATE.put(LuiServiceConstants.LUI_AO_STATE_DRAFT_KEY, LuiServiceConstants.LUI_FO_STATE_DRAFT_KEY);
147        SECOND_AO_STATE_TO_FO_STATE.put(LuiServiceConstants.LUI_AO_STATE_APPROVED_KEY, LuiServiceConstants.LUI_FO_STATE_PLANNED_KEY);
148        SECOND_AO_STATE_TO_FO_STATE.put(LuiServiceConstants.LUI_AO_STATE_OFFERED_KEY, LuiServiceConstants.LUI_FO_STATE_OFFERED_KEY);
149    }
150
151    /**
152     * What the CO state should be given two of the AO states are initially in draft and the "second" AO is in
153     * possibly a different state.
154     */
155    public static final Map<String, String> SECOND_AO_STATE_TO_CO_STATE;
156    static {
157        SECOND_AO_STATE_TO_CO_STATE = new HashMap<String, String>();
158        SECOND_AO_STATE_TO_CO_STATE.put(LuiServiceConstants.LUI_AO_STATE_DRAFT_KEY, LuiServiceConstants.LUI_CO_STATE_DRAFT_KEY);
159        SECOND_AO_STATE_TO_CO_STATE.put(LuiServiceConstants.LUI_AO_STATE_APPROVED_KEY, LuiServiceConstants.LUI_CO_STATE_PLANNED_KEY);
160        SECOND_AO_STATE_TO_CO_STATE.put(LuiServiceConstants.LUI_AO_STATE_OFFERED_KEY, LuiServiceConstants.LUI_CO_STATE_OFFERED_KEY);
161    }
162    /**
163     * Get a fresh copy from the DB
164     */
165    private void _refetch() throws PermissionDeniedException, MissingParameterException, InvalidParameterException, OperationFailedException, DoesNotExistException {
166        courseOfferingInfo = CourseOfferingManagementUtil.getCourseOfferingService().getCourseOffering(courseOfferingInfo.getId(), CONTEXT);
167        for (int i = 0; i < foInfos.size(); i++) {
168            FormatOfferingInfo foInfo = foInfos.get(i);
169            foInfos.set(i, CourseOfferingManagementUtil.getCourseOfferingService().getFormatOffering(foInfo.getId(), CONTEXT));
170        }
171        for (int i = 0; i < aoInfos.size(); i++) {
172            ActivityOfferingInfo aoInfo = aoInfos.get(i);
173            aoInfos.set(i, CourseOfferingManagementUtil.getCourseOfferingService().getActivityOffering(aoInfo.getId(), CONTEXT));
174        }
175    }
176
177    public TestStatePropagationViewHelperServiceImpl() {
178        CONTEXT = ContextUtils.createDefaultContextInfo();
179        CONTEXT.setPrincipalId("admin");
180        CONTEXT.setCurrentDate(new Date());
181    }
182
183    /**
184     * fromState The initial state to set
185
186     */
187    private void _resetFoCoAndSecondaryAoState(String fromState, String secondaryState) throws Exception {
188        // First, reset the secondary AO state
189        LuiInfo aoLui = CourseOfferingManagementUtil.getLuiService().getLui(secondaryAoId, CONTEXT);
190        aoLui.setStateKey(secondaryState);
191        CourseOfferingManagementUtil.getLuiService().updateLui(aoLui.getId(), aoLui, CONTEXT);
192        // The AO state that has the biggest index is used to determine what the FO/CO state is.
193        int fromStateIndex = AoStateTransitionRefSolution.AO_STATES_ORDERED.indexOf(fromState);
194        int secondaryStateIndex = AoStateTransitionRefSolution.AO_STATES_ORDERED.indexOf(secondaryState);
195        String aoStateWinner = fromState;
196        if (secondaryStateIndex > fromStateIndex) {
197            aoStateWinner = secondaryState;
198        }
199        // Set the FO state
200        String foId = foInfos.get(0).getId(); // Grab first FO
201        LuiInfo foLui = CourseOfferingManagementUtil.getLuiService().getLui(foId, CONTEXT);
202        String foState = SECOND_AO_STATE_TO_FO_STATE.get(aoStateWinner);
203        foLui.setStateKey(foState);
204        LuiInfo updateFoLui = CourseOfferingManagementUtil.getLuiService().updateLui(foLui.getId(), foLui, CONTEXT);
205
206        // Set the CO state
207        String coId = courseOfferingInfo.getId(); // Grab first FO
208        LuiInfo coLui = CourseOfferingManagementUtil.getLuiService().getLui(coId, CONTEXT);
209        String coState = SECOND_AO_STATE_TO_CO_STATE.get(aoStateWinner);
210        coLui.setStateKey(coState);
211        LuiInfo updateCoLui = CourseOfferingManagementUtil.getLuiService().updateLui(coLui.getId(), coLui, CONTEXT);
212    }
213
214    private void _resetSocOnly() throws Exception {
215        socInfo = _getMainSocForTerm(SAMPLE_TERM);
216        if (socInfo != null) {
217            CourseOfferingManagementUtil.getSocService().deleteSoc(socInfo.getId(), CONTEXT);
218        }
219        socInfo = _createSocForTerm(SAMPLE_TERM);
220    }
221
222    private void _cleanSoc(String termCode) throws Exception {
223        SocInfo socInfoLocal = _getMainSocForTerm(termCode);
224        if (socInfoLocal != null) {
225            List<String> coIds = null;
226            try {
227                coIds = CourseOfferingManagementUtil.getSocService().getCourseOfferingIdsBySoc(socInfoLocal.getId(), CONTEXT);
228                if (coIds != null) {
229                    if (coIds.size() > 2) {
230                        throw new PseudoUnitTestException("Should only have at most 2 COs in this term");
231                    } else if (!coIds.isEmpty()) { // Has one CO
232                        CourseOfferingManagementUtil.getCourseOfferingService().deleteCourseOfferingCascaded(coIds.get(0), CONTEXT);
233                        if (coIds.size() > 1) {
234                            CourseOfferingManagementUtil.getCourseOfferingService().deleteCourseOfferingCascaded(coIds.get(1), CONTEXT);
235                        }
236                    }
237                }
238            } catch (DoesNotExistException e) {
239                // Do nothing
240            }
241            List<String> itemIds = CourseOfferingManagementUtil.getSocService().getSocRolloverResultIdsBySourceSoc(socInfoLocal.getId(), CONTEXT);
242            // Fetch socRollover
243            for (String itemId: itemIds) {
244                SocRolloverResultInfo result = CourseOfferingManagementUtil.getSocService().getSocRolloverResult(itemId, CONTEXT);
245                List<SocRolloverResultItemInfo> items =
246                        CourseOfferingManagementUtil.getSocService().getSocRolloverResultItemsByResultId(result.getId(), CONTEXT);
247                if (items != null) {
248                    for (SocRolloverResultItemInfo item: items) {
249                        CourseOfferingManagementUtil.getSocService().deleteSocRolloverResultItem(item.getId(), CONTEXT);
250                    }
251                }
252                CourseOfferingManagementUtil.getSocService().deleteSocRolloverResult(result.getId(), CONTEXT);
253            }
254            CourseOfferingManagementUtil.getSocService().deleteSoc(socInfoLocal.getId(), CONTEXT);
255        }
256    }
257
258    private void _cleanSubterms(String parentTermCode)
259            throws MissingParameterException, InvalidParameterException, OperationFailedException, PermissionDeniedException, DoesNotExistException, ReadOnlyException, DataValidationErrorException, VersionMismatchException {
260        AtpService atpService = (AtpService) GlobalResourceLoader.getService(new QName(AtpServiceConstants.NAMESPACE,
261                AtpServiceConstants.SERVICE_NAME_LOCAL_PART));
262        TermInfo term = getTermByTermCode(parentTermCode);
263        List<TermInfo> childTerms = CourseOfferingManagementUtil.getAcademicCalendarService().getIncludedTermsInTerm(term.getId(), CONTEXT);
264        for (TermInfo child: childTerms) {
265            // Force into draft state so it can be deleted
266            AtpInfo atpInfo = atpService.getAtp(child.getId(), CONTEXT);
267            atpInfo.setStateKey(AtpServiceConstants.ATP_DRAFT_STATE_KEY);
268            atpService.updateAtp(atpInfo.getId(), atpInfo, CONTEXT);
269            // Then delete
270            CourseOfferingManagementUtil.getAcademicCalendarService().deleteTerm(child.getId(), CONTEXT);
271        }
272    }
273
274    private void _reset(boolean createCourseOffering) throws Exception {
275        for (int i = 0; i <= 4; i++) {
276            _cleanSoc("200" + i + "08");
277        }
278        // Fortunately, subterms are independent of SOCs
279        _cleanSubterms(SAMPLE_TERM);
280        _cleanSubterms(SAMPLE_ROLLOVER_TERM);
281        socInfo = _createSocForTerm(SAMPLE_TERM);
282        _createSocForTerm(SAMPLE_ROLLOVER_TERM);
283        if (createCourseOffering) {
284            Map<String, Object> keyToValues =
285                    rolloverCourseOfferingFromSourceTermToTargetTerm("CHEM237", "201201", SAMPLE_TERM);
286            targetTerm = getTermByTermCode(SAMPLE_TERM);
287            courseOfferingInfo = (CourseOfferingInfo) keyToValues.get(COURSE_OFFERING_KEY);
288            // Get FOs (should only be 1)
289            foInfos = CourseOfferingManagementUtil.getCourseOfferingService().getFormatOfferingsByCourseOffering(courseOfferingInfo.getId(), CONTEXT);
290            // Save all RGs
291            rgInfos = CourseOfferingManagementUtil.getCourseOfferingService().getRegistrationGroupsByFormatOffering(foInfos.get(0).getId(), CONTEXT);
292            // Pick first RG
293            rgInfo = rgInfos.get(0);
294            // Get list of AOs but only for this RG
295            aoInfos = CourseOfferingManagementUtil.getCourseOfferingService().getActivityOfferingsByIds(rgInfo.getActivityOfferingIds(), CONTEXT);
296            // Set AO Scheduling state to scheduled since it is a constraint for AO state change (approved->offered)
297            aoInfos.get(0).setSchedulingStateKey(LuiServiceConstants.LUI_AO_SCHEDULING_STATE_SCHEDULED_KEY);
298            try {
299                CourseOfferingManagementUtil.getCourseOfferingService().updateActivityOffering(aoInfos.get(0).getId(), aoInfos.get(0), CONTEXT);
300            } catch (Exception e) {
301                throw new RuntimeException(e);
302            }
303
304            primaryAoId = aoInfos.get(0).getId();
305            secondaryAoId = aoInfos.get(1).getId();
306        }
307    }
308
309    private String _computeFoState(String primaryAoId, String desiredAoState) throws Exception {
310        // Use actual AOs to compute CO state
311        Set<String> aoStates = new HashSet<String>();
312        List<ActivityOfferingInfo> aoInfos = CourseOfferingManagementUtil.getCourseOfferingService().getActivityOfferingsByFormatOffering(foInfos.get(0).getId(), CONTEXT);
313        boolean foundPrimaryAoId = false;
314        for (ActivityOfferingInfo ao: aoInfos) {
315            if (ao.getId().equals(primaryAoId)) {
316                aoStates.add(desiredAoState);
317                foundPrimaryAoId = true;
318            } else {
319                aoStates.add(ao.getStateKey());
320            }
321        }
322        if (!foundPrimaryAoId) {
323            throw new PseudoUnitTestException("primary AO ID not found");
324        }
325        String foStateComputed = LuiServiceConstants.LUI_FO_STATE_DRAFT_KEY;
326        if (aoStates.contains(LuiServiceConstants.LUI_AO_STATE_OFFERED_KEY)) {
327            foStateComputed = LuiServiceConstants.LUI_FO_STATE_OFFERED_KEY;
328        } else if (aoStates.contains(LuiServiceConstants.LUI_AO_STATE_APPROVED_KEY)) {
329            foStateComputed = LuiServiceConstants.LUI_FO_STATE_PLANNED_KEY;
330        }
331        return foStateComputed;
332    }
333
334    private String _computeCoState(String primaryAoId, String desiredAoState) throws Exception {
335        // Use actual AOs to compute CO state
336        Set<String> aoStates = new HashSet<String>();
337        List<ActivityOfferingInfo> aoInfos = CourseOfferingManagementUtil.getCourseOfferingService().getActivityOfferingsByCourseOffering(courseOfferingInfo.getId(), CONTEXT);
338        boolean foundPrimaryAoId = false;
339        for (ActivityOfferingInfo ao: aoInfos) {
340            if (ao.getId().equals(primaryAoId)) {
341                aoStates.add(desiredAoState);
342                foundPrimaryAoId = true;
343            } else {
344                aoStates.add(ao.getStateKey());
345            }
346        }
347        if (!foundPrimaryAoId) {
348            throw new PseudoUnitTestException("primary AO ID not found");
349        }
350
351        String coStateComputed = LuiServiceConstants.LUI_CO_STATE_DRAFT_KEY;
352        if (aoStates.contains(LuiServiceConstants.LUI_AO_STATE_OFFERED_KEY)) {
353            coStateComputed = LuiServiceConstants.LUI_CO_STATE_OFFERED_KEY;
354        } else if (aoStates.contains(LuiServiceConstants.LUI_AO_STATE_APPROVED_KEY)) {
355            coStateComputed = LuiServiceConstants.LUI_CO_STATE_PLANNED_KEY;
356        }
357        return coStateComputed;
358    }
359
360    private void _testRollover() throws Exception {
361        _reset(true);
362        TermInfo halfFallOne = new TermInfo();
363        halfFallOne.setStateKey(AtpServiceConstants.ATP_DRAFT_STATE_KEY);
364        halfFallOne.setTypeKey(AtpServiceConstants.ATP_HALF_FALL_1_TYPE_KEY);
365        halfFallOne.setStartDate(targetTerm.getStartDate());
366        halfFallOne.setEndDate(targetTerm.getEndDate());
367        RichTextInfo richTextInfo = new RichTextInfo();
368        richTextInfo.setPlain("foo");
369        richTextInfo.setFormatted("bar");
370        halfFallOne.setDescr(richTextInfo);
371        subtermOne = CourseOfferingManagementUtil.getAcademicCalendarService().createTerm(halfFallOne.getTypeKey(), halfFallOne, CONTEXT);
372        // Attach subterm to term
373        CourseOfferingManagementUtil.getAcademicCalendarService().addTermToTerm(targetTerm.getId(), subtermOne.getId(), CONTEXT);
374        // Change primary AO to refer to this term
375        ActivityOfferingInfo aoInfo = CourseOfferingManagementUtil.getCourseOfferingService().getActivityOffering(primaryAoId, CONTEXT);
376        aoInfo.setTermId(subtermOne.getId());
377        aoInfo.setTermCode(null);
378        CourseOfferingManagementUtil.getCourseOfferingService().updateActivityOffering(aoInfo.getId(), aoInfo, CONTEXT);
379        // Create SOC in another term to rollover
380        SocInfo socInfo2 = _createSocForTerm(SAMPLE_ROLLOVER_TERM);
381        TermInfo newTargetTerm = getTermByTermCode(SAMPLE_ROLLOVER_TERM);
382        // Create fall subterm in new target term
383        TermInfo halfFallTwo = new TermInfo();
384        halfFallTwo.setStateKey(AtpServiceConstants.ATP_DRAFT_STATE_KEY);
385        halfFallTwo.setTypeKey(AtpServiceConstants.ATP_HALF_FALL_1_TYPE_KEY);
386        halfFallTwo.setStartDate(newTargetTerm.getStartDate());
387        halfFallTwo.setEndDate(newTargetTerm.getEndDate());
388        richTextInfo = new RichTextInfo();
389        richTextInfo.setPlain("foo");
390        richTextInfo.setFormatted("bar");
391        halfFallTwo.setDescr(richTextInfo);
392        //
393        subtermTwo = CourseOfferingManagementUtil.getAcademicCalendarService().createTerm(halfFallTwo.getTypeKey(), halfFallTwo, CONTEXT);
394        // Attach subterm to term
395        CourseOfferingManagementUtil.getAcademicCalendarService().addTermToTerm(newTargetTerm.getId(), subtermTwo.getId(), CONTEXT);
396        AcademicCalendarServiceFacade acalServiceFacade
397                = (AcademicCalendarServiceFacade) GlobalResourceLoader.getService(new QName("http://student.kuali.org/wsdl/acalServiceFacade", "AcademicCalendarServiceFacade"));
398        acalServiceFacade.makeTermOfficialCascaded(subtermTwo.getId(), CONTEXT);
399        // Rollover (should cause exception)
400        try {
401            Map<String, Object> keyToValues =
402                    rolloverCourseOfferingFromSourceTermToTargetTerm("CHEM237", SAMPLE_TERM, SAMPLE_ROLLOVER_TERM);
403        } catch (Exception e) {
404            System.err.println(e.getMessage());
405        }
406
407        List<String> coIds = CourseOfferingManagementUtil.getSocService().getCourseOfferingIdsBySoc(socInfo2.getId(), CONTEXT);
408        List<ActivityOfferingInfo> aoInfos = CourseOfferingManagementUtil.getCourseOfferingService().getActivityOfferingsByCourseOffering(coIds.get(0), CONTEXT);
409        System.err.println("Hi");
410    }
411
412    private void _testColo(ContextInfo context)
413            throws PermissionDeniedException, MissingParameterException, InvalidParameterException,
414            OperationFailedException, DoesNotExistException, ReadOnlyException, DataValidationErrorException {
415
416        RolloverAssist rolloverAssist =
417                (RolloverAssist) GlobalResourceLoader.getService(new QName("http://student.kuali.org/wsdl/rolloverAssist", "RolloverAssist"));
418        List<ScheduleRequestSetInfo> srsList1 =
419                CourseOfferingManagementUtil.getSchedulingService().getScheduleRequestSetsByRefObject(CourseOfferingServiceConstants.REF_OBJECT_URI_ACTIVITY_OFFERING, primaryAoId, context);
420        List<ScheduleRequestSetInfo> srsList2 =
421                CourseOfferingManagementUtil.getSchedulingService().getScheduleRequestSetsByRefObject(CourseOfferingServiceConstants.REF_OBJECT_URI_ACTIVITY_OFFERING, secondaryAoId, context);
422        ScheduleRequestSetInfo srs1 = srsList1.get(0);
423        ScheduleRequestSetInfo srs2 = srsList2.get(0);
424        CourseOfferingManagementUtil.getSchedulingService().deleteScheduleRequestSet(srs2.getId(), context);
425        srs1.getRefObjectIds().add(secondaryAoId);
426        ScheduleRequestSetInfo srs1Fetched = null;
427        try {
428            srs1Fetched = CourseOfferingManagementUtil.getSchedulingService().updateScheduleRequestSet(srs1.getId(), srs1, context);
429        } catch (VersionMismatchException e) {
430            throw new OperationFailedException(e.getMessage());
431        }
432        try {
433            rolloverCourseOfferingFromSourceTermToTargetTerm("CHEM237", SAMPLE_TERM, SAMPLE_TERM);
434        } catch (Exception e) {
435            System.out.println("Woops");
436        }
437        System.out.println("Hi");
438    }
439
440    private void _testEventProcessor() throws Exception {
441        _reset(true);
442        KSEventProcessorImpl ksEventProcessor
443                = (KSEventProcessorImpl) GlobalResourceLoader.getService(new QName("http://student.kuali.org/wsdl/ksEventProcessor", "KSEventProcessor"));
444        // Get all the AOs for the initial RG
445        List<ActivityOfferingInfo> rgAos = CourseOfferingManagementUtil.getCourseOfferingService().getActivityOfferingsByIds(rgInfo.getActivityOfferingIds(), CONTEXT);
446        ActivityOfferingInfo sampleAO = null;
447        for (ActivityOfferingInfo ao: rgAos) {
448            // Find the one that's a lecture
449            if (ao.getTypeKey().equals(LuiServiceConstants.LECTURE_ACTIVITY_OFFERING_TYPE_KEY)) {
450                sampleAO = ao;
451                break;
452            }
453        }
454        // Find another RG that has the same AO as sampleAO and store it in secondRG
455        RegistrationGroupInfo secondRG = null;
456        for (int i = 1; i < rgInfos.size(); i++) {
457            // Skip the zeroth one
458            RegistrationGroupInfo rg = rgInfos.get(i);
459            rgAos = CourseOfferingManagementUtil.getCourseOfferingService().getActivityOfferingsByIds(rgInfo.getActivityOfferingIds(), CONTEXT);
460            for (ActivityOfferingInfo ao: rgAos) {
461                // Find the one that's a lecture
462                if (ao.getId().equals(sampleAO.getId())) {
463                    secondRG = rg;
464                    break;
465                }
466            }
467            if (secondRG != null) {
468                break;
469            }
470        }
471        ksEventProcessor.getCoService().scheduleActivityOffering(sampleAO.getId(), CONTEXT);
472        sampleAO = ksEventProcessor.getCoService().getActivityOffering(sampleAO.getId(), CONTEXT);
473        KSEvent changeAOState = KSEventFactory.createChangeActivityOfferingStateEvent(sampleAO.getId(), LuiServiceConstants.LUI_AO_STATE_CANCELED_KEY);
474        ksEventProcessor.fireEvent(changeAOState, CONTEXT);
475    }
476
477    private void _runTimings() throws Exception {
478        _reset(true);
479        List<LuiLuiRelationInfo> infos = null;
480
481        //        Stopwatch watch = new Stopwatch();
482        //        watch.reset();
483        //        int iterations = 2000;
484        //        for (int i = 0; i < iterations; i++) {
485        //            infos = CourseOfferingManagementUtil.getLuiService().getLuiLuiRelationsByLui(rgInfo.getId(), CONTEXT);
486        //        }
487        //        String overall = watch.computeAndAccumulate();
488        //        LOGGER.info("Total time: " + overall);
489        //
490        //        Stopwatch watch2 = new Stopwatch();
491        //        watch2.reset();
492        //        for (int i = 0; i < iterations; i++) {
493        //            List<String> aoIds = CourseOfferingManagementUtil.getLuiService().getLuiIdsByLuiAndRelationType(rgInfo.getId(),
494        //                            LuiServiceConstants.LUI_LUI_RELATION_REGISTERED_FOR_VIA_RG_TO_AO_TYPE_KEY, CONTEXT);
495        //            List<String> foIds = CourseOfferingManagementUtil.getLuiService().getLuiIdsByRelatedLuiAndRelationType(rgInfo.getId(),
496        //                            LuiServiceConstants.LUI_LUI_RELATION_DELIVERED_VIA_FO_TO_RG_TYPE_KEY, CONTEXT);
497        //        }
498        //        overall = watch2.computeAndAccumulate();
499        //        LOGGER.info("Total time 2: " + overall);
500    }
501
502
503    @Override
504    public void runTests(TestStatePropagationForm form) throws Exception {
505        // Now begin to test AO state transitions
506        System.err.println("<<<<<<<<<<<<<< Starting tests >>>>>>>>>>>>");
507        _reset(true);
508        _testAoStateTransition(form);
509        System.err.println("<<<<<<<<<<<<<< Ending tests >>>>>>>>>>>>");
510        System.err.println("<<<<<<<<<<<<<< Starting RG tests >>>>>>>>>>>>");
511        _reset(true);
512        System.err.println("------------------------- Testing draft");
513            RegGroupStateResult rgStateResult = testRegistrationGroupStatePropagation(LuiServiceConstants.LUI_AO_STATE_DRAFT_KEY);
514        _printRegGroupResults(rgStateResult, form);
515        System.err.println("------------------------- Testing approved");
516        rgStateResult = testRegistrationGroupStatePropagation(LuiServiceConstants.LUI_AO_STATE_APPROVED_KEY);
517        _printRegGroupResults(rgStateResult, form);
518        System.err.println("<<<<<<<<<<<<<< Ending RG tests >>>>>>>>>>>>");
519
520        System.err.println("<<<<<<<<<<<<<< Starting RG Invalid tests >>>>>>>>>>>>");
521        _reset(true);
522        PseudoUnitTestStateTransitionGrid grid = testRegistrationGroupInvalid(true);
523        _printRegGroupInvalid(grid, true, form);
524        grid = testRegistrationGroupInvalid(false);
525        _printRegGroupInvalid(grid, false, form);
526        System.err.println("<<<<<<<<<<<<<< END RG Invalid tests >>>>>>>>>>>>");
527    }
528
529    private void _printRegGroupInvalid(PseudoUnitTestStateTransitionGrid grid, boolean isRgOffered, TestStatePropagationForm form) {
530        for (int i = 0; i < grid.size(); i++) {
531            for (int j = 0; j < grid.size(); j++) {
532                String expected = grid.getTransition(AFUTTypeEnum.EXPECTED, i, j);
533                if (!expected.equals(TransitionGridYesNoEnum.INVALID.getName())) {
534                    String actual = grid.getTransition(AFUTTypeEnum.ACTUAL, i, j);
535                    String offered = "[RG offered] ";
536                    if (!isRgOffered) {
537                        offered = "[RG pending] ";
538                    }
539                    String message = offered + " expected/actual = " + _getAfterDot(expected)
540                            + "/" + _getAfterDot(actual);
541                    System.err.println(message);
542                    RGStateWrapper rgStateWrapper = new RGStateWrapper(grid.getStateKeyAt(i), grid.getStateKeyAt(j), _getAfterDot(expected) ,_getAfterDot(actual));
543                    if(rgStateWrapper.getStatus().equals("pass")){
544                        form.setPassCount(form.getPassCount()+1);
545                    } else if(rgStateWrapper.getStatus().equals("fail")){
546                        form.setFailCount(form.getFailCount()+1);
547                    }
548                    form.addRGFromTransitionStatePropagationWrapper(rgStateWrapper);
549                }
550            }
551        }
552    }
553
554    private void _setAosInRgToAoState(String aoState) throws Exception {
555        for (ActivityOfferingInfo aoInfo: aoInfos) {
556            _forceChangeLuiState(aoInfo.getId(), aoState);
557        }
558    }
559    public static final List<String> RG_STATES;
560    static {
561        RG_STATES = new ArrayList<String>();
562        RG_STATES.add(LuiServiceConstants.REGISTRATION_GROUP_PENDING_STATE_KEY);
563        RG_STATES.add(LuiServiceConstants.REGISTRATION_GROUP_OFFERED_STATE_KEY);
564        RG_STATES.add(LuiServiceConstants.REGISTRATION_GROUP_INVALID_STATE_KEY);
565    }
566
567    // Only testing three transitions
568    private PseudoUnitTestStateTransitionGrid _createRgStateGrid(boolean aosAllOffered) {
569        PseudoUnitTestStateTransitionGrid rgStateGrid =
570                new PseudoUnitTestStateTransitionGrid(RG_STATES, TransitionGridYesNoEnum.ALLOWED_VALUES, "rg");
571        if (aosAllOffered) {
572            rgStateGrid.setTransition(AFUTTypeEnum.EXPECTED,
573                    LuiServiceConstants.REGISTRATION_GROUP_OFFERED_STATE_KEY,
574                    LuiServiceConstants.REGISTRATION_GROUP_INVALID_STATE_KEY,
575                    TransitionGridYesNoEnum.YES.getName());
576            rgStateGrid.setTransition(AFUTTypeEnum.EXPECTED,
577                    LuiServiceConstants.REGISTRATION_GROUP_INVALID_STATE_KEY,
578                    LuiServiceConstants.REGISTRATION_GROUP_OFFERED_STATE_KEY,
579                    TransitionGridYesNoEnum.YES.getName());
580            rgStateGrid.setTransition(AFUTTypeEnum.EXPECTED,
581                    LuiServiceConstants.REGISTRATION_GROUP_INVALID_STATE_KEY,
582                    LuiServiceConstants.REGISTRATION_GROUP_PENDING_STATE_KEY,
583                    TransitionGridYesNoEnum.NO.getName());
584        } else {
585            rgStateGrid.setTransition(AFUTTypeEnum.EXPECTED,
586                    LuiServiceConstants.REGISTRATION_GROUP_PENDING_STATE_KEY,
587                    LuiServiceConstants.REGISTRATION_GROUP_INVALID_STATE_KEY,
588                    TransitionGridYesNoEnum.YES.getName());
589            rgStateGrid.setTransition(AFUTTypeEnum.EXPECTED,
590                    LuiServiceConstants.REGISTRATION_GROUP_INVALID_STATE_KEY,
591                    LuiServiceConstants.REGISTRATION_GROUP_OFFERED_STATE_KEY,
592                    TransitionGridYesNoEnum.NO.getName());
593            rgStateGrid.setTransition(AFUTTypeEnum.EXPECTED,
594                    LuiServiceConstants.REGISTRATION_GROUP_INVALID_STATE_KEY,
595                    LuiServiceConstants.REGISTRATION_GROUP_PENDING_STATE_KEY,
596                    TransitionGridYesNoEnum.YES.getName());
597        }
598        return rgStateGrid;
599    }
600
601    // Just test in SOC state of publishing
602    public PseudoUnitTestStateTransitionGrid testRegistrationGroupInvalid(boolean aosAllOffered) throws Exception {
603        String initRgState = LuiServiceConstants.REGISTRATION_GROUP_OFFERED_STATE_KEY;
604        String otherRgState = LuiServiceConstants.REGISTRATION_GROUP_PENDING_STATE_KEY;
605        if (aosAllOffered) {
606            _setAosInRgToAoState(LuiServiceConstants.LUI_AO_STATE_OFFERED_KEY);
607            _forceChangeLuiState(rgInfo.getId(), LuiServiceConstants.REGISTRATION_GROUP_OFFERED_STATE_KEY);
608        } else {
609            initRgState = LuiServiceConstants.REGISTRATION_GROUP_PENDING_STATE_KEY;
610            otherRgState = LuiServiceConstants.REGISTRATION_GROUP_OFFERED_STATE_KEY;
611            _setAosInRgToAoState(LuiServiceConstants.LUI_AO_STATE_DRAFT_KEY);
612            _forceChangeLuiState(rgInfo.getId(), LuiServiceConstants.REGISTRATION_GROUP_PENDING_STATE_KEY);
613        }
614
615        StatusInfo status = CourseOfferingManagementUtil.getCourseOfferingService().changeRegistrationGroupState(rgInfo.getId(), LuiServiceConstants.REGISTRATION_GROUP_INVALID_STATE_KEY, CONTEXT);
616        PseudoUnitTestStateTransitionGrid rgStateGrid = _createRgStateGrid(aosAllOffered);
617        String result = TransitionGridYesNoEnum.YES.getName();
618        if (!status.getIsSuccess()) {
619            result = TransitionGridYesNoEnum.NO.getName();
620        }
621        rgStateGrid.setTransition(AFUTTypeEnum.ACTUAL,
622                initRgState, LuiServiceConstants.REGISTRATION_GROUP_INVALID_STATE_KEY, result);
623        // RG should be in invalid state now
624        rgInfo = CourseOfferingManagementUtil.getCourseOfferingService().getRegistrationGroup(rgInfo.getId(), CONTEXT);
625        if (!rgInfo.getStateKey().equals(LuiServiceConstants.REGISTRATION_GROUP_INVALID_STATE_KEY)) {
626            throw new PseudoUnitTestException("RG should be in invalid state");
627        }
628        // Attempt to change it to state it shouldn't go to
629        status = CourseOfferingManagementUtil.getCourseOfferingService().changeRegistrationGroupState(rgInfo.getId(), otherRgState, CONTEXT);
630        result = TransitionGridYesNoEnum.YES.getName();
631        if (!status.getIsSuccess()) {
632            result = TransitionGridYesNoEnum.NO.getName();
633        }
634        rgStateGrid.setTransition(AFUTTypeEnum.ACTUAL,
635                LuiServiceConstants.REGISTRATION_GROUP_INVALID_STATE_KEY, otherRgState, result);
636        // Change back to invalid for now
637        _forceChangeLuiState(rgInfo.getId(), LuiServiceConstants.REGISTRATION_GROUP_INVALID_STATE_KEY);
638        // Attempt to change it to state it SHOULD go to
639        status = CourseOfferingManagementUtil.getCourseOfferingService().changeRegistrationGroupState(rgInfo.getId(), initRgState, CONTEXT);
640        result = TransitionGridYesNoEnum.YES.getName();
641        if (!status.getIsSuccess()) {
642            result = TransitionGridYesNoEnum.NO.getName();
643        }
644        rgStateGrid.setTransition(AFUTTypeEnum.ACTUAL,
645                LuiServiceConstants.REGISTRATION_GROUP_INVALID_STATE_KEY, initRgState, result);
646        return rgStateGrid;
647    }
648
649    private void _testAoStateTransition(TestStatePropagationForm form) throws Exception {
650        String aoId = aoInfos.get(0).getId();
651        for (String secondaryAoState: AoStateTransitionRefSolution.AO_STATES_ORDERED) {
652            secondAoState = secondaryAoState; // Save to instance variable
653
654            for (int i = 0; i < SOC_STATES_ORDERED.size(); i++) {
655                String socState = SOC_STATES_ORDERED.get(i);
656                if (secondAoState.equals(LuiServiceConstants.LUI_AO_STATE_OFFERED_KEY)) {
657                    if (! (socState.equals(CourseOfferingSetServiceConstants.PUBLISHED_SOC_STATE_KEY) ||
658                            socState.equals(CourseOfferingSetServiceConstants.PUBLISHING_SOC_STATE_KEY))) {
659                        // Can only have AO offered in SOC published/publishing, so skip if need be
660                        continue;
661                    }
662                }
663                System.err.println("======================= SOC state = " + socState + " (Second AO state: " + secondAoState + ")");
664                Map<String, PseudoUnitTestStateTransitionGrid> gridTypeToGrid =
665                        testAoStateTransitionsInSocState(aoId, socInfo.getId(), socState, secondaryAoState);
666                _compareGrids(gridTypeToGrid, socState, secondaryAoState, form);
667                _resetSocOnly(); // Make sure to reset the SOC
668            }
669        }
670    }
671
672    private String _computeAoStateString(int index, int size) {
673        // "O" stands for offered state and "X" stands for not offered
674        String binString = Integer.toString(index, 2);
675        binString = binString.replaceAll("1", "O");
676        binString = binString.replaceAll("0", "X");
677        while (binString.length() < size) {
678            binString = "X" + binString;
679        }
680        return binString;
681    }
682
683    private String _getAfterDot(String s) {
684        int index = s.lastIndexOf('.');
685        if (index == -1) {
686            return s;
687        }
688        s = s.substring(index + 1);
689        return s;
690    }
691
692    private void _printRegGroupResults(RegGroupStateResult rgStateResult, TestStatePropagationForm form) {
693        for (int i = 0; i < rgStateResult.size(); i++) {
694            String aoStatesString = _computeAoStateString(i, rgStateResult.numAos());
695            String expected = _getAfterDot(rgStateResult.getExpected(i));
696            String actual = _getAfterDot(rgStateResult.getActual(i));
697            System.err.println(aoStatesString + " expected/actual = " + expected + "/" + actual);
698            RGStateWrapper rgStateWrapper = new RGStateWrapper(aoStatesString, expected, actual);
699            if(rgStateWrapper.getStatus().equals("pass")){
700                form.setPassCount(form.getPassCount()+1);
701            } else if(rgStateWrapper.getStatus().equals("fail")){
702                form.setFailCount(form.getFailCount()+1);
703            }
704            form.addRGFromAOStatePropagationWrapper(rgStateWrapper);
705
706        }
707    }
708
709    private int _intPower(int base, int exponent) {
710        return (int) Math.pow(base, exponent);
711    }
712
713    public RegGroupStateResult testRegistrationGroupStatePropagation(String otherAoState) throws Exception {
714        RegGroupStateResult result = new RegGroupStateResult(rgInfo.getActivityOfferingIds().size());
715        _resetSocOnly();
716        _advanceSocState(socInfo.getId(), CourseOfferingSetServiceConstants.PUBLISHED_SOC_STATE_KEY);
717        List<Integer> ints = new ArrayList<Integer>();
718        int numPermutations = result.size();
719        for (int i = 0; i < numPermutations; i++) {
720            ints.add(i);
721        }
722        Random rn = new Random();
723        // Randomly select values from 0 to power - 1 to test RG states
724        for (int i = 0; i < numPermutations; i++) {
725            int index = rn.nextInt(ints.size());
726            int val = ints.get(index); // Pick a random int from the list of ints
727            String resultRgState = testRegGroupPermutation(val, otherAoState);
728            result.setActual(val, resultRgState);
729            ints.remove(index); // Remove it so it doesn't get picked again
730
731            _reset(true);
732            _resetSocOnly();
733            _advanceSocState(socInfo.getId(), CourseOfferingSetServiceConstants.PUBLISHED_SOC_STATE_KEY);
734        }
735
736        return result;
737    }
738
739    private String testRegGroupPermutation(int index, String otherAoState) throws PermissionDeniedException, MissingParameterException, InvalidParameterException, OperationFailedException, DoesNotExistException {
740        List<String> aoIds = rgInfo.getActivityOfferingIds();
741        int pow = _intPower(2, aoIds.size());
742        if (index >= pow) {
743            throw new IndexOutOfBoundsException(index + " too big for: " + pow);
744        }
745
746        String aoStateString = _computeAoStateString(index, aoIds.size());
747        for (int i = 0; i < aoStateString.length(); i++) {
748            char ch = aoStateString.charAt(i);
749            if (ch == 'X') {
750                CourseOfferingManagementUtil.getCourseOfferingService().changeActivityOfferingState(aoIds.get(i), otherAoState, CONTEXT);
751            } else {
752                CourseOfferingManagementUtil.getCourseOfferingService().changeActivityOfferingState(aoIds.get(i), LuiServiceConstants.LUI_AO_STATE_OFFERED_KEY, CONTEXT);
753            }
754        }
755        LuiInfo lui = CourseOfferingManagementUtil.getLuiService().getLui(rgInfo.getId(), CONTEXT);
756        return lui.getStateKey();
757    }
758
759    private String _computeDisplayString(String aoFromState, String aoToState, int size) {
760        StringBuilder transition = new StringBuilder("[AO ");
761        transition.append(aoFromState).append(" => AO ").append(aoToState).append("]");
762        while (transition.length() < size) {
763            transition.append(" ");
764        }
765        return transition.toString();
766    }
767
768    private String _computeExpectedActual(String expected, String actual, int size) {
769        StringBuilder result = new StringBuilder(expected);
770        result.append("/").append(actual);
771        while (result.length() < size) {
772            result.append(" ");
773        }
774        return result.toString();
775    }
776
777    private void _compareGrids(Map<String, PseudoUnitTestStateTransitionGrid> gridTypeToGrid,
778                               String socState, String secondAoState, TestStatePropagationForm form) {
779        List<Map<String, String>> aoResults = gridTypeToGrid.get("ao").compare();
780        System.err.println("---------------------- AO state results (SOC: " + socState + ")");
781        for (int i = 0; i < aoResults.size(); i++) {
782            // Put socState in front
783            Map<String, String> resultMap = aoResults.get(i);
784            String aoFromState = resultMap.get(PseudoUnitTestStateTransitionGrid.AO_STATE_FROM);
785            int indexOfLastDot = aoFromState.lastIndexOf(".");
786            aoFromState = aoFromState.substring(indexOfLastDot + 1);
787            String aoToState = resultMap.get(PseudoUnitTestStateTransitionGrid.AO_STATE_TO);
788            indexOfLastDot = aoToState.lastIndexOf(".");
789            aoToState = aoToState.substring(indexOfLastDot + 1);
790            String expectedVal = resultMap.get(PseudoUnitTestStateTransitionGrid.EXPECTED);
791            String actualVal = resultMap.get(PseudoUnitTestStateTransitionGrid.ACTUAL);
792            String passFail = resultMap.get(PseudoUnitTestStateTransitionGrid.PASS_FAIL);
793            String color = passFail.equals(PseudoUnitTestStateTransitionGrid.PASS_VAL) ? "((( GREEN )))" :
794                    (passFail.equals(PseudoUnitTestStateTransitionGrid.FAIL_VAL) ?
795                            "*** red ***" : "... White ...");
796            String transition = _computeDisplayString(aoFromState, aoToState, 30);
797            String message = "(AO) " + transition +
798                    " expected/actual = " + _computeExpectedActual(expectedVal, actualVal, 15) + " " + color;
799            System.err.println(message);
800            StatePropagationWrapper statePropagationWrapper = new StatePropagationWrapper(socState, aoFromState, aoToState, secondAoState, expectedVal, actualVal, passFail);
801            if(statePropagationWrapper.getStatus().equals("pass")){
802                form.setPassCount(form.getPassCount()+1);
803            } else if(statePropagationWrapper.getStatus().equals("fail")){
804                form.setFailCount(form.getFailCount()+1);
805            }
806            form.addAoStatePropagationWrapper(statePropagationWrapper);
807
808        }
809        System.err.println("---------------------- FO state results (SOC: " + socState + ")");
810        List<Map<String, String>> foResults = gridTypeToGrid.get("fo").compare();
811        for (int i = 0; i < foResults.size(); i++) {
812            // Put socState in front
813            Map<String, String> resultMap = foResults.get(i);
814            String aoFromState = resultMap.get(PseudoUnitTestStateTransitionGrid.AO_STATE_FROM);
815            int indexOfLastDot = aoFromState.lastIndexOf(".");
816            aoFromState = aoFromState.substring(indexOfLastDot + 1);
817
818            String aoToState = resultMap.get(PseudoUnitTestStateTransitionGrid.AO_STATE_TO);
819            indexOfLastDot = aoToState.lastIndexOf(".");
820            aoToState = aoToState.substring(indexOfLastDot + 1);
821
822            String expectedVal = resultMap.get(PseudoUnitTestStateTransitionGrid.EXPECTED);
823            indexOfLastDot = expectedVal.lastIndexOf(".");
824            expectedVal = expectedVal.substring(indexOfLastDot + 1);
825
826            String actualVal = resultMap.get(PseudoUnitTestStateTransitionGrid.ACTUAL);
827            indexOfLastDot = actualVal.lastIndexOf(".");
828            actualVal = actualVal.substring(indexOfLastDot + 1);
829            String passFail = resultMap.get(PseudoUnitTestStateTransitionGrid.PASS_FAIL);
830            String color = passFail.equals(PseudoUnitTestStateTransitionGrid.PASS_VAL) ? "((( GREEN )))" :
831                    (passFail.equals(PseudoUnitTestStateTransitionGrid.FAIL_VAL) ?
832                            "*** red ***" : "... White ...");
833            String transition = _computeDisplayString(aoFromState, aoToState, 30);
834            String message = "(FO) " + transition +
835                    " expected/actual = " + _computeExpectedActual(expectedVal, actualVal, 15) + " " + color;
836            StatePropagationWrapper statePropagationWrapper = new StatePropagationWrapper(socState, aoFromState, aoToState, secondAoState, expectedVal, actualVal, passFail);
837            if(statePropagationWrapper.getStatus().equals("pass")){
838                form.setPassCount(form.getPassCount()+1);
839            } else if(statePropagationWrapper.getStatus().equals("fail")){
840                form.setFailCount(form.getFailCount()+1);
841            }
842            form.addFoStatePropagationWrapper(statePropagationWrapper);
843            System.err.println(message);
844        }
845        System.err.println("---------------------- CO state results (SOC: " + socState + ")");
846        List<Map<String, String>> coResults = gridTypeToGrid.get("co").compare();
847        for (int i = 0; i < foResults.size(); i++) {
848            // Put socState in front
849            Map<String, String> resultMap = coResults.get(i);
850            String aoFromState = resultMap.get(PseudoUnitTestStateTransitionGrid.AO_STATE_FROM);
851            int indexOfLastDot = aoFromState.lastIndexOf(".");
852            aoFromState = aoFromState.substring(indexOfLastDot + 1);
853
854            String aoToState = resultMap.get(PseudoUnitTestStateTransitionGrid.AO_STATE_TO);
855            indexOfLastDot = aoToState.lastIndexOf(".");
856            aoToState = aoToState.substring(indexOfLastDot + 1);
857
858            String expectedVal = resultMap.get(PseudoUnitTestStateTransitionGrid.EXPECTED);
859            indexOfLastDot = expectedVal.lastIndexOf(".");
860            expectedVal = expectedVal.substring(indexOfLastDot + 1);
861            String actualVal = resultMap.get(PseudoUnitTestStateTransitionGrid.ACTUAL);
862            indexOfLastDot = actualVal.lastIndexOf(".");
863            actualVal = actualVal.substring(indexOfLastDot + 1);
864            String passFail = resultMap.get(PseudoUnitTestStateTransitionGrid.PASS_FAIL);
865            String color = passFail.equals(PseudoUnitTestStateTransitionGrid.PASS_VAL) ? "((( GREEN )))" :
866                    (passFail.equals(PseudoUnitTestStateTransitionGrid.FAIL_VAL) ?
867                            "*** red ***" : "... White ...");
868            String transition = _computeDisplayString(aoFromState, aoToState, 30);
869            String message = "(CO) " + transition +
870                    " expected/actual = " + _computeExpectedActual(expectedVal, actualVal, 15) + " " + color;
871            System.err.println(message);
872            StatePropagationWrapper statePropagationWrapper = new StatePropagationWrapper(socState, aoFromState, aoToState, secondAoState, expectedVal, actualVal, passFail);
873            if(statePropagationWrapper.getStatus().equals("pass")){
874                form.setPassCount(form.getPassCount()+1);
875            } else if(statePropagationWrapper.getStatus().equals("fail")){
876                form.setFailCount(form.getFailCount()+1);
877            }
878            form.addCoStatePropagationWrapper(statePropagationWrapper);
879        }
880        System.err.println("---------------------- end");
881    }
882
883    public void testSocStateHappy() throws Exception {
884        assertEquals(socInfo.getStateKey(), CourseOfferingSetServiceConstants.DRAFT_SOC_STATE_KEY, "SocHappy 1");
885        // Draft to Open
886        _changeSocState(socInfo.getId(), CourseOfferingSetServiceConstants.OPEN_SOC_STATE_KEY, "SocHappy 2");
887        // Open to Locked
888        _changeSocState(socInfo.getId(), CourseOfferingSetServiceConstants.LOCKED_SOC_STATE_KEY, "SocHappy 3");
889        // Locked to Final Edits
890        _changeSocState(socInfo.getId(), CourseOfferingSetServiceConstants.FINALEDITS_SOC_STATE_KEY, "SocHappy 4");
891        // Final Edits to Publishing
892        _changeSocState(socInfo.getId(), CourseOfferingSetServiceConstants.PUBLISHING_SOC_STATE_KEY, "SocHappy 5");
893        // Publishing to Published
894        _changeSocState(socInfo.getId(), CourseOfferingSetServiceConstants.PUBLISHED_SOC_STATE_KEY, "SocHappy 6");
895    }
896
897    public void testSocStateUnhappy(String socState, int index) throws Exception {
898        _advanceSocState(socInfo.getId(), socState); // Move us to the desired SOC state
899        int i = SOC_STATES_ORDERED.indexOf(socState);
900        for (int j = 0; j < SOC_STATES_ORDERED.size(); j++) {
901            if (j == i || j == i + 1) {
902                // Skip over transitioning to yourself or to the next state
903                continue;
904            }
905            _changeSocStateInvalid(socInfo.getId(), SOC_STATES_ORDERED.get(j), socState, "Unhappy Soc " + index + "-" + j);
906        }
907    }
908
909
910    public Map<String, PseudoUnitTestStateTransitionGrid>
911    testAoStateTransitionsInSocState(String aoId, String socId, String socState, String secondaryAoState) throws Exception {
912        SocInfo fetched = CourseOfferingManagementUtil.getSocService().getSoc(socId, CONTEXT);
913        if (!fetched.getStateKey().equals(CourseOfferingSetServiceConstants.DRAFT_SOC_STATE_KEY)) {
914            throw new PseudoUnitTestException("Initial soc state not in DRAFT state");
915        }
916        if (!socState.equals(CourseOfferingSetServiceConstants.DRAFT_SOC_STATE_KEY)) {
917            _advanceSocState(socId, socState);
918        }
919        List<String> allowedAoValues = new ArrayList<String>();
920        allowedAoValues.add(TransitionGridYesNoEnum.YES.getName());
921        allowedAoValues.add(TransitionGridYesNoEnum.NO.getName());
922        allowedAoValues.add(TransitionGridYesNoEnum.YES.getName());
923        PseudoUnitTestStateTransitionGrid foGrid =
924                new PseudoUnitTestStateTransitionGrid(AoStateTransitionRefSolution.AO_STATES_ORDERED, FO_STATES_ORDERED, "fo");
925        PseudoUnitTestStateTransitionGrid coGrid =
926                new PseudoUnitTestStateTransitionGrid(AoStateTransitionRefSolution.AO_STATES_ORDERED, CO_STATES_ORDERED, "co");
927        foGrid.setSocStateKey(socState);
928        coGrid.setSocStateKey(socState);
929        PseudoUnitTestStateTransitionGrid aoGrid = AoStateTransitionRefSolution.getReferenceGridForState(socState);
930        for (int i = 0; i < aoGrid.size(); i++) {
931            String fromState = aoGrid.getStateKeyAt(i);
932            for (int j = 0; j < aoGrid.size(); j++) {
933                String toState = aoGrid.getStateKeyAt(j);
934                boolean invalidTransition = aoGrid.getTransition(AFUTTypeEnum.EXPECTED, fromState, toState).equals(TransitionGridYesNoEnum.INVALID.getName());
935                if (invalidTransition) {
936                    aoGrid.setTransition(AFUTTypeEnum.ACTUAL, fromState, toState, TransitionGridYesNoEnum.INVALID.getName());
937                    continue;
938                }
939
940                // Force the original AO to be in fromState
941                _forceChangeLuiState(aoId, fromState);
942                // Adjust FO/CO states
943                _resetFoCoAndSecondaryAoState(fromState, secondaryAoState);
944                _refetch(); // Need to get latest CO/FO/AOs
945
946                // Attempt to change toState using normal services call
947                boolean change = _tryChangingAoState(aoId, toState);
948                aoGrid.setTransition(AFUTTypeEnum.ACTUAL, fromState, toState,
949                        change ? TransitionGridYesNoEnum.YES.getName() : TransitionGridYesNoEnum.NO.getName());
950                // Now to test FO/CO states
951                _refetch();
952                String localCoState = courseOfferingInfo.getStateKey();
953                String localFoState = foInfos.get(0).getStateKey();
954                // In order to test CO/FO state propagation, we need to pretend valid AO state transitions occurred.
955                String desiredAoState = fromState; // Initialize to fromState
956                if (aoGrid.getTransition(AFUTTypeEnum.EXPECTED, fromState, toState).equals(TransitionGridYesNoEnum.YES.getName())) {
957                    desiredAoState = toState; // Transition valid so this is the desired AO state
958                }
959                if (fromState.equals(LuiServiceConstants.LUI_AO_STATE_APPROVED_KEY) &&
960                        toState.equals(LuiServiceConstants.LUI_AO_STATE_OFFERED_KEY)  &&
961                        socState.equals(CourseOfferingSetServiceConstants.PUBLISHED_SOC_STATE_KEY)) {
962                    System.err.print("");
963                }
964                String foStateComputed = _computeFoState(primaryAoId, desiredAoState);
965                String coStateComputed = _computeCoState(primaryAoId, desiredAoState);
966                foGrid.setTransition(AFUTTypeEnum.EXPECTED, fromState, toState, foStateComputed);
967                foGrid.setTransition(AFUTTypeEnum.ACTUAL, fromState, toState, localFoState);
968                coGrid.setTransition(AFUTTypeEnum.EXPECTED, fromState, toState, coStateComputed);
969                coGrid.setTransition(AFUTTypeEnum.ACTUAL, fromState, toState, localCoState);
970            }
971        }
972        Map<String, PseudoUnitTestStateTransitionGrid> gridTypeToGrid = new HashMap<String, PseudoUnitTestStateTransitionGrid>();
973        gridTypeToGrid.put("ao", aoGrid);
974        gridTypeToGrid.put("fo", foGrid);
975        gridTypeToGrid.put("co", coGrid);
976        return gridTypeToGrid;
977    }
978
979    private void _advanceSocState(String socId, String socState) throws Exception {
980        if (socState.equals(CourseOfferingSetServiceConstants.DRAFT_SOC_STATE_KEY)) {
981            return;
982        }
983        int index = 1;
984        String nextSocState = SOC_STATES_ORDERED.get(index);
985        while (!nextSocState.equals(socState)) {
986            _changeSocState(socId, nextSocState, "Unhappy Soc " + index);
987            index++;
988            if (index == 6) {
989                System.out.println();
990            }
991            nextSocState = SOC_STATES_ORDERED.get(index);
992        }
993        if (!nextSocState.equals(SOC_STATES_ORDERED.get(0))) {
994            _changeSocState(socId, socState, "Unhappy Soc " + index);
995        }
996    }
997
998    private void _changeSocState(String socId, String nextState, String message) throws Exception {
999        CourseOfferingManagementUtil.getSocService().changeSocState(socId, nextState, ContextUtils.createDefaultContextInfo());
1000        SocInfo fetched = _getMainSocForTerm(SAMPLE_TERM);
1001        assertEquals(fetched.getStateKey(), nextState, message);
1002    }
1003
1004    private void _changeSocStateInvalid(String socId, String nextState, String origState, String message) throws Exception {
1005        boolean exceptionThrown = false;
1006        try {
1007            CourseOfferingManagementUtil.getSocService().changeSocState(socId, nextState, ContextUtils.createDefaultContextInfo());
1008        } catch (OperationFailedException e) {
1009            exceptionThrown = true;
1010        }
1011        assertTrue(exceptionThrown, "Exception not thrown for invalid Soc state change");
1012        SocInfo fetched = _getMainSocForTerm(SAMPLE_TERM);
1013        assertEquals(fetched.getStateKey(), origState, message);
1014    }
1015
1016    private boolean _forceChangeLuiState(String aoId, String nextState) throws PseudoUnitTestException {
1017        // DanEp suggested this sneaky way to circumvent state changes
1018        try {
1019            LuiInfo lui = CourseOfferingManagementUtil.getLuiService().getLui(aoId, CONTEXT);
1020            lui.setStateKey(nextState);
1021            CourseOfferingManagementUtil.getLuiService().updateLui(lui.getId(), lui, CONTEXT);
1022            return true;
1023        } catch (Exception e) {
1024            throw new PseudoUnitTestException("Unexpected exception in _forceChangeAoState: " + e.getMessage());
1025        }
1026    }
1027
1028    private boolean _tryChangingAoState(String aoId, String nextState) throws PseudoUnitTestException {
1029        try {
1030            if (nextState.equals(LuiServiceConstants.LUI_AO_STATE_SUSPENDED_KEY)) {
1031                System.out.println("Hi");
1032            }
1033            if (CourseOfferingManagementUtil.getCourseOfferingService().changeActivityOfferingState(aoId, nextState, CONTEXT).getIsSuccess()) {
1034                return true;
1035            } else {
1036                return false;
1037            }
1038
1039        } catch (OperationFailedException e) {
1040            return false;
1041        } catch (Exception e) {
1042            throw new PseudoUnitTestException("Unexpected exception: " + e.getMessage());
1043        }
1044    }
1045
1046    private void assertTrue(boolean actual, String message) throws AssertException {
1047        if (!actual) {
1048            throw new AssertException("assertTrue failed", message);
1049        }
1050    }
1051
1052    private void assertEquals(String expected, String actual, String testType) throws AssertException {
1053        if (!expected.equals(actual)) {
1054            throw new AssertException(expected + " != " + actual, testType);
1055        }
1056    }
1057
1058    private SocInfo _createSocForTerm(String termCode) throws Exception {
1059        SocInfo soc = null;
1060        if (_getMainSocForTerm(termCode) != null) {
1061            return null; // Already exists, so return null
1062        } else {
1063            TermInfo mainTerm = getTermByTermCode(termCode);
1064            SocInfo socInfo = new SocInfo();
1065            socInfo.setTypeKey(CourseOfferingSetServiceConstants.MAIN_SOC_TYPE_KEY);
1066                socInfo.setStateKey(CourseOfferingSetServiceConstants.DRAFT_SOC_STATE_KEY);
1067            socInfo.setTermId(mainTerm.getId());
1068            socInfo.setSchedulingStateKey(CourseOfferingSetServiceConstants.SOC_SCHEDULING_STATE_NOT_STARTED);
1069            soc = CourseOfferingManagementUtil.getSocService().createSoc(mainTerm.getId(), socInfo.getTypeKey(), socInfo, new ContextInfo());
1070        }
1071        return soc;
1072    }
1073
1074    private SocInfo _getMainSocForTerm(String termCode) throws Exception {
1075        TermInfo mainTerm = getTermByTermCode(termCode);
1076        ContextInfo contextInfo = new ContextInfo();
1077
1078        SocInfo socInfo = CourseOfferingSetUtil.getMainSocForTermId(mainTerm.getId(), contextInfo);
1079        if (socInfo != null) {
1080            return socInfo;
1081        }
1082        return null;
1083    }
1084
1085    public TermInfo getTermByTermCode(String termCode) throws InvalidParameterException, MissingParameterException, PermissionDeniedException, OperationFailedException {
1086        // TODO: Find sensible way to rewrap exception that acal service may throw
1087        // Find the term (alas, I think it does approximate search)
1088        QueryByCriteria.Builder qbcBuilder = QueryByCriteria.Builder.create();
1089        qbcBuilder.setPredicates(PredicateFactory.equal(CourseOfferingConstants.ATP_CODE, termCode));
1090        QueryByCriteria criteria = qbcBuilder.build();
1091
1092        // Do search.  In ideal case, terms returns one element, which is the desired term.
1093        List<TermInfo> terms = null;
1094        terms = CourseOfferingManagementUtil.getAcademicCalendarService().searchForTerms(criteria, new ContextInfo());
1095        TermInfo mainTerm = null;
1096        if (terms == null || terms.isEmpty()) {
1097            throw new InvalidParameterException("Unable to find term for: " + termCode);
1098        } else {
1099            mainTerm = terms.get(0);
1100        }
1101        return mainTerm;
1102    }
1103
1104    @Override
1105    public Map<String, Object> rolloverCourseOfferingFromSourceTermToTargetTerm(String courseOfferingCode, String sourceTermCode, String targetTermCode) throws Exception {
1106        TermInfo sourceTerm = getTermByTermCode(sourceTermCode);
1107        TermInfo targetTerm = getTermByTermCode(targetTermCode);
1108        List<CourseOfferingInfo> coInfos = _searchCourseOfferingByCOCodeAndTerm(courseOfferingCode, sourceTerm.getId());
1109        if (coInfos == null || coInfos.isEmpty()) {
1110            return null;
1111        }
1112        ContextInfo contextInfo = ContextUtils.createDefaultContextInfo();
1113        CourseOfferingInfo coInfo = coInfos.get(0); // Just get the first one
1114        Date start = new Date();
1115        List<String> optionKeys = CourseOfferingManagementUtil.getDefaultOptionKeysService().getDefaultOptionKeysForCopySingleCourseOffering();
1116        SocRolloverResultItemInfo rolloverResultInfo =
1117                CourseOfferingManagementUtil.getCourseOfferingService().rolloverCourseOffering(coInfo.getId(), targetTerm.getId(), optionKeys, contextInfo);
1118        Date end = new Date();
1119        String targetId = rolloverResultInfo.getTargetCourseOfferingId();
1120        CourseOfferingInfo targetCo = CourseOfferingManagementUtil.getCourseOfferingService().getCourseOffering(targetId, contextInfo);
1121
1122        Map<String, Object> keyToValues = new HashMap<String, Object>();
1123        keyToValues.put(COURSE_OFFERING_KEY, targetCo);
1124        return keyToValues;
1125    }
1126
1127    private List<CourseOfferingInfo> _searchCourseOfferingByCOCodeAndTerm(String courseOfferingCode, String termId) throws Exception {
1128        QueryByCriteria.Builder qbcBuilder = QueryByCriteria.Builder.create();
1129        qbcBuilder.setPredicates(
1130                PredicateFactory.and(
1131                        PredicateFactory.equal(CourseOfferingConstants.COURSEOFFERING_COURSE_OFFERING_CODE, courseOfferingCode),
1132                        PredicateFactory.equal(CourseOfferingConstants.ATP_ID, termId)
1133                ));
1134        QueryByCriteria criteria = qbcBuilder.build();
1135        List<CourseOfferingInfo> coList = CourseOfferingManagementUtil.getCourseOfferingService().searchForCourseOfferings(criteria, new ContextInfo());
1136        return coList;
1137    }
1138}