// // Copyright (c) 2011 Linkeos. // // This file is part of Elveos.org. // Elveos.org is free software: you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by the // Free Software Foundation, either version 3 of the License, or (at your // option) any later version. // // Elveos.org 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 General Public License for // more details. // You should have received a copy of the GNU General Public License along // with Elveos.org. If not, see http://www.gnu.org/licenses/. // package com.bloatit.model; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.math.BigDecimal; import java.util.Date; import java.util.Locale; import javassist.NotFoundException; import mockit.Mock; import mockit.Mockit; import org.junit.Test; import com.bloatit.data.DaoBug.Level; import com.bloatit.data.DaoDescription; import com.bloatit.data.DaoFeature; import com.bloatit.data.DaoFeature.FeatureState; import com.bloatit.data.DaoMember; import com.bloatit.data.DaoMember.Role; import com.bloatit.data.DaoSoftware; import com.bloatit.data.exceptions.NotEnoughMoneyException; import com.bloatit.framework.exceptions.highlevel.BadProgrammerException; import com.bloatit.framework.exceptions.lowlevel.NonOptionalParameterException; import com.bloatit.framework.model.ModelAccessor; import com.bloatit.framework.utils.datetime.DateUtils; import com.bloatit.framework.utils.i18n.Language; import com.bloatit.framework.webprocessor.context.User.ActivationState; import com.bloatit.model.feature.FeatureManager; import com.bloatit.model.feature.TaskUpdateDevelopingState; import com.bloatit.model.right.Action; import com.bloatit.model.right.AuthToken; import com.bloatit.model.right.UnauthorizedOperationException; public class FeatureImplementationTest extends ModelTestUnit { @Test public void testCreate() { final Feature feature = FeatureImplementation.create(DaoFeature.createAndPersist(memberTom.getDao(), null, DaoDescription.createAndPersist(memberTom.getDao(), null, Language.FR, "title", "description"), DaoSoftware.getByName("VLC"))); assertNotNull(feature); assertNull(FeatureImplementation.create(null)); } private Feature createFeatureByThomas() { return FeatureImplementation.create(DaoFeature.createAndPersist(memberTom.getDao(), null, DaoDescription.createAndPersist(memberTom.getDao(), null, Language.FR, "title", "description"), DaoSoftware.getByName("VLC"))); } @Test public void testFeature() { final Feature feature = new FeatureImplementation(memberTom, null, Language.FR, "title", "Description", Software.create(DaoSoftware.getByName("VLC"))); assertEquals(feature.getAuthor(), memberTom); assertEquals(feature.getDescription().getDefaultLanguage(), Language.FR); assertEquals(feature.getDescription().getTranslation(feature.getDescription().getDefaultLanguage()).getTitle(), "title"); assertEquals(feature.getDescription().getTranslation(feature.getDescription().getDefaultLanguage()).getText(), "Description"); } @Test public void testCanAccessComment() { final Feature feature = createFeatureByThomas(); assertTrue(feature.canAccessComment(Action.READ)); assertFalse(feature.canAccessComment(Action.WRITE)); assertFalse(feature.canAccessComment(Action.DELETE)); // other AuthToken.authenticate(memeberFred); assertTrue(feature.canAccessComment(Action.READ)); assertTrue(feature.canAccessComment(Action.WRITE)); assertFalse(feature.canAccessComment(Action.DELETE)); // feature creator AuthToken.authenticate(memberTom); assertTrue(feature.canAccessComment(Action.READ)); assertTrue(feature.canAccessComment(Action.WRITE)); assertFalse(feature.canAccessComment(Action.DELETE)); // For now nobody can delete a comment } @Test public void testCanAccessContribution() { final Feature feature = createFeatureByThomas(); assertTrue(feature.canAccessContribution(Action.READ)); assertFalse(feature.canAccessContribution(Action.WRITE)); assertFalse(feature.canAccessContribution(Action.DELETE)); // owner of feature. AuthToken.authenticate(memberTom); assertTrue(feature.canAccessContribution(Action.READ)); assertTrue(feature.canAccessContribution(Action.WRITE)); assertFalse(feature.canAccessContribution(Action.DELETE)); // other AuthToken.authenticate(memeberFred); assertTrue(feature.canAccessContribution(Action.READ)); assertTrue(feature.canAccessContribution(Action.WRITE)); assertFalse(feature.canAccessContribution(Action.DELETE)); } @Test public void testCanAccessOffer() { final Feature feature = createFeatureByThomas(); assertTrue(feature.canAccessOffer(Action.READ)); assertFalse(feature.canAccessOffer(Action.WRITE)); assertFalse(feature.canAccessOffer(Action.DELETE)); // other AuthToken.authenticate(memeberFred); assertTrue(feature.canAccessOffer(Action.READ)); assertTrue(feature.canAccessOffer(Action.WRITE)); assertFalse(feature.canAccessOffer(Action.DELETE)); // owner of feature. AuthToken.authenticate(memberTom); assertTrue(feature.canAccessOffer(Action.READ)); assertTrue(feature.canAccessOffer(Action.WRITE)); assertFalse(feature.canAccessOffer(Action.DELETE)); } @Test public void testAddContribution() { final Feature feature = createFeatureByThomas(); assertEquals(FeatureState.PENDING, feature.getFeatureState()); AuthToken.authenticate(memeberFred); try { feature.addContribution(new BigDecimal("10"), "comment"); } catch (final NotEnoughMoneyException e) { fail(); } catch (final UnauthorizedOperationException e) { fail(); } assertEquals(feature.getContribution(), new BigDecimal("10")); try { feature.addContribution(new BigDecimal("10"), null); } catch (final NotEnoughMoneyException e) { fail(); } catch (final UnauthorizedOperationException e) { fail(); } assertEquals(feature.getContribution(), new BigDecimal("20")); try { feature.addContribution(new BigDecimal("10"), ""); } catch (final NotEnoughMoneyException e) { fail(); } catch (final UnauthorizedOperationException e) { fail(); } assertEquals(feature.getContribution(), new BigDecimal("30")); try { feature.addContribution(null, "comment"); fail(); } catch (final NotEnoughMoneyException e) { fail(); } catch (final UnauthorizedOperationException e) { fail(); } catch (final NonOptionalParameterException e) { assertTrue(true); } assertEquals(feature.getContribution(), new BigDecimal("30")); try { feature.addContribution(new BigDecimal("-10"), "comment"); fail(); } catch (final NotEnoughMoneyException e) { fail(); } catch (final UnauthorizedOperationException e) { fail(); } catch (final BadProgrammerException e) { assertTrue(true); } assertEquals(feature.getContribution(), new BigDecimal("30")); try { feature.addContribution(new BigDecimal("0"), "comment"); fail(); } catch (final NotEnoughMoneyException e) { fail(); } catch (final UnauthorizedOperationException e) { fail(); } catch (final BadProgrammerException e) { assertTrue(true); } assertEquals(feature.getContribution(), new BigDecimal("30")); AuthToken.unAuthenticate(); ; try { feature.addContribution(new BigDecimal("10"), "comment"); fail(); } catch (final NotEnoughMoneyException e) { fail(); } catch (final UnauthorizedOperationException e) { assertTrue(true); } assertEquals(feature.getContribution(), new BigDecimal("30")); // Tom have 1000 money. AuthToken.authenticate(memberTom); try { feature.addContribution(new BigDecimal("1001"), "comment"); fail(); } catch (final NotEnoughMoneyException e) { assertTrue(true); } catch (final UnauthorizedOperationException e) { fail(); } assertEquals(feature.getContribution(), new BigDecimal("30")); assertEquals(FeatureState.PENDING, feature.getFeatureState()); } @Test public void testAddOffer() { final Feature feature = createFeatureByThomas(); assertEquals(FeatureState.PENDING, feature.getFeatureState()); AuthToken.authenticate(memeberFred); assertNull(feature.getSelectedOffer()); assertEquals(0, feature.getOffers().getPageSize()); try { AuthToken.authenticate(memeberFred); feature.addOffer(new BigDecimal("120"), "description", "GNU GPL V3",Language.FR, DateUtils.tomorrow(), 0); } catch (final UnauthorizedOperationException e) { fail(); } assertEquals(FeatureState.PREPARING, feature.getFeatureState()); assertNotNull(feature.getSelectedOffer()); assertEquals(FeatureState.PREPARING, feature.getFeatureState()); } public static class MockFeatureValidationTimeOut { @Mock public Date getValidationDate() { return DateUtils.yesterday(); } } @Test public void testBeginDevelopment() throws NotEnoughMoneyException, UnauthorizedOperationException { Feature feature = createFeatureByThomas(); assertEquals(FeatureState.PENDING, feature.getFeatureState()); AuthToken.authenticate(memeberFred); feature.addContribution(new BigDecimal("100"), "plop"); assertEquals(FeatureState.PENDING, feature.getFeatureState()); AuthToken.authenticate(memberTom); feature.addOffer(new BigDecimal("120"), "description", "GNU GPL V3", Language.FR, DateUtils.tomorrow(), 0); assertEquals(FeatureState.PREPARING, feature.getFeatureState()); AuthToken.authenticate(memberYo); feature.addContribution(new BigDecimal("20"), "plip"); assertEquals(FeatureState.PREPARING, feature.getFeatureState()); feature = passeIntoDev(feature); assertEquals(FeatureState.DEVELOPPING, feature.getFeatureState()); } @Test public void testRemoveOffer() throws NotEnoughMoneyException, UnauthorizedOperationException, NotFoundException { final Feature feature = createFeatureByThomas(); final DaoMember admin = DaoMember.createAndPersist("admin1", "admin1", "salt", "admin1", Locale.FRANCE); admin.setActivationState(ActivationState.ACTIVE); admin.setRole(Role.ADMIN); assertEquals(FeatureState.PENDING, feature.getFeatureState()); AuthToken.authenticate(memeberFred); feature.addContribution(new BigDecimal("100"), "plop"); assertEquals(FeatureState.PENDING, feature.getFeatureState()); AuthToken.authenticate(memberTom); feature.addOffer(new BigDecimal("120"), "description", "GNU GPL V3", Language.FR, DateUtils.tomorrow(), 0); assertEquals(FeatureState.PREPARING, feature.getFeatureState()); assertNotNull(feature.getSelectedOffer()); try { AuthToken.authenticate(memberTom); feature.removeOffer(feature.getSelectedOffer()); fail(); } catch (final UnauthorizedOperationException e) { assertTrue(true); } try { AuthToken.authenticate(memberAdmin); feature.removeOffer(feature.getSelectedOffer()); assertTrue(true); } catch (final UnauthorizedOperationException e) { fail(); } } @Test public void testCancelDevelopment() throws NotEnoughMoneyException, UnauthorizedOperationException { final Feature feature = createFeatureAddOffer120AddContribution120BeginDev(); Mockit.setUpMock(DaoFeature.class, new MockFeatureValidationTimeOut()); try { feature.cancelDevelopment(); fail(); } catch (final UnauthorizedOperationException e) { assertTrue(true); } try { AuthToken.authenticate(memberYo); feature.cancelDevelopment(); fail(); } catch (final UnauthorizedOperationException e) { assertTrue(true); } AuthToken.authenticate(memberTom); assertEquals(120, feature.getContribution().intValue()); feature.cancelDevelopment(); assertEquals(0, feature.getContribution().intValue()); assertEquals(FeatureState.DISCARDED, feature.getFeatureState()); Mockit.tearDownMocks(); } @Test public void testAddRelease() throws NotEnoughMoneyException, UnauthorizedOperationException { final Feature feature = createFeatureAddOffer120AddContribution120BeginDev(); try { feature.getSelectedOffer().getCurrentMilestone().addRelease("My release", "1.1.1", Locale.FRENCH, null); fail(); } catch (final UnauthorizedOperationException e) { assertTrue(true); } try { feature.getSelectedOffer().getCurrentMilestone().addRelease("My release", "1.1.1", Locale.FRENCH, null); feature.cancelDevelopment(); fail(); } catch (final UnauthorizedOperationException e) { assertTrue(true); } AuthToken.authenticate(memberTom); assertEquals(120, feature.getContribution().intValue()); Release release = feature.getSelectedOffer().getCurrentMilestone().addRelease("My release", "1.1.1", Locale.FRENCH, null); assertEquals(release, feature.getSelectedOffer().getCurrentMilestone().getReleases().iterator().next()); assertEquals(FeatureState.DEVELOPPING, feature.getFeatureState()); } @Test public void testAddReleaseValidate() throws NotEnoughMoneyException, UnauthorizedOperationException { final Feature feature = createFeatureAddOffer120AddContribution120BeginDev(); AuthToken.authenticate(memberTom); BigDecimal amount = AuthToken.getMember().getInternalAccount().getAmount(); Release release = feature.getSelectedOffer().getCurrentMilestone().addRelease("My release", "1.1.1", Locale.FRENCH, null); assertEquals(release, feature.getSelectedOffer().getCurrentMilestone().getReleases().iterator().next()); assertEquals(FeatureState.DEVELOPPING, feature.getFeatureState()); boolean validate = release.getMilestone().validate(); // assertFalse(validate); // assertEquals(FeatureState.DEVELOPPING, feature.getFeatureState()); // // validate =release.getMilestone().forceValidate(); assertTrue(validate); assertEquals(FeatureState.FINISHED, feature.getFeatureState()); assertEquals(AuthToken.getMember().getInternalAccount().getAmount(), amount.add(new BigDecimal(120))); } private Feature createFeatureAddOffer120AddContribution120BeginDev() throws NotEnoughMoneyException, UnauthorizedOperationException { Feature feature = createFeatureByThomas(); assertEquals(FeatureState.PENDING, feature.getFeatureState()); AuthToken.authenticate(memeberFred); feature.addContribution(new BigDecimal("100"), "plop"); assertEquals(FeatureState.PENDING, feature.getFeatureState()); AuthToken.authenticate(memberTom); feature.addOffer(new BigDecimal("120"), "description", "GNU GPL V3", Language.FR, DateUtils.tomorrow(), 0); assertEquals(FeatureState.PREPARING, feature.getFeatureState()); AuthToken.authenticate(memberYo); feature.addContribution(new BigDecimal("20"), "plip"); assertEquals(FeatureState.PREPARING, feature.getFeatureState()); feature = passeIntoDev(feature); assertEquals(FeatureState.DEVELOPPING, feature.getFeatureState()); return feature; } @Test public void testOfferWithALotOfMilestone() throws UnauthorizedOperationException, NotEnoughMoneyException { Feature feature = createFeatureByThomas(); AuthToken.authenticate(memberTom); final Offer offer = feature.addOffer(BigDecimal.TEN, "description", "GNU GPL V3", Language.FR, DateUtils.tomorrow(), DateUtils.SECOND_PER_WEEK); AuthToken.authenticate(memberTom); offer.addMilestone(BigDecimal.TEN, "description", Language.FR, DateUtils.tomorrow(), DateUtils.SECOND_PER_WEEK); offer.addMilestone(BigDecimal.TEN, "description", Language.FR, DateUtils.nowPlusSomeDays(2), DateUtils.SECOND_PER_WEEK); offer.addMilestone(BigDecimal.TEN, "description", Language.FR, DateUtils.nowPlusSomeDays(4), DateUtils.SECOND_PER_WEEK); offer.addMilestone(BigDecimal.TEN, "description", Language.FR, DateUtils.nowPlusSomeDays(8), DateUtils.SECOND_PER_WEEK); AuthToken.authenticate(memberYo); feature.addContribution(new BigDecimal("12"), null); feature.addContribution(new BigDecimal("13"), null); AuthToken.authenticate(memeberFred); feature.addContribution(new BigDecimal("14"), null); feature.addContribution(new BigDecimal("15"), null); AuthToken.authenticate(memberTom); feature.addContribution(new BigDecimal("16"), null); feature = passeIntoDev(feature); assertEquals(FeatureState.DEVELOPPING, feature.getFeatureState()); AuthToken.authenticate(memberTom); assertEquals(FeatureState.DEVELOPPING, feature.getFeatureState()); feature.getSelectedOffer().validateCurrentMilestone(true); assertEquals(FeatureState.DEVELOPPING, feature.getFeatureState()); assertEquals(FeatureState.DEVELOPPING, feature.getFeatureState()); feature.getSelectedOffer().validateCurrentMilestone(true); assertEquals(FeatureState.DEVELOPPING, feature.getFeatureState()); assertEquals(FeatureState.DEVELOPPING, feature.getFeatureState()); feature.getSelectedOffer().validateCurrentMilestone(true); assertEquals(FeatureState.DEVELOPPING, feature.getFeatureState()); assertEquals(FeatureState.DEVELOPPING, feature.getFeatureState()); feature.getSelectedOffer().validateCurrentMilestone(true); assertEquals(FeatureState.DEVELOPPING, feature.getFeatureState()); assertEquals(FeatureState.DEVELOPPING, feature.getFeatureState()); feature.getSelectedOffer().validateCurrentMilestone(true); assertEquals(FeatureState.FINISHED, feature.getFeatureState()); } // Passe into dev simulate the 1 day time to wait. // We assume that all the model has been closed, then the time out append, // and then // the model is re-closed // So you have to reload from the db the feature. (So it return it ...) private Feature passeIntoDev(final Feature feature) { ModelAccessor.close(); ModelAccessor.open(); Mockit.setUpMock(DaoFeature.class, new MockFeatureValidationTimeOut()); new TaskUpdateDevelopingState(feature.getId(), new Date()); try { Thread.sleep(1000); } catch (final InterruptedException e) { fail(); } Mockit.tearDownMocks(); // Some times has been spent. Model must have been closed and reopened. ModelAccessor.close(); ModelAccessor.open(); return FeatureManager.getFeatureById(feature.getId()); } /** * Test the creation of a complete feature test set (including offer, * milestones, contributions, kudos ...) and then delete the feature and * check if everything is correctly delete. * * @throws UnauthorizedOperationException * @throws NotEnoughMoneyException */ @Test public void testDeleteFeature() throws NotEnoughMoneyException, UnauthorizedOperationException { final Feature feature = createFeatureByThomas(); AuthToken.authenticate(memberTom); feature.addContribution(new BigDecimal(50), "Woohoo"); final Comment comm1 = feature.addComment("You shouldn't do it this way"); AuthToken.authenticate(memberTom); final Comment reply11 = comm1.addComment("Mmmh in fact you should ... I was wrong !"); AuthToken.authenticate(memeberFred); feature.addContribution(new BigDecimal(75), "Plop"); AuthToken.authenticate(memeberFred); final Comment reply12 = comm1.addComment("Indeed you were wrong"); AuthToken.authenticate(memberYo); feature.addContribution(new BigDecimal(200), "Yeahh"); final Comment comm2 = feature.addComment("Do it your way, ignore tom he's obviously wrong !"); AuthToken.authenticate(loser); final Offer offer = feature.addOffer(new BigDecimal("300"), "Beautiful offer", "AGPL", Language.FR, DateUtils.tomorrow(), 100); final Milestone milestone = offer.getCurrentMilestone(); final Release release = milestone.addRelease("Beta 1", "0.1", new Locale("fr", "fr"), null); final Bug bug1 = milestone.addBug("A new bug 1", "This is a new bug description 1", Language.EN, Level.FATAL); AuthToken.authenticate(memeberFred); final Comment bugComm11 = bug1.addComment("plop"); AuthToken.authenticate(memberTom); final Comment bugComm111 = bugComm11.addComment("plip"); AuthToken.authenticate(memberYo); final Comment bugComm12 = bug1.addComment("plap"); AuthToken.authenticate(memeberFred); final Bug bug2 = milestone.addBug("A new bug 2", "This is a new bug description 2", Language.EN, Level.MAJOR); AuthToken.authenticate(memberYo); final Bug bug3 = milestone.addBug("A new bug 3", "This is a new bug description 3", Language.EN, Level.MINOR); AuthToken.authenticate(memberTom); final Bug bug4 = milestone.addBug("A new bug 4", "This is a new bug description 4", Language.EN, Level.FATAL); AuthToken.authenticate(memeberFred); final Comment bugComm41 = bug4.addComment("plop"); AuthToken.authenticate(memberTom); final Comment bugComm411 = bugComm41.addComment("plip"); AuthToken.authenticate(memeberFred); final Comment bugComm42 = bug4.addComment("plap"); AuthToken.authenticate(memberYo); AuthToken.authenticate(memberAdmin); feature.delete(); assertTrue(feature.isDeleted()); assertTrue(comm1.isDeleted()); assertTrue(reply11.isDeleted()); assertTrue(reply12.isDeleted()); assertTrue(comm2.isDeleted()); assertTrue(release.isDeleted()); assertTrue(offer.isDeleted()); assertTrue(bug1.isDeleted()); assertTrue(bug2.isDeleted()); assertTrue(bug3.isDeleted()); assertTrue(bug4.isDeleted()); assertTrue(bugComm11.isDeleted()); assertTrue(bugComm111.isDeleted()); assertTrue(bugComm12.isDeleted()); assertTrue(bugComm41.isDeleted()); assertTrue(bugComm411.isDeleted()); assertTrue(bugComm42.isDeleted()); } }