# This file is part of sbs # # Copyright (C) 2025 Steve Milner # # SPDX-License-Identifier: GPL-3.0-or-later # Generated by Gemini 2.5 Flash import pytest from unittest.mock import patch, MagicMock from urllib import parse from sbs.ds.card import Card from sbs.ds.jql import jira @pytest.fixture def mock_response(): """ A pytest fixture to create a mock requests.Response object. """ mock_resp = MagicMock() mock_resp.status_code = 200 return mock_resp def test_jira_fetches_and_parses_single_issue(mock_response): """ Test that the jira function correctly fetches and parses a single Jira issue. """ # Define the mock JSON response from Jira API mock_jira_json = { "issues": [ { "key": "PROJ-123", "fields": { "assignee": {"name": "John Doe"}, "summary": "Implement feature A", "description": "Details for feature A implementation.", "customfield_12310243": 5, }, } ] } mock_response.json.return_value = mock_jira_json # Patch requests.get to return our mock_response with patch("requests.get", return_value=mock_response) as mock_get: endpoint = "https://jira.example.com/rest/api/2" token = "test_token" query = "project = PROJ AND status = Open" cards = list(jira(endpoint, token, query)) # Assertions expected_fjql = parse.quote_plus(query) expected_fields = parse.quote_plus( "assignee,summary,description,customfield_12310243" ) expected_url = f"{endpoint}/search/?fields={expected_fields}&maxResults=100&jql={expected_fjql}" mock_get.assert_called_once_with( expected_url, headers={"Authorization": f"Bearer {token}"} ) assert len(cards) == 1 card = cards[0] assert isinstance(card, Card) assert card.key == "PROJ-123" assert card.assignee == "John Doe" assert card.summary == "Implement feature A" assert card.description == "Details for feature A implementation." assert card.story_points == 5 def test_jira_fetches_and_parses_multiple_issues(mock_response): """ Test that the jira function correctly fetches and parses multiple Jira issues. """ mock_jira_json = { "issues": [ { "key": "PROJ-1", "fields": { "assignee": {"name": "User A"}, "summary": "Task 1", "description": "Description 1", "customfield_12310243": 3, }, }, { "key": "PROJ-2", "fields": { "assignee": {"name": "User B"}, "summary": "Task 2", "description": "Description 2", "customfield_12310243": 8, }, }, ] } mock_response.json.return_value = mock_jira_json with patch("requests.get", return_value=mock_response): cards = list(jira("dummy_endpoint", "dummy_token", "dummy_query")) assert len(cards) == 2 assert cards[0].key == "PROJ-1" assert cards[1].assignee == "User B" assert cards[1].story_points == 8 def test_jira_handles_issue_with_no_assignee(mock_response): """ Test that the jira function correctly handles issues where the assignee field is missing. It should default the assignee name to an empty string. """ mock_jira_json = { "issues": [ { "key": "PROJ-BUG", "fields": { "summary": "Untriaged Bug", "description": "This bug has no assignee.", "customfield_12310243": 1, }, } ] } mock_response.json.return_value = mock_jira_json with patch("requests.get", return_value=mock_response): cards = list(jira("dummy_endpoint", "dummy_token", "dummy_query")) assert len(cards) == 1 card = cards[0] assert card.key == "PROJ-BUG" assert card.assignee == "" # Should be an empty string assert card.summary == "Untriaged Bug" assert card.story_points == 1 def test_jira_handles_empty_issues_list(mock_response): """ Test that the jira function returns an empty list if the Jira API returns no issues. """ mock_jira_json = {"issues": []} mock_response.json.return_value = mock_jira_json with patch("requests.get", return_value=mock_response): cards = list(jira("dummy_endpoint", "dummy_token", "dummy_query")) assert len(cards) == 0 def test_jira_handles_api_error_response(mock_response): """ Test that the jira function gracefully handles a non-200 HTTP status code by letting the requests library's behavior (e.g., raise_for_status) propagate or by handling potential JSON decoding errors. """ mock_response.status_code = 404 # Simulate a JSON structure even for an error, as .json() is always called mock_response.json.return_value = {"errorMessages": ["Issue Does Not Exist"]} # Patch requests.get to return our mock_response with patch("requests.get", return_value=mock_response) as mock_get: endpoint = "https://jira.example.com/rest/api/2" token = "test_token" query = "project = NONEXISTENT" # The original code calls .json() directly, so it expects JSON. # If the API returns a non-200 status and valid JSON, the code # will proceed. If it returns non-JSON, .json() would raise an error. # For this test, we assume .json() works, and we are testing # how the internal logic handles the 'issues' key not being present # or the structure being different. # In this specific case, the KeyError will be raised because 'issues' # is not in the mock_jira_json for an error scenario. list() is used to # call the underlying __next__(). with pytest.raises(KeyError): list(jira(endpoint, token, query)) expected_fjql = parse.quote_plus(query) expected_fields = parse.quote_plus( "assignee,summary,description,customfield_12310243" ) expected_url = f"{endpoint}/search/?fields={expected_fields}&maxResults=100&jql={expected_fjql}" mock_get.assert_called_once_with( expected_url, headers={"Authorization": f"Bearer {token}"} )