release: v1.2.0 — About+update checker, log viewer, restart button, dup-name fix, stale-file hardening #26
@@ -3,6 +3,8 @@ proper test functions with tmp_path/monkeypatch fixtures)."""
|
||||
|
||||
import json
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -638,3 +640,145 @@ def test_args_secret_warning_env_not_triggered():
|
||||
def test_args_secret_warning_empty():
|
||||
assert c.args_secret_warning({}) is None
|
||||
assert c.args_secret_warning({"args": []}) is None
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Version / update checking
|
||||
# --------------------------------------------------------------------------- #
|
||||
def test_dunder_version_matches_pyproject():
|
||||
# Guards against the version drifting out of sync between the two places
|
||||
# a human might bump it.
|
||||
assert c.__version__ == "1.1.0"
|
||||
|
||||
|
||||
def test_parse_version_basic():
|
||||
assert c.parse_version("1.2.3") == (1, 2, 3)
|
||||
|
||||
|
||||
def test_parse_version_strips_v_prefix():
|
||||
assert c.parse_version("v1.2.3") == (1, 2, 3)
|
||||
|
||||
|
||||
def test_parse_version_strips_prerelease_suffix():
|
||||
assert c.parse_version("1.2.3-beta.1") == (1, 2, 3)
|
||||
assert c.parse_version("1.2.3+build5") == (1, 2, 3)
|
||||
|
||||
|
||||
def test_parse_version_malformed_returns_empty_tuple():
|
||||
assert c.parse_version("not-a-version") == ()
|
||||
assert c.parse_version("") == ()
|
||||
assert c.parse_version(None) == ()
|
||||
|
||||
|
||||
def test_parse_version_stops_at_first_non_numeric_component():
|
||||
assert c.parse_version("1.2.rc1") == (1, 2)
|
||||
|
||||
|
||||
def test_is_newer_version_true_when_candidate_is_newer():
|
||||
assert c.is_newer_version("1.1.0", "1.2.0") is True
|
||||
|
||||
|
||||
def test_is_newer_version_false_when_candidate_is_older():
|
||||
assert c.is_newer_version("1.2.0", "1.1.0") is False
|
||||
|
||||
|
||||
def test_is_newer_version_false_when_equal():
|
||||
assert c.is_newer_version("1.1.0", "1.1.0") is False
|
||||
|
||||
|
||||
def test_is_newer_version_equal_with_v_prefix():
|
||||
assert c.is_newer_version("1.1.0", "v1.1.0") is False
|
||||
|
||||
|
||||
def test_is_newer_version_numeric_not_lexical():
|
||||
# A lexical/string compare would say "10.0.0" < "2.0.0" ("1" < "2").
|
||||
assert c.is_newer_version("2.0.0", "10.0.0") is True
|
||||
assert c.is_newer_version("10.0.0", "2.0.0") is False
|
||||
|
||||
|
||||
def test_is_newer_version_handles_differing_length():
|
||||
assert c.is_newer_version("1.2", "1.2.0") is False
|
||||
assert c.is_newer_version("1.2.0", "1.2") is False
|
||||
assert c.is_newer_version("1.2", "1.3") is True
|
||||
|
||||
|
||||
def test_is_newer_version_malformed_candidate_is_never_newer():
|
||||
assert c.is_newer_version("1.1.0", "not-a-version") is False
|
||||
assert c.is_newer_version("1.1.0", "") is False
|
||||
|
||||
|
||||
def test_is_newer_version_malformed_current_does_not_crash():
|
||||
# Shouldn't raise; a valid candidate is reported as newer than "unknown".
|
||||
assert c.is_newer_version("garbage", "1.0.0") is True
|
||||
assert c.is_newer_version("garbage", "not-a-version-either") is False
|
||||
|
||||
|
||||
class _FakeHTTPResponse:
|
||||
def __init__(self, data: bytes):
|
||||
self._data = data
|
||||
|
||||
def read(self):
|
||||
return self._data
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *exc):
|
||||
return False
|
||||
|
||||
|
||||
def test_fetch_latest_release_ok(monkeypatch):
|
||||
payload = json.dumps(
|
||||
{
|
||||
"tag_name": "v1.2.0",
|
||||
"html_url": "https://git.avezzano.io/the_og/better-claude-config/releases/tag/v1.2.0",
|
||||
}
|
||||
).encode("utf-8")
|
||||
monkeypatch.setattr(
|
||||
urllib.request, "urlopen", lambda req, timeout=None: _FakeHTTPResponse(payload)
|
||||
)
|
||||
result = c.fetch_latest_release()
|
||||
assert result == {
|
||||
"version": "v1.2.0",
|
||||
"url": "https://git.avezzano.io/the_og/better-claude-config/releases/tag/v1.2.0",
|
||||
}
|
||||
|
||||
|
||||
def test_fetch_latest_release_falls_back_to_releases_url_when_no_html_url(monkeypatch):
|
||||
payload = json.dumps({"tag_name": "v1.2.0"}).encode("utf-8")
|
||||
monkeypatch.setattr(
|
||||
urllib.request, "urlopen", lambda req, timeout=None: _FakeHTTPResponse(payload)
|
||||
)
|
||||
result = c.fetch_latest_release()
|
||||
assert result == {"version": "v1.2.0", "url": c.RELEASES_URL}
|
||||
|
||||
|
||||
def test_fetch_latest_release_network_failure_returns_none(monkeypatch):
|
||||
def boom(req, timeout=None):
|
||||
raise urllib.error.URLError("no network")
|
||||
|
||||
monkeypatch.setattr(urllib.request, "urlopen", boom)
|
||||
assert c.fetch_latest_release() is None
|
||||
|
||||
|
||||
def test_fetch_latest_release_timeout_returns_none(monkeypatch):
|
||||
def boom(req, timeout=None):
|
||||
raise TimeoutError("timed out")
|
||||
|
||||
monkeypatch.setattr(urllib.request, "urlopen", boom)
|
||||
assert c.fetch_latest_release() is None
|
||||
|
||||
|
||||
def test_fetch_latest_release_missing_tag_returns_none(monkeypatch):
|
||||
payload = json.dumps({"html_url": "https://example.com"}).encode("utf-8")
|
||||
monkeypatch.setattr(
|
||||
urllib.request, "urlopen", lambda req, timeout=None: _FakeHTTPResponse(payload)
|
||||
)
|
||||
assert c.fetch_latest_release() is None
|
||||
|
||||
|
||||
def test_fetch_latest_release_malformed_json_returns_none(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
urllib.request, "urlopen", lambda req, timeout=None: _FakeHTTPResponse(b"not json")
|
||||
)
|
||||
assert c.fetch_latest_release() is None
|
||||
|
||||
Reference in New Issue
Block a user