feat: normalize URLs without scheme to https://
- Add normalize_url() helper function in commands.py - Automatically prefix URLs without scheme (e.g. github.com → https://github.com) - Applies to both -link flag and auto-detected URLs - Add 5 new tests for URL normalization - Fix existing tests to handle 5-value return from parse_args() Examples: /add Fix bug github.com/user/repo → stored as: https://github.com/user/repo
This commit is contained in:
@@ -81,6 +81,15 @@ def parse_args(
|
||||
return True
|
||||
return False
|
||||
|
||||
def normalize_url(url: str) -> str:
|
||||
"""Normalize URL by adding https:// prefix if missing."""
|
||||
if not url:
|
||||
return url
|
||||
if url.startswith("http://") or url.startswith("https://"):
|
||||
return url
|
||||
# Add https:// for URLs without scheme
|
||||
return f"https://{url}"
|
||||
|
||||
def is_time(s: str) -> bool:
|
||||
if not s or ":" not in s:
|
||||
return False
|
||||
@@ -106,7 +115,7 @@ def parse_args(
|
||||
|
||||
if arg == "-link":
|
||||
if i + 1 < len(args) and not args[i + 1].startswith("-"):
|
||||
link = args[i + 1]
|
||||
link = normalize_url(args[i + 1])
|
||||
i += 2
|
||||
else:
|
||||
clear_link = True
|
||||
@@ -129,7 +138,7 @@ def parse_args(
|
||||
clear_date = True
|
||||
i += 1
|
||||
elif not link and is_url(arg):
|
||||
link = arg
|
||||
link = normalize_url(arg)
|
||||
i += 1
|
||||
elif due_date_ts is None:
|
||||
due_date_ts = parse_date_with_tz(arg)
|
||||
|
||||
@@ -52,75 +52,97 @@ class TestExtractArgs:
|
||||
|
||||
class TestParseArgs:
|
||||
def test_text_only(self):
|
||||
text, link, due = parse_args(["hello", "world"])
|
||||
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"])
|
||||
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"])
|
||||
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"])
|
||||
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"])
|
||||
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"])
|
||||
_, 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"])
|
||||
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"])
|
||||
_, 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"])
|
||||
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([])
|
||||
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"])
|
||||
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"])
|
||||
_, link, _, _, _ = parse_args(["skip", "https://not-real.but-still-a-link"])
|
||||
assert link == "https://not-real.but-still-a-link"
|
||||
|
||||
def test_url_without_scheme_normalized_to_https(self):
|
||||
"""URLs without scheme should get https:// prefix."""
|
||||
_, link, _, _, _ = parse_args(["github.com/user/repo"])
|
||||
assert link == "https://github.com/user/repo"
|
||||
|
||||
def test_url_without_scheme_github_normalized(self):
|
||||
text, link, _, _, _ = parse_args(["Fix bug", "github.com/owner/repo"])
|
||||
assert text == "Fix bug"
|
||||
assert link == "https://github.com/owner/repo"
|
||||
|
||||
def test_url_with_explicit_https_unchanged(self):
|
||||
_, link, _, _, _ = parse_args(["task", "https://example.com/page"])
|
||||
assert link == "https://example.com/page"
|
||||
|
||||
def test_url_with_http_unchanged(self):
|
||||
_, link, _, _, _ = parse_args(["task", "http://example.com/page"])
|
||||
assert link == "http://example.com/page"
|
||||
|
||||
def test_url_link_flag_without_scheme_normalized(self):
|
||||
_, link, _, _, _ = parse_args(["-link", "example.com/path"])
|
||||
assert link == "https://example.com/path"
|
||||
|
||||
|
||||
class TestFormatBounty:
|
||||
def _row(
|
||||
|
||||
Reference in New Issue
Block a user