/* * 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.plugin.access.notification.v2; import com.thoughtworks.go.config.materials.PackageMaterial; import com.thoughtworks.go.config.materials.PluggableSCMMaterial; import com.thoughtworks.go.domain.*; import com.thoughtworks.go.domain.notificationdata.StageNotificationData; import com.thoughtworks.go.domain.packagerepository.PackageDefinition; import com.thoughtworks.go.domain.scm.SCM; import com.thoughtworks.go.helper.PipelineMother; import com.thoughtworks.go.plugin.api.response.Result; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.skyscreamer.jsonassert.JSONAssert; import org.skyscreamer.jsonassert.JSONCompareMode; import java.sql.Timestamp; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; import static com.thoughtworks.go.plugin.access.notification.v2.StageConverter.DATE_PATTERN; import static java.util.Arrays.asList; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; import static org.junit.Assert.fail; public class JsonMessageHandler2_0_Test { private JsonMessageHandler2_0 messageHandler; @Before public void setUp() throws Exception { messageHandler = new JsonMessageHandler2_0(); } @Test public void shouldBuildNotificationsInterestedInFromResponseBody() throws Exception { String responseBody = "{notifications=[\"pipeline-status\",\"stage-status\"]}"; List<String> notificationsInterestedIn = messageHandler.responseMessageForNotificationsInterestedIn(responseBody); assertThat(notificationsInterestedIn, is(Arrays.asList("pipeline-status", "stage-status"))); } @Test public void shouldValidateIncorrectJsonResponseForNotificationsInterestedIn() { assertThat(errorMessageForNotificationsInterestedIn("{\"notifications\":{}}"), is("Unable to de-serialize json response. 'notifications' should be of type list of string")); assertThat(errorMessageForNotificationsInterestedIn("{\"notifications\":[{},{}]}"), is("Unable to de-serialize json response. Notification 'name' should be of type string")); } @Test public void shouldBuildSuccessResultFromNotify() throws Exception { String responseBody = "{\"status\":\"success\",messages=[\"message-one\",\"message-two\"]}"; Result result = messageHandler.responseMessageForNotify(responseBody); assertSuccessResult(result, asList("message-one", "message-two")); } @Test public void shouldBuildFailureResultFromNotify() throws Exception { String responseBody = "{\"status\":\"failure\",messages=[\"message-one\",\"message-two\"]}"; Result result = messageHandler.responseMessageForNotify(responseBody); assertFailureResult(result, asList("message-one", "message-two")); } @Test public void shouldHandleNullMessagesForNotify() throws Exception { assertSuccessResult(messageHandler.responseMessageForNotify("{\"status\":\"success\"}"), new ArrayList<>()); assertFailureResult(messageHandler.responseMessageForNotify("{\"status\":\"failure\"}"), new ArrayList<>()); } @Test public void shouldValidateIncorrectJsonForResult() { assertThat(errorMessageForNotifyResult(""), is("Unable to de-serialize json response. Empty response body")); assertThat(errorMessageForNotifyResult("[{\"result\":\"success\"}]"), is("Unable to de-serialize json response. Notify result should be returned as map, with key represented as string and messages represented as list")); assertThat(errorMessageForNotifyResult("{\"status\":true}"), is("Unable to de-serialize json response. Notify result 'status' should be of type string")); assertThat(errorMessageForNotifyResult("{\"result\":true}"), is("Unable to de-serialize json response. Notify result 'status' is a required field")); assertThat(errorMessageForNotifyResult("{\"status\":\"success\",\"messages\":{}}"), is("Unable to de-serialize json response. Notify result 'messages' should be of type list of string")); assertThat(errorMessageForNotifyResult("{\"status\":\"success\",\"messages\":[{},{}]}"), is("Unable to de-serialize json response. Notify result 'message' should be of type string")); } @Test public void shouldConstructTheStageNotificationRequest() throws Exception { Pipeline pipeline = createPipeline(); String gitModifiedTime = new SimpleDateFormat(DATE_PATTERN).format(pipeline.getBuildCause().getMaterialRevisions().getMaterialRevision(0).getLatestModification().getModifiedTime()); String hgModifiedTime = new SimpleDateFormat(DATE_PATTERN).format(pipeline.getBuildCause().getMaterialRevisions().getMaterialRevision(1).getLatestModification().getModifiedTime()); String svnModifiedTime = new SimpleDateFormat(DATE_PATTERN).format(pipeline.getBuildCause().getMaterialRevisions().getMaterialRevision(2).getLatestModification().getModifiedTime()); String tfsModifiedTime = new SimpleDateFormat(DATE_PATTERN).format(pipeline.getBuildCause().getMaterialRevisions().getMaterialRevision(3).getLatestModification().getModifiedTime()); String p4ModifiedTime = new SimpleDateFormat(DATE_PATTERN).format(pipeline.getBuildCause().getMaterialRevisions().getMaterialRevision(4).getLatestModification().getModifiedTime()); String dependencyModifiedTime = new SimpleDateFormat(DATE_PATTERN).format(pipeline.getBuildCause().getMaterialRevisions().getMaterialRevision(5).getLatestModification().getModifiedTime()); String packageMaterialModifiedTime = new SimpleDateFormat(DATE_PATTERN).format(pipeline.getBuildCause().getMaterialRevisions().getMaterialRevision(6).getLatestModification().getModifiedTime()); String pluggableScmModifiedTime = new SimpleDateFormat(DATE_PATTERN).format(pipeline.getBuildCause().getMaterialRevisions().getMaterialRevision(7).getLatestModification().getModifiedTime()); String expected = "{\n" + "\t\"pipeline\": {\n" + "\t\t\"name\": \"pipeline-name\",\n" + "\t\t\"label\": \"LABEL-1\",\n" + "\t\t\"counter\": \"1\",\n" + "\t\t\"group\": \"pipeline-group\",\n" + "\t\t\"build-cause\": [{\n" + "\t\t\t\"material\": {\n" + "\t\t\t\t\"git-configuration\": {\n" + "\t\t\t\t\t\"shallow-clone\": false,\n" + "\t\t\t\t\t\"branch\": \"branch\",\n" + "\t\t\t\t\t\"url\": \"http://user:******@gitrepo.com\"\n" + "\t\t\t\t},\n" + "\t\t\t\t\"type\": \"git\"\n" + "\t\t\t},\n" + "\t\t\t\"changed\": true,\n" + "\t\t\t\"modifications\": [{\n" + "\t\t\t\t\"revision\": \"1\",\n" + "\t\t\t\t\"modified-time\": \""+ gitModifiedTime +"\",\n" + "\t\t\t\t\"data\": {}\n" + "\t\t\t}]\n" + "\t\t}, {\n" + "\t\t\t\"material\": {\n" + "\t\t\t\t\"type\": \"mercurial\",\n" + "\t\t\t\t\"mercurial-configuration\": {\n" + "\t\t\t\t\t\"url\": \"http://user:******@hgrepo.com\"\n" + "\t\t\t\t}\n" + "\t\t\t},\n" + "\t\t\t\"changed\": true,\n" + "\t\t\t\"modifications\": [{\n" + "\t\t\t\t\"revision\": \"1\",\n" + "\t\t\t\t\"modified-time\": \""+hgModifiedTime+"\",\n" + "\t\t\t\t\"data\": {}\n" + "\t\t\t}]\n" + "\t\t}, {\n" + "\t\t\t\"material\": {\n" + "\t\t\t\t\"svn-configuration\": {\n" + "\t\t\t\t\t\"check-externals\": false,\n" + "\t\t\t\t\t\"url\": \"http://user:******@svnrepo.com\",\n" + "\t\t\t\t\t\"username\": \"username\"\n" + "\t\t\t\t},\n" + "\t\t\t\t\"type\": \"svn\"\n" + "\t\t\t},\n" + "\t\t\t\"changed\": true,\n" + "\t\t\t\"modifications\": [{\n" + "\t\t\t\t\"revision\": \"1\",\n" + "\t\t\t\t\"modified-time\": \""+svnModifiedTime+"\",\n" + "\t\t\t\t\"data\": {}\n" + "\t\t\t}]\n" + "\t\t}, {\n" + "\t\t\t\"material\": {\n" + "\t\t\t\t\"type\": \"tfs\",\n" + "\t\t\t\t\"tfs-configuration\": {\n" + "\t\t\t\t\t\"domain\": \"domain\",\n" + "\t\t\t\t\t\"project-path\": \"project-path\",\n" + "\t\t\t\t\t\"url\": \"http://user:******@tfsrepo.com\",\n" + "\t\t\t\t\t\"username\": \"username\"\n" + "\t\t\t\t}\n" + "\t\t\t},\n" + "\t\t\t\"changed\": true,\n" + "\t\t\t\"modifications\": [{\n" + "\t\t\t\t\"revision\": \"1\",\n" + "\t\t\t\t\"modified-time\": \""+tfsModifiedTime+"\",\n" + "\t\t\t\t\"data\": {}\n" + "\t\t\t}]\n" + "\t\t}, {\n" + "\t\t\t\"material\": {\n" + "\t\t\t\t\"perforce-configuration\": {\n" + "\t\t\t\t\t\"view\": \"view\",\n" + "\t\t\t\t\t\"use-tickets\": false,\n" + "\t\t\t\t\t\"url\": \"127.0.0.1:1666\",\n" + "\t\t\t\t\t\"username\": \"username\"\n" + "\t\t\t\t},\n" + "\t\t\t\t\"type\": \"perforce\"\n" + "\t\t\t},\n" + "\t\t\t\"changed\": true,\n" + "\t\t\t\"modifications\": [{\n" + "\t\t\t\t\"revision\": \"1\",\n" + "\t\t\t\t\"modified-time\": \""+p4ModifiedTime+"\",\n" + "\t\t\t\t\"data\": {}\n" + "\t\t\t}]\n" + "\t\t}, {\n" + "\t\t\t\"material\": {\n" + "\t\t\t\t\"pipeline-configuration\": {\n" + "\t\t\t\t\t\"pipeline-name\": \"pipeline-name\",\n" + "\t\t\t\t\t\"stage-name\": \"stage-name\"\n" + "\t\t\t\t},\n" + "\t\t\t\t\"type\": \"pipeline\"\n" + "\t\t\t},\n" + "\t\t\t\"changed\": true,\n" + "\t\t\t\"modifications\": [{\n" + "\t\t\t\t\"revision\": \"pipeline-name/1/stage-name/1\",\n" + "\t\t\t\t\"modified-time\": \""+dependencyModifiedTime+"\",\n" + "\t\t\t\t\"data\": {}\n" + "\t\t\t}]\n" + "\t\t}, {\n" + "\t\t\t\"material\": {\n" + "\t\t\t\t\"plugin-id\": \"pluginid\",\n" + "\t\t\t\t\"package-configuration\": {\n" + "\t\t\t\t\t\"k3\": \"package-v1\"\n" + "\t\t\t\t},\n" + "\t\t\t\t\"repository-configuration\": {\n" + "\t\t\t\t\t\"k1\": \"repo-v1\"\n" + "\t\t\t\t},\n" + "\t\t\t\t\"type\": \"package\"\n" + "\t\t\t},\n" + "\t\t\t\"changed\": true,\n" + "\t\t\t\"modifications\": [{\n" + "\t\t\t\t\"revision\": \"1\",\n" + "\t\t\t\t\"modified-time\": \""+packageMaterialModifiedTime+"\",\n" + "\t\t\t\t\"data\": {}\n" + "\t\t\t}]\n" + "\t\t}, {\n" + "\t\t\t\"material\": {\n" + "\t\t\t\t\"plugin-id\": \"pluginid\",\n" + "\t\t\t\t\"scm-configuration\": {\n" + "\t\t\t\t\t\"k1\": \"v1\"\n" + "\t\t\t\t},\n" + "\t\t\t\t\"type\": \"scm\"\n" + "\t\t\t},\n" + "\t\t\t\"changed\": true,\n" + "\t\t\t\"modifications\": [{\n" + "\t\t\t\t\"revision\": \"1\",\n" + "\t\t\t\t\"modified-time\": \""+pluggableScmModifiedTime+"\",\n" + "\t\t\t\t\"data\": {}\n" + "\t\t\t}]\n" + "\t\t}],\n" + "\t\t\"stage\": {\n" + "\t\t\t\"name\": \"stage-name\",\n" + "\t\t\t\"counter\": \"1\",\n" + "\t\t\t\"approval-type\": \"success\",\n" + "\t\t\t\"approved-by\": \"changes\",\n" + "\t\t\t\"state\": \"Passed\",\n" + "\t\t\t\"result\": \"Passed\",\n" + "\t\t\t\"create-time\": \"2011-07-13T19:43:37.100Z\",\n" + "\t\t\t\"last-transition-time\": \"2011-07-13T19:43:37.100Z\",\n" + "\t\t\t\"jobs\": [{\n" + "\t\t\t\t\"name\": \"job-name\",\n" + "\t\t\t\t\"schedule-time\": \"2011-07-13T19:43:37.100Z\",\n" + "\t\t\t\t\"assign-time\": \"2011-07-13T19:43:37.100Z\",\n" + "\t\t\t\t\"complete-time\": \"2011-07-13T19:43:37.100Z\",\n" + "\t\t\t\t\"state\": \"Completed\",\n" + "\t\t\t\t\"result\": \"Passed\",\n" + "\t\t\t\t\"agent-uuid\": \"uuid\"\n" + "\t\t\t}]\n" + "\t\t}\n" + "\t}\n" + "}"; String request = messageHandler.requestMessageForNotify(new StageNotificationData(pipeline.getFirstStage(), pipeline.getBuildCause(), "pipeline-group")); JSONAssert.assertEquals(expected, request, JSONCompareMode.NON_EXTENSIBLE); } @Rule public ExpectedException thrown = ExpectedException.none(); @Test public void shouldThrowExceptionIfAnUnhandledObjectIsPassed(){ thrown.expectMessage(String.format("Converter for %s not supported", Pipeline.class.getCanonicalName())); messageHandler.requestMessageForNotify(new Pipeline()); } private void assertSuccessResult(Result result, List<String> messages) { assertThat(result.isSuccessful(), is(true)); assertThat(result.getMessages(), is(messages)); } private void assertFailureResult(Result result, List<String> messages) { assertThat(result.isSuccessful(), is(false)); assertThat(result.getMessages(), is(messages)); } private String errorMessageForNotificationsInterestedIn(String message) { try { messageHandler.responseMessageForNotificationsInterestedIn(message); fail("should have thrown exception"); } catch (Exception e) { return e.getMessage(); } return null; } private String errorMessageForNotifyResult(String message) { try { messageHandler.toResult(message); fail("should have thrown exception"); } catch (Exception e) { return e.getMessage(); } return null; } private Date getFixedDate() throws Exception { return new SimpleDateFormat(DATE_PATTERN).parse("2011-07-13T19:43:37.100Z"); } private Pipeline createPipeline() throws Exception { Pipeline pipeline = PipelineMother.pipelineWithAllTypesOfMaterials("pipeline-name", "stage-name", "job-name", "1"); List<MaterialRevision> materialRevisions = pipeline.getMaterialRevisions().getRevisions(); PackageDefinition packageDefinition = ((PackageMaterial) materialRevisions.get(6).getMaterial()).getPackageDefinition(); packageDefinition.getRepository().getConfiguration().get(1).handleSecureValueConfiguration(true); packageDefinition.getConfiguration().addNewConfigurationWithValue("k4", "package-v2", false); packageDefinition.getConfiguration().get(1).handleSecureValueConfiguration(true); SCM scm = ((PluggableSCMMaterial) materialRevisions.get(7).getMaterial()).getScmConfig(); scm.getConfiguration().get(1).handleSecureValueConfiguration(true); Stage stage = pipeline.getFirstStage(); stage.setId(1L); stage.setPipelineId(1L); stage.setCreatedTime(new Timestamp(getFixedDate().getTime())); stage.setLastTransitionedTime(new Timestamp(getFixedDate().getTime())); JobInstance job = stage.getJobInstances().get(0); job.setScheduledDate(getFixedDate()); job.getTransition(JobState.Assigned).setStateChangeTime(getFixedDate()); job.getTransition(JobState.Completed).setStateChangeTime(getFixedDate()); return pipeline; } }