From 91eb2ccabf45c3b535a4fb44fea5e1518bd2a73e Mon Sep 17 00:00:00 2001 From: Dimi Date: Sat, 23 Nov 2024 15:40:25 +0100 Subject: [PATCH 1/4] Supporting External Reference in List of Attendees Download Closes #1422 --- .../api/admin/EventApiController.java | 4 +- .../EventApiControllerIntegrationTest.java | 57 +++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/src/main/java/alfio/controller/api/admin/EventApiController.java b/src/main/java/alfio/controller/api/admin/EventApiController.java index ef511fc7c..c02654473 100644 --- a/src/main/java/alfio/controller/api/admin/EventApiController.java +++ b/src/main/java/alfio/controller/api/admin/EventApiController.java @@ -356,7 +356,8 @@ public ResponseEntity rearrangeCategories(@PathVariable String eventName } private static final String PAYMENT_METHOD = "Payment Method"; - private static final List FIXED_FIELDS = Arrays.asList("ID", "Category", "Event", "Status", "OriginalPrice", "PaidPrice", "Discount", "VAT", "ReservationID", "Full Name", "First Name", "Last Name", "E-Mail", "Locked", "Language", "Confirmation", "Billing Address", "Country Code", "Payment ID", PAYMENT_METHOD); + private static final String EXTERNAL_REFERENCE = "External Reference"; + private static final List FIXED_FIELDS = Arrays.asList("ID", "Category", "Event", "Status", "OriginalPrice", "PaidPrice", "Discount", "VAT", "ReservationID", "Full Name", "First Name", "Last Name", "E-Mail", "Locked", "Language", "Confirmation", "Billing Address", "Country Code", "Payment ID", PAYMENT_METHOD, EXTERNAL_REFERENCE); private static final List> FIXED_PAIRS = FIXED_FIELDS.stream().map(f -> SerializablePair.of(f, f)).collect(toList()); private static final String FISCAL_CODE = "Fiscal Code"; private static final String REFERENCE_TYPE = "Reference Type"; @@ -435,6 +436,7 @@ private Stream exportLines(String eventName, Principal principal, List if(paymentIdRequested) { line.add(Objects.toString(transaction.map(Transaction::getPaymentId).orElse(null), transaction.map(Transaction::getTransactionId).orElse(""))); } if(paymentGatewayRequested) { line.add(transaction.map(tr -> tr.getPaymentProxy().name()).orElse("")); } } + if(fields.contains(EXTERNAL_REFERENCE)) {line.add(t.getExtReference());} if(eInvoicingEnabled) { var billingDetails = trs.getBillingDetails(); diff --git a/src/test/java/alfio/controller/api/admin/EventApiControllerIntegrationTest.java b/src/test/java/alfio/controller/api/admin/EventApiControllerIntegrationTest.java index f3af5b0ee..7fb7617a9 100644 --- a/src/test/java/alfio/controller/api/admin/EventApiControllerIntegrationTest.java +++ b/src/test/java/alfio/controller/api/admin/EventApiControllerIntegrationTest.java @@ -20,19 +20,24 @@ import alfio.config.DataSourceConfiguration; import alfio.config.Initializer; import alfio.controller.api.ControllerConfiguration; +import alfio.manager.AdminReservationRequestManager; import alfio.manager.EventManager; import alfio.manager.user.UserManager; import alfio.model.Event; import alfio.model.TicketCategory; import alfio.model.metadata.AlfioMetadata; +import alfio.model.modification.AdminReservationModification; import alfio.model.modification.DateTimeModification; import alfio.model.modification.TicketCategoryModification; import alfio.repository.EventDeleterRepository; import alfio.repository.EventRepository; +import alfio.repository.TicketCategoryRepository; +import alfio.repository.TicketRepository; import alfio.repository.system.ConfigurationRepository; import alfio.repository.user.OrganizationRepository; import alfio.test.util.AlfioIntegrationTest; import alfio.test.util.IntegrationTestUtil; +import alfio.util.ClockProvider; import org.apache.commons.lang3.tuple.Pair; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; @@ -46,10 +51,14 @@ import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalTime; +import java.time.ZonedDateTime; +import java.util.Collections; import java.util.List; +import java.util.stream.IntStream; import static alfio.test.util.IntegrationTestUtil.*; import static alfio.test.util.TestUtil.clockProvider; +import static java.util.stream.Collectors.toList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.Mockito.when; @@ -73,6 +82,14 @@ class EventApiControllerIntegrationTest { private EventDeleterRepository eventDeleterRepository; @Autowired private EventApiController eventApiController; + @Autowired + private AttendeeBulkImportApiController attendeeBulkImportApiController; + @Autowired + private AdminReservationRequestManager adminReservationRequestManager; + @Autowired + private TicketCategoryRepository ticketCategoryRepository; + @Autowired + private TicketRepository ticketRepository; private Event event; @@ -119,6 +136,40 @@ void getAllEventsForExternalOnline() { assertEquals(event.getShortName(), events.get(0).getKey()); } + @Test + void testListAttendeesWithFieldsUploadAndSameFieldsAvailableOnDownload() { + // GIVEN - creation of event and registration of attendees + var eventAndUser = createEvent(Event.EventFormat.HYBRID); + event = eventAndUser.getKey(); + var principal = Mockito.mock(Authentication.class); + when(principal.getName()).thenReturn(owner(eventAndUser.getValue())); + var modification = getTestAdminReservationModification(); + var result = this.attendeeBulkImportApiController.createReservations(eventAndUser.getKey().getShortName(), modification, false, principal); + // GIVEN - invocation of async processing job + var requestStatus = this.attendeeBulkImportApiController.getRequestsStatus(eventAndUser.getKey().getShortName(), result.getData(), principal); + assertEquals(1, requestStatus.getData().getCountPending()); + // WHEN - processing of pending reservations completes + this.adminReservationRequestManager.processPendingReservations(); + // THEN + var tickets = this.ticketRepository.findAllConfirmedForCSV(event.getId()); + assertEquals(1, tickets.size()); + var foundTicket = tickets.get(0).getTicket(); + assertEquals("123", foundTicket.getExtReference()); + assertEquals("Attendee 0", foundTicket.getFirstName()); + assertEquals("Test0", foundTicket.getLastName()); + assertEquals("attendee0@test.ch", foundTicket.getEmail()); + assertEquals("en", foundTicket.getUserLanguage()); + } + + private AdminReservationModification getTestAdminReservationModification() { + DateTimeModification expiration = DateTimeModification.fromZonedDateTime(ZonedDateTime.now(ClockProvider.clock()).plusDays(1)); + AdminReservationModification.CustomerData customerData = new AdminReservationModification.CustomerData("Integration", "Test", "integration-test@test.ch", "Billing Address", "reference", "en", "1234", "CH", null); + var ticketCategoryList = this.ticketCategoryRepository.findAllTicketCategories(event.getId()); + AdminReservationModification.Category category = new AdminReservationModification.Category(ticketCategoryList.get(0).getId(), "name", new BigDecimal("100.00"), null); + List ticketsInfoList = Collections.singletonList(new AdminReservationModification.TicketsInfo(category, generateAttendees(1), true, false)); + return new AdminReservationModification(expiration, customerData, ticketsInfoList, "en", false, false, null, null, null, null); + } + private Pair createEvent(Event.EventFormat format) { IntegrationTestUtil.ensureMinimalConfiguration(configurationRepository); List categories = List.of( @@ -131,6 +182,12 @@ private Pair createEvent(Event.EventFormat format) { } + private List generateAttendees(int count) { + return IntStream.range(0, count) + .mapToObj(i -> new AdminReservationModification.Attendee(null, "Attendee "+i, "Test" + i, "attendee"+i+"@test.ch", "en",false, "123", null, Collections.emptyMap(), null)) + .collect(toList()); + } + @AfterEach void tearDown() { eventDeleterRepository.deleteAllForEvent(event.getId()); From 1232287344a1d22ddf733ea29d9675e0b35ee127 Mon Sep 17 00:00:00 2001 From: Dimi Date: Sat, 23 Nov 2024 20:50:28 +0100 Subject: [PATCH 2/4] Adding testing of CSV order of fields download Closes #1422 --- .../controller/api/admin/EventApiController.java | 2 +- .../admin/EventApiControllerIntegrationTest.java | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/java/alfio/controller/api/admin/EventApiController.java b/src/main/java/alfio/controller/api/admin/EventApiController.java index c02654473..536f19fae 100644 --- a/src/main/java/alfio/controller/api/admin/EventApiController.java +++ b/src/main/java/alfio/controller/api/admin/EventApiController.java @@ -357,7 +357,7 @@ public ResponseEntity rearrangeCategories(@PathVariable String eventName private static final String PAYMENT_METHOD = "Payment Method"; private static final String EXTERNAL_REFERENCE = "External Reference"; - private static final List FIXED_FIELDS = Arrays.asList("ID", "Category", "Event", "Status", "OriginalPrice", "PaidPrice", "Discount", "VAT", "ReservationID", "Full Name", "First Name", "Last Name", "E-Mail", "Locked", "Language", "Confirmation", "Billing Address", "Country Code", "Payment ID", PAYMENT_METHOD, EXTERNAL_REFERENCE); + static final List FIXED_FIELDS = Arrays.asList("ID", "Category", "Event", "Status", "OriginalPrice", "PaidPrice", "Discount", "VAT", "ReservationID", "Full Name", "First Name", "Last Name", "E-Mail", "Locked", "Language", "Confirmation", "Billing Address", "Country Code", "Payment ID", PAYMENT_METHOD, EXTERNAL_REFERENCE); private static final List> FIXED_PAIRS = FIXED_FIELDS.stream().map(f -> SerializablePair.of(f, f)).collect(toList()); private static final String FISCAL_CODE = "Fiscal Code"; private static final String REFERENCE_TYPE = "Reference Type"; diff --git a/src/test/java/alfio/controller/api/admin/EventApiControllerIntegrationTest.java b/src/test/java/alfio/controller/api/admin/EventApiControllerIntegrationTest.java index 7fb7617a9..ea13a80f5 100644 --- a/src/test/java/alfio/controller/api/admin/EventApiControllerIntegrationTest.java +++ b/src/test/java/alfio/controller/api/admin/EventApiControllerIntegrationTest.java @@ -44,10 +44,12 @@ import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.security.core.Authentication; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; +import java.io.IOException; import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalTime; @@ -61,6 +63,7 @@ import static java.util.stream.Collectors.toList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.when; @AlfioIntegrationTest @@ -137,7 +140,7 @@ void getAllEventsForExternalOnline() { } @Test - void testListAttendeesWithFieldsUploadAndSameFieldsAvailableOnDownload() { + void testGivenListOfAttendeesWithFieldsUploadThenSameFieldsAvailableOnDownload() throws IOException { // GIVEN - creation of event and registration of attendees var eventAndUser = createEvent(Event.EventFormat.HYBRID); event = eventAndUser.getKey(); @@ -150,7 +153,7 @@ void testListAttendeesWithFieldsUploadAndSameFieldsAvailableOnDownload() { assertEquals(1, requestStatus.getData().getCountPending()); // WHEN - processing of pending reservations completes this.adminReservationRequestManager.processPendingReservations(); - // THEN + // THEN - assert correctness of data persisted var tickets = this.ticketRepository.findAllConfirmedForCSV(event.getId()); assertEquals(1, tickets.size()); var foundTicket = tickets.get(0).getTicket(); @@ -159,6 +162,14 @@ void testListAttendeesWithFieldsUploadAndSameFieldsAvailableOnDownload() { assertEquals("Test0", foundTicket.getLastName()); assertEquals("attendee0@test.ch", foundTicket.getEmail()); assertEquals("en", foundTicket.getUserLanguage()); + // THEN - assert correct order of CSV fields upon download + MockHttpServletRequest mockRequest = new MockHttpServletRequest(); + mockRequest.addParameter("fields", EventApiController.FIXED_FIELDS.toArray(new String[0])); + MockHttpServletResponse mockResponse = new MockHttpServletResponse(); + this.eventApiController.downloadAllTicketsCSV(event.getShortName(), "csv", mockRequest, mockResponse, principal); + String expected = "\""+foundTicket.getUuid()+"\""+",default,"+"\""+event.getShortName()+"\""+",ACQUIRED,0,0,0,0,"+"\""+foundTicket.getTicketsReservationId()+"\""+",\"Attendee 0 Test0\",\"Attendee 0\",Test0,attendee0@test.ch,false,en"; + assertTrue(String.join(",",mockResponse.getContentAsString().trim()).contains(expected)); + assertTrue(String.join(",",mockResponse.getContentAsString().trim()).endsWith("\"Billing Address\",,,,123")); } private AdminReservationModification getTestAdminReservationModification() { From 3487acf7ea77e1fb69bb4aa2ab5502f37fb5fce4 Mon Sep 17 00:00:00 2001 From: Dimi Date: Sun, 24 Nov 2024 12:39:13 +0100 Subject: [PATCH 3/4] Refactoring test and improve sort order assertion Closes #1422 --- .../EventApiControllerIntegrationTest.java | 54 +++++++++++++------ 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/src/test/java/alfio/controller/api/admin/EventApiControllerIntegrationTest.java b/src/test/java/alfio/controller/api/admin/EventApiControllerIntegrationTest.java index ea13a80f5..efbde1bd1 100644 --- a/src/test/java/alfio/controller/api/admin/EventApiControllerIntegrationTest.java +++ b/src/test/java/alfio/controller/api/admin/EventApiControllerIntegrationTest.java @@ -56,11 +56,10 @@ import java.time.ZonedDateTime; import java.util.Collections; import java.util.List; -import java.util.stream.IntStream; +import static alfio.controller.api.admin.EventApiController.FIXED_FIELDS; import static alfio.test.util.IntegrationTestUtil.*; import static alfio.test.util.TestUtil.clockProvider; -import static java.util.stream.Collectors.toList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -95,6 +94,11 @@ class EventApiControllerIntegrationTest { private TicketRepository ticketRepository; private Event event; + private static final String TEST_ATTENDEE_EXTERNAL_REFERENCE = "123"; + private static final String TEST_ATTENDEE_USER_LANGUAGE = "en"; + private static final String TEST_ATTENDEE_FIRST_NAME = "Attendee"; + private static final String TEST_ATTENDEE_LAST_NAME = "Test"; + private static final String TEST_ATTENDEE_EMAIL = "attendee@test.com"; @Test void getAllEventsForExternalInPerson() { @@ -140,7 +144,7 @@ void getAllEventsForExternalOnline() { } @Test - void testGivenListOfAttendeesWithFieldsUploadThenSameFieldsAvailableOnDownload() throws IOException { + void testGivenListOfAttendeesWithFieldsUploadThenSameFieldsAvailableOnCsvDownload() throws IOException { // GIVEN - creation of event and registration of attendees var eventAndUser = createEvent(Event.EventFormat.HYBRID); event = eventAndUser.getKey(); @@ -148,28 +152,33 @@ void testGivenListOfAttendeesWithFieldsUploadThenSameFieldsAvailableOnDownload() when(principal.getName()).thenReturn(owner(eventAndUser.getValue())); var modification = getTestAdminReservationModification(); var result = this.attendeeBulkImportApiController.createReservations(eventAndUser.getKey().getShortName(), modification, false, principal); + // GIVEN - invocation of async processing job var requestStatus = this.attendeeBulkImportApiController.getRequestsStatus(eventAndUser.getKey().getShortName(), result.getData(), principal); assertEquals(1, requestStatus.getData().getCountPending()); + // WHEN - processing of pending reservations completes this.adminReservationRequestManager.processPendingReservations(); + // THEN - assert correctness of data persisted var tickets = this.ticketRepository.findAllConfirmedForCSV(event.getId()); assertEquals(1, tickets.size()); var foundTicket = tickets.get(0).getTicket(); - assertEquals("123", foundTicket.getExtReference()); - assertEquals("Attendee 0", foundTicket.getFirstName()); - assertEquals("Test0", foundTicket.getLastName()); - assertEquals("attendee0@test.ch", foundTicket.getEmail()); - assertEquals("en", foundTicket.getUserLanguage()); + assertEquals(TEST_ATTENDEE_EXTERNAL_REFERENCE, foundTicket.getExtReference()); + assertEquals(TEST_ATTENDEE_FIRST_NAME, foundTicket.getFirstName()); + assertEquals(TEST_ATTENDEE_LAST_NAME, foundTicket.getLastName()); + assertEquals(TEST_ATTENDEE_EMAIL, foundTicket.getEmail()); + assertEquals(TEST_ATTENDEE_USER_LANGUAGE, foundTicket.getUserLanguage()); + // THEN - assert correct order of CSV fields upon download MockHttpServletRequest mockRequest = new MockHttpServletRequest(); - mockRequest.addParameter("fields", EventApiController.FIXED_FIELDS.toArray(new String[0])); + mockRequest.addParameter("fields", FIXED_FIELDS.toArray(new String[0])); MockHttpServletResponse mockResponse = new MockHttpServletResponse(); this.eventApiController.downloadAllTicketsCSV(event.getShortName(), "csv", mockRequest, mockResponse, principal); - String expected = "\""+foundTicket.getUuid()+"\""+",default,"+"\""+event.getShortName()+"\""+",ACQUIRED,0,0,0,0,"+"\""+foundTicket.getTicketsReservationId()+"\""+",\"Attendee 0 Test0\",\"Attendee 0\",Test0,attendee0@test.ch,false,en"; - assertTrue(String.join(",",mockResponse.getContentAsString().trim()).contains(expected)); - assertTrue(String.join(",",mockResponse.getContentAsString().trim()).endsWith("\"Billing Address\",,,,123")); + String expectedTestAttendeeCsvLine = "\""+foundTicket.getUuid()+"\""+",default,"+"\""+event.getShortName()+"\""+",ACQUIRED,0,0,0,0,"+"\""+foundTicket.getTicketsReservationId()+"\""+",\""+TEST_ATTENDEE_FIRST_NAME+" "+TEST_ATTENDEE_LAST_NAME+"\","+TEST_ATTENDEE_FIRST_NAME+","+TEST_ATTENDEE_LAST_NAME+","+TEST_ATTENDEE_EMAIL+",false,"+TEST_ATTENDEE_USER_LANGUAGE; + String returnedCsvContent = mockResponse.getContentAsString().trim().replace("\uFEFF", ""); // remove BOM + assertTrue(String.join(",",returnedCsvContent).startsWith(getExpectedHeaderCsvLine() + "\n" + expectedTestAttendeeCsvLine)); + assertTrue(String.join(",",returnedCsvContent).endsWith("\"Billing Address\",,,," + TEST_ATTENDEE_EXTERNAL_REFERENCE)); } private AdminReservationModification getTestAdminReservationModification() { @@ -177,7 +186,7 @@ private AdminReservationModification getTestAdminReservationModification() { AdminReservationModification.CustomerData customerData = new AdminReservationModification.CustomerData("Integration", "Test", "integration-test@test.ch", "Billing Address", "reference", "en", "1234", "CH", null); var ticketCategoryList = this.ticketCategoryRepository.findAllTicketCategories(event.getId()); AdminReservationModification.Category category = new AdminReservationModification.Category(ticketCategoryList.get(0).getId(), "name", new BigDecimal("100.00"), null); - List ticketsInfoList = Collections.singletonList(new AdminReservationModification.TicketsInfo(category, generateAttendees(1), true, false)); + List ticketsInfoList = Collections.singletonList(new AdminReservationModification.TicketsInfo(category, Collections.singletonList(generateTestAttendee()), true, false)); return new AdminReservationModification(expiration, customerData, ticketsInfoList, "en", false, false, null, null, null, null); } @@ -193,10 +202,21 @@ private Pair createEvent(Event.EventFormat format) { } - private List generateAttendees(int count) { - return IntStream.range(0, count) - .mapToObj(i -> new AdminReservationModification.Attendee(null, "Attendee "+i, "Test" + i, "attendee"+i+"@test.ch", "en",false, "123", null, Collections.emptyMap(), null)) - .collect(toList()); + private String getExpectedHeaderCsvLine() { + String expectedHeaderCsvLine = String.join(",", FIXED_FIELDS); + expectedHeaderCsvLine = expectedHeaderCsvLine.replaceAll("Full Name", "\"Full Name\""); + expectedHeaderCsvLine = expectedHeaderCsvLine.replaceAll("First Name", "\"First Name\""); + expectedHeaderCsvLine = expectedHeaderCsvLine.replaceAll("Last Name", "\"Last Name\""); + expectedHeaderCsvLine = expectedHeaderCsvLine.replaceAll("Billing Address", "\"Billing Address\""); + expectedHeaderCsvLine = expectedHeaderCsvLine.replaceAll("Country Code", "\"Country Code\""); + expectedHeaderCsvLine = expectedHeaderCsvLine.replaceAll("Payment ID", "\"Payment ID\""); + expectedHeaderCsvLine = expectedHeaderCsvLine.replaceAll("Payment Method", "\"Payment Method\""); + expectedHeaderCsvLine = expectedHeaderCsvLine.replaceAll("External Reference", "\"External Reference\""); + return expectedHeaderCsvLine; + } + + private AdminReservationModification.Attendee generateTestAttendee() { + return new AdminReservationModification.Attendee(null, TEST_ATTENDEE_FIRST_NAME, TEST_ATTENDEE_LAST_NAME, TEST_ATTENDEE_EMAIL, TEST_ATTENDEE_USER_LANGUAGE,false, TEST_ATTENDEE_EXTERNAL_REFERENCE, null, Collections.emptyMap(), null); } @AfterEach From 85a5c1fbe3abd68d6abd904ae1a323e045ea5787 Mon Sep 17 00:00:00 2001 From: Dimi Date: Sun, 24 Nov 2024 13:00:46 +0100 Subject: [PATCH 4/4] Removing unnecessary string join Closes #1422 --- .../api/admin/EventApiControllerIntegrationTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/alfio/controller/api/admin/EventApiControllerIntegrationTest.java b/src/test/java/alfio/controller/api/admin/EventApiControllerIntegrationTest.java index efbde1bd1..733b7c867 100644 --- a/src/test/java/alfio/controller/api/admin/EventApiControllerIntegrationTest.java +++ b/src/test/java/alfio/controller/api/admin/EventApiControllerIntegrationTest.java @@ -177,8 +177,8 @@ void testGivenListOfAttendeesWithFieldsUploadThenSameFieldsAvailableOnCsvDownloa this.eventApiController.downloadAllTicketsCSV(event.getShortName(), "csv", mockRequest, mockResponse, principal); String expectedTestAttendeeCsvLine = "\""+foundTicket.getUuid()+"\""+",default,"+"\""+event.getShortName()+"\""+",ACQUIRED,0,0,0,0,"+"\""+foundTicket.getTicketsReservationId()+"\""+",\""+TEST_ATTENDEE_FIRST_NAME+" "+TEST_ATTENDEE_LAST_NAME+"\","+TEST_ATTENDEE_FIRST_NAME+","+TEST_ATTENDEE_LAST_NAME+","+TEST_ATTENDEE_EMAIL+",false,"+TEST_ATTENDEE_USER_LANGUAGE; String returnedCsvContent = mockResponse.getContentAsString().trim().replace("\uFEFF", ""); // remove BOM - assertTrue(String.join(",",returnedCsvContent).startsWith(getExpectedHeaderCsvLine() + "\n" + expectedTestAttendeeCsvLine)); - assertTrue(String.join(",",returnedCsvContent).endsWith("\"Billing Address\",,,," + TEST_ATTENDEE_EXTERNAL_REFERENCE)); + assertTrue(returnedCsvContent.startsWith(getExpectedHeaderCsvLine() + "\n" + expectedTestAttendeeCsvLine)); + assertTrue(returnedCsvContent.endsWith("\"Billing Address\",,,," + TEST_ATTENDEE_EXTERNAL_REFERENCE)); } private AdminReservationModification getTestAdminReservationModification() {