Skip to content

Commit 365e076

Browse files
committed
wip
1 parent e8e81f8 commit 365e076

File tree

2 files changed

+309
-2
lines changed

2 files changed

+309
-2
lines changed

src/main.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -595,13 +595,14 @@ def parse_push_output(output: str) -> dict:
595595

596596
# Extract release URL
597597
# Patterns: https://github.com/owner/repo/releases/tag/v1.2.3
598-
release_url_match = re.search(r'(https://github\.com/[^/\s]+/[^/\s]+/releases/tag/[^\s]+)', output)
598+
# Use specific character class for tag names to avoid matching ANSI codes or other special chars
599+
release_url_match = re.search(r'(https://github\.com/[^/\s]+/[^/\s]+/releases/tag/[a-zA-Z0-9._/-]+)', output)
599600
if release_url_match:
600601
result['release_url'] = release_url_match.group(1)
601602

602603
# Also try pattern for release creation message
603604
if not result['release_url']:
604-
release_url_match = re.search(r'(https://github\.com/[^/\s]+/[^/\s]+/releases/[^\s]+)', output)
605+
release_url_match = re.search(r'(https://github\.com/[^/\s]+/[^/\s]+/releases/[a-zA-Z0-9._/-]+)', output)
605606
if release_url_match:
606607
result['release_url'] = release_url_match.group(1)
607608

tests/test_initial_comment.py

Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
# SPDX-FileCopyrightText: 2025 Sequent Tech Inc <legal@sequentech.io>
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
"""Tests for initial issue comment functionality."""
6+
7+
import pytest
8+
from unittest.mock import Mock, patch, MagicMock
9+
import sys
10+
from pathlib import Path
11+
12+
# Add parent directory to path for imports
13+
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
14+
15+
from main import (
16+
parse_push_output,
17+
post_initial_issue_comment,
18+
)
19+
20+
21+
class TestParsePushOutput:
22+
"""Tests for parse_push_output function."""
23+
24+
def test_parse_created_issue(self):
25+
"""Test parsing output when issue is created."""
26+
output = """
27+
Created issue #123
28+
Issue URL: https://github.com/owner/repo/issues/123
29+
"""
30+
result = parse_push_output(output)
31+
32+
assert result['issue_number'] == 123
33+
assert result['issue_url'] == "https://github.com/owner/repo/issues/123"
34+
35+
def test_parse_updated_issue(self):
36+
"""Test parsing output when issue is updated."""
37+
output = "Updated issue #456 details (title, body, labels, milestone, type)"
38+
result = parse_push_output(output)
39+
40+
assert result['issue_number'] == 456
41+
42+
def test_parse_reused_issue(self):
43+
"""Test parsing output when existing issue is reused."""
44+
output = "Reusing existing issue #789 (--force)"
45+
result = parse_push_output(output)
46+
47+
assert result['issue_number'] == 789
48+
49+
def test_parse_release_url(self):
50+
"""Test parsing release URL from output."""
51+
output = """
52+
Created GitHub release: https://github.com/owner/repo/releases/tag/v1.2.3
53+
"""
54+
result = parse_push_output(output)
55+
56+
assert result['release_url'] == "https://github.com/owner/repo/releases/tag/v1.2.3"
57+
58+
def test_parse_complete_output(self):
59+
"""Test parsing output with all information."""
60+
output = """
61+
Generating release notes...
62+
Created issue #123
63+
Issue URL: https://github.com/owner/repo/issues/123
64+
Created GitHub release: https://github.com/owner/repo/releases/tag/v1.2.3
65+
"""
66+
result = parse_push_output(output)
67+
68+
assert result['issue_number'] == 123
69+
assert result['issue_url'] == "https://github.com/owner/repo/issues/123"
70+
assert result['release_url'] == "https://github.com/owner/repo/releases/tag/v1.2.3"
71+
72+
def test_parse_empty_output(self):
73+
"""Test parsing empty output."""
74+
result = parse_push_output("")
75+
76+
assert result['issue_number'] is None
77+
assert result['issue_url'] is None
78+
assert result['release_url'] is None
79+
80+
def test_parse_none_output(self):
81+
"""Test parsing None output."""
82+
result = parse_push_output(None)
83+
84+
assert result['issue_number'] is None
85+
assert result['issue_url'] is None
86+
assert result['release_url'] is None
87+
88+
def test_parse_issue_without_url(self):
89+
"""Test parsing when issue number is present but URL is not."""
90+
output = "Created issue #999"
91+
result = parse_push_output(output)
92+
93+
assert result['issue_number'] == 999
94+
assert result['issue_url'] is None
95+
96+
def test_parse_lowercase_issue(self):
97+
"""Test parsing lowercase 'issue' pattern."""
98+
output = "Creating issue #111 in owner/repo..."
99+
result = parse_push_output(output)
100+
101+
assert result['issue_number'] == 111
102+
103+
def test_parse_alternative_release_url_format(self):
104+
"""Test parsing alternative release URL formats."""
105+
output = "View release: https://github.com/owner/repo/releases/v2.0.0"
106+
result = parse_push_output(output)
107+
108+
assert result['release_url'] == "https://github.com/owner/repo/releases/v2.0.0"
109+
110+
def test_parse_long_untagged_release_url(self):
111+
"""Test parsing long untagged release URLs with hash suffixes."""
112+
output = """
113+
URL: https://github.com/sequentech/release-tool/releases/tag/untagged-c646978674c90c99ae21
114+
"""
115+
result = parse_push_output(output)
116+
117+
assert result['release_url'] == "https://github.com/sequentech/release-tool/releases/tag/untagged-c646978674c90c99ae21"
118+
119+
def test_parse_release_url_with_ansi_codes(self):
120+
"""Test parsing release URL when output contains ANSI escape codes."""
121+
# Simulate Rich console output with ANSI codes
122+
output = "\x1b[33m URL: https://github.com/owner/repo/releases/tag/v1.2.3\x1b[0m"
123+
result = parse_push_output(output)
124+
125+
assert result['release_url'] == "https://github.com/owner/repo/releases/tag/v1.2.3"
126+
127+
128+
class TestPostInitialIssueComment:
129+
"""Tests for post_initial_issue_comment function."""
130+
131+
@patch('main.Github')
132+
def test_post_comment_with_all_info(self, mock_github_class):
133+
"""Test posting comment with all information available."""
134+
# Setup mocks
135+
mock_issue = Mock()
136+
mock_repo = Mock()
137+
mock_repo.get_issue.return_value = mock_issue
138+
mock_github = Mock()
139+
mock_github.get_repo.return_value = mock_repo
140+
mock_github_class.return_value = mock_github
141+
142+
# Call function
143+
post_initial_issue_comment(
144+
token="fake_token",
145+
repo_name="owner/repo",
146+
issue_number=123,
147+
version="1.2.3",
148+
release_url="https://github.com/owner/repo/releases/tag/v1.2.3",
149+
workflow_run_url="https://github.com/owner/repo/actions/runs/123456"
150+
)
151+
152+
# Assertions
153+
mock_repo.get_issue.assert_called_once_with(123)
154+
mock_issue.create_comment.assert_called_once()
155+
156+
# Check comment content
157+
comment_body = mock_issue.create_comment.call_args[0][0]
158+
assert "## 🤖 Release Bot" in comment_body
159+
assert "`1.2.3`" in comment_body
160+
assert "https://github.com/owner/repo/releases/tag/v1.2.3" in comment_body
161+
assert "https://github.com/owner/repo/actions/runs/123456" in comment_body
162+
assert "/release-bot update" in comment_body
163+
assert "/release-bot push" in comment_body
164+
assert "/release-bot generate" in comment_body
165+
assert "/release-bot list" in comment_body
166+
assert "/release-bot merge" in comment_body
167+
168+
@patch('main.Github')
169+
def test_post_comment_without_release_url(self, mock_github_class):
170+
"""Test posting comment when release URL is not available."""
171+
# Setup mocks
172+
mock_issue = Mock()
173+
mock_repo = Mock()
174+
mock_repo.get_issue.return_value = mock_issue
175+
mock_github = Mock()
176+
mock_github.get_repo.return_value = mock_repo
177+
mock_github_class.return_value = mock_github
178+
179+
# Call function
180+
post_initial_issue_comment(
181+
token="fake_token",
182+
repo_name="owner/repo",
183+
issue_number=123,
184+
version="1.2.3",
185+
release_url=None,
186+
workflow_run_url="https://github.com/owner/repo/actions/runs/123456"
187+
)
188+
189+
# Assertions
190+
mock_issue.create_comment.assert_called_once()
191+
comment_body = mock_issue.create_comment.call_args[0][0]
192+
assert "## 🤖 Release Bot" in comment_body
193+
assert "`1.2.3`" in comment_body
194+
assert "GitHub Release" not in comment_body # Should not include release link
195+
assert "https://github.com/owner/repo/actions/runs/123456" in comment_body
196+
197+
@patch('main.Github')
198+
def test_post_comment_without_workflow_url(self, mock_github_class):
199+
"""Test posting comment when workflow URL is not available."""
200+
# Setup mocks
201+
mock_issue = Mock()
202+
mock_repo = Mock()
203+
mock_repo.get_issue.return_value = mock_issue
204+
mock_github = Mock()
205+
mock_github.get_repo.return_value = mock_repo
206+
mock_github_class.return_value = mock_github
207+
208+
# Call function
209+
post_initial_issue_comment(
210+
token="fake_token",
211+
repo_name="owner/repo",
212+
issue_number=123,
213+
version="1.2.3",
214+
release_url="https://github.com/owner/repo/releases/tag/v1.2.3",
215+
workflow_run_url=None
216+
)
217+
218+
# Assertions
219+
mock_issue.create_comment.assert_called_once()
220+
comment_body = mock_issue.create_comment.call_args[0][0]
221+
assert "## 🤖 Release Bot" in comment_body
222+
assert "https://github.com/owner/repo/releases/tag/v1.2.3" in comment_body
223+
assert "Workflow Run" not in comment_body # Should not include workflow link
224+
225+
@patch('main.Github')
226+
def test_post_comment_minimal(self, mock_github_class):
227+
"""Test posting comment with only required fields."""
228+
# Setup mocks
229+
mock_issue = Mock()
230+
mock_repo = Mock()
231+
mock_repo.get_issue.return_value = mock_issue
232+
mock_github = Mock()
233+
mock_github.get_repo.return_value = mock_repo
234+
mock_github_class.return_value = mock_github
235+
236+
# Call function
237+
post_initial_issue_comment(
238+
token="fake_token",
239+
repo_name="owner/repo",
240+
issue_number=123,
241+
version="1.2.3"
242+
)
243+
244+
# Assertions
245+
mock_issue.create_comment.assert_called_once()
246+
comment_body = mock_issue.create_comment.call_args[0][0]
247+
assert "## 🤖 Release Bot" in comment_body
248+
assert "`1.2.3`" in comment_body
249+
assert "/release-bot update" in comment_body
250+
251+
@patch('main.Github')
252+
def test_comment_format_includes_all_commands(self, mock_github_class):
253+
"""Test that comment includes all available commands."""
254+
# Setup mocks
255+
mock_issue = Mock()
256+
mock_repo = Mock()
257+
mock_repo.get_issue.return_value = mock_issue
258+
mock_github = Mock()
259+
mock_github.get_repo.return_value = mock_repo
260+
mock_github_class.return_value = mock_github
261+
262+
# Call function
263+
post_initial_issue_comment(
264+
token="fake_token",
265+
repo_name="owner/repo",
266+
issue_number=123,
267+
version="1.2.3"
268+
)
269+
270+
comment_body = mock_issue.create_comment.call_args[0][0]
271+
272+
# Verify all commands are listed
273+
assert "/release-bot update" in comment_body
274+
assert "/release-bot push" in comment_body
275+
assert "/release-bot generate" in comment_body
276+
assert "/release-bot list" in comment_body
277+
assert "/release-bot merge" in comment_body
278+
279+
# Verify command descriptions
280+
assert "Regenerate release notes and publish" in comment_body
281+
assert "Publish the release" in comment_body
282+
assert "Generate release notes only" in comment_body
283+
assert "List all draft releases" in comment_body
284+
assert "Merge PR, publish release, and close this issue" in comment_body
285+
286+
@patch('main.Github')
287+
def test_comment_with_rc_version(self, mock_github_class):
288+
"""Test posting comment for release candidate version."""
289+
# Setup mocks
290+
mock_issue = Mock()
291+
mock_repo = Mock()
292+
mock_repo.get_issue.return_value = mock_issue
293+
mock_github = Mock()
294+
mock_github.get_repo.return_value = mock_repo
295+
mock_github_class.return_value = mock_github
296+
297+
# Call function
298+
post_initial_issue_comment(
299+
token="fake_token",
300+
repo_name="owner/repo",
301+
issue_number=123,
302+
version="1.2.3-rc.0"
303+
)
304+
305+
comment_body = mock_issue.create_comment.call_args[0][0]
306+
assert "`1.2.3-rc.0`" in comment_body

0 commit comments

Comments
 (0)