/** * *************************************************************************** * Copyright (c) 2010 Qcadoo Limited * Project: Qcadoo Framework * Version: 1.4 * * This file is part of Qcadoo. * * Qcadoo is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *************************************************************************** */ package com.qcadoo.model.integration; import junit.framework.Assert; import org.junit.After; import org.junit.Before; import org.junit.Test; import com.google.common.collect.Lists; import com.qcadoo.model.api.DataDefinition; import com.qcadoo.model.api.Entity; import com.qcadoo.model.api.EntityOpResult; public class CyclesPreventionTest extends IntegrationTest { protected static final String PLUGIN_CYCLES = "cycles"; protected static final String MODEL_A = "modelA"; protected static final String MODEL_B = "modelB"; protected static final String MODEL_C = "modelC"; protected static final String TABLE_NAME_A = PLUGIN_CYCLES + "_" + MODEL_A; protected static final String TABLE_NAME_B = PLUGIN_CYCLES + "_" + MODEL_B; protected static final String TABLE_NAME_C = PLUGIN_CYCLES + "_" + MODEL_C; protected static final String JOINTABLE_A_B = "JOINTABLE_" + MODEL_A.toUpperCase() + "_" + MODEL_B.toUpperCase(); protected static final String JOINTABLE_B_C = "JOINTABLE_" + MODEL_B.toUpperCase() + "_" + MODEL_C.toUpperCase(); protected static final String JOINTABLE_A_C = "JOINTABLE_" + MODEL_A.toUpperCase() + "_" + MODEL_C.toUpperCase(); protected static final String FIELD_MTM_A = "manyToManyA"; protected static final String FIELD_MTM_B = "manyToManyB"; protected static final String FIELD_MTM_C = "manyToManyC"; protected static final String FIELD_BT_A = "belongsToA"; protected static final String FIELD_BT_B = "belongsToB"; protected static final String FIELD_BT_C = "belongsToC"; protected static final String FIELD_BT_A_NULLIFY = "nullifyBelongsToA"; protected static final String FIELD_BT_B_NULLIFY = "nullifyBelongsToB"; protected static final String FIELD_BT_C_NULLIFY = "nullifyBelongsToC"; protected DataDefinition aDataDefinition, bDataDefinition, cDataDefinition; @Before public final void cyclesTestInit() { pluginManager.enablePlugin(PLUGIN_CYCLES); aDataDefinition = dataDefinitionService.get(PLUGIN_CYCLES, MODEL_A); bDataDefinition = dataDefinitionService.get(PLUGIN_CYCLES, MODEL_B); cDataDefinition = dataDefinitionService.get(PLUGIN_CYCLES, MODEL_C); } @After public void cyclesTestDestroy() { jdbcTemplate.execute("delete from " + JOINTABLE_A_B); jdbcTemplate.execute("delete from " + JOINTABLE_B_C); jdbcTemplate.execute("delete from " + JOINTABLE_A_C); // deletion order matters jdbcTemplate.execute("delete from " + TABLE_NAME_C); jdbcTemplate.execute("delete from " + TABLE_NAME_B); jdbcTemplate.execute("delete from " + TABLE_NAME_A); } protected Entity buildEntity(final DataDefinition dataDefinition) { return save(dataDefinition.create()); } protected Entity save(final Entity entity) { return entity.getDataDefinition().save(entity); } @Test public final void shouldNotFallIntoInfiniteCycleDuringCascadeManyToManyDeletion2entities() { // given Entity modelA = buildEntity(aDataDefinition); Entity modelB = buildEntity(bDataDefinition); modelA.setField(FIELD_MTM_B, Lists.newArrayList(modelB)); modelB.setField(FIELD_MTM_A, Lists.newArrayList(modelA)); modelA = fromDb(save(modelA)); modelB = fromDb(save(modelB)); // when EntityOpResult result = aDataDefinition.delete(modelA.getId()); // then Assert.assertTrue(result.isSuccessfull()); Assert.assertNull(fromDb(modelA)); Assert.assertNull(fromDb(modelB)); } @Test public final void shouldNotFallIntoInfiniteCycleDuringCascadeManyToManyDeletion3entities() { // given Entity modelA = buildEntity(aDataDefinition); Entity modelB = buildEntity(bDataDefinition); Entity modelC = buildEntity(cDataDefinition); modelA.setField(FIELD_MTM_B, Lists.newArrayList(modelB)); modelB.setField(FIELD_MTM_A, Lists.newArrayList(modelA)); modelB.setField(FIELD_MTM_C, Lists.newArrayList(modelC)); modelC.setField(FIELD_MTM_B, Lists.newArrayList(modelB)); modelC.setField(FIELD_MTM_A, Lists.newArrayList(modelA)); modelA.setField(FIELD_MTM_C, Lists.newArrayList(modelC)); // BEWARE! Remember about filling both sides before save! modelA = fromDb(save(modelA)); modelB = fromDb(save(modelB)); modelC = fromDb(save(modelC)); // when EntityOpResult result = aDataDefinition.delete(modelA.getId()); // then Assert.assertTrue(result.isSuccessfull()); Assert.assertNull(fromDb(modelA)); Assert.assertNull(fromDb(modelB)); Assert.assertNull(fromDb(modelC)); } @Test public final void shouldNotFallIntoInfiniteCycleDuringCascadeHasManyDeletion2entities() { // given Entity modelA = buildEntity(aDataDefinition); Entity modelB = buildEntity(bDataDefinition); modelA.setField(FIELD_BT_B, modelB.getId()); modelB.setField(FIELD_BT_A, modelA.getId()); modelA = fromDb(save(modelA)); modelB = fromDb(save(modelB)); // when EntityOpResult result = aDataDefinition.delete(modelA.getId()); // then Assert.assertTrue(result.isSuccessfull()); Assert.assertNull(fromDb(modelA)); Assert.assertNull(fromDb(modelB)); } @Test public final void shouldNotFallIntoInfiniteCycleDuringCascadeHasManyDeletion3entities() { // given Entity modelA = buildEntity(aDataDefinition); Entity modelB = buildEntity(bDataDefinition); Entity modelC = buildEntity(cDataDefinition); modelA.setField(FIELD_BT_C, modelC.getId()); modelB.setField(FIELD_BT_A, modelA.getId()); modelC.setField(FIELD_BT_B, modelB.getId()); modelA = fromDb(save(modelA)); modelB = fromDb(save(modelB)); modelC = fromDb(save(modelC)); // when EntityOpResult result = aDataDefinition.delete(modelA.getId()); // then Assert.assertTrue(result.isSuccessfull()); Assert.assertNull(fromDb(modelA)); Assert.assertNull(fromDb(modelB)); Assert.assertNull(fromDb(modelC)); } @Test public final void shouldNotFallIntoInfiniteCycleDuringCascadeHasManyNullification2entities() { // given Entity modelA = buildEntity(aDataDefinition); Entity modelB = buildEntity(bDataDefinition); modelA.setField(FIELD_BT_B_NULLIFY, modelB.getId()); modelB.setField(FIELD_BT_A_NULLIFY, modelA.getId()); modelA = fromDb(save(modelA)); modelB = fromDb(save(modelB)); // when EntityOpResult result = aDataDefinition.delete(modelA.getId()); // then Assert.assertTrue(result.isSuccessfull()); Assert.assertNull(fromDb(modelA)); Assert.assertNotNull(fromDb(modelB)); Assert.assertNull(fromDb(modelB).getField(FIELD_BT_A_NULLIFY)); } @Test public final void shouldNotFallIntoInfiniteCycleDuringCascadeHasManyNullification3entities() { // given Entity modelA = buildEntity(aDataDefinition); Entity modelB = buildEntity(bDataDefinition); Entity modelC = buildEntity(cDataDefinition); modelA.setField(FIELD_BT_C_NULLIFY, modelC.getId()); // modelC.setField("nullifyHasManyA", Lists.newArrayList(modelA)); modelB.setField(FIELD_BT_A_NULLIFY, modelA.getId()); // modelA.setField("nullifyHasManyB", Lists.newArrayList(modelB)); modelC.setField(FIELD_BT_B_NULLIFY, modelB.getId()); // modelB.setField("nullifyHasManyC", Lists.newArrayList(modelC)); modelC = fromDb(save(modelC)); modelB = fromDb(save(modelB)); modelA = fromDb(save(modelA)); // when EntityOpResult result = aDataDefinition.delete(modelA.getId()); // then Assert.assertTrue(result.isSuccessfull()); Assert.assertNull(fromDb(modelA)); Assert.assertNotNull(fromDb(modelB)); Assert.assertNull(fromDb(modelB).getField(FIELD_BT_A_NULLIFY)); Assert.assertNotNull(fromDb(modelC)); Assert.assertNotNull(fromDb(modelC).getField(FIELD_BT_B_NULLIFY)); } }