/*
* Copyright 2016 ThoughtWorks, 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.thoughtworks.go.domain;
import com.thoughtworks.go.util.HttpService;
import com.thoughtworks.go.util.LogFixture;
import com.thoughtworks.go.util.TestingClock;
import org.apache.log4j.Level;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.net.SocketException;
import static com.thoughtworks.go.util.LogFixture.logFixtureFor;
import static javax.servlet.http.HttpServletResponse.*;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.Matchers.lessThanOrEqualTo;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.number.OrderingComparison.greaterThanOrEqualTo;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.junit.matchers.JUnitMatchers.hasItems;
import static org.mockito.Mockito.*;
public class DownloadActionTest {
private TestingClock clock;
private FetchHandler fetchHandler;
private StubGoPublisher publisher;
@Before
public void setUp() throws Exception {
clock = new TestingClock();
fetchHandler = mock(FetchHandler.class);
publisher = new StubGoPublisher();
}
@Test
public void shouldRetryWhenCreatingFolderZipCache() throws Exception {
when(fetchHandler.handleResult(200, publisher)).thenReturn(true);
MockCachingFetchZipHttpService httpService = new MockCachingFetchZipHttpService(3);
DownloadAction downloadAction = new DownloadAction(httpService, publisher, clock);
downloadAction.perform("foo", fetchHandler);
assertThat(httpService.timesCalled, is(3));
assertThat(clock.getSleeps(), hasItems(5000L));
}
@Test
public void shouldRetryThreeTimesWhenDownloadFails() throws Exception {
when(fetchHandler.handleResult(200, publisher)).thenReturn(true);
try (LogFixture logging = logFixtureFor(DownloadAction.class, Level.DEBUG)) {
FailSometimesHttpService httpService = new FailSometimesHttpService(3);
DownloadAction downloadAction = new DownloadAction(httpService, publisher, clock);
downloadAction.perform("foo", fetchHandler);
assertThat(httpService.timesCalled, is(4));
shouldHaveLogged(logging, Level.WARN, "Could not fetch artifact foo.");
shouldHaveLogged(logging, Level.WARN, "Error was : Caught an exception 'Connection Reset'");
assertBetween(clock.getSleeps().get(0), 10000L, 20000L);
assertBetween(clock.getSleeps().get(1), 20000L, 30000L);
assertBetween(clock.getSleeps().get(2), 30000L, 40000L);
assertThat(clock.getSleeps().size(), is(3));
}
}
@Test
public void shouldFailAfterFourthTryWhenDownloadFails() throws Exception {
try (LogFixture logging = logFixtureFor(DownloadAction.class, Level.DEBUG)) {
FailSometimesHttpService httpService = new FailSometimesHttpService(99);
try {
new DownloadAction(httpService, new StubGoPublisher(), clock).perform("foo", fetchHandler);
fail("Expected to throw exception after four tries");
} catch (Exception e) {
assertThat(httpService.timesCalled, is(4));
shouldHaveLogged(logging, Level.ERROR, "Giving up fetching resource 'foo'. Tried 4 times and failed.");
}
}
}
@Test
public void shouldReturnWithoutRetryingArtifactIsNotModified() throws Exception {
fetchHandler = new FileHandler(new File(""), getSrc());
HttpService httpService = mock(HttpService.class);
StubGoPublisher goPublisher = new StubGoPublisher();
when(httpService.download("foo", fetchHandler)).thenReturn(SC_NOT_MODIFIED);
new DownloadAction(httpService, goPublisher, clock).perform("foo", fetchHandler);
verify(httpService).download("foo", this.fetchHandler);
verifyNoMoreInteractions(httpService);
assertThat(goPublisher.getMessage(), containsString("Artifact is not modified, skipped fetching it"));
}
private String getSrc() {
return "";
}
private class MockCachingFetchZipHttpService extends HttpService {
private final int count;
private int timesCalled = 0;
MockCachingFetchZipHttpService(int count) {
this.count = count;
}
public int download(String url, FetchHandler handler) throws IOException {
timesCalled += 1;
if (timesCalled < count) {
return SC_ACCEPTED;
} else {
return SC_OK;
}
}
}
private void shouldHaveLogged(LogFixture logging, Level level, String message) {
Assert.assertTrue(
"Expected log to contain " + message + " but got:\n" + logging.allLogs(),
logging.contains(level, message));
}
private void assertBetween(Long actual, long min, long max) {
assertThat(actual, greaterThanOrEqualTo(min));
assertThat(actual, lessThanOrEqualTo(max));
}
private class FailSometimesHttpService extends HttpService {
private int count;
private int timesCalled = 0;
public FailSometimesHttpService(int count) {
this.count = count;
}
public int download(String url, FetchHandler handler) throws IOException {
timesCalled += 1;
if (timesCalled <= count) {
throw new SocketException("Connection Reset");
} else {
return SC_OK;
}
}
}
}