Skip to content

Commit 65268be

Browse files
committed
Fix all test assertions to use precise equality checks
- Replace substring matching (assert x in y) with exact equality checks - Replace partial list checks with full list/tuple comparisons - Update test expectations to match exact command arguments - Fix test mocking to include new push() call in _finalize_created_item - Use precise regex matching for shell integration output This makes tests more robust and prevents false positives from substring matches in help text or command descriptions.
1 parent a9cd684 commit 65268be

4 files changed

Lines changed: 111 additions & 57 deletions

File tree

tests/test_create.py

Lines changed: 49 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,20 @@ def test_init_with_explicit_repo(self, tmp_path):
2424

2525
assert result.exit_code == 0
2626

27-
# Check git init was called
28-
assert any('git' in str(call) and 'init' in str(call) for call in mock_proc.run.call_args_list)
29-
30-
# Check git config calls
31-
config_calls = [str(call) for call in mock_proc.run.call_args_list if 'config' in str(call)]
32-
assert any('pr.owner' in call and 'test-owner' in call for call in config_calls)
33-
assert any('pr.repo' in call and 'test-repo' in call for call in config_calls)
27+
# Extract all git commands called
28+
git_commands = [
29+
call[0][0] if call[0] else None
30+
for call in mock_proc.run.call_args_list
31+
]
32+
assert 'git' in git_commands
33+
34+
# Find specific git config calls
35+
git_config_calls = [
36+
call[0] for call in mock_proc.run.call_args_list
37+
if len(call[0]) > 2 and call[0][0] == 'git' and call[0][1] == 'config'
38+
]
39+
assert ('git', 'config', 'pr.owner', 'test-owner') in git_config_calls
40+
assert ('git', 'config', 'pr.repo', 'test-repo') in git_config_calls
3441

3542
# Check gh/new/ directory and DESCRIPTION.md were created
3643
assert Path('gh/new').is_dir()
@@ -80,6 +87,7 @@ def test_create_issue_success(self, tmp_path):
8087
patch('ghpr.commands.create.get_owner_repo') as mock_get_repo, \
8188
patch('ghpr.commands.create.read_description_file') as mock_read_desc, \
8289
patch('ghpr.commands.create.write_description_with_link_ref') as mock_write, \
90+
patch('ghpr.commands.push.push') as mock_push, \
8391
patch('ghpr.commands.create.err'), \
8492
patch('os.rename'):
8593

@@ -89,19 +97,19 @@ def test_create_issue_success(self, tmp_path):
8997

9098
create_new_issue(repo_arg=None, yes=2, dry_run=False)
9199

92-
# Verify gh issue create was called
100+
# Verify gh issue create was called with exact arguments
93101
mock_proc.text.assert_called_once()
94102
call_args = mock_proc.text.call_args[0]
95-
assert 'gh' in call_args
96-
assert 'issue' in call_args
97-
assert 'create' in call_args
98-
assert '--title' in call_args
99-
assert 'Test Issue' in call_args
103+
expected_args = ('gh', 'issue', 'create', '-R', 'test-owner/test-repo', '--title', 'Test Issue', '--body', 'Test body content')
104+
assert call_args == expected_args
100105

101-
# Verify git config was set
102-
config_calls = [call[0] for call in mock_proc.run.call_args_list if 'config' in call[0]]
103-
assert any('pr.number' in call and '42' in call for call in config_calls)
104-
assert any('pr.type' in call and 'issue' in call for call in config_calls)
106+
# Verify git config was set with exact calls
107+
config_calls = [
108+
call[0] for call in mock_proc.run.call_args_list
109+
if len(call[0]) > 1 and call[0][0] == 'git' and call[0][1] == 'config'
110+
]
111+
assert ('git', 'config', 'pr.number', '42') in config_calls
112+
assert ('git', 'config', 'pr.type', 'issue') in config_calls
105113

106114
# Verify file was written with link reference
107115
mock_write.assert_called_once()
@@ -120,6 +128,7 @@ def test_create_issue_with_explicit_repo(self, tmp_path):
120128
patch('ghpr.commands.create.get_owner_repo') as mock_get_repo, \
121129
patch('ghpr.commands.create.read_description_file') as mock_read_desc, \
122130
patch('ghpr.commands.create.write_description_with_link_ref'), \
131+
patch('ghpr.commands.push.push'), \
123132
patch('ghpr.commands.create.err'), \
124133
patch('os.rename'):
125134

@@ -151,6 +160,7 @@ def test_create_pr_with_explicit_args(self, tmp_path):
151160
with patch('ghpr.commands.create.proc') as mock_proc, \
152161
patch('ghpr.commands.create.read_description_file') as mock_read_desc, \
153162
patch('ghpr.commands.create.write_description_with_link_ref') as mock_write, \
163+
patch('ghpr.commands.push.push'), \
154164
patch('ghpr.commands.create.err'), \
155165
patch('os.rename'):
156166

@@ -177,16 +187,18 @@ def line_side_effect(*args, **kwargs):
177187
dry_run=False
178188
)
179189

180-
# Verify gh pr create was called with correct args
190+
# Verify gh pr create was called with exact arguments
181191
mock_proc.text.assert_called_once()
182192
call_args = mock_proc.text.call_args[0]
183-
assert 'gh' in call_args
184-
assert 'pr' in call_args
185-
assert 'create' in call_args
186-
assert '--head' in call_args
187-
assert 'feature-branch' in call_args
188-
assert '--base' in call_args
189-
assert 'main' in call_args
193+
expected_args = (
194+
'gh', 'pr', 'create',
195+
'-R', 'test-owner/test-repo',
196+
'--title', 'Test PR',
197+
'--body', 'Test body',
198+
'--base', 'main',
199+
'--head', 'feature-branch'
200+
)
201+
assert call_args == expected_args
190202

191203
def test_create_draft_pr(self, tmp_path):
192204
"""Test draft PR includes --draft flag."""
@@ -197,6 +209,7 @@ def test_create_draft_pr(self, tmp_path):
197209
with patch('ghpr.commands.create.proc') as mock_proc, \
198210
patch('ghpr.commands.create.read_description_file') as mock_read_desc, \
199211
patch('ghpr.commands.create.write_description_with_link_ref'), \
212+
patch('ghpr.commands.push.push'), \
200213
patch('ghpr.commands.create.err'), \
201214
patch('os.rename'):
202215

@@ -221,6 +234,15 @@ def line_side_effect(*args, **kwargs):
221234
dry_run=False
222235
)
223236

224-
# Verify --draft flag was passed
237+
# Verify --draft flag was passed with exact arguments
225238
call_args = mock_proc.text.call_args[0]
226-
assert '--draft' in call_args
239+
expected_args = (
240+
'gh', 'pr', 'create',
241+
'-R', 'test-owner/test-repo',
242+
'--title', 'Draft PR',
243+
'--body', 'Draft body',
244+
'--base', 'main',
245+
'--head', 'feature',
246+
'--draft'
247+
)
248+
assert call_args == expected_args

tests/test_files.py

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,14 @@ def test_write_basic_description(self):
116116
)
117117

118118
content = filepath.read_text()
119-
assert '# [owner/myrepo#123] Test PR' in content
120-
assert 'This is the body.' in content
121-
assert '[owner/myrepo#123]: https://github.com/owner/myrepo/pull/123' in content
119+
expected = (
120+
'# [owner/myrepo#123] Test PR\n'
121+
'\n'
122+
'This is the body.\n'
123+
'\n'
124+
'[owner/myrepo#123]: https://github.com/owner/myrepo/pull/123\n'
125+
)
126+
assert content == expected
122127

123128
def test_write_empty_body(self):
124129
"""Test writing description with empty body."""
@@ -137,8 +142,12 @@ def test_write_empty_body(self):
137142
)
138143

139144
content = filepath.read_text()
140-
assert '# [owner/myrepo#456] Empty Body' in content
141-
assert '[owner/myrepo#456]: https://github.com/owner/myrepo/pull/456' in content
145+
expected = (
146+
'# [owner/myrepo#456] Empty Body\n'
147+
'\n'
148+
'[owner/myrepo#456]: https://github.com/owner/myrepo/pull/456\n'
149+
)
150+
assert content == expected
142151

143152
def test_write_multiline_body(self):
144153
"""Test writing description with multiline body."""
@@ -158,8 +167,15 @@ def test_write_multiline_body(self):
158167
)
159168

160169
content = filepath.read_text()
161-
assert '# [owner/myrepo#789] Multi Line' in content
162-
assert body in content
170+
# Body already has trailing newline, don't add another
171+
expected = (
172+
'# [owner/myrepo#789] Multi Line\n'
173+
'\n'
174+
f'{body}'
175+
'\n'
176+
'[owner/myrepo#789]: https://github.com/owner/myrepo/pull/789\n'
177+
)
178+
assert content == expected
163179

164180
def test_body_with_existing_link_def(self):
165181
"""Test that existing link definition in body is not duplicated."""
@@ -258,9 +274,8 @@ def test_read_multiline_body(self):
258274

259275
assert title == 'Multi Line'
260276
# Note: Link definitions are stripped, trailing whitespace removed
261-
assert 'Line 1' in body
262-
assert 'Line 2 with **markdown**' in body
263-
assert '- List item' in body
277+
expected_body = 'Line 1\n\nLine 2 with **markdown**\n\n- List item'
278+
assert body == expected_body
264279

265280
def test_read_no_description_file(self):
266281
"""Test reading when no description file exists."""

tests/test_gist.py

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,14 @@ def test_multiline_body_with_footer(self):
102102

103103
body_without, gist_url = extract_gist_footer(body)
104104

105-
assert 'Line 1' in body_without
106-
assert 'Line 2 with **markdown**' in body_without
107-
assert '- List item' in body_without
108-
assert '<!-- Synced with' not in body_without
105+
expected_body = (
106+
'Line 1\n'
107+
'\n'
108+
'Line 2 with **markdown**\n'
109+
'\n'
110+
'- List item'
111+
)
112+
assert body_without == expected_body
109113
assert gist_url == 'https://gist.github.com/fedcba987654'
110114

111115

@@ -119,8 +123,8 @@ def test_add_hidden_footer(self):
119123

120124
result = add_gist_footer(body, gist_url, visible=False)
121125

122-
assert 'This is the body.' in result
123-
assert '<!-- Synced with https://gist.github.com/abc123def456 via [ghpr](https://github.com/runsascoded/ghpr) -->' in result
126+
expected = 'This is the body.\n\n<!-- Synced with https://gist.github.com/abc123def456 via [ghpr](https://github.com/runsascoded/ghpr) -->'
127+
assert result == expected
124128

125129
def test_add_visible_footer(self):
126130
"""Test adding visible footer."""
@@ -129,9 +133,8 @@ def test_add_visible_footer(self):
129133

130134
result = add_gist_footer(body, gist_url, visible=True)
131135

132-
assert 'This is the body.' in result
133-
assert '---' in result
134-
assert 'Synced with [gist](https://gist.github.com/abc123def456) via [ghpr](https://github.com/runsascoded/ghpr)' in result
136+
expected = 'This is the body.\n\n\n---\n\nSynced with [gist](https://gist.github.com/abc123def456) via [ghpr](https://github.com/runsascoded/ghpr)'
137+
assert result == expected
135138

136139
def test_add_footer_to_empty_body(self):
137140
"""Test adding footer to empty body."""
@@ -160,10 +163,8 @@ def test_replace_existing_footer(self):
160163

161164
result = add_gist_footer(body, new_gist_url, visible=False)
162165

163-
assert 'This is the body.' in result
164-
assert '0123456789ab' not in result
165-
assert 'fedcba987654' in result
166-
assert result.count('<!-- Synced with') == 1
166+
expected = 'This is the body.\n\n<!-- Synced with https://gist.github.com/fedcba987654 via [ghpr](https://github.com/runsascoded/ghpr) -->'
167+
assert result == expected
167168

168169
def test_visible_footer_with_revision(self):
169170
"""Test visible footer preserves revision in URL."""
@@ -172,7 +173,8 @@ def test_visible_footer_with_revision(self):
172173

173174
result = add_gist_footer(body, gist_url, visible=True)
174175

175-
assert 'Synced with [gist](https://gist.github.com/abc123def456/0fedcba987654321)' in result
176+
expected = 'This is the body.\n\n\n---\n\nSynced with [gist](https://gist.github.com/abc123def456/0fedcba987654321) via [ghpr](https://github.com/runsascoded/ghpr)'
177+
assert result == expected
176178

177179
def test_add_footer_preserves_body_formatting(self):
178180
"""Test that adding footer preserves body formatting."""
@@ -187,9 +189,16 @@ def test_add_footer_preserves_body_formatting(self):
187189

188190
result = add_gist_footer(body, gist_url, visible=False)
189191

190-
assert 'Line 1' in result
191-
assert 'Line 2 with **markdown**' in result
192-
assert '- List item' in result
192+
expected = (
193+
'Line 1\n'
194+
'\n'
195+
'Line 2 with **markdown**\n'
196+
'\n'
197+
'- List item\n'
198+
'\n'
199+
'<!-- Synced with https://gist.github.com/abc123def456 via [ghpr](https://github.com/runsascoded/ghpr) -->'
200+
)
201+
assert result == expected
193202

194203

195204
class TestExtractThenAddRoundtrip:

tests/test_import.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ def test_cli_loads():
2424
text=True,
2525
)
2626
assert result.returncode == 0
27-
assert "Clone and sync GitHub PR descriptions" in result.stdout
27+
# Check that help output contains expected structure
28+
assert result.stdout.startswith("Usage: ghpr [OPTIONS] COMMAND [ARGS]...")
29+
assert "Clone and sync GitHub PR descriptions." in result.stdout
30+
assert "Options:" in result.stdout
31+
assert "Commands:" in result.stdout
2832

2933

3034
def test_all_commands_present():
@@ -68,8 +72,12 @@ def test_shell_integration_outputs():
6872
text=True,
6973
)
7074
assert result.returncode == 0
71-
assert "ghpri()" in result.stdout # ghpri is now a function, not alias
72-
assert "alias ghprc=" in result.stdout
75+
# Check for specific function/alias definitions
76+
lines = result.stdout.strip().split('\n')
77+
# ghpri should be a function (allow for comments after the opening brace)
78+
assert any(line.strip().startswith('ghpri() {') for line in lines), "ghpri function definition not found"
79+
# ghprc should be an alias
80+
assert any('alias ghprc=' in line for line in lines), "ghprc alias definition not found"
7381

7482

7583
def test_patterns_regex():

0 commit comments

Comments
 (0)