66from unittest import mock
77
88from elastalert .alerters .jira import JiraFormattedMatchString , JiraAlerter
9- from elastalert .util import ts_now
9+ from elastalert .util import ts_now , EAException
1010from tests .alerts_test import mock_rule
1111
1212
@@ -90,14 +90,15 @@ def test_jira(caplog):
9090 mock .patch ('elastalert.alerters.jira.read_yaml' ) as mock_open :
9191 mock_open .return_value = {'user' : 'jirauser' , 'password' : 'jirapassword' }
9292 mock_jira .return_value = mock .Mock ()
93- mock_jira .return_value .search_issues .return_value = []
93+ # Mock enhanced_search_issues method
94+ mock_jira .return_value .enhanced_search_issues .return_value = []
9495 mock_jira .return_value .priorities .return_value = [mock_priority ]
9596 mock_jira .return_value .fields .return_value = []
9697
9798 alert = JiraAlerter (rule )
9899 alert .alert ([{'test_term' : 'test_value' , '@timestamp' : '2014-10-31T00:00:00' }])
99100
100- expected .insert (3 , mock .call ().search_issues (mock .ANY ))
101+ expected .insert (3 , mock .call ().enhanced_search_issues (mock .ANY ))
101102 assert mock_jira .mock_calls == expected
102103
103104 # Remove a field if jira_ignore_in_title set
@@ -106,21 +107,24 @@ def test_jira(caplog):
106107 mock .patch ('elastalert.alerters.jira.read_yaml' ) as mock_open :
107108 mock_open .return_value = {'user' : 'jirauser' , 'password' : 'jirapassword' }
108109 mock_jira .return_value = mock .Mock ()
109- mock_jira .return_value .search_issues .return_value = []
110+ # Mock enhanced_search_issues method
111+ mock_jira .return_value .enhanced_search_issues .return_value = []
110112 mock_jira .return_value .priorities .return_value = [mock_priority ]
111113 mock_jira .return_value .fields .return_value = []
112114
113115 alert = JiraAlerter (rule )
114116 alert .alert ([{'test_term' : 'test_value' , '@timestamp' : '2014-10-31T00:00:00' }])
115117
118+ # Check that 'test_value' was removed from the JQL query when jira_ignore_in_title is set
116119 assert 'test_value' not in mock_jira .mock_calls [3 ][1 ][0 ]
117120
118- # Issue is still created if search_issues throws an exception
121+ # Issue is still created if enhanced_search_issues throws an exception
119122 with mock .patch ('elastalert.alerters.jira.JIRA' ) as mock_jira , \
120123 mock .patch ('elastalert.alerters.jira.read_yaml' ) as mock_open :
121124 mock_open .return_value = {'user' : 'jirauser' , 'password' : 'jirapassword' }
122125 mock_jira .return_value = mock .Mock ()
123- mock_jira .return_value .search_issues .side_effect = JIRAError
126+ # Mock enhanced_search_issues to raise an exception
127+ mock_jira .return_value .enhanced_search_issues .side_effect = JIRAError
124128 mock_jira .return_value .priorities .return_value = [mock_priority ]
125129 mock_jira .return_value .fields .return_value = []
126130
@@ -143,6 +147,8 @@ def test_jira(caplog):
143147 mock .patch ('elastalert.alerters.jira.read_yaml' ) as mock_open :
144148 mock_open .return_value = {'user' : 'jirauser' , 'password' : 'jirapassword' }
145149 mock_jira .return_value = mock .Mock ()
150+ # Mock both search methods
151+ mock_jira .return_value .enhanced_search_issues .return_value = [mock_issue ]
146152 mock_jira .return_value .search_issues .return_value = [mock_issue ]
147153 mock_jira .return_value .priorities .return_value = [mock_priority ]
148154 mock_jira .return_value .fields .return_value = []
@@ -159,6 +165,8 @@ def test_jira(caplog):
159165 mock .patch ('elastalert.alerters.jira.read_yaml' ) as mock_open :
160166 mock_open .return_value = {'user' : 'jirauser' , 'password' : 'jirapassword' }
161167 mock_jira .return_value = mock .Mock ()
168+ # Mock both search methods
169+ mock_jira .return_value .enhanced_search_issues .return_value = [mock_issue ]
162170 mock_jira .return_value .search_issues .return_value = [mock_issue ]
163171 mock_jira .return_value .priorities .return_value = [mock_priority ]
164172 mock_jira .return_value .fields .return_value = []
@@ -194,6 +202,8 @@ def test_jira(caplog):
194202 mock .patch ('elastalert.alerters.jira.read_yaml' ) as mock_open :
195203 mock_open .return_value = {'user' : 'jirauser' , 'password' : 'jirapassword' }
196204 mock_jira .return_value = mock .Mock ()
205+ # Mock both search methods
206+ mock_jira .return_value .enhanced_search_issues .return_value = [mock_issue ]
197207 mock_jira .return_value .search_issues .return_value = [mock_issue ]
198208 mock_jira .return_value .fields .return_value = mock_fields
199209 mock_jira .return_value .priorities .return_value = [mock_priority ]
@@ -490,3 +500,159 @@ def test_create_subtask(caplog):
490500 assert 'elastalert' == user
491501 assert logging .INFO == level
492502 assert 'pened Jira ticket:' in message
503+
504+
505+ def test_jira_enhanced_search_when_available ():
506+ """Test that enhanced_search_issues is used when available"""
507+ rule = {
508+ 'name' : 'test alert' ,
509+ 'jira_account_file' : 'jirafile' ,
510+ 'type' : mock_rule (),
511+ 'jira_project' : 'testproject' ,
512+ 'jira_priority' : 0 ,
513+ 'jira_issuetype' : 'testtype' ,
514+ 'jira_server' : 'https://test.atlassian.net' ,
515+ 'jira_bump_tickets' : True ,
516+ 'jira_max_age' : 30 ,
517+ 'timestamp_field' : '@timestamp' ,
518+ 'rule_file' : '/tmp/foo.yaml'
519+ }
520+
521+ mock_priority = mock .Mock (id = '5' )
522+ mock_issue = mock .Mock ()
523+ mock_issue .key = 'TEST-123'
524+
525+ with mock .patch ('elastalert.alerters.jira.JIRA' ) as mock_jira , \
526+ mock .patch ('elastalert.alerters.jira.read_yaml' ) as mock_open :
527+
528+ mock_open .return_value = {'user' : 'jirauser' , 'password' : 'jirapassword' }
529+ mock_jira .return_value .priorities .return_value = [mock_priority ]
530+ mock_jira .return_value .fields .return_value = []
531+
532+ # Mock enhanced_search_issues method
533+ mock_jira .return_value .enhanced_search_issues = mock .Mock (return_value = [mock_issue ])
534+
535+ alert = JiraAlerter (rule )
536+ result = alert .find_existing_ticket ([{'test' : 'value' , '@timestamp' : '2014-10-31T00:00:00' }])
537+
538+ # Verify enhanced_search_issues was called
539+ assert mock_jira .return_value .enhanced_search_issues .called
540+ call_args = mock_jira .return_value .enhanced_search_issues .call_args
541+ assert 'project=testproject' in call_args [0 ][0 ] # First positional argument
542+
543+ # Verify legacy search was NOT called
544+ assert not hasattr (mock_jira .return_value , 'search_issues' ) or not mock_jira .return_value .search_issues .called
545+
546+ # Verify the result
547+ assert result is not None
548+ assert result .key == 'TEST-123'
549+
550+
551+ def test_jira_enhanced_search_not_available_error ():
552+ """Test that Jira alerter raises error when enhanced_search_issues is not available"""
553+ rule = {
554+ 'name' : 'test alert' ,
555+ 'jira_account_file' : 'jirafile' ,
556+ 'type' : mock_rule (),
557+ 'jira_project' : 'testproject' ,
558+ 'jira_priority' : 0 ,
559+ 'jira_issuetype' : 'testtype' ,
560+ 'jira_server' : 'https://test.atlassian.net' ,
561+ 'jira_bump_tickets' : True ,
562+ 'jira_max_age' : 30 ,
563+ 'timestamp_field' : '@timestamp' ,
564+ 'rule_file' : '/tmp/foo.yaml'
565+ }
566+
567+ mock_priority = mock .Mock (id = '5' )
568+
569+ with mock .patch ('elastalert.alerters.jira.JIRA' ) as mock_jira , \
570+ mock .patch ('elastalert.alerters.jira.read_yaml' ) as mock_open :
571+
572+ mock_open .return_value = {'user' : 'jirauser' , 'password' : 'jirapassword' }
573+ mock_jira .return_value .priorities .return_value = [mock_priority ]
574+ mock_jira .return_value .fields .return_value = []
575+
576+ # Simulate enhanced_search_issues not being available
577+ if hasattr (mock_jira .return_value , 'enhanced_search_issues' ):
578+ delattr (mock_jira .return_value , 'enhanced_search_issues' )
579+
580+ alert = JiraAlerter (rule )
581+
582+ # Should raise EAException when enhanced_search_issues is not available
583+ with pytest .raises (EAException , match = "enhanced_search_issues method not available" ):
584+ alert .find_existing_ticket ([{'test' : 'value' , '@timestamp' : '2014-10-31T00:00:00' }])
585+
586+
587+ def test_jira_error_handling ():
588+ """Test error handling when search methods throw exceptions"""
589+ rule = {
590+ 'name' : 'test alert' ,
591+ 'jira_account_file' : 'jirafile' ,
592+ 'type' : mock_rule (),
593+ 'jira_project' : 'testproject' ,
594+ 'jira_priority' : 0 ,
595+ 'jira_issuetype' : 'testtype' ,
596+ 'jira_server' : 'https://test.atlassian.net' ,
597+ 'jira_bump_tickets' : True ,
598+ 'jira_max_age' : 30 ,
599+ 'timestamp_field' : '@timestamp' ,
600+ 'rule_file' : '/tmp/foo.yaml'
601+ }
602+
603+ mock_priority = mock .Mock (id = '5' )
604+
605+ with mock .patch ('elastalert.alerters.jira.JIRA' ) as mock_jira , \
606+ mock .patch ('elastalert.alerters.jira.read_yaml' ) as mock_open :
607+
608+ mock_open .return_value = {'user' : 'jirauser' , 'password' : 'jirapassword' }
609+ mock_jira .return_value .priorities .return_value = [mock_priority ]
610+ mock_jira .return_value .fields .return_value = []
611+
612+ # Mock enhanced_search_issues to raise an exception
613+ mock_jira .return_value .enhanced_search_issues = mock .Mock (side_effect = JIRAError ("API Error" ))
614+
615+ alert = JiraAlerter (rule )
616+ result = alert .find_existing_ticket ([{'test' : 'value' , '@timestamp' : '2014-10-31T00:00:00' }])
617+
618+ # Verify enhanced_search_issues was called but returned None due to error
619+ assert mock_jira .return_value .enhanced_search_issues .called
620+ assert result is None
621+
622+
623+ def test_jira_no_results ():
624+ """Test that search returns None when no tickets are found"""
625+ rule = {
626+ 'name' : 'test alert' ,
627+ 'jira_account_file' : 'jirafile' ,
628+ 'type' : mock_rule (),
629+ 'jira_project' : 'testproject' ,
630+ 'jira_priority' : 0 ,
631+ 'jira_issuetype' : 'testtype' ,
632+ 'jira_server' : 'https://test.atlassian.net' ,
633+ 'jira_bump_tickets' : True ,
634+ 'jira_max_age' : 30 ,
635+ 'timestamp_field' : '@timestamp' ,
636+ 'rule_file' : '/tmp/foo.yaml'
637+ }
638+
639+ mock_priority = mock .Mock (id = '5' )
640+
641+ with mock .patch ('elastalert.alerters.jira.JIRA' ) as mock_jira , \
642+ mock .patch ('elastalert.alerters.jira.read_yaml' ) as mock_open :
643+
644+ mock_open .return_value = {'user' : 'jirauser' , 'password' : 'jirapassword' }
645+ mock_jira .return_value .priorities .return_value = [mock_priority ]
646+ mock_jira .return_value .fields .return_value = []
647+
648+ # Mock enhanced_search_issues to return empty list
649+ mock_jira .return_value .enhanced_search_issues = mock .Mock (return_value = [])
650+
651+ alert = JiraAlerter (rule )
652+ result = alert .find_existing_ticket ([{'test' : 'value' , '@timestamp' : '2014-10-31T00:00:00' }])
653+
654+ # Verify enhanced_search_issues was called
655+ assert mock_jira .return_value .enhanced_search_issues .called
656+
657+ # Verify no result was returned
658+ assert result is None
0 commit comments