/*
* 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.publishers;
import com.thoughtworks.go.domain.AgentRuntimeStatus;
import com.thoughtworks.go.domain.ArtifactMd5Checksums;
import com.thoughtworks.go.domain.JobIdentifier;
import com.thoughtworks.go.remote.AgentIdentifier;
import com.thoughtworks.go.remote.work.BuildRepositoryRemoteStub;
import com.thoughtworks.go.remote.work.GoArtifactsManipulatorStub;
import com.thoughtworks.go.server.service.AgentRuntimeInfo;
import com.thoughtworks.go.util.*;
import com.thoughtworks.go.work.DefaultGoPublisher;
import org.apache.commons.collections.buffer.CircularFifoBuffer;
import org.apache.commons.io.FileUtils;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.Properties;
import static com.thoughtworks.go.util.SystemUtil.currentWorkingDirectory;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.*;
public class GoArtifactsManipulatorTest {
private HttpService httpService;
private File tempFile;
private GoArtifactsManipulatorStub goArtifactsManipulatorStub;
private JobIdentifier jobIdentifier;
private DefaultGoPublisher goPublisher;
private File artifactFolder;
@Before
public void setUp() throws Exception {
httpService = mock(HttpService.class);
artifactFolder = TestFileUtil.createTempFolder("artifact_folder");
tempFile = TestFileUtil.createTestFile(artifactFolder, "file.txt");
goArtifactsManipulatorStub = new GoArtifactsManipulatorStub(httpService);
jobIdentifier = new JobIdentifier("pipeline1", 1, "label-1", "stage1", "1", "job1");
AgentRuntimeInfo agentRuntimeInfo = new AgentRuntimeInfo(new AgentIdentifier("h", "1", "u"), AgentRuntimeStatus.Idle, currentWorkingDirectory(), null, false);
goPublisher = new DefaultGoPublisher(goArtifactsManipulatorStub, jobIdentifier, new BuildRepositoryRemoteStub(), agentRuntimeInfo);
}
@After
public void tearDown() throws IOException {
FileUtil.tryDeleting(artifactFolder);
}
@Test
public void shouldBombWithErrorWhenStatusCodeReturnedIsRequestEntityTooLarge() throws IOException, InterruptedException {
long size = anyLong();
when(httpService.upload(any(String.class), size, any(File.class), any(Properties.class))).thenReturn(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE);
CircularFifoBuffer buffer = (CircularFifoBuffer) ReflectionUtil.getField(ReflectionUtil.getField(goPublisher, "consoleOutputTransmitter"), "buffer");
synchronized (buffer) {
try {
goArtifactsManipulatorStub.publish(goPublisher, "some_dest", tempFile, jobIdentifier);
fail("should have thrown request entity too large error");
} catch (RuntimeException e) {
String expectedMessage = "Artifact upload for file " + tempFile.getAbsolutePath() + " (Size: "+size+") was denied by the server. This usually happens when server runs out of disk space.";
assertThat(e.getMessage(), is("java.lang.RuntimeException: " + expectedMessage + ". HTTP return code is 413"));
assertThat(buffer.toString().contains(expectedMessage), is(true));
}
}
}
@Test
public void uploadShouldBeGivenFileSize() throws IOException {
when(httpService.upload(any(String.class), eq(tempFile.length()), any(File.class), any(Properties.class))).thenReturn(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE);
try {
goArtifactsManipulatorStub.publish(goPublisher, "dest", tempFile, jobIdentifier);
fail("should have thrown request entity too large error");
} catch (RuntimeException e) {
verify(httpService).upload(any(String.class), eq(tempFile.length()), any(File.class), any(Properties.class));
}
}
@Test
public void shouldUploadArtifactChecksumAlongWithArtifact() throws IOException {
String data = "Some text whose checksum can be asserted";
final String md5 = CachedDigestUtils.md5Hex(data);
FileUtils.writeStringToFile(tempFile, data);
Properties properties = new Properties();
properties.setProperty("dest/path/file.txt", md5);
when(httpService.upload(any(String.class), eq(tempFile.length()), any(File.class), eq(properties))).thenReturn(HttpServletResponse.SC_OK);
goArtifactsManipulatorStub.publish(goPublisher, "/dest/path", tempFile, jobIdentifier);
}
@Test
public void shouldUploadArtifactChecksumWithRightPathWhenArtifactDestinationPathIsEmpty() throws IOException {
String data = "Some text whose checksum can be asserted";
final String md5 = CachedDigestUtils.md5Hex(data);
FileUtils.writeStringToFile(tempFile, data);
Properties properties = new Properties();
properties.setProperty("file.txt", md5);
when(httpService.upload(any(String.class), eq(tempFile.length()), any(File.class), eq(properties))).thenReturn(HttpServletResponse.SC_OK);
goArtifactsManipulatorStub.publish(goPublisher, "", tempFile, jobIdentifier);
}
@Test
public void shouldUploadArtifactChecksumForADirectory() throws IOException {
String data = "Some text whose checksum can be asserted";
String secondData = "some more";
FileUtils.writeStringToFile(tempFile, data);
File anotherFile = new File(artifactFolder, "bond/james_bond/another_file");
FileUtils.writeStringToFile(anotherFile, secondData);
when(httpService.upload(any(String.class), eq(FileUtils.sizeOfDirectory(artifactFolder)), any(File.class), eq(expectedProperties(data, secondData)))).thenReturn(HttpServletResponse.SC_OK);
goArtifactsManipulatorStub.publish(goPublisher, "dest", artifactFolder, jobIdentifier);
}
private Properties expectedProperties(String data, String secondData) {
Properties properties = new Properties();
properties.setProperty("dest/artifact_folder/file.txt", CachedDigestUtils.md5Hex(data));
properties.setProperty("dest/artifact_folder/bond/james_bond/another_file", CachedDigestUtils.md5Hex(secondData));
return properties;
}
private BaseMatcher<File> matchesContent(final Properties expectedProperties) {
return new BaseMatcher<File>() {
public boolean matches(Object item) {
File checksumFile = (File) item;
return new ArtifactMd5Checksums(checksumFile).equals(new ArtifactMd5Checksums(expectedProperties));
}
public void describeTo(Description description) {
description.appendText("Checksum file should also be uploaded along with the artifact");
}
};
}
}