Skip to content

Commit 4fc7058

Browse files
authored
feat: Add more information in SD description (#44)
1 parent 0613881 commit 4fc7058

File tree

4 files changed

+140
-7
lines changed

4 files changed

+140
-7
lines changed

src/lightman_ai/main.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ def _create_service_desk_issues(
2828
) -> None:
2929
async def schedule_task(article: SelectedArticle) -> None:
3030
try:
31+
description = f"*Why is relevant:*\n{article.why_is_relevant}\n\n*Source:* {article.link}\n\n*Score:* {article.relevance_score}/10"
3132
await service_desk_client.create_request_of_type(
3233
project_key=project_key,
3334
summary=article.title,
34-
description=article.link,
35+
description=description,
3536
request_id_type=request_id_type,
3637
)
3738
logger.info("Created issue for article %s", article.link)
@@ -80,7 +81,7 @@ def lightman(
8081

8182
if not dry_run:
8283
if not project_key or not request_id_type:
83-
raise ValueError("Missing Service Desk's project key or issue id type")
84+
raise ValueError("Missing Service Desk's project key or request id type")
8485

8586
service_desk_client = ServiceDeskIntegration.from_env()
8687
_create_service_desk_issues(

tests/conftest.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
from collections.abc import Generator, Iterator
33
from contextlib import contextmanager
44
from typing import Any
5-
from unittest.mock import patch
5+
from unittest.mock import AsyncMock, Mock, patch
66

77
import pytest
88
import stamina
9-
from lightman_ai.article.models import ArticlesList
9+
from lightman_ai.article.models import ArticlesList, SelectedArticle
10+
from lightman_ai.integrations.service_desk.integration import ServiceDeskIntegration
1011
from lightman_ai.sources.the_hacker_news import TheHackerNewsSource
1112
from stamina._core import _RetryContextIterator
1213

@@ -28,6 +29,33 @@ def patched_retry_context(*args: Any, **kwargs: Any) -> _RetryContextIterator:
2829
yield mock
2930

3031

32+
@pytest.fixture
33+
def selected_articles() -> list[SelectedArticle]:
34+
"""Create test articles for service desk issue creation."""
35+
return [
36+
SelectedArticle(
37+
title="Critical Security Vulnerability in Popular Library",
38+
link="https://example.com/article1",
39+
why_is_relevant="This affects our production systems",
40+
relevance_score=9,
41+
),
42+
SelectedArticle(
43+
title="New Attack Vector Discovered",
44+
link="https://example.com/article2",
45+
why_is_relevant="Could impact our infrastructure",
46+
relevance_score=8,
47+
),
48+
]
49+
50+
51+
@pytest.fixture
52+
def mock_service_desk() -> Mock:
53+
"""Create a mock ServiceDeskIntegration."""
54+
mock = Mock(spec=ServiceDeskIntegration)
55+
mock.create_request_of_type = AsyncMock(return_value="PROJ-123")
56+
return mock
57+
58+
3159
@pytest.fixture
3260
def thn_xml() -> str:
3361
return """<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><rss xmlns:atom=\"http://www.w3.org/2005/Atom\"

tests/integrations/test_service_desk.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ async def test_create_request_of_type_payload(self, service_desk_integration: Se
150150
project_key="PROJ", summary="summary", description="desc", request_id_type="REQ_TYPE"
151151
)
152152
post_mock.assert_called_once()
153-
args, kwargs = post_mock.call_args
153+
_, kwargs = post_mock.call_args
154154
assert kwargs["json"] == {
155155
"serviceDeskId": "PROJ",
156156
"requestTypeId": "REQ_TYPE",

tests/test_main.py

Lines changed: 106 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import logging
22
from typing import Any
3-
from unittest.mock import AsyncMock, patch
3+
from unittest.mock import AsyncMock, Mock, patch
44

5+
import pytest
56
from lightman_ai.article.models import SelectedArticle, SelectedArticlesList
6-
from lightman_ai.main import lightman
7+
from lightman_ai.main import _create_service_desk_issues, lightman
78
from tests.utils import patch_agent
89

910

@@ -70,3 +71,106 @@ def test_lightman_no_publish_if_dry_run(self, caplog: Any, test_prompt: str, thn
7071
# Check ServiceDesk integration is NOT called in dry_run mode
7172
mock_service_desk_env.assert_not_called()
7273
assert mock_service_desk.create_request_of_type.call_count == 0
74+
75+
76+
class TestCreateServiceDeskIssues:
77+
"""Tests for the _create_service_desk_issues function."""
78+
79+
def test_create_service_desk_issues_success(
80+
self, selected_articles: list[SelectedArticle], mock_service_desk: Mock, caplog: pytest.LogCaptureFixture
81+
) -> None:
82+
"""Test successful creation of service desk issues for all articles."""
83+
with caplog.at_level(logging.INFO):
84+
_create_service_desk_issues(
85+
selected_articles=selected_articles,
86+
service_desk_client=mock_service_desk,
87+
project_key="TEST",
88+
request_id_type="10001",
89+
)
90+
91+
# Verify service desk client was called for each article
92+
assert mock_service_desk.create_request_of_type.call_count == 2
93+
94+
# Check the calls were made with correct parameters
95+
calls = mock_service_desk.create_request_of_type.call_args_list
96+
97+
# First article call
98+
first_call = calls[0]
99+
assert first_call.kwargs["project_key"] == "TEST"
100+
assert first_call.kwargs["summary"] == "Critical Security Vulnerability in Popular Library"
101+
assert first_call.kwargs["request_id_type"] == "10001"
102+
expected_desc_1 = "*Why is relevant:*\nThis affects our production systems\n\n*Source:* https://example.com/article1\n\n*Score:* 9/10"
103+
assert first_call.kwargs["description"] == expected_desc_1
104+
105+
# Second article call
106+
second_call = calls[1]
107+
assert second_call.kwargs["project_key"] == "TEST"
108+
assert second_call.kwargs["summary"] == "New Attack Vector Discovered"
109+
assert second_call.kwargs["request_id_type"] == "10001"
110+
expected_desc_2 = "*Why is relevant:*\nCould impact our infrastructure\n\n*Source:* https://example.com/article2\n\n*Score:* 8/10"
111+
assert second_call.kwargs["description"] == expected_desc_2
112+
113+
# Check success log messages
114+
assert "Created issue for article https://example.com/article1" in caplog.text
115+
assert "Created issue for article https://example.com/article2" in caplog.text
116+
117+
def test_create_service_desk_issues_single_failure(
118+
self, selected_articles: list[SelectedArticle], mock_service_desk: Mock, caplog: pytest.LogCaptureFixture
119+
) -> None:
120+
"""Test handling when one article fails to create service desk issue."""
121+
# Make the first call succeed, second call fail
122+
mock_service_desk.create_request_of_type.side_effect = [
123+
"PROJ-123", # Success for first article
124+
Exception("Service desk unavailable"), # Failure for second article
125+
]
126+
127+
with caplog.at_level(logging.INFO), pytest.raises(ExceptionGroup) as exc_info:
128+
_create_service_desk_issues(
129+
selected_articles=selected_articles,
130+
service_desk_client=mock_service_desk,
131+
project_key="TEST",
132+
request_id_type="10001",
133+
)
134+
135+
# Verify both calls were attempted
136+
assert mock_service_desk.create_request_of_type.call_count == 2
137+
138+
# Check that ExceptionGroup contains the failure
139+
assert "Could not create all ServiceDesk issues" in str(exc_info.value)
140+
assert len(exc_info.value.exceptions) == 1
141+
assert "Service desk unavailable" in str(exc_info.value.exceptions[0])
142+
143+
# Check that success was logged for the first article
144+
assert "Created issue for article https://example.com/article1" in caplog.text
145+
146+
def test_create_service_desk_issues_all_failures(
147+
self, selected_articles: list[SelectedArticle], mock_service_desk: Mock, caplog: pytest.LogCaptureFixture
148+
) -> None:
149+
"""Test handling when all articles fail to create service desk issues."""
150+
# Make all calls fail
151+
mock_service_desk.create_request_of_type.side_effect = Exception("Service desk down")
152+
153+
with caplog.at_level(logging.ERROR), pytest.raises(ExceptionGroup) as exc_info:
154+
_create_service_desk_issues(
155+
selected_articles=selected_articles,
156+
service_desk_client=mock_service_desk,
157+
project_key="TEST",
158+
request_id_type="10001",
159+
)
160+
161+
# Verify both calls were attempted
162+
assert mock_service_desk.create_request_of_type.call_count == 2
163+
164+
# Check that ExceptionGroup contains all failures
165+
assert "Could not create all ServiceDesk issues" in str(exc_info.value)
166+
assert len(exc_info.value.exceptions) == 2
167+
168+
# Check error logging
169+
assert (
170+
"Could not create ServiceDesk issue: Critical Security Vulnerability in Popular Library, https://example.com/article1"
171+
in caplog.text
172+
)
173+
assert (
174+
"Could not create ServiceDesk issue: New Attack Vector Discovered, https://example.com/article2"
175+
in caplog.text
176+
)

0 commit comments

Comments
 (0)