"""Tests for commands.py — parsing and formatting functions only.""" import time from unittest.mock import MagicMock import pytest from commands import extract_args, parse_args, format_bounty class TestExtractArgs: def test_basic_split(self): assert extract_args("/add hello world") == ["hello", "world"] assert extract_args("/track 42") == ["42"] def test_no_args(self): assert extract_args("/bounty") == [] assert extract_args("/add") == [] def test_preserves_urls(self): result = extract_args("/add check https://example.com stuff") assert result == ["check", "https://example.com", "stuff"] def test_none_input(self): assert extract_args(None) == [] def test_strips_whitespace(self): assert extract_args(" /add hello world ") == ["hello", "world"] class TestParseArgs: def test_text_only(self): text, link, due = parse_args(["hello", "world"]) assert text == "hello world" assert link is None assert due is None def test_link_extracted(self): text, link, due = parse_args(["hello", "https://example.com"]) # "hello" is non-link non-date → becomes text; only the URL becomes link assert text == "hello" assert link == "https://example.com" assert due is None def test_text_and_link(self): text, link, due = parse_args(["hello", "world", "https://example.com"]) assert text == "hello world" assert link == "https://example.com" def test_due_date_parsed(self): text, link, due = parse_args(["hello", "tomorrow"]) assert text == "hello" assert due is not None # Should be some time in the future assert due > int(time.time()) def test_all_three(self): text, link, due = parse_args(["hello", "https://example.com", "tomorrow"]) assert text == "hello" assert link == "https://example.com" assert due is not None def test_http_and_https_both_detected(self): _, link1, _ = parse_args(["http://example.com"]) _, link2, _ = parse_args(["https://example.com"]) assert link1 == "http://example.com" assert link2 == "https://example.com" def test_non_url_non_date_becomes_text(self): text, link, due = parse_args(["fix", "the", "bug"]) assert text == "fix the bug" assert link is None assert due is None def test_multiple_links_first_only(self): _, link, _ = parse_args(["text", "https://first.com", "https://second.com"]) assert link == "https://first.com" def test_due_date_after_link(self): text, link, due = parse_args(["task", "https://example.com", "in 5 days"]) assert text == "task" assert link == "https://example.com" assert due is not None def test_empty_args(self): text, link, due = parse_args([]) assert text is None assert link is None assert due is None def test_date_parser_failure_returns_none(self): # "asdfjkl" is not parseable → goes to text text, link, due = parse_args(["hello", "asdfjkl"]) assert text == "hello asdfjkl" assert due is None def test_link_takes_first_match(self): # Even if it's not a valid URL, starts with https:// _, link, _ = parse_args(["skip", "https://not-real.but-still-a-link"]) assert link == "https://not-real.but-still-a-link" class TestFormatBounty: def _row( self, id=1, text="Test bounty", link="https://example.com", due_date_ts=None, created_by_username="alice", ): row = MagicMock() row.__getitem__ = lambda s, k: { "id": id, "text": text, "link": link, "due_date_ts": due_date_ts, "created_by_username": created_by_username, }[k] return row def test_shows_id(self): b = self._row(id=42) out = format_bounty(b, show_id=True) assert "[#42]" in out def test_hides_id_when_requested(self): b = self._row(id=42) out = format_bounty(b, show_id=False) assert "[#42]" not in out def test_text_included(self): b = self._row(text="Fix the login bug") out = format_bounty(b) assert "Fix the login bug" in out def test_link_shown(self): b = self._row(link="https://github.com/bob/repo") out = format_bounty(b) assert "https://github.com/bob/repo" in out assert "🔗" in out def test_no_link(self): b = self._row(link=None) out = format_bounty(b) assert "🔗" not in out def test_due_date_future(self): future = int(time.time()) + 3 * 86400 b = self._row(due_date_ts=future) out = format_bounty(b) assert "⏰" in out assert "3d" in out def test_due_date_today(self): today = int(time.time()) + 3600 # within today b = self._row(due_date_ts=today) out = format_bounty(b) assert "TODAY" in out def test_due_date_overdue(self): past = int(time.time()) - 86400 # yesterday b = self._row(due_date_ts=past) out = format_bounty(b) assert "OVERDUE" in out def test_created_by_shown(self): b = self._row(created_by_username="bob") out = format_bounty(b) assert "@bob" in out def test_created_by_unknown_fallback(self): b = self._row(created_by_username=None) out = format_bounty(b) assert "@unknown" in out