package org.activityinfo.server.command;
/*
* #%L
* ActivityInfo Server
* %%
* Copyright (C) 2009 - 2013 UNICEF
* %%
* This program 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.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #L%
*/
import com.extjs.gxt.ui.client.data.BaseModelData;
import org.activityinfo.fixtures.InjectionSupport;
import org.activityinfo.legacy.shared.command.CloneDatabase;
import org.activityinfo.legacy.shared.command.GetActivityForm;
import org.activityinfo.legacy.shared.command.GetFormClass;
import org.activityinfo.legacy.shared.command.GetSchema;
import org.activityinfo.legacy.shared.command.result.CreateResult;
import org.activityinfo.legacy.shared.exception.CommandException;
import org.activityinfo.legacy.shared.model.*;
import org.activityinfo.model.form.FormClass;
import org.activityinfo.model.form.FormElement;
import org.activityinfo.model.form.FormField;
import org.activityinfo.model.form.FormSection;
import org.activityinfo.model.type.ReferenceType;
import org.activityinfo.model.type.enumerated.EnumType;
import org.activityinfo.server.database.OnDataSet;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
/**
* @author yuriyz on 11/26/2014.
*/
@RunWith(InjectionSupport.class)
@OnDataSet("/dbunit/clone-database.db.xml")
public class CloneDatabaseTest extends CommandTestCase2 {
public static final int PEAR_DATABASE_ID = 1;
public static final int UKRAINE_COUNTRY_ID = 2;
@Test
public void fullClone() throws CommandException {
SchemaDTO schema = execute(new GetSchema());
UserDatabaseDTO pearDb = schema.getDatabaseById(PEAR_DATABASE_ID);
CloneDatabase cloneDatabase = new CloneDatabase()
.setSourceDatabaseId(pearDb.getId())
.setCopyData(true)
.setCopyPartners(true)
.setCopyUserPermissions(true)
.setCountryId(pearDb.getCountry().getId())
.setName("PearClone")
.setDescription("PearClone Description");
CreateResult cloneResult = execute(cloneDatabase);
assertNotEquals(cloneResult.getNewId(), pearDb.getId());
assertDbCloned(cloneResult.getNewId(), pearDb.getId(), cloneDatabase);
// todo assert data copy
}
@Test
public void cloneWithoutDataCopy() throws CommandException {
SchemaDTO schema = execute(new GetSchema());
UserDatabaseDTO pearDb = schema.getDatabaseById(PEAR_DATABASE_ID);
CloneDatabase cloneDatabase = new CloneDatabase()
.setSourceDatabaseId(pearDb.getId())
.setCopyData(false)
.setCopyPartners(true)
.setCopyUserPermissions(true)
.setCountryId(pearDb.getCountry().getId())
.setName("PearClone")
.setDescription("PearClone Description");
CreateResult cloneResult = execute(cloneDatabase);
assertNotEquals(cloneResult.getNewId(), pearDb.getId());
assertDbCloned(cloneResult.getNewId(), pearDb.getId(), cloneDatabase);
}
// https://bedatadriven.atlassian.net/browse/AI-315
// it seems that the problem occurs when there is a classic form with a "Village" location. If you change countries,
// you need to change the locationTypeId to a location type in the new country. Users can change the location type afterwards,
// so I would suggest that we apply a simple rule:
//
// 1. If there is a location type with the same name in the new country, use that location Type
// 2. if the source locationtype is bound to an adminlevel, choose the first root adminlevel in the new country
// 3. If the source locationtype is the null location type ( = Country) then the use the corresponding null locationtype in the new form
// 4. Otherwise use the "Village" location type in the target country.
@Test
public void cloneWithDifferentCountry() throws CommandException {
SchemaDTO schema = execute(new GetSchema());
UserDatabaseDTO pearDb = schema.getDatabaseById(PEAR_DATABASE_ID);
CloneDatabase cloneDatabase = new CloneDatabase()
.setSourceDatabaseId(pearDb.getId())
.setCopyData(false)
.setCopyPartners(true)
.setCopyUserPermissions(true)
.setCountryId(UKRAINE_COUNTRY_ID)
.setName("PearClone")
.setDescription("PearClone Description");
CreateResult cloneResult = execute(cloneDatabase);
assertNotEquals(cloneResult.getNewId(), pearDb.getId());
assertDbCloned(cloneResult.getNewId(), pearDb.getId(), cloneDatabase);
}
private UserDatabaseDTO assertDbCloned(int newDbId, int sourceDbId, CloneDatabase cloneDatabase) {
assertNotEquals(newDbId, sourceDbId);
SchemaDTO schema = execute(new GetSchema());
UserDatabaseDTO sourceDb = schema.getDatabaseById(sourceDbId);
UserDatabaseDTO targetDb = schema.getDatabaseById(newDbId);
assertEquals(targetDb.getName(), "PearClone");
assertEquals(targetDb.getFullName(), "PearClone Description");
assertFormClassesCloned(sourceDb, targetDb);
if (cloneDatabase.isCopyPartners()) {
assertPropertyForEach(sourceDb.getPartners(), targetDb.getPartners(),
"name", "fullName");
}
if (cloneDatabase.isCopyUserPermissions()) {
// todo
}
if (cloneDatabase.isCopyData()) {
// todo assert data
}
return targetDb;
}
private void assertFormClassesCloned(UserDatabaseDTO sourceDb, UserDatabaseDTO targetDb) {
assertEquals(sourceDb.getActivities().size(), targetDb.getActivities().size());
for (ActivityDTO activityDTO : sourceDb.getActivities()) {
ActivityFormDTO sourceActivity = execute(new GetActivityForm(activityDTO.getId()));
ActivityFormDTO targetActivity = execute(new GetActivityForm(entityByName(targetDb.getActivities(), sourceActivity.getName()).getId()));
// legacy level
assertActivityClone(sourceActivity, targetActivity);
// form class level
FormClass sourceFormClass = execute(new GetFormClass(sourceActivity.getResourceId())).getFormClass();
FormClass targetFormClass = execute(new GetFormClass(targetActivity.getResourceId())).getFormClass();
assertFormClass(sourceFormClass, targetFormClass);
}
}
private void assertActivityClone(ActivityFormDTO sourceActivity, ActivityFormDTO targetActivity) {
assertProperties(sourceActivity, targetActivity,
"name", "category", "classicView");
// indicators
assertPropertyForEach(sourceActivity.getIndicators(), targetActivity.getIndicators(),
"name", "units", "expression", "skipExpression", "nameInExpression", "calculatedAutomatically");
// attributes groups
for (AttributeGroupDTO sourceAttributeGroup : sourceActivity.getAttributeGroups()) {
AttributeGroupDTO targetGroup = entityByName(targetActivity.getAttributeGroups(), sourceAttributeGroup.getName());
assertProperties(sourceAttributeGroup, targetGroup,
"name", "mandatory", "defaultValue", "workflow", "multipleAllowed");
// attributes
assertPropertyForEach(sourceAttributeGroup.getAttributes(), targetGroup.getAttributes(), "name");
}
}
private void assertFormClass(FormClass sourceFormClass, FormClass targetFormClass) {
assertNotEquals(sourceFormClass.getId(), targetFormClass.getId());
assertEquals(sourceFormClass.getLabel(), targetFormClass.getLabel());
assertEquals(sourceFormClass.getDescription(), targetFormClass.getDescription());
// fields
for (FormField sourceField : sourceFormClass.getFields()) {
FormField targetField = (FormField) elementByName(targetFormClass.getElements(), sourceField.getLabel());
assertNotEquals(sourceField.getId(), targetField.getId());
assertEquals(sourceField.getDescription(), targetField.getDescription());
assertEquals(sourceField.getCode(), targetField.getCode());
assertEquals(sourceField.getRelevanceConditionExpression(), targetField.getRelevanceConditionExpression());
assertEquals(sourceField.getType().getTypeClass(), targetField.getType().getTypeClass());
// todo
if (sourceField.getType() instanceof ReferenceType) {
// need something more sophisticated to check equality of ReferenceType
} else if (sourceField.getType() instanceof EnumType) {
// need something more sophisticated to check equality of ReferenceType
}
}
// sections
for (FormSection sourceSection : sourceFormClass.getSections()) {
FormSection targetSection = (FormSection) elementByName(targetFormClass.getElements(), sourceSection.getLabel());
assertNotEquals(sourceSection.getId(), targetSection.getId());
assertEquals(sourceSection.getLabel(), targetSection.getLabel());
}
}
private static <T extends BaseModelData> void assertPropertyForEach(List<T> sourceList, List<T> targetList, String... properties) {
assertEquals(sourceList.size(), targetList.size());
for (T sourceItem : sourceList) {
T targetTarget = entityByName(targetList, (String) sourceItem.get("name"));
assertProperties(sourceItem, targetTarget, properties);
}
}
private static <T extends BaseModelData> void assertProperties(T entity1, T entity2, String... propertyNames) {
if (propertyNames == null || propertyNames.length == 0) {
throw new RuntimeException("No property names specified.");
}
assertNotEquals(entity1.get("id"), entity2.get("id"));
for (String property : propertyNames) {
assertEquals("Failed assert property: " + property + ", sourceId: " + entity1.get("id") + ", targetId: " + entity2.get("id"),
entity1.get(property), entity2.get(property));
}
}
private static <T extends BaseModelData> T entityByName(List<T> entities, String name) {
for (T entity : entities) {
if (name.equals(entity.get("name"))) {
return entity;
}
}
throw new RuntimeException("No entity with name:" + name);
}
private static <T extends FormElement> T elementByName(List<T> list, String label) {
for (T entity : list) {
if (label.equals(entity.getLabel())) {
return entity;
}
}
throw new RuntimeException("No FormElement with label:" + label);
}
}