/* * Copyright (C) 2005-2011 Alfresco Software Limited. * * This file is part of Alfresco * * Alfresco is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Alfresco is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . */ package org.alfresco.repo.blog; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.List; import org.alfresco.model.ContentModel; import org.alfresco.query.PagingRequest; import org.alfresco.query.PagingResults; import org.alfresco.repo.blog.BlogService.BlogPostInfo; import org.alfresco.repo.policy.BehaviourFilter; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.site.SiteModel; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.MutableAuthenticationService; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.site.SiteInfo; import org.alfresco.service.cmr.site.SiteService; import org.alfresco.service.cmr.site.SiteVisibility; import org.alfresco.service.cmr.tagging.TaggingService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.util.ApplicationContextHelper; import org.alfresco.util.Pair; import org.alfresco.util.PropertyMap; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.context.ApplicationContext; /** * Test cases for {@link BlogServiceImpl}. * * @author Neil Mc Erlean * @since 4.0 */ public class BlogServiceImplTest { private static final ApplicationContext testContext = ApplicationContextHelper.getApplicationContext(); // injected services private static MutableAuthenticationService AUTHENTICATION_SERVICE; private static BehaviourFilter BEHAVIOUR_FILTER; private static BlogService BLOG_SERVICE; private static DictionaryService DICTIONARY_SERVICE; private static NodeService NODE_SERVICE; private static PersonService PERSON_SERVICE; private static RetryingTransactionHelper TRANSACTION_HELPER; private static SiteService SITE_SERVICE; private static TaggingService TAGGING_SERVICE; private static final String TEST_USER = BlogServiceImplTest.class.getSimpleName() + "_testuser"; private static final String ADMIN_USER = AuthenticationUtil.getAdminUserName(); /** * Temporary test nodes (created during a test method) that need deletion after the test method. */ private List testNodesToTidy = new ArrayList(); /** * Temporary test nodes (created BeforeClass) that need deletion after this test class. */ private static List CLASS_TEST_NODES_TO_TIDY = new ArrayList(); private static NodeRef BLOG_CONTAINER_NODE; @BeforeClass public static void initTestsContext() throws Exception { AUTHENTICATION_SERVICE = (MutableAuthenticationService)testContext.getBean("authenticationService"); BEHAVIOUR_FILTER = (BehaviourFilter)testContext.getBean("policyBehaviourFilter"); BLOG_SERVICE = (BlogService)testContext.getBean("blogService"); DICTIONARY_SERVICE = (DictionaryService)testContext.getBean("dictionaryService"); NODE_SERVICE = (NodeService)testContext.getBean("nodeService"); PERSON_SERVICE = (PersonService)testContext.getBean("personService"); TRANSACTION_HELPER = (RetryingTransactionHelper)testContext.getBean("retryingTransactionHelper"); SITE_SERVICE = (SiteService)testContext.getBean("siteService"); TAGGING_SERVICE = (TaggingService)testContext.getBean("TaggingService"); AuthenticationUtil.setFullyAuthenticatedUser(ADMIN_USER); createUser(TEST_USER); // We need to create the test site as the test user so that they can contribute content to it in tests below. AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER); createTestSiteWithBlogContainer(); } private static void createTestSiteWithBlogContainer() throws Exception { BLOG_CONTAINER_NODE = TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { @Override public NodeRef execute() throws Throwable { SiteInfo site = SITE_SERVICE.createSite("BlogSitePreset", BlogServiceImplTest.class.getSimpleName() + "_testSite" + System.currentTimeMillis(), "test site title", "test site description", SiteVisibility.PUBLIC); CLASS_TEST_NODES_TO_TIDY.add(site.getNodeRef()); NodeRef result = SITE_SERVICE.getContainer(site.getShortName(), "blog"); if (result == null) { result = NODE_SERVICE.createNode(site.getNodeRef(), ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "blog"), ContentModel.TYPE_FOLDER, null).getChildRef(); CLASS_TEST_NODES_TO_TIDY.add(result); } return result; } }); } /** * By default, all tests are run as the admin user. */ @Before public void setAdminUser() { AuthenticationUtil.setFullyAuthenticatedUser(ADMIN_USER); } @After public void deleteTestNodes() throws Exception { performDeletionOfNodes(testNodesToTidy); } @AfterClass public static void deleteClassTestNodesAndUsers() throws Exception { performDeletionOfNodes(CLASS_TEST_NODES_TO_TIDY); deleteUser(TEST_USER); } /** * Deletes the specified NodeRefs, if they exist. * @param nodesToDelete */ private static void performDeletionOfNodes(final List nodesToDelete) { TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { @Override public Void execute() throws Throwable { AuthenticationUtil.setFullyAuthenticatedUser(ADMIN_USER); for (NodeRef node : nodesToDelete) { if (NODE_SERVICE.exists(node)) { // st:site nodes can only be deleted via the SiteService if (NODE_SERVICE.getType(node).equals(SiteModel.TYPE_SITE)) { SiteInfo siteInfo = SITE_SERVICE.getSite(node); SITE_SERVICE.deleteSite(siteInfo.getShortName()); } else { NODE_SERVICE.deleteNode(node); } } } return null; } }); } @Test public void createDraftBlogPostsAndGetPagedResults() throws Exception { final int arbitraryNumberGreaterThanPageSize = 42; final List submittedBlogPosts = TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback>() { @Override public List execute() throws Throwable { List results = new ArrayList(); for (int i = 0; i < arbitraryNumberGreaterThanPageSize; i++) { ChildAssociationRef newBlogPost = BLOG_SERVICE.createBlogPost(BLOG_CONTAINER_NODE, "title_" + i, "Hello world", true); results.add(newBlogPost.getChildRef()); testNodesToTidy.add(newBlogPost.getChildRef()); } return results; } }); TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { @Override public Void execute() throws Throwable { List recoveredBlogPosts = new ArrayList(arbitraryNumberGreaterThanPageSize); final int pageSize = 10; PagingRequest pagingReq = new PagingRequest(0, pageSize, null); PagingResults pagedResults = BLOG_SERVICE.getDrafts(BLOG_CONTAINER_NODE, ADMIN_USER, pagingReq); assertEquals("Wrong total result count.", arbitraryNumberGreaterThanPageSize, (int)pagedResults.getTotalResultCount().getFirst()); while (pagedResults.hasMoreItems()) { recoveredBlogPosts.addAll(pagedResults.getPage()); pagingReq = new PagingRequest(pagingReq.getSkipCount() + pageSize, pageSize, null); pagedResults = BLOG_SERVICE.getDrafts(BLOG_CONTAINER_NODE, ADMIN_USER, pagingReq); } // and the last page, which only has 2 items in it. recoveredBlogPosts.addAll(pagedResults.getPage()); assertEquals("Wrong number of blog posts.", submittedBlogPosts.size(), recoveredBlogPosts.size()); // Check the list is sorted by cm:created, descending order. assertNodeRefsAreSortedBy(recoveredBlogPosts, ContentModel.PROP_CREATED, false); return null; } }); } /** * This method asserts that the given List has NodeRefs in order of the specified date property. * * @param blogPosts * @param property a Date property * @param ascendingOrder true if ascending order, false for descending. */ private void assertNodeRefsAreSortedBy(List blogPosts, QName property, boolean ascendingOrder) { final PropertyDefinition propertyDef = DICTIONARY_SERVICE.getProperty(property); assertNotNull("Property not recognised.", propertyDef); assertEquals("Property was not a Date", DataTypeDefinition.DATETIME, propertyDef.getDataType().getName()); if (blogPosts.size() > 1) { for (int i = 0; i < blogPosts.size() - 1; i++) { NodeRef nodeRef1 = blogPosts.get(i).getNodeRef(); NodeRef nodeRef2 = blogPosts.get(i + 1).getNodeRef(); Date date1 = (Date) NODE_SERVICE.getProperty(nodeRef1, property); Date date2 = (Date) NODE_SERVICE.getProperty(nodeRef2, property); if (ascendingOrder) { assertTrue("BlogPosts not asc-sorted by " + property + ". Error at index " + i, date1.before(date2)); } else { assertTrue("BlogPosts not desc-sorted by " + property + ". Error at index " + i, date1.after(date2)); } } } } @Test public void createTaggedDraftBlogPost() throws Exception { final List tags = Arrays.asList(new String[]{"foo", "bar"}); final NodeRef blogPost = TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { @Override public NodeRef execute() throws Throwable { ChildAssociationRef newBlogPost = BLOG_SERVICE.createBlogPost(BLOG_CONTAINER_NODE, "draftWithTag", "Hello world", true); TAGGING_SERVICE.addTags(newBlogPost.getChildRef(), tags); testNodesToTidy.add(newBlogPost.getChildRef()); return newBlogPost.getChildRef(); } }); TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { @Override public Void execute() throws Throwable { PagingRequest pagingReq = new PagingRequest(0, 10, null); PagingResults pagedResults = BLOG_SERVICE.getDrafts(BLOG_CONTAINER_NODE, ADMIN_USER, pagingReq); assertEquals("Expected one blog post", 1, pagedResults.getPage().size()); NodeRef blogNode = pagedResults.getPage().get(0).getNodeRef(); assertEquals("Incorrect NodeRef.", blogNode, blogPost); List recoveredTags = TAGGING_SERVICE.getTags(blogNode); assertEquals("Incorrect tags.", tags, recoveredTags); return null; } }); } /** * This test uses two different users to create draft and internally published blog posts. * Then it ensures that each user sees the correct posts when they retrieve them from the service. */ @Test public void multipleUsersCreateDraftsAndPublishedPostsAndBrowse() throws Exception { // Admin creates a draft and an internally-published blog post. final Pair adminPosts = TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback>() { @Override public Pair execute() throws Throwable { ChildAssociationRef newDraftBlogPost = BLOG_SERVICE.createBlogPost(BLOG_CONTAINER_NODE, "adminDraft", "", true); testNodesToTidy.add(newDraftBlogPost.getChildRef()); ChildAssociationRef newPublishedBlogPost = BLOG_SERVICE.createBlogPost(BLOG_CONTAINER_NODE, "adminPublished", "", false); testNodesToTidy.add(newPublishedBlogPost.getChildRef()); return new Pair(newDraftBlogPost.getChildRef(), newPublishedBlogPost.getChildRef()); } }); // Then another user does the same. AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER); final Pair userPosts = TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback>() { @Override public Pair execute() throws Throwable { ChildAssociationRef newDraftBlogPost = BLOG_SERVICE.createBlogPost(BLOG_CONTAINER_NODE, "userDraft", "", true); testNodesToTidy.add(newDraftBlogPost.getChildRef()); ChildAssociationRef newPublishedBlogPost = BLOG_SERVICE.createBlogPost(BLOG_CONTAINER_NODE, "userPublished", "", false); testNodesToTidy.add(newPublishedBlogPost.getChildRef()); return new Pair(newDraftBlogPost.getChildRef(), newPublishedBlogPost.getChildRef()); } }); // Now check what we see from the service. AuthenticationUtil.setFullyAuthenticatedUser(ADMIN_USER); TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { @Override public Void execute() throws Throwable { PagingRequest pagingReq = new PagingRequest(0, 10, null); PagingResults pagedDraftResults = BLOG_SERVICE.getDrafts(BLOG_CONTAINER_NODE, ADMIN_USER, pagingReq); assertEquals("Wrong number of admin draft blog posts", 1, pagedDraftResults.getPage().size()); NodeRef blogNode = pagedDraftResults.getPage().get(0).getNodeRef(); assertEquals("Incorrect admin draft NodeRef.", blogNode, adminPosts.getFirst()); PagingResults pagedPublishedResults = BLOG_SERVICE.getPublished(BLOG_CONTAINER_NODE, null, null, ADMIN_USER, pagingReq); assertEquals("Wrong number of admin published blog posts", 1, pagedPublishedResults.getPage().size()); blogNode = pagedPublishedResults.getPage().get(0).getNodeRef(); assertEquals("Incorrect admin published NodeRef.", blogNode, adminPosts.getSecond()); return null; } }); AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER); TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { @Override public Void execute() throws Throwable { PagingRequest pagingReq = new PagingRequest(0, 10, null); PagingResults pagedDraftResults = BLOG_SERVICE.getDrafts(BLOG_CONTAINER_NODE, TEST_USER, pagingReq); assertEquals("Wrong number of user draft blog posts", 1, pagedDraftResults.getPage().size()); NodeRef blogNode = pagedDraftResults.getPage().get(0).getNodeRef(); assertEquals("Incorrect user draft NodeRef.", blogNode, userPosts.getFirst()); PagingResults pagedPublishedResults = BLOG_SERVICE.getPublished(BLOG_CONTAINER_NODE, null, null, TEST_USER, pagingReq); assertEquals("Wrong number of user published blog posts", 1, pagedPublishedResults.getPage().size()); blogNode = pagedPublishedResults.getPage().get(0).getNodeRef(); assertEquals("Incorrect user published NodeRef.", blogNode, userPosts.getSecond()); return null; } }); } @Test public void getBlogPostsFilteredByDateRange() throws Exception { final int numberOfPosts = 31 + 31 + 29; final List posts = TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback>() { @Override public List execute() throws Throwable { List results = new ArrayList(); for (int i = 0; i < numberOfPosts; i++) { ChildAssociationRef newBlogPost = BLOG_SERVICE.createBlogPost(BLOG_CONTAINER_NODE, "date-specific-post" + i, "", false); testNodesToTidy.add(newBlogPost.getChildRef()); results.add(newBlogPost.getChildRef()); } return results; } }); // Now go through and set their creation dates to specific points in the past. TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { @Override public Void execute() throws Throwable { // FROM 1st December 1999 final Calendar current = Calendar.getInstance(); current.set(1999, 11, 1, 11, 0); // should give us: // 31 posts in december 99 // 31 posts in january 00 // 29 posts in february 00 Date currentDate = current.getTime(); for (NodeRef nr : posts) { // We'll permanently turn off auditing on this node. // This should allow us to set the cm:created date without auditing overwriting our value. // These nodes get deleted after the test anyway. BEHAVIOUR_FILTER.disableBehaviour(nr, ContentModel.ASPECT_AUDITABLE); // Yes, cm:published will be before cm:created. But I don't think that matter. NODE_SERVICE.setProperty(nr, ContentModel.PROP_PUBLISHED, currentDate); current.add(Calendar.DATE, 1); currentDate = current.getTime(); } return null; } }); TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { @Override public Void execute() throws Throwable { PagingRequest pagingReq = new PagingRequest(0, 100, null); Calendar cal = Calendar.getInstance(); cal.set(1999, 11, 1, 0, 0, 0); Date firstDec99 = cal.getTime(); cal.set(2000, 0, 1, 0, 0, 0); Date firstJan00 = cal.getTime(); cal.set(2000, 1, 1, 0, 0, 0); Date firstFeb00 = cal.getTime(); cal.set(2000, 2, 1, 0, 0, 0); Date firstMar00 = cal.getTime(); PagingResults pagedResults = BLOG_SERVICE.getPublished(BLOG_CONTAINER_NODE, firstDec99, firstJan00, null, pagingReq); assertEquals("Wrong number of user blog posts", 31, pagedResults.getPage().size()); pagedResults = BLOG_SERVICE.getPublished(BLOG_CONTAINER_NODE, firstFeb00, firstMar00, null, pagingReq); assertEquals("Wrong number of user blog posts", 29, pagedResults.getPage().size()); pagedResults = BLOG_SERVICE.getPublished(BLOG_CONTAINER_NODE, firstJan00, firstMar00, null, pagingReq); assertEquals("Wrong number of user blog posts", 31 + 29, pagedResults.getPage().size()); return null; } }); } private static void createUser(final String userName) { TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { @Override public Void execute() throws Throwable { if (!AUTHENTICATION_SERVICE.authenticationExists(userName)) { AUTHENTICATION_SERVICE.createAuthentication(userName, "PWD".toCharArray()); } if (!PERSON_SERVICE.personExists(userName)) { PropertyMap ppOne = new PropertyMap(); ppOne.put(ContentModel.PROP_USERNAME, userName); ppOne.put(ContentModel.PROP_FIRSTNAME, "firstName"); ppOne.put(ContentModel.PROP_LASTNAME, "lastName"); ppOne.put(ContentModel.PROP_EMAIL, "email@email.com"); ppOne.put(ContentModel.PROP_JOBTITLE, "jobTitle"); PERSON_SERVICE.createPerson(ppOne); } return null; } }); } private static void deleteUser(final String userName) { TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { @Override public Void execute() throws Throwable { if (PERSON_SERVICE.personExists(userName)) { PERSON_SERVICE.deletePerson(userName); } return null; } }); } }