/* * Copyright 2017 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.remote; import com.thoughtworks.go.domain.*; import com.thoughtworks.go.server.messaging.JobStatusMessage; import com.thoughtworks.go.server.messaging.JobStatusTopic; import com.thoughtworks.go.server.service.AgentRuntimeInfo; import com.thoughtworks.go.server.service.AgentService; import com.thoughtworks.go.server.service.BuildRepositoryService; import com.thoughtworks.go.util.LogFixture; import com.thoughtworks.go.util.SystemEnvironment; import org.apache.log4j.Level; import org.hamcrest.Matchers; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.remoting.RemoteAccessException; import java.util.Arrays; import java.util.List; import static com.thoughtworks.go.util.LogFixture.logFixtureFor; import static com.thoughtworks.go.util.SystemUtil.currentWorkingDirectory; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.sameInstance; 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 BuildRepositoryRemoteImplTest { private BuildRepositoryService repositoryService; private AgentService agentService; private JobStatusTopic jobStatusTopic; private BuildRepositoryRemoteImpl buildRepository; private LogFixture logFixture; private AgentRuntimeInfo info; @Before public void setUp() { repositoryService = mock(BuildRepositoryService.class); agentService = mock(AgentService.class); jobStatusTopic = mock(JobStatusTopic.class); buildRepository = new BuildRepositoryRemoteImpl(repositoryService, agentService, jobStatusTopic); logFixture = logFixtureFor(BuildRepositoryRemoteImpl.class, Level.TRACE); info = new AgentRuntimeInfo(new AgentIdentifier("host", "192.168.1.1", "uuid"), AgentRuntimeStatus.Idle, currentWorkingDirectory(), "cookie", false); } @After public void tearDown() throws Exception { logFixture.close(); } @Test public void shouldUpdateAgentServiceOnPing() { info.setStatus(AgentStatus.Cancelled); when(agentService.findAgentAndRefreshStatus(info.getUUId())).thenReturn(AgentInstance.createFromLiveAgent(info, new SystemEnvironment())); AgentInstruction instruction = buildRepository.ping(info); assertThat(instruction.isShouldCancelJob(), is(true)); verify(agentService).updateRuntimeInfo(info); assertThat(log(), hasItem(info + " ping received.")); } @Test public void shouldLogFailureToUpdateAgentServiceOnPing() { RuntimeException runtimeException = new RuntimeException("holy smoke"); doThrow(runtimeException).when(agentService).updateRuntimeInfo(info); try { buildRepository.ping(info); fail("should have bombed because agentService could not be updated"); } catch (Exception e) { assertRemoteException(e, runtimeException); } assertThat(log(), hasItem("Error occurred in " + info + " ping.")); } @Test public void shouldReportCurrentStatus() throws Exception { JobIdentifier jobId = new JobIdentifier(new StageIdentifier("pipelineName", 1, "stageName", "1"), "job"); buildRepository.reportCurrentStatus(info, jobId, JobState.Building); verify(agentService).updateRuntimeInfo(info); verify(repositoryService).updateStatusFromAgent(jobId, JobState.Building, info.getUUId()); verify(jobStatusTopic).post(new JobStatusMessage(jobId, JobState.Building, info.getUUId())); assertThat(log(), hasItem(String.format("[%s] is reporting status [%s] for [%s]", info.agentInfoDebugString(), JobState.Building, jobId.toFullString()))); } @Test public void shouldLogAgentReportingStatusExceptions() throws Exception { JobIdentifier jobId = new JobIdentifier(new StageIdentifier("pipelineName", 1, "stageName", "1"), "job"); RuntimeException runtimeException = new RuntimeException("holy smoke"); doThrow(runtimeException).when(repositoryService).updateStatusFromAgent(jobId, JobState.Building, info.getUUId()); try { buildRepository.reportCurrentStatus(info, jobId, JobState.Building); fail("should have propagated exception raised by build repository service"); } catch (Exception e) { assertRemoteException(e, runtimeException); } assertThat(log(), hasItem(String.format("Exception occurred when [%s] tries to report status [%s] for [%s]", info.agentInfoDebugString(), JobState.Building, jobId.toFullString()))); } @Test public void shouldReportResult() throws Exception { JobIdentifier jobId = new JobIdentifier(new StageIdentifier("pipelineName", 1, "stageName", "1"), "job"); buildRepository.reportCompleting(info, jobId, JobResult.Passed); verify(repositoryService).completing(jobId, JobResult.Passed, info.getUUId()); verify(agentService).updateRuntimeInfo(info); assertThat(log(), hasItem(String.format("[%s] is reporting result [%s] for [%s]", info.agentInfoDebugString(), JobResult.Passed, jobId.toFullString()))); } @Test public void shouldLogReportResultException() { JobIdentifier jobId = new JobIdentifier(new StageIdentifier("pipelineName", 1, "stageName", "1"), "job"); RuntimeException runtimeException = new RuntimeException("holy smoke"); doThrow(runtimeException).when(repositoryService).completing(jobId, JobResult.Passed, info.getUUId()); try { buildRepository.reportCompleting(info, jobId, JobResult.Passed); fail("should have propagated exception raised by build repository service"); } catch (Exception e) { assertRemoteException(e, runtimeException); } assertThat(log(), hasItem(String.format("Exception occurred when [%s] tries to report result [%s] for [%s]", info.agentInfoDebugString(), JobResult.Passed, jobId.toFullString()))); } @Test public void shouldReportCompletedWithResult() throws Exception { JobIdentifier jobId = new JobIdentifier(new StageIdentifier("pipelineName", 1, "stageName", "1"), "job"); buildRepository.reportCompleted(info, jobId, JobResult.Passed); verify(repositoryService).completing(jobId, JobResult.Passed, info.getUUId()); verify(agentService).updateRuntimeInfo(info); verify(repositoryService).updateStatusFromAgent(jobId, JobState.Completed, info.getUUId()); assertThat(log(), hasItem(String.format("[%s] is reporting status and result [%s, %s] for [%s]", info.agentInfoDebugString(), JobState.Completed, JobResult.Passed, jobId.toFullString()))); } @Test public void shouldLogReportResultExceptionDuringReportCompletedFailure() { JobIdentifier jobId = new JobIdentifier(new StageIdentifier("pipelineName", 1, "stageName", "1"), "job"); RuntimeException runtimeException = new RuntimeException("holy smoke"); doThrow(runtimeException).when(repositoryService).completing(jobId, JobResult.Passed, info.getUUId()); try { buildRepository.reportCompleted(info, jobId, JobResult.Passed); fail("should have propagated exception raised by build repository service"); } catch (Exception e) { assertRemoteException(e, runtimeException); } assertThat(log(), hasItem(String.format("Exception occurred when [%s] tries to report status and result [%s, %s] for [%s]", info.agentInfoDebugString(), JobState.Completed, JobResult.Passed, jobId.toFullString()))); } @Test public void shouldUnderstandIfTestIsIgnored() { JobIdentifier jobId = new JobIdentifier(new StageIdentifier("pipelineName", 1, "stageName", "1"), "job"); jobId.setBuildId(12l); buildRepository.isIgnored(jobId); verify(repositoryService).isCancelledOrRescheduled(jobId.getBuildId()); } @Test public void shouldThrowRemoteExceptionWhenIgnoreJobThrowsException() { JobIdentifier jobId = new JobIdentifier(new StageIdentifier("pipelineName", 1, "stageName", "1"), "job"); jobId.setBuildId(12l); RuntimeException runtimeException = new RuntimeException("holy smoke"); doThrow(runtimeException).when(repositoryService).isCancelledOrRescheduled(jobId.getBuildId()); try { buildRepository.isIgnored(jobId); fail("should throw exception"); } catch (Exception e) { assertRemoteException(e, runtimeException); } } @Test public void shouldUnderstandGetingCookie() { when(agentService.assignCookie(info.getIdentifier())).thenReturn("cookie"); assertThat(buildRepository.getCookie(info.getIdentifier(), "/foo/bar"), is("cookie")); assertThat(log(), hasItem("[Agent Cookie] Agent [Agent [host, 192.168.1.1, uuid]] at location [/foo/bar] asked for a new cookie, assigned [cookie]")); } @Test public void shouldPropagateExceptionWhenGettingCookie() { RuntimeException runtimeException = new RuntimeException("holy smoke"); when(agentService.assignCookie(info.getIdentifier())).thenThrow(runtimeException); try { buildRepository.getCookie(info.getIdentifier(), "/foo/bar"); fail("should propagate exception raised by agent service assignCookie"); } catch (Exception e) { assertRemoteException(e, runtimeException); } } private void assertRemoteException(Exception exception, RuntimeException innerException) { assertThat(exception, is(Matchers.<Object>instanceOf(RemoteAccessException.class))); assertThat(exception.getCause(), sameInstance(innerException)); } private List<String> log() { return Arrays.asList(logFixture.getMessages()); } }