diff --git a/.github/workflows/jira_cloud_ci.yml b/.github/workflows/jira_cloud_ci.yml index bdd71d618..859011d3d 100644 --- a/.github/workflows/jira_cloud_ci.yml +++ b/.github/workflows/jira_cloud_ci.yml @@ -1,6 +1,11 @@ name: cloud on: + pull_request: + push: + branches: + - main + - "*" workflow_call: inputs: ref: @@ -39,7 +44,8 @@ jobs: steps: - uses: actions/checkout@v5 with: - ref: ${{ github.event.inputs.ref }} + # Use correct ref for PRs and pushes + ref: ${{ github.head_ref || github.ref_name || github.event.inputs.ref }} - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v6 @@ -57,10 +63,10 @@ jobs: run: tox run -e py310 -- -m allow_on_cloud env: CI_JIRA_TYPE: CLOUD - CI_JIRA_CLOUD_ADMIN: ${{ secrets.CLOUD_ADMIN }} - CI_JIRA_CLOUD_ADMIN_TOKEN: ${{ secrets.CLOUD_ADMIN_TOKEN }} - CI_JIRA_CLOUD_USER: ${{ secrets.CLOUD_USER }} - CI_JIRA_CLOUD_USER_TOKEN: ${{ secrets.CLOUD_USER_TOKEN }} + CI_JIRA_CLOUD_ADMIN: ${{ secrets.CI_JIRA_CLOUD_ADMIN }} + CI_JIRA_CLOUD_ADMIN_TOKEN: ${{ secrets.CI_JIRA_CLOUD_ADMIN_TOKEN }} + CI_JIRA_CLOUD_USER: ${{ secrets.CI_JIRA_CLOUD_USER }} + CI_JIRA_CLOUD_USER_TOKEN: ${{ secrets.CI_JIRA_CLOUD_USER_TOKEN }} - name: Upload coverage to Codecov uses: codecov/codecov-action@v5 diff --git a/tests/resources/test_board.py b/tests/resources/test_board.py index 183d84fe2..f37b6777a 100644 --- a/tests/resources/test_board.py +++ b/tests/resources/test_board.py @@ -4,7 +4,7 @@ from contextlib import contextmanager from jira.resources import Board -from tests.conftest import JiraTestCase, rndstr +from tests.conftest import JiraTestCase, allow_on_cloud, rndstr class BoardTests(JiraTestCase): @@ -41,6 +41,7 @@ def _create_board(self) -> Iterator[Board]: if board is not None: board.delete() + @allow_on_cloud def test_create_and_delete(self): # GIVEN: The filter # WHEN: we create a board diff --git a/tests/resources/test_comment.py b/tests/resources/test_comment.py index 31b9c3444..a9c0437f6 100644 --- a/tests/resources/test_comment.py +++ b/tests/resources/test_comment.py @@ -1,6 +1,6 @@ from __future__ import annotations -from tests.conftest import JiraTestCase +from tests.conftest import JiraTestCase, allow_on_cloud class CommentTests(JiraTestCase): @@ -15,6 +15,7 @@ def tearDown(self) -> None: for comment in self.jira.comments(issue): comment.delete() + @allow_on_cloud def test_comments(self): for issue in [self.issue_1_key, self.jira.issue(self.issue_2_key)]: self.jira.issue(issue) @@ -28,6 +29,7 @@ def test_comments(self): comments = self.jira.comments(issue) assert len(comments) == 0 + @allow_on_cloud def test_comments_start_at(self): comments_created = [] for i in range(10): @@ -42,6 +44,7 @@ def test_comments_start_at(self): comments = self.jira.comments(self.issue_1_key) assert len(comments) == 0 + @allow_on_cloud def test_comments_max_results(self): comments_created = [] for i in range(10): @@ -56,6 +59,7 @@ def test_comments_max_results(self): comments = self.jira.comments(self.issue_1_key) assert len(comments) == 0 + @allow_on_cloud def test_comments_order_by(self): comments_created = [] for i in range(10): @@ -73,6 +77,7 @@ def test_comments_order_by(self): comments = self.jira.comments(self.issue_1_key) assert len(comments) == 0 + @allow_on_cloud def test_expanded_comments(self): comment1 = self.jira.add_comment(self.issue_1_key, "First comment") comment2 = self.jira.add_comment(self.issue_1_key, "Second comment") @@ -89,6 +94,7 @@ def test_expanded_comments(self): comments = self.jira.comments(self.issue_1_key) assert len(comments) == 0 + @allow_on_cloud def test_add_comment(self): comment = self.jira.add_comment( self.issue_3_key, @@ -100,6 +106,7 @@ def test_add_comment(self): self.assertEqual(comment.visibility.value, "Administrators") comment.delete() + @allow_on_cloud def test_add_comment_with_issue_obj(self): issue = self.jira.issue(self.issue_3_key) comment = self.jira.add_comment( @@ -112,6 +119,7 @@ def test_add_comment_with_issue_obj(self): self.assertEqual(comment.visibility.value, "Administrators") comment.delete() + @allow_on_cloud def test_update_comment(self): comment = self.jira.add_comment(self.issue_3_key, "updating soon!") comment.update(body="updated!") @@ -120,6 +128,7 @@ def test_update_comment(self): # self.assertEqual(comment.visibility.value, 'Administrators') comment.delete() + @allow_on_cloud def test_update_comment_with_notify(self): comment = self.jira.add_comment(self.issue_3_key, "updating soon!") comment.update(body="updated! without notification", notify=False) diff --git a/tests/resources/test_component.py b/tests/resources/test_component.py index 784403dde..83cc5aa06 100644 --- a/tests/resources/test_component.py +++ b/tests/resources/test_component.py @@ -1,7 +1,7 @@ from __future__ import annotations from jira.exceptions import JIRAError -from tests.conftest import JiraTestCase, rndstr +from tests.conftest import JiraTestCase, allow_on_cloud, rndstr class ComponentTests(JiraTestCase): @@ -10,6 +10,7 @@ def setUp(self): self.issue_1 = self.test_manager.project_b_issue1 self.issue_2 = self.test_manager.project_b_issue2 + @allow_on_cloud def test_2_create_component(self): proj = self.jira.project(self.project_b) name = f"project-{proj}-component-{rndstr()}" @@ -67,6 +68,7 @@ def test_3_update(self): self.assertEqual(component.lead.name, self.jira.current_user()) component.delete() + @allow_on_cloud def test_4_delete(self): component = self.jira.create_component( "To be deleted", self.project_b, description="not long for this world" @@ -75,6 +77,7 @@ def test_4_delete(self): component.delete() self.assertRaises(JIRAError, self.jira.component, myid) + @allow_on_cloud def test_delete_component_by_id(self): component = self.jira.create_component( "To be deleted", self.project_b, description="not long for this world" diff --git a/tests/resources/test_custom_field_option.py b/tests/resources/test_custom_field_option.py index 7d564861a..cf43fb9a7 100644 --- a/tests/resources/test_custom_field_option.py +++ b/tests/resources/test_custom_field_option.py @@ -1,9 +1,13 @@ from __future__ import annotations -from tests.conftest import JiraTestCase +from tests.conftest import JiraTestCase, allow_on_cloud class CustomFieldOptionTests(JiraTestCase): + @allow_on_cloud def test_custom_field_option(self): + expected = "Extensive / Widespread" + if not self.jira._is_cloud: + expected = "To Do" option = self.jira.custom_field_option("10000") - self.assertEqual(option.value, "To Do") + self.assertEqual(option.value, expected) diff --git a/tests/resources/test_dashboard.py b/tests/resources/test_dashboard.py index a282f3538..8cd8b2b5c 100644 --- a/tests/resources/test_dashboard.py +++ b/tests/resources/test_dashboard.py @@ -1,5 +1,7 @@ from __future__ import annotations +import time +from contextlib import suppress from unittest import mock import pytest @@ -35,7 +37,8 @@ def setUp(self): def tearDown(self): for dashboard in self.dashboards_to_delete: - dashboard.delete() + with suppress(JIRAError): + dashboard.delete() super().tearDown() def test_dashboards(self): @@ -261,6 +264,7 @@ def test_update_gadget(self): name=rndstr(), share_permissions=[{"type": "authenticated"}] ) self.dashboards_to_delete.append(dashboard) + time.sleep(1) available_gadgets = self.jira.all_dashboard_gadgets() filter_gadget = next( gadget for gadget in available_gadgets if gadget.title == self.gadget_title @@ -271,7 +275,7 @@ def test_update_gadget(self): ignore_uri_and_module_key_validation=True, uri=filter_gadget.uri, ) - + time.sleep(1) gadget = gadget.update(dashboard.id, color=new_color) self.assertEqual(gadget.color, new_color) self.assertEqual(gadget.raw["color"], new_color) diff --git a/tests/resources/test_epic.py b/tests/resources/test_epic.py index 8d82c2b86..f1c2ed87a 100644 --- a/tests/resources/test_epic.py +++ b/tests/resources/test_epic.py @@ -7,7 +7,7 @@ from parameterized import parameterized from jira.resources import Issue -from tests.conftest import JiraTestCase, rndstr +from tests.conftest import JiraTestCase, allow_on_cloud, rndstr class EpicTests(JiraTestCase): @@ -33,13 +33,16 @@ def epic_field_name(self): def make_epic(self, **kwargs) -> Iterator[Issue]: try: # TODO: create_epic() method should exist! - new_epic = self.jira.create_issue( - fields={ + new_epic_fields = { "issuetype": {"name": "Epic"}, "project": self.project_b, - self.epic_field_name: self.epic_name, "summary": f"Epic summary for '{self.epic_name}'", - }, + } + if not self.is_jira_cloud_ci: + new_epic_fields[self.epic_field_name] = self.epic_name + new_epic_fields.update(kwargs) + new_epic = self.jira.create_issue( + fields=new_epic_fields, ) if len(kwargs): raise ValueError("Incorrect kwarg used !") @@ -47,10 +50,12 @@ def make_epic(self, **kwargs) -> Iterator[Issue]: finally: new_epic.delete() + @allow_on_cloud def test_epic_create_delete(self): with self.make_epic(): pass + @allow_on_cloud @parameterized.expand( [("str", str), ("list", list)], ) diff --git a/tests/resources/test_filter.py b/tests/resources/test_filter.py index 0bdfdb05f..5151bc3f7 100644 --- a/tests/resources/test_filter.py +++ b/tests/resources/test_filter.py @@ -1,8 +1,9 @@ from __future__ import annotations +import time from contextlib import contextmanager -from tests.conftest import JiraTestCase, rndstr +from tests.conftest import JiraTestCase, allow_on_cloud, rndstr class FilterTests(JiraTestCase): @@ -27,21 +28,27 @@ def make_filter(self, **kwargs): ) if len(kwargs): raise ValueError("Incorrect kwarg used !") + if self.jira._is_cloud: + time.sleep(1) yield new_filter finally: new_filter.delete() + @allow_on_cloud def test_filter(self): with self.make_filter() as myfilter: self.assertEqual(myfilter.name, self.filter_name) - self.assertEqual(myfilter.owner.name, self.test_manager.user_admin.name) + if not self.is_jira_cloud_ci: + self.assertEqual(myfilter.owner.name, self.test_manager.user_admin.name) + @allow_on_cloud def test_favourite_filters(self): filter_name = f"filter-to-fav-{self.filter_name}" with self.make_filter(name=filter_name, favourite=True): new_filters = self.jira.favourite_filters() assert filter_name in [f.name for f in new_filters] + @allow_on_cloud def test_filter_update_empty_description(self): new_jql = f"{self.filter_jql} ORDER BY created ASC" new_name = f"new_{self.filter_name}" @@ -58,6 +65,7 @@ def test_filter_update_empty_description(self): assert updated_filter.jql == new_jql assert not hasattr(updated_filter, "description") + @allow_on_cloud def test_filter_update_empty_description_with_new_description(self): new_desc = "new description" with self.make_filter(description=None) as myfilter: diff --git a/tests/resources/test_generic_resource.py b/tests/resources/test_generic_resource.py index 383e61eb1..126684907 100644 --- a/tests/resources/test_generic_resource.py +++ b/tests/resources/test_generic_resource.py @@ -3,6 +3,7 @@ import pytest import jira.resources +from tests.conftest import allow_on_cloud MOCK_URL = "http://customized-jira.com/rest/" @@ -43,6 +44,7 @@ class TestResource: ], ) # fmt: on + @allow_on_cloud def test_cls_for_resource(self, example_url, expected_class): """Test the regex recognizes the right class for a given URL.""" assert jira.resources.cls_for_resource(example_url) == expected_class diff --git a/tests/resources/test_issue.py b/tests/resources/test_issue.py index 27d3d67cd..a70f4be20 100644 --- a/tests/resources/test_issue.py +++ b/tests/resources/test_issue.py @@ -1,16 +1,20 @@ from __future__ import annotations import logging +import time from jira.exceptions import JIRAError -from tests.conftest import JiraTestCase, find_by_key, find_by_key_value +from tests.conftest import JiraTestCase, allow_on_cloud, find_by_key, find_by_key_value LOGGER = logging.getLogger(__name__) class IssueTests(JiraTestCase): + sleeptime = 0 def setUp(self): JiraTestCase.setUp(self) + if self.jira._is_cloud: + self.sleeptime = 2 self.issue_1 = self.test_manager.project_b_issue1 self.issue_2 = self.test_manager.project_b_issue2 self.issue_3 = self.test_manager.project_b_issue3 @@ -20,16 +24,19 @@ def test_issue(self): self.assertEqual(issue.key, self.issue_1) self.assertEqual(issue.fields.summary, f"issue 1 from {self.project_b}") + @allow_on_cloud def test_issue_search_finds_issue(self): issues = self.jira.search_issues(f"key={self.issue_1}") self.assertEqual(self.issue_1, issues[0].key) + @allow_on_cloud def test_issue_search_return_type(self): issues = self.jira.search_issues(f"key={self.issue_1}") self.assertIsInstance(issues, list) issues = self.jira.search_issues(f"key={self.issue_1}", json_result=True) self.assertIsInstance(issues, dict) + @allow_on_cloud def test_issue_search_only_includes_provided_fields(self): issues = self.jira.search_issues( f"key={self.issue_1}", fields="comment,assignee" @@ -38,11 +45,12 @@ def test_issue_search_only_includes_provided_fields(self): self.assertTrue(hasattr(issues[0].fields, "assignee")) self.assertFalse(hasattr(issues[0].fields, "reporter")) + @allow_on_cloud def test_issue_search_default_behaviour_included_fields(self): search_str = f"key={self.issue_1}" issues = self.jira.search_issues(search_str) - self.assertTrue(hasattr(issues[0].fields, "reporter")) - self.assertTrue(hasattr(issues[0].fields, "comment")) + self.assertTrue(hasattr(issues[0].fields, "summary")) + self.assertTrue(hasattr(issues[0].fields, "description")) # fields=None should be valid and return all fields (ie. default behavior) self.assertEqual( @@ -50,6 +58,7 @@ def test_issue_search_default_behaviour_included_fields(self): self.jira.search_issues(search_str, fields=None), ) + @allow_on_cloud def test_issue_get_field(self): issue = self.jira.issue(self.issue_1) self.assertEqual( @@ -62,6 +71,7 @@ def test_issue_get_field(self): with self.assertRaisesRegex(AttributeError, "customfield_1234"): issue.get_field("customfield_1234") + @allow_on_cloud def test_issue_field_limiting(self): issue = self.jira.issue(self.issue_2, fields="summary,comment") self.assertEqual(issue.fields.summary, f"issue 2 from {self.project_b}") @@ -76,14 +86,17 @@ def test_issue_field_limiting(self): comment2.delete() comment3.delete() + @allow_on_cloud def test_issue_equal(self): issue1 = self.jira.issue(self.issue_1) issue2 = self.jira.issue(self.issue_2) + time.sleep(self.sleeptime) issues = self.jira.search_issues(f"key={self.issue_1}") self.assertTrue(issue1 is not None) self.assertTrue(issue1 == issues[0]) self.assertFalse(issue2 == issues[0]) + @allow_on_cloud def test_issue_expand(self): issue = self.jira.issue(self.issue_1, expand="editmeta,schema") self.assertTrue(hasattr(issue, "editmeta")) @@ -91,6 +104,7 @@ def test_issue_expand(self): # testing for changelog is not reliable because it may exist or not based on test order # self.assertFalse(hasattr(issue, 'changelog')) + @allow_on_cloud def test_create_issue_with_fieldargs(self): issue = self.jira.create_issue( summary="Test issue created", @@ -105,6 +119,7 @@ def test_create_issue_with_fieldargs(self): # self.assertEqual(issue.fields.customfield_10022, 'XSS') issue.delete() + @allow_on_cloud def test_create_issue_with_fielddict(self): fields = { "summary": "Issue created from field dict", @@ -123,6 +138,7 @@ def test_create_issue_with_fielddict(self): self.assertEqual(issue.fields.priority.name, "High") issue.delete() + @allow_on_cloud def test_create_issue_without_prefetch(self): issue = self.jira.create_issue( summary="Test issue created", @@ -137,6 +153,7 @@ def test_create_issue_without_prefetch(self): assert "fields" not in issue.raw issue.delete() + @allow_on_cloud def test_create_issues(self): field_list = [ { @@ -232,6 +249,7 @@ def test_create_issues_one_failure(self): if issue["issue"] is not None: issue["issue"].delete() + @allow_on_cloud def test_create_issues_without_prefetch(self): field_list = [ dict( @@ -257,6 +275,7 @@ def test_create_issues_without_prefetch(self): assert "fields" not in issues[1]["issue"].raw for issue in issues: issue["issue"].delete() + time.sleep(1) def test_create_issue_with_integer_issuetype(self): # take first existing issuetype to avoid problems due to hardcoded name/id later @@ -281,6 +300,7 @@ def test_create_issue_with_issue_type_name(self): ) self.assertEqual(issue.get_field("issuetype").name, dyn_it.name) + @allow_on_cloud def test_update_with_fieldargs(self): issue = self.jira.create_issue( summary="Test issue for updating with fieldargs", @@ -301,6 +321,7 @@ def test_update_with_fieldargs(self): self.assertEqual(issue.fields.project.key, self.project_b) issue.delete() + @allow_on_cloud def test_update_with_fielddict(self): issue = self.jira.create_issue( summary="Test issue for updating with fielddict", @@ -323,6 +344,7 @@ def test_update_with_fielddict(self): self.assertEqual(issue.fields.priority.name, "High") issue.delete() + @allow_on_cloud def test_update_with_label(self): issue = self.jira.create_issue( summary="Test issue for updating labels", @@ -353,6 +375,7 @@ def test_update_label_with_proxy(self): issue.update(fields=fields) self.assertEqual(issue.fields.labels, ["testLabel"]) + @allow_on_cloud def test_update_with_bad_label(self): issue = self.jira.create_issue( summary="Test issue for updating bad labels", @@ -367,6 +390,7 @@ def test_update_with_bad_label(self): self.assertRaises(JIRAError, issue.update, fields=fields) + @allow_on_cloud def test_update_with_notify_false(self): issue = self.jira.create_issue( summary="Test issue for updating wiith notify false", @@ -378,6 +402,7 @@ def test_update_with_notify_false(self): self.assertEqual(issue.fields.description, "Now updated, but silently") issue.delete() + @allow_on_cloud def test_delete(self): issue = self.jira.create_issue( summary="Test issue created", @@ -403,17 +428,20 @@ def test_delete_with_proxy(self): issue.delete() self.assertRaises(JIRAError, self.jira.issue, key) + @allow_on_cloud def test_createmeta(self): meta = self.jira.createmeta() proj = find_by_key(meta["projects"], self.project_b) # we assume that this project should allow at least one issue type self.assertGreaterEqual(len(proj["issuetypes"]), 1) + @allow_on_cloud def test_createmeta_filter_by_projectkey_and_name(self): meta = self.jira.createmeta(projectKeys=self.project_b, issuetypeNames="Bug") self.assertEqual(len(meta["projects"]), 1) self.assertEqual(len(meta["projects"][0]["issuetypes"]), 1) + @allow_on_cloud def test_createmeta_filter_by_projectkeys_and_name(self): meta = self.jira.createmeta( projectKeys=(self.project_a, self.project_b), issuetypeNames="Task" @@ -422,6 +450,7 @@ def test_createmeta_filter_by_projectkeys_and_name(self): for project in meta["projects"]: self.assertEqual(len(project["issuetypes"]), 1) + @allow_on_cloud def test_createmeta_filter_by_id(self): projects = self.jira.projects() proja = find_by_key_value(projects, self.project_a) @@ -452,6 +481,7 @@ def test_createmeta_filter_by_id(self): len(project["issuetypes"]), len(for_lookup_common_issue_ids) ) + @allow_on_cloud def test_createmeta_expand(self): # limit to SCR project so the call returns promptly meta = self.jira.createmeta( @@ -473,6 +503,7 @@ def test_assign_issue_with_issue_obj(self): self.jira.issue(self.issue_1).fields.assignee.name, self.user_normal.name ) + @allow_on_cloud def test_assign_to_bad_issue_raises(self): self.assertRaises(JIRAError, self.jira.assign_issue, "NOPE-1", "notauser") @@ -498,6 +529,7 @@ def test_assign_issue_automatic(self): # Then: the issue has the default assignee (the admin user) self.assertEqual(self.jira.issue(self.issue_1).fields.assignee, self.user_admin) + @allow_on_cloud def test_editmeta(self): expected_fields = { "assignee", @@ -517,6 +549,7 @@ def test_editmeta(self): meta_field_set.intersection(expected_fields), expected_fields ) + @allow_on_cloud def test_transitioning(self): # we check with both issue-as-string or issue-as-object transitions = [] @@ -557,6 +590,7 @@ def test_transitioning(self): # self.assertEqual(issue.fields.assignee.name, self.test_manager.CI_JIRA_USER) # self.assertEqual(issue.fields.status.id, transition_id) + @allow_on_cloud def test_rank(self): def get_issues_ordered_by_rank(): """Search for the issues, returned in the order determined by their rank.""" @@ -565,13 +599,16 @@ def get_issues_ordered_by_rank(): ) self.jira.rank(self.issue_1, next_issue=self.issue_2) + time.sleep(self.sleeptime) issues = get_issues_ordered_by_rank() assert (issues[0].key, issues[1].key) == (self.issue_1, self.issue_2) self.jira.rank(self.issue_2, next_issue=self.issue_1) + time.sleep(self.sleeptime) issues = get_issues_ordered_by_rank() assert (issues[0].key, issues[1].key) == (self.issue_2, self.issue_1) self.jira.rank(self.issue_2, prev_issue=self.issue_1) + time.sleep(self.sleeptime) issues = get_issues_ordered_by_rank() assert (issues[0].key, issues[1].key) == (self.issue_1, self.issue_2) diff --git a/tests/resources/test_issue_property.py b/tests/resources/test_issue_property.py index 506e31168..812ad43b6 100644 --- a/tests/resources/test_issue_property.py +++ b/tests/resources/test_issue_property.py @@ -1,8 +1,9 @@ from __future__ import annotations -from tests.conftest import JiraTestCase +from tests.conftest import JiraTestCase, allow_on_cloud +@allow_on_cloud class IssuePropertyTests(JiraTestCase): def setUp(self): JiraTestCase.setUp(self) @@ -19,5 +20,6 @@ def test_issue_property(self): self.assertEqual(prop.key, "custom-property") self.assertEqual(prop.value, "Testing a property value") prop.delete() - properties = self.jira.issue_properties(self.issue_1) - self.assertEqual(len(properties), 0) + if not self.jira._is_cloud: + properties = self.jira.issue_properties(self.issue_1) + self.assertEqual(len(properties), 0) diff --git a/tests/resources/test_priority.py b/tests/resources/test_priority.py index cba042768..17f8d2071 100644 --- a/tests/resources/test_priority.py +++ b/tests/resources/test_priority.py @@ -1,8 +1,9 @@ from __future__ import annotations -from tests.conftest import JiraTestCase +from tests.conftest import JiraTestCase, allow_on_cloud +@allow_on_cloud class PrioritiesTests(JiraTestCase): def test_priorities(self): priorities = self.jira.priorities() diff --git a/tests/resources/test_project.py b/tests/resources/test_project.py index 19d14d64a..fdfdf6acb 100644 --- a/tests/resources/test_project.py +++ b/tests/resources/test_project.py @@ -1,24 +1,29 @@ from __future__ import annotations from jira import JIRAError -from tests.conftest import JiraTestCase, find_by_id, rndstr +from tests.conftest import JiraTestCase, allow_on_cloud, find_by_id, rndstr class ProjectTests(JiraTestCase): + + @allow_on_cloud def test_projects(self): projects = self.jira.projects() self.assertGreaterEqual(len(projects), 2) + @allow_on_cloud def test_project(self): project = self.jira.project(self.project_b) self.assertEqual(project.key, self.project_b) + @allow_on_cloud def test_project_expand(self): project = self.jira.project(self.project_b) self.assertFalse(hasattr(project, "projectKeys")) project = self.jira.project(self.project_b, expand="projectKeys") self.assertTrue(hasattr(project, "projectKeys")) + @allow_on_cloud def test_projects_expand(self): projects = self.jira.projects() for project in projects: @@ -91,6 +96,7 @@ def test_projects_expand(self): # avatars = self.jira.project_avatars(project) # self.assertEqual(find_selected_avatar(avatars)['id'], '10208') + @allow_on_cloud def test_project_components(self): proj = self.jira.project(self.project_b) name = f"component-{proj} from project {rndstr()}" @@ -108,6 +114,7 @@ def test_project_components(self): self.assertEqual(sample.name, name) component.delete() + @allow_on_cloud def test_project_versions(self): name = f"version-{rndstr()}" version = self.jira.create_version(name, self.project_b, "will be deleted soon") @@ -121,6 +128,7 @@ def test_project_versions(self): i.update(fields={"fixVersions": [{"id": version.id}]}) version.delete() + @allow_on_cloud def test_update_project_version(self): # given name = f"version-{rndstr()}" @@ -132,6 +140,7 @@ def test_update_project_version(self): self.assertEqual(updated_name, version.name) version.delete() + @allow_on_cloud def test_get_project_version_by_name(self): name = f"version-{rndstr()}" version = self.jira.create_version(name, self.project_b, "will be deleted soon") @@ -149,6 +158,7 @@ def test_get_project_version_by_name(self): i.update(fields={"fixVersions": [{"id": version.id}]}) version.delete() + @allow_on_cloud def test_rename_version(self): old_name = f"version-{rndstr()}" version = self.jira.create_version( @@ -171,6 +181,7 @@ def test_rename_version(self): i.update(fields={"fixVersions": [{"id": version.id}]}) version.delete() + @allow_on_cloud def test_project_versions_with_project_obj(self): name = f"version-{rndstr()}" version = self.jira.create_version(name, self.project_b, "will be deleted soon") @@ -204,6 +215,7 @@ def test_project_roles(self): self.assertIn(user.name, [a.name for a in role.actors]) self.assertIn(actor_admin, [a.name for a in role.actors]) + @allow_on_cloud def test_project_permission_scheme(self): permissionscheme = self.jira.project_permissionscheme(self.project_b) self.assertEqual(permissionscheme.name, "Default Permission Scheme") @@ -212,6 +224,7 @@ def test_project_priority_scheme(self): priorityscheme = self.jira.project_priority_scheme(self.project_b) self.assertEqual(priorityscheme.name, "Default priority scheme") + @allow_on_cloud def test_project_notification_scheme(self): notificationscheme = self.jira.project_notification_scheme(self.project_b) self.assertEqual(notificationscheme.name, "Default Notification Scheme") diff --git a/tests/resources/test_project_statuses.py b/tests/resources/test_project_statuses.py index 3f48b577d..f0a44bcbe 100644 --- a/tests/resources/test_project_statuses.py +++ b/tests/resources/test_project_statuses.py @@ -1,8 +1,9 @@ from __future__ import annotations -from tests.conftest import JiraTestCase +from tests.conftest import JiraTestCase, allow_on_cloud +@allow_on_cloud class ProjectStatusesByIssueTypeTests(JiraTestCase): def test_issue_types_for_project(self): issue_types = self.jira.issue_types_for_project(self.project_a) diff --git a/tests/resources/test_remote_link.py b/tests/resources/test_remote_link.py index 841b1f8b7..70c2de249 100644 --- a/tests/resources/test_remote_link.py +++ b/tests/resources/test_remote_link.py @@ -1,11 +1,11 @@ from __future__ import annotations from jira.exceptions import JIRAError -from tests.conftest import JiraTestCase +from tests.conftest import JiraTestCase, allow_on_cloud DEFAULT_NEW_REMOTE_LINK_OBJECT = {"url": "http://google.com", "title": "googlicious!"} - +@allow_on_cloud class RemoteLinkTests(JiraTestCase): def setUp(self): JiraTestCase.setUp(self) diff --git a/tests/resources/test_resolution.py b/tests/resources/test_resolution.py index 71e5c4cbd..aaac24138 100644 --- a/tests/resources/test_resolution.py +++ b/tests/resources/test_resolution.py @@ -1,8 +1,9 @@ from __future__ import annotations -from tests.conftest import JiraTestCase +from tests.conftest import JiraTestCase, allow_on_cloud +@allow_on_cloud class ResolutionTests(JiraTestCase): def test_resolutions(self): resolutions = self.jira.resolutions() diff --git a/tests/resources/test_sprint.py b/tests/resources/test_sprint.py index 2ccb14837..56bc0e1fa 100644 --- a/tests/resources/test_sprint.py +++ b/tests/resources/test_sprint.py @@ -8,9 +8,10 @@ from jira.exceptions import JIRAError from jira.resources import Board, Filter, Sprint -from tests.conftest import JiraTestCase, rndstr +from tests.conftest import JiraTestCase, allow_on_cloud, rndstr +@allow_on_cloud class SprintTests(JiraTestCase): def setUp(self): super().setUp() @@ -117,7 +118,7 @@ def test_add_issue_to_sprint(self): serialised_sprint = updated_issue_1.get_field(self._sprint_customfield())[0] # THEN: We find this sprint in the Sprint field of the Issue - assert f"[id={sprint.id}," in serialised_sprint + assert sprint.name == serialised_sprint.name def test_move_issue_to_backlog(self): with self._create_sprint() as sprint: diff --git a/tests/resources/test_status.py b/tests/resources/test_status.py index a76837f11..13d589870 100644 --- a/tests/resources/test_status.py +++ b/tests/resources/test_status.py @@ -1,8 +1,9 @@ from __future__ import annotations -from tests.conftest import JiraTestCase +from tests.conftest import JiraTestCase, allow_on_cloud +@allow_on_cloud class StatusTests(JiraTestCase): def test_statuses(self): found = False diff --git a/tests/resources/test_status_category.py b/tests/resources/test_status_category.py index b609c2986..ae5d4a8d9 100644 --- a/tests/resources/test_status_category.py +++ b/tests/resources/test_status_category.py @@ -1,8 +1,9 @@ from __future__ import annotations -from tests.conftest import JiraTestCase +from tests.conftest import JiraTestCase, allow_on_cloud +@allow_on_cloud class StatusCategoryTests(JiraTestCase): def test_statuscategories(self): found = False diff --git a/tests/resources/test_user.py b/tests/resources/test_user.py index 2ae636b4c..611408f58 100644 --- a/tests/resources/test_user.py +++ b/tests/resources/test_user.py @@ -178,6 +178,7 @@ def test_search_users_maxresults(self): users = self.jira.search_users(self.test_manager.CI_JIRA_USER, maxResults=1) self.assertGreaterEqual(1, len(users)) + @allow_on_cloud def test_search_allowed_users_for_issue_by_project(self): users = self.jira.search_allowed_users_for_issue( self.test_manager.CI_JIRA_USER, projectKey=self.project_a @@ -190,6 +191,7 @@ def test_search_allowed_users_for_issue_by_issue(self): self.assertGreaterEqual(len(users), 1) self.assertIsInstance(users[0], User) + @allow_on_cloud def test_search_allowed_users_for_issue_maxresults(self): users = self.jira.search_allowed_users_for_issue( "a", projectKey=self.project_b, maxResults=2 @@ -202,6 +204,7 @@ def test_search_allowed_users_for_issue_startat(self): ) self.assertGreaterEqual(len(users), 0) + @allow_on_cloud def test_add_users_to_set(self): users_set = {self.test_manager.user_admin, self.test_manager.user_admin} self.assertEqual(len(users_set), 1) diff --git a/tests/resources/test_version.py b/tests/resources/test_version.py index ae1e66dcd..adbe5d647 100644 --- a/tests/resources/test_version.py +++ b/tests/resources/test_version.py @@ -1,9 +1,10 @@ from __future__ import annotations from jira.exceptions import JIRAError -from tests.conftest import JiraTestCase +from tests.conftest import JiraTestCase, allow_on_cloud +@allow_on_cloud class VersionTests(JiraTestCase): def test_create_version(self): name = "new version " + self.project_b diff --git a/tests/resources/test_vote.py b/tests/resources/test_vote.py index b702ff6b2..ae9037104 100644 --- a/tests/resources/test_vote.py +++ b/tests/resources/test_vote.py @@ -1,8 +1,9 @@ from __future__ import annotations -from tests.conftest import JiraTestCase +from tests.conftest import JiraTestCase, allow_on_cloud +@allow_on_cloud class VoteTests(JiraTestCase): def setUp(self): JiraTestCase.setUp(self) diff --git a/tests/test_client.py b/tests/test_client.py index c7e3d72e3..8ca3aa900 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -9,7 +9,7 @@ import jira.client from jira.exceptions import JIRAError, NotJIRAInstanceError -from tests.conftest import JiraTestManager, get_unique_project_name +from tests.conftest import JiraTestManager, allow_on_cloud, get_unique_project_name @pytest.fixture() @@ -120,11 +120,12 @@ def __init__(self, status_code=404): return MockResponse - +@allow_on_cloud def test_delete_project(cl_admin, cl_normal, slug): assert cl_admin.delete_project(slug) +@allow_on_cloud def test_delete_inexistent_project(cl_admin): slug = "abogus123" with pytest.raises(JIRAError) as ex: @@ -154,6 +155,7 @@ def test_templates(cl_admin): assert templates == expected_templates +@allow_on_cloud def test_result_list(): iterable = [2, 3] startAt = 0 @@ -172,6 +174,7 @@ def test_result_list(): next(results) +@allow_on_cloud def test_result_list_if_empty(): results = jira.client.ResultList() @@ -182,6 +185,7 @@ def test_result_list_if_empty(): next(results) +@allow_on_cloud @pytest.mark.parametrize( "options_arg", [ @@ -222,7 +226,7 @@ def test_headers_unclobbered_update(options_arg, no_fields): assert session_headers[header_to_check] == expected_header_value assert session_headers[invariant_header_name] == invariant_header_value - +@allow_on_cloud def test_headers_unclobbered_update_with_no_provided_headers(no_fields): options_arg = {} # a dict with "headers" not set diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index 088ff6dc1..dde2ca3be 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -55,7 +55,7 @@ def __init__( self.url = url self.status_code = status_code - def test_jira_error_response_added(self): + def test_unit_jira_error_response_added(self): err = JIRAError( response=self.MockResponse(headers=DUMMY_HEADERS, text=DUMMY_TEXT) ) @@ -64,7 +64,7 @@ def test_jira_error_response_added(self): assert f"headers = {DUMMY_HEADERS}" in err_str assert f"text = {DUMMY_TEXT}" in err_str - def test_jira_error_malformed_response(self): + def test_unit_jira_error_malformed_response(self): # GIVEN: a malformed Response object, without headers or text set bad_repsonse = self.MalformedMockResponse() # WHEN: The JiraError's __str__ method is called @@ -74,7 +74,7 @@ def test_jira_error_malformed_response(self): assert "headers = " not in err_str assert "text = " not in err_str - def test_jira_error_request_added(self): + def test_unit_jira_error_request_added(self): err = JIRAError( request=self.MockResponse(headers=DUMMY_HEADERS, text=DUMMY_TEXT) ) @@ -83,7 +83,7 @@ def test_jira_error_request_added(self): assert f"headers = {DUMMY_HEADERS}" in err_str assert f"text = {DUMMY_TEXT}" in err_str - def test_jira_error_malformed_request(self): + def test_unit_jira_error_malformed_request(self): # GIVEN: a malformed Response object, without headers or text set bad_repsonse = self.MalformedMockResponse() # WHEN: The JiraError's __str__ method is called @@ -93,23 +93,23 @@ def test_jira_error_malformed_request(self): assert "headers = " not in err_str assert "text = " not in err_str - def test_jira_error_url_added(self): + def test_unit_jira_error_url_added(self): assert f"url: {DUMMY_URL}" in str(JIRAError(url=DUMMY_URL)) - def test_jira_error_status_code_added(self): + def test_unit_jira_error_status_code_added(self): assert f"JiraError HTTP {DUMMY_STATUS_CODE}" in str( JIRAError(status_code=DUMMY_STATUS_CODE) ) - def test_jira_error_text_added(self): + def test_unit_jira_error_text_added(self): dummy_text = "wow\tthis\nis\nso cool" assert f"text: {dummy_text}" in str(JIRAError(text=dummy_text)) - def test_jira_error_log_to_tempfile_if_env_var_set(self): + def test_unit_jira_error_log_to_tempfile_if_env_var_set(self): # GIVEN: the right env vars are set and the tempfile's filename env_vars = {"PYJIRA_LOG_TO_TEMPFILE": "so true"} - test_jira_error_filename = ( - Path(__file__).parent / "test_jira_error_log_to_tempfile.bak" + test_unit_jira_error_filename = ( + Path(__file__).parent / "test_unit_jira_error_log_to_tempfile.bak" ) # https://docs.python.org/3/library/unittest.mock.html#mock-open mocked_open = mock_open() @@ -121,19 +121,19 @@ def test_jira_error_log_to_tempfile_if_env_var_set(self): patch(f"{PATCH_BASE}.tempfile.mkstemp", autospec=True) as mock_mkstemp, patch(f"{PATCH_BASE}.open", mocked_open), ): - mock_mkstemp.return_value = 0, str(test_jira_error_filename) + mock_mkstemp.return_value = 0, str(test_unit_jira_error_filename) str(JIRAError(response=self.MockResponse(text=DUMMY_TEXT))) # THEN: the known filename is opened and contains the exception details - mocked_open.assert_called_once_with(str(test_jira_error_filename), "w") + mocked_open.assert_called_once_with(str(test_unit_jira_error_filename), "w") mock_file_stream = mocked_open() assert f"text = {DUMMY_TEXT}" in mock_file_stream.write.call_args[0][0] - def test_jira_error_log_to_tempfile_not_used_if_env_var_not_set(self): + def test_unit_jira_error_log_to_tempfile_not_used_if_env_var_not_set(self): # GIVEN: no env vars are set and the tempfile's filename env_vars = {} - test_jira_error_filename = ( - Path(__file__).parent / "test_jira_error_log_to_tempfile.bak" + test_unit_jira_error_filename = ( + Path(__file__).parent / "test_unit_jira_error_log_to_tempfile.bak" ) # https://docs.python.org/3/library/unittest.mock.html#mock-open mocked_open = mock_open() @@ -144,7 +144,7 @@ def test_jira_error_log_to_tempfile_not_used_if_env_var_not_set(self): patch(f"{PATCH_BASE}.tempfile.mkstemp", autospec=True) as mock_mkstemp, patch(f"{PATCH_BASE}.open", mocked_open), ): - mock_mkstemp.return_value = 0, str(test_jira_error_filename) + mock_mkstemp.return_value = 0, str(test_unit_jira_error_filename) str(JIRAError(response=self.MockResponse(text=DUMMY_TEXT))) # THEN: no files are opened diff --git a/tests/test_qsh.py b/tests/test_qsh.py index 805320b94..4dfc65223 100644 --- a/tests/test_qsh.py +++ b/tests/test_qsh.py @@ -43,7 +43,7 @@ def __init__(self, method, url): "repeated parameters with whitespace", ], ) -def test_qsh(method, url, expected): +def test_unit_qsh(method, url, expected): gen = QshGenerator("http://example.com") req = MockRequest(method, url) assert gen._generate_qsh(req) == expected diff --git a/tests/test_resilientsession.py b/tests/test_resilientsession.py index e49b73fd7..74d3bf310 100644 --- a/tests/test_resilientsession.py +++ b/tests/test_resilientsession.py @@ -10,7 +10,7 @@ import jira.resilientsession from jira.exceptions import JIRAError from jira.resilientsession import parse_error_msg, parse_errors -from tests.conftest import JiraTestCase +from tests.conftest import JiraTestCase, allow_on_cloud class ListLoggingHandler(logging.Handler): @@ -34,6 +34,7 @@ def setUp(self): self.loggingHandler = ListLoggingHandler() jira.resilientsession.logging.getLogger().addHandler(self.loggingHandler) + @allow_on_cloud def test_logging_with_connection_error(self): """No sensitive data shall be written to the log in case of a connection error.""" witness = "etwhpxbhfniqnbbjoqvw" # random string; hopefully unique @@ -102,7 +103,7 @@ def tearDown(self): "status_code,with_rate_limit_header,with_retry_after_header,retry_expected", status_codes_retries_test_data, ) -def test_status_codes_retries( +def test_unit_status_codes_retries( mocked_sleep_method: Mock, mocked_request_method: Mock, status_code: int, @@ -172,7 +173,7 @@ def test_status_codes_retries( "status_code,headers,content,expected_errors", errors_parsing_test_data, ) -def test_error_parsing(status_code, headers, content, expected_errors): +def test_unit_error_parsing(status_code, headers, content, expected_errors): mocked_response: Response = Response() mocked_response.status_code = status_code mocked_response.headers.update(headers) @@ -183,7 +184,7 @@ def test_error_parsing(status_code, headers, content, expected_errors): assert error_msg == ", ".join(expected_errors) -def test_passthrough_class(): +def test_unit_passthrough_class(): # GIVEN: The passthrough class and a dict of request args passthrough_class = jira.resilientsession.PassthroughRetryPrepare() my_kwargs = {"nice": "arguments"} @@ -193,7 +194,7 @@ def test_passthrough_class(): @patch("requests.Session.request") -def test_unspecified_body_remains_unspecified(mocked_request_method: Mock): +def test_unit_unspecified_body_remains_unspecified(mocked_request_method: Mock): # Disable retries for this test. session = jira.resilientsession.ResilientSession(max_retries=0) # Data is not specified here. @@ -203,7 +204,7 @@ def test_unspecified_body_remains_unspecified(mocked_request_method: Mock): @patch("requests.Session.request") -def test_nonempty_body_is_forwarded(mocked_request_method: Mock): +def test_unit_nonempty_body_is_forwarded(mocked_request_method: Mock): # Disable retries for this test. session = jira.resilientsession.ResilientSession(max_retries=0) session.get(url="mocked_url", data={"some": "fake-data"}) @@ -212,7 +213,7 @@ def test_nonempty_body_is_forwarded(mocked_request_method: Mock): @patch("requests.Session.request") -def test_with_requests_simple_timeout(mocked_request_method: Mock): +def test_unit_with_requests_simple_timeout(mocked_request_method: Mock): # Disable retries for this test. session = jira.resilientsession.ResilientSession(max_retries=0, timeout=1) session.get(url="mocked_url", data={"some": "fake-data"}) @@ -221,7 +222,7 @@ def test_with_requests_simple_timeout(mocked_request_method: Mock): @patch("requests.Session.request") -def test_with_requests_tuple_timeout(mocked_request_method: Mock): +def test_unit_with_requests_tuple_timeout(mocked_request_method: Mock): # Disable retries for this test. session = jira.resilientsession.ResilientSession(max_retries=0, timeout=(1, 3.5)) session.get(url="mocked_url", data={"some": "fake-data"}) @@ -230,7 +231,7 @@ def test_with_requests_tuple_timeout(mocked_request_method: Mock): @patch("requests.Session.request") -def test_verify_is_forwarded(mocked_request_method: Mock): +def test_unit_verify_is_forwarded(mocked_request_method: Mock): # Disable retries for this test. session = jira.resilientsession.ResilientSession(max_retries=0) @@ -245,7 +246,7 @@ def test_verify_is_forwarded(mocked_request_method: Mock): @patch("requests.Session.request") -def test_empty_dict_body_not_forwarded(mocked_request_method: Mock): +def test_unit_empty_dict_body_not_forwarded(mocked_request_method: Mock): # Disable retries for this test. session = jira.resilientsession.ResilientSession(max_retries=0) # Empty dictionary should not be converted to JSON