/** * 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; import alfio.controller.form.UpdateTicketOwnerForm; import alfio.manager.plugin.PluginManager; import alfio.manager.support.PartialTicketTextGenerator; import alfio.manager.support.PaymentResult; import alfio.manager.support.TextTemplateGenerator; import alfio.manager.system.ConfigurationManager; import alfio.model.*; import alfio.model.Ticket.TicketStatus; import alfio.model.TicketReservation.TicketReservationStatus; import alfio.model.modification.TicketReservationWithOptionalCodeModification; import alfio.model.system.Configuration; import alfio.model.system.ConfigurationKeys; import alfio.model.transaction.PaymentProxy; import alfio.model.user.Organization; import alfio.model.user.Role; import alfio.repository.*; import alfio.repository.user.OrganizationRepository; import alfio.util.TemplateManager; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.context.MessageSource; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.transaction.PlatformTransactionManager; import java.io.IOException; import java.time.LocalDate; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; import java.util.*; import java.util.stream.Collectors; import static alfio.model.TicketReservation.TicketReservationStatus.*; import static alfio.model.system.ConfigurationKeys.ASSIGNMENT_REMINDER_START; import static alfio.model.system.ConfigurationKeys.OFFLINE_PAYMENT_DAYS; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static org.junit.Assert.*; import static org.mockito.Mockito.*; @RunWith(MockitoJUnitRunner.class) public class TicketReservationManagerTest { private static final int EVENT_ID = 42; private static final int TICKET_CATEGORY_ID = 24; private static final String SPECIAL_PRICE_CODE = "SPECIAL-PRICE"; private static final String SPECIAL_PRICE_SESSION_ID = "session-id"; private static final int SPECIAL_PRICE_ID = -42; private static final String USER_LANGUAGE = "it"; private static final int TICKET_ID = 2048756; private static final int ORGANIZATION_ID = 938249873; private static final String EVENT_NAME = "VoxxedDaysTicino"; private static final String RESERVATION_ID = "what-a-reservation"; private static final String RESERVATION_EMAIL = "me@mydomain.com"; private static final String TRANSACTION_ID = "transaction-id"; private static final String GATEWAY_TOKEN = "token"; private static final String BASE_URL = "http://my-website/"; public static final String ORG_EMAIL = "org@org.org"; private TicketReservationManager trm; @Mock private NotificationManager notificationManager; @Mock private MessageSource messageSource; @Mock private TicketReservationRepository ticketReservationRepository; @Mock private PluginManager pluginManager; @Mock private FileUploadManager fileUploadManager; @Mock private TicketFieldRepository ticketFieldRepository; @Mock private ConfigurationManager configurationManager; @Mock private EventRepository eventRepository; @Mock private OrganizationRepository organizationRepository; @Mock private TicketRepository ticketRepository; @Mock private TicketCategoryRepository ticketCategoryRepository; @Mock private TicketCategoryDescriptionRepository ticketCategoryDescriptionRepository; @Mock private PaymentManager paymentManager; @Mock private PromoCodeDiscountRepository promoCodeDiscountRepository; @Mock private SpecialPriceRepository specialPriceRepository; @Mock private TransactionRepository transactionRepository; @Mock private TemplateManager templateManager; @Mock private PlatformTransactionManager transactionManager; @Mock private WaitingQueueManager waitingQueueManager; @Mock private AdditionalServiceRepository additionalServiceRepository; @Mock private AdditionalServiceTextRepository additionalServiceTextRepository; @Mock private AdditionalServiceItemRepository additionalServiceItemRepository; @Mock private InvoiceSequencesRepository invoiceSequencesRepository; @Mock private Event event; @Mock private SpecialPrice specialPrice; @Mock private TicketCategory ticketCategory; @Mock private Ticket ticket; @Mock private TicketReservationWithOptionalCodeModification reservationModification; @Mock private TicketReservation ticketReservation; @Mock private Organization organization; @Before public void init() { trm = new TicketReservationManager(eventRepository, organizationRepository, ticketRepository, ticketReservationRepository, ticketCategoryRepository, ticketCategoryDescriptionRepository, configurationManager, paymentManager, promoCodeDiscountRepository, specialPriceRepository, transactionRepository, notificationManager, messageSource, templateManager, transactionManager, waitingQueueManager, pluginManager, fileUploadManager, ticketFieldRepository, additionalServiceRepository, additionalServiceItemRepository, additionalServiceTextRepository, invoiceSequencesRepository); when(event.getId()).thenReturn(EVENT_ID); when(event.getOrganizationId()).thenReturn(ORGANIZATION_ID); when(event.mustUseFirstAndLastName()).thenReturn(false); when(ticketCategoryRepository.getById(eq(TICKET_CATEGORY_ID), eq(EVENT_ID))).thenReturn(ticketCategory); when(specialPrice.getCode()).thenReturn(SPECIAL_PRICE_CODE); when(specialPrice.getId()).thenReturn(SPECIAL_PRICE_ID); when(eventRepository.findByReservationId(eq(RESERVATION_ID))).thenReturn(event); when(eventRepository.findAll()).thenReturn(Collections.singletonList(event)); when(configurationManager.getRequiredValue(Configuration.from(event.getOrganizationId(), event.getId(), ConfigurationKeys.BASE_URL))).thenReturn(BASE_URL); when(configurationManager.hasAllConfigurationsForInvoice(eq(event))).thenReturn(false); when(ticketReservationRepository.findReservationById(RESERVATION_ID)).thenReturn(ticketReservation); when(ticket.getId()).thenReturn(TICKET_ID); when(ticket.getSrcPriceCts()).thenReturn(10); when(ticketCategory.getId()).thenReturn(TICKET_CATEGORY_ID); when(organizationRepository.getById(eq(ORGANIZATION_ID))).thenReturn(organization); when(event.getZoneId()).thenReturn(ZoneId.systemDefault()); when(event.getBegin()).thenReturn(ZonedDateTime.now().plusDays(1)); } private void initUpdateTicketOwner(Ticket original, Ticket modified, String ticketId, String originalEmail, String originalName, UpdateTicketOwnerForm form) { when(original.getUuid()).thenReturn(ticketId); when(original.getEmail()).thenReturn(originalEmail); when(original.getFullName()).thenReturn(originalName); when(ticketRepository.findByUUID(eq(ticketId))).thenReturn(modified); form.setEmail("new@email.tld"); form.setFullName(originalName); form.setUserLanguage(USER_LANGUAGE); when(organization.getEmail()).thenReturn(ORG_EMAIL); } @Test public void doNotSendWarningEmailIfAdmin() { final String ticketId = "abcde"; final String ticketReservationId = "abcdef"; final String originalEmail = "me@myaddress.com"; final String originalName = "First Last"; Ticket original = mock(Ticket.class); Ticket modified = mock(Ticket.class); UpdateTicketOwnerForm form = new UpdateTicketOwnerForm(); when(event.getShortName()).thenReturn("short-name"); initUpdateTicketOwner(original, modified, ticketId, originalEmail, originalName, form); TicketReservation reservation = mock(TicketReservation.class); when(original.getTicketsReservationId()).thenReturn(ticketReservationId); when(ticketReservationRepository.findReservationById(eq(ticketReservationId))).thenReturn(reservation); UserDetails userDetails = new User("user", "password", singletonList(new SimpleGrantedAuthority(Role.ADMIN.getRoleName()))); trm.updateTicketOwner(original, Locale.ENGLISH, event, form, (a) -> null,(b) -> null, Optional.of(userDetails)); verify(messageSource, never()).getMessage(eq("ticket-has-changed-owner-subject"), eq(new Object[] {"short-name"}), eq(Locale.ITALIAN)); } @Test public void sendWarningEmailIfNotAdmin() { final String ticketId = "abcde"; final String originalEmail = "me@myaddress.com"; final String originalName = "First Last"; Ticket original = mock(Ticket.class); when(original.getStatus()).thenReturn(TicketStatus.ACQUIRED); Ticket modified = mock(Ticket.class); UpdateTicketOwnerForm form = new UpdateTicketOwnerForm(); when(event.getShortName()).thenReturn("short-name"); initUpdateTicketOwner(original, modified, ticketId, originalEmail, originalName, form); PartialTicketTextGenerator ownerChangeTextBuilder = mock(PartialTicketTextGenerator.class); when(ownerChangeTextBuilder.generate(eq(modified))).thenReturn("Hello, world"); when(original.getUserLanguage()).thenReturn(USER_LANGUAGE); trm.updateTicketOwner(original, Locale.ENGLISH, event, form, (a) -> null, ownerChangeTextBuilder, Optional.empty()); verify(messageSource, times(1)).getMessage(eq("ticket-has-changed-owner-subject"), any(), eq(Locale.ITALIAN)); verify(notificationManager, times(1)).sendSimpleEmail(eq(event), eq(originalEmail), anyString(), any(TextTemplateGenerator.class)); } @Test public void sendWarningEmailToOrganizer() { final String ticketId = "abcde"; final String originalEmail = "me@myaddress.com"; final String originalName = "First Last"; Ticket original = mock(Ticket.class); when(original.getStatus()).thenReturn(TicketStatus.ACQUIRED); Ticket modified = mock(Ticket.class); UpdateTicketOwnerForm form = new UpdateTicketOwnerForm(); when(event.getShortName()).thenReturn("short-name"); initUpdateTicketOwner(original, modified, ticketId, originalEmail, originalName, form); PartialTicketTextGenerator ownerChangeTextBuilder = mock(PartialTicketTextGenerator.class); when(ownerChangeTextBuilder.generate(eq(modified))).thenReturn("Hello, world"); when(original.getUserLanguage()).thenReturn(USER_LANGUAGE); when(event.getBegin()).thenReturn(ZonedDateTime.now().minusSeconds(1)); trm.updateTicketOwner(original, Locale.ENGLISH, event, form, (a) -> null, ownerChangeTextBuilder, Optional.empty()); verify(messageSource, times(1)).getMessage(eq("ticket-has-changed-owner-subject"), any(), eq(Locale.ITALIAN)); verify(notificationManager, times(1)).sendSimpleEmail(eq(event), eq(originalEmail), anyString(), any(TextTemplateGenerator.class)); verify(notificationManager, times(1)).sendSimpleEmail(eq(event), eq(ORG_EMAIL), anyString(), any(TextTemplateGenerator.class)); } // check we don't send the ticket-has-changed-owner email if the originalEmail and name are present and the status is not ACQUIRED @Test public void dontSendWarningEmailIfNotAcquiredStatus() { final String ticketId = "abcde"; final String originalEmail = "me@myaddress.com"; final String originalName = "First Last"; Ticket original = mock(Ticket.class); when(original.getStatus()).thenReturn(TicketStatus.FREE); Ticket modified = mock(Ticket.class); UpdateTicketOwnerForm form = new UpdateTicketOwnerForm(); when(event.getShortName()).thenReturn("short-name"); initUpdateTicketOwner(original, modified, ticketId, originalEmail, originalName, form); PartialTicketTextGenerator ownerChangeTextBuilder = mock(PartialTicketTextGenerator.class); when(ownerChangeTextBuilder.generate(eq(modified))).thenReturn("Hello, world"); when(original.getUserLanguage()).thenReturn(USER_LANGUAGE); trm.updateTicketOwner(original, Locale.ENGLISH, event, form, (a) -> null, ownerChangeTextBuilder, Optional.empty()); verifyZeroInteractions(messageSource); } @Test public void fallbackToCurrentLocale() throws IOException { final String ticketId = "abcde"; final String originalEmail = "me@myaddress.com"; final String originalName = "First Last"; Ticket original = mock(Ticket.class); when(original.getStatus()).thenReturn(TicketStatus.ACQUIRED); Ticket modified = mock(Ticket.class); when(modified.getStatus()).thenReturn(TicketStatus.ACQUIRED); UpdateTicketOwnerForm form = new UpdateTicketOwnerForm(); when(event.getShortName()).thenReturn("short-name"); initUpdateTicketOwner(original, modified, ticketId, originalEmail, originalName, form); form.setUserLanguage(""); PartialTicketTextGenerator ownerChangeTextBuilder = mock(PartialTicketTextGenerator.class); when(ownerChangeTextBuilder.generate(eq(modified))).thenReturn("Hello, world"); when(original.getUserLanguage()).thenReturn(USER_LANGUAGE); trm.updateTicketOwner(original, Locale.ENGLISH, event, form, (a) -> null, ownerChangeTextBuilder, Optional.empty()); verify(messageSource, times(1)).getMessage(eq("ticket-has-changed-owner-subject"), any(), eq(Locale.ITALIAN)); verify(notificationManager, times(1)).sendTicketByEmail(eq(modified), eq(event), eq(Locale.ENGLISH), any(), any(), any()); verify(notificationManager, times(1)).sendSimpleEmail(eq(event), eq(originalEmail), anyString(), any(TextTemplateGenerator.class)); } @Test public void sendAssignmentReminderBeforeEventEnd() { TicketReservation reservation = mock(TicketReservation.class); when(configurationManager.getIntConfigValue(eq(Configuration.from(event.getOrganizationId(), event.getId(), ASSIGNMENT_REMINDER_START)), anyInt())).thenReturn(10); when(configurationManager.getStringConfigValue(any())).thenReturn(Optional.empty()); when(reservation.latestNotificationTimestamp(any())).thenReturn(Optional.empty()); when(reservation.getId()).thenReturn("abcd"); when(reservation.getUserLanguage()).thenReturn("en"); when(reservation.getValidity()).thenReturn(new Date()); when(ticketReservationRepository.findReservationById(eq("abcd"))).thenReturn(reservation); when(eventRepository.findByReservationId("abcd")).thenReturn(event); when(eventRepository.findAll()).thenReturn(singletonList(event)); when(ticketRepository.findAllReservationsConfirmedButNotAssigned(anyInt())).thenReturn(singletonList("abcd")); trm.sendReminderForTicketAssignment(); verify(notificationManager, times(1)).sendSimpleEmail(eq(event), anyString(), anyString(), any(TextTemplateGenerator.class)); } @Test public void doNotSendAssignmentReminderAfterEventEnd() { TicketReservation reservation = mock(TicketReservation.class); when(configurationManager.getIntConfigValue(eq(Configuration.from(event.getOrganizationId(), event.getId(), ASSIGNMENT_REMINDER_START)), anyInt())).thenReturn(10); when(configurationManager.getStringConfigValue(any())).thenReturn(Optional.empty()); when(reservation.latestNotificationTimestamp(any())).thenReturn(Optional.empty()); when(reservation.getId()).thenReturn("abcd"); when(ticketReservationRepository.findReservationById(eq("abcd"))).thenReturn(reservation); when(eventRepository.findByReservationId("abcd")).thenReturn(event); when(event.getZoneId()).thenReturn(ZoneId.systemDefault()); when(event.getBegin()).thenReturn(ZonedDateTime.now().minusDays(1)); when(eventRepository.findAll()).thenReturn(singletonList(event)); when(ticketRepository.findAllReservationsConfirmedButNotAssigned(anyInt())).thenReturn(singletonList("abcd")); trm.sendReminderForTicketAssignment(); verify(notificationManager, never()).sendSimpleEmail(eq(event), anyString(), anyString(), any(TextTemplateGenerator.class)); } @Test public void considerZoneIdWhileChecking() { TicketReservation reservation = mock(TicketReservation.class); when(configurationManager.getIntConfigValue(eq(Configuration.from(event.getOrganizationId(), event.getId(), ASSIGNMENT_REMINDER_START)), anyInt())).thenReturn(10); when(configurationManager.getStringConfigValue(any())).thenReturn(Optional.empty()); when(reservation.latestNotificationTimestamp(any())).thenReturn(Optional.empty()); when(reservation.getId()).thenReturn("abcd"); when(reservation.getUserLanguage()).thenReturn("en"); when(reservation.getValidity()).thenReturn(new Date()); when(ticketReservationRepository.findReservationById(eq("abcd"))).thenReturn(reservation); when(eventRepository.findByReservationId("abcd")).thenReturn(event); when(event.getZoneId()).thenReturn(ZoneId.of("GMT-4")); when(event.getBegin()).thenReturn(ZonedDateTime.now(ZoneId.of("GMT-4")).plusDays(1)); when(eventRepository.findAll()).thenReturn(singletonList(event)); when(ticketRepository.findAllReservationsConfirmedButNotAssigned(anyInt())).thenReturn(singletonList("abcd")); trm.sendReminderForTicketAssignment(); verify(notificationManager, times(1)).sendSimpleEmail(eq(event), anyString(), anyString(), any(TextTemplateGenerator.class)); } @Test public void considerZoneIdWhileCheckingExpired() { TicketReservation reservation = mock(TicketReservation.class); when(configurationManager.getIntConfigValue(eq(Configuration.from(event.getOrganizationId(), event.getId(), ASSIGNMENT_REMINDER_START)), anyInt())).thenReturn(10); when(configurationManager.getStringConfigValue(any())).thenReturn(Optional.empty()); when(reservation.latestNotificationTimestamp(any())).thenReturn(Optional.empty()); when(reservation.getId()).thenReturn("abcd"); when(ticketReservationRepository.findReservationById(eq("abcd"))).thenReturn(reservation); when(eventRepository.findByReservationId("abcd")).thenReturn(event); when(event.getZoneId()).thenReturn(ZoneId.of("UTC-8")); when(event.getBegin()).thenReturn(ZonedDateTime.now(ZoneId.of("UTC-8")));//same day when(eventRepository.findAll()).thenReturn(singletonList(event)); when(ticketRepository.findAllReservationsConfirmedButNotAssigned(anyInt())).thenReturn(singletonList("abcd")); trm.sendReminderForTicketAssignment(); verify(notificationManager, never()).sendSimpleEmail(eq(event), anyString(), anyString(), any(TextTemplateGenerator.class)); } @Test public void doNotSendReminderTooEarly() { TicketReservation reservation = mock(TicketReservation.class); when(configurationManager.getIntConfigValue(eq(Configuration.from(event.getOrganizationId(), event.getId(), ASSIGNMENT_REMINDER_START)), anyInt())).thenReturn(10); when(configurationManager.getStringConfigValue(any())).thenReturn(Optional.empty()); when(reservation.latestNotificationTimestamp(any())).thenReturn(Optional.empty()); when(reservation.getId()).thenReturn("abcd"); when(ticketReservationRepository.findReservationById(eq("abcd"))).thenReturn(reservation); when(eventRepository.findByReservationId("abcd")).thenReturn(event); when(event.getZoneId()).thenReturn(ZoneId.of("UTC-8")); when(event.getBegin()).thenReturn(ZonedDateTime.now(ZoneId.of("UTC-8")).plusMonths(3).plusDays(1)); when(eventRepository.findAll()).thenReturn(singletonList(event)); when(ticketRepository.findAllReservationsConfirmedButNotAssigned(anyInt())).thenReturn(singletonList("abcd")); List<Event> events = trm.getNotifiableEventsStream().collect(Collectors.toList()); assertEquals(0, events.size()); verify(notificationManager, never()).sendSimpleEmail(eq(event), anyString(), anyString(), any(TextTemplateGenerator.class)); } private void initOfflinePaymentTest() { when(configurationManager.getIntConfigValue(eq(Configuration.from(event.getOrganizationId(), event.getId(), OFFLINE_PAYMENT_DAYS)), anyInt())).thenReturn(2); when(event.getZoneId()).thenReturn(ZoneId.systemDefault()); } @Test public void returnTheExpiredDateAsConfigured() { initOfflinePaymentTest(); when(event.getBegin()).thenReturn(ZonedDateTime.now().plusDays(3)); ZonedDateTime offlinePaymentDeadline = TicketReservationManager.getOfflinePaymentDeadline(event, configurationManager); assertEquals(2L, ChronoUnit.DAYS.between(LocalDate.now(), offlinePaymentDeadline.toLocalDate())); } @Test public void returnTheConfiguredWaitingTime() { initOfflinePaymentTest(); when(event.getBegin()).thenReturn(ZonedDateTime.now().plusDays(3)); assertEquals(2, TicketReservationManager.getOfflinePaymentWaitingPeriod(event, configurationManager)); } @Test public void considerEventBeginDateWhileCalculatinExpDate() { initOfflinePaymentTest(); when(event.getBegin()).thenReturn(ZonedDateTime.now().plusDays(1)); ZonedDateTime offlinePaymentDeadline = TicketReservationManager.getOfflinePaymentDeadline(event, configurationManager); assertEquals(1L, ChronoUnit.DAYS.between(LocalDate.now(), offlinePaymentDeadline.toLocalDate())); } @Test public void returnConfiguredWaitingTimeConsideringEventStart() { initOfflinePaymentTest(); when(event.getBegin()).thenReturn(ZonedDateTime.now().plusDays(1)); assertEquals(1, TicketReservationManager.getOfflinePaymentWaitingPeriod(event, configurationManager)); } @Test public void neverReturnADateInThePast() { initOfflinePaymentTest(); when(event.getBegin()).thenReturn(ZonedDateTime.now()); ZonedDateTime offlinePaymentDeadline = TicketReservationManager.getOfflinePaymentDeadline(event, configurationManager); assertEquals(true, offlinePaymentDeadline.isAfter(ZonedDateTime.now())); } @Test(expected = TicketReservationManager.OfflinePaymentException.class) public void throwExceptionAfterEventStart() { initOfflinePaymentTest(); when(event.getBegin()).thenReturn(ZonedDateTime.now().minusDays(1)); TicketReservationManager.getOfflinePaymentDeadline(event, configurationManager); fail(); } //fix token @Test public void doNothingIfPrerequisitesAreNotSatisfied() { //do nothing if the category is not restricted assertFalse(trm.fixToken(Optional.empty(), TICKET_CATEGORY_ID, EVENT_ID, Optional.empty(), mock(TicketReservationWithOptionalCodeModification.class)).isPresent()); //do nothing if special price status is pending and sessionId don't match assertFalse(trm.renewSpecialPrice(Optional.of(specialPrice), Optional.empty()).isPresent()); //do nothing if special price status is pending and sessionId don't match when(specialPrice.getStatus()).thenReturn(SpecialPrice.Status.PENDING); when(specialPrice.getSessionIdentifier()).thenReturn("another-id"); assertFalse(trm.renewSpecialPrice(Optional.of(specialPrice), Optional.of(SPECIAL_PRICE_SESSION_ID)).isPresent()); } @Test public void renewSpecialPrice() { when(ticketCategory.isAccessRestricted()).thenReturn(true); when(specialPrice.getStatus()).thenReturn(SpecialPrice.Status.FREE); when(specialPriceRepository.getByCode(eq(SPECIAL_PRICE_CODE))).thenReturn(specialPrice); Optional<SpecialPrice> renewed = trm.renewSpecialPrice(Optional.of(specialPrice), Optional.of(SPECIAL_PRICE_SESSION_ID)); verify(specialPriceRepository).bindToSession(eq(SPECIAL_PRICE_ID), eq(SPECIAL_PRICE_SESSION_ID)); assertTrue(renewed.isPresent()); assertSame(specialPrice, renewed.get()); } @Test public void cancelPendingReservationAndRenewCode() { String RESERVATION_ID = "rid"; when(ticketRepository.releaseExpiredTicket(eq(RESERVATION_ID), anyInt(), anyInt())).thenReturn(1); when(eventRepository.findByReservationId(eq(RESERVATION_ID))).thenReturn(event); when(ticketRepository.findTicketsInReservation(eq(RESERVATION_ID))).thenReturn(Collections.singletonList(ticket)); when(ticket.getTicketsReservationId()).thenReturn(RESERVATION_ID); when(ticketRepository.findBySpecialPriceId(eq(SPECIAL_PRICE_ID))).thenReturn(ticket); TicketReservation reservation = mock(TicketReservation.class); when(ticketReservationRepository.findReservationById(eq(RESERVATION_ID))).thenReturn(reservation); when(reservation.getStatus()).thenReturn(TicketReservationStatus.PENDING); when(reservation.getId()).thenReturn(RESERVATION_ID); when(ticketRepository.freeFromReservation(eq(singletonList(RESERVATION_ID)))).thenReturn(1); when(ticketReservationRepository.remove(eq(singletonList(RESERVATION_ID)))).thenReturn(1); when(specialPriceRepository.getByCode(eq(SPECIAL_PRICE_CODE))).thenReturn(specialPrice); when(specialPrice.getStatus()).thenReturn(SpecialPrice.Status.PENDING); when(specialPrice.getSessionIdentifier()).thenReturn(SPECIAL_PRICE_SESSION_ID); Optional<SpecialPrice> renewed = trm.renewSpecialPrice(Optional.of(specialPrice), Optional.of(SPECIAL_PRICE_SESSION_ID)); verify(specialPriceRepository).updateStatusForReservation(eq(singletonList(RESERVATION_ID)), eq(SpecialPrice.Status.FREE.toString())); verify(ticketRepository).resetCategoryIdForUnboundedCategories(eq(singletonList(RESERVATION_ID))); verify(ticketRepository).releaseExpiredTicket(RESERVATION_ID, EVENT_ID, TICKET_ID); verify(ticketReservationRepository).remove(eq(singletonList(RESERVATION_ID))); verify(waitingQueueManager).fireReservationExpired(eq(RESERVATION_ID)); assertTrue(renewed.isPresent()); assertSame(specialPrice, renewed.get()); } //reserve tickets for category @Test public void reserveTicketsForBoundedCategories() throws Exception { when(ticketCategory.isBounded()).thenReturn(true); List<Integer> ids = singletonList(1); when(ticketRepository.selectTicketInCategoryForUpdate(eq(EVENT_ID), eq(TICKET_CATEGORY_ID), eq(1), eq(singletonList(Ticket.TicketStatus.FREE.name())))).thenReturn(ids); when(reservationModification.getAmount()).thenReturn(1); when(reservationModification.getTicketCategoryId()).thenReturn(TICKET_CATEGORY_ID); when(ticketRepository.findById(1, TICKET_CATEGORY_ID)).thenReturn(ticket); trm.reserveTicketsForCategory(event, Optional.empty(), "trid", reservationModification, Locale.ENGLISH, false, null); verify(ticketRepository).reserveTickets("trid", ids, TICKET_CATEGORY_ID, Locale.ENGLISH.getLanguage(), 0); } @Test public void reserveTicketsForBoundedCategoriesWaitingQueue() throws Exception { when(ticketCategory.isBounded()).thenReturn(true); List<Integer> ids = singletonList(1); when(ticketRepository.selectTicketInCategoryForUpdate(eq(EVENT_ID), eq(TICKET_CATEGORY_ID), eq(1), eq(asList(TicketStatus.RELEASED.name(), TicketStatus.PRE_RESERVED.name())))).thenReturn(ids); when(reservationModification.getAmount()).thenReturn(1); when(reservationModification.getTicketCategoryId()).thenReturn(TICKET_CATEGORY_ID); when(ticketRepository.findById(1, TICKET_CATEGORY_ID)).thenReturn(ticket); trm.reserveTicketsForCategory(event, Optional.empty(), "trid", reservationModification, Locale.ENGLISH, true, null); verify(ticketRepository).reserveTickets("trid", ids, TICKET_CATEGORY_ID, Locale.ENGLISH.getLanguage(), 0); } @Test public void reserveTicketsForUnboundedCategories() throws Exception { when(ticketCategory.isBounded()).thenReturn(false); List<Integer> ids = singletonList(1); when(ticketRepository.selectNotAllocatedTicketsForUpdate(eq(EVENT_ID), eq(1), eq(singletonList(Ticket.TicketStatus.FREE.name())))).thenReturn(ids); when(reservationModification.getAmount()).thenReturn(1); when(reservationModification.getTicketCategoryId()).thenReturn(TICKET_CATEGORY_ID); when(ticketRepository.findById(1, TICKET_CATEGORY_ID)).thenReturn(ticket); trm.reserveTicketsForCategory(event, Optional.empty(), "trid", reservationModification, Locale.ENGLISH, false, null); verify(ticketRepository).reserveTickets("trid", ids, TICKET_CATEGORY_ID, Locale.ENGLISH.getLanguage(), 0); } @Test public void reserveTicketsForUnboundedCategoriesWaitingQueue() throws Exception { when(ticketCategory.isBounded()).thenReturn(false); List<Integer> ids = singletonList(1); when(ticketRepository.selectNotAllocatedTicketsForUpdate(eq(EVENT_ID), eq(1), eq(asList(TicketStatus.RELEASED.name(), TicketStatus.PRE_RESERVED.name())))).thenReturn(ids); when(reservationModification.getAmount()).thenReturn(1); when(reservationModification.getTicketCategoryId()).thenReturn(TICKET_CATEGORY_ID); when(ticketRepository.findById(1, TICKET_CATEGORY_ID)).thenReturn(ticket); trm.reserveTicketsForCategory(event, Optional.empty(), "trid", reservationModification, Locale.ENGLISH, true, null); verify(ticketRepository).reserveTickets("trid", ids, TICKET_CATEGORY_ID, Locale.ENGLISH.getLanguage(), 0); } //cleanup expired reservations @Test public void doNothingIfNoReservations() throws Exception { Date now = new Date(); when(ticketReservationRepository.findExpiredReservation(eq(now))).thenReturn(Collections.emptyList()); trm.cleanupExpiredReservations(now); verify(ticketReservationRepository).findExpiredReservation(eq(now)); verifyNoMoreInteractions(ticketReservationRepository, specialPriceRepository, ticketRepository, waitingQueueManager); } @Test public void cancelExpiredReservations() throws Exception { Date now = new Date(); List<String> reservationIds = singletonList("reservation-id"); when(ticketReservationRepository.findExpiredReservation(eq(now))).thenReturn(reservationIds); trm.cleanupExpiredReservations(now); verify(ticketReservationRepository).findExpiredReservation(eq(now)); verify(specialPriceRepository).updateStatusForReservation(eq(reservationIds), eq(SpecialPrice.Status.FREE.toString())); verify(ticketRepository).resetCategoryIdForUnboundedCategories(eq(reservationIds)); verify(ticketRepository).freeFromReservation(eq(reservationIds)); verify(ticketReservationRepository).remove(eq(reservationIds)); verify(waitingQueueManager).cleanExpiredReservations(eq(reservationIds)); verifyNoMoreInteractions(ticketReservationRepository, specialPriceRepository, ticketRepository); } @Test public void countAvailableTickets() throws Exception { //count how many tickets yet available for a category when(ticketCategory.isBounded()).thenReturn(true); trm.countAvailableTickets(event, ticketCategory); verify(ticketRepository).countFreeTickets(eq(EVENT_ID), eq(TICKET_CATEGORY_ID)); //count how many tickets are available for unbounded categories when(ticketCategory.isBounded()).thenReturn(false); trm.countAvailableTickets(event, ticketCategory); verify(ticketRepository).countFreeTicketsForUnbounded(eq(EVENT_ID)); } private void initReleaseTicket() { when(ticket.getId()).thenReturn(TICKET_ID); when(ticketCategoryDescriptionRepository.findByTicketCategoryIdAndLocale(anyInt(), anyString())).thenReturn(Optional.of("desc")); when(ticket.getEmail()).thenReturn(RESERVATION_EMAIL); when(ticket.getUserLanguage()).thenReturn(USER_LANGUAGE); when(ticket.getEventId()).thenReturn(EVENT_ID); when(ticket.getStatus()).thenReturn(Ticket.TicketStatus.ACQUIRED); when(event.getId()).thenReturn(EVENT_ID); when(event.getOrganizationId()).thenReturn(ORGANIZATION_ID); when(ticket.getCategoryId()).thenReturn(TICKET_CATEGORY_ID); when(ticketReservation.getId()).thenReturn(RESERVATION_ID); when(ticketCategoryRepository.getById(eq(TICKET_CATEGORY_ID), eq(EVENT_ID))).thenReturn(ticketCategory); when(organizationRepository.getById(eq(ORGANIZATION_ID))).thenReturn(organization); when(event.getShortName()).thenReturn(EVENT_NAME); } @Test public void sendEmailToAssigneeOnSuccess() throws Exception { initReleaseTicket(); String organizationEmail = "ciccio@test"; when(organization.getEmail()).thenReturn(organizationEmail); when(ticketCategory.isAccessRestricted()).thenReturn(false); when(ticketRepository.releaseTicket(eq(RESERVATION_ID), eq(EVENT_ID), eq(TICKET_ID))).thenReturn(1); when(ticketCategory.isAccessRestricted()).thenReturn(false); List<String> expectedReservations = singletonList(RESERVATION_ID); when(ticketReservationRepository.remove(eq(expectedReservations))).thenReturn(1); when(transactionRepository.loadOptionalByReservationId(anyString())).thenReturn(Optional.empty()); trm.releaseTicket(event, ticketReservation, ticket); verify(ticketRepository).releaseTicket(eq(RESERVATION_ID), eq(EVENT_ID), eq(TICKET_ID)); verify(notificationManager).sendSimpleEmail(eq(event), eq(RESERVATION_EMAIL), any(), any(TextTemplateGenerator.class)); verify(notificationManager).sendSimpleEmail(eq(event), eq(organizationEmail), any(), any(TextTemplateGenerator.class)); verify(organizationRepository).getById(eq(ORGANIZATION_ID)); verify(ticketReservationRepository).remove(eq(expectedReservations)); } @Test(expected = IllegalStateException.class) public void cannotReleaseRestrictedTicketIfNoUnboundedCategory() throws Exception { initReleaseTicket(); when(ticketCategoryRepository.getById(eq(TICKET_CATEGORY_ID), eq(EVENT_ID))).thenReturn(ticketCategory); when(ticketCategoryRepository.countUnboundedCategoriesByEventId(eq(EVENT_ID))).thenReturn(0); when(ticketCategory.isAccessRestricted()).thenReturn(true); trm.releaseTicket(event, ticketReservation, ticket); verify(ticketCategoryRepository).countUnboundedCategoriesByEventId(eq(EVENT_ID)); } @Test public void releaseRestrictedTicketIfUnboundedCategoryPresent() throws Exception { initReleaseTicket(); when(ticketCategory.getId()).thenReturn(TICKET_CATEGORY_ID); when(ticketRepository.releaseTicket(eq(RESERVATION_ID), eq(EVENT_ID), eq(TICKET_ID))).thenReturn(1); when(ticketCategoryRepository.getById(eq(TICKET_CATEGORY_ID), eq(EVENT_ID))).thenReturn(ticketCategory); when(ticketCategory.isAccessRestricted()).thenReturn(true); when(ticketCategoryRepository.countUnboundedCategoriesByEventId(eq(EVENT_ID))).thenReturn(1); List<String> expectedReservations = singletonList(RESERVATION_ID); when(ticketReservationRepository.remove(eq(expectedReservations))).thenReturn(1); when(transactionRepository.loadOptionalByReservationId(anyString())).thenReturn(Optional.empty()); trm.releaseTicket(event, ticketReservation, ticket); verify(ticketRepository).releaseTicket(eq(RESERVATION_ID), eq(EVENT_ID), eq(TICKET_ID)); verify(ticketRepository).unbindTicketsFromCategory(eq(EVENT_ID), eq(TICKET_CATEGORY_ID), eq(singletonList(TICKET_ID))); verify(notificationManager).sendSimpleEmail(eq(event), eq(RESERVATION_EMAIL), any(), any(TextTemplateGenerator.class)); verify(organizationRepository).getById(eq(ORGANIZATION_ID)); verify(ticketReservationRepository).remove(eq(expectedReservations)); } @Test public void throwExceptionIfMultipleTickets() throws Exception { initReleaseTicket(); when(ticketRepository.releaseTicket(eq(RESERVATION_ID), eq(EVENT_ID), eq(TICKET_ID))).thenReturn(2); try { trm.releaseTicket(event, ticketReservation, ticket); fail(); } catch (IllegalArgumentException e) { verify(ticketRepository).releaseTicket(eq(RESERVATION_ID), eq(EVENT_ID), eq(TICKET_ID)); verify(notificationManager, never()).sendSimpleEmail(any(), any(), any(), any(TextTemplateGenerator.class)); } } //confirm reservation private void initConfirmReservation() { when(event.getZoneId()).thenReturn(ZoneId.systemDefault()); when(event.getBegin()).thenReturn(ZonedDateTime.now().plusDays(5)); } @Test public void confirmPaidReservation() throws Exception { initConfirmReservation(); when(ticketReservationRepository.updateTicketReservation(eq(RESERVATION_ID), eq(TicketReservationStatus.COMPLETE.toString()), anyString(), anyString(), anyString(), anyString(), anyString(), anyString(), any(), eq(PaymentProxy.STRIPE.toString()))).thenReturn(1); when(ticketRepository.updateTicketsStatusWithReservationId(eq(RESERVATION_ID), eq(TicketStatus.ACQUIRED.toString()))).thenReturn(1); when(ticketReservationRepository.updateTicketReservation(eq(RESERVATION_ID), eq(IN_PAYMENT.toString()), anyString(), anyString(), anyString(), anyString(),anyString(), anyString(), isNull(ZonedDateTime.class), eq(PaymentProxy.STRIPE.toString()))).thenReturn(1); when(paymentManager.processStripePayment(eq(RESERVATION_ID), eq(GATEWAY_TOKEN), anyInt(), eq(event), anyString(), any(CustomerName.class), anyString())).thenReturn(PaymentResult.successful(TRANSACTION_ID)); PaymentResult result = trm.confirm(GATEWAY_TOKEN, null, event, RESERVATION_ID, "", new CustomerName("Full Name", null, null, event), Locale.ENGLISH, "", new TotalPrice(100, 0, 0, 0), Optional.empty(), Optional.of(PaymentProxy.STRIPE)); assertTrue(result.isSuccessful()); assertEquals(Optional.of(TRANSACTION_ID), result.getGatewayTransactionId()); verify(ticketReservationRepository).updateTicketReservation(eq(RESERVATION_ID), eq(TicketReservationStatus.IN_PAYMENT.toString()), anyString(), anyString(), anyString(),anyString(), anyString(), anyString(), any(), eq(PaymentProxy.STRIPE.toString())); verify(ticketReservationRepository).lockReservationForUpdate(eq(RESERVATION_ID)); verify(paymentManager).processStripePayment(eq(RESERVATION_ID), eq(GATEWAY_TOKEN), anyInt(), eq(event), anyString(), any(CustomerName.class), anyString()); verify(ticketRepository).updateTicketsStatusWithReservationId(eq(RESERVATION_ID), eq(TicketStatus.ACQUIRED.toString())); verify(specialPriceRepository).updateStatusForReservation(eq(singletonList(RESERVATION_ID)), eq(SpecialPrice.Status.TAKEN.toString())); verify(ticketReservationRepository).updateTicketReservation(eq(RESERVATION_ID), eq(TicketReservationStatus.COMPLETE.toString()), anyString(), anyString(),anyString(), anyString(), anyString(), anyString(), any(), eq(PaymentProxy.STRIPE.toString())); verify(waitingQueueManager).fireReservationConfirmed(eq(RESERVATION_ID)); verify(pluginManager).handleReservationConfirmation(ticketReservation, EVENT_ID); verify(ticketReservationRepository).findReservationById(RESERVATION_ID); verify(configurationManager).hasAllConfigurationsForInvoice(eq(event)); verifyNoMoreInteractions(ticketReservationRepository, paymentManager, ticketRepository, specialPriceRepository, waitingQueueManager, configurationManager); } @Test public void returnFailureCodeIfPaymentNotSuccesful() throws Exception { initConfirmReservation(); when(ticketReservationRepository.updateTicketReservation(eq(RESERVATION_ID), eq(IN_PAYMENT.toString()), anyString(), anyString(), anyString(), anyString(), anyString(), anyString(), isNull(ZonedDateTime.class), eq(PaymentProxy.STRIPE.toString()))).thenReturn(1); when(ticketReservationRepository.updateTicketStatus(eq(RESERVATION_ID), eq(TicketReservationStatus.PENDING.toString()))).thenReturn(1); when(paymentManager.processStripePayment(eq(RESERVATION_ID), eq(GATEWAY_TOKEN), anyInt(), eq(event), anyString(), any(CustomerName.class), anyString())).thenReturn(PaymentResult.unsuccessful("error-code")); PaymentResult result = trm.confirm(GATEWAY_TOKEN, null, event, RESERVATION_ID, "", new CustomerName("Full Name", null, null, event), Locale.ENGLISH, "", new TotalPrice(100, 0, 0, 0), Optional.empty(), Optional.of(PaymentProxy.STRIPE)); assertFalse(result.isSuccessful()); assertFalse(result.getGatewayTransactionId().isPresent()); assertEquals(Optional.of("error-code"), result.getErrorCode()); verify(ticketReservationRepository).updateTicketReservation(eq(RESERVATION_ID), eq(TicketReservationStatus.IN_PAYMENT.toString()), anyString(), anyString(), anyString(), anyString(), anyString(), anyString(), any(), eq(PaymentProxy.STRIPE.toString())); verify(ticketReservationRepository).lockReservationForUpdate(eq(RESERVATION_ID)); verify(paymentManager).processStripePayment(eq(RESERVATION_ID), eq(GATEWAY_TOKEN), anyInt(), eq(event), anyString(), any(CustomerName.class), anyString()); verify(ticketReservationRepository).updateTicketStatus(eq(RESERVATION_ID), eq(TicketReservationStatus.PENDING.toString())); verify(configurationManager).hasAllConfigurationsForInvoice(eq(event)); verifyNoMoreInteractions(ticketReservationRepository, paymentManager, ticketRepository, specialPriceRepository, waitingQueueManager, configurationManager); } @Test public void handleOnSitePaymentMethod() throws Exception { initConfirmReservation(); when(ticketRepository.updateTicketsStatusWithReservationId(eq(RESERVATION_ID), eq(TicketStatus.TO_BE_PAID.toString()))).thenReturn(1); when(ticketReservationRepository.updateTicketReservation(eq(RESERVATION_ID), eq(COMPLETE.toString()), anyString(), anyString(), anyString(), anyString(), anyString(), anyString(), any(ZonedDateTime.class), eq(PaymentProxy.ON_SITE.toString()))).thenReturn(1); when(paymentManager.processStripePayment(eq(RESERVATION_ID), eq(GATEWAY_TOKEN), anyInt(), eq(event), anyString(), any(CustomerName.class), anyString())).thenReturn(PaymentResult.unsuccessful("error-code")); PaymentResult result = trm.confirm(GATEWAY_TOKEN, null, event, RESERVATION_ID, "", new CustomerName("Full Name", null, null, event), Locale.ENGLISH, "", new TotalPrice(100, 0, 0, 0), Optional.empty(), Optional.of(PaymentProxy.ON_SITE)); assertTrue(result.isSuccessful()); assertEquals(Optional.of(TicketReservationManager.NOT_YET_PAID_TRANSACTION_ID), result.getGatewayTransactionId()); verify(ticketReservationRepository).updateTicketReservation(eq(RESERVATION_ID), eq(TicketReservationStatus.COMPLETE.toString()), anyString(), anyString(), anyString(), anyString(), anyString(), anyString(), any(), eq(PaymentProxy.ON_SITE.toString())); verify(ticketReservationRepository).lockReservationForUpdate(eq(RESERVATION_ID)); verify(ticketRepository).updateTicketsStatusWithReservationId(eq(RESERVATION_ID), eq(TicketStatus.TO_BE_PAID.toString())); verify(specialPriceRepository).updateStatusForReservation(eq(singletonList(RESERVATION_ID)), eq(SpecialPrice.Status.TAKEN.toString())); verify(waitingQueueManager).fireReservationConfirmed(eq(RESERVATION_ID)); verify(pluginManager).handleReservationConfirmation(ticketReservation, EVENT_ID); verify(ticketReservationRepository).findReservationById(RESERVATION_ID); verify(configurationManager).hasAllConfigurationsForInvoice(eq(event)); verifyNoMoreInteractions(ticketReservationRepository, paymentManager, ticketRepository, specialPriceRepository, waitingQueueManager, configurationManager); } @Test public void handleOfflinePaymentMethod() throws Exception { initConfirmReservation(); when(ticketReservationRepository.postponePayment(eq(RESERVATION_ID), any(Date.class), anyString(), anyString(), anyString(), anyString(), anyString())).thenReturn(1); PaymentResult result = trm.confirm(GATEWAY_TOKEN, null, event, RESERVATION_ID, "", new CustomerName("Full Name", null, null, event), Locale.ENGLISH, "", new TotalPrice(100, 0, 0, 0), Optional.empty(), Optional.of(PaymentProxy.OFFLINE)); assertTrue(result.isSuccessful()); assertEquals(Optional.of(TicketReservationManager.NOT_YET_PAID_TRANSACTION_ID), result.getGatewayTransactionId()); verify(waitingQueueManager, never()).fireReservationConfirmed(eq(RESERVATION_ID)); verify(ticketReservationRepository).lockReservationForUpdate(eq(RESERVATION_ID)); verify(ticketReservationRepository).postponePayment(eq(RESERVATION_ID), any(Date.class), any(), anyString(), anyString(), anyString(), anyString()); verify(configurationManager).getIntConfigValue(eq(Configuration.from(event.getOrganizationId(), event.getId(), OFFLINE_PAYMENT_DAYS)), eq(5)); verify(configurationManager).hasAllConfigurationsForInvoice(eq(event)); verifyNoMoreInteractions(ticketReservationRepository, paymentManager, ticketRepository, specialPriceRepository, waitingQueueManager, configurationManager); } @Test public void confirmOfflinePayments() throws Exception { initConfirmReservation(); TicketReservation reservation = mock(TicketReservation.class); when(reservation.getConfirmationTimestamp()).thenReturn(ZonedDateTime.now()); when(reservation.getId()).thenReturn(RESERVATION_ID); when(reservation.getPaymentMethod()).thenReturn(PaymentProxy.OFFLINE); when(reservation.getStatus()).thenReturn(OFFLINE_PAYMENT); when(reservation.getUserLanguage()).thenReturn("en"); when(reservation.getFullName()).thenReturn("Full Name"); when(reservation.getValidity()).thenReturn(new Date()); when(ticketReservationRepository.findReservationById(eq(RESERVATION_ID))).thenReturn(reservation); when(ticketRepository.updateTicketsStatusWithReservationId(eq(RESERVATION_ID), eq(TicketStatus.ACQUIRED.toString()))).thenReturn(1); when(ticketReservationRepository.updateTicketReservation(eq(RESERVATION_ID), eq(COMPLETE.toString()), anyString(), anyString(), anyString(), anyString(), anyString(), anyString(), any(ZonedDateTime.class), eq(PaymentProxy.OFFLINE.toString()))).thenReturn(1); when(configurationManager.getStringConfigValue(any())).thenReturn(Optional.of("vatnr")); when(ticketRepository.findTicketsInReservation(eq(RESERVATION_ID))).thenReturn(Collections.emptyList()); when(eventRepository.findByReservationId(eq(RESERVATION_ID))).thenReturn(event); trm.confirmOfflinePayment(event, RESERVATION_ID); verify(ticketReservationRepository, atLeastOnce()).findReservationById(RESERVATION_ID); verify(ticketReservationRepository).lockReservationForUpdate(eq(RESERVATION_ID)); verify(ticketReservationRepository).confirmOfflinePayment(eq(RESERVATION_ID), eq(COMPLETE.toString()), any(ZonedDateTime.class)); verify(ticketRepository).updateTicketsStatusWithReservationId(eq(RESERVATION_ID), eq(TicketStatus.ACQUIRED.toString())); verify(ticketReservationRepository).updateTicketReservation(eq(RESERVATION_ID), eq(TicketReservationStatus.COMPLETE.toString()), anyString(), anyString(), anyString(), anyString(), anyString(), anyString(), any(), eq(PaymentProxy.OFFLINE.toString())); verify(waitingQueueManager).fireReservationConfirmed(eq(RESERVATION_ID)); verify(ticketRepository, atLeastOnce()).findTicketsInReservation(RESERVATION_ID); verify(specialPriceRepository).updateStatusForReservation(eq(singletonList(RESERVATION_ID)), eq(SpecialPrice.Status.TAKEN.toString())); verify(configurationManager, atLeastOnce()).getStringConfigValue(any()); verify(configurationManager, atLeastOnce()).getRequiredValue(any()); verify(configurationManager, atLeastOnce()).getShortReservationID(eq(event), eq(RESERVATION_ID)); verifyNoMoreInteractions(ticketReservationRepository, paymentManager, ticketRepository, specialPriceRepository, waitingQueueManager, configurationManager); } @Test public void reservationURLGeneration() throws Exception { String shortName = "shortName"; String ticketId = "ticketId"; when(event.getShortName()).thenReturn(shortName); when(ticketReservation.getUserLanguage()).thenReturn("en"); when(ticketReservationRepository.findReservationById(RESERVATION_ID)).thenReturn(ticketReservation); when(ticketRepository.findByUUID(ticketId)).thenReturn(ticket); when(ticket.getUserLanguage()).thenReturn(USER_LANGUAGE); //generate the reservationUrl from RESERVATION_ID assertEquals(BASE_URL + "event/" + shortName + "/reservation/" + RESERVATION_ID + "?lang=en", trm.reservationUrl(RESERVATION_ID)); //generate the reservationUrl from RESERVATION_ID and event assertEquals(BASE_URL + "event/" + shortName + "/reservation/" + RESERVATION_ID + "?lang=en", trm.reservationUrl(RESERVATION_ID, event)); //generate the ticket URL assertEquals(BASE_URL + "event/" + shortName + "/ticket/ticketId?lang=it", trm.ticketUrl(event, ticketId)); //generate the ticket update URL assertEquals(BASE_URL + "event/" + shortName + "/ticket/ticketId/update?lang=it", trm.ticketUpdateUrl(event, "ticketId")); } //sendReminderForOptionalInfo private void initReminder() { when(ticketFieldRepository.countAdditionalFieldsForEvent(EVENT_ID)).thenReturn(1); } @Test public void sendReminderOnlyIfNoPreviousNotifications() throws Exception { initReminder(); when(event.getId()).thenReturn(EVENT_ID); when(configurationManager.getIntConfigValue(eq(Configuration.from(event.getOrganizationId(), event.getId(), ASSIGNMENT_REMINDER_START)), anyInt())).thenReturn(10); when(configurationManager.getStringConfigValue(any())).thenReturn(Optional.empty()); when(ticketReservation.latestNotificationTimestamp(any())).thenReturn(Optional.empty()); when(ticketReservation.getId()).thenReturn(RESERVATION_ID); when(ticket.getTicketsReservationId()).thenReturn(RESERVATION_ID); int ticketId = 2; when(ticket.getId()).thenReturn(ticketId); when(ticketRepository.findAllAssignedButNotYetNotified(EVENT_ID)).thenReturn(singletonList(ticket)); when(ticketReservationRepository.findReservationById(eq(RESERVATION_ID))).thenReturn(ticketReservation); when(eventRepository.findByReservationId(RESERVATION_ID)).thenReturn(event); when(event.getZoneId()).thenReturn(ZoneId.systemDefault()); when(event.getBegin()).thenReturn(ZonedDateTime.now().plusDays(1)); when(eventRepository.findAll()).thenReturn(singletonList(event)); when(ticketRepository.findAllReservationsConfirmedButNotAssigned(anyInt())).thenReturn(singletonList(RESERVATION_ID)); when(ticketRepository.flagTicketAsReminderSent(ticketId)).thenReturn(1); when(ticketRepository.findByUUID(anyString())).thenReturn(ticket); trm.sendReminderForOptionalData(); verify(notificationManager, times(1)).sendSimpleEmail(eq(event), anyString(), anyString(), any(TextTemplateGenerator.class)); } @Test public void doNotSendReminderIfPreviousNotifications() throws Exception { initReminder(); when(event.getId()).thenReturn(EVENT_ID); when(configurationManager.getIntConfigValue(eq(Configuration.from(event.getOrganizationId(), event.getId(), ASSIGNMENT_REMINDER_START)), anyInt())).thenReturn(10); when(configurationManager.getStringConfigValue(any())).thenReturn(Optional.empty()); when(ticketReservation.latestNotificationTimestamp(any())).thenReturn(Optional.of(ZonedDateTime.now().minusDays(10))); String RESERVATION_ID = "abcd"; when(ticketReservation.getId()).thenReturn(RESERVATION_ID); when(ticket.getTicketsReservationId()).thenReturn(RESERVATION_ID); int ticketId = 2; when(ticket.getId()).thenReturn(ticketId); when(ticketRepository.findAllAssignedButNotYetNotified(EVENT_ID)).thenReturn(singletonList(ticket)); when(ticketReservationRepository.findReservationById(eq(RESERVATION_ID))).thenReturn(ticketReservation); when(eventRepository.findByReservationId(RESERVATION_ID)).thenReturn(event); when(event.getZoneId()).thenReturn(ZoneId.systemDefault()); when(event.getBegin()).thenReturn(ZonedDateTime.now().plusDays(1)); when(eventRepository.findAll()).thenReturn(singletonList(event)); when(ticketRepository.flagTicketAsReminderSent(ticketId)).thenReturn(1); trm.sendReminderForOptionalData(); verify(notificationManager, never()).sendSimpleEmail(eq(event), anyString(), anyString(), any(TextTemplateGenerator.class)); } @Test public void doNotSendReminderIfTicketHasAlreadyBeenModified() throws Exception { initReminder(); when(event.getId()).thenReturn(EVENT_ID); when(configurationManager.getIntConfigValue(eq(Configuration.from(event.getOrganizationId(), event.getId(), ASSIGNMENT_REMINDER_START)), anyInt())).thenReturn(10); when(configurationManager.getStringConfigValue(any())).thenReturn(Optional.empty()); when(ticketReservation.latestNotificationTimestamp(any())).thenReturn(Optional.empty()); String RESERVATION_ID = "abcd"; when(ticketReservation.getId()).thenReturn(RESERVATION_ID); when(ticket.getTicketsReservationId()).thenReturn(RESERVATION_ID); int ticketId = 2; when(ticket.getId()).thenReturn(ticketId); when(ticketRepository.findAllAssignedButNotYetNotified(EVENT_ID)).thenReturn(singletonList(ticket)); when(ticketReservationRepository.findReservationById(eq(RESERVATION_ID))).thenReturn(ticketReservation); when(eventRepository.findByReservationId(RESERVATION_ID)).thenReturn(event); when(event.getZoneId()).thenReturn(ZoneId.systemDefault()); when(event.getBegin()).thenReturn(ZonedDateTime.now().plusDays(1)); when(eventRepository.findAll()).thenReturn(singletonList(event)); when(ticketRepository.flagTicketAsReminderSent(ticketId)).thenReturn(0); trm.sendReminderForOptionalData(); verify(notificationManager, never()).sendSimpleEmail(eq(event), anyString(), anyString(), any(TextTemplateGenerator.class)); } }