Java tutorial
/** * Copyright 2015-2017 Robin Steel * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package squash.booking.lambdas.core; import static org.hamcrest.Matchers.startsWith; import static org.junit.Assert.assertTrue; import squash.booking.lambdas.core.ILifecycleManager.LifecycleState; import squash.deployment.lambdas.utils.IS3TransferManager; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.tuple.ImmutablePair; import org.jmock.Expectations; import org.jmock.Mockery; import org.jmock.Sequence; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import com.amazonaws.AmazonServiceException; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.LambdaLogger; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.model.CopyObjectRequest; import com.amazonaws.services.s3.model.DeleteObjectRequest; import com.amazonaws.services.s3.model.PutObjectRequest; import com.amazonaws.services.s3.transfer.Transfer; import com.amazonaws.services.sns.AmazonSNS; import com.google.common.io.CharStreams; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.URL; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; import java.util.Optional; /** * Tests the {@link PageManager}. * * @author robinsteel19@outlook.com (Robin Steel) */ public class PageManagerTest { // Variables for setting up subclass of class under test LocalDate fakeCurrentDate; String fakeCurrentDateString; List<String> validDates; squash.booking.lambdas.core.PageManagerTest.TestPageManager pageManager; Optional<ImmutablePair<LifecycleState, Optional<String>>> activeLifecycleState; String adminSnsTopicArn; String apiGatewayBaseUrl; String websiteBucketName; // Mocks Mockery mockery = new Mockery(); Context mockContext; LambdaLogger mockLogger; IS3TransferManager mockTransferManager; IBookingManager mockBookingManager; ILifecycleManager mockLifecycleManager; AmazonS3 mockS3Client; AmazonSNS mockSNSClient; Integer court; Integer courtSpan; Integer slot; Integer slotSpan; String name; Booking booking; List<Booking> bookings; String revvingSuffix; @Rule public ExpectedException thrown = ExpectedException.none(); @Before public void beforeTest() throws Exception { fakeCurrentDate = LocalDate.of(2015, 10, 6); fakeCurrentDateString = fakeCurrentDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); validDates = new ArrayList<>(); validDates.add(fakeCurrentDateString); validDates.add(fakeCurrentDate.plusDays(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); pageManager = new squash.booking.lambdas.core.PageManagerTest.TestPageManager(); pageManager.setCurrentLocalDate(fakeCurrentDate); activeLifecycleState = Optional .of(new ImmutablePair<LifecycleState, Optional<String>>(LifecycleState.ACTIVE, Optional.empty())); mockery = new Mockery(); // Set up mock context mockContext = mockery.mock(Context.class); mockery.checking(new Expectations() { { ignoring(mockContext); } }); // Set up mock logger mockLogger = mockery.mock(LambdaLogger.class); mockery.checking(new Expectations() { { ignoring(mockLogger); } }); // Set up mock booking manager mockBookingManager = mockery.mock(IBookingManager.class); mockery.checking(new Expectations() { { ignoring(mockBookingManager); } }); // Set up mock lifecycle manager mockLifecycleManager = mockery.mock(ILifecycleManager.class); mockery.checking(new Expectations() { { // Some page manager methods query for lifecycle state - return ACTIVE // state by default. allowing(mockLifecycleManager).getLifecycleState(); will(returnValue(activeLifecycleState.get())); ignoring(mockLifecycleManager); } }); websiteBucketName = "websiteBucketName"; pageManager.setS3WebsiteBucketName(websiteBucketName); // Use a single booking for most of the tests court = 5; courtSpan = 1; slot = 12; slotSpan = 1; name = "D.Playerd/F.Playerf"; booking = new Booking(court, courtSpan, slot, slotSpan, name); bookings = new ArrayList<>(); bookings.add(booking); revvingSuffix = "revvingSuffix"; apiGatewayBaseUrl = "apiGatewayBaseUrl"; adminSnsTopicArn = "adminSnsTopicArn"; pageManager.setAdminSnsTopicArn(adminSnsTopicArn); } private void initialisePageManager() throws Exception { // Call this to initialise the page manager in tests where this // initialisation is not the subject of the test. pageManager.initialise(mockBookingManager, mockLifecycleManager, mockLogger); } @After public void afterTest() { mockery.assertIsSatisfied(); } // Define a test page manager with some overrides to facilitate testing public class TestPageManager extends PageManager { private AmazonSNS snsClient; private String adminSnsTopicArn; private LocalDate currentLocalDate; private String websiteBucket; private IS3TransferManager transferManager; public void setS3TransferManager(IS3TransferManager transferManager) { this.transferManager = transferManager; } @Override public IS3TransferManager getS3TransferManager() { return transferManager; } public void setS3WebsiteBucketName(String websiteBucketName) { websiteBucket = websiteBucketName; } public void setSNSClient(AmazonSNS snsClient) { this.snsClient = snsClient; } @Override public AmazonSNS getSNSClient() { return snsClient; } public void setAdminSnsTopicArn(String adminSnsTopicArn) { this.adminSnsTopicArn = adminSnsTopicArn; } @Override public String getEnvironmentVariable(String variableName) { if (variableName.equals("AdminSNSTopicArn")) { return adminSnsTopicArn; } if (variableName.equals("WebsiteBucket")) { return websiteBucket; } if (variableName.equals("AWS_REGION")) { return "eu-west-1"; } return null; } public void setCurrentLocalDate(LocalDate localDate) { currentLocalDate = localDate; } @Override public LocalDate getCurrentLocalDate() { return currentLocalDate; } } @Test public void testRefreshPageThrowsWhenPageManagerUninitialised() throws Exception { // ARRANGE thrown.expect(Exception.class); thrown.expectMessage("The page manager has not been initialised"); // ACT // Do not initialise the page manager first - so we should throw pageManager.refreshPage(fakeCurrentDateString, validDates, apiGatewayBaseUrl, true, bookings, revvingSuffix); } @Test public void testUploadFamousPlayersThrowsWhenPageManagerUninitialised() throws Exception { // ARRANGE thrown.expect(Exception.class); thrown.expectMessage("The page manager has not been initialised"); // ACT // Do not initialise the page manager first - so we should throw pageManager.uploadFamousPlayers(); } @Test public void testRefreshPageCallsTheLifecycleManagerCorrectly() throws Exception { // refreshPage should query the lifecycle manager for the lifecycle // state. // ARRANGE // Expect method to query the lifecycle manager for the lifecycle // state. N.B. Name mock as it's replacing the default lifecycleManager // mock mockLifecycleManager = mockery.mock(ILifecycleManager.class, "replacementLifecycleManagerMock"); mockery.checking(new Expectations() { { // One call for creating the page, and one for creating the json data. exactly(2).of(mockLifecycleManager).getLifecycleState(); will(returnValue(activeLifecycleState.get())); } }); initialisePageManager(); // Set up S3 expectations - these are incidental to current test: Transfer mockTransfer = mockery.mock(Transfer.class); mockery.checking(new Expectations() { { allowing(mockTransfer).isDone(); will(returnValue(true)); allowing(mockTransfer).waitForCompletion(); } }); mockTransferManager = mockery.mock(IS3TransferManager.class); // Just check S3 methods called correct number of times - don't bother // checking argument details. mockery.checking(new Expectations() { { // We have one upload for the page and one for the cached data allowing(mockTransferManager).upload(with(any(PutObjectRequest.class))); will(returnValue(mockTransfer)); } }); pageManager.setS3TransferManager(mockTransferManager); // ACT pageManager.refreshPage(fakeCurrentDateString, validDates, apiGatewayBaseUrl, false, bookings, revvingSuffix); } @Test public void testRefreshPageWithDuplicateCorrectlyCallsS3() throws Exception { // Refresh page when Cloudformation creates the stack should just // upload pages to S3 but not duplicate them. Duplicating is done as // bookings are later mutated - and is a workaround to ensure // ReadAfterWrite consistency. This tests that this duplication // does happen when we ask for it. initialisePageManager(); // Set up S3 expectations for copy: // Transfer interface is implemented by Uploads, Downloads, and Copies Transfer mockTransfer = mockery.mock(Transfer.class); mockery.checking(new Expectations() { { allowing(mockTransfer).isDone(); will(returnValue(true)); allowing(mockTransfer).waitForCompletion(); } }); mockTransferManager = mockery.mock(IS3TransferManager.class); // Just check S3 methods called correct number of times - don't bother // checking argument details. mockery.checking(new Expectations() { { // We have one upload for the page and one for the cached data exactly(2).of(mockTransferManager).upload(with(any(PutObjectRequest.class))); will(returnValue(mockTransfer)); // We _do_ have the copy in this case - for the page, but not for the // cached data oneOf(mockTransferManager).copy(with(any(CopyObjectRequest.class))); will(returnValue(mockTransfer)); } }); pageManager.setS3TransferManager(mockTransferManager); // ACT pageManager.refreshPage(fakeCurrentDateString, validDates, apiGatewayBaseUrl, true, bookings, revvingSuffix); } @Test public void testRefreshPageWithoutDuplicateCorrectlyCallsS3() throws Exception { // Refresh page when Cloudformation creates the stack should just // upload pages to S3 but not duplicate them. Duplicating is done as // bookings are later mutated - and is a workaround to ensure // ReadAfterWrite consistency. This tests that this duplication // does not happen when we do not ask for it (i.e. on stack creation). initialisePageManager(); // Set up S3 expectations for no copy: // Transfer interface is implemented by Uploads, Downloads, and Copies Transfer mockTransfer = mockery.mock(Transfer.class); mockery.checking(new Expectations() { { allowing(mockTransfer).isDone(); will(returnValue(true)); allowing(mockTransfer).waitForCompletion(); } }); mockTransferManager = mockery.mock(IS3TransferManager.class); // Just check S3 methods called correct number of times - don't bother // checking argument details. mockery.checking(new Expectations() { { // We have one upload for the page and one for the cached data exactly(2).of(mockTransferManager).upload(with(any(PutObjectRequest.class))); will(returnValue(mockTransfer)); // We do _not_ have the copy in this case never(mockTransferManager).copy(with(anything())); } }); pageManager.setS3TransferManager(mockTransferManager); // ACT pageManager.refreshPage(fakeCurrentDateString, validDates, apiGatewayBaseUrl, false, bookings, revvingSuffix); } @Test public void testRefreshPageThrowsWhenS3Throws() throws Exception { // ARRANGE thrown.expect(Exception.class); thrown.expectMessage("Exception caught while copying booking page to S3"); initialisePageManager(); // Make S3 throw: // Transfer interface is implemented by Uploads, Downloads, and Copies Transfer mockTransfer = mockery.mock(Transfer.class); mockery.checking(new Expectations() { { allowing(mockTransfer).isDone(); will(returnValue(true)); allowing(mockTransfer).waitForCompletion(); } }); mockTransferManager = mockery.mock(IS3TransferManager.class); mockery.checking(new Expectations() { { oneOf(mockTransferManager).upload(with(any(PutObjectRequest.class))); will(throwException(new AmazonServiceException("Grrr..."))); // Should throw before copy is called never(mockTransferManager).copy(with(any(CopyObjectRequest.class))); } }); pageManager.setS3TransferManager(mockTransferManager); // ACT - this should throw pageManager.refreshPage(fakeCurrentDateString, validDates, apiGatewayBaseUrl, false, bookings, revvingSuffix); } @Test public void testRefreshAllPagesCorrectlyCallsS3() throws Exception { initialisePageManager(); // Set up S3 expectations for upload (without copy) for each valid date: // Transfer interface is implemented by Uploads, Downloads, and Copies Transfer mockTransfer = mockery.mock(Transfer.class); mockery.checking(new Expectations() { { allowing(mockTransfer).isDone(); will(returnValue(true)); allowing(mockTransfer).waitForCompletion(); } }); mockTransferManager = mockery.mock(IS3TransferManager.class); // Just check S3 methods called correct number of times - don't bother // checking argument details. final Sequence refreshSequence = mockery.sequence("refresh"); mockery.checking(new Expectations() { { // 2 uploads for each date + 3 uploads for the index pages + 1 upload // for the validdates json + 1 upload for the famous players json. exactly(2 * validDates.size() + 5).of(mockTransferManager) .upload(with(any(PutObjectRequest.class))); will(returnValue(mockTransfer)); inSequence(refreshSequence); // We do _not_ have the copy in this case never(mockTransferManager).copy(with(anything())); } }); // Delete previous day's bookings and cached data at end mockS3Client = mockery.mock(AmazonS3.class); mockery.checking(new Expectations() { { exactly(2).of(mockS3Client).deleteObject(with(aNonNull(DeleteObjectRequest.class))); // Ensures this delete occurs after uploads of new pages and cached data inSequence(refreshSequence); exactly(1).of(mockTransferManager).getAmazonS3Client(); will(returnValue(mockS3Client)); } }); pageManager.setS3TransferManager(mockTransferManager); // ACT pageManager.refreshAllPages(validDates, apiGatewayBaseUrl, revvingSuffix); } @Test public void testRefreshAllPagesThrowsWhenPageManagerUninitialised() throws Exception { // ARRANGE thrown.expect(Exception.class); thrown.expectMessage("The page manager has not been initialised"); // ACT // Do not initialise the page manager first - so we should throw pageManager.refreshAllPages(validDates, apiGatewayBaseUrl, revvingSuffix); } @Test public void testRefreshAllPagesThrowsWhenS3Throws() throws Exception { // ARRANGE thrown.expect(Exception.class); thrown.expectMessage("Exception caught while copying booking page to S3"); initialisePageManager(); // Make S3 throw: // Transfer interface is implemented by Uploads, Downloads, and Copies Transfer mockTransfer = mockery.mock(Transfer.class); mockery.checking(new Expectations() { { allowing(mockTransfer).isDone(); will(returnValue(true)); allowing(mockTransfer).waitForCompletion(); } }); mockTransferManager = mockery.mock(IS3TransferManager.class); mockery.checking(new Expectations() { { oneOf(mockTransferManager).upload(with(any(PutObjectRequest.class))); will(throwException(new AmazonServiceException("Grrr..."))); // Should throw before copy is called never(mockTransferManager).copy(with(any(CopyObjectRequest.class))); } }); pageManager.setS3TransferManager(mockTransferManager); mockSNSClient = mockery.mock(AmazonSNS.class); mockery.checking(new Expectations() { { ignoring(mockSNSClient); } }); pageManager.setSNSClient(mockSNSClient); // ACT - this should throw pageManager.refreshAllPages(validDates, apiGatewayBaseUrl, revvingSuffix); } @Test public void testRefreshAllPagesNotifiesTheSnsTopicWhenItThrows() throws Exception { // It is useful for the admin user to be notified whenever the refreshing // of booking pages does not succeed - so that they can update the pages // manually instead. This tests that whenever the page manager catches an // exception while refreshing pages, it notifies the admin SNS topic. // ARRANGE thrown.expect(Exception.class); String message = "Exception caught while copying booking page to S3"; thrown.expectMessage(message); initialisePageManager(); // Make S3 throw: // Transfer interface is implemented by Uploads, Downloads, and Copies Transfer mockTransfer = mockery.mock(Transfer.class); mockery.checking(new Expectations() { { allowing(mockTransfer).isDone(); will(returnValue(true)); allowing(mockTransfer).waitForCompletion(); } }); mockTransferManager = mockery.mock(IS3TransferManager.class); mockery.checking(new Expectations() { { oneOf(mockTransferManager).upload(with(any(PutObjectRequest.class))); will(throwException(new AmazonServiceException("Grrr..."))); // Should throw before copy is called never(mockTransferManager).copy(with(any(CopyObjectRequest.class))); } }); pageManager.setS3TransferManager(mockTransferManager); // Set up mock SNS client to expect a notification mockSNSClient = mockery.mock(AmazonSNS.class); String partialMessage = "Apologies - but there was an error refreshing the booking pages in S3"; mockery.checking(new Expectations() { { oneOf(mockSNSClient).publish(with(equal(adminSnsTopicArn)), with(startsWith(partialMessage)), with(equal("Sqawsh booking pages in S3 failed to refresh"))); } }); pageManager.setSNSClient(mockSNSClient); // ACT - this should throw - and notify the SNS topic pageManager.refreshAllPages(validDates, apiGatewayBaseUrl, revvingSuffix); } @Test public void testCreateTodayIndexPageReturnsCorrectPage() throws Exception { doTestCreateTodayIndexPageReturnsCorrectPage(true); } @Test public void testCreateNoscriptIndexPageReturnsCorrectPage() throws Exception { doTestCreateTodayIndexPageReturnsCorrectPage(false); } private void doTestCreateTodayIndexPageReturnsCorrectPage(Boolean showRedirectMessage) throws Exception { // We verify against a previously-saved regression file. // ARRANGE initialisePageManager(); String redirectionUrl = "http://squashwebsite42.s3-website-eu-west-1.amazonaws.com"; // Load in the expected page String expectedIndexPage; String indextype = showRedirectMessage ? "Today" : "Noscript"; try (InputStream stream = PageManagerTest.class.getResourceAsStream( "/squash/booking/lambdas/TestCreate" + indextype + "IndexPageReturnsCorrectPage.html")) { expectedIndexPage = CharStreams.toString(new InputStreamReader(stream, "UTF-8")); } // ACT String actualIndexPage = pageManager.createIndexPage(redirectionUrl, showRedirectMessage); // ASSERT boolean pageIsCorrect = actualIndexPage.equals(expectedIndexPage); if (!pageIsCorrect) { // Save the generated page only in the error case // Get path to resource so can save attempt alongside it URL regressionPage = PageManagerTest.class.getResource( "/squash/booking/lambdas/TestCreate" + indextype + "IndexPageReturnsCorrectPage.html"); String regressionPagePath = regressionPage.getPath(); String pathMinusFilename = FilenameUtils.getPath(regressionPagePath); File outputPage = new File("/" + pathMinusFilename, indextype + "PageFailingTestResult.html"); try (PrintStream out = new PrintStream(new FileOutputStream(outputPage))) { out.print(actualIndexPage); } } assertTrue("Created " + indextype + " index page is incorrect: Actual: " + actualIndexPage + " Expected: " + expectedIndexPage, pageIsCorrect); } @Test public void testCreateBookingPageReturnsCorrectPage_activeLifecycleState() throws Exception { // In active lifecycle state the normal booking page should be created, with // enabled buttons, no forwarding meta-header, and no 'under maintenance' // banner. String regressionPath = "/squash/booking/lambdas/TestCreateBookingPageReturnsCorrectPage_ActiveLifecycle.html"; String outputPath = "BookingPageFailingTestResult_ActiveLifecycle.html"; doTestCreateBookingPageReturnsCorrectPage(activeLifecycleState.get(), regressionPath, outputPath); } @Test public void testCreateBookingPageReturnsCorrectPage_readonlyLifecycleState() throws Exception { // In readonly lifecycle state a booking page should be created, with // disabled buttons, an 'under maintenance' banner, but no forwarding // meta-header. String regressionPath = "/squash/booking/lambdas/TestCreateBookingPageReturnsCorrectPage_ReadonlyLifecycle.html"; String outputPath = "BookingPageFailingTestResult_ReadonlyLifecycle.html"; doTestCreateBookingPageReturnsCorrectPage( new ImmutablePair<LifecycleState, Optional<String>>(LifecycleState.READONLY, Optional.empty()), regressionPath, outputPath); } @Test public void testCreateBookingPageReturnsCorrectPage_retiredLifecycleState() throws Exception { // In retired lifecycle state a booking page should be created, with a // forwarding meta-header, and a body containing only a redirection message // and link. String regressionPath = "/squash/booking/lambdas/TestCreateBookingPageReturnsCorrectPage_RetiredLifecycle.html"; String outputPath = "BookingPageFailingTestResult_RetiredLifecycle.html"; doTestCreateBookingPageReturnsCorrectPage(new ImmutablePair<LifecycleState, Optional<String>>( LifecycleState.RETIRED, Optional.of("someForwardingUrl")), regressionPath, outputPath); } private void doTestCreateBookingPageReturnsCorrectPage( ImmutablePair<LifecycleState, Optional<String>> lifecycleState, String regressionPath, String outputPath) throws Exception { // We create two single bookings, and 2 block bookings, and verify resulting // html directly against a previously-saved regression file. // ARRANGE // Expect method to query the lifecycle manager for the lifecycle // state. N.B. Name mock as it's replacing the default lifecycleManager // mock mockLifecycleManager = mockery.mock(ILifecycleManager.class, "replacementLifecycleManagerMock"); mockery.checking(new Expectations() { { oneOf(mockLifecycleManager).getLifecycleState(); will(returnValue(lifecycleState)); } }); initialisePageManager(); // Set some values that will get embedded into the booking page String s3WebsiteUrl = "http://squashwebsite.s3-website-eu-west-1.amazonaws.com"; String reservationFormGetUrl = "reserveUrl"; String cancellationFormGetUrl = "cancelUrl"; // Set up 2 bookings Booking booking1 = new Booking(); booking1.setSlot(3); booking1.setSlotSpan(1); booking1.setCourt(5); booking1.setCourtSpan(1); booking1.setName("A.Playera/B.Playerb"); Booking booking2 = new Booking(); booking2.setSlot(4); booking2.setSlotSpan(1); booking2.setCourt(3); booking2.setCourtSpan(1); booking2.setName("C.Playerc/D.Playerd"); Booking booking3 = new Booking(); booking3.setSlot(10); booking3.setSlotSpan(3); booking3.setCourt(2); booking3.setCourtSpan(2); booking3.setName("E.Playere/F.Playerf"); Booking booking4 = new Booking(); booking4.setSlot(13); booking4.setSlotSpan(4); booking4.setCourt(1); booking4.setCourtSpan(5); booking4.setName("Club Night"); List<Booking> bookingsForPage = new ArrayList<>(); bookingsForPage.add(booking1); bookingsForPage.add(booking2); bookingsForPage.add(booking3); bookingsForPage.add(booking4); // Load in the expected page String expectedBookingPage; try (InputStream stream = PageManagerTest.class.getResourceAsStream(regressionPath)) { expectedBookingPage = CharStreams.toString(new InputStreamReader(stream, "UTF-8")); } // ACT String actualBookingPage = pageManager.createBookingPage(fakeCurrentDateString, validDates, reservationFormGetUrl, cancellationFormGetUrl, s3WebsiteUrl, bookingsForPage, "DummyGuid", revvingSuffix); // ASSERT boolean pageIsCorrect = actualBookingPage.equals(expectedBookingPage); if (!pageIsCorrect) { // Save the generated page only in the error case // Get path to resource so can save attempt alongside it URL regressionPage = PageManagerTest.class.getResource(regressionPath); String regressionPagePath = regressionPage.getPath(); String pathMinusFilename = FilenameUtils.getPath(regressionPagePath); File outputPage = new File("/" + pathMinusFilename, outputPath); try (PrintStream out = new PrintStream(new FileOutputStream(outputPage))) { out.print(actualBookingPage); } } assertTrue("Created booking page is incorrect: " + actualBookingPage, pageIsCorrect); } @Test public void testCreateCachedBookingDataCreatesCorrectData_activeLifecycleState() throws Exception { // The data's lifecycleState property should have active state and empty // url. String regressionData = ",\"lifecycleState\":{\"state\":\"ACTIVE\",\"url\":\"\"}"; doTestCreateCachedBookingDataCreatesCorrectData(activeLifecycleState.get(), regressionData); } @Test public void testCreateCachedBookingDataCreatesCorrectData_readonlyLifecycleState() throws Exception { // The data's lifecycleState property should have readonly state and empty // url. String regressionData = ",\"lifecycleState\":{\"state\":\"READONLY\",\"url\":\"\"}"; doTestCreateCachedBookingDataCreatesCorrectData( new ImmutablePair<LifecycleState, Optional<String>>(LifecycleState.READONLY, Optional.empty()), regressionData); } @Test public void testCreateCachedBookingDataCreatesCorrectData_retiredLifecycleState() throws Exception { // The data's lifecycleState property should have retired state and // non-empty url. String regressionData = ",\"lifecycleState\":{\"state\":\"RETIRED\",\"url\":\"someForwardingUrl\"}"; doTestCreateCachedBookingDataCreatesCorrectData(new ImmutablePair<LifecycleState, Optional<String>>( LifecycleState.RETIRED, Optional.of("someForwardingUrl")), regressionData); } private void doTestCreateCachedBookingDataCreatesCorrectData( ImmutablePair<LifecycleState, Optional<String>> lifecycleState, String regressionData) throws Exception { // We create two single bookings, and 1 block booking, and verify resulting // json directly against regression data. // ARRANGE // Expect method to query the lifecycle manager for the lifecycle // state. N.B. Name mock as it's replacing the default lifecycleManager // mock mockLifecycleManager = mockery.mock(ILifecycleManager.class, "replacementLifecycleManagerMock"); mockery.checking(new Expectations() { { oneOf(mockLifecycleManager).getLifecycleState(); will(returnValue(lifecycleState)); } }); initialisePageManager(); // Set up 2 bookings Booking booking1 = new Booking(); booking1.setSlot(3); booking1.setCourt(5); booking1.setName("A.Playera/B.Playerb"); Booking booking2 = new Booking(); booking2.setSlot(4); booking2.setCourt(3); booking2.setName("C.Playerc/D.Playerd"); Booking booking3 = new Booking(); booking3.setSlot(10); booking3.setSlotSpan(3); booking3.setCourt(2); booking3.setCourtSpan(2); booking3.setName("E.Playere/F.Playerf"); List<Booking> bookingsForDate = new ArrayList<>(); bookingsForDate.add(booking1); bookingsForDate.add(booking2); bookingsForDate.add(booking3); // Set up the expected cached data String expectedCachedBookingData = "{\"date\":\"2015-10-06\",\"validdates\":[\"2015-10-06\",\"2015-10-07\"],\"bookings\":[{\"court\":5,\"courtSpan\":1,\"slot\":3,\"slotSpan\":1,\"name\":\"A.Playera/B.Playerb\"},{\"court\":3,\"courtSpan\":1,\"slot\":4,\"slotSpan\":1,\"name\":\"C.Playerc/D.Playerd\"},{\"court\":2,\"courtSpan\":2,\"slot\":10,\"slotSpan\":3,\"name\":\"E.Playere/F.Playerf\"}]" + regressionData + "}"; // ACT String actualCachedBookingData = pageManager.createCachedBookingData(fakeCurrentDateString, validDates, bookingsForDate); // ASSERT boolean dataIsCorrect = actualCachedBookingData.equals(expectedCachedBookingData); assertTrue("Created cached booking data is incorrect: " + actualCachedBookingData + " versus " + expectedCachedBookingData, dataIsCorrect); } @Test public void testCreateCachedBookingDataHasBookingsArrayWhenThereIsOneBooking() throws Exception { // Aim here is to check that a single booking is encoded as a 1-element // array rather than degenerating to a non-array object. // ARRANGE initialisePageManager(); // We create 1 bookings, and verify resulting json directly // against regression data. Booking booking = new Booking(); booking.setSlot(3); booking.setCourt(5); booking.setName("A.Playera/B.Playerb"); List<Booking> bookingsForDate = new ArrayList<>(); bookingsForDate.add(booking); // Set up the expected cached data String expectedCachedBookingData = "{\"date\":\"2015-10-06\",\"validdates\":[\"2015-10-06\",\"2015-10-07\"],\"bookings\":[{\"court\":5,\"courtSpan\":1,\"slot\":3,\"slotSpan\":1,\"name\":\"A.Playera/B.Playerb\"}],\"lifecycleState\":{\"state\":\"ACTIVE\",\"url\":\"\"}}"; // ACT String actualCachedBookingData = pageManager.createCachedBookingData(fakeCurrentDateString, validDates, bookingsForDate); // ASSERT boolean dataIsCorrect = actualCachedBookingData.equals(expectedCachedBookingData); assertTrue("Created cached booking data is incorrect: " + actualCachedBookingData + " versus " + expectedCachedBookingData, dataIsCorrect); } @Test public void testCreateCachedBookingDataHasEmptyBookingsArrayWhenThereAreNoBookings() throws Exception { // Aim here is to check that no bookings is encoded as an empty array // rather than being dropped altogether from the JSON. // We create no bookings, and verify resulting json directly // against regression data. // ARRANGE initialisePageManager(); // Create empty bookings array List<Booking> bookingsForDate = new ArrayList<>(); // Set up the expected cached data String expectedCachedBookingData = "{\"date\":\"2015-10-06\",\"validdates\":[\"2015-10-06\",\"2015-10-07\"],\"bookings\":[],\"lifecycleState\":{\"state\":\"ACTIVE\",\"url\":\"\"}}"; // ACT String actualCachedBookingData = pageManager.createCachedBookingData(fakeCurrentDateString, validDates, bookingsForDate); // ASSERT boolean dataIsCorrect = actualCachedBookingData.equals(expectedCachedBookingData); assertTrue("Created cached booking data is incorrect: " + actualCachedBookingData + " versus " + expectedCachedBookingData, dataIsCorrect); } @Test public void testCreateCachedValidDatesDataCreatesCorrectData() throws Exception { // ARRANGE initialisePageManager(); // Set up the expected cached data String expectedCachedValidDatesData = "{\"dates\":[\"2015-10-06\",\"2015-10-07\"]}"; // ACT String actualCachedValidDatesData = pageManager.createValidDatesData(validDates); // ASSERT boolean dataIsCorrect = actualCachedValidDatesData.equals(expectedCachedValidDatesData); assertTrue("Created cached valid dates data is incorrect: " + actualCachedValidDatesData + " versus " + expectedCachedValidDatesData, dataIsCorrect); } @Test public void testCreateCachedValidDatesDataWhenThereIsOneValidDate() throws Exception { // Aim here is to check that a single date is encoded as a 1-element // array rather than degenerating to a non-array object. // ARRANGE initialisePageManager(); // We create a single valid date, and verify resulting json directly // against regression data. validDates = new ArrayList<>(); validDates.add(fakeCurrentDateString); // Set up the expected cached data String expectedCachedValidDatesData = "{\"dates\":[\"2015-10-06\"]}"; // ACT String actualCachedValidDatesData = pageManager.createValidDatesData(validDates); // ASSERT boolean dataIsCorrect = actualCachedValidDatesData.equals(expectedCachedValidDatesData); assertTrue("Created cached valid dates data is incorrect: " + actualCachedValidDatesData + " versus " + expectedCachedValidDatesData, dataIsCorrect); } @Test public void testCreateCachedValidDatesDataWhenThereAreNoValidDates() throws Exception { // Aim here is to check that no valid dates is encoded as an empty array // rather than being dropped altogether from the JSON. // ARRANGE initialisePageManager(); // We create no valid dates, and verify resulting json directly // against regression data. validDates = new ArrayList<>(); // Set up the expected cached data String expectedCachedValidDatesData = "{\"dates\":[]}"; // ACT String actualCachedValidDatesData = pageManager.createValidDatesData(validDates); // ASSERT boolean dataIsCorrect = actualCachedValidDatesData.equals(expectedCachedValidDatesData); assertTrue("Created cached valid dates data is incorrect: " + actualCachedValidDatesData + " versus " + expectedCachedValidDatesData, dataIsCorrect); } }