diff --git a/components-starter/camel-jira-starter/pom.xml b/components-starter/camel-jira-starter/pom.xml
index 775b48d1e32..7714147bbd7 100644
--- a/components-starter/camel-jira-starter/pom.xml
+++ b/components-starter/camel-jira-starter/pom.xml
@@ -47,6 +47,29 @@
+
+
+ org.apache.camel
+ camel-test-spring-junit5
+
+
+ org.apache.camel
+ camel-spring-xml
+
+
+ test
+
+
+ org.awaitility
+ awaitility
+ test
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ ${spring-boot-version}
+ test
+
org.apache.camel.springboot
diff --git a/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/AddCommentProducerTest.java b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/AddCommentProducerTest.java
new file mode 100644
index 00000000000..80b8dd8ddad
--- /dev/null
+++ b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/AddCommentProducerTest.java
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.component.jira.springboot.test;
+
+
+import static org.apache.camel.component.jira.JiraConstants.ISSUE_KEY;
+import static org.apache.camel.component.jira.JiraConstants.JIRA_REST_CLIENT_FACTORY;
+import static org.apache.camel.component.jira.springboot.test.JiraTestConstants.JIRA_CREDENTIALS;
+import static org.apache.camel.component.jira.springboot.test.Utils.createIssueWithComments;
+import static org.apache.camel.component.jira.springboot.test.Utils.newComment;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+
+import com.atlassian.jira.rest.client.api.IssueRestClient;
+import com.atlassian.jira.rest.client.api.JiraRestClient;
+import com.atlassian.jira.rest.client.api.JiraRestClientFactory;
+import com.atlassian.jira.rest.client.api.domain.Comment;
+import com.atlassian.jira.rest.client.api.domain.Issue;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.spring.boot.CamelContextConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.mockito.stubbing.Answer;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import io.atlassian.util.concurrent.Promises;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.annotation.DirtiesContext.ClassMode;
+
+
+
+@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
+@CamelSpringBootTest
+@SpringBootTest(
+ classes = {
+ CamelAutoConfiguration.class,
+ AddCommentProducerTest.class,
+ AddCommentProducerTest.TestConfiguration.class
+ }
+)
+
+public class AddCommentProducerTest {
+
+ @Autowired
+ private CamelContext camelContext;
+
+
+
+ @Autowired
+ @Produce("direct:start")
+ ProducerTemplate template;
+
+ @EndpointInject("mock:result")
+ MockEndpoint mockResult;
+
+ static JiraRestClient jiraClient;
+
+ static JiraRestClientFactory jiraRestClientFactory;
+
+ static IssueRestClient issueRestClient;
+
+ static Issue backendIssue;
+
+ static Issue issue;
+
+ static String comment;
+
+ @Bean
+ CamelContextConfiguration contextConfiguration() {
+ return new CamelContextConfiguration() {
+ @Override
+ public void beforeApplicationStart(CamelContext context) {
+ //get chance to mock camelContext/Registry
+ jiraRestClientFactory = mock(JiraRestClientFactory.class);
+ jiraClient = mock(JiraRestClient.class);
+ issueRestClient = mock(IssueRestClient.class);
+ lenient().when(jiraRestClientFactory.createWithBasicHttpAuthentication(any(), any(), any())).thenReturn(jiraClient);
+ lenient().when(jiraClient.getIssueClient()).thenReturn(issueRestClient);
+
+ when(issueRestClient.addComment(any(), any())).then((Answer) inv -> {
+ Collection comments = new ArrayList<>();
+ for (Comment c : backendIssue.getComments()) {
+ comments.add(c);
+ }
+ comments.add(newComment(backendIssue.getId(), comments.size() + 1, comment));
+ backendIssue = createIssueWithComments(backendIssue.getId(), comments);
+ return null;
+ });
+ backendIssue = createIssueWithComments(1, 1);
+ when(issueRestClient.getIssue(any())).then(inv -> Promises.promise(backendIssue));
+
+ camelContext.getRegistry().bind(JIRA_REST_CLIENT_FACTORY, jiraRestClientFactory);
+ }
+
+ @Override
+ public void afterApplicationStart(CamelContext camelContext) {
+ //do nothing here
+ }
+ };
+ }
+
+ @Test
+ public void verifyLastComment() throws InterruptedException {
+ template.sendBodyAndHeader(comment, ISSUE_KEY, backendIssue.getKey());
+
+ Issue issue = issueRestClient.getIssue(backendIssue.getKey()).claim();
+ String lastComment = null;
+ for (Comment c : issue.getComments()) {
+ lastComment = c.getBody();
+ }
+ assertEquals(comment, lastComment);
+ mockResult.expectedMessageCount(1);
+ mockResult.assertIsSatisfied();
+ }
+
+
+ @Configuration
+ public class TestConfiguration {
+
+
+
+ @Bean
+ public RouteBuilder routeBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ comment = "A new test comment " + new Date();
+ from("direct:start")
+ .to("jira://addComment?jiraUrl=" + JIRA_CREDENTIALS)
+ .to(mockResult);
+ }
+ };
+ }
+
+
+ }
+
+
+
+
+}
diff --git a/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/AddIssueLinkProducerTest.java b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/AddIssueLinkProducerTest.java
new file mode 100644
index 00000000000..177fb6d773b
--- /dev/null
+++ b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/AddIssueLinkProducerTest.java
@@ -0,0 +1,272 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.component.jira.springboot.test;
+
+
+import static org.apache.camel.component.jira.JiraConstants.CHILD_ISSUE_KEY;
+import static org.apache.camel.component.jira.JiraConstants.JIRA_REST_CLIENT_FACTORY;
+import static org.apache.camel.component.jira.JiraConstants.LINK_TYPE;
+import static org.apache.camel.component.jira.JiraConstants.PARENT_ISSUE_KEY;
+import static org.apache.camel.component.jira.springboot.test.JiraTestConstants.JIRA_CREDENTIALS;
+import static org.apache.camel.component.jira.springboot.test.Utils.createIssue;
+import static org.apache.camel.component.jira.springboot.test.Utils.createIssueWithLinks;
+import static org.apache.camel.component.jira.springboot.test.Utils.newIssueLink;
+import static org.apache.camel.test.junit5.TestSupport.assertIsInstanceOf;
+import static org.apache.camel.test.junit5.TestSupport.assertStringContains;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.atlassian.jira.rest.client.api.IssueRestClient;
+import com.atlassian.jira.rest.client.api.JiraRestClient;
+import com.atlassian.jira.rest.client.api.JiraRestClientFactory;
+import com.atlassian.jira.rest.client.api.domain.Issue;
+import com.atlassian.jira.rest.client.api.domain.IssueLink;
+import com.atlassian.jira.rest.client.api.domain.input.LinkIssuesInput;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.spring.boot.CamelContextConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+import org.mockito.stubbing.Answer;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.fail;
+
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.annotation.DirtiesContext.ClassMode;
+
+
+
+@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
+@CamelSpringBootTest
+@SpringBootTest(
+ classes = {
+ CamelAutoConfiguration.class,
+ AddIssueLinkProducerTest.class,
+ AddIssueLinkProducerTest.TestConfiguration.class
+ }
+)
+
+public class AddIssueLinkProducerTest {
+
+ @Autowired
+ private CamelContext camelContext;
+
+
+
+ @Autowired
+ @Produce("direct:start")
+ ProducerTemplate template;
+
+ @EndpointInject("mock:result")
+ MockEndpoint mockResult;
+
+ static JiraRestClient jiraClient;
+
+ static JiraRestClientFactory jiraRestClientFactory;
+
+ static IssueRestClient issueRestClient;
+
+ static Issue parentIssue;
+ static Issue childIssue;
+
+ @Bean
+ CamelContextConfiguration contextConfiguration() {
+ return new CamelContextConfiguration() {
+ @Override
+ public void beforeApplicationStart(CamelContext context) {
+ //get chance to mock camelContext/Registry
+ jiraRestClientFactory = mock(JiraRestClientFactory.class);
+ jiraClient = mock(JiraRestClient.class);
+ issueRestClient = mock(IssueRestClient.class);
+ lenient().when(jiraRestClientFactory.createWithBasicHttpAuthentication(any(), any(), any())).thenReturn(jiraClient);
+ lenient().when(jiraClient.getIssueClient()).thenReturn(issueRestClient);
+
+ parentIssue = createIssue(1);
+ childIssue = createIssue(2);
+
+ camelContext.getRegistry().bind(JIRA_REST_CLIENT_FACTORY, jiraRestClientFactory);
+ }
+
+ @Override
+ public void afterApplicationStart(CamelContext camelContext) {
+ //do nothing here
+ }
+ };
+ }
+
+ @Test
+ public void testAddIssueLink() throws InterruptedException {
+ String comment = "A new test comment " + new Date();
+ String linkType = "Relates";
+ Map headers = new HashMap<>();
+ headers.put(PARENT_ISSUE_KEY, parentIssue.getKey());
+ headers.put(CHILD_ISSUE_KEY, childIssue.getKey());
+ headers.put(LINK_TYPE, linkType);
+
+ when(issueRestClient.linkIssue(any(LinkIssuesInput.class)))
+ .then((Answer) inv -> {
+ Collection links = new ArrayList<>();
+ links.add(newIssueLink(childIssue.getId(), 1, comment));
+ parentIssue = createIssueWithLinks(parentIssue.getId(), links);
+ return null;
+ });
+
+ template.sendBodyAndHeaders(comment, headers);
+
+ mockResult.expectedMessageCount(1);
+ mockResult.assertIsSatisfied();
+
+ verify(issueRestClient).linkIssue(any(LinkIssuesInput.class));
+ }
+
+ @Test
+ public void testAddIssueLinkNoComment() throws InterruptedException {
+ String linkType = "Relates";
+ Map headers = new HashMap<>();
+ headers.put(PARENT_ISSUE_KEY, parentIssue.getKey());
+ headers.put(CHILD_ISSUE_KEY, childIssue.getKey());
+ headers.put(LINK_TYPE, linkType);
+
+ when(issueRestClient.linkIssue(any(LinkIssuesInput.class)))
+ .then((Answer) inv -> {
+ Collection links = new ArrayList<>();
+ links.add(newIssueLink(childIssue.getId(), 1, null));
+ parentIssue = createIssueWithLinks(parentIssue.getId(), links);
+ return null;
+ });
+
+ template.sendBodyAndHeaders(null, headers);
+
+ mockResult.expectedMessageCount(1);
+ mockResult.assertIsSatisfied();
+
+ verify(issueRestClient).linkIssue(any(LinkIssuesInput.class));
+ }
+
+ @Test
+ public void testAddIssueLinkMissingParentIssueKey() throws InterruptedException {
+ String comment = "A new test comment " + new Date();
+ String linkType = "Relates";
+ Map headers = new HashMap<>();
+ headers.put(CHILD_ISSUE_KEY, childIssue.getKey());
+ headers.put(LINK_TYPE, linkType);
+
+ try {
+ template.sendBodyAndHeaders(comment, headers);
+ fail("Should have thrown an exception");
+ } catch (CamelExecutionException e) {
+ IllegalArgumentException cause = assertIsInstanceOf(IllegalArgumentException.class, e.getCause());
+ assertStringContains(cause.getMessage(), PARENT_ISSUE_KEY);
+ }
+
+ mockResult.expectedMessageCount(0);
+ mockResult.assertIsSatisfied();
+
+ verify(issueRestClient, never()).linkIssue(any(LinkIssuesInput.class));
+ }
+
+ @Test
+ public void testAddIssueLinkMissingChildIssueKey() throws InterruptedException {
+ String comment = "A new test comment " + new Date();
+ String linkType = "Relates";
+ Map headers = new HashMap<>();
+ headers.put(PARENT_ISSUE_KEY, parentIssue.getKey());
+ headers.put(LINK_TYPE, linkType);
+
+ try {
+ template.sendBodyAndHeaders(comment, headers);
+ fail("Should have thrown an exception");
+ } catch (CamelExecutionException e) {
+ IllegalArgumentException cause = assertIsInstanceOf(IllegalArgumentException.class, e.getCause());
+ assertStringContains(cause.getMessage(), CHILD_ISSUE_KEY);
+ }
+
+ mockResult.expectedMessageCount(0);
+ mockResult.assertIsSatisfied();
+
+ verify(issueRestClient, never()).linkIssue(any(LinkIssuesInput.class));
+ }
+
+ @Test
+ public void testAddIssueLinkMissingLinkType() throws InterruptedException {
+ String comment = "A new test comment " + new Date();
+ Map headers = new HashMap<>();
+ headers.put(PARENT_ISSUE_KEY, parentIssue.getKey());
+ headers.put(CHILD_ISSUE_KEY, childIssue.getKey());
+
+ try {
+ template.sendBodyAndHeaders(comment, headers);
+ fail("Should have thrown an exception");
+ } catch (CamelExecutionException e) {
+ IllegalArgumentException cause = assertIsInstanceOf(IllegalArgumentException.class, e.getCause());
+ assertStringContains(cause.getMessage(), LINK_TYPE);
+ }
+
+ mockResult.expectedMessageCount(0);
+ mockResult.assertIsSatisfied();
+
+ verify(issueRestClient, never()).linkIssue(any(LinkIssuesInput.class));
+ }
+
+
+ @Configuration
+ public class TestConfiguration {
+
+
+
+ @Bean
+ public RouteBuilder routeBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:start")
+ .to("jira://addIssueLink?jiraUrl=" + JIRA_CREDENTIALS)
+ .to(mockResult);
+ }
+ };
+ }
+
+
+ }
+
+
+
+
+}
diff --git a/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/AddIssueProducerTest.java b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/AddIssueProducerTest.java
new file mode 100644
index 00000000000..b7449d3f214
--- /dev/null
+++ b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/AddIssueProducerTest.java
@@ -0,0 +1,239 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.component.jira.springboot.test;
+
+
+import static org.apache.camel.component.jira.JiraConstants.ISSUE_ASSIGNEE;
+import static org.apache.camel.component.jira.JiraConstants.ISSUE_PRIORITY_ID;
+import static org.apache.camel.component.jira.JiraConstants.ISSUE_PRIORITY_NAME;
+import static org.apache.camel.component.jira.JiraConstants.ISSUE_PROJECT_KEY;
+import static org.apache.camel.component.jira.JiraConstants.ISSUE_SUMMARY;
+import static org.apache.camel.component.jira.JiraConstants.ISSUE_TYPE_ID;
+import static org.apache.camel.component.jira.JiraConstants.ISSUE_TYPE_NAME;
+import static org.apache.camel.component.jira.JiraConstants.JIRA_REST_CLIENT_FACTORY;
+import static org.apache.camel.component.jira.springboot.test.JiraTestConstants.JIRA_CREDENTIALS;
+import static org.apache.camel.component.jira.springboot.test.JiraTestConstants.KEY;
+import static org.apache.camel.component.jira.springboot.test.Utils.createIssue;
+import static org.apache.camel.component.jira.springboot.test.Utils.userAssignee;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.atlassian.jira.rest.client.api.IssueRestClient;
+import com.atlassian.jira.rest.client.api.JiraRestClient;
+import com.atlassian.jira.rest.client.api.JiraRestClientFactory;
+import com.atlassian.jira.rest.client.api.MetadataRestClient;
+import com.atlassian.jira.rest.client.api.domain.BasicIssue;
+import com.atlassian.jira.rest.client.api.domain.BasicPriority;
+import com.atlassian.jira.rest.client.api.domain.Issue;
+import com.atlassian.jira.rest.client.api.domain.IssueType;
+import com.atlassian.jira.rest.client.api.domain.Priority;
+import com.atlassian.jira.rest.client.api.domain.input.ComplexIssueInputFieldValue;
+import com.atlassian.jira.rest.client.api.domain.input.IssueInput;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.spring.boot.CamelContextConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import io.atlassian.util.concurrent.Promise;
+import io.atlassian.util.concurrent.Promises;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.annotation.DirtiesContext.ClassMode;
+
+
+
+@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
+@CamelSpringBootTest
+@SpringBootTest(
+ classes = {
+ CamelAutoConfiguration.class,
+ AddIssueProducerTest.class,
+ AddIssueProducerTest.TestConfiguration.class
+ }
+)
+
+public class AddIssueProducerTest {
+
+ @Autowired
+ private CamelContext camelContext;
+
+
+
+ @Autowired
+ @Produce("direct:start")
+ ProducerTemplate template;
+
+ @EndpointInject("mock:result")
+ MockEndpoint mockResult;
+
+ static JiraRestClient jiraClient;
+
+ static JiraRestClientFactory jiraRestClientFactory;
+
+ static IssueRestClient issueRestClient;
+
+ static Issue backendIssue;
+
+ static MetadataRestClient metadataRestClient;
+
+ @Bean
+ CamelContextConfiguration contextConfiguration() {
+ return new CamelContextConfiguration() {
+ @Override
+ public void beforeApplicationStart(CamelContext context) {
+ //get chance to mock camelContext/Registry
+ jiraRestClientFactory = mock(JiraRestClientFactory.class);
+ jiraClient = mock(JiraRestClient.class);
+ issueRestClient = mock(IssueRestClient.class);
+ metadataRestClient = mock(MetadataRestClient.class);
+ lenient().when(jiraRestClientFactory.createWithBasicHttpAuthentication(any(), any(), any())).thenReturn(jiraClient);
+ lenient().when(jiraClient.getIssueClient()).thenReturn(issueRestClient);
+
+ lenient().when(jiraClient.getMetadataClient()).thenReturn(metadataRestClient);
+
+ Map issueTypes = new HashMap<>();
+ issueTypes.put(1, new IssueType(null, 1L, "Bug", false, null, null));
+ issueTypes.put(2, new IssueType(null, 2L, "Task", false, null, null));
+ Promise> promiseIssueTypes = Promises.promise(issueTypes.values());
+ lenient().when(metadataRestClient.getIssueTypes()).thenReturn(promiseIssueTypes);
+
+ Map issuePriorities = new HashMap<>();
+ issuePriorities.put(1, new Priority(null, 1L, "High", null, null, null));
+ issuePriorities.put(2, new Priority(null, 2L, "Low", null, null, null));
+ Promise> promisePriorities = Promises.promise(issuePriorities.values());
+ lenient().when(metadataRestClient.getPriorities()).thenReturn(promisePriorities);
+
+ lenient().when(issueRestClient.createIssue(any(IssueInput.class))).then(inv -> {
+ IssueInput issueInput = inv.getArgument(0);
+ String summary = (String) issueInput.getField("summary").getValue();
+ Integer issueTypeId = Integer.parseInt(getValue(issueInput, "issuetype", "id"));
+ IssueType issueType = issueTypes.get(issueTypeId);
+ String project = getValue(issueInput, "project", "key");
+ String description = (String) issueInput.getField("description").getValue();
+ Integer priorityId = Integer.parseInt(getValue(issueInput, "priority", "id"));
+ BasicPriority priority = issuePriorities.get(priorityId);
+ backendIssue = createIssue(11L, summary, project, issueType, description, priority, userAssignee, null, null);
+ BasicIssue basicIssue = new BasicIssue(backendIssue.getSelf(), backendIssue.getKey(), backendIssue.getId());
+ return Promises.promise(basicIssue);
+ });
+ lenient().when(issueRestClient.getIssue(any())).then(inv -> Promises.promise(backendIssue));
+
+ camelContext.getRegistry().bind(JIRA_REST_CLIENT_FACTORY, jiraRestClientFactory);
+ }
+
+ @Override
+ public void afterApplicationStart(CamelContext camelContext) {
+ //do nothing here
+ }
+ };
+ }
+
+ private String getValue(IssueInput issueInput, String field, String key) {
+ ComplexIssueInputFieldValue complexField = (ComplexIssueInputFieldValue) issueInput.getField(field).getValue();
+ return (String) complexField.getValuesMap().get(key);
+ }
+
+ @Test
+ public void verifyIssueAdded() throws InterruptedException {
+
+ Map headers = new HashMap<>();
+ headers.put(ISSUE_PROJECT_KEY, KEY);
+ headers.put(ISSUE_TYPE_NAME, "Task");
+ headers.put(ISSUE_SUMMARY, "Demo Bug jira " + (new Date()));
+ headers.put(ISSUE_PRIORITY_NAME, "Low");
+ headers.put(ISSUE_ASSIGNEE, "tom");
+
+ template.sendBodyAndHeaders("Minha descrição jira " + (new Date()), headers);
+
+ Issue issue = issueRestClient.getIssue(backendIssue.getKey()).claim();
+ assertEquals(backendIssue, issue);
+ assertEquals(backendIssue.getIssueType(), issue.getIssueType());
+ assertEquals(backendIssue.getPriority(), issue.getPriority());
+ assertEquals(backendIssue.getSummary(), issue.getSummary());
+ assertEquals(backendIssue.getProject(), issue.getProject());
+ assertEquals(backendIssue.getDescription(), issue.getDescription());
+ mockResult.expectedMessageCount(1);
+ mockResult.assertIsSatisfied();
+ }
+
+ @Test
+ public void verifyIssueAddedWithIds() throws InterruptedException {
+
+ Map headers = new HashMap<>();
+ headers.put(ISSUE_PROJECT_KEY, KEY);
+ headers.put(ISSUE_TYPE_ID, 2);
+ headers.put(ISSUE_SUMMARY, "Demo Bug jira " + (new Date()));
+ headers.put(ISSUE_PRIORITY_ID, 1);
+
+ template.sendBodyAndHeaders("Minha descrição jira " + (new Date()), headers);
+
+ Issue issue = issueRestClient.getIssue(backendIssue.getKey()).claim();
+ assertEquals(backendIssue, issue);
+ assertEquals(backendIssue.getIssueType(), issue.getIssueType());
+ assertEquals(backendIssue.getPriority(), issue.getPriority());
+ assertEquals(backendIssue.getSummary(), issue.getSummary());
+ assertEquals(backendIssue.getProject(), issue.getProject());
+ assertEquals(backendIssue.getDescription(), issue.getDescription());
+ mockResult.expectedMessageCount(1);
+ mockResult.assertIsSatisfied();
+ }
+
+
+
+ @Configuration
+ public class TestConfiguration {
+
+
+
+ @Bean
+ public RouteBuilder routeBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:start")
+ .to("jira://addIssue?jiraUrl=" + JIRA_CREDENTIALS)
+ .to(mockResult);
+ }
+ };
+ }
+
+
+ }
+
+
+
+
+}
diff --git a/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/AddWorkLogProducerTest.java b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/AddWorkLogProducerTest.java
new file mode 100644
index 00000000000..712bbb62e26
--- /dev/null
+++ b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/AddWorkLogProducerTest.java
@@ -0,0 +1,251 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.component.jira.springboot.test;
+
+
+import static org.apache.camel.component.jira.JiraConstants.ISSUE_KEY;
+import static org.apache.camel.component.jira.JiraConstants.JIRA_REST_CLIENT_FACTORY;
+import static org.apache.camel.component.jira.JiraConstants.MINUTES_SPENT;
+import static org.apache.camel.component.jira.springboot.test.Utils.createIssueWithComments;
+import static org.apache.camel.component.jira.springboot.test.Utils.createIssueWithWorkLogs;
+import static org.apache.camel.component.jira.springboot.test.Utils.newWorkLog;
+import static org.apache.camel.test.junit5.TestSupport.assertIsInstanceOf;
+import static org.apache.camel.test.junit5.TestSupport.assertStringContains;
+import static org.apache.camel.component.jira.springboot.test.JiraTestConstants.JIRA_CREDENTIALS;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.atlassian.jira.rest.client.api.IssueRestClient;
+import com.atlassian.jira.rest.client.api.JiraRestClient;
+import com.atlassian.jira.rest.client.api.JiraRestClientFactory;
+import com.atlassian.jira.rest.client.api.domain.Issue;
+import com.atlassian.jira.rest.client.api.domain.Worklog;
+import com.atlassian.jira.rest.client.api.domain.input.WorklogInput;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.spring.boot.CamelContextConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.fail;
+
+import io.atlassian.util.concurrent.Promises;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.annotation.DirtiesContext.ClassMode;
+import org.mockito.stubbing.Answer;
+
+
+@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
+@CamelSpringBootTest
+@SpringBootTest(
+ classes = {
+ CamelAutoConfiguration.class,
+ AddWorkLogProducerTest.class,
+ AddWorkLogProducerTest.TestConfiguration.class
+ }
+)
+
+public class AddWorkLogProducerTest {
+
+ @Autowired
+ private CamelContext camelContext;
+
+
+
+ @Autowired
+ @Produce("direct:start")
+ ProducerTemplate template;
+
+ @EndpointInject("mock:result")
+ MockEndpoint mockResult;
+
+ static JiraRestClient jiraClient;
+
+ static JiraRestClientFactory jiraRestClientFactory;
+
+ static IssueRestClient issueRestClient;
+
+ static Issue backendIssue;
+
+
+
+ @Bean
+ CamelContextConfiguration contextConfiguration() {
+ return new CamelContextConfiguration() {
+ @Override
+ public void beforeApplicationStart(CamelContext context) {
+ //get chance to mock camelContext/Registry
+ jiraRestClientFactory = mock(JiraRestClientFactory.class);
+ jiraClient = mock(JiraRestClient.class);
+ issueRestClient = mock(IssueRestClient.class);
+ lenient().when(jiraRestClientFactory.createWithBasicHttpAuthentication(any(), any(), any())).thenReturn(jiraClient);
+ lenient().when(jiraClient.getIssueClient()).thenReturn(issueRestClient);
+
+ backendIssue = createIssueWithComments(1, 1);
+ lenient().when(issueRestClient.getIssue(any())).then(inv -> Promises.promise(backendIssue));
+ camelContext.getRegistry().bind(JIRA_REST_CLIENT_FACTORY, jiraRestClientFactory);
+ }
+
+ @Override
+ public void afterApplicationStart(CamelContext camelContext) {
+ //do nothing here
+ }
+ };
+ }
+
+ @Test
+ public void testAddWorkLog() throws InterruptedException {
+ int minutesSpent = 10;
+ Map headers = new HashMap<>();
+ headers.put(ISSUE_KEY, backendIssue.getKey());
+ headers.put(MINUTES_SPENT, minutesSpent);
+ String comment = "A new test comment " + new Date();
+
+ when(issueRestClient.addWorklog(any(URI.class), any(WorklogInput.class)))
+ .then((Answer) inv -> {
+ Collection workLogs = new ArrayList<>();
+ workLogs.add(newWorkLog(backendIssue.getId(), minutesSpent, comment));
+ backendIssue = createIssueWithWorkLogs(backendIssue.getId(), workLogs);
+ return null;
+ });
+
+ template.sendBodyAndHeaders(comment, headers);
+
+ mockResult.expectedMessageCount(1);
+ mockResult.assertIsSatisfied();
+
+ verify(issueRestClient).getIssue(backendIssue.getKey());
+ verify(issueRestClient).addWorklog(eq(backendIssue.getWorklogUri()), any(WorklogInput.class));
+ }
+
+ @Test
+ public void testAddWorkLogMissingIssueKey() throws InterruptedException {
+ int minutesSpent = 3;
+ Map headers = new HashMap<>();
+ headers.put(MINUTES_SPENT, minutesSpent);
+ String comment = "A new test comment " + new Date();
+
+ try {
+ template.sendBodyAndHeaders(comment, headers);
+ fail("Should have thrown an exception");
+ } catch (CamelExecutionException e) {
+ IllegalArgumentException cause = assertIsInstanceOf(IllegalArgumentException.class, e.getCause());
+ assertStringContains(cause.getMessage(), ISSUE_KEY);
+ }
+ mockResult.reset();
+ mockResult.expectedMessageCount(0);
+ mockResult.assertIsSatisfied();
+
+ verify(issueRestClient, never()).getIssue(any(String.class));
+ verify(issueRestClient, never()).addWorklog(any(URI.class), any(WorklogInput.class));
+ }
+
+ @Test
+ public void testAddWorkLogMissingMinutesSpent() throws InterruptedException {
+
+ Map headers = new HashMap<>();
+ headers.put(ISSUE_KEY, backendIssue.getKey());
+ String comment = "A new test comment " + new Date();
+
+ try {
+ template.sendBodyAndHeaders(comment, headers);
+ fail("Should have thrown an exception");
+ } catch (CamelExecutionException e) {
+ IllegalArgumentException cause = assertIsInstanceOf(IllegalArgumentException.class, e.getCause());
+ assertStringContains(cause.getMessage(), MINUTES_SPENT);
+ }
+
+ mockResult.reset();
+ mockResult.expectedMessageCount(0);
+ mockResult.assertIsSatisfied();
+
+ verify(issueRestClient, never()).getIssue(any(String.class));
+ verify(issueRestClient, never()).addWorklog(any(URI.class), any(WorklogInput.class));
+ }
+
+ @Test
+ public void testAddWorkLogMissingComment() throws InterruptedException {
+ int minutesSpent = 60;
+ Map headers = new HashMap<>();
+ headers.put(ISSUE_KEY, backendIssue.getKey());
+ headers.put(MINUTES_SPENT, minutesSpent);
+
+ try {
+ template.sendBodyAndHeaders(null, headers);
+ fail("Should have thrown an exception");
+ } catch (CamelExecutionException e) {
+ IllegalArgumentException cause = assertIsInstanceOf(IllegalArgumentException.class, e.getCause());
+ assertStringContains(cause.getMessage(), "Missing exchange body");
+ }
+ mockResult.reset();
+ mockResult.expectedMessageCount(0);
+ mockResult.assertIsSatisfied();
+
+ verify(issueRestClient, never()).getIssue(any(String.class));
+ verify(issueRestClient, never()).addWorklog(any(URI.class), any(WorklogInput.class));
+ }
+
+
+ @Configuration
+ public class TestConfiguration {
+
+
+
+ @Bean
+ public RouteBuilder routeBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ from("direct:start")
+ .to("jira://addWorkLog?jiraUrl=" + JIRA_CREDENTIALS)
+ .to(mockResult);
+ }
+ };
+ }
+
+
+ }
+
+
+
+
+}
diff --git a/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/AttachFileProducerTest.java b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/AttachFileProducerTest.java
new file mode 100644
index 00000000000..7b15884fe87
--- /dev/null
+++ b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/AttachFileProducerTest.java
@@ -0,0 +1,196 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.component.jira.springboot.test;
+
+
+import static org.apache.camel.component.jira.JiraConstants.ISSUE_KEY;
+import static org.apache.camel.component.jira.JiraConstants.JIRA_REST_CLIENT_FACTORY;
+import static org.apache.camel.component.jira.springboot.test.JiraTestConstants.JIRA_CREDENTIALS;
+import static org.apache.camel.component.jira.springboot.test.JiraTestConstants.KEY;
+import static org.apache.camel.component.jira.springboot.test.Utils.createIssue;
+import static org.apache.camel.component.jira.springboot.test.Utils.createIssueWithAttachment;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import com.atlassian.jira.rest.client.api.IssueRestClient;
+import com.atlassian.jira.rest.client.api.JiraRestClient;
+import com.atlassian.jira.rest.client.api.JiraRestClientFactory;
+import com.atlassian.jira.rest.client.api.domain.Attachment;
+import com.atlassian.jira.rest.client.api.domain.Issue;
+
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.spring.boot.CamelContextConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import io.atlassian.util.concurrent.Promises;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.annotation.DirtiesContext.ClassMode;
+
+
+
+@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
+@CamelSpringBootTest
+@SpringBootTest(
+ classes = {
+ CamelAutoConfiguration.class,
+ AttachFileProducerTest.class,
+ AttachFileProducerTest.TestConfiguration.class
+ }
+)
+
+public class AttachFileProducerTest {
+
+ @Autowired
+ private CamelContext camelContext;
+
+
+
+ @Autowired
+ @Produce("direct:start")
+ ProducerTemplate template;
+
+ @EndpointInject("mock:result")
+ MockEndpoint mockResult;
+
+ static JiraRestClient jiraClient;
+
+ static JiraRestClientFactory jiraRestClientFactory;
+
+ static IssueRestClient issueRestClient;
+
+ static File attachedFile;
+
+ static Issue issue;
+
+ @Bean
+ CamelContextConfiguration contextConfiguration() {
+ return new CamelContextConfiguration() {
+ @Override
+ public void beforeApplicationStart(CamelContext context) {
+ //get chance to mock camelContext/Registry
+ jiraRestClientFactory = mock(JiraRestClientFactory.class);
+ jiraClient = mock(JiraRestClient.class);
+ issueRestClient = mock(IssueRestClient.class);
+ lenient().when(jiraRestClientFactory.createWithBasicHttpAuthentication(any(), any(), any())).thenReturn(jiraClient);
+ lenient().when(jiraClient.getIssueClient()).thenReturn(issueRestClient);
+
+ when(issueRestClient.getIssue(any())).then(inv -> {
+ if (issue == null) {
+ issue = createIssue(1);
+ }
+ return Promises.promise(issue);
+ });
+ when(issueRestClient.addAttachments(any(URI.class), any(File.class))).then(inv -> {
+ File attachedFileTmp = inv.getArgument(1);
+ // create a temp destiny file as the attached file is marked for removal on AttachFileProducer
+ attachedFile = File.createTempFile("camel-jira-test-", null);
+ Files.copy(attachedFileTmp.toPath(), attachedFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
+ attachedFile.deleteOnExit();
+ Collection attachments = new ArrayList<>();
+ attachments.add(new Attachment(
+ issue.getAttachmentsUri(), attachedFile.getName(), null, null,
+ Long.valueOf(attachedFile.length()).intValue(), null, null, null));
+ // re-create the issue with the attachment sent by the route
+ issue = createIssueWithAttachment(issue.getId(), issue.getSummary(), issue.getKey(), issue.getIssueType(),
+ issue.getDescription(), issue.getPriority(), issue.getAssignee(), attachments);
+ return null;
+ });
+
+ camelContext.getRegistry().bind(JIRA_REST_CLIENT_FACTORY, jiraRestClientFactory);
+ }
+
+ @Override
+ public void afterApplicationStart(CamelContext camelContext) {
+ //do nothing here
+ }
+ };
+ }
+
+ private File generateSampleFile() throws IOException {
+ File sampleRandomFile = File.createTempFile("attach-test", null);
+ sampleRandomFile.deleteOnExit();
+ String text = "A random text to use on the AttachFileProducerTest.java of camel-jira component.";
+ Files.write(sampleRandomFile.toPath(), text.getBytes(), StandardOpenOption.CREATE);
+ return sampleRandomFile;
+ }
+
+ @Test
+ public void verifyAttachment() throws InterruptedException, IOException {
+ template.sendBody(generateSampleFile());
+ Issue retrievedIssue = issueRestClient.getIssue(issue.getKey()).claim();
+ assertEquals(issue, retrievedIssue);
+ // there is only one attachment
+ Attachment attachFile = retrievedIssue.getAttachments().iterator().next();
+ assertEquals(attachFile.getFilename(), attachedFile.getName());
+ assertEquals(attachFile.getSize(), attachedFile.length());
+ mockResult.expectedMessageCount(1);
+ mockResult.assertIsSatisfied();
+ }
+
+
+ @Configuration
+ public class TestConfiguration {
+
+
+
+ @Bean
+ public RouteBuilder routeBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:start")
+ .setHeader(ISSUE_KEY, () -> KEY + "-1")
+ .to("jira://attach?jiraUrl=" + JIRA_CREDENTIALS)
+ .to(mockResult);
+ }
+ };
+ }
+
+
+ }
+
+
+
+
+}
diff --git a/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/DeleteIssueProducerTest.java b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/DeleteIssueProducerTest.java
new file mode 100644
index 00000000000..d4c5a6df376
--- /dev/null
+++ b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/DeleteIssueProducerTest.java
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.component.jira.springboot.test;
+
+
+import static org.apache.camel.component.jira.JiraConstants.ISSUE_KEY;
+import static org.apache.camel.component.jira.JiraConstants.JIRA_REST_CLIENT_FACTORY;
+import static org.apache.camel.component.jira.springboot.test.JiraTestConstants.JIRA_CREDENTIALS;
+import static org.apache.camel.component.jira.springboot.test.JiraTestConstants.KEY;
+import static org.apache.camel.component.jira.springboot.test.Utils.createIssue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+
+import com.atlassian.jira.rest.client.api.IssueRestClient;
+import com.atlassian.jira.rest.client.api.JiraRestClient;
+import com.atlassian.jira.rest.client.api.JiraRestClientFactory;
+import com.atlassian.jira.rest.client.api.domain.Issue;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.spring.boot.CamelContextConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import io.atlassian.util.concurrent.Promises;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.annotation.DirtiesContext.ClassMode;
+
+
+
+@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
+@CamelSpringBootTest
+@SpringBootTest(
+ classes = {
+ CamelAutoConfiguration.class,
+ DeleteIssueProducerTest.class,
+ DeleteIssueProducerTest.TestConfiguration.class
+ }
+)
+
+public class DeleteIssueProducerTest {
+
+ @Autowired
+ private CamelContext camelContext;
+
+
+
+ @Autowired
+ @Produce("direct:start")
+ ProducerTemplate template;
+
+ @EndpointInject("mock:result")
+ MockEndpoint mockResult;
+
+ static JiraRestClient jiraClient;
+
+ static JiraRestClientFactory jiraRestClientFactory;
+
+ static IssueRestClient issueRestClient;
+
+ static Issue backendIssue;
+
+ static Issue issue;
+
+ @Bean
+ CamelContextConfiguration contextConfiguration() {
+ return new CamelContextConfiguration() {
+ @Override
+ public void beforeApplicationStart(CamelContext context) {
+ //get chance to mock camelContext/Registry
+ jiraRestClientFactory = mock(JiraRestClientFactory.class);
+ jiraClient = mock(JiraRestClient.class);
+ issueRestClient = mock(IssueRestClient.class);
+ lenient().when(jiraRestClientFactory.createWithBasicHttpAuthentication(any(), any(), any())).thenReturn(jiraClient);
+ lenient().when(jiraClient.getIssueClient()).thenReturn(issueRestClient);
+
+ issue = createIssue(1);
+ when(issueRestClient.getIssue(any())).then(inv -> Promises.promise(issue));
+ when(issueRestClient.deleteIssue(anyString(), anyBoolean())).then(inv -> {
+ issue = null;
+ return null;
+ });
+
+ camelContext.getRegistry().bind(JIRA_REST_CLIENT_FACTORY, jiraRestClientFactory);
+ }
+
+ @Override
+ public void afterApplicationStart(CamelContext camelContext) {
+ //do nothing here
+ }
+ };
+ }
+
+ @Test
+ public void verifyDeleteIssue() throws InterruptedException {
+ String issueKey = issue.getKey();
+ template.sendBody(null);
+ Issue retrievedIssue = issueRestClient.getIssue(issueKey).claim();
+ assertNull(retrievedIssue);
+ assertEquals(issue, retrievedIssue);
+ mockResult.expectedMessageCount(1);
+ mockResult.assertIsSatisfied();
+ }
+
+ @Configuration
+ public class TestConfiguration {
+
+
+
+ @Bean
+ public RouteBuilder routeBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws IOException {
+ from("direct:start")
+ .setHeader(ISSUE_KEY, () -> KEY + "-1")
+ .to("jira://deleteIssue?jiraUrl=" + JIRA_CREDENTIALS)
+ .to(mockResult);
+ }
+ };
+ }
+
+
+ }
+
+
+
+
+}
diff --git a/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/FetchCommentsProducerTest.java b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/FetchCommentsProducerTest.java
new file mode 100644
index 00000000000..97fbaae47be
--- /dev/null
+++ b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/FetchCommentsProducerTest.java
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.component.jira.springboot.test;
+
+
+import static org.apache.camel.component.jira.JiraConstants.ISSUE_KEY;
+import static org.apache.camel.component.jira.JiraConstants.JIRA_REST_CLIENT_FACTORY;
+import static org.apache.camel.component.jira.springboot.test.JiraTestConstants.JIRA_CREDENTIALS;
+import static org.apache.camel.test.junit5.TestSupport.assertIsInstanceOf;
+import static org.apache.camel.test.junit5.TestSupport.assertStringContains;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+
+import com.atlassian.jira.rest.client.api.IssueRestClient;
+import com.atlassian.jira.rest.client.api.JiraRestClient;
+import com.atlassian.jira.rest.client.api.JiraRestClientFactory;
+import com.atlassian.jira.rest.client.api.domain.Issue;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.spring.boot.CamelContextConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.fail;
+
+import io.atlassian.util.concurrent.Promises;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.annotation.DirtiesContext.ClassMode;
+
+
+
+@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
+@CamelSpringBootTest
+@SpringBootTest(
+ classes = {
+ CamelAutoConfiguration.class,
+ FetchCommentsProducerTest.class,
+ FetchCommentsProducerTest.TestConfiguration.class
+ }
+)
+
+public class FetchCommentsProducerTest {
+
+ @Autowired
+ private CamelContext camelContext;
+
+
+
+ @Autowired
+ @Produce("direct:start")
+ ProducerTemplate template;
+
+ @EndpointInject("mock:result")
+ MockEndpoint mockResult;
+
+ static JiraRestClient jiraClient;
+
+ static JiraRestClientFactory jiraRestClientFactory;
+
+ static IssueRestClient issueRestClient;
+
+ static Issue backendIssue;
+
+
+
+ @Bean
+ CamelContextConfiguration contextConfiguration() {
+ return new CamelContextConfiguration() {
+ @Override
+ public void beforeApplicationStart(CamelContext context) {
+ //get chance to mock camelContext/Registry
+ jiraRestClientFactory = mock(JiraRestClientFactory.class);
+ jiraClient = mock(JiraRestClient.class);
+ issueRestClient = mock(IssueRestClient.class);
+ backendIssue = mock(Issue.class);
+ lenient().when(jiraRestClientFactory.createWithBasicHttpAuthentication(any(), any(), any())).thenReturn(jiraClient);
+ lenient().when(jiraClient.getIssueClient()).thenReturn(issueRestClient);
+
+
+ camelContext.getRegistry().bind(JIRA_REST_CLIENT_FACTORY, jiraRestClientFactory);
+ }
+
+ @Override
+ public void afterApplicationStart(CamelContext camelContext) {
+ //do nothing here
+ }
+ };
+ }
+
+ @Test
+ public void testFetchComments() throws InterruptedException {
+ when(backendIssue.getKey()).thenReturn("TEST-123");
+ when(issueRestClient.getIssue(any())).then(inv -> Promises.promise(backendIssue));
+
+ template.sendBodyAndHeader(null, ISSUE_KEY, backendIssue.getKey());
+
+ mockResult.expectedMessageCount(1);
+ mockResult.assertIsSatisfied();
+
+ verify(issueRestClient).getIssue(backendIssue.getKey());
+ verify(backendIssue).getComments();
+ }
+
+ @Test
+ public void testFetchCommentsMissingIssueKey() throws InterruptedException {
+ try {
+ template.sendBody(null);
+ fail("Should have thrown an exception");
+ } catch (CamelExecutionException e) {
+ IllegalArgumentException cause = assertIsInstanceOf(IllegalArgumentException.class, e.getCause());
+ assertStringContains(cause.getMessage(), ISSUE_KEY);
+ }
+
+ mockResult.expectedMessageCount(0);
+ mockResult.assertIsSatisfied();
+
+ verify(issueRestClient, never()).getIssue(backendIssue.getKey());
+ verify(backendIssue, never()).getComments();
+ }
+
+
+ @Configuration
+ public class TestConfiguration {
+
+
+
+ @Bean
+ public RouteBuilder routeBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:start")
+ .to("jira://fetchComments?jiraUrl=" + JIRA_CREDENTIALS)
+ .to(mockResult);
+ }
+ };
+ }
+
+
+ }
+
+
+
+
+}
diff --git a/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/FetchIssueProducerTest.java b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/FetchIssueProducerTest.java
new file mode 100644
index 00000000000..88b991f0bec
--- /dev/null
+++ b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/FetchIssueProducerTest.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.component.jira.springboot.test;
+
+
+import static org.apache.camel.component.jira.JiraConstants.ISSUE_KEY;
+import static org.apache.camel.component.jira.JiraConstants.JIRA_REST_CLIENT_FACTORY;
+import static org.apache.camel.test.junit5.TestSupport.assertIsInstanceOf;
+import static org.apache.camel.test.junit5.TestSupport.assertStringContains;
+import static org.apache.camel.component.jira.springboot.test.JiraTestConstants.JIRA_CREDENTIALS;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+
+import com.atlassian.jira.rest.client.api.IssueRestClient;
+import com.atlassian.jira.rest.client.api.JiraRestClient;
+import com.atlassian.jira.rest.client.api.JiraRestClientFactory;
+import com.atlassian.jira.rest.client.api.domain.Issue;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.spring.boot.CamelContextConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.fail;
+
+import io.atlassian.util.concurrent.Promises;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.annotation.DirtiesContext.ClassMode;
+
+
+
+@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
+@CamelSpringBootTest
+@SpringBootTest(
+ classes = {
+ CamelAutoConfiguration.class,
+ FetchIssueProducerTest.class,
+ FetchIssueProducerTest.TestConfiguration.class
+ }
+)
+
+public class FetchIssueProducerTest {
+
+ @Autowired
+ private CamelContext camelContext;
+
+
+
+ @Autowired
+ @Produce("direct:start")
+ ProducerTemplate template;
+
+ @EndpointInject("mock:result")
+ MockEndpoint mockResult;
+
+ static JiraRestClient jiraClient;
+
+ static JiraRestClientFactory jiraRestClientFactory;
+
+ static IssueRestClient issueRestClient;
+
+ static Issue backendIssue;
+
+
+
+ @Bean
+ CamelContextConfiguration contextConfiguration() {
+ return new CamelContextConfiguration() {
+ @Override
+ public void beforeApplicationStart(CamelContext context) {
+ //get chance to mock camelContext/Registry
+ jiraRestClientFactory = mock(JiraRestClientFactory.class);
+ jiraClient = mock(JiraRestClient.class);
+ issueRestClient = mock(IssueRestClient.class);
+ backendIssue = mock(Issue.class);
+ lenient().when(jiraRestClientFactory.createWithBasicHttpAuthentication(any(), any(), any())).thenReturn(jiraClient);
+ lenient().when(jiraClient.getIssueClient()).thenReturn(issueRestClient);
+
+ camelContext.getRegistry().bind(JIRA_REST_CLIENT_FACTORY, jiraRestClientFactory);
+ }
+
+ @Override
+ public void afterApplicationStart(CamelContext camelContext) {
+ //do nothing here
+ }
+ };
+ }
+
+ @Test
+ public void testFetchIssue() throws InterruptedException {
+ when(backendIssue.getKey()).thenReturn("TEST-123");
+ when(issueRestClient.getIssue(any())).then(inv -> Promises.promise(backendIssue));
+
+ template.sendBodyAndHeader(null, ISSUE_KEY, backendIssue.getKey());
+
+ mockResult.expectedMessageCount(1);
+ mockResult.assertIsSatisfied();
+
+ verify(issueRestClient).getIssue(backendIssue.getKey());
+ }
+
+ @Test
+ public void testFetchIssueMissingIssueKey() throws InterruptedException {
+ try {
+ template.sendBody(null);
+ fail("Should have thrown an exception");
+ } catch (CamelExecutionException e) {
+ IllegalArgumentException cause = assertIsInstanceOf(IllegalArgumentException.class, e.getCause());
+ assertStringContains(cause.getMessage(), ISSUE_KEY);
+ }
+
+ mockResult.expectedMessageCount(0);
+ mockResult.assertIsSatisfied();
+
+ verify(issueRestClient, never()).getIssue(backendIssue.getKey());
+ }
+
+
+ @Configuration
+ public class TestConfiguration {
+
+
+
+ @Bean
+ public RouteBuilder routeBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws Exception {
+ from("direct:start")
+ .to("jira://fetchIssue?jiraUrl=" + JIRA_CREDENTIALS)
+ .to(mockResult);
+ }
+ };
+ }
+
+
+ }
+
+
+
+
+}
diff --git a/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/JiraTestConstants.java b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/JiraTestConstants.java
new file mode 100644
index 00000000000..a21e135d381
--- /dev/null
+++ b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/JiraTestConstants.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.component.jira.springboot.test;
+
+public interface JiraTestConstants {
+
+ String KEY = "TST";
+ String TEST_JIRA_URL = "https://somerepo.atlassian.net";
+ String PROJECT = "TST";
+ String USERNAME = "someguy";
+ String PASSWORD = "my_password";
+ String JIRA_CREDENTIALS = TEST_JIRA_URL + "&username=" + USERNAME + "&password=" + PASSWORD;
+ String WATCHED_COMPONENTS = "Priority,Status,Resolution";
+}
diff --git a/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/NewCommentsConsumerTest.java b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/NewCommentsConsumerTest.java
new file mode 100644
index 00000000000..a814c2fdb41
--- /dev/null
+++ b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/NewCommentsConsumerTest.java
@@ -0,0 +1,236 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.component.jira.springboot.test;
+
+
+
+import static org.apache.camel.component.jira.JiraConstants.JIRA_REST_CLIENT_FACTORY;
+import static org.apache.camel.component.jira.springboot.test.Utils.createIssueWithComments;
+import static org.apache.camel.component.jira.springboot.test.JiraTestConstants.JIRA_CREDENTIALS;
+import static org.apache.camel.component.jira.springboot.test.JiraTestConstants.PROJECT;
+import static org.apache.camel.component.jira.springboot.test.Utils.createIssue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.atlassian.jira.rest.client.api.IssueRestClient;
+import com.atlassian.jira.rest.client.api.JiraRestClient;
+import com.atlassian.jira.rest.client.api.JiraRestClientFactory;
+import com.atlassian.jira.rest.client.api.SearchRestClient;
+import com.atlassian.jira.rest.client.api.domain.Comment;
+import com.atlassian.jira.rest.client.api.domain.Issue;
+import com.atlassian.jira.rest.client.api.domain.SearchResult;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.spring.boot.CamelContextConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+
+import io.atlassian.util.concurrent.Promise;
+import io.atlassian.util.concurrent.Promises;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.annotation.DirtiesContext.ClassMode;
+
+
+
+@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
+@CamelSpringBootTest
+@SpringBootTest(
+ classes = {
+ CamelAutoConfiguration.class,
+ NewCommentsConsumerTest.class,
+ NewCommentsConsumerTest.TestConfiguration.class
+ }
+)
+
+public class NewCommentsConsumerTest {
+
+ @Autowired
+ private CamelContext camelContext;
+
+
+
+ @Autowired
+ @Produce("direct:start")
+ ProducerTemplate template;
+
+ @EndpointInject("mock:result")
+ MockEndpoint mockResult;
+
+ static JiraRestClient jiraClient;
+
+ static JiraRestClientFactory jiraRestClientFactory;
+
+ static IssueRestClient issueRestClient;
+
+ static SearchRestClient searchRestClient;
+
+ static List issues = new ArrayList<>();
+
+
+ @BeforeAll
+ public static void beforeAll() {
+ issues.add(createIssueWithComments(1L, 1));
+ issues.add(createIssueWithComments(2L, 1));
+ issues.add(createIssueWithComments(3L, 1));
+ }
+
+ @Bean
+ CamelContextConfiguration contextConfiguration() {
+ return new CamelContextConfiguration() {
+ @Override
+ public void beforeApplicationStart(CamelContext context) {
+ //get chance to mock camelContext/Registry
+ jiraRestClientFactory = mock(JiraRestClientFactory.class);
+ jiraClient = mock(JiraRestClient.class);
+ issueRestClient = mock(IssueRestClient.class);
+ searchRestClient = mock(SearchRestClient.class);
+ SearchResult result = new SearchResult(0, 50, 100, issues);
+ Promise promiseSearchResult = Promises.promise(result);
+ Issue issue = createIssueWithComments(4L, 1);
+ Promise promiseIssue = Promises.promise(issue);
+
+ when(jiraClient.getSearchClient()).thenReturn(searchRestClient);
+ when(jiraClient.getIssueClient()).thenReturn(issueRestClient);
+ when(jiraRestClientFactory.createWithBasicHttpAuthentication(any(), any(), any())).thenReturn(jiraClient);
+ when(searchRestClient.searchJql(any(), any(), any(), any())).thenReturn(promiseSearchResult);
+ when(issueRestClient.getIssue(anyString())).thenReturn(promiseIssue);
+
+ camelContext.getRegistry().bind(JIRA_REST_CLIENT_FACTORY, jiraRestClientFactory);
+ }
+
+ @Override
+ public void afterApplicationStart(CamelContext camelContext) {
+ //do nothing here
+ }
+ };
+ }
+
+ @Test
+ public void emptyAtStartupTest() throws Exception {
+ mockResult.expectedMessageCount(0);
+ mockResult.assertIsSatisfied();
+ }
+
+ @Test
+ public void singleIssueCommentsTest() throws Exception {
+ Issue issueWithCommends = createIssueWithComments(11L, 3000);
+ Issue issueWithNoComments = createIssue(51L);
+
+ reset(issueRestClient);
+ AtomicInteger regulator = new AtomicInteger();
+ when(issueRestClient.getIssue(anyString())).then(inv -> {
+ int idx = regulator.getAndIncrement();
+ Issue issue = issueWithNoComments;
+ if (idx < 1) {
+ issue = issueWithCommends;
+ }
+ return Promises.promise(issue);
+ });
+ List comments = new ArrayList<>();
+ for (Comment c : issueWithCommends.getComments()) {
+ comments.add(c);
+ }
+ // reverse the order, from oldest comment to recent
+ Collections.reverse(comments);
+ // expect 3000 comments
+ mockResult.expectedBodiesReceived(comments);
+ mockResult.assertIsSatisfied();
+ }
+
+ @Test
+ public void multipleIssuesTest() throws Exception {
+ Issue issue1 = createIssueWithComments(20L, 2000);
+ Issue issue2 = createIssueWithComments(21L, 3000);
+ Issue issue3 = createIssueWithComments(22L, 1000);
+ List newIssues = new ArrayList<>();
+ newIssues.add(issue1);
+ newIssues.add(issue2);
+ newIssues.add(issue3);
+ Issue issueWithNoComments = createIssue(31L);
+
+ reset(searchRestClient);
+ reset(issueRestClient);
+ SearchResult searchResult = new SearchResult(0, 50, 3, newIssues);
+ Promise searchResultPromise = Promises.promise(searchResult);
+ when(searchRestClient.searchJql(anyString(), any(), any(), any())).thenReturn(searchResultPromise);
+ AtomicInteger regulator = new AtomicInteger();
+ when(issueRestClient.getIssue(anyString())).then(inv -> {
+ int idx = regulator.getAndIncrement();
+ Issue issue = issueWithNoComments;
+ if (idx < newIssues.size()) {
+ issue = newIssues.get(idx);
+ }
+ return Promises.promise(issue);
+ });
+ List comments = new ArrayList<>();
+ for (Issue issue : newIssues) {
+ for (Comment c : issue.getComments()) {
+ comments.add(c);
+ }
+ }
+ // reverse the order, from oldest comment to recent
+ Collections.reverse(comments);
+ // expect 6000 comments
+ mockResult.expectedBodiesReceived(comments);
+ mockResult.assertIsSatisfied();
+ }
+
+ @Configuration
+ public class TestConfiguration {
+
+
+
+ @Bean
+ public RouteBuilder routeBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("jira://newComments?jiraUrl=" + JIRA_CREDENTIALS + "&jql=project=" + PROJECT + "&delay=1000")
+ .to(mockResult);
+ }
+ };
+ }
+
+
+ }
+
+
+
+
+}
diff --git a/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/NewIssuesConsumerTest.java b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/NewIssuesConsumerTest.java
new file mode 100644
index 00000000000..69acd3bac3b
--- /dev/null
+++ b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/NewIssuesConsumerTest.java
@@ -0,0 +1,205 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.component.jira.springboot.test;
+
+
+import static org.apache.camel.component.jira.JiraConstants.JIRA_REST_CLIENT_FACTORY;
+import static org.apache.camel.component.jira.springboot.test.JiraTestConstants.JIRA_CREDENTIALS;
+import static org.apache.camel.component.jira.springboot.test.JiraTestConstants.PROJECT;
+import static org.apache.camel.component.jira.springboot.test.Utils.createIssue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.atlassian.jira.rest.client.api.IssueRestClient;
+import com.atlassian.jira.rest.client.api.JiraRestClient;
+import com.atlassian.jira.rest.client.api.JiraRestClientFactory;
+import com.atlassian.jira.rest.client.api.SearchRestClient;
+import com.atlassian.jira.rest.client.api.domain.Issue;
+import com.atlassian.jira.rest.client.api.domain.SearchResult;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.spring.boot.CamelContextConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+
+import io.atlassian.util.concurrent.Promise;
+import io.atlassian.util.concurrent.Promises;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.annotation.DirtiesContext.ClassMode;
+
+
+
+@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
+@CamelSpringBootTest
+@SpringBootTest(
+ classes = {
+ CamelAutoConfiguration.class,
+ NewIssuesConsumerTest.class,
+ NewIssuesConsumerTest.TestConfiguration.class
+ }
+)
+
+public class NewIssuesConsumerTest {
+
+ @Autowired
+ private CamelContext camelContext;
+
+
+
+ @Autowired
+ @Produce("direct:start")
+ ProducerTemplate template;
+
+ @EndpointInject("mock:result")
+ MockEndpoint mockResult;
+
+ static JiraRestClient jiraClient;
+
+ static JiraRestClientFactory jiraRestClientFactory;
+
+ static IssueRestClient issueRestClient;
+
+ static SearchRestClient searchRestClient;
+
+ static List issues = new ArrayList<>();
+
+ @BeforeAll
+ public static void beforeAll() {
+ issues.add(createIssue(1L));
+ issues.add(createIssue(2L));
+ issues.add(createIssue(3L));
+ }
+
+ @Bean
+ CamelContextConfiguration contextConfiguration() {
+ return new CamelContextConfiguration() {
+ @Override
+ public void beforeApplicationStart(CamelContext context) {
+ //get chance to mock camelContext/Registry
+ jiraRestClientFactory = mock(JiraRestClientFactory.class);
+ jiraClient = mock(JiraRestClient.class);
+ issueRestClient = mock(IssueRestClient.class);
+ searchRestClient = mock(SearchRestClient.class);
+ SearchResult result = new SearchResult(0, 50, 100, issues);
+ Promise promiseSearchResult = Promises.promise(result);
+
+ when(jiraClient.getSearchClient()).thenReturn(searchRestClient);
+ when(jiraRestClientFactory.createWithBasicHttpAuthentication(any(), any(), any())).thenReturn(jiraClient);
+ when(searchRestClient.searchJql(any(), any(), any(), any())).thenReturn(promiseSearchResult);
+ camelContext.getRegistry().bind(JIRA_REST_CLIENT_FACTORY, jiraRestClientFactory);
+ }
+
+ @Override
+ public void afterApplicationStart(CamelContext camelContext) {
+ //do nothing here
+ }
+ };
+ }
+
+ @Test
+ public void emptyAtStartupTest() throws Exception {
+ mockResult.expectedMessageCount(0);
+ mockResult.assertIsSatisfied();
+ }
+
+ @Test
+ public void singleIssueTest() throws Exception {
+ Issue issue = createIssue(11);
+
+ reset(searchRestClient);
+ AtomicBoolean searched = new AtomicBoolean();
+ when(searchRestClient.searchJql(any(), any(), any(), any())).then(invocation -> {
+ List newIissues = new ArrayList<>();
+ if (!searched.get()) {
+ newIissues.add(issue);
+ searched.set(true);
+ }
+ SearchResult result = new SearchResult(0, 50, 100, newIissues);
+ return Promises.promise(result);
+ });
+ mockResult.expectedBodiesReceived(issue);
+ mockResult.assertIsSatisfied();
+ }
+
+ @Test
+ public void multipleIssuesTest() throws Exception {
+ Issue issue1 = createIssue(21);
+ Issue issue2 = createIssue(22);
+ Issue issue3 = createIssue(23);
+
+ reset(searchRestClient);
+ AtomicBoolean searched = new AtomicBoolean();
+ when(searchRestClient.searchJql(any(), any(), any(), any())).then(invocation -> {
+ List newIssues = new ArrayList<>();
+ if (!searched.get()) {
+ newIssues.add(issue1);
+ newIssues.add(issue2);
+ newIssues.add(issue3);
+ searched.set(true);
+ }
+ SearchResult result = new SearchResult(0, 50, 3, newIssues);
+ return Promises.promise(result);
+ });
+
+ mockResult.expectedBodiesReceived(issue3, issue2, issue1);
+ mockResult.assertIsSatisfied();
+ }
+
+
+ @Configuration
+ public class TestConfiguration {
+
+
+
+ @Bean
+ public RouteBuilder routeBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("jira://newIssues?jiraUrl=" + JIRA_CREDENTIALS + "&jql=project=" + PROJECT + "&delay=5000")
+ .to(mockResult);
+ }
+ };
+ }
+
+
+ }
+
+
+
+
+}
diff --git a/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/TransitionIssueProducerTest.java b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/TransitionIssueProducerTest.java
new file mode 100644
index 00000000000..71e092ea91b
--- /dev/null
+++ b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/TransitionIssueProducerTest.java
@@ -0,0 +1,173 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.component.jira.springboot.test;
+
+
+import static org.apache.camel.component.jira.JiraConstants.ISSUE_KEY;
+import static org.apache.camel.component.jira.JiraConstants.ISSUE_TRANSITION_ID;
+import static org.apache.camel.component.jira.JiraConstants.JIRA_REST_CLIENT_FACTORY;
+import static org.apache.camel.component.jira.springboot.test.JiraTestConstants.TEST_JIRA_URL;
+import static org.apache.camel.component.jira.springboot.test.Utils.createIssue;
+import static org.apache.camel.component.jira.springboot.test.Utils.transitionIssueDone;
+import static org.apache.camel.component.jira.springboot.test.JiraTestConstants.JIRA_CREDENTIALS;
+import static org.apache.camel.component.jira.springboot.test.JiraTestConstants.KEY;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.net.URI;
+
+import com.atlassian.jira.rest.client.api.IssueRestClient;
+import com.atlassian.jira.rest.client.api.JiraRestClient;
+import com.atlassian.jira.rest.client.api.JiraRestClientFactory;
+import com.atlassian.jira.rest.client.api.domain.Issue;
+import com.atlassian.jira.rest.client.api.domain.Resolution;
+import com.atlassian.jira.rest.client.api.domain.Status;
+import com.atlassian.jira.rest.client.api.domain.input.TransitionInput;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.spring.boot.CamelContextConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import io.atlassian.util.concurrent.Promises;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.annotation.DirtiesContext.ClassMode;
+
+
+
+@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
+@CamelSpringBootTest
+@SpringBootTest(
+ classes = {
+ CamelAutoConfiguration.class,
+ TransitionIssueProducerTest.class,
+ TransitionIssueProducerTest.TestConfiguration.class
+ }
+)
+
+public class TransitionIssueProducerTest {
+
+ @Autowired
+ private CamelContext camelContext;
+
+
+
+ @Autowired
+ @Produce("direct:start")
+ ProducerTemplate template;
+
+ @EndpointInject("mock:result")
+ MockEndpoint mockResult;
+
+ static JiraRestClient jiraClient;
+
+ static JiraRestClientFactory jiraRestClientFactory;
+
+ static IssueRestClient issueRestClient;
+
+ static Issue backendIssue;
+
+ static Issue issue;
+
+ @Bean
+ CamelContextConfiguration contextConfiguration() {
+ return new CamelContextConfiguration() {
+ @Override
+ public void beforeApplicationStart(CamelContext context) {
+ //get chance to mock camelContext/Registry
+ jiraRestClientFactory = mock(JiraRestClientFactory.class);
+ jiraClient = mock(JiraRestClient.class);
+ issueRestClient = mock(IssueRestClient.class);
+ lenient().when(jiraRestClientFactory.createWithBasicHttpAuthentication(any(), any(), any())).thenReturn(jiraClient);
+ lenient().when(jiraClient.getIssueClient()).thenReturn(issueRestClient);
+
+ issue = createIssue(1);
+ when(issueRestClient.getIssue(any())).then(inv -> Promises.promise(issue));
+ when(issueRestClient.transition(any(Issue.class), any(TransitionInput.class))).then(inv -> {
+ URI doneStatusUri = URI.create(TEST_JIRA_URL + "/rest/api/2/status/1");
+ URI doneResolutionUri = URI.create(TEST_JIRA_URL + "/rest/api/2/resolution/1");
+ Status status = new Status(doneStatusUri, 1L, "Done", "Done", null, null);
+ Resolution resolution = new Resolution(doneResolutionUri, 1L, "Resolution", "Resolution");
+ issue = transitionIssueDone(issue, status, resolution);
+ return null;
+ });
+
+ camelContext.getRegistry().bind(JIRA_REST_CLIENT_FACTORY, jiraRestClientFactory);
+ }
+
+ @Override
+ public void afterApplicationStart(CamelContext camelContext) {
+ //do nothing here
+ }
+ };
+ }
+
+ @Test
+ public void verifyTransition() throws InterruptedException {
+ template.sendBody(null);
+ Issue retrievedIssue = issueRestClient.getIssue(issue.getKey()).claim();
+ assertEquals(issue, retrievedIssue);
+ assertEquals(issue.getStatus(), retrievedIssue.getStatus());
+ assertEquals(issue.getResolution(), retrievedIssue.getResolution());
+ mockResult.expectedMessageCount(1);
+ mockResult.assertIsSatisfied();
+ }
+
+
+ @Configuration
+ public class TestConfiguration {
+
+
+
+ @Bean
+ public RouteBuilder routeBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() throws IOException {
+ from("direct:start")
+ .setHeader(ISSUE_KEY, () -> KEY + "-1")
+ .setHeader(ISSUE_TRANSITION_ID, () -> 31)
+ .to("jira://transitionIssue?jiraUrl=" + JIRA_CREDENTIALS)
+ .to(mockResult);
+ }
+ };
+ }
+
+
+ }
+
+
+
+
+}
diff --git a/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/UpdateIssueProducerTest.java b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/UpdateIssueProducerTest.java
new file mode 100644
index 00000000000..ae736ff7212
--- /dev/null
+++ b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/UpdateIssueProducerTest.java
@@ -0,0 +1,216 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.component.jira.springboot.test;
+
+
+import static org.apache.camel.component.jira.JiraConstants.ISSUE_ASSIGNEE;
+import static org.apache.camel.component.jira.JiraConstants.ISSUE_KEY;
+import static org.apache.camel.component.jira.JiraConstants.ISSUE_PRIORITY_NAME;
+import static org.apache.camel.component.jira.JiraConstants.ISSUE_SUMMARY;
+import static org.apache.camel.component.jira.JiraConstants.ISSUE_TYPE_NAME;
+import static org.apache.camel.component.jira.JiraConstants.JIRA_REST_CLIENT_FACTORY;
+import static org.apache.camel.component.jira.springboot.test.JiraTestConstants.JIRA_CREDENTIALS;
+import static org.apache.camel.component.jira.springboot.test.Utils.createIssue;
+import static org.apache.camel.component.jira.springboot.test.Utils.userAssignee;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.atlassian.jira.rest.client.api.IssueRestClient;
+import com.atlassian.jira.rest.client.api.JiraRestClient;
+import com.atlassian.jira.rest.client.api.JiraRestClientFactory;
+import com.atlassian.jira.rest.client.api.MetadataRestClient;
+import com.atlassian.jira.rest.client.api.domain.BasicIssue;
+import com.atlassian.jira.rest.client.api.domain.BasicPriority;
+import com.atlassian.jira.rest.client.api.domain.Issue;
+import com.atlassian.jira.rest.client.api.domain.IssueType;
+import com.atlassian.jira.rest.client.api.domain.Priority;
+import com.atlassian.jira.rest.client.api.domain.input.ComplexIssueInputFieldValue;
+import com.atlassian.jira.rest.client.api.domain.input.IssueInput;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.spring.boot.CamelContextConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import io.atlassian.util.concurrent.Promise;
+import io.atlassian.util.concurrent.Promises;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.annotation.DirtiesContext.ClassMode;
+
+
+
+@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
+@CamelSpringBootTest
+@SpringBootTest(
+ classes = {
+ CamelAutoConfiguration.class,
+ UpdateIssueProducerTest.class,
+ UpdateIssueProducerTest.TestConfiguration.class
+ }
+)
+
+public class UpdateIssueProducerTest {
+
+ @Autowired
+ private CamelContext camelContext;
+
+
+
+ @Autowired
+ @Produce("direct:start")
+ ProducerTemplate template;
+
+ @EndpointInject("mock:result")
+ MockEndpoint mockResult;
+
+ static JiraRestClient jiraClient;
+
+ static JiraRestClientFactory jiraRestClientFactory;
+
+ static IssueRestClient issueRestClient;
+
+ static MetadataRestClient metadataRestClient;
+
+ static Issue backendIssue;
+
+
+ @Bean
+ CamelContextConfiguration contextConfiguration() {
+ return new CamelContextConfiguration() {
+ @Override
+ public void beforeApplicationStart(CamelContext context) {
+ //get chance to mock camelContext/Registry
+ jiraRestClientFactory = mock(JiraRestClientFactory.class);
+ jiraClient = mock(JiraRestClient.class);
+ issueRestClient = mock(IssueRestClient.class);
+ metadataRestClient = mock(MetadataRestClient.class);
+ when(jiraRestClientFactory.createWithBasicHttpAuthentication(any(), any(), any())).thenReturn(jiraClient);
+ when(jiraClient.getIssueClient()).thenReturn(issueRestClient);
+ when(jiraClient.getMetadataClient()).thenReturn(metadataRestClient);
+
+ Map issueTypes = new HashMap<>();
+ issueTypes.put(1, new IssueType(null, 1L, "Bug", false, null, null));
+ issueTypes.put(2, new IssueType(null, 2L, "Task", false, null, null));
+ Promise> promiseIssueTypes = Promises.promise(issueTypes.values());
+ when(metadataRestClient.getIssueTypes()).thenReturn(promiseIssueTypes);
+
+ Map issuePriorities = new HashMap<>();
+ issuePriorities.put(1, new Priority(null, 1L, "High", null, null, null));
+ issuePriorities.put(2, new Priority(null, 2L, "Low", null, null, null));
+ Promise> promisePriorities = Promises.promise(issuePriorities.values());
+ when(metadataRestClient.getPriorities()).thenReturn(promisePriorities);
+
+ backendIssue = createIssue(11L);
+ when(issueRestClient.updateIssue(anyString(), any(IssueInput.class))).then(inv -> {
+ String issueKey = inv.getArgument(0);
+ IssueInput issueInput = inv.getArgument(1);
+ String summary = (String) issueInput.getField("summary").getValue();
+ Integer issueTypeId = Integer.parseInt(getValue(issueInput, "issuetype", "id"));
+ IssueType issueType = issueTypes.get(issueTypeId);
+ String description = (String) issueInput.getField("description").getValue();
+ Integer priorityId = Integer.parseInt(getValue(issueInput, "priority", "id"));
+ BasicPriority priority = issuePriorities.get(priorityId);
+ backendIssue = createIssue(11L, summary, issueKey, issueType, description, priority, userAssignee, null, null);
+ BasicIssue basicIssue = new BasicIssue(backendIssue.getSelf(), backendIssue.getKey(), backendIssue.getId());
+ return Promises.promise(basicIssue);
+ });
+ when(issueRestClient.getIssue(any())).then(inv -> Promises.promise(backendIssue));
+
+ camelContext.getRegistry().bind(JIRA_REST_CLIENT_FACTORY, jiraRestClientFactory);
+ }
+
+ @Override
+ public void afterApplicationStart(CamelContext camelContext) {
+ //do nothing here
+ }
+ };
+ }
+
+ @Test
+ public void verifyIssueUpdated() throws InterruptedException {
+
+ Map headers = new HashMap<>();
+ headers.put(ISSUE_KEY, backendIssue.getKey());
+ headers.put(ISSUE_TYPE_NAME, "Task");
+ headers.put(ISSUE_SUMMARY, "Demo Bug jira " + (new Date()));
+ headers.put(ISSUE_PRIORITY_NAME, "Low");
+ headers.put(ISSUE_ASSIGNEE, "tom");
+
+ template.sendBodyAndHeaders("New description " + (new Date()), headers);
+
+ Issue issue = issueRestClient.getIssue(backendIssue.getKey()).claim();
+ assertEquals(backendIssue, issue);
+ assertEquals(backendIssue.getIssueType(), issue.getIssueType());
+ assertEquals(backendIssue.getPriority(), issue.getPriority());
+ assertEquals(backendIssue.getSummary(), issue.getSummary());
+ assertEquals(backendIssue.getProject(), issue.getProject());
+ assertEquals(backendIssue.getDescription(), issue.getDescription());
+ mockResult.expectedMessageCount(1);
+ mockResult.assertIsSatisfied();
+ }
+
+ private String getValue(IssueInput issueInput, String field, String key) {
+ ComplexIssueInputFieldValue complexField = (ComplexIssueInputFieldValue) issueInput.getField(field).getValue();
+ return (String) complexField.getValuesMap().get(key);
+ }
+
+
+ @Configuration
+ public class TestConfiguration {
+
+
+
+ @Bean
+ public RouteBuilder routeBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:start")
+ .to("jira://updateIssue?jiraUrl=" + JIRA_CREDENTIALS)
+ .to(mockResult);
+ }
+ };
+ }
+
+
+ }
+
+
+
+
+}
diff --git a/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/Utils.java b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/Utils.java
new file mode 100644
index 00000000000..a26af43c8e3
--- /dev/null
+++ b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/Utils.java
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.component.jira.springboot.test;
+
+import java.net.URI;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+
+import com.atlassian.jira.rest.client.api.StatusCategory;
+import com.atlassian.jira.rest.client.api.domain.Attachment;
+import com.atlassian.jira.rest.client.api.domain.BasicComponent;
+import com.atlassian.jira.rest.client.api.domain.BasicPriority;
+import com.atlassian.jira.rest.client.api.domain.BasicWatchers;
+import com.atlassian.jira.rest.client.api.domain.Comment;
+import com.atlassian.jira.rest.client.api.domain.Issue;
+import com.atlassian.jira.rest.client.api.domain.IssueLink;
+import com.atlassian.jira.rest.client.api.domain.IssueLinkType;
+import com.atlassian.jira.rest.client.api.domain.IssueType;
+import com.atlassian.jira.rest.client.api.domain.Priority;
+import com.atlassian.jira.rest.client.api.domain.Resolution;
+import com.atlassian.jira.rest.client.api.domain.Status;
+import com.atlassian.jira.rest.client.api.domain.User;
+import com.atlassian.jira.rest.client.api.domain.Worklog;
+import com.google.common.collect.ImmutableMap;
+import org.apache.commons.lang3.StringUtils;
+import org.joda.time.DateTime;
+import org.slf4j.LoggerFactory;
+
+import static com.atlassian.jira.rest.client.api.domain.User.S48_48;
+import static org.apache.camel.component.jira.springboot.test.JiraTestConstants.KEY;
+import static org.apache.camel.component.jira.springboot.test.JiraTestConstants.TEST_JIRA_URL;
+
+public final class Utils {
+ public static User userAssignee;
+ static {
+ try {
+ userAssignee = new User(
+ null, "user-test", "User Test", "user@test", true, null, buildUserAvatarUris("user-test", 10082L), null);
+ } catch (Exception e) {
+
+ LoggerFactory.getLogger(Utils.class).debug("Failed to build test user: {}", e.getMessage(), e);
+ }
+ }
+ private static IssueType issueType = new IssueType(null, 1L, "Bug", false, "Bug", null);
+
+ private Utils() {
+ }
+
+ public static Issue createIssue(long id) {
+ return createIssueWithComments(id, 0);
+ }
+
+ public static Issue createIssue(
+ long id, String summary, String key, IssueType issueType, String description,
+ BasicPriority priority, User assignee, Collection components, BasicWatchers watchers) {
+ URI selfUri = URI.create(TEST_JIRA_URL + "/rest/api/latest/issue/" + id);
+ return new Issue(
+ summary, selfUri, KEY + "-" + id, id, null, issueType, null, description, priority, null, null, null,
+ assignee, null, null, null, null, null, components, null, null, null, null, null, null, null, watchers,
+ null, null, null, null, null);
+ }
+
+ public static Issue transitionIssueDone(Issue issue, Status status, Resolution resolution) {
+ return new Issue(
+ issue.getSummary(), issue.getSelf(), issue.getKey(), issue.getId(), null, issue.getIssueType(),
+ status, issue.getDescription(), issue.getPriority(), resolution, null, null,
+ issue.getAssignee(), null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null);
+ }
+
+ public static Issue setPriority(Issue issue, Priority p) {
+ return new Issue(
+ issue.getSummary(), issue.getSelf(), issue.getKey(), issue.getId(), null, issue.getIssueType(),
+ issue.getStatus(), issue.getDescription(), p, issue.getResolution(), null, null,
+ issue.getAssignee(), null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null);
+ }
+
+ public static Issue transitionIssueDone(Issue issue) {
+ URI doneStatusUri = URI.create(TEST_JIRA_URL + "/rest/api/2/status/1");
+ URI doneResolutionUri = URI.create(TEST_JIRA_URL + "/rest/api/2/resolution/1");
+ StatusCategory sc = new StatusCategory(doneResolutionUri, "statusCategory", 1L, "SC-1", "GREEN");
+ Status status = new Status(doneStatusUri, 1L, "Done", "Done", null, sc);
+ Resolution resolution = new Resolution(doneResolutionUri, 5L, "Resolution", "Resolution");
+ return transitionIssueDone(issue, status, resolution);
+ }
+
+ public static Issue createIssueWithAttachment(
+ long id, String summary, String key, IssueType issueType, String description,
+ BasicPriority priority, User assignee, Collection attachments) {
+ URI selfUri = URI.create(TEST_JIRA_URL + "/rest/api/latest/issue/" + id);
+ return new Issue(
+ summary, selfUri, KEY + "-" + id, id, null, issueType, null, description, priority, null, attachments, null,
+ assignee, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null);
+ }
+
+ public static Issue createIssueWithComments(long id, int numComments) {
+ Collection comments = new ArrayList<>();
+ if (numComments > 0) {
+ for (int idx = 1; idx < numComments + 1; idx++) {
+ Comment c = newComment(id, idx, "A test comment " + idx + " for " + KEY + "-" + id);
+ comments.add(c);
+ }
+ }
+ return createIssueWithComments(id, comments);
+ }
+
+ public static Issue createIssueWithComments(long id, Collection comments) {
+ URI selfUri = URI.create(TEST_JIRA_URL + "/rest/api/latest/issue/" + id);
+ return new Issue(
+ "jira summary test " + id, selfUri, KEY + "-" + id, id, null, issueType, null, "Description " + id,
+ null, null, null, null, userAssignee, null, null, null, null, null, null, null, null, comments, null, null,
+ null, null, null, null, null, null, null, null);
+ }
+
+ public static Issue createIssueWithLinks(long id, Collection issueLinks) {
+ URI selfUri = URI.create(TEST_JIRA_URL + "/rest/api/latest/issue/" + id);
+ return new Issue(
+ "jira summary test " + id, selfUri, KEY + "-" + id, id, null, issueType, null, "Description " + id,
+ null, null, null, null, userAssignee, null, null, null, null, null, null, null, null, null, null, issueLinks,
+ null, null, null, null, null, null, null, null);
+ }
+
+ public static Issue createIssueWithWorkLogs(long id, Collection worklogs) {
+ URI selfUri = URI.create(TEST_JIRA_URL + "/rest/api/latest/issue/" + id);
+ return new Issue(
+ "jira summary test " + id, selfUri, KEY + "-" + id, id, null, issueType, null, "Description " + id,
+ null, null, null, null, userAssignee, null, null, null, null, null, null, null, null, null, null, null,
+ null, worklogs, null, null, null, null, null, null);
+ }
+
+ public static Comment newComment(long issueId, int newCommentId, String comment) {
+ DateTime now = DateTime.now();
+ long id = Long.parseLong(issueId + "0" + newCommentId);
+ URI selfUri = URI.create(TEST_JIRA_URL + "/rest/api/latest/issue/" + issueId + "/comment");
+ return new Comment(selfUri, comment, null, null, now, null, null, id);
+ }
+
+ public static IssueLink newIssueLink(long issueId, int newLinkId, String comment) {
+ long id = Long.parseLong(issueId + "0" + newLinkId);
+ URI issueUri = URI.create(TEST_JIRA_URL + "/rest/api/latest/issue/" + id);
+ IssueLinkType relatesTo = new IssueLinkType("Relates", "relates to", IssueLinkType.Direction.OUTBOUND);
+
+ return new IssueLink(KEY, issueUri, relatesTo);
+ }
+
+ public static Worklog newWorkLog(long issueId, Integer minutesSpent, String comment) {
+ DateTime now = DateTime.now();
+ URI issueUri = URI.create(TEST_JIRA_URL + "/rest/api/latest/issue/" + issueId);
+ URI selfUri = URI.create(TEST_JIRA_URL + "/rest/api/latest/issue/" + issueId + "/comment");
+
+ return new Worklog(selfUri, issueUri, null, null, comment, now, null, null, minutesSpent, null);
+ }
+
+ private static Map buildUserAvatarUris(String user, Long avatarId) throws Exception {
+ final ImmutableMap.Builder builder = ImmutableMap.builder();
+ builder.put(S48_48, buildUserAvatarUri(user, avatarId));
+ return builder.build();
+ }
+
+ private static URI buildUserAvatarUri(String userName, Long avatarId) throws Exception {
+ // secure/useravatar?size=small&ownerId=admin&avatarId=10054
+ final StringBuilder sb = new StringBuilder("secure/useravatar?");
+
+ // Optional user name
+ if (StringUtils.isNotBlank(userName)) {
+ sb.append("ownerId=").append(userName).append("&");
+ }
+
+ // avatar Id
+ sb.append("avatarId=").append(avatarId);
+ String relativeAvatarUrl = sb.toString();
+ URI avatarUrl = new URL(TEST_JIRA_URL + "/" + relativeAvatarUrl).toURI();
+ return avatarUrl;
+ }
+
+}
diff --git a/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/WatchUpdatesConsumerTest.java b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/WatchUpdatesConsumerTest.java
new file mode 100644
index 00000000000..cdb6a7045c0
--- /dev/null
+++ b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/WatchUpdatesConsumerTest.java
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.component.jira.springboot.test;
+
+
+import static org.apache.camel.component.jira.JiraConstants.JIRA_REST_CLIENT_FACTORY;
+import static org.apache.camel.component.jira.springboot.test.JiraTestConstants.JIRA_CREDENTIALS;
+import static org.apache.camel.component.jira.springboot.test.JiraTestConstants.PROJECT;
+import static org.apache.camel.component.jira.springboot.test.JiraTestConstants.WATCHED_COMPONENTS;
+import static org.apache.camel.component.jira.springboot.test.Utils.createIssue;
+import static org.apache.camel.component.jira.springboot.test.Utils.setPriority;
+import static org.apache.camel.component.jira.springboot.test.Utils.transitionIssueDone;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.atlassian.jira.rest.client.api.IssueRestClient;
+import com.atlassian.jira.rest.client.api.JiraRestClient;
+import com.atlassian.jira.rest.client.api.JiraRestClientFactory;
+import com.atlassian.jira.rest.client.api.SearchRestClient;
+import com.atlassian.jira.rest.client.api.domain.Issue;
+import com.atlassian.jira.rest.client.api.domain.Priority;
+import com.atlassian.jira.rest.client.api.domain.SearchResult;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.jira.JiraConstants;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.spring.boot.CamelContextConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+
+import io.atlassian.util.concurrent.Promise;
+import io.atlassian.util.concurrent.Promises;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.annotation.DirtiesContext.ClassMode;
+
+
+
+@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
+@CamelSpringBootTest
+@SpringBootTest(
+ classes = {
+ CamelAutoConfiguration.class,
+ WatchUpdatesConsumerTest.class,
+ WatchUpdatesConsumerTest.TestConfiguration.class
+ }
+)
+
+public class WatchUpdatesConsumerTest {
+
+ @Autowired
+ private CamelContext camelContext;
+
+
+
+ @Autowired
+ @Produce("direct:start")
+ ProducerTemplate template;
+
+ @EndpointInject("mock:result")
+ MockEndpoint mockResult;
+
+ static JiraRestClient jiraClient;
+
+ static JiraRestClientFactory jiraRestClientFactory;
+
+ static IssueRestClient issueRestClient;
+
+ static SearchRestClient searchRestClient;
+
+ static List issues = new ArrayList<>();
+
+ @BeforeAll
+ public static void beforeAll() {
+ issues.clear();
+ issues.add(createIssue(1L));
+ issues.add(createIssue(2L));
+ issues.add(createIssue(3L));
+ }
+
+ @Bean
+ CamelContextConfiguration contextConfiguration() {
+ return new CamelContextConfiguration() {
+ @Override
+ public void beforeApplicationStart(CamelContext context) {
+ //get chance to mock camelContext/Registry
+ jiraRestClientFactory = mock(JiraRestClientFactory.class);
+ jiraClient = mock(JiraRestClient.class);
+ issueRestClient = mock(IssueRestClient.class);
+ searchRestClient = mock(SearchRestClient.class);
+ SearchResult result = new SearchResult(0, 50, 100, issues);
+ Promise promiseSearchResult = Promises.promise(result);
+
+ when(jiraClient.getSearchClient()).thenReturn(searchRestClient);
+ when(jiraRestClientFactory.createWithBasicHttpAuthentication(any(), any(), any())).thenReturn(jiraClient);
+ when(searchRestClient.searchJql(any(), any(), any(), any())).thenReturn(promiseSearchResult);
+
+ camelContext.getRegistry().bind(JIRA_REST_CLIENT_FACTORY, jiraRestClientFactory);
+ }
+
+ @Override
+ public void afterApplicationStart(CamelContext camelContext) {
+ //do nothing here
+ }
+ };
+ }
+
+ @Test
+ public void emptyAtStartupTest() throws Exception {
+ mockResult.expectedMessageCount(0);
+ mockResult.assertIsSatisfied();
+ }
+
+ @Test
+ public void singleChangeTest() throws Exception {
+ Issue issue = setPriority(issues.get(0), new Priority(
+ null, 4L, "High", null, null, null));
+ reset(searchRestClient);
+ AtomicBoolean searched = new AtomicBoolean();
+ when(searchRestClient.searchJql(any(), any(), any(), any())).then(invocation -> {
+
+ if (!searched.get()) {
+ issues.remove(0);
+ issues.add(0, issue);
+ }
+ SearchResult result = new SearchResult(0, 50, 100, issues);
+ return Promises.promise(result);
+ });
+
+ mockResult.expectedBodiesReceived(issue.getPriority());
+ mockResult.expectedHeaderReceived(JiraConstants.ISSUE_CHANGED, "Priority");
+ mockResult.expectedHeaderReceived(JiraConstants.ISSUE_KEY, "TST-1");
+ mockResult.expectedMessageCount(1);
+ mockResult.assertIsSatisfied(0);
+ }
+
+ @Test
+ public void multipleChangesWithAddedNewIssueTest() throws Exception {
+ final Issue issue = transitionIssueDone(issues.get(1));
+ final Issue issue2 = setPriority(issues.get(2), new Priority(
+ null, 4L, "High", null, null, null));
+
+ reset(searchRestClient);
+ AtomicBoolean searched = new AtomicBoolean();
+ when(searchRestClient.searchJql(any(), any(), any(), any())).then(invocation -> {
+ if (!searched.get()) {
+ issues.add(createIssue(4L));
+ issues.remove(1);
+ issues.add(1, issue);
+ issues.remove(2);
+ issues.add(2, issue2);
+ searched.set(true);
+ }
+
+ SearchResult result = new SearchResult(0, 50, 3, issues);
+ return Promises.promise(result);
+ });
+
+ mockResult.expectedMessageCount(3);
+ mockResult.expectedBodiesReceivedInAnyOrder(issue.getStatus(), issue.getResolution(), issue2.getPriority());
+ mockResult.assertIsSatisfied(1000);
+ }
+
+
+ @Configuration
+ public class TestConfiguration {
+
+
+
+ @Bean
+ public RouteBuilder routeBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("jira://watchUpdates?jiraUrl=" + JIRA_CREDENTIALS
+ + "&jql=project=" + PROJECT + "&delay=5000&watchedFields=" + WATCHED_COMPONENTS)
+ .to(mockResult);
+ }
+ };
+ }
+
+
+ }
+
+
+
+
+}
diff --git a/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/WatcherProducerTest.java b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/WatcherProducerTest.java
new file mode 100644
index 00000000000..5fe8fc45c78
--- /dev/null
+++ b/components-starter/camel-jira-starter/src/test/java/org/apache/camel/component/jira/springboot/test/WatcherProducerTest.java
@@ -0,0 +1,264 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.camel.component.jira.springboot.test;
+
+
+import static org.apache.camel.component.jira.JiraConstants.ISSUE_KEY;
+import static org.apache.camel.component.jira.JiraConstants.ISSUE_WATCHERS_ADD;
+import static org.apache.camel.component.jira.JiraConstants.ISSUE_WATCHERS_REMOVE;
+import static org.apache.camel.component.jira.JiraConstants.JIRA_REST_CLIENT_FACTORY;
+import static org.apache.camel.component.jira.springboot.test.JiraTestConstants.JIRA_CREDENTIALS;
+import static org.apache.camel.component.jira.springboot.test.JiraTestConstants.KEY;
+import static org.apache.camel.component.jira.springboot.test.JiraTestConstants.TEST_JIRA_URL;
+import static org.apache.camel.component.jira.springboot.test.Utils.createIssue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.atlassian.jira.rest.client.api.IssueRestClient;
+import com.atlassian.jira.rest.client.api.JiraRestClient;
+import com.atlassian.jira.rest.client.api.JiraRestClientFactory;
+import com.atlassian.jira.rest.client.api.domain.BasicUser;
+import com.atlassian.jira.rest.client.api.domain.BasicWatchers;
+import com.atlassian.jira.rest.client.api.domain.Issue;
+import com.atlassian.jira.rest.client.api.domain.Watchers;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.EndpointInject;
+import org.apache.camel.Produce;
+import org.apache.camel.ProducerTemplate;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.spring.boot.CamelContextConfiguration;
+import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import io.atlassian.util.concurrent.Promises;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.annotation.DirtiesContext.ClassMode;
+
+
+
+@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
+@CamelSpringBootTest
+@SpringBootTest(
+ classes = {
+ CamelAutoConfiguration.class,
+ WatcherProducerTest.class,
+ WatcherProducerTest.TestConfiguration.class
+ }
+)
+
+public class WatcherProducerTest {
+
+ @Autowired
+ private CamelContext camelContext;
+
+
+
+ @Autowired
+ @Produce("direct:start")
+ ProducerTemplate template;
+
+ @EndpointInject("mock:result")
+ MockEndpoint mockResult;
+
+ static JiraRestClient jiraClient;
+
+ static JiraRestClientFactory jiraRestClientFactory;
+
+ static IssueRestClient issueRestClient;
+
+ static Issue backendIssue;
+
+ static Issue issue;
+
+ static List backendwatchers = new ArrayList<>();
+
+ @Bean
+ CamelContextConfiguration contextConfiguration() {
+ return new CamelContextConfiguration() {
+ @Override
+ public void beforeApplicationStart(CamelContext context) {
+ //get chance to mock camelContext/Registry
+ jiraRestClientFactory = mock(JiraRestClientFactory.class);
+ jiraClient = mock(JiraRestClient.class);
+ issueRestClient = mock(IssueRestClient.class);
+ lenient().when(jiraRestClientFactory.createWithBasicHttpAuthentication(any(), any(), any())).thenReturn(jiraClient);
+ lenient().when(jiraClient.getIssueClient()).thenReturn(issueRestClient);
+
+ backendwatchers.add("user1");
+ backendwatchers.add("user2");
+ backendwatchers.add("user3");
+ backendwatchers.add("user4");
+ backendwatchers.add("user5");
+ URI watchersUri = URI.create(TEST_JIRA_URL + "/rest/api/2/backendIssue/" + KEY + "-11/backendwatchers");
+ BasicWatchers initialBasicWatchers = new BasicWatchers(watchersUri, true, backendwatchers.size());
+ backendIssue
+ = createIssue(11L, "Test backendIssue", KEY + "-" + 11, null, null, null, null, null, initialBasicWatchers);
+ lenient().when(issueRestClient.addWatcher(any(URI.class), anyString())).then(inv -> {
+ String username = inv.getArgument(1);
+ backendwatchers.add(username);
+ BasicWatchers basicWatchers = new BasicWatchers(watchersUri, true, backendwatchers.size());
+ backendIssue = createIssue(backendIssue.getId(), backendIssue.getSummary(), backendIssue.getKey(),
+ backendIssue.getIssueType(), backendIssue.getDescription(),
+ backendIssue.getPriority(), backendIssue.getAssignee(), null, basicWatchers);
+ return null;
+ });
+ lenient().when(issueRestClient.removeWatcher(any(URI.class), anyString())).then(inv -> {
+ String username = inv.getArgument(1);
+ backendwatchers.remove(username);
+ BasicWatchers basicWatchers = new BasicWatchers(watchersUri, true, backendwatchers.size());
+ backendIssue = createIssue(backendIssue.getId(), backendIssue.getSummary(), backendIssue.getKey(),
+ backendIssue.getIssueType(), backendIssue.getDescription(),
+ backendIssue.getPriority(), backendIssue.getAssignee(), null, basicWatchers);
+ return null;
+ });
+ lenient().when(issueRestClient.getIssue(anyString())).then(inv -> Promises.promise(backendIssue));
+ lenient().when(issueRestClient.getWatchers(any(URI.class))).then(inv -> {
+ Collection users = new ArrayList<>();
+ for (String watcher : backendwatchers) {
+ users.add(new BasicUser(null, watcher, watcher));
+ }
+ BasicWatchers basicWatchers = new BasicWatchers(watchersUri, true, users.size());
+ Watchers watchers = new Watchers(basicWatchers, users);
+ return Promises.promise(watchers);
+ });
+
+ camelContext.getRegistry().bind(JIRA_REST_CLIENT_FACTORY, jiraRestClientFactory);
+ }
+
+ @Override
+ public void afterApplicationStart(CamelContext camelContext) {
+ //do nothing here
+ }
+ };
+ }
+
+ @Test
+ public void addWatchers() throws InterruptedException {
+ List watchersToAdd = new ArrayList<>();
+ watchersToAdd.add("user1A");
+ watchersToAdd.add("user1B");
+ Map headers = new HashMap<>();
+ headers.put(ISSUE_KEY, backendIssue.getKey());
+ headers.put(ISSUE_WATCHERS_ADD, watchersToAdd);
+ template.sendBodyAndHeaders(null, headers);
+
+ Issue retrievedIssue = issueRestClient.getIssue(backendIssue.getKey()).claim();
+ assertEquals(backendIssue, retrievedIssue);
+ assertEquals(retrievedIssue.getWatchers().getNumWatchers(), backendwatchers.size());
+
+ Watchers watchers = issueRestClient.getWatchers(retrievedIssue.getWatchers().getSelf()).claim();
+ for (BasicUser user : watchers.getUsers()) {
+ assertTrue(backendwatchers.contains(user.getName()));
+ }
+ mockResult.expectedMessageCount(1);
+ mockResult.assertIsSatisfied();
+ }
+
+ @Test
+ public void removeWatchers() throws InterruptedException {
+ List watchersToRemove = new ArrayList<>();
+ watchersToRemove.add("user2");
+ watchersToRemove.add("user3");
+ Map headers = new HashMap<>();
+ headers.put(ISSUE_KEY, backendIssue.getKey());
+ headers.put(ISSUE_WATCHERS_REMOVE, watchersToRemove);
+ template.sendBodyAndHeaders(null, headers);
+
+ Issue retrievedIssue = issueRestClient.getIssue(backendIssue.getKey()).claim();
+ assertEquals(backendIssue, retrievedIssue);
+ assertEquals(retrievedIssue.getWatchers().getNumWatchers(), backendwatchers.size());
+
+ Watchers watchers = issueRestClient.getWatchers(retrievedIssue.getWatchers().getSelf()).claim();
+ for (BasicUser user : watchers.getUsers()) {
+ assertTrue(backendwatchers.contains(user.getName()));
+ }
+ mockResult.expectedMessageCount(1);
+ mockResult.assertIsSatisfied();
+ }
+
+ @Test
+ public void addRemoveWatchers() throws InterruptedException {
+ List watchersToAdd = new ArrayList<>();
+ watchersToAdd.add("user2A");
+ watchersToAdd.add("user2B");
+ List watchersToRemove = new ArrayList<>();
+ watchersToRemove.add("user4");
+ watchersToRemove.add("user5");
+ Map headers = new HashMap<>();
+ headers.put(ISSUE_KEY, backendIssue.getKey());
+ headers.put(ISSUE_WATCHERS_ADD, watchersToAdd);
+ headers.put(ISSUE_WATCHERS_REMOVE, watchersToRemove);
+ template.sendBodyAndHeaders(null, headers);
+
+ Issue retrievedIssue = issueRestClient.getIssue(backendIssue.getKey()).claim();
+ assertEquals(backendIssue, retrievedIssue);
+ assertEquals(retrievedIssue.getWatchers().getNumWatchers(), backendwatchers.size());
+
+ Watchers watchers = issueRestClient.getWatchers(retrievedIssue.getWatchers().getSelf()).claim();
+ for (BasicUser user : watchers.getUsers()) {
+ assertTrue(backendwatchers.contains(user.getName()));
+ }
+ mockResult.expectedMessageCount(1);
+ mockResult.assertIsSatisfied();
+ }
+
+
+ @Configuration
+ public class TestConfiguration {
+
+
+
+ @Bean
+ public RouteBuilder routeBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:start")
+ .to("jira://watchers?jiraUrl=" + JIRA_CREDENTIALS)
+ .to(mockResult);
+ }
+ };
+ }
+
+
+ }
+
+
+
+
+}