From cb9fedebcb057af6263f59f5eabfdd4503263f17 Mon Sep 17 00:00:00 2001 From: Robert Goldmann <deadlocker@gmx.de> Date: Mon, 28 Mar 2022 22:53:22 +0200 Subject: [PATCH] #691 - started extracting importer classes --- .../budgetmaster/charts/Chart.java | 3 +- .../database/importer/AccountImporter.java | 79 +++ .../database/importer/CategoryImporter.java | 67 +++ .../database/importer/ChartImporter.java | 39 ++ .../database/importer/IconImporter.java | 36 ++ .../database/importer/ImageImporter.java | 33 ++ .../database/importer/ItemImporter.java | 74 +++ .../budgetmaster/services/EntityType.java | 45 +- .../services/ImportResultItem.java | 17 +- .../budgetmaster/services/ImportService.java | 407 +-------------- .../budgetmaster/utils/ProvidesID.java | 2 + .../unit/database/ImportServiceTest.java | 487 +++--------------- .../importer/AccountImporterTest.java | 96 ++++ .../importer/CategoryImporterTest.java | 109 ++++ .../database/importer/ChartImporterTest.java | 51 ++ .../database/importer/IconImporterTest.java | 68 +++ .../database/importer/ImageImporterTest.java | 60 +++ 17 files changed, 840 insertions(+), 833 deletions(-) create mode 100644 src/main/java/de/deadlocker8/budgetmaster/database/importer/AccountImporter.java create mode 100644 src/main/java/de/deadlocker8/budgetmaster/database/importer/CategoryImporter.java create mode 100644 src/main/java/de/deadlocker8/budgetmaster/database/importer/ChartImporter.java create mode 100644 src/main/java/de/deadlocker8/budgetmaster/database/importer/IconImporter.java create mode 100644 src/main/java/de/deadlocker8/budgetmaster/database/importer/ImageImporter.java create mode 100644 src/main/java/de/deadlocker8/budgetmaster/database/importer/ItemImporter.java create mode 100644 src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/AccountImporterTest.java create mode 100644 src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/CategoryImporterTest.java create mode 100644 src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/ChartImporterTest.java create mode 100644 src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/IconImporterTest.java create mode 100644 src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/ImageImporterTest.java diff --git a/src/main/java/de/deadlocker8/budgetmaster/charts/Chart.java b/src/main/java/de/deadlocker8/budgetmaster/charts/Chart.java index 45362237f..36ab65fa6 100644 --- a/src/main/java/de/deadlocker8/budgetmaster/charts/Chart.java +++ b/src/main/java/de/deadlocker8/budgetmaster/charts/Chart.java @@ -1,6 +1,7 @@ package de.deadlocker8.budgetmaster.charts; import com.google.gson.annotations.Expose; +import de.deadlocker8.budgetmaster.utils.ProvidesID; import javax.persistence.Column; import javax.persistence.Entity; @@ -10,7 +11,7 @@ import javax.validation.constraints.Size; import java.util.Objects; @Entity -public class Chart +public class Chart implements ProvidesID { @Id @Expose diff --git a/src/main/java/de/deadlocker8/budgetmaster/database/importer/AccountImporter.java b/src/main/java/de/deadlocker8/budgetmaster/database/importer/AccountImporter.java new file mode 100644 index 000000000..a010dd422 --- /dev/null +++ b/src/main/java/de/deadlocker8/budgetmaster/database/importer/AccountImporter.java @@ -0,0 +1,79 @@ +package de.deadlocker8.budgetmaster.database.importer; + +import de.deadlocker8.budgetmaster.accounts.Account; +import de.deadlocker8.budgetmaster.accounts.AccountRepository; +import de.deadlocker8.budgetmaster.database.accountmatches.AccountMatch; +import de.deadlocker8.budgetmaster.database.accountmatches.AccountMatchList; +import de.deadlocker8.budgetmaster.icon.Icon; +import de.deadlocker8.budgetmaster.services.EntityType; +import de.deadlocker8.budgetmaster.services.ImportResultItem; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; + +public class AccountImporter extends ItemImporter<Account> +{ + private static final Logger LOGGER = LoggerFactory.getLogger(AccountImporter.class); + + public AccountImporter(AccountRepository accountRepository) + { + super(accountRepository, EntityType.ACCOUNT, false); + } + + public ImportResultItem importItems(List<Account> accounts, AccountMatchList accountMatchList) + { + LOGGER.debug(MessageFormat.format("Importing {0} {1}...", accountMatchList.getAccountMatches().size(), entityType.getAllItemsName())); + final List<String> collectedErrorMessages = new ArrayList<>(); + int numberOfImportedItems = 0; + + for(AccountMatch accountMatch : accountMatchList.getAccountMatches()) + { + LOGGER.debug(MessageFormat.format("Importing account {0} -> {1}", accountMatch.getAccountSource().getName(), accountMatch.getAccountDestination().getName())); + + try + { + final Account sourceAccount = accounts.stream() + .filter(account -> account.getID().equals(accountMatch.getAccountSource().getID())) + .findFirst() + .orElseThrow(); + + final Account destinationAccount = repository.findById(accountMatch.getAccountDestination().getID()).orElseThrow(); + + final Icon sourceIcon = sourceAccount.getIconReference(); + if(sourceIcon != null) + { + LOGGER.debug("Overwriting destination account icon"); + destinationAccount.setIconReference(sourceIcon); + repository.save(destinationAccount); + } + + sourceAccount.setID(destinationAccount.getID()); + numberOfImportedItems++; + } + catch(Exception e) + { + final String errorMessage = MessageFormat.format("Error while importing {0} \"{1}\"", entityType.getSingleItemName(), getNameForItem(accountMatch.getAccountSource())); + LOGGER.error(errorMessage, e); + collectedErrorMessages.add(formatErrorMessage(errorMessage, e)); + } + } + + LOGGER.debug(MessageFormat.format("Importing accounts DONE ({0}/{1})", numberOfImportedItems, accountMatchList.getAccountMatches().size())); + return new ImportResultItem(entityType, numberOfImportedItems, accountMatchList.getAccountMatches().size(), collectedErrorMessages); + } + + @Override + protected int importSingleItem(Account account) + { + throw new UnsupportedOperationException(); + } + + @Override + protected String getNameForItem(Account item) + { + return item.getName(); + } +} diff --git a/src/main/java/de/deadlocker8/budgetmaster/database/importer/CategoryImporter.java b/src/main/java/de/deadlocker8/budgetmaster/database/importer/CategoryImporter.java new file mode 100644 index 000000000..8e1ef2e6b --- /dev/null +++ b/src/main/java/de/deadlocker8/budgetmaster/database/importer/CategoryImporter.java @@ -0,0 +1,67 @@ +package de.deadlocker8.budgetmaster.database.importer; + +import de.deadlocker8.budgetmaster.categories.Category; +import de.deadlocker8.budgetmaster.categories.CategoryRepository; +import de.deadlocker8.budgetmaster.categories.CategoryType; +import de.deadlocker8.budgetmaster.services.EntityType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.text.MessageFormat; + +public class CategoryImporter extends ItemImporter<Category> +{ + private static final Logger LOGGER = LoggerFactory.getLogger(CategoryImporter.class); + + public CategoryImporter(CategoryRepository categoryRepository) + { + super(categoryRepository, EntityType.CATEGORY, false); + } + + @Override + protected int importSingleItem(Category category) + { + if(!(repository instanceof CategoryRepository repository)) + { + throw new IllegalArgumentException("Invalid repository type"); + } + + LOGGER.debug(MessageFormat.format("Importing category {0}", category.getName())); + + Category existingCategory; + if(category.getType().equals(CategoryType.NONE) || category.getType().equals(CategoryType.REST)) + { + existingCategory = repository.findByType(category.getType()); + } + else + { + existingCategory = repository.findByNameAndColorAndType(category.getName(), category.getColor(), category.getType()); + } + + int newCategoryID; + if(existingCategory == null) + { + //category does not exist --> create it + LOGGER.debug(MessageFormat.format("No matching category found for category \"{0}\". Creating new one...", category.getName())); + + Category categoryToCreate = new Category(category.getName(), category.getColor(), category.getType(), category.getIconReference()); + repository.save(categoryToCreate); + + Category newCategory = repository.findByNameAndColorAndType(category.getName(), category.getColor(), category.getType()); + newCategoryID = newCategory.getID(); + } + else + { + //category already exists + newCategoryID = existingCategory.getID(); + LOGGER.debug(MessageFormat.format("Found matching category with ID: {0} for category \"{1}\".", newCategoryID, category.getName())); + } + return newCategoryID; + } + + @Override + protected String getNameForItem(Category item) + { + return item.getName(); + } +} diff --git a/src/main/java/de/deadlocker8/budgetmaster/database/importer/ChartImporter.java b/src/main/java/de/deadlocker8/budgetmaster/database/importer/ChartImporter.java new file mode 100644 index 000000000..6be82d801 --- /dev/null +++ b/src/main/java/de/deadlocker8/budgetmaster/database/importer/ChartImporter.java @@ -0,0 +1,39 @@ +package de.deadlocker8.budgetmaster.database.importer; + +import de.deadlocker8.budgetmaster.charts.Chart; +import de.deadlocker8.budgetmaster.charts.ChartRepository; +import de.deadlocker8.budgetmaster.charts.ChartService; +import de.deadlocker8.budgetmaster.services.EntityType; + +public class ChartImporter extends ItemImporter<Chart> +{ + private final ChartService chartService; + + public ChartImporter(ChartService chartService) + { + super(chartService.getRepository(), EntityType.CHART, true); + this.chartService = chartService; + } + + @Override + protected int importSingleItem(Chart chart) + { + if(!(repository instanceof ChartRepository repository)) + { + throw new IllegalArgumentException("Invalid repository type"); + } + + final int highestUsedID = chartService.getHighestUsedID(); + chart.setID(highestUsedID + 1); + + repository.save(chart); + + return chart.getID(); + } + + @Override + protected String getNameForItem(Chart item) + { + return item.getName(); + } +} diff --git a/src/main/java/de/deadlocker8/budgetmaster/database/importer/IconImporter.java b/src/main/java/de/deadlocker8/budgetmaster/database/importer/IconImporter.java new file mode 100644 index 000000000..e9b818147 --- /dev/null +++ b/src/main/java/de/deadlocker8/budgetmaster/database/importer/IconImporter.java @@ -0,0 +1,36 @@ +package de.deadlocker8.budgetmaster.database.importer; + +import de.deadlocker8.budgetmaster.icon.Icon; +import de.deadlocker8.budgetmaster.icon.IconRepository; +import de.deadlocker8.budgetmaster.services.EntityType; + +public class IconImporter extends ItemImporter<Icon> +{ + public IconImporter(IconRepository iconRepository) + { + super(iconRepository, EntityType.ICON, true); + } + + @Override + protected int importSingleItem(Icon icon) + { + if(!(repository instanceof IconRepository repository)) + { + throw new IllegalArgumentException("Invalid repository type"); + } + + final Icon iconToCreate = new Icon(); + iconToCreate.setImage(icon.getImage()); + iconToCreate.setBuiltinIdentifier(icon.getBuiltinIdentifier()); + + final Icon savedIcon = repository.save(iconToCreate); + + return savedIcon.getID(); + } + + @Override + protected String getNameForItem(Icon item) + { + return String.valueOf(item.getID()); + } +} diff --git a/src/main/java/de/deadlocker8/budgetmaster/database/importer/ImageImporter.java b/src/main/java/de/deadlocker8/budgetmaster/database/importer/ImageImporter.java new file mode 100644 index 000000000..ccce64132 --- /dev/null +++ b/src/main/java/de/deadlocker8/budgetmaster/database/importer/ImageImporter.java @@ -0,0 +1,33 @@ +package de.deadlocker8.budgetmaster.database.importer; + +import de.deadlocker8.budgetmaster.images.Image; +import de.deadlocker8.budgetmaster.images.ImageRepository; +import de.deadlocker8.budgetmaster.services.EntityType; + +public class ImageImporter extends ItemImporter<Image> +{ + public ImageImporter(ImageRepository imageRepository) + { + super(imageRepository, EntityType.IMAGE, true); + } + + @Override + protected int importSingleItem(Image image) + { + if(!(repository instanceof ImageRepository repository)) + { + throw new IllegalArgumentException("Invalid repository type"); + } + + Image imageToCreate = new Image(image.getImage(), image.getFileName(), image.getFileExtension()); + + final Image savedImage = repository.save(imageToCreate); + return savedImage.getID(); + } + + @Override + protected String getNameForItem(Image item) + { + return String.valueOf(item.getID()); + } +} diff --git a/src/main/java/de/deadlocker8/budgetmaster/database/importer/ItemImporter.java b/src/main/java/de/deadlocker8/budgetmaster/database/importer/ItemImporter.java new file mode 100644 index 000000000..a4059f1ae --- /dev/null +++ b/src/main/java/de/deadlocker8/budgetmaster/database/importer/ItemImporter.java @@ -0,0 +1,74 @@ +package de.deadlocker8.budgetmaster.database.importer; + +import de.deadlocker8.budgetmaster.services.EntityType; +import de.deadlocker8.budgetmaster.services.ImportResultItem; +import de.deadlocker8.budgetmaster.utils.ProvidesID; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; + +public abstract class ItemImporter<T extends ProvidesID> +{ + private static final Logger LOGGER = LoggerFactory.getLogger(ItemImporter.class); + + protected final JpaRepository<T, Integer> repository; + protected final EntityType entityType; + protected final boolean alwaysCreateNewInstance; + + protected ItemImporter(JpaRepository<T, Integer> repository, EntityType entityType, boolean alwaysCreateNewInstance) + { + this.repository = repository; + this.entityType = entityType; + this.alwaysCreateNewInstance = alwaysCreateNewInstance; + } + + public ImportResultItem importItems(List<T> items) + { + LOGGER.debug(MessageFormat.format("Importing {0} {1}...", items.size(), entityType.getAllItemsName())); + final List<String> collectedErrorMessages = new ArrayList<>(); + int numberOfImportedItems = 0; + + for(int i = 0; i < items.size(); i++) + { + T item = items.get(i); + LOGGER.debug(MessageFormat.format("Importing {0} {1}/{2} (ID: {3})", entityType.getSingleItemName(), i + 1, items.size(), item.getID())); + + try + { + int oldID = item.getID(); + int newID = importSingleItem(item); + + if(oldID == newID && !alwaysCreateNewInstance) + { + numberOfImportedItems++; + continue; + } + + item.setID(newID); + numberOfImportedItems++; + } + catch(Exception e) + { + final String errorMessage = MessageFormat.format("Error while importing {0} \"{1}\"", entityType.getSingleItemName(), getNameForItem(item)); + LOGGER.error(errorMessage, e); + collectedErrorMessages.add(formatErrorMessage(errorMessage, e)); + } + } + + LOGGER.debug(MessageFormat.format("Importing {0} DONE ({1}/{2})", entityType.getAllItemsName(), numberOfImportedItems, items.size())); + return new ImportResultItem(entityType, numberOfImportedItems, items.size(), collectedErrorMessages); + } + + protected abstract int importSingleItem(T item); + + protected abstract String getNameForItem(T item); + + protected String formatErrorMessage(String errorMessage, Exception e) + { + return MessageFormat.format("{0}: {1} ({2})", errorMessage, e.getClass().getName(), e.getMessage()); + } +} diff --git a/src/main/java/de/deadlocker8/budgetmaster/services/EntityType.java b/src/main/java/de/deadlocker8/budgetmaster/services/EntityType.java index ee0a10e30..8947051c0 100644 --- a/src/main/java/de/deadlocker8/budgetmaster/services/EntityType.java +++ b/src/main/java/de/deadlocker8/budgetmaster/services/EntityType.java @@ -4,20 +4,21 @@ import de.deadlocker8.budgetmaster.utils.LocalizedEnum; public enum EntityType implements LocalizedEnum { - HOME("home", "background-blue", ImportRequired.NONE), - ACCOUNT("account_balance", "background-red", ImportRequired.REQUIRED), - TRANSACTION("list", "background-blue-baby", ImportRequired.REQUIRED), - TEMPLATE("file_copy", "background-orange-dark", ImportRequired.OPTIONAL), - CHART("show_chart", "background-purple", ImportRequired.OPTIONAL), - REPORT("description", "background-green", ImportRequired.NONE), - CATEGORY("label", "background-orange", ImportRequired.REQUIRED), - TAGS("local_offer", "background-grey", ImportRequired.NONE), - STATISTICS("insert_chart", "background-grey", ImportRequired.NONE), - SETTINGS("settings", "background-red", ImportRequired.NONE), - IMAGE("image", "background-grey", ImportRequired.REQUIRED), - HOTKEYS("keyboard", "background-grey", ImportRequired.NONE), - ABOUT("info", "background-grey", ImportRequired.NONE), - TEMPLATE_GROUP("folder", "background-orange-dark", ImportRequired.OPTIONAL); + HOME("home", "background-blue", ImportRequired.NONE, "categories", "category"), + ACCOUNT("account_balance", "background-red", ImportRequired.REQUIRED, "accounts", "account"), + TRANSACTION("list", "background-blue-baby", ImportRequired.REQUIRED, "transactions", "transaction"), + TEMPLATE("file_copy", "background-orange-dark", ImportRequired.OPTIONAL, "templates", "template"), + CHART("show_chart", "background-purple", ImportRequired.OPTIONAL, "charts", "chart"), + REPORT("description", "background-green", ImportRequired.NONE, null, null), + CATEGORY("label", "background-orange", ImportRequired.REQUIRED, "categories", "category"), + TAGS("local_offer", "background-grey", ImportRequired.NONE, null, null), + STATISTICS("insert_chart", "background-grey", ImportRequired.NONE, null, null), + SETTINGS("settings", "background-red", ImportRequired.NONE, null, null), + IMAGE("image", "background-grey", ImportRequired.REQUIRED, "images", "image"), + HOTKEYS("keyboard", "background-grey", ImportRequired.NONE, null, null), + ABOUT("info", "background-grey", ImportRequired.NONE, null, null), + TEMPLATE_GROUP("folder", "background-orange-dark", ImportRequired.OPTIONAL, "template groups", "template group"), + ICON("icon", "background-grey", ImportRequired.NONE, "icons", "icon"); public enum ImportRequired @@ -30,12 +31,16 @@ public enum EntityType implements LocalizedEnum private final String icon; private final String color; private final ImportRequired importRequired; + private final String allItemsName; + private final String singleItemName; - EntityType(String icon, String color, ImportRequired importRequired) + EntityType(String icon, String color, ImportRequired importRequired, String allItemsName, String singleItemName) { this.icon = icon; this.color = color; this.importRequired = importRequired; + this.allItemsName = allItemsName; + this.singleItemName = singleItemName; } public String getIcon() @@ -53,6 +58,16 @@ public enum EntityType implements LocalizedEnum return importRequired; } + public String getAllItemsName() + { + return allItemsName; + } + + public String getSingleItemName() + { + return singleItemName; + } + public String getColorAsTextColor() { return color.replace("background", "text"); diff --git a/src/main/java/de/deadlocker8/budgetmaster/services/ImportResultItem.java b/src/main/java/de/deadlocker8/budgetmaster/services/ImportResultItem.java index 37b604832..d766c4891 100644 --- a/src/main/java/de/deadlocker8/budgetmaster/services/ImportResultItem.java +++ b/src/main/java/de/deadlocker8/budgetmaster/services/ImportResultItem.java @@ -1,5 +1,6 @@ package de.deadlocker8.budgetmaster.services; +import java.util.List; import java.util.Objects; public class ImportResultItem @@ -7,12 +8,15 @@ public class ImportResultItem private final EntityType entityType; private final int numberOfImportedItems; private final int numberOfAvailableItems; + private final List<String> collectedErrorMessages; - public ImportResultItem(EntityType entityType, int numberOfImportedItems, int numberOfAvailableItems) + + public ImportResultItem(EntityType entityType, int numberOfImportedItems, int numberOfAvailableItems, List<String> collectedErrorMessages) { this.entityType = entityType; this.numberOfImportedItems = numberOfImportedItems; this.numberOfAvailableItems = numberOfAvailableItems; + this.collectedErrorMessages = collectedErrorMessages; } public EntityType getEntityType() @@ -30,19 +34,25 @@ public class ImportResultItem return numberOfAvailableItems; } + public List<String> getCollectedErrorMessages() + { + return collectedErrorMessages; + } + @Override public boolean equals(Object o) { + if(this == o) return true; if(o == null || getClass() != o.getClass()) return false; ImportResultItem that = (ImportResultItem) o; - return numberOfImportedItems == that.numberOfImportedItems && numberOfAvailableItems == that.numberOfAvailableItems && entityType == that.entityType; + return numberOfImportedItems == that.numberOfImportedItems && numberOfAvailableItems == that.numberOfAvailableItems && entityType == that.entityType && Objects.equals(collectedErrorMessages, that.collectedErrorMessages); } @Override public int hashCode() { - return Objects.hash(entityType, numberOfImportedItems, numberOfAvailableItems); + return Objects.hash(entityType, numberOfImportedItems, numberOfAvailableItems, collectedErrorMessages); } @Override @@ -52,6 +62,7 @@ public class ImportResultItem "entityType=" + entityType + ", numberOfImportedItems=" + numberOfImportedItems + ", numberOfAvailableItems=" + numberOfAvailableItems + + ", collectedErrorMessages=" + collectedErrorMessages + '}'; } } diff --git a/src/main/java/de/deadlocker8/budgetmaster/services/ImportService.java b/src/main/java/de/deadlocker8/budgetmaster/services/ImportService.java index 3e723c17a..1dad5b5ae 100644 --- a/src/main/java/de/deadlocker8/budgetmaster/services/ImportService.java +++ b/src/main/java/de/deadlocker8/budgetmaster/services/ImportService.java @@ -2,18 +2,19 @@ package de.deadlocker8.budgetmaster.services; import de.deadlocker8.budgetmaster.accounts.Account; import de.deadlocker8.budgetmaster.accounts.AccountRepository; -import de.deadlocker8.budgetmaster.categories.Category; import de.deadlocker8.budgetmaster.categories.CategoryRepository; -import de.deadlocker8.budgetmaster.categories.CategoryType; import de.deadlocker8.budgetmaster.charts.Chart; import de.deadlocker8.budgetmaster.charts.ChartService; import de.deadlocker8.budgetmaster.database.InternalDatabase; import de.deadlocker8.budgetmaster.database.accountmatches.AccountMatch; import de.deadlocker8.budgetmaster.database.accountmatches.AccountMatchList; +import de.deadlocker8.budgetmaster.database.importer.*; import de.deadlocker8.budgetmaster.icon.Icon; +import de.deadlocker8.budgetmaster.icon.IconRepository; import de.deadlocker8.budgetmaster.icon.IconService; import de.deadlocker8.budgetmaster.icon.Iconizable; import de.deadlocker8.budgetmaster.images.Image; +import de.deadlocker8.budgetmaster.images.ImageRepository; import de.deadlocker8.budgetmaster.images.ImageService; import de.deadlocker8.budgetmaster.repeating.RepeatingTransactionUpdater; import de.deadlocker8.budgetmaster.tags.Tag; @@ -48,10 +49,10 @@ public class ImportService private final TemplateRepository templateRepository; private final TagRepository tagRepository; private final ChartService chartService; - private final ImageService imageService; + private final ImageRepository imageRepository; private final RepeatingTransactionUpdater repeatingTransactionUpdater; private final AccountRepository accountRepository; - private final IconService iconService; + private final IconRepository iconRepository; private InternalDatabase database; @@ -59,7 +60,7 @@ public class ImportService @Autowired public ImportService(CategoryRepository categoryRepository, TransactionRepository transactionRepository, TemplateGroupRepository templateGroupRepository, TemplateRepository templateRepository, - TagRepository tagRepository, ChartService chartService, ImageService imageService, RepeatingTransactionUpdater repeatingTransactionUpdater, AccountRepository accountRepository, IconService iconService) + TagRepository tagRepository, ChartService chartService, ImageRepository imageRepository, RepeatingTransactionUpdater repeatingTransactionUpdater, AccountRepository accountRepository, IconRepository iconRepository) { this.categoryRepository = categoryRepository; this.transactionRepository = transactionRepository; @@ -67,10 +68,10 @@ public class ImportService this.templateRepository = templateRepository; this.tagRepository = tagRepository; this.chartService = chartService; - this.imageService = imageService; + this.imageRepository = imageRepository; this.repeatingTransactionUpdater = repeatingTransactionUpdater; this.accountRepository = accountRepository; - this.iconService = iconService; + this.iconRepository = iconRepository; } public List<ImportResultItem> importDatabase(InternalDatabase database, AccountMatchList accountMatchList, Boolean importTemplateGroups, Boolean importTemplates, Boolean importCharts) @@ -81,10 +82,10 @@ public class ImportService final List<ImportResultItem> importResultItems = new ArrayList<>(); LOGGER.debug("Importing database..."); - importResultItems.add(importImages()); - importIcons(); - importResultItems.add(importCategories()); - importResultItems.add(importAccounts(accountMatchList)); + importResultItems.add(new ImageImporter(imageRepository).importItems(database.getImages())); + new IconImporter(iconRepository).importItems(database.getIcons()); + importResultItems.add(new CategoryImporter(categoryRepository).importItems(database.getCategories())); + importResultItems.add(new AccountImporter(accountRepository).importItems(database.getAccounts(), accountMatchList)); importResultItems.add(importTransactions()); if(importTemplateGroups) @@ -93,7 +94,7 @@ public class ImportService } else { - importResultItems.add(new ImportResultItem(EntityType.TEMPLATE_GROUP, 0, 0)); + importResultItems.add(new ImportResultItem(EntityType.TEMPLATE_GROUP, 0, 0, collectedErrorMessages)); } if(importTemplates) @@ -102,16 +103,16 @@ public class ImportService } else { - importResultItems.add(new ImportResultItem(EntityType.TEMPLATE, 0, 0)); + importResultItems.add(new ImportResultItem(EntityType.TEMPLATE, 0, 0, collectedErrorMessages)); } if(importCharts) { - importResultItems.add(importCharts()); + importResultItems.add(new ChartImporter(chartService).importItems(database.getCharts())); } else { - importResultItems.add(new ImportResultItem(EntityType.CHART, 0, 0)); + importResultItems.add(new ImportResultItem(EntityType.CHART, 0, 0, collectedErrorMessages)); } LOGGER.debug("Updating repeating transactions..."); @@ -136,209 +137,6 @@ public class ImportService return MessageFormat.format("{0}: {1} ({2})", errorMessage, e.getClass().getName(), e.getMessage()); } - private ImportResultItem importCategories() - { - List<Category> categories = database.getCategories(); - LOGGER.debug(MessageFormat.format("Importing {0} categories...", categories.size())); - List<TransactionBase> alreadyUpdatedTransactions = new ArrayList<>(); - List<TransactionBase> alreadyUpdatedTemplates = new ArrayList<>(); - int numberOfImportedCategories = 0; - - for(Category category : categories) - { - LOGGER.debug(MessageFormat.format("Importing category {0}", category.getName())); - - try - { - int oldCategoryID = category.getID(); - int newCategoryID = importCategory(category); - - if(oldCategoryID == newCategoryID) - { - numberOfImportedCategories++; - continue; - } - - List<TransactionBase> transactions = new ArrayList<>(database.getTransactions()); - transactions.removeAll(alreadyUpdatedTransactions); - alreadyUpdatedTransactions.addAll(updateCategoriesForItems(transactions, oldCategoryID, newCategoryID)); - - List<TransactionBase> templates = new ArrayList<>(database.getTemplates()); - templates.removeAll(alreadyUpdatedTemplates); - alreadyUpdatedTemplates.addAll(updateCategoriesForItems(templates, oldCategoryID, newCategoryID)); - - numberOfImportedCategories++; - } - catch(Exception e) - { - final String errorMessage = MessageFormat.format("Error while importing category with name \"{0}\"", category.getName()); - LOGGER.error(errorMessage, e); - collectedErrorMessages.add(formatErrorMessage(errorMessage, e)); - } - } - - LOGGER.debug(MessageFormat.format("Importing categories DONE ({0}/{1})", numberOfImportedCategories, categories.size())); - return new ImportResultItem(EntityType.CATEGORY, numberOfImportedCategories, categories.size()); - } - - private int importCategory(Category category) - { - Category existingCategory; - if(category.getType().equals(CategoryType.NONE) || category.getType().equals(CategoryType.REST)) - { - existingCategory = categoryRepository.findByType(category.getType()); - } - else - { - existingCategory = categoryRepository.findByNameAndColorAndType(category.getName(), category.getColor(), category.getType()); - } - - int newCategoryID; - if(existingCategory == null) - { - //category does not exist --> create it - Category categoryToCreate = new Category(category.getName(), category.getColor(), category.getType(), category.getIconReference()); - categoryRepository.save(categoryToCreate); - - Category newCategory = categoryRepository.findByNameAndColorAndType(category.getName(), category.getColor(), category.getType()); - newCategoryID = newCategory.getID(); - } - else - { - //category already exists - newCategoryID = existingCategory.getID(); - } - return newCategoryID; - } - - public List<TransactionBase> updateCategoriesForItems(List<TransactionBase> items, int oldCategoryID, int newCategoryID) - { - List<TransactionBase> updatedItems = new ArrayList<>(); - for(TransactionBase item : items) - { - final Category category = item.getCategory(); - if(category == null) - { - continue; - } - - if(category.getID() == oldCategoryID) - { - category.setID(newCategoryID); - updatedItems.add(item); - } - } - - return updatedItems; - } - - private ImportResultItem importAccounts(AccountMatchList accountMatchList) - { - LOGGER.debug(MessageFormat.format("Importing {0} accounts...", accountMatchList.getAccountMatches().size())); - List<TransactionBase> alreadyUpdatedTransactions = new ArrayList<>(); - List<TransactionBase> alreadyUpdatedTransferTransactions = new ArrayList<>(); - List<TransactionBase> alreadyUpdatedTemplates = new ArrayList<>(); - List<TransactionBase> alreadyUpdatedTransferTemplates = new ArrayList<>(); - - int numberOfImportedAccounts = 0; - - for(AccountMatch accountMatch : accountMatchList.getAccountMatches()) - { - LOGGER.debug(MessageFormat.format("Importing account {0} -> {1}", accountMatch.getAccountSource().getName(), accountMatch.getAccountDestination().getName())); - - try - { - Account sourceAccount = database.getAccounts().stream() - .filter(account -> account.getID().equals(accountMatch.getAccountSource().getID())) - .findFirst() - .orElseThrow(); - - Account destinationAccount = accountRepository.findById(accountMatch.getAccountDestination().getID()).orElseThrow(); - - Icon sourceIcon = sourceAccount.getIconReference(); - if(sourceIcon != null) - { - LOGGER.debug("Overwriting destination account icon"); - destinationAccount.setIconReference(sourceIcon); - accountRepository.save(destinationAccount); - } - - List<TransactionBase> transactions = new ArrayList<>(database.getTransactions()); - transactions.removeAll(alreadyUpdatedTransactions); - alreadyUpdatedTransactions.addAll(updateAccountsForItems(transactions, accountMatch.getAccountSource().getID(), accountMatch.getAccountDestination())); - - List<TransactionBase> transferTransactions = new ArrayList<>(database.getTransactions()); - transferTransactions.removeAll(alreadyUpdatedTransferTransactions); - alreadyUpdatedTransferTransactions.addAll(updateTransferAccountsForItems(transferTransactions, accountMatch.getAccountSource().getID(), accountMatch.getAccountDestination())); - - List<TransactionBase> templates = new ArrayList<>(database.getTemplates()); - templates.removeAll(alreadyUpdatedTemplates); - alreadyUpdatedTemplates.addAll(updateAccountsForItems(templates, accountMatch.getAccountSource().getID(), accountMatch.getAccountDestination())); - - List<TransactionBase> transferTemplates = new ArrayList<>(database.getTemplates()); - transferTemplates.removeAll(alreadyUpdatedTransferTemplates); - alreadyUpdatedTransferTemplates.addAll(updateTransferAccountsForItems(transferTemplates, accountMatch.getAccountSource().getID(), accountMatch.getAccountDestination())); - - numberOfImportedAccounts++; - } - catch(Exception e) - { - final String errorMessage = MessageFormat.format("Error while importing account with name \"{0}\"", accountMatch.getAccountSource().getName()); - LOGGER.error(errorMessage, e); - collectedErrorMessages.add(formatErrorMessage(errorMessage, e)); - } - } - - LOGGER.debug(MessageFormat.format("Importing accounts DONE ({0}/{1})", numberOfImportedAccounts, accountMatchList.getAccountMatches().size())); - return new ImportResultItem(EntityType.ACCOUNT, numberOfImportedAccounts, accountMatchList.getAccountMatches().size()); - } - - public List<TransactionBase> updateAccountsForItems(List<TransactionBase> items, int oldAccountID, Account newAccount) - { - List<TransactionBase> updatedTransactions = new ArrayList<>(); - for(TransactionBase item : items) - { - // legacy database - if(oldAccountID == -1) - { - item.setAccount(newAccount); - updatedTransactions.add(item); - continue; - } - - if(item.getAccount() == null) - { - continue; - } - - if(item.getAccount().getID() != oldAccountID) - { - continue; - } - - // account needs to be updated - item.setAccount(newAccount); - updatedTransactions.add(item); - } - - return updatedTransactions; - } - - public List<TransactionBase> updateTransferAccountsForItems(List<TransactionBase> transactions, int oldAccountID, Account newAccount) - { - List<TransactionBase> updatedTransactions = new ArrayList<>(); - for(TransactionBase transaction : transactions) - { - if(transaction.getTransferAccount() != null && transaction.getTransferAccount().getID() == oldAccountID) - { - transaction.setTransferAccount(newAccount); - updatedTransactions.add(transaction); - } - } - - return updatedTransactions; - } - private ImportResultItem importTransactions() { List<Transaction> transactions = database.getTransactions(); @@ -367,7 +165,7 @@ public class ImportService } LOGGER.debug(MessageFormat.format("Importing transactions DONE ({0}/{1})", numberOfImportedTransactions, transactions.size())); - return new ImportResultItem(EntityType.TRANSACTION, numberOfImportedTransactions, transactions.size()); + return new ImportResultItem(EntityType.TRANSACTION, numberOfImportedTransactions, transactions.size(), collectedErrorMessages); } public void updateTagsForItem(TransactionBase item) @@ -426,7 +224,7 @@ public class ImportService } LOGGER.debug(MessageFormat.format("Importing template groups DONE ({0}/{1})", numberOfImportedTemplateGroups, templateGroups.size())); - return new ImportResultItem(EntityType.TEMPLATE_GROUP, numberOfImportedTemplateGroups, templateGroups.size()); + return new ImportResultItem(EntityType.TEMPLATE_GROUP, numberOfImportedTemplateGroups, templateGroups.size(), collectedErrorMessages); } private int importTemplateGroup(TemplateGroup templateGroup) @@ -513,173 +311,6 @@ public class ImportService } LOGGER.debug(MessageFormat.format("Importing templates DONE ({0}/{1})", numberOfImportedTemplates, templates.size())); - return new ImportResultItem(EntityType.TEMPLATE, numberOfImportedTemplates, templates.size()); - } - - private ImportResultItem importCharts() - { - List<Chart> charts = database.getCharts(); - LOGGER.debug(MessageFormat.format("Importing {0} charts...", charts.size())); - - int numberOfImportedCharts = 0; - - for(int i = 0; i < charts.size(); i++) - { - Chart chart = charts.get(i); - try - { - LOGGER.debug(MessageFormat.format("Importing chart {0}/{1} (name: {2})", i + 1, charts.size(), chart.getName())); - - final int highestUsedID = chartService.getHighestUsedID(); - chart.setID(highestUsedID + 1); - - chartService.getRepository().save(chart); - - numberOfImportedCharts++; - } - catch(Exception e) - { - final String errorMessage = MessageFormat.format("Error while importing chart with name \"{0}\"", chart.getName()); - LOGGER.error(errorMessage, e); - collectedErrorMessages.add(formatErrorMessage(errorMessage, e)); - } - } - - LOGGER.debug(MessageFormat.format("Importing charts DONE ({0}/{1})", numberOfImportedCharts, charts.size())); - return new ImportResultItem(EntityType.CHART, numberOfImportedCharts, charts.size()); - - } - - private ImportResultItem importImages() - { - List<Image> images = database.getImages(); - LOGGER.debug(MessageFormat.format("Importing {0} images...", images.size())); - List<Icon> alreadyUpdatedIcons = new ArrayList<>(); - - int numberOfImportedImages = 0; - - for(int i = 0; i < images.size(); i++) - { - Image image = images.get(i); - try - { - LOGGER.debug(MessageFormat.format("Importing image {0}/{1} (ID: {2})", i + 1, images.size(), image.getID())); - - // always create new image - int oldImageID = image.getID(); - Image imageToCreate = new Image(image.getImage(), image.getFileName(), image.getFileExtension()); - - final Image savedImage = imageService.getRepository().save(imageToCreate); - int newImageID = savedImage.getID(); - - List<Icon> icons = new ArrayList<>(database.getIcons()); - icons.removeAll(alreadyUpdatedIcons); - alreadyUpdatedIcons.addAll(updateImagesForIcons(icons, oldImageID, newImageID)); - - numberOfImportedImages++; - } - catch(Exception e) - { - final String errorMessage = MessageFormat.format("Error while importing image with ID {0}", image.getID()); - LOGGER.error(errorMessage, e); - collectedErrorMessages.add(formatErrorMessage(errorMessage, e)); - } - } - - LOGGER.debug(MessageFormat.format("Importing images DONE ({0}/{1})", numberOfImportedImages, images.size())); - return new ImportResultItem(EntityType.IMAGE, numberOfImportedImages, images.size()); - } - - public List<Icon> updateImagesForIcons(List<Icon> items, int oldImageId, int newImageID) - { - List<Icon> updatedItems = new ArrayList<>(); - for(Icon item : items) - { - final Image image = item.getImage(); - if(image == null) - { - continue; - } - - if(image.getID() == oldImageId) - { - image.setID(newImageID); - updatedItems.add(item); - } - } - - return updatedItems; - } - - private void importIcons() - { - List<Icon> icons = database.getIcons(); - LOGGER.debug(MessageFormat.format("Importing {0} icons...", icons.size())); - List<Iconizable> alreadyUpdatedAccounts = new ArrayList<>(); - List<Iconizable> alreadyUpdatedTemplates = new ArrayList<>(); - List<Iconizable> alreadyUpdatedCategories = new ArrayList<>(); - - int numberOfImportedIcons = 0; - - for(int i = 0; i < icons.size(); i++) - { - Icon icon = icons.get(i); - try - { - LOGGER.debug(MessageFormat.format("Importing icon {0}/{1} (ID: {2})", i + 1, icons.size(), icon.getID())); - - // always create new icon - int oldIconID = icon.getID(); - Icon iconToCreate = new Icon(); - iconToCreate.setImage(icon.getImage()); - iconToCreate.setBuiltinIdentifier(icon.getBuiltinIdentifier()); - - final Icon savedIcon = iconService.getRepository().save(iconToCreate); - int newIconID = savedIcon.getID(); - - List<Iconizable> accounts = new ArrayList<>(database.getAccounts()); - accounts.removeAll(alreadyUpdatedAccounts); - alreadyUpdatedAccounts.addAll(updateIconsForItems(accounts, oldIconID, newIconID)); - - List<Iconizable> templates = new ArrayList<>(database.getTemplates()); - templates.removeAll(alreadyUpdatedTemplates); - alreadyUpdatedTemplates.addAll(updateIconsForItems(templates, oldIconID, newIconID)); - - List<Iconizable> categories = new ArrayList<>(database.getCategories()); - categories.removeAll(alreadyUpdatedCategories); - alreadyUpdatedCategories.addAll(updateIconsForItems(categories, oldIconID, newIconID)); - - numberOfImportedIcons++; - } - catch(Exception e) - { - final String errorMessage = MessageFormat.format("Error while importing icon with ID {0}", icon.getID()); - LOGGER.error(errorMessage, e); - collectedErrorMessages.add(formatErrorMessage(errorMessage, e)); - } - } - - LOGGER.debug(MessageFormat.format("Importing icon DONE ({0}/{1})", numberOfImportedIcons, icons.size())); - } - - public List<Iconizable> updateIconsForItems(List<? extends Iconizable> items, int oldIconID, int newIconID) - { - List<Iconizable> updatedItems = new ArrayList<>(); - for(Iconizable item : items) - { - final Icon iconReference = item.getIconReference(); - if(iconReference == null) - { - continue; - } - - if(iconReference.getID() == oldIconID) - { - iconReference.setID(newIconID); - updatedItems.add(item); - } - } - - return updatedItems; + return new ImportResultItem(EntityType.TEMPLATE, numberOfImportedTemplates, templates.size(), collectedErrorMessages); } } diff --git a/src/main/java/de/deadlocker8/budgetmaster/utils/ProvidesID.java b/src/main/java/de/deadlocker8/budgetmaster/utils/ProvidesID.java index 783961088..6f9853f76 100644 --- a/src/main/java/de/deadlocker8/budgetmaster/utils/ProvidesID.java +++ b/src/main/java/de/deadlocker8/budgetmaster/utils/ProvidesID.java @@ -3,4 +3,6 @@ package de.deadlocker8.budgetmaster.utils; public interface ProvidesID { Integer getID(); + + void setID(Integer ID); } diff --git a/src/test/java/de/deadlocker8/budgetmaster/unit/database/ImportServiceTest.java b/src/test/java/de/deadlocker8/budgetmaster/unit/database/ImportServiceTest.java index af645034e..f74b0da6d 100644 --- a/src/test/java/de/deadlocker8/budgetmaster/unit/database/ImportServiceTest.java +++ b/src/test/java/de/deadlocker8/budgetmaster/unit/database/ImportServiceTest.java @@ -16,14 +16,11 @@ import de.deadlocker8.budgetmaster.database.accountmatches.AccountMatchList; import de.deadlocker8.budgetmaster.icon.Icon; import de.deadlocker8.budgetmaster.icon.IconRepository; import de.deadlocker8.budgetmaster.icon.IconService; -import de.deadlocker8.budgetmaster.icon.Iconizable; import de.deadlocker8.budgetmaster.images.Image; import de.deadlocker8.budgetmaster.images.ImageFileExtension; import de.deadlocker8.budgetmaster.images.ImageRepository; import de.deadlocker8.budgetmaster.images.ImageService; import de.deadlocker8.budgetmaster.repeating.RepeatingTransactionUpdater; -import de.deadlocker8.budgetmaster.services.EntityType; -import de.deadlocker8.budgetmaster.services.ImportResultItem; import de.deadlocker8.budgetmaster.services.ImportService; import de.deadlocker8.budgetmaster.tags.Tag; import de.deadlocker8.budgetmaster.tags.TagRepository; @@ -33,7 +30,6 @@ import de.deadlocker8.budgetmaster.templategroup.TemplateGroupType; import de.deadlocker8.budgetmaster.templates.Template; import de.deadlocker8.budgetmaster.templates.TemplateRepository; import de.deadlocker8.budgetmaster.transactions.Transaction; -import de.deadlocker8.budgetmaster.transactions.TransactionBase; import de.deadlocker8.budgetmaster.transactions.TransactionRepository; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -85,235 +81,6 @@ class ImportServiceTest @InjectMocks private ImportService importService; - @Test - void test_updateCategoriesForTransactions() - { - Category category1 = new Category("Category1", "#ff0000", CategoryType.CUSTOM); - category1.setID(3); - - Category category2 = new Category("Category2", "#ff0000", CategoryType.CUSTOM); - category2.setID(4); - - List<TransactionBase> transactionList = new ArrayList<>(); - Transaction transaction1 = new Transaction(); - transaction1.setName("Test"); - transaction1.setAmount(200); - transaction1.setDate(LocalDate.of(2018, 10, 3)); - transaction1.setCategory(category1); - transactionList.add(transaction1); - - Transaction transaction2 = new Transaction(); - transaction2.setName("Test_2"); - transaction2.setAmount(-525); - transaction2.setDate(LocalDate.of(2018, 10, 3)); - transaction2.setCategory(category2); - transactionList.add(transaction2); - - List<TransactionBase> updatedTransactions = importService.updateCategoriesForItems(transactionList, 3, 5); - assertThat(updatedTransactions).hasSize(1); - assertThat(updatedTransactions.get(0).getCategory()).hasFieldOrPropertyWithValue("ID", 5); - } - - @Test - void test_updateCategoriesForTemplates() - { - Category category1 = new Category("Category1", "#ff0000", CategoryType.CUSTOM); - category1.setID(3); - - Category category2 = new Category("Category2", "#ff0000", CategoryType.CUSTOM); - category2.setID(4); - - Template template1 = new Template(); - template1.setTemplateName("MyTemplate"); - template1.setCategory(category1); - template1.setAmount(200); - template1.setName("Test"); - template1.setTags(new ArrayList<>()); - - Template template2 = new Template(); - template2.setTemplateName("MyTemplate_2"); - template2.setCategory(category2); - template2.setAmount(-525); - template2.setName("Test_2"); - template2.setTags(new ArrayList<>()); - - List<TransactionBase> templateList = new ArrayList<>(); - templateList.add(template1); - templateList.add(template2); - - List<TransactionBase> updatedTemplates = importService.updateCategoriesForItems(templateList, 3, 5); - assertThat(updatedTemplates).hasSize(1); - assertThat(updatedTemplates.get(0).getCategory()).hasFieldOrPropertyWithValue("ID", 5); - } - - @Test - void test_removeAlreadyUpdatedTransactions() - { - Category category1 = new Category("Category1", "#ff0000", CategoryType.CUSTOM); - category1.setID(3); - - Category category2 = new Category("Category2", "#ff0000", CategoryType.CUSTOM); - category2.setID(4); - - List<Transaction> transactionList = new ArrayList<>(); - Transaction transaction1 = new Transaction(); - transaction1.setName("Test"); - transaction1.setAmount(200); - transaction1.setDate(LocalDate.of(2018, 10, 3)); - transaction1.setCategory(category1); - transaction1.setAccount(new Account("Account", AccountType.CUSTOM)); - transactionList.add(transaction1); - - Transaction transaction2 = new Transaction(); - transaction2.setName("Test_2"); - transaction2.setAmount(-525); - transaction2.setDate(LocalDate.of(2018, 10, 3)); - transaction2.setCategory(category2); - transaction2.setAccount(new Account("Account", AccountType.CUSTOM)); - transactionList.add(transaction2); - - List<Transaction> alreadyUpdatedTransactions = new ArrayList<>(); - transaction1.setCategory(category2); - alreadyUpdatedTransactions.add(transaction1); - - transactionList.removeAll(alreadyUpdatedTransactions); - assertThat(transactionList) - .hasSize(1) - .contains(transaction2); - } - - @Test - void test_updateAccountsForTransactions() - { - Account account1 = new Account("Account_1", AccountType.CUSTOM); - account1.setID(2); - Account account2 = new Account("Account_2", AccountType.CUSTOM); - account2.setID(3); - - Account destinationAccount = new Account("DestinationAccount_1", AccountType.CUSTOM); - destinationAccount.setID(5); - - List<TransactionBase> transactionList = new ArrayList<>(); - Transaction transaction1 = new Transaction(); - transaction1.setAccount(account1); - transaction1.setName("ShouldGoInAccount_1"); - transaction1.setAmount(200); - transaction1.setDate(LocalDate.of(2018, 10, 3)); - transactionList.add(transaction1); - - Transaction transaction2 = new Transaction(); - transaction2.setAccount(account2); - transaction2.setName("ImPartOfAccount_2"); - transaction2.setAmount(-525); - transaction2.setDate(LocalDate.of(2018, 10, 3)); - transactionList.add(transaction2); - - List<TransactionBase> updatedTransactions = importService.updateAccountsForItems(transactionList, account1.getID(), destinationAccount); - assertThat(updatedTransactions).hasSize(1); - assertThat(updatedTransactions.get(0).getAccount().getID()).isEqualTo(5); - } - - @Test - void test_updateTransferAccountsForTransactions() - { - Account account1 = new Account("Account_1", AccountType.CUSTOM); - account1.setID(2); - Account transferAccount = new Account("TransferAccount", AccountType.CUSTOM); - transferAccount.setID(3); - - Account destinationAccount = new Account("DestinationAccount_1", AccountType.CUSTOM); - destinationAccount.setID(5); - - List<TransactionBase> transactionList = new ArrayList<>(); - Transaction transaction = new Transaction(); - transaction.setAccount(account1); - transaction.setTransferAccount(transferAccount); - transaction.setName("Whatever"); - transaction.setAmount(-525); - transaction.setDate(LocalDate.of(2018, 10, 3)); - transactionList.add(transaction); - - // expected - Transaction expectedTransaction = new Transaction(); - expectedTransaction.setAccount(account1); - expectedTransaction.setTransferAccount(destinationAccount); - expectedTransaction.setName("Whatever"); - expectedTransaction.setAmount(-525); - expectedTransaction.setDate(LocalDate.of(2018, 10, 3)); - - assertThat(importService.updateTransferAccountsForItems(transactionList, transferAccount.getID(), destinationAccount)) - .hasSize(1) - .contains(expectedTransaction); - } - - @Test - void test_updateAccountsForTemplates() - { - Account account1 = new Account("Account_1", AccountType.CUSTOM); - account1.setID(2); - Account account2 = new Account("Account_2", AccountType.CUSTOM); - account2.setID(3); - - Account destinationAccount = new Account("DestinationAccount_1", AccountType.CUSTOM); - destinationAccount.setID(5); - - Template template1 = new Template(); - template1.setAccount(account1); - template1.setTemplateName("ShouldGoInAccount_1"); - template1.setAmount(200); - template1.setName("Test"); - template1.setTags(new ArrayList<>()); - - Template template2 = new Template(); - template2.setAccount(account2); - template2.setTemplateName("ImPartOfAccount_2"); - template2.setAmount(-525); - template2.setName("Test_2"); - template2.setTags(new ArrayList<>()); - - List<TransactionBase> templateList = new ArrayList<>(); - templateList.add(template1); - templateList.add(template2); - - List<TransactionBase> updatedTransactions = importService.updateAccountsForItems(templateList, account1.getID(), destinationAccount); - assertThat(updatedTransactions).hasSize(1); - assertThat(updatedTransactions.get(0).getAccount().getID()).isEqualTo(5); - } - - @Test - void test_updateTransferAccountsForTemplates() - { - Account account1 = new Account("Account_1", AccountType.CUSTOM); - account1.setID(2); - Account transferAccount = new Account("TransferAccount", AccountType.CUSTOM); - transferAccount.setID(3); - - Account destinationAccount = new Account("DestinationAccount_1", AccountType.CUSTOM); - destinationAccount.setID(5); - - Template template1 = new Template(); - template1.setAccount(account1); - template1.setTemplateName("ShouldGoInAccount_1"); - template1.setAmount(200); - template1.setName("Test"); - template1.setTags(new ArrayList<>()); - - Template template2 = new Template(); - template2.setAccount(account1); - template2.setTemplateName("ImPartOfAccount_2"); - template2.setAmount(-525); - template2.setName("Test_2"); - template2.setTransferAccount(transferAccount); - template2.setTags(new ArrayList<>()); - - List<TransactionBase> templateList = new ArrayList<>(); - templateList.add(template1); - templateList.add(template2); - - List<TransactionBase> updatedTemplates = importService.updateTransferAccountsForItems(templateList, transferAccount.getID(), destinationAccount); - assertThat(updatedTemplates).hasSize(1); - assertThat(updatedTemplates.get(0).getTransferAccount().getID()).isEqualTo(5); - } @Test void test_updateTagsForItem_ExistingTag() @@ -396,6 +163,11 @@ class ImportServiceTest List<Tag> tags = new ArrayList<>(); tags.add(tag1); + // categories + Category category1 = new Category("Category1", "#ff0000", CategoryType.CUSTOM); + Category category2 = new Category("Category2", "#00ffff", CategoryType.CUSTOM); + List<Category> categories = List.of(category1, category2); + // transactions List<Transaction> transactions = new ArrayList<>(); Transaction transaction1 = new Transaction(); @@ -404,16 +176,27 @@ class ImportServiceTest transaction1.setAmount(200); transaction1.setDate(LocalDate.of(2018, 10, 3)); transaction1.setTags(tags); + transaction1.setCategory(category1); transactions.add(transaction1); Transaction transaction2 = new Transaction(); - transaction2.setAccount(sourceAccount2); - transaction2.setName("ImPartOfAccount_2"); - transaction2.setAmount(-525); + transaction2.setAccount(sourceAccount1); + transaction2.setName("ShouldGoInAccount_1_Too"); + transaction2.setAmount(100); transaction2.setDate(LocalDate.of(2018, 10, 3)); - transaction2.setTags(new ArrayList<>()); + transaction2.setTags(tags); + transaction2.setCategory(category1); transactions.add(transaction2); + Transaction transaction3 = new Transaction(); + transaction3.setAccount(sourceAccount2); + transaction3.setName("ImPartOfAccount_2"); + transaction3.setAmount(-525); + transaction3.setDate(LocalDate.of(2018, 10, 3)); + transaction3.setTags(new ArrayList<>()); + transaction3.setCategory(category2); + transactions.add(transaction3); + // template group TemplateGroup templateGroup = new TemplateGroup(1, "My Template Group", TemplateGroupType.CUSTOM); @@ -423,6 +206,7 @@ class ImportServiceTest template1.setAmount(1500); template1.setAccount(sourceAccount1); template1.setName("Transaction from Template"); + template1.setCategory(category1); List<Tag> tags2 = new ArrayList<>(); tags2.add(tag1); template1.setTags(tags2); @@ -434,11 +218,18 @@ class ImportServiceTest Template template2 = new Template(); template2.setTemplateName("MyTemplate2"); template2.setTransferAccount(sourceAccount2); + template2.setCategory(category1); template2.setTags(new ArrayList<>()); + Template template3 = new Template(); + template3.setTemplateName("MyTemplate3"); + template3.setTransferAccount(sourceAccount1); + template3.setTags(new ArrayList<>()); + List<Template> templates = new ArrayList<>(); templates.add(template1); templates.add(template2); + templates.add(template3); // charts Chart chart = new Chart(); @@ -449,7 +240,7 @@ class ImportServiceTest chart.setScript("/* This list will be dynamically filled with all the transactions between\r\n* the start and and date you select on the \"Show Chart\" page\r\n* and filtered according to your specified filter.\r\n* An example entry for this list and tutorial about how to create custom charts ca be found in the BudgetMaster wiki:\r\n* https://github.com/deadlocker8/BudgetMaster/wiki/How-to-create-custom-charts\r\n*/\r\nvar transactionData \u003d [];\r\n\r\n// Prepare your chart settings here (mandatory)\r\nvar plotlyData \u003d [{\r\n x: [],\r\n y: [],\r\n type: \u0027bar\u0027\r\n}];\r\n\r\n// Add your Plotly layout settings here (optional)\r\nvar plotlyLayout \u003d {};\r\n\r\n// Add your Plotly configuration settings here (optional)\r\nvar plotlyConfig \u003d {\r\n showSendToCloud: false,\r\n displaylogo: false,\r\n showLink: false,\r\n responsive: true\r\n};\r\n\r\n// Don\u0027t touch this line\r\nPlotly.newPlot(\"containerID\", plotlyData, plotlyLayout, plotlyConfig);\r\n"); // database - InternalDatabase database = new InternalDatabase(new ArrayList<>(), accounts, transactions, List.of(templateGroup), templates, List.of(chart), List.of(), List.of(icon1)); + InternalDatabase database = new InternalDatabase(categories, accounts, transactions, List.of(templateGroup), templates, List.of(chart), List.of(), List.of(icon1)); // account matches AccountMatch match1 = new AccountMatch(sourceAccount1); @@ -465,6 +256,11 @@ class ImportServiceTest AccountMatchList accountMatchList = new AccountMatchList(matches); // expected + Category expectedCategory1 = new Category("Category1", "#ff0000", CategoryType.CUSTOM); + expectedCategory1.setID(1); + Category expectedCategory2 = new Category("Category2", "#00ffff", CategoryType.CUSTOM); + expectedCategory2.setID(2); + Transaction expectedTransaction1 = new Transaction(); expectedTransaction1.setAccount(destAccount1); expectedTransaction1.setName("ShouldGoInAccount_1"); @@ -473,11 +269,18 @@ class ImportServiceTest expectedTransaction1.setTags(tags); Transaction expectedTransaction2 = new Transaction(); - expectedTransaction2.setAccount(destAccount2); - expectedTransaction2.setName("ImPartOfAccount_2"); - expectedTransaction2.setAmount(-525); + expectedTransaction2.setAccount(destAccount1); + expectedTransaction2.setName("ShouldGoInAccount_1_Too"); + expectedTransaction2.setAmount(100); expectedTransaction2.setDate(LocalDate.of(2018, 10, 3)); - expectedTransaction2.setTags(new ArrayList<>()); + expectedTransaction2.setTags(tags); + + Transaction expectedTransaction3 = new Transaction(); + expectedTransaction3.setAccount(destAccount2); + expectedTransaction3.setName("ImPartOfAccount_2"); + expectedTransaction3.setAmount(-525); + expectedTransaction3.setDate(LocalDate.of(2018, 10, 3)); + expectedTransaction3.setTags(new ArrayList<>()); Template expectedTemplate1 = new Template(); expectedTemplate1.setTemplateName("MyTemplate"); @@ -497,9 +300,17 @@ class ImportServiceTest expectedTemplate2.setTransferAccount(destAccount2); expectedTemplate2.setTags(new ArrayList<>()); + Template expectedTemplate3 = new Template(); + expectedTemplate3.setTemplateName("MyTemplate3"); + expectedTemplate3.setTransferAccount(destAccount1); + expectedTemplate3.setTags(new ArrayList<>()); + TemplateGroup expectedTemplateGroup = new TemplateGroup(5, "My Template Group", TemplateGroupType.CUSTOM); // act + Mockito.when(categoryRepository.save(category1)).thenReturn(expectedCategory1); + Mockito.when(categoryRepository.save(category2)).thenReturn(expectedCategory2); + Mockito.when(tagRepository.save(Mockito.any(Tag.class))).thenReturn(tag1); Mockito.when(chartService.getHighestUsedID()).thenReturn(8); @@ -519,147 +330,24 @@ class ImportServiceTest InternalDatabase databaseResult = importService.getDatabase(); // assert - assertThat(databaseResult.getTransactions()) + assertThat(databaseResult.getCategories()) .hasSize(2) - .contains(expectedTransaction1, expectedTransaction2); + .contains(expectedCategory1, expectedCategory2); + assertThat(databaseResult.getTransactions()) + .hasSize(3) + .contains(expectedTransaction1, expectedTransaction2, expectedTransaction3); assertThat(databaseResult.getTemplateGroups()) .hasSize(1) .contains(templateGroup); assertThat(databaseResult.getTemplates()) - .hasSize(2) - .contains(expectedTemplate1, expectedTemplate2); + .hasSize(3) + .contains(expectedTemplate1, expectedTemplate2, expectedTemplate3); assertThat(databaseResult.getCharts()) .hasSize(1) .contains(chart); assertThat(importService.getCollectedErrorMessages()).isEmpty(); } - @Test - void test_chartId() - { - Chart chart = new Chart(); - chart.setID(9); - chart.setName("The best chart"); - chart.setType(ChartType.CUSTOM); - chart.setVersion(7); - chart.setScript("/* This list will be dynamically filled with all the transactions between\r\n* the start and and date you select on the \"Show Chart\" page\r\n* and filtered according to your specified filter.\r\n* An example entry for this list and tutorial about how to create custom charts ca be found in the BudgetMaster wiki:\r\n* https://github.com/deadlocker8/BudgetMaster/wiki/How-to-create-custom-charts\r\n*/\r\nvar transactionData \u003d [];\r\n\r\n// Prepare your chart settings here (mandatory)\r\nvar plotlyData \u003d [{\r\n x: [],\r\n y: [],\r\n type: \u0027bar\u0027\r\n}];\r\n\r\n// Add your Plotly layout settings here (optional)\r\nvar plotlyLayout \u003d {};\r\n\r\n// Add your Plotly configuration settings here (optional)\r\nvar plotlyConfig \u003d {\r\n showSendToCloud: false,\r\n displaylogo: false,\r\n showLink: false,\r\n responsive: true\r\n};\r\n\r\n// Don\u0027t touch this line\r\nPlotly.newPlot(\"containerID\", plotlyData, plotlyLayout, plotlyConfig);\r\n"); - - // database - InternalDatabase database = new InternalDatabase(List.of(), List.of(), List.of(), List.of(), List.of(), List.of(chart), List.of(), List.of()); - - // act - int highestUsedID = 22; - Mockito.when(chartService.getHighestUsedID()).thenReturn(highestUsedID); - final ChartRepository chartRepositoryMock = Mockito.mock(ChartRepository.class); - Mockito.when(chartService.getRepository()).thenReturn(chartRepositoryMock); - - importService.importDatabase(database, new AccountMatchList(List.of()), true, true, true); - InternalDatabase databaseResult = importService.getDatabase(); - - // assert - assertThat(databaseResult.getCharts().get(0)) - .hasFieldOrPropertyWithValue("ID", highestUsedID + 1); - } - - @Test - void test_updateImagesForIcons() - { - Image image1 = new Image(new Byte[0], "awesomeIcon.png", ImageFileExtension.PNG); - image1.setID(3); - - Image image2 = new Image(new Byte[0], "awesomeIcon.png", ImageFileExtension.JPG); - image2.setID(4); - - Icon icon1 = new Icon(image1); - Icon icon2 = new Icon(image2); - - final List<Icon> iconList = List.of(icon1, icon2); - - List<Icon> updatedIcons = importService.updateImagesForIcons(iconList, 3, 5); - assertThat(updatedIcons).hasSize(1); - final Image icon = updatedIcons.get(0).getImage(); - assertThat(icon.getBase64EncodedImage()).isEqualTo("data:image/png;base64,"); - assertThat(icon.getID()).isEqualTo(5); - } - - @Test - void test_importImages_notExisting() - { - Image image = new Image(new Byte[0], "awesomeIcon.png", ImageFileExtension.PNG); - image.setID(3); - - Image newImage = new Image(new Byte[0], "awesomeIcon.png", ImageFileExtension.PNG); - newImage.setID(5); - - final ImageRepository imageRepositoryMock = Mockito.mock(ImageRepository.class); - Mockito.when(imageService.getRepository()).thenReturn(imageRepositoryMock); - Mockito.when(imageRepositoryMock.save(Mockito.any())).thenReturn(newImage); - - InternalDatabase database = new InternalDatabase(List.of(), List.of(), List.of(), List.of(), List.of(), List.of(), List.of(image), List.of()); - importService.importDatabase(database, new AccountMatchList(List.of()), true, true, true); - - Image expectedImage = new Image(image.getImage(), image.getFileName(), image.getFileExtension()); - Mockito.verify(imageRepositoryMock, Mockito.atLeast(1)).save(expectedImage); - } - - @Test - void test_importImages_alreadyExisting() - { - Image image = new Image(new Byte[0], "awesomeIcon.png", ImageFileExtension.PNG); - image.setID(3); - - Image newImage = new Image(new Byte[0], "awesomeIcon.png", ImageFileExtension.PNG); - newImage.setID(5); - - final ImageRepository imageRepositoryMock = Mockito.mock(ImageRepository.class); - Mockito.when(imageService.getRepository()).thenReturn(imageRepositoryMock); - Mockito.when(imageRepositoryMock.save(Mockito.any())).thenReturn(newImage); - - InternalDatabase database = new InternalDatabase(List.of(), List.of(), List.of(), List.of(), List.of(), List.of(), List.of(image), List.of()); - importService.importDatabase(database, new AccountMatchList(List.of()), true, true, true); - - Image expectedImage = new Image(image.getImage(), image.getFileName(), image.getFileExtension()); - Mockito.verify(imageRepositoryMock, Mockito.atLeast(1)).save(expectedImage); - } - - @Test - void test_importAccounts_icon() - { - Image image = new Image(new Byte[0], "awesomeIcon.png", ImageFileExtension.PNG); - image.setID(3); - Icon icon = new Icon(image); - icon.setID(15); - - Account accountSource = new Account("my account with icon", AccountType.CUSTOM, icon); - accountSource.setID(1); - Account accountDestination = new Account("destination", AccountType.CUSTOM); - accountDestination.setID(15); - - InternalDatabase database = new InternalDatabase(List.of(), List.of(accountSource), List.of(), List.of(), List.of(), List.of(), List.of(image), List.of(icon)); - AccountMatch accountMatch = new AccountMatch(accountSource); - accountMatch.setAccountDestination(accountDestination); - - Icon expectedIcon = new Icon(image); - expectedIcon.setID(1); - - Account expectedAccount = new Account("destination", AccountType.CUSTOM, expectedIcon); - expectedAccount.setID(15); - Mockito.when(accountRepository.save(Mockito.any())).thenReturn(expectedAccount); - Mockito.when(accountRepository.findById(15)).thenReturn(Optional.of(accountDestination)); - - ImageRepository imageRepositoryMock = Mockito.mock(ImageRepository.class); - Mockito.when(imageService.getRepository()).thenReturn(imageRepositoryMock); - Mockito.when(imageRepositoryMock.save(Mockito.any())).thenReturn(image); - - IconRepository iconRepositoryMock = Mockito.mock(IconRepository.class); - Mockito.when(iconService.getRepository()).thenReturn(iconRepositoryMock); - Mockito.when(iconRepositoryMock.save(Mockito.any())).thenReturn(expectedIcon); - - importService.importDatabase(database, new AccountMatchList(List.of(accountMatch)), true, true, true); - - Mockito.verify(accountRepository, Mockito.atLeast(1)).save(expectedAccount); - } - @Test void test_skipTemplates() { @@ -710,7 +398,7 @@ class ImportServiceTest } @Test - void test_skipCarts() + void test_skipCharts() { Chart chart = new Chart(); chart.setID(9); @@ -732,59 +420,6 @@ class ImportServiceTest Mockito.verify(chartRepositoryMock, Mockito.never()).save(Mockito.any()); } - @Test - void test_errorWhileImportingCategory_shouldBeCollected() - { - Category category1 = new Category("Category1", "#ff0000", CategoryType.CUSTOM); - category1.setID(3); - - Category category2 = new Category("Category2", "#ff0000", CategoryType.CUSTOM); - category2.setID(4); - - // raise exception - Mockito.when(categoryRepository.findByNameAndColorAndType(Mockito.eq("Category1"), Mockito.any(), Mockito.any())).thenThrow(new NullPointerException()); - Mockito.when(categoryRepository.findByNameAndColorAndType(Mockito.eq("Category2"), Mockito.any(), Mockito.any())).thenReturn(category2); - - InternalDatabase database = new InternalDatabase(List.of(category1, category2), List.of(), List.of(), List.of(), List.of(), List.of(), List.of(), List.of()); - final List<ImportResultItem> importResultItems = importService.importDatabase(database, new AccountMatchList(List.of()), false, false, false); - - assertThat(importResultItems).hasSize(7) - .contains(new ImportResultItem(EntityType.CATEGORY, 1, 2)); - assertThat(importService.getCollectedErrorMessages()).hasSize(1) - .contains("Error while importing category with name \"Category1\": java.lang.NullPointerException (null)"); - } - - @Test - void test_updateIconsForAccounts() - { - Image image1 = new Image(new Byte[0], "awesomeIcon.png", ImageFileExtension.PNG); - image1.setID(3); - - Icon icon1 = new Icon(image1); - icon1.setID(3); - - Icon icon2 = new Icon("fas fa-icons"); - icon2.setID(4); - - Account account1 = new Account("account with image icon", AccountType.CUSTOM, icon1); - Account account2 = new Account("account with built-in icon", AccountType.CUSTOM, icon2); - List<Account> accounts = List.of(account1, account2); - - List<Iconizable> updatedAccounts = importService.updateIconsForItems(accounts, 3, 5); - assertThat(updatedAccounts).hasSize(1); - assertThat(updatedAccounts.get(0).getIconReference()) - .hasFieldOrPropertyWithValue("ID", 5) - .hasFieldOrPropertyWithValue("image", image1) - .hasFieldOrPropertyWithValue("builtinIdentifier", null); - - updatedAccounts = importService.updateIconsForItems(accounts, 4, 6); - assertThat(updatedAccounts).hasSize(1); - assertThat(updatedAccounts.get(0).getIconReference()) - .hasFieldOrPropertyWithValue("ID", 6) - .hasFieldOrPropertyWithValue("image", null) - .hasFieldOrPropertyWithValue("builtinIdentifier", "fas fa-icons"); - } - @Test void test_updateTemplateGroupsForTemplates() { diff --git a/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/AccountImporterTest.java b/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/AccountImporterTest.java new file mode 100644 index 000000000..d8a26f1c1 --- /dev/null +++ b/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/AccountImporterTest.java @@ -0,0 +1,96 @@ +package de.deadlocker8.budgetmaster.unit.database.importer; + +import de.deadlocker8.budgetmaster.accounts.Account; +import de.deadlocker8.budgetmaster.accounts.AccountRepository; +import de.deadlocker8.budgetmaster.accounts.AccountType; +import de.deadlocker8.budgetmaster.database.accountmatches.AccountMatch; +import de.deadlocker8.budgetmaster.database.accountmatches.AccountMatchList; +import de.deadlocker8.budgetmaster.database.importer.AccountImporter; +import de.deadlocker8.budgetmaster.database.importer.IconImporter; +import de.deadlocker8.budgetmaster.icon.Icon; +import de.deadlocker8.budgetmaster.icon.IconRepository; +import de.deadlocker8.budgetmaster.services.EntityType; +import de.deadlocker8.budgetmaster.services.ImportResultItem; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@ExtendWith(SpringExtension.class) +@DataJpaTest +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +class AccountImporterTest +{ + @Autowired + private IconRepository iconRepository; + + @Autowired + private AccountRepository accountRepository; + + @Test + void test_importAccounts() + { + final Account sourceAccount = new Account("SourceAccount", AccountType.CUSTOM); + sourceAccount.setID(2); + + final Account sourceAccount2 = new Account("SourceAccount 2", AccountType.CUSTOM); + sourceAccount2.setID(7); + + final Account destinationAccount = new Account("DestinationAccount", AccountType.CUSTOM); + destinationAccount.setID(1); + accountRepository.save(destinationAccount); + + final Account destinationAccount2 = new Account("DestinationAccount 2", AccountType.CUSTOM); + destinationAccount.setID(2); + accountRepository.save(destinationAccount2); + + final AccountMatch accountMatch = new AccountMatch(sourceAccount); + accountMatch.setAccountDestination(destinationAccount); + + final AccountMatch accountMatch2 = new AccountMatch(sourceAccount2); + accountMatch2.setAccountDestination(destinationAccount2); + final AccountMatchList accountMatchList = new AccountMatchList(List.of(accountMatch, accountMatch2)); + + final AccountImporter importer = new AccountImporter(accountRepository); + final ImportResultItem resultItem = importer.importItems(List.of(sourceAccount, sourceAccount2), accountMatchList); + + final ImportResultItem expected = new ImportResultItem(EntityType.ACCOUNT, 2, 2, List.of()); + assertThat(resultItem).isEqualTo(expected); + assertThat(sourceAccount).hasFieldOrPropertyWithValue("ID", destinationAccount.getID()); + assertThat(sourceAccount2).hasFieldOrPropertyWithValue("ID", destinationAccount2.getID()); + } + + @Test + void test_importAccounts_updateIcon() + { + Icon icon = new Icon("fas fa-icons"); + icon = iconRepository.save(icon); + + final Account sourceAccount = new Account("SourceAccount", AccountType.CUSTOM); + sourceAccount.setID(2); + sourceAccount.setIconReference(icon); + + Account destinationAccount = new Account("DestinationAccount", AccountType.CUSTOM); + destinationAccount.setID(1); + destinationAccount = accountRepository.save(destinationAccount); + + final AccountMatch accountMatch = new AccountMatch(sourceAccount); + accountMatch.setAccountDestination(destinationAccount); + + final AccountMatchList accountMatchList = new AccountMatchList(List.of(accountMatch)); + + final AccountImporter importer = new AccountImporter(accountRepository); + final ImportResultItem resultItem = importer.importItems(List.of(sourceAccount), accountMatchList); + + final ImportResultItem expected = new ImportResultItem(EntityType.ACCOUNT, 1, 1, List.of()); + assertThat(resultItem).isEqualTo(expected); + assertThat(sourceAccount).hasFieldOrPropertyWithValue("ID", destinationAccount.getID()); + assertThat(accountRepository.getById(destinationAccount.getID())).hasFieldOrPropertyWithValue("iconReference", icon); + } +} \ No newline at end of file diff --git a/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/CategoryImporterTest.java b/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/CategoryImporterTest.java new file mode 100644 index 000000000..f7603d31b --- /dev/null +++ b/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/CategoryImporterTest.java @@ -0,0 +1,109 @@ +package de.deadlocker8.budgetmaster.unit.database.importer; + +import de.deadlocker8.budgetmaster.categories.Category; +import de.deadlocker8.budgetmaster.categories.CategoryRepository; +import de.deadlocker8.budgetmaster.categories.CategoryType; +import de.deadlocker8.budgetmaster.database.importer.CategoryImporter; +import de.deadlocker8.budgetmaster.services.EntityType; +import de.deadlocker8.budgetmaster.services.ImportResultItem; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@ExtendWith(SpringExtension.class) +@DataJpaTest +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +class CategoryImporterTest +{ + @Autowired + private CategoryRepository categoryRepository; + + @Test + void test_importCategories() + { + final Category category1 = new Category("Category1", "#ff0000", CategoryType.CUSTOM); + category1.setID(2); + + final Category category2 = new Category("Category2", "#ff0000", CategoryType.CUSTOM); + category2.setID(3); + + final CategoryImporter importer = new CategoryImporter(categoryRepository); + final ImportResultItem resultItem = importer.importItems(List.of(category1, category2)); + + final ImportResultItem expected = new ImportResultItem(EntityType.CATEGORY, 2, 2, List.of()); + assertThat(resultItem).isEqualTo(expected); + assertThat(category1).hasFieldOrPropertyWithValue("ID", 1); + assertThat(category2).hasFieldOrPropertyWithValue("ID", 2); + } + + @Test + void test_importCategories_skipExisting_defaultNone() + { + final Category category = new Category("No category", "#ff0000", CategoryType.NONE); + category.setID(1); + + categoryRepository.save(category); + + final CategoryImporter importer = new CategoryImporter(categoryRepository); + final ImportResultItem resultItem = importer.importItems(List.of(category)); + + final ImportResultItem expected = new ImportResultItem(EntityType.CATEGORY, 1, 1, List.of()); + assertThat(resultItem).isEqualTo(expected); + assertThat(category).hasFieldOrPropertyWithValue("ID", 1); + } + + @Test + void test_importCategories_skipExisting_defaultRest() + { + final Category category = new Category("Rest", "#ff0000", CategoryType.REST); + category.setID(1); + + categoryRepository.save(category); + + final CategoryImporter importer = new CategoryImporter(categoryRepository); + final ImportResultItem resultItem = importer.importItems(List.of(category)); + + final ImportResultItem expected = new ImportResultItem(EntityType.CATEGORY, 1, 1, List.of()); + assertThat(resultItem).isEqualTo(expected); + assertThat(category).hasFieldOrPropertyWithValue("ID", 1); + } + + @Test + void test_importCategories_skipExisting_custom() + { + final Category category = new Category("Category1", "#ff0000", CategoryType.CUSTOM); + category.setID(3); + + categoryRepository.save(new Category("existing category 1", "#ffffff", CategoryType.CUSTOM)); + categoryRepository.save(new Category("existing category 2", "#ffffff", CategoryType.CUSTOM)); + categoryRepository.save(category); + + final CategoryImporter importer = new CategoryImporter(categoryRepository); + final ImportResultItem resultItem = importer.importItems(List.of(category)); + + final ImportResultItem expected = new ImportResultItem(EntityType.CATEGORY, 1, 1, List.of()); + assertThat(resultItem).isEqualTo(expected); + assertThat(category).hasFieldOrPropertyWithValue("ID", 3); + } + + @Test + void test_importCategories_errorIsCollected() + { + final Category category = new Category("Category1", "#ff0000", CategoryType.CUSTOM); + + final CategoryImporter importer = new CategoryImporter(categoryRepository); + final ImportResultItem resultItem = importer.importItems(List.of(category)); + + assertThat(resultItem.getNumberOfImportedItems()) + .isZero(); + assertThat(resultItem.getCollectedErrorMessages()) + .hasSize(1); + } +} \ No newline at end of file diff --git a/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/ChartImporterTest.java b/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/ChartImporterTest.java new file mode 100644 index 000000000..129d1b3bd --- /dev/null +++ b/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/ChartImporterTest.java @@ -0,0 +1,51 @@ +package de.deadlocker8.budgetmaster.unit.database.importer; + +import de.deadlocker8.budgetmaster.Main; +import de.deadlocker8.budgetmaster.charts.Chart; +import de.deadlocker8.budgetmaster.charts.ChartService; +import de.deadlocker8.budgetmaster.charts.ChartType; +import de.deadlocker8.budgetmaster.database.importer.ChartImporter; +import de.deadlocker8.budgetmaster.database.importer.ImageImporter; +import de.deadlocker8.budgetmaster.images.Image; +import de.deadlocker8.budgetmaster.images.ImageFileExtension; +import de.deadlocker8.budgetmaster.images.ImageRepository; +import de.deadlocker8.budgetmaster.services.EntityType; +import de.deadlocker8.budgetmaster.services.ImportResultItem; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest(classes = Main.class) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +class ChartImporterTest +{ + @Autowired + private ChartService chartService; + + @Test + void test_importCharts() + { + final Chart chart = new Chart(); + chart.setID(9); + chart.setName("The best chart"); + chart.setType(ChartType.CUSTOM); + chart.setVersion(7); + chart.setScript("/* This list will be dynamically filled with all the transactions between\r\n* the start and and date you select on the \"Show Chart\" page\r\n* and filtered according to your specified filter.\r\n* An example entry for this list and tutorial about how to create custom charts ca be found in the BudgetMaster wiki:\r\n* https://github.com/deadlocker8/BudgetMaster/wiki/How-to-create-custom-charts\r\n*/\r\nvar transactionData \u003d [];\r\n\r\n// Prepare your chart settings here (mandatory)\r\nvar plotlyData \u003d [{\r\n x: [],\r\n y: [],\r\n type: \u0027bar\u0027\r\n}];\r\n\r\n// Add your Plotly layout settings here (optional)\r\nvar plotlyLayout \u003d {};\r\n\r\n// Add your Plotly configuration settings here (optional)\r\nvar plotlyConfig \u003d {\r\n showSendToCloud: false,\r\n displaylogo: false,\r\n showLink: false,\r\n responsive: true\r\n};\r\n\r\n// Don\u0027t touch this line\r\nPlotly.newPlot(\"containerID\", plotlyData, plotlyLayout, plotlyConfig);\r\n"); + + final ChartImporter importer = new ChartImporter(chartService); + final ImportResultItem resultItem = importer.importItems(List.of(chart)); + + final ImportResultItem expected = new ImportResultItem(EntityType.CHART, 1, 1, List.of()); + assertThat(resultItem).isEqualTo(expected); + + assertThat(chart).hasFieldOrPropertyWithValue("ID", chartService.getRepository().findAll().size()); + } +} \ No newline at end of file diff --git a/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/IconImporterTest.java b/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/IconImporterTest.java new file mode 100644 index 000000000..d7faaf516 --- /dev/null +++ b/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/IconImporterTest.java @@ -0,0 +1,68 @@ +package de.deadlocker8.budgetmaster.unit.database.importer; + +import de.deadlocker8.budgetmaster.database.importer.IconImporter; +import de.deadlocker8.budgetmaster.icon.Icon; +import de.deadlocker8.budgetmaster.icon.IconRepository; +import de.deadlocker8.budgetmaster.images.Image; +import de.deadlocker8.budgetmaster.images.ImageFileExtension; +import de.deadlocker8.budgetmaster.images.ImageRepository; +import de.deadlocker8.budgetmaster.services.EntityType; +import de.deadlocker8.budgetmaster.services.ImportResultItem; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@ExtendWith(SpringExtension.class) +@DataJpaTest +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +class IconImporterTest +{ + @Autowired + private ImageRepository imageRepository; + + @Autowired + private IconRepository iconRepository; + + @Test + void test_importIcons() + { + Image image1 = new Image(new Byte[0], "awesomeIcon.png", ImageFileExtension.PNG); + image1 = imageRepository.save(image1); + + final Icon icon1 = new Icon(image1); + icon1.setID(3); + + final Icon icon2 = new Icon("fas fa-icons"); + icon2.setID(4); + + final IconImporter importer = new IconImporter(iconRepository); + final ImportResultItem resultItem = importer.importItems(List.of(icon1, icon2)); + + final ImportResultItem expected = new ImportResultItem(EntityType.ICON, 2, 2, List.of()); + assertThat(resultItem).isEqualTo(expected); + assertThat(icon1).hasFieldOrPropertyWithValue("ID", 1); + assertThat(icon2).hasFieldOrPropertyWithValue("ID", 2); + } + + @Test + void test_importIcons_alreadyExisting() + { + final Icon icon = new Icon("fas fa-icons"); + icon.setID(1); + iconRepository.save(icon); + + final IconImporter importer = new IconImporter(iconRepository); + final ImportResultItem resultItem = importer.importItems(List.of(icon)); + + final ImportResultItem expected = new ImportResultItem(EntityType.ICON, 1, 1, List.of()); + assertThat(resultItem).isEqualTo(expected); + assertThat(icon).hasFieldOrPropertyWithValue("ID", 2); + } +} \ No newline at end of file diff --git a/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/ImageImporterTest.java b/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/ImageImporterTest.java new file mode 100644 index 000000000..0d0dd1398 --- /dev/null +++ b/src/test/java/de/deadlocker8/budgetmaster/unit/database/importer/ImageImporterTest.java @@ -0,0 +1,60 @@ +package de.deadlocker8.budgetmaster.unit.database.importer; + +import de.deadlocker8.budgetmaster.database.importer.ImageImporter; +import de.deadlocker8.budgetmaster.images.Image; +import de.deadlocker8.budgetmaster.images.ImageFileExtension; +import de.deadlocker8.budgetmaster.images.ImageRepository; +import de.deadlocker8.budgetmaster.services.EntityType; +import de.deadlocker8.budgetmaster.services.ImportResultItem; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +@ExtendWith(SpringExtension.class) +@DataJpaTest +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +class ImageImporterTest +{ + @Autowired + private ImageRepository imageRepository; + + @Test + void test_importImages() + { + final Image image1 = new Image(new Byte[0], "awesomeIcon.png", ImageFileExtension.PNG); + image1.setID(3); + + final Image image2 = new Image(new Byte[0], "awesomeIcon.png", ImageFileExtension.JPG); + image2.setID(4); + + final ImageImporter importer = new ImageImporter(imageRepository); + final ImportResultItem resultItem = importer.importItems(List.of(image1, image2)); + + final ImportResultItem expected = new ImportResultItem(EntityType.IMAGE, 2, 2, List.of()); + assertThat(resultItem).isEqualTo(expected); + assertThat(image1).hasFieldOrPropertyWithValue("ID", 1); + assertThat(image2).hasFieldOrPropertyWithValue("ID", 2); + } + + @Test + void test_importImages_alreadyExisting() + { + final Image image = new Image(new Byte[0], "awesomeIcon.png", ImageFileExtension.PNG); + image.setID(1); + imageRepository.save(image); + + final ImageImporter importer = new ImageImporter(imageRepository); + final ImportResultItem resultItem = importer.importItems(List.of(image)); + + final ImportResultItem expected = new ImportResultItem(EntityType.IMAGE, 1, 1, List.of()); + assertThat(resultItem).isEqualTo(expected); + assertThat(image).hasFieldOrPropertyWithValue("ID", 2); + } +} \ No newline at end of file -- GitLab