"""Integration test fixtures and helpers for jira_cli handler.""" from typing import Any import pytest import requests class MockResponse: """Minimal mock of requests.Response.""" def __init__(self, status_code: int = 200, json_data: Any = None) -> None: """Initialize the mock response.""" self.status_code = status_code self._json_data = json_data or {} def raise_for_status(self) -> None: """Raise HTTPError for non-2xx status codes.""" if not (200 <= self.status_code < 300): raise requests.HTTPError(f'{self.status_code} error', response=self) def json(self) -> Any: """Return the JSON data.""" return self._json_data def _text(t: str) -> dict: """Return an ADF text node.""" return {'type': 'text', 'text': t} def _hard_break() -> dict: """Return an ADF hardBreak node.""" return {'type': 'hardBreak'} def _paragraph(*nodes: dict) -> dict: """Return an ADF paragraph block wrapping the given nodes.""" return {'type': 'paragraph', 'content': list(nodes)} def make_jira_ticket( key: str = 'SYS-1', full_name: str = 'Jane Doe', email: str = 'jane@example.com', last_working_day: str = '2026-04-11', ) -> dict: """Build a Jira issue dict using the new multi-paragraph ADF structure. Mirrors the real Atlassian Document Format payload: each field value lives in its own text node, paragraphs are separated, and inline values are separated by hardBreak nodes — exactly as seen in the real API response. """ description_content = [ _paragraph(_text('EXTERNAL SENDER')), _paragraph( _text(f'An offboarding request has been submitted for {full_name}.') ), _paragraph( _text( 'Please ensure all application access is removed, and the account ' 'is fully deprovisioned on their last day of employment.' ) ), _paragraph( _text( 'If the offboarding is scheduled for a future date, a reminder will be sent on their final day.' ), _hard_break(), _text('User ID: JD001'), _hard_break(), _text('Employee No: EMP999'), _hard_break(), _text('Title: Software Engineer'), _hard_break(), _text(f'Email: {email}'), _hard_break(), _text('Department: 10000001 - Engineering'), _hard_break(), _text('Location: United States'), _hard_break(), _text(f'Last day of Employment: {last_working_day}'), _hard_break(), _text('Offboard Time:'), _hard_break(), _text('Litigation On Hold: False'), _hard_break(), _text('Grant Email Access: False'), _hard_break(), _text('Retain Mailbox: True'), ), _paragraph( _text( 'Please call the Global Technology Service Desk if you have any questions.' ) ), ] return { 'key': key, 'fields': {'description': {'content': description_content}}, } def make_jira_ticket_single_paragraph( key: str = 'SYS-1', description_text: str = ( 'An offboarding request has been submitted for Jane Doe. ' 'Email: jane@example.com ' 'Last day of Employment: 2026-04-11' ), ) -> dict: """Build a Jira issue dict with all text in a single paragraph node. Used to verify the flatten logic still works when there is only one paragraph with a single text node (old-style ticket shape). """ return { 'key': key, 'fields': {'description': {'content': [_paragraph(_text(description_text))]}}, } def make_query_event(dry_run: bool = False) -> dict: """Build a query-offboarding-tickets Lambda event.""" return {'action': 'query-offboarding-tickets', 'dry_run': dry_run} @pytest.fixture(autouse=True) def reset_app_singletons() -> None: """Reset module-level JiraConfig/JiraClient singletons between tests. Without this, the first test to initialise the singletons would pollute all subsequent tests (different monkeypatched env vars would be ignored). """ import jira_client.app jira_client.app._config = None jira_client.app._client = None yield # type: ignore[misc] jira_client.app._config = None jira_client.app._client = None