Files
thefuck/tests/resolvers/test_error_suggestion.py
T
2026-09-14 20:48:26 +02:00

174 lines
5.9 KiB
Python

"""Tests for the error-suggestion resolver.
The main fixture is the byte-exact stderr of `git psuh` captured from
a real git (od -c verified, see .omo/evidence/
task-1-intelligent-correction.txt); one test additionally runs a live
git so the resolver is pinned against the binary, not the fixture.
"""
import os
import shutil
import subprocess
import pytest
from thefuck import shell_ast
from thefuck.resolvers.error_suggestion import get_suggestion_candidates
from thefuck.types import Command
# Real `git psuh` stderr on this machine (87 bytes, stdout empty):
# "git: 'psuh' is not a git command. See 'git --help'.\n"
# "\n"
# "The most similar command is\n"
# "\tpush\n"
_GIT_PSUH_OUTPUT = (
"git: 'psuh' is not a git command. See 'git --help'.\n"
"\n"
"The most similar command is\n"
"\tpush\n")
# lein-rpl-style output with two suggestions that each pass the
# length-scaled floor for 'rpl' (repl and rple are single edits).
_LEIN_MULTI_OUTPUT = (
"'rpl' is not a task. See 'lein help'.\n"
"\n"
"Did you mean this?\n"
" repl\n"
" rple\n")
requires_ast = pytest.mark.skipif(not shell_ast.AST_AVAILABLE,
reason='bashlex is not available')
def test_corrects_subcommand_from_git_output():
command = Command('git psuh', _GIT_PSUH_OUTPUT)
assert get_suggestion_candidates(command) == 'git push'
def test_corrects_token_in_head_position():
# The binary's error nominates the position: a token in the head
# is replaced exactly like a subcommand.
command = Command('psuh origin master', _GIT_PSUH_OUTPUT)
assert get_suggestion_candidates(command) == 'push origin master'
def test_replaces_whole_quoted_span():
command = Command("git 'psuh'", _GIT_PSUH_OUTPUT)
assert get_suggestion_candidates(command) == 'git push'
def test_replaces_first_occurrence_only():
command = Command('git psuh psuh', _GIT_PSUH_OUTPUT)
assert get_suggestion_candidates(command) == 'git push psuh'
def test_extracts_capitalized_unknown_command():
output = "Unknown command 'gti'\n\nDid you mean\n\tgit\n"
command = Command('gti push', output)
assert get_suggestion_candidates(command) == 'git push'
def test_extracts_lowercase_unknown_command():
output = "unknown command 'gti'\n\nDid you mean\n\tgit\n"
command = Command('gti push', output)
assert get_suggestion_candidates(command) == 'git push'
def test_duplicate_suggestions_are_not_ambiguity():
# Two suggestion sections naming the same command collapse into
# one candidate (unique-survivor semantics); the second section's
# header is consumed as a separator, the quoted lines unquoted.
output = ("Unknown command 'psuh'\n"
"\n"
"Did you mean\n"
" 'push'\n"
"\n"
"The most similar command\n"
" push\n")
command = Command('git psuh', output)
assert get_suggestion_candidates(command) == 'git push'
def test_declines_multi_suggestion_output():
command = Command('lein rpl', _LEIN_MULTI_OUTPUT)
assert get_suggestion_candidates(command) is None
def test_declines_when_no_suggestion_section():
output = "git: 'psuh' is not a git command. See 'git --help'.\n"
command = Command('git psuh', output)
assert get_suggestion_candidates(command) is None
def test_declines_separator_with_no_lines_after():
output = ("'psuh' is not a git command.\n"
"\n"
"The most similar command is\n")
command = Command('git psuh', output)
assert get_suggestion_candidates(command) is None
def test_declines_unrelated_output():
output = ("E: Could not open lock file /var/lib/dpkg/lock - "
"open (13: Permission denied)\n")
command = Command('apt-get install vim', output)
assert get_suggestion_candidates(command) is None
def test_declines_empty_output():
command = Command('git psuh', '')
assert get_suggestion_candidates(command) is None
@requires_ast
def test_declines_when_token_not_in_script():
command = Command('git checkout', _GIT_PSUH_OUTPUT)
assert get_suggestion_candidates(command) is None
def test_declines_self_suggestion():
# The binary naming the typed token back is not a fix: a
# candidate equal to the input would splice to a no-op.
output = ("'push' is not a task. See 'git help'.\n"
"\n"
"Did you mean this?\n"
"\tpush\n")
command = Command('git push', output)
assert get_suggestion_candidates(command) is None
def test_declines_when_suggestion_below_floor():
# burnnoabcd vs burnno1234 scores 2 * 6 / 20 = 0.6, under the
# len-10 floor 0.7, and is not a single edit (pinned in
# tests/resolvers/test_help_resolver.py arithmetic).
output = ("'burnnoabcd' is not a task.\n"
"\n"
"Did you mean this?\n"
"\tburnno1234\n")
command = Command('fake burnnoabcd', output)
assert get_suggestion_candidates(command) is None
def test_returns_none_when_ast_unavailable(monkeypatch):
monkeypatch.setattr(shell_ast, 'AST_AVAILABLE', False)
command = Command('git psuh', _GIT_PSUH_OUTPUT)
assert get_suggestion_candidates(command) is None
def test_corrects_live_git_output(tmpdir):
if shutil.which('git') is None:
pytest.skip('git is not available')
env = dict(os.environ, LC_ALL='C', LANG='C')
try:
proc = subprocess.Popen(
['git', 'psuh'], stdout=subprocess.PIPE,
stderr=subprocess.PIPE, env=env, cwd=str(tmpdir))
_, stderr = proc.communicate(timeout=5)
except (OSError, subprocess.TimeoutExpired):
pytest.skip('git psuh probe failed')
output = stderr.decode('utf-8', 'replace')
if ('The most similar command' not in output
and 'Did you mean' not in output):
pytest.skip('git prints no suggestions on this machine')
command = Command('git psuh', output)
assert get_suggestion_candidates(command) == 'git push'