Skip to content

Commit d8a81b2

Browse files
authored
test(workflows): cover executable override fallback preflight (#2843)
1 parent a0305fc commit d8a81b2

1 file changed

Lines changed: 81 additions & 0 deletions

File tree

tests/test_workflows.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,47 @@ def test_dispatch_with_mock_cli(self, tmp_path, monkeypatch):
658658
# Claude is a SkillsIntegration so uses /speckit-specify
659659
assert "/speckit-specify login" in call_args[0][0][2]
660660

661+
def test_dispatch_uses_executable_override_for_fallback_preflight(self, tmp_path, monkeypatch):
662+
"""Command preflight falls back to build_exec_args() argv[0]."""
663+
from unittest.mock import MagicMock, patch
664+
from specify_cli.workflows.steps.command import CommandStep
665+
from specify_cli.workflows.base import StepContext, StepStatus
666+
667+
monkeypatch.setenv("SPECKIT_INTEGRATION_CLAUDE_EXECUTABLE", "/opt/claude")
668+
seen_which: list[str] = []
669+
670+
def fake_which(name: str) -> str | None:
671+
seen_which.append(name)
672+
return name if name == "/opt/claude" else None
673+
674+
step = CommandStep()
675+
ctx = StepContext(
676+
inputs={"name": "login"},
677+
default_integration="claude",
678+
project_root=str(tmp_path),
679+
)
680+
config = {
681+
"id": "test",
682+
"command": "speckit.specify",
683+
"input": {"args": "{{ inputs.name }}"},
684+
}
685+
686+
mock_result = MagicMock()
687+
mock_result.returncode = 0
688+
mock_result.stdout = '{"result": "done"}'
689+
mock_result.stderr = ""
690+
691+
with patch("specify_cli.workflows.steps.command.shutil.which", side_effect=fake_which), \
692+
patch("subprocess.run", return_value=mock_result) as mock_run:
693+
result = step.execute(config, ctx)
694+
695+
assert result.status == StepStatus.COMPLETED
696+
assert result.output["dispatched"] is True
697+
assert seen_which[:2] == ["claude", "/opt/claude"]
698+
call_args = mock_run.call_args
699+
assert call_args[0][0][0] == "/opt/claude"
700+
assert "/speckit-specify login" in call_args[0][0][2]
701+
661702
def test_dispatch_failure_returns_failed_status(self, tmp_path):
662703
"""When the CLI exits non-zero, the step should fail."""
663704
from unittest.mock import patch, MagicMock
@@ -810,6 +851,46 @@ def test_dispatch_with_mock_cli(self, tmp_path):
810851
assert result.output["dispatched"] is True
811852
assert result.output["exit_code"] == 0
812853

854+
def test_dispatch_uses_executable_override_for_fallback_preflight(self, tmp_path, monkeypatch):
855+
"""Prompt preflight falls back to build_exec_args() argv[0]."""
856+
from unittest.mock import MagicMock, patch
857+
from specify_cli.workflows.steps.prompt import PromptStep
858+
from specify_cli.workflows.base import StepContext, StepStatus
859+
860+
monkeypatch.setenv("SPECKIT_INTEGRATION_CLAUDE_EXECUTABLE", "/opt/claude")
861+
seen_which: list[str] = []
862+
863+
def fake_which(name: str) -> str | None:
864+
seen_which.append(name)
865+
return name if name == "/opt/claude" else None
866+
867+
step = PromptStep()
868+
ctx = StepContext(
869+
default_integration="claude",
870+
project_root=str(tmp_path),
871+
)
872+
config = {
873+
"id": "ask",
874+
"type": "prompt",
875+
"prompt": "Explain this code",
876+
}
877+
878+
mock_result = MagicMock()
879+
mock_result.returncode = 0
880+
mock_result.stdout = "Here is the explanation"
881+
mock_result.stderr = ""
882+
883+
with patch("specify_cli.workflows.steps.prompt.shutil.which", side_effect=fake_which), \
884+
patch("subprocess.run", return_value=mock_result) as mock_run:
885+
result = step.execute(config, ctx)
886+
887+
assert result.status == StepStatus.COMPLETED
888+
assert result.output["dispatched"] is True
889+
assert seen_which[:2] == ["claude", "/opt/claude"]
890+
call_args = mock_run.call_args
891+
assert call_args[0][0][0] == "/opt/claude"
892+
assert call_args[0][0][2] == "Explain this code"
893+
813894
def test_validate_missing_prompt(self):
814895
from specify_cli.workflows.steps.prompt import PromptStep
815896

0 commit comments

Comments
 (0)