Back to Repositories

Testing Authentication Plugin System in HTTPie CLI

This test suite validates authentication plugin functionality in HTTPie’s CLI, focusing on different authentication scenarios and plugin configurations. The tests verify custom auth plugin behaviors, credential handling, and integration with HTTPie’s authentication system.

Test Coverage Overview

The test suite provides comprehensive coverage of HTTPie’s authentication plugin system.

Key areas tested include:
  • Auth plugin parsing configuration
  • Optional authentication handling
  • Credential processing
  • Password prompting behavior
Edge cases cover scenarios with missing credentials, raw auth headers, and various plugin configuration combinations.

Implementation Analysis

The testing approach uses pytest with mock objects to simulate authentication interactions.

Key implementation patterns include:
  • Custom AuthPlugin class inheritance
  • Plugin registration/unregistration lifecycle management
  • Credential separation and processing
  • HTTP request modification via auth handlers

Technical Details

Testing tools and configuration:
  • unittest.mock for password prompt simulation
  • pytest fixtures for HTTP endpoints
  • Custom AuthPlugin implementations
  • HTTPie plugin registry integration
  • Basic auth encoding validation

Best Practices Demonstrated

The test suite exemplifies strong testing practices through isolated test cases, proper teardown, and comprehensive assertion coverage.

Notable practices include:
  • Proper plugin cleanup after tests
  • Isolated test scenarios
  • Explicit assertion messages
  • Consistent test structure
  • Clear test naming conventions

httpie/cli

tests/test_auth_plugins.py

            
from unittest import mock

from httpie.cli.constants import SEPARATOR_CREDENTIALS
from httpie.plugins import AuthPlugin
from httpie.plugins.registry import plugin_manager
from .utils import http, HTTP_OK


# TODO: run all these tests in session mode as well

USERNAME = 'user'
PASSWORD = 'password'
# Basic auth encoded `USERNAME` and `PASSWORD`
# noinspection SpellCheckingInspection
BASIC_AUTH_HEADER_VALUE = 'Basic dXNlcjpwYXNzd29yZA=='
BASIC_AUTH_URL = f'/basic-auth/{USERNAME}/{PASSWORD}'
AUTH_OK = {'authenticated': True, 'user': USERNAME}


def basic_auth(header=BASIC_AUTH_HEADER_VALUE):

    def inner(r):
        r.headers['Authorization'] = header
        return r

    return inner


def test_auth_plugin_parse_auth_false(httpbin):

    class Plugin(AuthPlugin):
        auth_type = 'test-parse-false'
        auth_parse = False

        def get_auth(self, username=None, password=None):
            assert username is None
            assert password is None
            assert self.raw_auth == BASIC_AUTH_HEADER_VALUE
            return basic_auth(self.raw_auth)

    plugin_manager.register(Plugin)
    try:
        r = http(
            httpbin + BASIC_AUTH_URL,
            '--auth-type',
            Plugin.auth_type,
            '--auth',
            BASIC_AUTH_HEADER_VALUE,
        )
        assert HTTP_OK in r
        assert r.json == AUTH_OK
    finally:
        plugin_manager.unregister(Plugin)


def test_auth_plugin_require_auth_false(httpbin):

    class Plugin(AuthPlugin):
        auth_type = 'test-require-false'
        auth_require = False

        def get_auth(self, username=None, password=None):
            assert self.raw_auth is None
            assert username is None
            assert password is None
            return basic_auth()

    plugin_manager.register(Plugin)
    try:
        r = http(
            httpbin + BASIC_AUTH_URL,
            '--auth-type',
            Plugin.auth_type,
        )
        assert HTTP_OK in r
        assert r.json == AUTH_OK
    finally:
        plugin_manager.unregister(Plugin)


def test_auth_plugin_require_auth_false_and_auth_provided(httpbin):

    class Plugin(AuthPlugin):
        auth_type = 'test-require-false-yet-provided'
        auth_require = False

        def get_auth(self, username=None, password=None):
            assert self.raw_auth == USERNAME + SEPARATOR_CREDENTIALS + PASSWORD
            assert username == USERNAME
            assert password == PASSWORD
            return basic_auth()

    plugin_manager.register(Plugin)
    try:
        r = http(
            httpbin + BASIC_AUTH_URL,
            '--auth-type',
            Plugin.auth_type,
            '--auth',
            USERNAME + SEPARATOR_CREDENTIALS + PASSWORD,
        )
        assert HTTP_OK in r
        assert r.json == AUTH_OK
    finally:
        plugin_manager.unregister(Plugin)


@mock.patch('httpie.cli.argtypes.AuthCredentials._getpass',
            new=lambda self, prompt: 'UNEXPECTED_PROMPT_RESPONSE')
def test_auth_plugin_prompt_password_false(httpbin):

    class Plugin(AuthPlugin):
        auth_type = 'test-prompt-false'
        prompt_password = False

        def get_auth(self, username=None, password=None):
            assert self.raw_auth == USERNAME
            assert username == USERNAME
            assert password is None
            return basic_auth()

    plugin_manager.register(Plugin)

    try:
        r = http(
            httpbin + BASIC_AUTH_URL,
            '--auth-type',
            Plugin.auth_type,
            '--auth',
            USERNAME,
        )
        assert HTTP_OK in r
        assert r.json == AUTH_OK
    finally:
        plugin_manager.unregister(Plugin)