From 4bf305fe3a13f012efad57c2348a9f0de880d2b5 Mon Sep 17 00:00:00 2001
From: srosse <none@none>
Date: Thu, 13 Sep 2012 11:50:57 +0200
Subject: [PATCH] OO-291: implements access control in group (with waiting
 list, special case for paypal...) ad write some unit tests to control the
 different behaviors

---
 .../org/olat/group/BusinessGroupService.java  |  12 +
 .../manager/BusinessGroupServiceImpl.java     |  56 ++++
 .../org/olat/group/model/EnrollState.java     |  55 ++++
 .../manager/ACFrontendManager.java            |  33 +--
 .../accesscontrol/ACFrontendManagerTest.java  | 254 ++++++++++++++++--
 5 files changed, 361 insertions(+), 49 deletions(-)
 create mode 100644 src/main/java/org/olat/group/model/EnrollState.java

diff --git a/src/main/java/org/olat/group/BusinessGroupService.java b/src/main/java/org/olat/group/BusinessGroupService.java
index b61e0d5a396..a55f2ea629a 100644
--- a/src/main/java/org/olat/group/BusinessGroupService.java
+++ b/src/main/java/org/olat/group/BusinessGroupService.java
@@ -33,6 +33,7 @@ import org.olat.group.model.BGRepositoryEntryRelation;
 import org.olat.group.model.BusinessGroupEnvironment;
 import org.olat.group.model.BusinessGroupMembershipChange;
 import org.olat.group.model.DisplayMembers;
+import org.olat.group.model.EnrollState;
 import org.olat.group.model.MembershipModification;
 import org.olat.group.model.SearchBusinessGroupParams;
 import org.olat.repository.RepositoryEntry;
@@ -327,6 +328,17 @@ public interface BusinessGroupService {
 	 * @param flags
 	 */
 	public void removeOwners(Identity ureqIdentity, Collection<Identity> identitiesToRemove, BusinessGroup group);
+	
+	/**
+	 * Enroll an identity to the group following the rules set by reservation, max participants,
+	 * waiting list, auto close ranks...
+	 * 
+	 * 
+	 * @param group
+	 * @param identity
+	 * @return
+	 */
+	public EnrollState enroll(final BusinessGroup group,  final Identity identity);
 
 	/**
 	 * Adds a user to a group as participant and does all the magic that needs to
diff --git a/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java b/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java
index e1533eb8695..11b5d11b903 100644
--- a/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java
+++ b/src/main/java/org/olat/group/manager/BusinessGroupServiceImpl.java
@@ -62,6 +62,7 @@ import org.olat.core.util.mail.MailerWithTemplate;
 import org.olat.core.util.notifications.NotificationsManager;
 import org.olat.core.util.notifications.Subscriber;
 import org.olat.core.util.resource.OLATResourceableJustBeforeDeletedEvent;
+import org.olat.core.util.resource.OresHelper;
 import org.olat.course.nodes.projectbroker.service.ProjectBrokerManagerFactory;
 import org.olat.group.BusinessGroup;
 import org.olat.group.BusinessGroupAddResponse;
@@ -76,6 +77,7 @@ import org.olat.group.GroupLoggingAction;
 import org.olat.group.area.BGArea;
 import org.olat.group.area.BGAreaManager;
 import org.olat.group.model.AddToGroupsEvent;
+import org.olat.group.model.BGMembership;
 import org.olat.group.model.BGRepositoryEntryRelation;
 import org.olat.group.model.BGResourceRelation;
 import org.olat.group.model.BusinessGroupEnvironment;
@@ -84,6 +86,7 @@ import org.olat.group.model.BusinessGroupMembershipImpl;
 import org.olat.group.model.BusinessGroupMembershipViewImpl;
 import org.olat.group.model.BusinessGroupMembershipsChanges;
 import org.olat.group.model.DisplayMembers;
+import org.olat.group.model.EnrollState;
 import org.olat.group.model.IdentityGroupKey;
 import org.olat.group.model.MembershipModification;
 import org.olat.group.model.SearchBusinessGroupParams;
@@ -100,6 +103,7 @@ import org.olat.repository.RepositoryEntry;
 import org.olat.resource.OLATResource;
 import org.olat.resource.OLATResourceImpl;
 import org.olat.resource.accesscontrol.ACService;
+import org.olat.resource.accesscontrol.model.ResourceReservation;
 import org.olat.testutils.codepoints.server.Codepoint;
 import org.olat.user.UserDataDeletable;
 import org.olat.util.logging.activity.LoggingResourceable;
@@ -1155,6 +1159,58 @@ public class BusinessGroupServiceImpl implements BusinessGroupService, UserDataD
 		  log.audit("removed identiy '" + identity.getName() + "' from securitygroup with key " + secGroup.getKey());
 		}
 	}
+	
+	@Override
+	public EnrollState enroll(final BusinessGroup group,  final Identity identity) {
+		return CoordinatorManager.getInstance().getCoordinator().getSyncer().doInSync(group, new SyncerCallback<EnrollState>(){
+			public EnrollState execute() {
+				log.info("doEnroll start: group=" + OresHelper.createStringRepresenting(group), identity.getName());
+				EnrollState enrollStatus = new EnrollState();
+
+				BusinessGroup reloadedGroup = loadBusinessGroup(group);
+				ResourceReservation reservation = acService.getReservation(identity, reloadedGroup.getResource());
+				
+				//reservation has the highest priority over max participant or other settings
+				if(reservation != null) {
+					addParticipant(null, identity, reloadedGroup);
+					enrollStatus.setEnrolled(BGMembership.participant);
+					log.info("doEnroll (reservation) - setIsEnrolled ", identity.getName());
+					if(reservation != null) {
+						acService.removeReservation(reservation);
+					}
+				} else if (reloadedGroup.getMaxParticipants() != null) {
+					int participantsCounter = securityManager.countIdentitiesOfSecurityGroup(reloadedGroup.getPartipiciantGroup());
+					int reservations = acService.countReservations(reloadedGroup.getResource());
+					
+					log.info("doEnroll - participantsCounter: " + participantsCounter + ", reservations: " + reservations + " maxParticipants: " + reloadedGroup.getMaxParticipants().intValue(), identity.getName());
+					if (reservation == null && (participantsCounter + reservations) >= reloadedGroup.getMaxParticipants().intValue()) {
+						// already full, show error and updated choose page again
+						if (reloadedGroup.getWaitingListEnabled().booleanValue()) {
+							addToWaitingList(null, identity, reloadedGroup);
+							enrollStatus.setEnrolled(BGMembership.waiting);
+						} else {
+							// No Waiting List => List is full
+							enrollStatus.setI18nErrorMessage("error.group.full");
+							enrollStatus.setFailed(true);
+						}
+					} else {
+						//enough place
+						addParticipant(null, identity, reloadedGroup);
+						enrollStatus.setEnrolled(BGMembership.participant);
+						log.info("doEnroll - setIsEnrolled ", identity.getName());
+					}
+				} else {
+					if (log.isDebug()) log.debug("doEnroll as participant beginTransaction");
+					addParticipant(null, identity, reloadedGroup);
+					enrollStatus.setEnrolled(BGMembership.participant);						
+					if (log.isDebug()) log.debug("doEnroll as participant committed");
+				}
+				
+				log.info("doEnroll end", identity.getName());
+				return enrollStatus;
+			}				
+		});// end of doInSync
+	}
 
 	@Override
 	public String[] addIdentityToGroups(final AddToGroupsEvent groupsEv, final Identity ident, final Identity addingIdentity) {
diff --git a/src/main/java/org/olat/group/model/EnrollState.java b/src/main/java/org/olat/group/model/EnrollState.java
new file mode 100644
index 00000000000..6616036a039
--- /dev/null
+++ b/src/main/java/org/olat/group/model/EnrollState.java
@@ -0,0 +1,55 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.group.model;
+
+/**
+ * 
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ */
+public class EnrollState {
+	
+	private boolean failed = false;
+	private String i18nErrorMessage;
+	private BGMembership enrolled;
+	
+	public boolean isFailed() {
+		return failed;
+	}
+
+	public void setFailed(boolean failed) {
+		this.failed = failed;
+	}
+
+	public String getI18nErrorMessage() {
+		return i18nErrorMessage;
+	}
+
+	public void setI18nErrorMessage(String i18nErrorMessage) {
+		this.i18nErrorMessage = i18nErrorMessage;
+	}
+
+	public BGMembership getEnrolled() {
+		return enrolled;
+	}
+
+	public void setEnrolled(BGMembership enrolled) {
+		this.enrolled = enrolled;
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/resource/accesscontrol/manager/ACFrontendManager.java b/src/main/java/org/olat/resource/accesscontrol/manager/ACFrontendManager.java
index eba4bb1efde..64505ebff4c 100644
--- a/src/main/java/org/olat/resource/accesscontrol/manager/ACFrontendManager.java
+++ b/src/main/java/org/olat/resource/accesscontrol/manager/ACFrontendManager.java
@@ -36,6 +36,7 @@ import org.olat.core.util.coordinate.CoordinatorManager;
 import org.olat.core.util.coordinate.SyncerCallback;
 import org.olat.group.BusinessGroup;
 import org.olat.group.BusinessGroupService;
+import org.olat.group.model.EnrollState;
 import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryManager;
 import org.olat.resource.OLATResource;
@@ -315,9 +316,6 @@ public class ACFrontendManager extends BasicManager implements ACService {
 			return true;//don't need reservation
 		} else if("BusinessGroup".equals(resourceType)) {
 			final BusinessGroup group = businessGroupService.loadBusinessGroup(resource);
-			if(group.getWaitingListEnabled() != null && group.getWaitingListEnabled().booleanValue()) {
-				return true; //don't need reservation
-			}
 			if(group.getMaxParticipants() == null && group.getMaxParticipants() <= 0) {
 				return true;//don't need reservation
 			}
@@ -378,33 +376,10 @@ public class ACFrontendManager extends BasicManager implements ACService {
 				return true;
 			}
 		} else if("BusinessGroup".equals(resourceType)) {
-			final BusinessGroup group = businessGroupService.loadBusinessGroup(resource);
+			BusinessGroup group = businessGroupService.loadBusinessGroup(resource);
 			if(group != null) {
-				Boolean success = CoordinatorManager.getInstance().getCoordinator().getSyncer().doInSync(group, new SyncerCallback<Boolean>() {
-					public Boolean execute() {
-						
-						ResourceReservation reservation = reservationDao.loadReservation(identity, group.getResource());
-						if(reservation != null
-								|| (group.getMaxParticipants() == null || group.getMaxParticipants().intValue() <=0)
-								|| (group.getMaxParticipants() != null && (group.getMaxParticipants().intValue() > 
-								   (countReservations(group.getResource()) + securityManager.countIdentitiesOfSecurityGroup(group.getPartipiciantGroup()))))) {
-							if(!securityManager.isIdentityInSecurityGroup(identity, group.getPartipiciantGroup())) {
-								securityManager.addIdentityToSecurityGroup(identity, group.getPartipiciantGroup());
-							}
-						} else if(group.getWaitingListEnabled() != null && group.getWaitingListEnabled().booleanValue()) {
-							if(!securityManager.isIdentityInSecurityGroup(identity, group.getWaitingGroup())) {
-								securityManager.addIdentityToSecurityGroup(identity, group.getWaitingGroup());
-							}
-						} else {
-							return Boolean.FALSE;
-						}
-
-						if(reservation != null) {
-							reservationDao.deleteReservation(reservation);
-						}
-						return Boolean.TRUE;
-					}});
-				return success.booleanValue();
+				EnrollState result = businessGroupService.enroll(group, identity);
+				return result.isFailed() ? Boolean.FALSE : Boolean.TRUE;
 			}
 		}
 		return false;
diff --git a/src/test/java/org/olat/resource/accesscontrol/ACFrontendManagerTest.java b/src/test/java/org/olat/resource/accesscontrol/ACFrontendManagerTest.java
index 4fe45e5861c..2d71d54d461 100644
--- a/src/test/java/org/olat/resource/accesscontrol/ACFrontendManagerTest.java
+++ b/src/test/java/org/olat/resource/accesscontrol/ACFrontendManagerTest.java
@@ -27,17 +27,27 @@ import static org.junit.Assert.assertTrue;
 import java.util.List;
 import java.util.UUID;
 
+import junit.framework.Assert;
+
 import org.junit.Test;
 import org.olat.basesecurity.BaseSecurity;
-import org.olat.basesecurity.SecurityGroup;
 import org.olat.core.commons.persistence.DB;
+import org.olat.core.id.Identity;
 import org.olat.core.id.OLATResourceable;
+import org.olat.group.BusinessGroup;
+import org.olat.group.BusinessGroupService;
 import org.olat.repository.RepositoryEntry;
 import org.olat.repository.RepositoryManager;
 import org.olat.resource.OLATResource;
 import org.olat.resource.OLATResourceManager;
+import org.olat.resource.accesscontrol.manager.ACMethodManager;
 import org.olat.resource.accesscontrol.manager.ACOfferManager;
+import org.olat.resource.accesscontrol.model.AccessMethod;
+import org.olat.resource.accesscontrol.model.FreeAccessMethod;
 import org.olat.resource.accesscontrol.model.Offer;
+import org.olat.resource.accesscontrol.model.OfferAccess;
+import org.olat.resource.accesscontrol.provider.paypal.model.PaypalAccessMethod;
+import org.olat.test.JunitTestHelper;
 import org.olat.test.OlatTestCase;
 import org.springframework.beans.factory.annotation.Autowired;
 
@@ -54,21 +64,22 @@ public class ACFrontendManagerTest extends OlatTestCase {
 	
 	@Autowired
 	private DB dbInstance;
-	
 	@Autowired
 	private ACOfferManager acOfferManager;
-	
 	@Autowired
 	private ACService acService;
-	
 	@Autowired
 	private OLATResourceManager resourceManager;
-	
 	@Autowired
 	private RepositoryManager repositoryManager;
-	
 	@Autowired
-	private BaseSecurity baseSecurityManager;
+	private BaseSecurity securityManager;
+	@Autowired
+	private BusinessGroupService businessGroupService;
+	@Autowired
+	private ACMethodManager acMethodManager;
+	@Autowired
+	private AccessControlModule acModule;
 	
 	@Test
 	public void testManagers() {
@@ -77,7 +88,7 @@ public class ACFrontendManagerTest extends OlatTestCase {
 		assertNotNull(dbInstance);
 		assertNotNull(resourceManager);
 		assertNotNull(repositoryManager);
-		assertNotNull(baseSecurityManager);
+		assertNotNull(securityManager);
 	}
 	
 	@Test
@@ -102,6 +113,217 @@ public class ACFrontendManagerTest extends OlatTestCase {
 		assertTrue(re.getOlatResource().equalsByPersistableKey(savedOffer.getResource()));
 	}
 	
+	/**
+	 * Test free access to a group without waiting list
+	 */
+	@Test
+	public void testFreeAccesToBusinessGroup() {
+		//create a group with a free offer
+		Identity id = JunitTestHelper.createAndPersistIdentityAsUser("agp-" + UUID.randomUUID().toString());
+		BusinessGroup group = businessGroupService.createBusinessGroup(null, "Free group", "Really free", null, null, false, false, null);
+		Offer offer = acService.createOffer(group.getResource(), "FreeGroup");
+		acService.save(offer);	
+		List<AccessMethod> freeMethods = acMethodManager.getAvailableMethodsByType(FreeAccessMethod.class);
+		OfferAccess offerAccess = acService.createOfferAccess(offer, freeMethods.get(0));
+		Assert.assertNotNull(offerAccess);
+		dbInstance.commitAndCloseSession();
+		
+		//access it
+		AccessResult result = acService.accessResource(id, offerAccess, null);
+		Assert.assertNotNull(result);
+		Assert.assertTrue(result.isAccessible());
+		dbInstance.commitAndCloseSession();
+		
+		//is id a participant?
+		boolean participant = securityManager.isIdentityInSecurityGroup(id, group.getPartipiciantGroup());
+		Assert.assertTrue(participant);
+	}
+	
+	/**
+	 * Test free access to a group without waiting list and which is full
+	 */
+	@Test
+	public void testFreeAccesToBusinessGroup_full() {
+		//create a group with a free offer, fill 2 places on 2
+		Identity id1 = JunitTestHelper.createAndPersistIdentityAsUser("agp-" + UUID.randomUUID().toString());
+		Identity id2 = JunitTestHelper.createAndPersistIdentityAsUser("agp-" + UUID.randomUUID().toString());
+		Identity id3 = JunitTestHelper.createAndPersistIdentityAsUser("agp-" + UUID.randomUUID().toString());
+		BusinessGroup group = businessGroupService.createBusinessGroup(null, "Free group", "But you must wait", new Integer(0), new Integer(2), false, false, null);
+		securityManager.addIdentityToSecurityGroup(id1, group.getPartipiciantGroup());
+		securityManager.addIdentityToSecurityGroup(id2, group.getPartipiciantGroup());
+		
+		Offer offer = acService.createOffer(group.getResource(), "Free group (waiting)");
+		acService.save(offer);	
+		List<AccessMethod> freeMethods = acMethodManager.getAvailableMethodsByType(FreeAccessMethod.class);
+		OfferAccess offerAccess = acService.createOfferAccess(offer, freeMethods.get(0));
+		Assert.assertNotNull(offerAccess);
+		dbInstance.commitAndCloseSession();
+		
+		//access it
+		AccessResult result = acService.accessResource(id3, offerAccess, null);
+		Assert.assertNotNull(result);
+		Assert.assertFalse(result.isAccessible());
+		dbInstance.commitAndCloseSession();
+		
+		//is id a waiting?
+		boolean participant = securityManager.isIdentityInSecurityGroup(id3, group.getPartipiciantGroup());
+		Assert.assertFalse(participant);
+		boolean waiting = securityManager.isIdentityInSecurityGroup(id3, group.getWaitingGroup());
+		Assert.assertFalse(waiting);
+	}
+	
+	/**
+	 * Test free access to a group with waiting list enough place
+	 */
+	@Test
+	public void testFreeAccesToBusinessGroupWithWaitingList_enoughPlace() {
+		//create a group with a free offer
+		Identity id = JunitTestHelper.createAndPersistIdentityAsUser("agp-" + UUID.randomUUID().toString());
+		BusinessGroup group = businessGroupService.createBusinessGroup(null, "Free group", "But you must wait", new Integer(0), new Integer(10), true, false, null);
+		Offer offer = acService.createOffer(group.getResource(), "Free group (waiting)");
+		acService.save(offer);	
+		List<AccessMethod> freeMethods = acMethodManager.getAvailableMethodsByType(FreeAccessMethod.class);
+		OfferAccess offerAccess = acService.createOfferAccess(offer, freeMethods.get(0));
+		Assert.assertNotNull(offerAccess);
+		dbInstance.commitAndCloseSession();
+		
+		//access it
+		AccessResult result = acService.accessResource(id, offerAccess, null);
+		Assert.assertNotNull(result);
+		Assert.assertTrue(result.isAccessible());
+		dbInstance.commitAndCloseSession();
+		
+		//is id a waiting?
+		boolean participant = securityManager.isIdentityInSecurityGroup(id, group.getPartipiciantGroup());
+		Assert.assertTrue(participant);
+		boolean waiting = securityManager.isIdentityInSecurityGroup(id, group.getWaitingGroup());
+		Assert.assertFalse(waiting);
+	}
+	
+	/**
+	 * Test free access to a group with waiting list enough place
+	 */
+	@Test
+	public void testFreeAccesToBusinessGroupWithWaitingList_full() {
+		//create a group with a free offer, fill 2 places on 2
+		Identity id1 = JunitTestHelper.createAndPersistIdentityAsUser("agp-" + UUID.randomUUID().toString());
+		Identity id2 = JunitTestHelper.createAndPersistIdentityAsUser("agp-" + UUID.randomUUID().toString());
+		Identity id3 = JunitTestHelper.createAndPersistIdentityAsUser("agp-" + UUID.randomUUID().toString());
+		BusinessGroup group = businessGroupService.createBusinessGroup(null, "Free group", "But you must wait", new Integer(0), new Integer(2), true, false, null);
+		securityManager.addIdentityToSecurityGroup(id1, group.getPartipiciantGroup());
+		securityManager.addIdentityToSecurityGroup(id2, group.getPartipiciantGroup());
+		
+		Offer offer = acService.createOffer(group.getResource(), "Free group (waiting)");
+		acService.save(offer);	
+		List<AccessMethod> freeMethods = acMethodManager.getAvailableMethodsByType(FreeAccessMethod.class);
+		OfferAccess offerAccess = acService.createOfferAccess(offer, freeMethods.get(0));
+		Assert.assertNotNull(offerAccess);
+		dbInstance.commitAndCloseSession();
+		
+		//access it
+		AccessResult result = acService.accessResource(id3, offerAccess, null);
+		Assert.assertNotNull(result);
+		Assert.assertTrue(result.isAccessible());
+		dbInstance.commitAndCloseSession();
+		
+		//is id a waiting?
+		boolean participant = securityManager.isIdentityInSecurityGroup(id3, group.getPartipiciantGroup());
+		Assert.assertFalse(participant);
+		boolean waiting = securityManager.isIdentityInSecurityGroup(id3, group.getWaitingGroup());
+		Assert.assertTrue(waiting);
+	}
+	
+	
+	/**
+	 * Test paypal scenario where a user begin the process to pay an access
+	 * to a group while an administrator is filling the group,
+	 */
+	@Test
+	public void testPaiedAccesToBusinessGroupWithWaitingList_enoughPlaceButAdmin() {
+		//enable paypal
+		boolean enabled = acModule.isPaypalEnabled();
+		if(!enabled) {
+			acModule.setPaypalEnabled(true);
+		}
+
+		//create a group with a free offer
+		Identity id1 = JunitTestHelper.createAndPersistIdentityAsUser("pay-1-" + UUID.randomUUID().toString());
+		Identity id2 = JunitTestHelper.createAndPersistIdentityAsUser("agp-" + UUID.randomUUID().toString());
+		Identity id3 = JunitTestHelper.createAndPersistIdentityAsUser("agp-" + UUID.randomUUID().toString());
+
+		BusinessGroup group = businessGroupService.createBusinessGroup(null, "Free group", "But you must wait", new Integer(0), new Integer(2), true, false, null);
+		Offer offer = acService.createOffer(group.getResource(), "Free group (waiting)");
+		acService.save(offer);	
+		List<AccessMethod> methods = acMethodManager.getAvailableMethodsByType(PaypalAccessMethod.class);
+		Assert.assertFalse(methods.isEmpty());
+		OfferAccess offerAccess = acService.createOfferAccess(offer, methods.get(0));
+		Assert.assertNotNull(offerAccess);
+		dbInstance.commitAndCloseSession();
+		
+		//id1 start payment process
+		boolean reserved = acService.reserveAccessToResource(id1, offerAccess);
+		Assert.assertTrue(reserved);
+		dbInstance.commitAndCloseSession();
+		
+		//admin fill the group
+		securityManager.addIdentityToSecurityGroup(id2, group.getPartipiciantGroup());
+		securityManager.addIdentityToSecurityGroup(id3, group.getPartipiciantGroup());
+		dbInstance.commitAndCloseSession();
+		
+		//id1 finish the process
+		AccessResult result = acService.accessResource(id1, offerAccess, null);
+		Assert.assertNotNull(result);
+		Assert.assertTrue(result.isAccessible());
+		dbInstance.commitAndCloseSession();
+		
+		//is id a waiting?
+		boolean participant = securityManager.isIdentityInSecurityGroup(id1, group.getPartipiciantGroup());
+		Assert.assertTrue(participant);
+		boolean waiting = securityManager.isIdentityInSecurityGroup(id1, group.getWaitingGroup());
+		Assert.assertFalse(waiting);
+		
+		if(!enabled) {
+			acModule.setPaypalEnabled(false);
+		}
+	}
+	
+	@Test
+	public void testPaiedAccesToBusinessGroup_full() {
+		//enable paypal
+		boolean enabled = acModule.isPaypalEnabled();
+		if(!enabled) {
+			acModule.setPaypalEnabled(true);
+		}
+
+		//create a group with a free offer
+		Identity id1 = JunitTestHelper.createAndPersistIdentityAsUser("pay-1-" + UUID.randomUUID().toString());
+		Identity id2 = JunitTestHelper.createAndPersistIdentityAsUser("agp-" + UUID.randomUUID().toString());
+		Identity id3 = JunitTestHelper.createAndPersistIdentityAsUser("agp-" + UUID.randomUUID().toString());
+
+		BusinessGroup group = businessGroupService.createBusinessGroup(null, "Free group", "But you must wait", new Integer(0), new Integer(2), false, false, null);
+		Offer offer = acService.createOffer(group.getResource(), "Free group (waiting)");
+		acService.save(offer);	
+		List<AccessMethod> methods = acMethodManager.getAvailableMethodsByType(PaypalAccessMethod.class);
+		Assert.assertFalse(methods.isEmpty());
+		OfferAccess offerAccess = acService.createOfferAccess(offer, methods.get(0));
+		Assert.assertNotNull(offerAccess);
+		dbInstance.commitAndCloseSession();
+
+		//admin fill the group
+		securityManager.addIdentityToSecurityGroup(id2, group.getPartipiciantGroup());
+		securityManager.addIdentityToSecurityGroup(id3, group.getPartipiciantGroup());
+		dbInstance.commitAndCloseSession();
+
+		//id1 try to reserve a place before the payment process
+		boolean reserved = acService.reserveAccessToResource(id1, offerAccess);
+		Assert.assertFalse(reserved);
+		
+		if(!enabled) {
+			acModule.setPaypalEnabled(false);
+		}
+	}
+	
+	
 	private RepositoryEntry createRepositoryEntry() {
 		//create a repository entry
 		OLATResourceable resourceable = new TypedResourceable(UUID.randomUUID().toString().replace("-", ""));
@@ -113,20 +335,12 @@ public class ACFrontendManagerTest extends OlatTestCase {
 		re.setDisplayname("JunitRE" + UUID.randomUUID().toString().replace("-", ""));
 		re.setOlatResource(r);
 		re.setAccess(RepositoryEntry.ACC_OWNERS_AUTHORS);
-		
-		SecurityGroup ownerGroup = baseSecurityManager.createAndPersistSecurityGroup();
-		re.setOwnerGroup(ownerGroup);
-		
-		SecurityGroup participantGroup = baseSecurityManager.createAndPersistSecurityGroup();
-		re.setParticipantGroup(participantGroup);
-		
-		SecurityGroup tutorGroup = baseSecurityManager.createAndPersistSecurityGroup();
-		re.setTutorGroup(tutorGroup);
-		
+
+		repositoryManager.createParticipantSecurityGroup(re);
+		repositoryManager.createTutorSecurityGroup(re);
+		repositoryManager.createOwnerSecurityGroup(re);
 		repositoryManager.saveRepositoryEntry(re);
-		
 		dbInstance.commitAndCloseSession();
-
 		return re;
 	}
 }
\ No newline at end of file
-- 
GitLab