/**
* This file is part of alf.io.
*
* alf.io 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.
*
* alf.io 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 alf.io. If not, see <http://www.gnu.org/licenses/>.
*/
package alfio.manager.system;
import alfio.TestConfiguration;
import alfio.config.DataSourceConfiguration;
import alfio.config.Initializer;
import alfio.config.RepositoryConfiguration;
import alfio.controller.form.UpdateTicketOwnerForm;
import alfio.controller.support.TemplateProcessor;
import alfio.manager.EventManager;
import alfio.manager.FileUploadManager;
import alfio.manager.TicketReservationManager;
import alfio.manager.support.PartialTicketPDFGenerator;
import alfio.manager.user.UserManager;
import alfio.model.*;
import alfio.model.modification.*;
import alfio.model.modification.support.LocationDescriptor;
import alfio.model.plugin.PluginConfigOption;
import alfio.model.system.ComponentType;
import alfio.model.system.EventMigration;
import alfio.model.transaction.PaymentProxy;
import alfio.model.user.Organization;
import alfio.model.user.Role;
import alfio.repository.EventRepository;
import alfio.repository.TicketCategoryRepository;
import alfio.repository.TicketRepository;
import alfio.repository.plugin.PluginConfigurationRepository;
import alfio.repository.system.EventMigrationRepository;
import alfio.repository.user.OrganizationRepository;
import alfio.test.util.IntegrationTestUtil;
import alfio.util.TemplateManager;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.*;
import static org.junit.Assert.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {RepositoryConfiguration.class, DataSourceConfiguration.class, TestConfiguration.class})
@ActiveProfiles({Initializer.PROFILE_DEV, Initializer.PROFILE_DISABLE_JOBS})
public class DataMigratorIntegrationTest {
public static final int AVAILABLE_SEATS = 20;
private static final Map<String, String> DESCRIPTION = Collections.singletonMap("en", "desc");
@Autowired
private EventManager eventManager;
@Autowired
private EventRepository eventRepository;
@Autowired
private OrganizationRepository organizationRepository;
@Autowired
private UserManager userManager;
@Autowired
private TicketRepository ticketRepository;
@Autowired
private DataMigrator dataMigrator;
@Autowired
private EventMigrationRepository eventMigrationRepository;
@Autowired
private TicketReservationManager ticketReservationManager;
@Autowired
private TicketCategoryRepository ticketCategoryRepository;
@Autowired
private TemplateManager templateManager;
@Autowired
private FileUploadManager fileUploadManager;
@Autowired
private PluginConfigurationRepository pluginConfigurationRepository;
@Value("${alfio.version}")
private String currentVersion;
@Value("${alfio.build-ts}")
private String buildTimestamp;
@BeforeClass
public static void initEnv() {
IntegrationTestUtil.initSystemProperties();
}
private Pair<Event, String> initEvent(List<TicketCategoryModification> categories) {
return initEvent(categories, "display name");
}
private Pair<Event,String> initEvent(List<TicketCategoryModification> categories, String displayName) {
String organizationName = UUID.randomUUID().toString();
String username = UUID.randomUUID().toString();
String eventName = UUID.randomUUID().toString();
organizationRepository.create(organizationName, "org", "email@example.com");
Organization organization = organizationRepository.findByName(organizationName).get(0);
userManager.insertUser(organization.getId(), username, "test", "test", "test@example.com", Role.OPERATOR);
Map<String, String> desc = new HashMap<>();
desc.put("en", "muh description");
desc.put("it", "muh description");
desc.put("de", "muh description");
EventModification em = new EventModification(null, Event.EventType.INTERNAL, "url", "url", "url", null, null,
eventName, displayName, organization.getId(),
"muh location", desc,
new DateTimeModification(LocalDate.now().plusDays(5), LocalTime.now()),
new DateTimeModification(LocalDate.now().plusDays(5), LocalTime.now().plusHours(1)),
BigDecimal.TEN, "CHF", AVAILABLE_SEATS, BigDecimal.ONE, true, null, categories, false, new LocationDescriptor("","","",""), 7, null, null);
eventManager.createEvent(em);
return Pair.of(eventManager.getSingleEvent(eventName, username), username);
}
@Test
public void testMigration() {
List<TicketCategoryModification> categories = Collections.singletonList(
new TicketCategoryModification(null, "default", AVAILABLE_SEATS,
new DateTimeModification(LocalDate.now(), LocalTime.now()),
new DateTimeModification(LocalDate.now(), LocalTime.now()),
DESCRIPTION, BigDecimal.TEN, false, "", false));
Pair<Event, String> eventUsername = initEvent(categories);
Event event = eventUsername.getKey();
try {
eventRepository.updatePrices("CHF", 40, false, BigDecimal.ONE, "STRIPE", event.getId(), PriceContainer.VatStatus.NOT_INCLUDED, 1000);
dataMigrator.migrateEventsToCurrentVersion();
EventMigration eventMigration = eventMigrationRepository.loadEventMigration(event.getId());
assertNotNull(eventMigration);
//assertEquals(buildTimestamp, eventMigration.getBuildTimestamp().toString());
assertEquals(currentVersion, eventMigration.getCurrentVersion());
List<Ticket> tickets = ticketRepository.findFreeByEventId(event.getId());
assertNotNull(tickets);
assertFalse(tickets.isEmpty());
assertEquals(40, tickets.size());
assertTrue(tickets.stream().allMatch(t -> t.getCategoryId() == null));
} finally {
eventManager.deleteEvent(event.getId(), eventUsername.getValue());
}
}
@Test
public void testMigrationWithExistingRecord() {
List<TicketCategoryModification> categories = Collections.singletonList(
new TicketCategoryModification(null, "default", AVAILABLE_SEATS,
new DateTimeModification(LocalDate.now(), LocalTime.now()),
new DateTimeModification(LocalDate.now(), LocalTime.now()),
DESCRIPTION, BigDecimal.TEN, false, "", false));
Pair<Event, String> eventUsername = initEvent(categories);
Event event = eventUsername.getKey();
try {
eventMigrationRepository.insertMigrationData(event.getId(), "1.4", ZonedDateTime.now(ZoneId.of("UTC")).minusDays(1), EventMigration.Status.COMPLETE.toString());
eventRepository.updatePrices("CHF", 40, false, BigDecimal.ONE, "STRIPE", event.getId(), PriceContainer.VatStatus.NOT_INCLUDED, 1000);
dataMigrator.migrateEventsToCurrentVersion();
EventMigration eventMigration = eventMigrationRepository.loadEventMigration(event.getId());
assertNotNull(eventMigration);
//assertEquals(buildTimestamp, eventMigration.getBuildTimestamp().toString());
assertEquals(currentVersion, eventMigration.getCurrentVersion());
List<Ticket> tickets = ticketRepository.findFreeByEventId(event.getId());
assertNotNull(tickets);
assertFalse(tickets.isEmpty());
assertEquals(40, tickets.size());
assertTrue(tickets.stream().allMatch(t -> t.getCategoryId() == null));
} finally {
eventManager.deleteEvent(event.getId(), eventUsername.getValue());
}
}
@Test
public void testAlreadyMigratedEvent() {
List<TicketCategoryModification> categories = Collections.singletonList(
new TicketCategoryModification(null, "default", AVAILABLE_SEATS,
new DateTimeModification(LocalDate.now(), LocalTime.now()),
new DateTimeModification(LocalDate.now(), LocalTime.now()),
DESCRIPTION, BigDecimal.TEN, false, "", false));
Pair<Event, String> eventUsername = initEvent(categories);
Event event = eventUsername.getKey();
try {
ZonedDateTime migrationTs = ZonedDateTime.now(ZoneId.of("UTC"));
eventMigrationRepository.insertMigrationData(event.getId(), currentVersion, migrationTs, EventMigration.Status.COMPLETE.toString());
eventRepository.updatePrices("CHF", 40, false, BigDecimal.ONE, "STRIPE", event.getId(), PriceContainer.VatStatus.NOT_INCLUDED, 1000);
dataMigrator.migrateEventsToCurrentVersion();
EventMigration eventMigration = eventMigrationRepository.loadEventMigration(event.getId());
assertNotNull(eventMigration);
//assertEquals(migrationTs.toString(), eventMigration.getBuildTimestamp().toString());
assertEquals(currentVersion, eventMigration.getCurrentVersion());
List<Ticket> tickets = ticketRepository.findFreeByEventId(event.getId());
assertNotNull(tickets);
assertFalse(tickets.isEmpty());
assertEquals(AVAILABLE_SEATS, tickets.size());//<-- the migration has not been done
assertTrue(tickets.stream().allMatch(t -> t.getCategoryId() == null));
} finally {
eventManager.deleteEvent(event.getId(), eventUsername.getValue());
}
}
@Test
public void testUpdateDisplayName() {
List<TicketCategoryModification> categories = Collections.singletonList(
new TicketCategoryModification(null, "default", AVAILABLE_SEATS,
new DateTimeModification(LocalDate.now(), LocalTime.now()),
new DateTimeModification(LocalDate.now(), LocalTime.now()),
DESCRIPTION, BigDecimal.TEN, false, "", false));
Pair<Event, String> eventUsername = initEvent(categories, null);
Event event = eventUsername.getKey();
try {
dataMigrator.migrateEventsToCurrentVersion();
EventMigration eventMigration = eventMigrationRepository.loadEventMigration(event.getId());
assertNotNull(eventMigration);
//assertEquals(buildTimestamp, eventMigration.getBuildTimestamp().toString());
assertEquals(currentVersion, eventMigration.getCurrentVersion());
Event withDescription = eventRepository.findById(event.getId());
assertNotNull(withDescription.getDisplayName());
assertEquals(event.getShortName(), withDescription.getShortName());
assertEquals(event.getShortName(), withDescription.getDisplayName());
} finally {
eventManager.deleteEvent(event.getId(), eventUsername.getValue());
}
}
@Test
public void testUpdateTicketReservation() {
List<TicketCategoryModification> categories = Collections.singletonList(
new TicketCategoryModification(null, "default", AVAILABLE_SEATS,
new DateTimeModification(LocalDate.now(), LocalTime.now()),
new DateTimeModification(LocalDate.now(), LocalTime.now()),
DESCRIPTION, BigDecimal.TEN, false, "", false));
Pair<Event, String> eventUsername = initEvent(categories);
Event event = eventUsername.getKey();
try {
TicketReservationModification trm = new TicketReservationModification();
trm.setAmount(1);
trm.setTicketCategoryId(eventManager.loadTicketCategories(event).get(0).getId());
TicketReservationWithOptionalCodeModification r = new TicketReservationWithOptionalCodeModification(trm, Optional.empty());
Date expiration = DateUtils.addDays(new Date(), 1);
String reservationId = ticketReservationManager.createTicketReservation(event, Collections.singletonList(r), Collections.emptyList(), expiration, Optional.empty(), Optional.empty(), Locale.ENGLISH, false);
dataMigrator.fillReservationsLanguage();
TicketReservation ticketReservation = ticketReservationManager.findById(reservationId).get();
assertEquals("en", ticketReservation.getUserLanguage());
} finally {
eventManager.deleteEvent(event.getId(), eventUsername.getValue());
}
}
@Test
public void testUpdateGender() {
List<TicketCategoryModification> categories = Collections.singletonList(
new TicketCategoryModification(null, "default", AVAILABLE_SEATS,
new DateTimeModification(LocalDate.now(), LocalTime.now()),
new DateTimeModification(LocalDate.now(), LocalTime.now()),
DESCRIPTION, BigDecimal.TEN, false, "", false));
Pair<Event, String> eventUsername = initEvent(categories);
Event event = eventUsername.getKey();
try {
TicketReservationModification trm = new TicketReservationModification();
trm.setAmount(2);
trm.setTicketCategoryId(eventManager.loadTicketCategories(event).get(0).getId());
TicketReservationWithOptionalCodeModification r = new TicketReservationWithOptionalCodeModification(trm, Optional.empty());
Date expiration = DateUtils.addDays(new Date(), 1);
String reservationId = ticketReservationManager.createTicketReservation(event, Collections.singletonList(r), Collections.emptyList(), expiration, Optional.empty(), Optional.empty(), Locale.ENGLISH, false);
ticketReservationManager.confirm("TOKEN", null, event, reservationId, "email@email.ch", new CustomerName("Full Name", "Full", "Name", event), Locale.ENGLISH, null, new TotalPrice(1000, 10, 0, 0), Optional.empty(), Optional.of(PaymentProxy.ON_SITE));
List<Ticket> tickets = ticketRepository.findTicketsInReservation(reservationId);
UpdateTicketOwnerForm first = new UpdateTicketOwnerForm();
first.setEmail("email@email.ch");
//first.setTShirtSize("SMALL");
//first.setGender("F");
first.setFirstName("Full");
first.setLastName("Name");
UpdateTicketOwnerForm second = new UpdateTicketOwnerForm();
//second.setTShirtSize("SMALL-F");
second.setEmail("email@email.ch");
second.setFirstName("Full");
second.setLastName("Name");
PartialTicketPDFGenerator generator = TemplateProcessor.buildPartialPDFTicket(Locale.ITALIAN, event, ticketReservationManager.findById(reservationId).get(),
ticketCategoryRepository.getById(tickets.get(0).getCategoryId(), event.getId()), organizationRepository.getById(event.getOrganizationId()),
templateManager, fileUploadManager, "");
ticketReservationManager.updateTicketOwner(tickets.get(0), Locale.ITALIAN, event, first, (t) -> "", (t) -> "", Optional.empty());
ticketReservationManager.updateTicketOwner(tickets.get(1), Locale.ITALIAN, event, second, (t) -> "", (t) -> "", Optional.empty());
//FIXME
//dataMigrator.fillTicketsGender();
//ticketRepository.findTicketsInReservation(reservationId).forEach(t -> assertEquals("F", t.getGender()));
} finally {
eventManager.deleteEvent(event.getId(), eventUsername.getValue());
}
}
@Test
public void testUpdatePluginConfiguration() {
List<TicketCategoryModification> categories = Collections.singletonList(new TicketCategoryModification(null,
"default", AVAILABLE_SEATS, new DateTimeModification(LocalDate.now(), LocalTime.now()),
new DateTimeModification(LocalDate.now(), LocalTime.now()), DESCRIPTION, BigDecimal.TEN, false, "",
false));
Pair<Event, String> eventUsername = initEvent(categories);
Event event = eventUsername.getKey();
try {
pluginConfigurationRepository.delete("my-plugin");
pluginConfigurationRepository.insert("my-plugin", -1, "name", "value", "description", ComponentType.TEXT);
dataMigrator.migratePluginConfig(event);
List<PluginConfigOption> options = pluginConfigurationRepository.loadByPluginIdAndEventId("my-plugin",
event.getId());
assertFalse(options.isEmpty());
assertEquals(1, options.size());
assertEquals(event.getId(), options.get(0).getEventId());
} finally {
pluginConfigurationRepository.delete("my-plugin");
eventManager.deleteEvent(event.getId(), eventUsername.getValue());
}
}
}