Skip to content

Commit 67fecd3

Browse files
chore(tests): fix ruff lint violations in tests/ (#2827)
Clear pre-existing lint debt flagged by repo-wide `ruff check` (the lint config only scopes src/, so tests/ had drifted). No behavior change. - F401/F541: drop unused imports and redundant f-string prefixes (autofix) - E741: rename ambiguous `l` to `ln` in comprehensions - E702: split semicolon-joined statements onto separate lines - F841: drop unused bindings while keeping the side-effecting calls (_minimal_feature, install_from_directory) Full suite: 3344 passed, 40 skipped. ruff check (repo-wide): clean.
1 parent bb2b49d commit 67fecd3

13 files changed

Lines changed: 48 additions & 44 deletions

tests/extensions/git/test_git_extension.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ def test_no_git_graceful_degradation(self, tmp_path: Path):
371371
)
372372
assert result.returncode == 0, result.stderr
373373
# pwsh may prefix warnings to stdout; find the JSON line
374-
json_line = [l for l in result.stdout.splitlines() if l.strip().startswith("{")]
374+
json_line = [ln for ln in result.stdout.splitlines() if ln.strip().startswith("{")]
375375
assert json_line, f"No JSON in output: {result.stdout}"
376376
data = json.loads(json_line[-1])
377377
assert "BRANCH_NAME" in data

tests/integrations/test_integration_agy.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,5 +131,5 @@ def test_hook_note_preserves_indentation(self):
131131
)
132132
result = AgyIntegration._inject_hook_command_note(content)
133133
lines = result.splitlines()
134-
note_line = [l for l in lines if "replace dots" in l][0]
134+
note_line = [ln for ln in lines if "replace dots" in ln][0]
135135
assert note_line.startswith(" "), "Note should preserve indentation"

tests/integrations/test_integration_base_markdown.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -269,10 +269,10 @@ def _expected_files(self, script_variant: str) -> list[str]:
269269
files.append(f"{cmd_dir}/speckit.{stem}.md")
270270

271271
# Framework files
272-
files.append(f".specify/integration.json")
273-
files.append(f".specify/init-options.json")
272+
files.append(".specify/integration.json")
273+
files.append(".specify/init-options.json")
274274
files.append(f".specify/integrations/{self.KEY}.manifest.json")
275-
files.append(f".specify/integrations/speckit.manifest.json")
275+
files.append(".specify/integrations/speckit.manifest.json")
276276

277277
if script_variant == "sh":
278278
for name in ["check-prerequisites.sh", "common.sh", "create-new-feature.sh",

tests/integrations/test_integration_base_yaml.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ def test_yaml_is_valid(self, tmp_path):
152152
content = f.read_text(encoding="utf-8")
153153
# Strip trailing source comment before parsing
154154
lines = content.split("\n")
155-
yaml_lines = [l for l in lines if not l.startswith("# Source:")]
155+
yaml_lines = [ln for ln in lines if not ln.startswith("# Source:")]
156156
try:
157157
parsed = yaml.safe_load("\n".join(yaml_lines))
158158
except Exception as exc:
@@ -183,7 +183,7 @@ def test_yaml_prompt_excludes_frontmatter(self, tmp_path, monkeypatch):
183183
content = cmd_files[0].read_text(encoding="utf-8")
184184
# Strip source comment for parsing
185185
lines = content.split("\n")
186-
yaml_lines = [l for l in lines if not l.startswith("# Source:")]
186+
yaml_lines = [ln for ln in lines if not ln.startswith("# Source:")]
187187
parsed = yaml.safe_load("\n".join(yaml_lines))
188188

189189
assert "description:" not in parsed["prompt"]

tests/integrations/test_integration_subcommand.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import json
44
import os
55

6-
import pytest
76
from typer.testing import CliRunner
87

98
from specify_cli import app

tests/test_authentication.py

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -573,7 +573,9 @@ def test_open_url_attaches_auth_for_matching_host(self, monkeypatch):
573573
mock_opener = MagicMock()
574574
def fake_open(req, timeout=None):
575575
captured["req"] = req
576-
resp = MagicMock(); resp.__enter__ = lambda s: s; resp.__exit__ = MagicMock(return_value=False)
576+
resp = MagicMock()
577+
resp.__enter__ = lambda s: s
578+
resp.__exit__ = MagicMock(return_value=False)
577579
return resp
578580
mock_opener.open.side_effect = fake_open
579581
with patch("specify_cli.authentication.http.urllib.request.build_opener", return_value=mock_opener):
@@ -588,7 +590,9 @@ def test_open_url_no_auth_for_non_matching_host(self, monkeypatch):
588590
captured = {}
589591
def fake_urlopen(req, timeout=None):
590592
captured["req"] = req
591-
resp = MagicMock(); resp.__enter__ = lambda s: s; resp.__exit__ = MagicMock(return_value=False)
593+
resp = MagicMock()
594+
resp.__enter__ = lambda s: s
595+
resp.__exit__ = MagicMock(return_value=False)
592596
return resp
593597
with patch("specify_cli.authentication.http.urllib.request.urlopen", side_effect=fake_urlopen):
594598
open_url("https://example.com/file.json")
@@ -601,7 +605,9 @@ def test_open_url_no_auth_when_no_config(self, monkeypatch):
601605
captured = {}
602606
def fake_urlopen(req, timeout=None):
603607
captured["req"] = req
604-
resp = MagicMock(); resp.__enter__ = lambda s: s; resp.__exit__ = MagicMock(return_value=False)
608+
resp = MagicMock()
609+
resp.__enter__ = lambda s: s
610+
resp.__exit__ = MagicMock(return_value=False)
605611
return resp
606612
with patch("specify_cli.authentication.http.urllib.request.urlopen", side_effect=fake_urlopen):
607613
open_url("https://github.com/org/repo")
@@ -615,12 +621,16 @@ def test_open_url_falls_through_on_401(self, monkeypatch):
615621
self._set_config(monkeypatch, [_github_entry()])
616622
call_count = 0
617623
def fake_side_effect(req, timeout=None):
618-
nonlocal call_count; call_count += 1
624+
nonlocal call_count
625+
call_count += 1
619626
if call_count == 1:
620627
raise urllib.error.HTTPError("url", 401, "Unauthorized", {}, None)
621-
resp = MagicMock(); resp.__enter__ = lambda s: s; resp.__exit__ = MagicMock(return_value=False)
628+
resp = MagicMock()
629+
resp.__enter__ = lambda s: s
630+
resp.__exit__ = MagicMock(return_value=False)
622631
return resp
623-
mock_opener = MagicMock(); mock_opener.open.side_effect = fake_side_effect
632+
mock_opener = MagicMock()
633+
mock_opener.open.side_effect = fake_side_effect
624634
with patch("specify_cli.authentication.http.urllib.request.build_opener", return_value=mock_opener), \
625635
patch("specify_cli.authentication.http.urllib.request.urlopen", side_effect=fake_side_effect):
626636
open_url("https://github.com/org/repo")
@@ -692,7 +702,6 @@ def test_config_cached_after_first_load(self, monkeypatch):
692702
"""_load_config() should call load_auth_config only once per process."""
693703
from unittest.mock import patch
694704
from specify_cli.authentication import http as _mod
695-
from specify_cli.authentication.config import AuthConfigEntry
696705
# Allow the real load path (no override)
697706
monkeypatch.setattr(_mod, "_config_override", None)
698707
monkeypatch.setattr(_mod, "_config_cache", None)
@@ -825,8 +834,11 @@ def _capture_request(self):
825834
def side_effect(req, timeout=None):
826835
captured["request"] = req
827836
body = _json.dumps({"tag_name": "v9.9.9"}).encode()
828-
resp = MagicMock(); resp.read.return_value = body
829-
cm = MagicMock(); cm.__enter__.return_value = resp; cm.__exit__.return_value = False
837+
resp = MagicMock()
838+
resp.read.return_value = body
839+
cm = MagicMock()
840+
cm.__enter__.return_value = resp
841+
cm.__exit__.return_value = False
830842
return cm
831843
return captured, side_effect
832844

@@ -836,7 +848,8 @@ def test_gh_token_forwarded_when_configured(self, monkeypatch):
836848
monkeypatch.setenv("GH_TOKEN", "forwarded-sentinel")
837849
self._set_config(monkeypatch, [_github_entry()])
838850
captured, side_effect = self._capture_request()
839-
mock_opener = MagicMock(); mock_opener.open.side_effect = side_effect
851+
mock_opener = MagicMock()
852+
mock_opener.open.side_effect = side_effect
840853
with patch("specify_cli.authentication.http.urllib.request.build_opener", return_value=mock_opener):
841854
_fetch_latest_release_tag()
842855
assert captured["request"].get_header("Authorization") == "Bearer forwarded-sentinel"

tests/test_commands_package.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def test_agent_config_importable():
2929

3030

3131
def test_agent_config_re_exported_from_init():
32-
from specify_cli import AGENT_CONFIG, AI_ASSISTANT_ALIASES, AI_ASSISTANT_HELP, SCRIPT_TYPE_CHOICES
32+
from specify_cli import AGENT_CONFIG, SCRIPT_TYPE_CHOICES
3333
assert isinstance(AGENT_CONFIG, dict)
3434
assert "sh" in SCRIPT_TYPE_CHOICES
3535

tests/test_console_imports.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,7 @@
22
from specify_cli import (
33
console,
44
StepTracker,
5-
get_key,
65
select_with_arrows,
7-
BannerGroup,
8-
show_banner,
9-
BANNER,
10-
TAGLINE,
116
)
127

138

tests/test_extension_skills.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
from specify_cli.extensions import (
2222
ExtensionManifest,
2323
ExtensionManager,
24-
ExtensionError,
2524
)
2625

2726

@@ -241,7 +240,7 @@ def test_skills_created_when_ai_skills_active(self, skills_project, extension_di
241240
"""Skills should be created when ai_skills is enabled."""
242241
project_dir, skills_dir = skills_project
243242
manager = ExtensionManager(project_dir)
244-
manifest = manager.install_from_directory(
243+
manager.install_from_directory(
245244
extension_dir, "0.1.0", register_commands=False
246245
)
247246

@@ -784,7 +783,7 @@ def test_command_without_frontmatter(self, skills_project, temp_dir):
784783
)
785784

786785
manager = ExtensionManager(project_dir)
787-
manifest = manager.install_from_directory(
786+
manager.install_from_directory(
788787
ext_dir, "0.1.0", register_commands=False
789788
)
790789

@@ -803,7 +802,7 @@ def test_gemini_agent_skills(self, project_dir, temp_dir):
803802
ext_dir = _create_extension_dir(temp_dir, ext_id="test-ext")
804803

805804
manager = ExtensionManager(project_dir)
806-
manifest = manager.install_from_directory(
805+
manager.install_from_directory(
807806
ext_dir, "0.1.0", register_commands=False
808807
)
809808

@@ -819,10 +818,10 @@ def test_multiple_extensions_independent_skills(self, skills_project, temp_dir):
819818
ext_dir_b = _create_extension_dir(temp_dir, ext_id="ext-b")
820819

821820
manager = ExtensionManager(project_dir)
822-
manifest_a = manager.install_from_directory(
821+
manager.install_from_directory(
823822
ext_dir_a, "0.1.0", register_commands=False
824823
)
825-
manifest_b = manager.install_from_directory(
824+
manager.install_from_directory(
826825
ext_dir_b, "0.1.0", register_commands=False
827826
)
828827

@@ -880,7 +879,7 @@ def test_malformed_frontmatter_handled(self, skills_project, temp_dir):
880879

881880
manager = ExtensionManager(project_dir)
882881
# Should not raise
883-
manifest = manager.install_from_directory(
882+
manager.install_from_directory(
884883
ext_dir, "0.1.0", register_commands=False
885884
)
886885

tests/test_setup_tasks.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ def test_setup_tasks_bash_core_template_resolved(tasks_repo: Path) -> None:
123123
setup-tasks.sh --json should exit 0 and return an absolute, existing
124124
TASKS_TEMPLATE path pointing to the core template.
125125
"""
126-
feat = _minimal_feature(tasks_repo)
126+
_minimal_feature(tasks_repo)
127127
script = tasks_repo / ".specify" / "scripts" / "bash" / "setup-tasks.sh"
128128

129129
result = subprocess.run(
@@ -150,7 +150,7 @@ def test_setup_tasks_bash_override_wins(tasks_repo: Path) -> None:
150150
When an override exists at .specify/templates/overrides/tasks-template.md,
151151
setup-tasks.sh --json must return the override path, not the core path.
152152
"""
153-
feat = _minimal_feature(tasks_repo)
153+
_minimal_feature(tasks_repo)
154154

155155
# Create the override
156156
overrides_dir = tasks_repo / ".specify" / "templates" / "overrides"
@@ -187,7 +187,7 @@ def test_setup_tasks_bash_extension_wins_over_core(tasks_repo: Path) -> None:
187187
When an extension template exists, setup-tasks.sh --json must resolve
188188
tasks-template.md from the extension before falling back to the core path.
189189
"""
190-
feat = _minimal_feature(tasks_repo)
190+
_minimal_feature(tasks_repo)
191191

192192
# FIX: real extension layout is .specify/extensions/<id>/templates/<name>.md
193193
extension_dir = (
@@ -225,7 +225,7 @@ def test_setup_tasks_bash_preset_wins_over_extension(tasks_repo: Path) -> None:
225225
When both preset and extension templates exist, setup-tasks.sh --json must
226226
resolve the preset path because presets outrank extensions.
227227
"""
228-
feat = _minimal_feature(tasks_repo)
228+
_minimal_feature(tasks_repo)
229229

230230
# FIX: real extension layout is .specify/extensions/<id>/templates/<name>.md
231231
extension_dir = (
@@ -269,7 +269,7 @@ def test_setup_tasks_bash_preset_priority_order(tasks_repo: Path) -> None:
269269
When two presets both provide tasks-template.md, the one listed first in
270270
.specify/presets/.registry wins.
271271
"""
272-
feat = _minimal_feature(tasks_repo)
272+
_minimal_feature(tasks_repo)
273273

274274
# resolve_template reads .specify/presets/.registry as a JSON object with a
275275
# "presets" map where each entry has a numeric "priority" (lower = higher
@@ -329,7 +329,7 @@ def test_setup_tasks_bash_missing_template_errors(tasks_repo: Path) -> None:
329329
When tasks-template.md is absent from all locations, setup-tasks.sh must
330330
exit non-zero and print a helpful ERROR message to stderr.
331331
"""
332-
feat = _minimal_feature(tasks_repo)
332+
_minimal_feature(tasks_repo)
333333

334334
# Remove the core template so no template exists anywhere
335335
core = tasks_repo / ".specify" / "templates" / "tasks-template.md"
@@ -429,7 +429,7 @@ def test_setup_tasks_ps_core_template_resolved(tasks_repo: Path) -> None:
429429
setup-tasks.ps1 -Json should exit 0 and return an absolute, existing
430430
TASKS_TEMPLATE path.
431431
"""
432-
feat = _minimal_feature(tasks_repo)
432+
_minimal_feature(tasks_repo)
433433
script = tasks_repo / ".specify" / "scripts" / "powershell" / "setup-tasks.ps1"
434434
exe = "pwsh" if HAS_PWSH else _POWERSHELL
435435

@@ -457,7 +457,7 @@ def test_setup_tasks_ps_override_wins(tasks_repo: Path) -> None:
457457
When an override exists at .specify/templates/overrides/tasks-template.md,
458458
setup-tasks.ps1 -Json must return the override path, not the core path.
459459
"""
460-
feat = _minimal_feature(tasks_repo)
460+
_minimal_feature(tasks_repo)
461461

462462
overrides_dir = tasks_repo / ".specify" / "templates" / "overrides"
463463
overrides_dir.mkdir(parents=True, exist_ok=True)
@@ -493,7 +493,7 @@ def test_setup_tasks_ps_missing_template_errors(tasks_repo: Path) -> None:
493493
When tasks-template.md is absent from all locations, setup-tasks.ps1 must
494494
exit non-zero and write a helpful error to stderr.
495495
"""
496-
feat = _minimal_feature(tasks_repo)
496+
_minimal_feature(tasks_repo)
497497

498498
core = tasks_repo / ".specify" / "templates" / "tasks-template.md"
499499
core.unlink()

0 commit comments

Comments
 (0)