001/**
002 * Copyright 2005-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.rice.kew.clientapp;
017
018import static org.junit.Assert.assertEquals;
019import static org.junit.Assert.assertNotNull;
020import static org.junit.Assert.assertTrue;
021
022import org.apache.commons.lang.StringUtils;
023import org.custommonkey.xmlunit.XMLAssert;
024import org.junit.Test;
025import org.kuali.rice.kew.api.KewApiServiceLocator;
026import org.kuali.rice.kew.api.WorkflowDocument;
027import org.kuali.rice.kew.api.WorkflowDocumentFactory;
028import org.kuali.rice.kew.api.document.DocumentContent;
029import org.kuali.rice.kew.api.document.DocumentContentUpdate;
030import org.kuali.rice.kew.api.document.attribute.WorkflowAttributeDefinition;
031import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue;
032import org.kuali.rice.kew.rule.TestRuleAttribute;
033import org.kuali.rice.kew.service.KEWServiceLocator;
034import org.kuali.rice.kew.test.KEWTestCase;
035import org.kuali.rice.kew.api.KewApiConstants;
036
037/**
038 * Tests that client interaction with document content behaves approriately.
039 * 
040 * @author Kuali Rice Team (rice.collab@kuali.org)
041 */
042public class DocumentContentTest extends KEWTestCase {
043
044    private static final String DOCUMENT_CONTENT = KewApiConstants.DOCUMENT_CONTENT_ELEMENT;
045    private static final String ATTRIBUTE_CONTENT = KewApiConstants.ATTRIBUTE_CONTENT_ELEMENT;
046    private static final String SEARCHABLE_CONTENT = KewApiConstants.SEARCHABLE_CONTENT_ELEMENT;
047    private static final String APPLICATION_CONTENT = KewApiConstants.APPLICATION_CONTENT_ELEMENT;
048
049    @Test public void testEmptyDocumentContent() throws Exception {
050        DocumentContent content = DocumentContent.Builder.create("1234").build();
051        assertEquals("<"+DOCUMENT_CONTENT + "></"+DOCUMENT_CONTENT+">", content.getFullContent());
052    }
053
054    @Test public void testDocumentContent() throws Exception {
055        String startContent = "<"+DOCUMENT_CONTENT+">";
056        String endContent = "</"+DOCUMENT_CONTENT+">";
057        String emptyContent1 = startContent+endContent;
058        String emptyContent2 = "<"+DOCUMENT_CONTENT+"/>";
059        
060        WorkflowDocument document = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("ewestfal"), "TestDocumentType");
061        
062        // test no content prior to server creation
063        assertEquals("Content should be empty.", "", document.getApplicationContent());
064        assertEquals("Content should be empty.", "", document.getAttributeContent());
065        assertEquals("Content should be empty.", "", document.getDocumentContent().getSearchableContent());
066        String fullContent = document.getDocumentContent().getFullContent();
067        assertTrue("Invalid content conversion.", fullContent.equals(emptyContent1) || fullContent.equals(emptyContent2));
068        
069        // test content after server creation
070        document = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("ewestfal"), "TestDocumentType");
071        // this will create the document on the server
072        document.saveDocumentData();
073        assertNotNull(document.getDocumentId());
074        // the document id on the document content should be there now
075        assertEquals("Incorrect document id.", document.getDocumentId(), document.getDocumentContent().getDocumentId());
076        assertEquals("Content should be empty.", "", document.getApplicationContent());
077        assertEquals("Content should be empty.", "", document.getAttributeContent());
078        assertEquals("Content should be empty.", "", document.getDocumentContent().getSearchableContent());
079        fullContent = document.getDocumentContent().getFullContent();
080        assertTrue("Invalid content conversion.", fullContent.equals(emptyContent1) || fullContent.equals(emptyContent2));
081        // verify the content on the actual document stored in the database
082        DocumentRouteHeaderValue routeHeader = KEWServiceLocator.getRouteHeaderService().getRouteHeader(document.getDocumentId());
083        assertTrue("Invalid initial content.", routeHeader.getDocContent().equals(emptyContent1) || routeHeader.getDocContent().equals(emptyContent2));
084        
085        // test simple case, no attributes
086        document = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("ewestfal"), "TestDocumentType");
087        String attributeContent = "<attribute1><id value=\"3\"/></attribute1>";
088        String searchableContent = "<searchable1><data>hello</data></searchable1>";
089        DocumentContentUpdate.Builder contentVO = DocumentContentUpdate.Builder.create(document.getDocumentContent());
090        contentVO.setAttributeContent(constructContent(ATTRIBUTE_CONTENT, attributeContent));
091        contentVO.setSearchableContent(constructContent(SEARCHABLE_CONTENT, searchableContent));
092        document.updateDocumentContent(contentVO.build());
093        document.saveDocumentData();
094        // now reload the document
095        document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("ewestfal"), document.getDocumentId());
096        String expectedContent = startContent+constructContent(ATTRIBUTE_CONTENT, attributeContent)+constructContent(SEARCHABLE_CONTENT, searchableContent)+endContent;
097        fullContent = document.getDocumentContent().getFullContent();
098        assertEquals("Invalid content conversion.", StringUtils.deleteWhitespace(expectedContent), StringUtils.deleteWhitespace(fullContent));
099        
100        // now, add an attribute and then clear it, document content should remain the same
101        String testAttributeContent = new TestRuleAttribute().getDocContent();
102        WorkflowAttributeDefinition attributeDefinition = WorkflowAttributeDefinition.Builder.create("TestRuleAttribute").build();
103        document.addAttributeDefinition(attributeDefinition);
104        document.clearAttributeDefinitions();
105        document.saveDocumentData();
106        fullContent = document.getDocumentContent().getFullContent();
107        assertEquals("Invalid content conversion.", StringUtils.deleteWhitespace(expectedContent), StringUtils.deleteWhitespace(fullContent));
108        
109        // now really add an attribute and save the content
110        document.addAttributeDefinition(attributeDefinition);
111        document.saveDocumentData();
112        fullContent = document.getDocumentContent().getFullContent();
113        expectedContent = startContent+
114            constructContent(ATTRIBUTE_CONTENT, attributeContent+testAttributeContent)+
115            constructContent(SEARCHABLE_CONTENT, searchableContent)+
116            endContent;
117        assertEquals("Invalid content conversion.", StringUtils.deleteWhitespace(expectedContent), StringUtils.deleteWhitespace(fullContent));
118
119        // let's reload the document and try appending a couple of attributes for good measure, this will test appending to existing content on non-materialized document content
120        document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("ewestfal"), document.getDocumentId());
121        document.addAttributeDefinition(attributeDefinition);
122        document.addAttributeDefinition(attributeDefinition);
123        document.saveDocumentData();
124        fullContent = document.getDocumentContent().getFullContent();
125        expectedContent = startContent+
126            constructContent(ATTRIBUTE_CONTENT, attributeContent+testAttributeContent+testAttributeContent+testAttributeContent)+
127            constructContent(SEARCHABLE_CONTENT, searchableContent)+
128            endContent;
129        assertEquals("Invalid content conversion.", StringUtils.deleteWhitespace(expectedContent), StringUtils.deleteWhitespace(fullContent));
130        
131        // now let's try clearing the attribute content
132        document.clearAttributeContent();
133        expectedContent = startContent+constructContent(SEARCHABLE_CONTENT, searchableContent)+endContent;
134        fullContent = document.getDocumentContent().getFullContent();
135        assertEquals("Invalid content conversion.", StringUtils.deleteWhitespace(expectedContent), StringUtils.deleteWhitespace(fullContent));
136        // now save it and make sure it comes back from the server the same way
137        document.saveDocumentData();
138        fullContent = document.getDocumentContent().getFullContent();
139        assertEquals("Invalid content conversion.", StringUtils.deleteWhitespace(expectedContent), StringUtils.deleteWhitespace(fullContent));
140        
141        // Test backward compatibility with old-school document content, this document content could look something
142        // like <myRadContent>abcd</myRadContent>, when converted to the new form, it should come out like
143        // <documentContent><applicationContent><myRadContent>abcd</myRadContent></applicationContent></documentContent>
144        String myRadContent = "<myRadContent>abcd</myRadContent>";
145        document = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("ewestfal"), "TestDocumentType");
146        DocumentRouteHeaderValue documentValue = KEWServiceLocator.getRouteHeaderService().getRouteHeader(document.getDocumentId());
147        documentValue.setDocContent(myRadContent);
148        KEWServiceLocator.getRouteHeaderService().saveRouteHeader(documentValue);
149        // reload the client document and check that the XML has been auto-magically converted
150        document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("ewestfal"), document.getDocumentId());
151        String expected = startContent+constructContent(APPLICATION_CONTENT, myRadContent)+endContent;
152        fullContent = document.getDocumentContent().getFullContent();
153        assertEquals("Backward compatibility failure.", StringUtils.deleteWhitespace(expected), StringUtils.deleteWhitespace(fullContent));
154    }
155    
156    private String constructContent(String type, String content) {
157        if (content == null) {
158            return "";
159        }
160        return "<"+type+">"+content+"</"+type+">";
161    }
162    
163    /**
164     * Tests that document content is reloaded from the database after every call (such as Approve, etc.)
165     * so as to verify that the document content stored on the WorkflowDocument will not go stale in between
166     * calls.
167     */
168    @Test public void testDocumentContentConsistency() throws Exception {
169        WorkflowDocument document = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("ewestfal"), "TestDocumentType");
170        String appContent = "<app>content</app>";
171        document.setApplicationContent(appContent);
172        document.saveDocumentData();
173        XMLAssert.assertXMLEqual(appContent, document.getApplicationContent());
174        
175        // load the document and modify the content
176        WorkflowDocument document2 = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("ewestfal"), document.getDocumentId());
177        XMLAssert.assertXMLEqual(appContent, document2.getApplicationContent());
178        String appContent2 = "<app>content2</app>";
179        document2.setApplicationContent(appContent2);
180        XMLAssert.assertXMLEqual(appContent2, document2.getApplicationContent());
181        document2.saveDocumentData();
182        
183        // the original document should not notice these changes yet
184        XMLAssert.assertXMLEqual(appContent, document.getApplicationContent());
185        // but if we saveRoutingData, we should see the new value
186        document.saveDocumentData();
187        XMLAssert.assertXMLEqual(appContent2, document.getApplicationContent());
188        
189        // also verify that just setting the content, but not saving it, doesn't get persisted
190        document2.setApplicationContent("<bad>content</bad>");
191        document2 = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("ewestfal"), document2.getDocumentId());
192        XMLAssert.assertXMLEqual(appContent2, document.getApplicationContent());
193    }
194    
195    /**
196     * Tests modification of the DocumentContentVO object directly.
197     */
198    @Test public void testManualDocumentContentModification() throws Exception {
199        WorkflowDocument document = WorkflowDocumentFactory.createDocument(getPrincipalIdForName("ewestfal"), "TestDocumentType");
200        document.saveDocumentData();
201        
202        // fetch it from WorkflowInfo
203        DocumentContent content = KewApiServiceLocator.getWorkflowDocumentService().getDocumentContent(document.getDocumentId());
204        assertTrue("Should contain default content, was " + content.getFullContent(), KewApiConstants.DEFAULT_DOCUMENT_CONTENT.equals(content.getFullContent()) ||
205                        KewApiConstants.DEFAULT_DOCUMENT_CONTENT2.equals(content.getFullContent()));
206        
207        String appContent = "<abcdefg>hijklm n o p</abcdefg>";
208        DocumentContentUpdate.Builder contentUpdate = DocumentContentUpdate.Builder.create(content);
209        contentUpdate.setApplicationContent(appContent);
210        document.updateDocumentContent(contentUpdate.build());
211        document.saveDocumentData();
212        
213        // test that the content on the document is the same as the content we just set
214        XMLAssert.assertXMLEqual(appContent, document.getApplicationContent());
215        
216        // fetch the document fresh and make sure the content is correct
217        document = WorkflowDocumentFactory.loadDocument(getPrincipalIdForName("ewestfal"), document.getDocumentId());
218        XMLAssert.assertXMLEqual(appContent, document.getApplicationContent());
219        
220    }
221}