-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
2 changed files
with
388 additions
and
0 deletions.
There are no files selected for viewing
272 changes: 272 additions & 0 deletions
272
src/test/java/com/mesosphere/velocity/marathon/MarathonRecorderTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} | ||
} |
116 changes: 116 additions & 0 deletions
116
src/test/java/com/mesosphere/velocity/marathon/impl/MarathonBuilderImplTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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")); | ||
} | ||
} |