243 lines
6.6 KiB
Python
243 lines
6.6 KiB
Python
"""
|
|
©AngelaMos | 2026
|
|
test_cli.py
|
|
|
|
Integration tests for all five CLI commands via Typer's CliRunner
|
|
|
|
Invokes each CLI command end-to-end without spawning subprocesses and
|
|
verifies exit codes and output content. Covers encode, decode, detect,
|
|
peel, and chain along with the --version flag and error paths (invalid
|
|
format, bad input).
|
|
|
|
Tests:
|
|
TestEncodeCommand - base64, hex, base32, url, empty input
|
|
TestDecodeCommand - base64, hex, invalid input returns non-zero exit
|
|
TestDetectCommand - base64 detection, hex detection, no-match message
|
|
TestPeelCommand - single layer, plain text with no layers
|
|
TestChainCommand - single step, multiple steps, unknown format error
|
|
TestVersionFlag - version string present in output
|
|
|
|
Connects to:
|
|
cli.py - imports app (the Typer application under test)
|
|
"""
|
|
|
|
from typer.testing import CliRunner
|
|
|
|
from base64_tool.cli import app
|
|
|
|
|
|
runner = CliRunner()
|
|
|
|
|
|
class TestEncodeCommand:
|
|
def test_encode_base64(self) -> None:
|
|
"""
|
|
Checks that the encode command outputs the correct base64 string
|
|
"""
|
|
result = runner.invoke(app, ["encode", "Hello World"])
|
|
assert result.exit_code == 0
|
|
assert "SGVsbG8gV29ybGQ=" in result.output
|
|
|
|
def test_encode_hex(self) -> None:
|
|
"""
|
|
Checks that the encode command outputs the correct hex string with --format hex
|
|
"""
|
|
result = runner.invoke(
|
|
app,
|
|
["encode",
|
|
"Hello",
|
|
"--format",
|
|
"hex"],
|
|
)
|
|
assert result.exit_code == 0
|
|
assert "48656c6c6f" in result.output
|
|
|
|
def test_encode_base32(self) -> None:
|
|
"""
|
|
Checks that the encode command outputs the correct base32 string with --format base32
|
|
"""
|
|
result = runner.invoke(
|
|
app,
|
|
["encode",
|
|
"Hello",
|
|
"--format",
|
|
"base32"],
|
|
)
|
|
assert result.exit_code == 0
|
|
assert "JBSWY3DP" in result.output
|
|
|
|
def test_encode_url(self) -> None:
|
|
"""
|
|
Checks that the encode command percent-encodes special characters with --format url
|
|
"""
|
|
result = runner.invoke(
|
|
app,
|
|
["encode",
|
|
"hello world&test",
|
|
"--format",
|
|
"url"],
|
|
)
|
|
assert result.exit_code == 0
|
|
assert "%20" in result.output or "hello" in result.output
|
|
|
|
def test_encode_empty_string(self) -> None:
|
|
"""
|
|
Checks that encoding an empty string succeeds without error
|
|
"""
|
|
result = runner.invoke(app, ["encode", ""])
|
|
assert result.exit_code == 0
|
|
|
|
|
|
class TestDecodeCommand:
|
|
def test_decode_base64(self) -> None:
|
|
"""
|
|
Checks that the decode command recovers 'Hello World' from a known base64 string
|
|
"""
|
|
result = runner.invoke(
|
|
app,
|
|
["decode",
|
|
"SGVsbG8gV29ybGQ="],
|
|
)
|
|
assert result.exit_code == 0
|
|
assert "Hello World" in result.output
|
|
|
|
def test_decode_hex(self) -> None:
|
|
"""
|
|
Checks that the decode command recovers 'Hello' from a hex string
|
|
"""
|
|
result = runner.invoke(
|
|
app,
|
|
["decode",
|
|
"48656c6c6f",
|
|
"--format",
|
|
"hex"],
|
|
)
|
|
assert result.exit_code == 0
|
|
assert "Hello" in result.output
|
|
|
|
def test_decode_invalid_base64(self) -> None:
|
|
"""
|
|
Checks that decoding garbage input exits with a non-zero code
|
|
"""
|
|
result = runner.invoke(
|
|
app,
|
|
["decode",
|
|
"!!!invalid!!!"],
|
|
)
|
|
assert result.exit_code != 0
|
|
|
|
|
|
class TestDetectCommand:
|
|
def test_detect_base64(self) -> None:
|
|
"""
|
|
Checks that the detect command identifies base64 in its output
|
|
"""
|
|
result = runner.invoke(
|
|
app,
|
|
["detect",
|
|
"SGVsbG8gV29ybGQ="],
|
|
)
|
|
assert result.exit_code == 0
|
|
assert "base64" in result.output.lower()
|
|
|
|
def test_detect_hex(self) -> None:
|
|
"""
|
|
Checks that the detect command identifies hex in its output
|
|
"""
|
|
result = runner.invoke(
|
|
app,
|
|
["detect",
|
|
"48656c6c6f20576f726c64"],
|
|
)
|
|
assert result.exit_code == 0
|
|
assert "hex" in result.output.lower()
|
|
|
|
def test_detect_no_match(self) -> None:
|
|
"""
|
|
Checks that the detect command reports no encoding found for plain text
|
|
"""
|
|
result = runner.invoke(
|
|
app,
|
|
["detect",
|
|
"just plain text"],
|
|
)
|
|
assert result.exit_code == 0
|
|
assert "no encoding" in result.output.lower()
|
|
|
|
|
|
class TestPeelCommand:
|
|
def test_peel_single_layer(self) -> None:
|
|
"""
|
|
Checks that the peel command reports at least one layer for a base64 string
|
|
"""
|
|
result = runner.invoke(
|
|
app,
|
|
["peel",
|
|
"SGVsbG8gV29ybGQ="],
|
|
)
|
|
assert result.exit_code == 0
|
|
assert "layer" in result.output.lower()
|
|
|
|
def test_peel_no_encoding(self) -> None:
|
|
"""
|
|
Checks that the peel command exits cleanly when no encoding is found
|
|
"""
|
|
result = runner.invoke(
|
|
app,
|
|
["peel",
|
|
"hello world"],
|
|
)
|
|
assert result.exit_code == 0
|
|
|
|
|
|
class TestChainCommand:
|
|
def test_chain_single_step(self) -> None:
|
|
"""
|
|
Checks that the chain command applies one base64 step correctly
|
|
"""
|
|
result = runner.invoke(
|
|
app,
|
|
["chain",
|
|
"Hello",
|
|
"--steps",
|
|
"base64"],
|
|
)
|
|
assert result.exit_code == 0
|
|
assert "SGVsbG8=" in result.output
|
|
|
|
def test_chain_multiple_steps(self) -> None:
|
|
"""
|
|
Checks that the chain command applies two steps in sequence without error
|
|
"""
|
|
result = runner.invoke(
|
|
app,
|
|
["chain",
|
|
"Hi",
|
|
"--steps",
|
|
"base64,hex"],
|
|
)
|
|
assert result.exit_code == 0
|
|
|
|
def test_chain_invalid_format(self) -> None:
|
|
"""
|
|
Checks that an unknown format name causes the chain command to exit with an error
|
|
"""
|
|
result = runner.invoke(
|
|
app,
|
|
["chain",
|
|
"test",
|
|
"--steps",
|
|
"fake"],
|
|
)
|
|
assert result.exit_code != 0
|
|
|
|
|
|
class TestVersionFlag:
|
|
def test_version_output(self) -> None:
|
|
"""
|
|
Checks that --version prints the tool name and exits cleanly
|
|
"""
|
|
result = runner.invoke(app, ["--version"])
|
|
assert result.exit_code == 0
|
|
assert "b64tool" in result.output
|