/* * * Copyright 2016 Netflix, Inc. * * 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 com.netflix.genie.web.services.impl; import com.netflix.genie.common.exceptions.GenieException; import com.netflix.genie.common.exceptions.GenieServerException; import com.netflix.genie.test.categories.UnitTest; import com.netflix.spectator.api.Registry; import com.netflix.spectator.api.Timer; import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.rules.TemporaryFolder; import org.mockito.Mockito; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.test.web.client.MockRestServiceServer; import org.springframework.test.web.client.match.MockRestRequestMatchers; import org.springframework.test.web.client.response.MockRestResponseCreators; import org.springframework.web.client.ResourceAccessException; import org.springframework.web.client.RestTemplate; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.nio.charset.Charset; import java.time.Instant; import java.util.UUID; import java.util.concurrent.TimeUnit; /** * Tests for the HttpFileTransferImpl class. * * @author tgianos * @since 3.0.0 */ @Category(UnitTest.class) public class HttpFileTransferImplTest { private static final String TEST_URL = "http://localhost/myFile.txt"; /** * Used for reading and writing files during tests. Cleaned up at end. */ @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); private MockRestServiceServer server; private HttpFileTransferImpl httpFileTransfer; private Timer downloadTimer; private Timer uploadTimer; private Timer metadataTimer; /** * Setup for the tests. */ @Before public void setup() { final RestTemplate restTemplate = new RestTemplate(); this.server = MockRestServiceServer.createServer(restTemplate); this.downloadTimer = Mockito.mock(Timer.class); this.uploadTimer = Mockito.mock(Timer.class); this.metadataTimer = Mockito.mock(Timer.class); final Registry registry = Mockito.mock(Registry.class); Mockito.when(registry.timer("genie.files.http.download.timer")).thenReturn(this.downloadTimer); Mockito.when(registry.timer("genie.files.http.upload.timer")).thenReturn(this.uploadTimer); Mockito.when(registry.timer("genie.files.http.getLastModified.timer")).thenReturn(this.metadataTimer); this.httpFileTransfer = new HttpFileTransferImpl(restTemplate, registry); } /** * Make sure valid url's return true. * * @throws GenieException On error */ @Test public void canValidate() throws GenieException { Assert.assertTrue(this.httpFileTransfer.isValid("http://netflix.github.io/genie")); Assert.assertTrue(this.httpFileTransfer.isValid("https://netflix.github.io/genie")); Assert.assertFalse(this.httpFileTransfer.isValid("ftp://netflix.github.io/genie")); Assert.assertFalse(this.httpFileTransfer.isValid("file:///tmp/blah")); Assert.assertTrue(this.httpFileTransfer.isValid("http://localhost/someFile.txt")); Assert.assertTrue(this.httpFileTransfer.isValid("https://localhost:8080/someFile.txt")); } /** * Make sure we can actually get a file. * * @throws GenieException On error * @throws IOException On error */ @Test public void canGet() throws GenieException, IOException { final File output = this.temporaryFolder.newFile(); final String contents = UUID.randomUUID().toString(); this.server .expect(MockRestRequestMatchers.requestTo(TEST_URL)) .andExpect(MockRestRequestMatchers.method(HttpMethod.GET)) .andRespond( MockRestResponseCreators .withSuccess(contents.getBytes(Charset.forName("UTF-8")), MediaType.APPLICATION_OCTET_STREAM) ); this.httpFileTransfer.getFile(TEST_URL, output.getCanonicalPath()); this.server.verify(); Mockito .verify(this.downloadTimer, Mockito.times(1)) .record(Mockito.anyLong(), Mockito.eq(TimeUnit.NANOSECONDS)); } /** * Make sure can't get a file if the intput isn't a valid url. * * @throws GenieException On Error * @throws IOException On Error */ @Test(expected = GenieServerException.class) public void cantGetWithInvalidUrl() throws GenieException, IOException { this.httpFileTransfer.getFile(UUID.randomUUID().toString(), this.temporaryFolder.getRoot().getCanonicalPath()); } /** * Make sure can't get a file if the output location is a directory. * * @throws GenieException On Error * @throws IOException On Error */ @Test(expected = ResourceAccessException.class) public void cantGetWithDirectoryAsOutput() throws GenieException, IOException { this.server .expect(MockRestRequestMatchers.requestTo(TEST_URL)) .andExpect(MockRestRequestMatchers.method(HttpMethod.GET)) .andRespond( MockRestResponseCreators .withSuccess("junk".getBytes(Charset.forName("UTF-8")), MediaType.APPLICATION_OCTET_STREAM) ); this.httpFileTransfer.getFile(TEST_URL, this.temporaryFolder.getRoot().getCanonicalPath()); } /** * Make sure that there is no implementation of the putFile method. * * @throws GenieException on error */ @Test public void cantPutFile() throws GenieException { try { final String file = UUID.randomUUID().toString(); this.httpFileTransfer.putFile(file, file); Assert.fail(); } catch (final UnsupportedOperationException e) { Mockito .verify(this.uploadTimer, Mockito.times(1)) .record(Mockito.anyLong(), Mockito.eq(TimeUnit.NANOSECONDS)); } } /** * Make sure can get the last update time of a file. * * @throws GenieException On error */ @Test public void canGetLastModifiedTime() throws GenieException { final long lastModified = 28424323000L; final HttpHeaders headers = new HttpHeaders(); headers.setLastModified(lastModified); this.server .expect(MockRestRequestMatchers.requestTo(TEST_URL)) .andExpect(MockRestRequestMatchers.method(HttpMethod.HEAD)) .andRespond(MockRestResponseCreators.withSuccess().headers(headers)); Assert.assertThat(this.httpFileTransfer.getLastModifiedTime(TEST_URL), Matchers.is(lastModified)); this.server.verify(); Mockito .verify(this.metadataTimer, Mockito.times(1)) .record(Mockito.anyLong(), Mockito.eq(TimeUnit.NANOSECONDS)); } /** * Make sure can get the last update time of a file. * * @throws GenieException On error */ @Test public void canGetLastModifiedTimeIfNoHeader() throws GenieException { final long time = Instant.now().toEpochMilli() - 1; this.server .expect(MockRestRequestMatchers.requestTo(TEST_URL)) .andExpect(MockRestRequestMatchers.method(HttpMethod.HEAD)) .andRespond(MockRestResponseCreators.withSuccess()); Assert.assertTrue(this.httpFileTransfer.getLastModifiedTime(TEST_URL) > time); Mockito .verify(this.metadataTimer, Mockito.times(1)) .record(Mockito.anyLong(), Mockito.eq(TimeUnit.NANOSECONDS)); } /** * Make sure can get the last update time of a file. * * @throws GenieException On error */ @Test public void cantGetLastModifiedTimeIfNotURL() throws GenieException { try { this.httpFileTransfer.getLastModifiedTime(UUID.randomUUID().toString()); Assert.fail(); } catch (final GenieServerException e) { Assert.assertTrue(e.getCause() instanceof MalformedURLException); Mockito .verify(this.metadataTimer, Mockito.times(1)) .record(Mockito.anyLong(), Mockito.eq(TimeUnit.NANOSECONDS)); } } }