Back to Repositories

Testing DNF Command Correction Logic in thefuck

This test suite validates the DNF command correction functionality in thefuck project, specifically handling scenarios where users mistype DNF commands. It ensures the application can properly identify and suggest corrections for invalid DNF operations.

Test Coverage Overview

The test suite covers DNF command validation and correction mechanisms, focusing on common typos in DNF operations. Key test scenarios include:

  • Invalid command detection for mistyped operations
  • Command correction suggestions for common typos
  • Proper handling of DNF operation list parsing
  • Edge cases with non-DNF commands

Implementation Analysis

The testing approach utilizes pytest’s parametrize feature for comprehensive test case coverage. The implementation employs mock objects to simulate DNF help output and command responses, ensuring consistent test behavior across different environments.

  • Parametrized test cases for various command scenarios
  • Mocked subprocess calls for deterministic testing
  • Fixture-based test setup for reusable components

Technical Details

The test suite leverages several technical components:

  • pytest framework for test organization
  • BytesIO for simulating command output streams
  • Mock objects for subprocess isolation
  • Command class from thefuck.types for command handling

Best Practices Demonstrated

The test implementation showcases several testing best practices:

  • Separation of concerns with distinct test functions
  • Comprehensive error case coverage
  • Efficient test data management using fixtures
  • Clear test case organization using parametrization

nvbn/thefuck

tests/rules/test_dnf_no_such_command.py

            
from io import BytesIO
import pytest
from thefuck.types import Command
from thefuck.rules.dnf_no_such_command import match, get_new_command, _get_operations


help_text = b'''usage: dnf [options] COMMAND

List of Main Commands:

autoremove                remove all unneeded packages that were originally installed as dependencies
check                     check for problems in the packagedb
check-update              check for available package upgrades
clean                     remove cached data
deplist                   List package's dependencies and what packages provide them
distro-sync               synchronize installed packages to the latest available versions
downgrade                 Downgrade a package
group                     display, or use, the groups information
help                      display a helpful usage message
history                   display, or use, the transaction history
info                      display details about a package or group of packages
install                   install a package or packages on your system
list                      list a package or groups of packages
makecache                 generate the metadata cache
mark                      mark or unmark installed packages as installed by user.
provides                  find what package provides the given value
reinstall                 reinstall a package
remove                    remove a package or packages from your system
repolist                  display the configured software repositories
repoquery                 search for packages matching keyword
repository-packages       run commands on top of all packages in given repository
search                    search package details for the given string
shell                     run an interactive DNF shell
swap                      run an interactive dnf mod for remove and install one spec
updateinfo                display advisories about packages
upgrade                   upgrade a package or packages on your system
upgrade-minimal           upgrade, but only 'newest' package match which fixes a problem that affects your system

List of Plugin Commands:

builddep                  Install build dependencies for package or spec file
config-manager            manage dnf configuration options and repositories
copr                      Interact with Copr repositories.
debug-dump                dump information about installed rpm packages to file
debug-restore             restore packages recorded in debug-dump file
debuginfo-install         install debuginfo packages
download                  Download package to current directory
needs-restarting          determine updated binaries that need restarting
playground                Interact with Playground repository.
repoclosure               Display a list of unresolved dependencies for repositories
repograph                 Output a full package dependency graph in dot format
repomanage                Manage a directory of rpm packages
reposync                  download all packages from remote repo

Optional arguments:
  -c [config file], --config [config file]
                        config file location
  -q, --quiet           quiet operation
  -v, --verbose         verbose operation
  --version             show DNF version and exit
  --installroot [path]  set install root
  --nodocs              do not install documentations
  --noplugins           disable all plugins
  --enableplugin [plugin]
                        enable plugins by name
  --disableplugin [plugin]
                        disable plugins by name
  --releasever RELEASEVER
                        override the value of $releasever in config and repo
                        files
  --setopt SETOPTS      set arbitrary config and repo options
  --skip-broken         resolve depsolve problems by skipping packages
  -h, --help, --help-cmd
                        show command help
  --allowerasing        allow erasing of installed packages to resolve
                        dependencies
  -b, --best            try the best available package versions in
                        transactions.
  -C, --cacheonly       run entirely from system cache, don't update cache
  -R [minutes], --randomwait [minutes]
                        maximum command wait time
  -d [debug level], --debuglevel [debug level]
                        debugging output level
  --debugsolver         dumps detailed solving results into files
  --showduplicates      show duplicates, in repos, in list/search commands
  -e ERRORLEVEL, --errorlevel ERRORLEVEL
                        error output level
  --obsoletes           enables dnf's obsoletes processing logic for upgrade
                        or display capabilities that the package obsoletes for
                        info, list and repoquery
  --rpmverbosity [debug level name]
                        debugging output level for rpm
  -y, --assumeyes       automatically answer yes for all questions
  --assumeno            automatically answer no for all questions
  --enablerepo [repo]
  --disablerepo [repo]
  --repo [repo], --repoid [repo]
                        enable just specific repositories by an id or a glob,
                        can be specified multiple times
  -x [package], --exclude [package], --excludepkgs [package]
                        exclude packages by name or glob
  --disableexcludes [repo], --disableexcludepkgs [repo]
                        disable excludepkgs
  --repofrompath [repo,path]
                        label and path to additional repository, can be
                        specified multiple times.
  --noautoremove        disable removal of dependencies that are no longer
                        used
  --nogpgcheck          disable gpg signature checking
  --color COLOR         control whether colour is used
  --refresh             set metadata as expired before running the command
  -4                    resolve to IPv4 addresses only
  -6                    resolve to IPv6 addresses only
  --destdir DESTDIR, --downloaddir DESTDIR
                        set directory to copy packages to
  --downloadonly        only download packages
  --bugfix              Include bugfix relevant packages, in updates
  --enhancement         Include enhancement relevant packages, in updates
  --newpackage          Include newpackage relevant packages, in updates
  --security            Include security relevant packages, in updates
  --advisory ADVISORY, --advisories ADVISORY
                        Include packages needed to fix the given advisory, in
                        updates
  --bzs BUGZILLA        Include packages needed to fix the given BZ, in
                        updates
  --cves CVES           Include packages needed to fix the given CVE, in
                        updates
  --sec-severity {Critical,Important,Moderate,Low}, --secseverity {Critical,Important,Moderate,Low}
                        Include security relevant packages matching the
                        severity, in updates
  --forcearch ARCH      Force the use of an architecture
'''

dnf_operations = ['autoremove', 'check', 'check-update', 'clean', 'deplist',
                  'distro-sync', 'downgrade', 'group', 'help', 'history',
                  'info', 'install', 'list', 'makecache', 'mark', 'provides',
                  'reinstall', 'remove', 'repolist', 'repoquery',
                  'repository-packages', 'search', 'shell', 'swap', 'updateinfo',
                  'upgrade', 'upgrade-minimal', 'builddep', 'config-manager',
                  'copr', 'debug-dump', 'debug-restore', 'debuginfo-install',
                  'download', 'needs-restarting', 'playground', 'repoclosure',
                  'repograph', 'repomanage', 'reposync']


def invalid_command(command):
    return """No such command: %s. Please use /usr/bin/dnf --help
It could be a DNF plugin command, try: "dnf install 'dnf-command(%s)'"
""" % (command, command)


@pytest.mark.parametrize('output', [
    (invalid_command('saerch')),
    (invalid_command('isntall'))
])
def test_match(output):
    assert match(Command('dnf', output))


@pytest.mark.parametrize('script, output', [
    ('pip', invalid_command('isntall')),
    ('vim', "")
])
def test_not_match(script, output):
    assert not match(Command(script, output))


@pytest.fixture
def set_help(mocker):
    mock = mocker.patch('subprocess.Popen')

    def _set_text(text):
        mock.return_value.stdout = BytesIO(text)

    return _set_text


def test_get_operations(set_help):
    set_help(help_text)
    assert _get_operations() == dnf_operations


@pytest.mark.parametrize('script, output, result', [
    ('dnf isntall vim', invalid_command('isntall'),
     'dnf install vim'),
    ('dnf saerch vim', invalid_command('saerch'),
     'dnf search vim'),
])
def test_get_new_command(set_help, output, script, result):
    set_help(help_text)
    assert result in get_new_command(Command(script, output))