/*
*
* 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.resources.handlers;
import com.google.common.collect.Lists;
import com.netflix.genie.core.jobs.JobConstants;
import com.netflix.genie.test.categories.UnitTest;
import com.netflix.genie.web.resources.writers.DirectoryWriter;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.mockito.Mockito;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.HandlerMapping;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.List;
import java.util.UUID;
/**
* Unit tests for the GenieResourceHttpRequestHandler class.
*
* @author tgianos
* @since 3.0.0
*/
@Category(UnitTest.class)
public class GenieResourceHttpRequestHandlerUnitTests {
private DirectoryWriter directoryWriter;
private GenieResourceHttpRequestHandler handler;
private Resource location;
/**
* Setup for the tests.
*/
@Before
public void setup() {
this.directoryWriter = Mockito.mock(DirectoryWriter.class);
this.handler = new GenieResourceHttpRequestHandler(this.directoryWriter);
this.location = Mockito.mock(Resource.class);
final List<Resource> locations = Lists.newArrayList(this.location);
this.handler.setLocations(locations);
}
/**
* Make sure we can't handle any requests for resources without a resource location to look in.
*
* @throws ServletException on error
* @throws IOException on error
*/
@Test(expected = IllegalStateException.class)
public void cantHandleRequestWithOutAnyLocations() throws ServletException, IOException {
this.handler.setLocations(Lists.newArrayList());
this.handler.handleRequest(Mockito.mock(HttpServletRequest.class), Mockito.mock(HttpServletResponse.class));
}
/**
* Make sure we can't handle any requests for resources with more than one resource available.
*
* @throws ServletException on error
* @throws IOException on error
*/
@Test(expected = IllegalStateException.class)
public void cantHandleRequestWithTooManyLocations() throws ServletException, IOException {
this.handler.setLocations(Lists.newArrayList(Mockito.mock(Resource.class), Mockito.mock(Resource.class)));
this.handler.handleRequest(Mockito.mock(HttpServletRequest.class), Mockito.mock(HttpServletResponse.class));
}
/**
* Make sure we can't handle the HTTP request for a resource if we don't have the proper attribute in the request.
*
* @throws ServletException on error
* @throws IOException on error
*/
@Test(expected = IllegalStateException.class)
public void cantHandleRequestWithoutHandlerMappingAttribute() throws ServletException, IOException {
final HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
final HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
Mockito.when(request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)).thenReturn(null);
this.handler.handleRequest(request, response);
}
/**
* Make sure if the resource doesn't exist we return a 404.
*
* @throws ServletException On any error
* @throws IOException On any error
*/
@Test
public void cantHandleRequestIfResourceDoesntExist() throws ServletException, IOException {
final HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
final HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
final String path = UUID.randomUUID().toString();
Mockito.when(request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)).thenReturn(path);
final Resource resource = Mockito.mock(Resource.class);
Mockito.when(this.location.createRelative(Mockito.eq(path))).thenReturn(resource);
Mockito.when(resource.exists()).thenReturn(false);
this.handler.handleRequest(request, response);
Mockito.verify(response, Mockito.times(1)).sendError(HttpStatus.NOT_FOUND.value());
}
/**
* Make sure if the resource isn't a directory it's sent to super.
* <p>
* Note: This doesn't actually test returning a file as we leverage Spring's implementation
* which we assume is working.
*
* @throws ServletException On any error
* @throws IOException On any error
*/
@Test
public void canHandleRequestForFile() throws ServletException, IOException {
final HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
final HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
final String path = UUID.randomUUID().toString();
Mockito.when(request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)).thenReturn(path);
final Resource resource = Mockito.mock(Resource.class);
Mockito.when(this.location.createRelative(Mockito.eq(path))).thenReturn(resource);
Mockito.when(resource.exists()).thenReturn(true);
final File file = Mockito.mock(File.class);
Mockito.when(resource.getFile()).thenReturn(file);
Mockito.when(file.isDirectory()).thenReturn(false);
this.handler.handleRequest(request, response);
Mockito.verify(response, Mockito.times(1)).sendError(HttpStatus.NOT_FOUND.value());
}
/**
* Make sure if the resource is a directory as HTML it's handled properly.
*
* @throws Exception On any error
*/
@Test
public void canHandleRequestForDirectoryHtml() throws Exception {
final HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
final HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
final String path = UUID.randomUUID().toString();
Mockito.when(request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)).thenReturn(path);
Mockito.when(request.getHeader(HttpHeaders.ACCEPT)).thenReturn(MediaType.TEXT_HTML_VALUE);
final String forwardedUrl = UUID.randomUUID().toString();
Mockito.when(request.getHeader(JobConstants.GENIE_FORWARDED_FROM_HEADER)).thenReturn(forwardedUrl);
final Resource resource = Mockito.mock(Resource.class);
Mockito.when(this.location.createRelative(Mockito.eq(path))).thenReturn(resource);
Mockito.when(resource.exists()).thenReturn(true);
final File file = Mockito.mock(File.class);
Mockito.when(resource.getFile()).thenReturn(file);
Mockito.when(file.isDirectory()).thenReturn(true);
final String html = UUID.randomUUID().toString();
Mockito.when(
this.directoryWriter.toHtml(Mockito.eq(file), Mockito.eq(forwardedUrl), Mockito.eq(false))
).thenReturn(html);
final ServletOutputStream os = Mockito.mock(ServletOutputStream.class);
Mockito.when(response.getOutputStream()).thenReturn(os);
this.handler.handleRequest(request, response);
Mockito.verify(response, Mockito.times(1)).setContentType(MediaType.TEXT_HTML_VALUE);
Mockito.verify(response, Mockito.times(1)).getOutputStream();
Mockito.verify(this.directoryWriter, Mockito.times(1))
.toHtml(Mockito.eq(file), Mockito.eq(forwardedUrl), Mockito.eq(false));
Mockito.verify(os, Mockito.times(1)).write(html.getBytes(Charset.forName("UTF-8")));
}
/**
* Make sure if the resource is a directory as JSON it's handled properly.
*
* @throws Exception On any error
*/
@Test
public void canHandleRequestForDirectoryJson() throws Exception {
final HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
final HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
final String path = UUID.randomUUID().toString();
Mockito.when(request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)).thenReturn(path);
Mockito.when(request.getHeader(HttpHeaders.ACCEPT)).thenReturn(null);
final String requestUrl = UUID.randomUUID().toString();
Mockito.when(request.getRequestURL()).thenReturn(new StringBuffer(requestUrl));
final Resource resource = Mockito.mock(Resource.class);
Mockito.when(this.location.createRelative(Mockito.eq(path))).thenReturn(resource);
Mockito.when(resource.exists()).thenReturn(true);
final File file = Mockito.mock(File.class);
Mockito.when(resource.getFile()).thenReturn(file);
Mockito.when(file.isDirectory()).thenReturn(true);
final String html = UUID.randomUUID().toString();
Mockito.when(
this.directoryWriter.toJson(Mockito.eq(file), Mockito.eq(requestUrl), Mockito.eq(false))
).thenReturn(html);
final ServletOutputStream os = Mockito.mock(ServletOutputStream.class);
Mockito.when(response.getOutputStream()).thenReturn(os);
this.handler.handleRequest(request, response);
Mockito.verify(response, Mockito.times(1)).setContentType(MediaType.APPLICATION_JSON_VALUE);
Mockito.verify(response, Mockito.times(1)).getOutputStream();
Mockito.verify(this.directoryWriter, Mockito.times(1))
.toJson(Mockito.eq(file), Mockito.eq(requestUrl), Mockito.eq(false));
Mockito.verify(os, Mockito.times(1)).write(html.getBytes(Charset.forName("UTF-8")));
}
/**
* Make sure if the resource is a directory it's handled properly until exception thrown.
*
* @throws Exception On any error
*/
@Test(expected = ServletException.class)
public void cantHandleRequestForDirectoryWhenException() throws Exception {
final HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
final HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
final String path = UUID.randomUUID().toString();
Mockito.when(request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)).thenReturn(path);
Mockito.when(request.getAttribute(GenieResourceHttpRequestHandler.GENIE_JOB_IS_ROOT_DIRECTORY))
.thenReturn(false);
final String requestUrl = UUID.randomUUID().toString();
Mockito.when(request.getRequestURL()).thenReturn(new StringBuffer(requestUrl));
final Resource resource = Mockito.mock(Resource.class);
Mockito.when(this.location.createRelative(Mockito.eq(path))).thenReturn(resource);
Mockito.when(resource.exists()).thenReturn(true);
final File file = Mockito.mock(File.class);
Mockito.when(resource.getFile()).thenReturn(file);
Mockito.when(file.isDirectory()).thenReturn(true);
Mockito.when(
this.directoryWriter.toHtml(Mockito.eq(file), Mockito.eq(requestUrl), Mockito.eq(true))
).thenThrow(new Exception());
final ServletOutputStream os = Mockito.mock(ServletOutputStream.class);
Mockito.when(response.getOutputStream()).thenReturn(os);
this.handler.handleRequest(request, response);
}
/**
* Make sure we can use the overridden set headers method properly for large file sizes.
*
* @throws IOException on error
*/
@Test
public void canSetHeaders() throws IOException {
final HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
final Resource resource = Mockito.mock(Resource.class);
final MediaType mediaType = MediaType.APPLICATION_OCTET_STREAM;
final long justRight = (long) Integer.MAX_VALUE;
final long tooLong = (long) Integer.MAX_VALUE + 1;
Mockito
.when(resource.contentLength())
.thenReturn(justRight)
.thenReturn(tooLong);
this.handler.setHeaders(response, resource, mediaType);
this.handler.setHeaders(response, resource, null);
Mockito.verify(response, Mockito.times(1)).setContentLengthLong(justRight);
Mockito.verify(response, Mockito.times(1)).setContentLengthLong(tooLong);
Mockito.verify(response, Mockito.times(1)).setContentType(Mockito.anyString());
}
}