feat: matrix-hermes project structure and Matrix authentication

This commit is contained in:
2026-05-23 11:38:00 -04:00
commit 017acc8253
14 changed files with 2152 additions and 0 deletions
+205
View File
@@ -0,0 +1,205 @@
"""Tests for matrix_hermes.cli module."""
import json
from unittest.mock import MagicMock, patch
import pytest
from matrix_hermes.cli import (
_mask_token,
build_parser,
cmd_check,
cmd_login,
cmd_logout,
cmd_show_config,
cmd_whoami,
main,
)
class TestMaskToken:
"""Tests for _mask_token helper."""
def test_short_token(self):
assert _mask_token("abc") == "***"
def test_long_token(self):
result = _mask_token("syt_abcdefghijklmnopqrstuvwxyz")
assert result.startswith("syt_ab")
assert result.endswith("uvwxyz")
assert "..." in result
def test_exact_length(self):
"""12-char token with visible=6: len<=12 means fully masked."""
result = _mask_token("123456789012")
assert result == "************"
def test_just_over_threshold(self):
"""13-char token with visible=6: len>12 means partial mask."""
result = _mask_token("1234567890123")
assert result == "123456...890123"
class TestParser:
"""Tests for CLI argument parser."""
def test_build_parser(self):
parser = build_parser()
assert "login" in parser._subparsers._group_actions[0].choices
assert "whoami" in parser._subparsers._group_actions[0].choices
assert "check" in parser._subparsers._group_actions[0].choices
assert "show-config" in parser._subparsers._group_actions[0].choices
def test_no_args_shows_help(self, capsys):
"""Running with no args shows help."""
result = main([])
# Should not crash
captured = capsys.readouterr()
assert "usage" in captured.out.lower() or result == 0
class TestCmdCheck:
"""Tests for 'check' command."""
def test_not_configured(self, clean_env, capsys):
"""When no Matrix vars are set, check should fail."""
args = build_parser().parse_args(["check"])
result = cmd_check(args)
assert result == 1
captured = capsys.readouterr()
assert "not configured" in captured.out.lower()
def test_configured(self, mock_env, capsys):
"""When Matrix vars are set, check should succeed."""
args = build_parser().parse_args(["check"])
result = cmd_check(args)
assert result == 0
captured = capsys.readouterr()
assert "valid" in captured.out.lower()
class TestCmdShowConfig:
"""Tests for 'show-config' command."""
def test_output_format(self, mock_env, capsys):
"""show-config outputs env var format."""
args = build_parser().parse_args(["show-config"])
result = cmd_show_config(args)
assert result == 0
captured = capsys.readouterr()
assert "MATRIX_HOMESERVER=" in captured.out
assert "MATRIX_ACCESS_TOKEN=" in captured.out
def test_redacted(self, mock_env, capsys):
"""show-config --redact masks sensitive values."""
args = build_parser().parse_args(["show-config", "--redact"])
result = cmd_show_config(args)
assert result == 0
captured = capsys.readouterr()
# Token should be masked
assert "syt_test_token" not in captured.out
assert "syt_test" in captured.out or "..." in captured.out
class TestCmdLogin:
"""Tests for 'login' command."""
def test_no_homeserver(self, clean_env, capsys):
"""Login fails without homeserver."""
args = build_parser().parse_args(["login"])
result = cmd_login(args)
assert result == 1
def test_with_valid_token(self, mock_env, capsys):
"""Login with valid token prints success."""
with patch("urllib.request.urlopen") as mock_urlopen:
mock_resp = MagicMock()
mock_resp.read.return_value = json.dumps(
{"user_id": "@bot:example.org", "device_id": "DEV1"}
).encode()
mock_resp.__enter__.return_value = mock_resp
mock_urlopen.return_value = mock_resp
args = build_parser().parse_args(["login"])
result = cmd_login(args)
assert result == 0
def test_with_bad_credentials(self, mock_env, capsys):
"""Login with bad credentials prints error."""
with patch("urllib.request.urlopen") as mock_urlopen:
mock_resp = MagicMock()
mock_resp.read.return_value = json.dumps(
{"errcode": "M_FORBIDDEN", "error": "Bad password"}
).encode()
mock_resp.__enter__.return_value = mock_resp
mock_urlopen.return_value = mock_resp
# Override with password-based login (no valid token)
import os
os.environ["MATRIX_ACCESS_TOKEN"] = ""
os.environ["MATRIX_PASSWORD"] = "wrong"
args = build_parser().parse_args(["login"])
result = cmd_login(args)
assert result == 1
class TestCmdWhoami:
"""Tests for 'whoami' command."""
def test_no_homeserver(self, clean_env, capsys):
"""whoami fails without homeserver."""
args = build_parser().parse_args(["whoami"])
result = cmd_whoami(args)
assert result == 1
def test_valid(self, mock_env, capsys):
"""whoami with valid token."""
with patch("urllib.request.urlopen") as mock_urlopen:
mock_resp = MagicMock()
mock_resp.read.return_value = json.dumps(
{"user_id": "@bot:example.org"}
).encode()
mock_resp.__enter__.return_value = mock_resp
mock_urlopen.return_value = mock_resp
args = build_parser().parse_args(["whoami"])
result = cmd_whoami(args)
assert result == 0
def test_invalid(self, mock_env, capsys):
"""whoami with invalid token."""
with patch("urllib.request.urlopen") as mock_urlopen:
mock_resp = MagicMock()
mock_resp.read.return_value = json.dumps(
{"errcode": "M_UNKNOWN_TOKEN", "error": "Unrecognised"}
).encode()
mock_resp.__enter__.return_value = mock_resp
mock_urlopen.return_value = mock_resp
args = build_parser().parse_args(["whoami"])
result = cmd_whoami(args)
assert result == 1
class TestCmdLogout:
"""Tests for 'logout' command."""
def test_no_homeserver(self, clean_env, capsys):
"""logout fails without homeserver."""
args = build_parser().parse_args(["logout"])
result = cmd_logout(args)
assert result == 1
def test_success(self, mock_env, capsys):
"""logout with valid token."""
with patch("urllib.request.urlopen") as mock_urlopen:
mock_resp = MagicMock()
mock_resp.read.return_value = json.dumps({}).encode()
mock_resp.__enter__.return_value = mock_resp
mock_urlopen.return_value = mock_resp
args = build_parser().parse_args(["logout"])
result = cmd_logout(args)
assert result == 0