Skip to content

Commit

Permalink
Add Tests
Browse files Browse the repository at this point in the history
Initial tests.
  • Loading branch information
colin-msphere committed Feb 26, 2016
1 parent fdefcd4 commit 124f37a
Show file tree
Hide file tree
Showing 2 changed files with 388 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
package com.mesosphere.velocity.marathon;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import hudson.Launcher;
import hudson.model.*;
import hudson.tasks.Shell;
import org.apache.commons.io.FileUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.TestBuilder;

import java.io.IOException;
import java.net.InetSocketAddress;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public class MarathonRecorderTest {
@Rule
public JenkinsRule j = new JenkinsRule();

/**
* An HTTP Server to receive requests from the plugin.
*/
HttpServer httpServer;
TestHandler handler;
InetSocketAddress serverAddress;

@Before
public void setUp() throws IOException {
handler = new TestHandler();
serverAddress = new InetSocketAddress("localhost", 0);

httpServer = HttpServer.create(serverAddress, 500);
httpServer.createContext("/", handler);
httpServer.start();
}

@After
public void tearDown() {
httpServer.stop(0);
}

/**
* Test that when "marathon.json" is not present the build is failed
* and no requests are made to the configured Marathon instance.
*
* @throws Exception
*/
@Test
public void testRecorderNoFile() throws Exception {
final FreeStyleProject project = j.createFreeStyleProject();
project.getBuildersList().add(new Shell("echo hello"));

// add recorder
project.getPublishersList().add(new MarathonRecorder(getHttpAddresss()));

// run a build with the shell step and recorder publisher
final FreeStyleBuild build = project.scheduleBuild2(0).get();

// get console log
final String s = FileUtils.readFileToString(build.getLogFile());

// assert things
assertEquals("Build should fail", Result.FAILURE, build.getResult());
assertTrue(s.contains("[Marathon]"));
assertTrue(s.contains("marathon.json"));
assertEquals("No web requests were made", 0, handler.getRequestCount());
}

/**
* Test a basic successful scenario. The Marathon instance will return
* a 200 OK.
*
* @throws Exception
*/
@Test
public void testRecorderPass() throws Exception {
final String payload = "{\"id\":\"myapp\"}";
final FreeStyleProject project = j.createFreeStyleProject();

// add builders
project.getBuildersList().add(new Shell("echo hello"));
project.getBuildersList().add(createMarathonFileBuilder(payload));

// add post-builder
project.getPublishersList().add(new MarathonRecorder(getHttpAddresss()));

// run a build with the shell step and recorder publisher
final FreeStyleBuild build = project.scheduleBuild2(0).get();

// get console log
final String s = FileUtils.readFileToString(build.getLogFile());

// assert things
assertEquals("Build should fail", Result.SUCCESS, build.getResult());
assertTrue(s.contains("[Marathon]"));
assertTrue(s.contains("application updated"));
assertEquals("Only 1 web request", 1, handler.getRequestCount());
}

private TestBuilder createMarathonFileBuilder(final String payload) {
return new TestBuilder() {
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher,
BuildListener listener) throws InterruptedException, IOException {
build.getWorkspace().child("marathon.json").write(payload, "UTF-8");
return true;
}
};
}

/**
* Test that a 409 response from the Marathon instance triggers
* retry logic. The default logic is to try 3 times with X
* seconds in between each request.
*
* @throws Exception
*/
@Test
public void testRecorderMaxRetries() throws Exception {
final String payload = "{\"id\":\"myapp\"}";
final FreeStyleProject project = j.createFreeStyleProject();

// add builders
project.getBuildersList().add(new Shell("echo hello"));
project.getBuildersList().add(createMarathonFileBuilder(payload));

// add post-builder
project.getPublishersList().add(new MarathonRecorder(getHttpAddresss()));

// return 409 to trigger retry logic
handler.setResponseCode(409);

// run a build with the shell step and recorder publisher
final FreeStyleBuild build = project.scheduleBuild2(0).get();

// get console log
final String s = FileUtils.readFileToString(build.getLogFile());

// assert things
assertEquals("Build should fail", Result.FAILURE, build.getResult());
assertTrue(s.contains("[Marathon]"));
assertTrue(s.contains("max retries"));
assertEquals("Should be 3 retries", 3, handler.getRequestCount());
}

/**
* Test that a 4xx (404 in this case) response code does not
* trigger retries. This should result in only one request
* being made to the configured Marathon instance.
*
* @throws Exception
*/
@Test
public void testRecorder404() throws Exception {
final String payload = "{\"id\":\"myapp\"}";
final FreeStyleProject project = j.createFreeStyleProject();

// add builders
project.getBuildersList().add(new Shell("echo hello"));
project.getBuildersList().add(createMarathonFileBuilder(payload));

// add post-builder
project.getPublishersList().add(new MarathonRecorder(getHttpAddresss()));

// return a 404, which will fail the build
handler.setResponseCode(404);

// run a build with the shell step and recorder publisher
final FreeStyleBuild build = project.scheduleBuild2(0).get();

// get console log
final String s = FileUtils.readFileToString(build.getLogFile());

// assert things
assertEquals("Build should fail", Result.FAILURE, build.getResult());
assertTrue(s.contains("[Marathon]"));
assertTrue(s.contains("Failed to update"));
assertEquals("Only 1 request should be made", 1, handler.getRequestCount());
}

/**
* Test that a 5xx (503 in this case) response code does not
* trigger retries. This should result in only one request
* being made to the configured Marathon instance.
*
* @throws Exception
*/
@Test
public void testRecorder503() throws Exception {
final String payload = "{\"id\":\"myapp\"}";
final FreeStyleProject project = j.createFreeStyleProject();

// add builders
project.getBuildersList().add(new Shell("echo hello"));
project.getBuildersList().add(createMarathonFileBuilder(payload));

// add post-builder
project.getPublishersList().add(new MarathonRecorder(getHttpAddresss()));

// return a 503, which will fail the build
handler.setResponseCode(503);

// run a build with the shell step and recorder publisher
final FreeStyleBuild build = project.scheduleBuild2(0).get();

// get console log
final String s = FileUtils.readFileToString(build.getLogFile());

// assert things
assertEquals("Build should fail", Result.FAILURE, build.getResult());
assertTrue(s.contains("[Marathon]"));
assertTrue(s.contains("Failed to update"));
assertEquals("Only 1 request should be made", 1, handler.getRequestCount());
}

private String getHttpAddresss() {
return "http://" + httpServer.getAddress().getHostName() + ":" + httpServer.getAddress().getPort();
}

/**
* An {@link HttpHandler} that counts the number of requests.
* The response body and status code can be altered for each
* test scenario.
*/
class TestHandler implements HttpHandler {
private int requestCount;
private String responseBody;
private int responseCode;

public TestHandler() {
this.requestCount = 0;
this.responseBody = null;
this.responseCode = 200;
}

@Override
public void handle(HttpExchange httpExchange) throws IOException {
requestCount++;
httpExchange.sendResponseHeaders(responseCode, responseBody != null ? responseBody.length() : 0);
}

public int getRequestCount() {
return requestCount;
}

public String getResponseBody() {
return responseBody;
}

public void setResponseBody(String responseBody) {
this.responseBody = responseBody;
}

public int getResponseCode() {
return responseCode;
}

public void setResponseCode(int responseCode) {
this.responseCode = responseCode;
}

public void resetRequestCount() {
this.requestCount = 0;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package com.mesosphere.velocity.marathon.impl;

import com.mesosphere.velocity.marathon.exceptions.MarathonFileInvalidException;
import com.mesosphere.velocity.marathon.exceptions.MarathonFileMissingException;
import com.mesosphere.velocity.marathon.interfaces.AppConfig;
import com.mesosphere.velocity.marathon.interfaces.MarathonBuilder;
import hudson.FilePath;
import net.sf.json.JSONObject;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import java.io.IOException;

import static org.junit.Assert.*;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.when;

@RunWith(PowerMockRunner.class)
@PrepareForTest({FilePath.class})
public class MarathonBuilderImplTest {
@Rule
public final ExpectedException exception = ExpectedException.none();
@Mock
private AppConfig appConfig;
private MarathonBuilder builder;

@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}

@Test
public void testSetJson() {
final String key = "key";
final String val = "testvalue";
final JSONObject json = new JSONObject();

builder = new MarathonBuilderImpl(appConfig);
assertNull("JSON is null on initial creation.", builder.getJson());

builder.setJson(json);
assertNotNull("An empty JSON object is set correctly.", builder.getJson());

json.put(key, val);
builder.setJson(json);
assertEquals("Simple JSON object with values is set correctly.", builder.getJson().getString(key), val);
}

@Test
public void testReadNonexistingFile()
throws IOException, InterruptedException, MarathonFileMissingException, MarathonFileInvalidException {
final String filename = "somefile";
final FilePath wsMock = PowerMockito.mock(FilePath.class);
final FilePath fileMock = PowerMockito.mock(FilePath.class);
when(wsMock.child(anyString())).thenReturn(fileMock);
when(fileMock.exists()).thenReturn(false);

// create builder
builder = new MarathonBuilderImpl(appConfig);

// setup FileMissing exception
exception.expect(MarathonFileMissingException.class);
builder.setWorkspace(wsMock).read(filename);
}

@Test
public void testReadDirectoryFile() throws InterruptedException, MarathonFileMissingException, MarathonFileInvalidException, IOException {
final String filename = "somefile";
final FilePath wsMock = PowerMockito.mock(FilePath.class);
final FilePath fileMock = PowerMockito.mock(FilePath.class);
when(wsMock.child(anyString())).thenReturn(fileMock);
when(fileMock.exists()).thenReturn(true);
when(fileMock.isDirectory()).thenReturn(true);

// create builder
builder = new MarathonBuilderImpl(appConfig);

// setup FileInvalid exception
exception.expect(MarathonFileInvalidException.class);
builder.setWorkspace(wsMock).read(filename);
}

@Test
public void testReadPositive() throws Exception {
final String filename = "somefile";
final FilePath wsMock = PowerMockito.mock(FilePath.class);
final FilePath fileMock = PowerMockito.mock(FilePath.class);
final JSONObject expectedJson = new JSONObject();

when(wsMock.child(anyString())).thenReturn(fileMock);
when(fileMock.exists()).thenReturn(true);
when(fileMock.isDirectory()).thenReturn(false);

// the magic...
when(fileMock.readToString()).thenReturn("{}");

builder = new MarathonBuilderImpl(appConfig);
builder.setWorkspace(wsMock).read(filename);
assertEquals("Empty JSON Object was read in", expectedJson, builder.getJson());

// now non-empty
when(fileMock.readToString()).thenReturn("{\"id\": \"myid\"}");
expectedJson.put("id", "myid");
builder.read(filename);
assertEquals("JSON should have same id",
expectedJson.getString("id"), builder.getJson().getString("id"));
}
}

0 comments on commit 124f37a

Please sign in to comment.