import textwrap import pytest from datetime import datetime, timezone, timedelta from openvpncertupdate import load_all_certs, load_expiring_certs, CertInfo, _parse_index_line def _fmt(dt: datetime) -> str: return dt.strftime("%y%m%d%H%M%S") + "Z" def make_pki(tmp_path, now): pki = tmp_path / "pki" pki.mkdir() content = textwrap.dedent(f"""\ V\t{_fmt(now + timedelta(days=10))}\t\t02\tunknown\t/CN=soon V\t{_fmt(now + timedelta(days=90))}\t\t03\tunknown\t/CN=later V\t{_fmt(now - timedelta(days=15))}\t\t04\tunknown\t/CN=past15 V\t{_fmt(now - timedelta(days=40))}\t\t05\tunknown\t/CN=past40 R\t{_fmt(now + timedelta(days=10))}\t230101Z,keyCompromise\t06\tunknown\t/CN=revoked """) (pki / "index.txt").write_text(content) return str(pki) def test_filters_within_window(tmp_path): now = datetime.now(tz=timezone.utc) pki = make_pki(tmp_path, now) certs = load_expiring_certs(pki, days_past=30, days_ahead=14) cns = {c.cn for c in certs} assert "soon" in cns # 10d ahead < 14d threshold assert "later" not in cns # 90d > 14d assert "past15" in cns # 15d past < 30d threshold assert "past40" not in cns # 40d past > 30d threshold assert "revoked" not in cns # R status skipped def test_sorted_ascending(tmp_path): now = datetime.now(tz=timezone.utc) pki = make_pki(tmp_path, now) certs = load_expiring_certs(pki, days_past=30, days_ahead=14) dates = [c.expires for c in certs] assert dates == sorted(dates) def test_days_left_negative_for_expired(tmp_path): now = datetime.now(tz=timezone.utc) pki = make_pki(tmp_path, now) certs = load_expiring_certs(pki, days_past=30, days_ahead=14) expired = next(c for c in certs if c.cn == "past15") assert expired.days_left < 0 def test_parse_4digit_year(): line = "V\t20251231120000Z\t\t07\tunknown\t/CN=future" result = _parse_index_line(line) assert result is not None cn, dt = result assert cn == "future" assert dt.year == 2025 def test_load_all_certs_includes_all_valid(tmp_path): now = datetime.now(tz=timezone.utc) pki = make_pki(tmp_path, now) certs = load_all_certs(pki) cns = {c.cn for c in certs} # All V-status entries included regardless of expiry window assert cns == {"soon", "later", "past15", "past40"} # Revoked entry excluded assert "revoked" not in cns def test_load_all_certs_sorted_ascending(tmp_path): now = datetime.now(tz=timezone.utc) pki = make_pki(tmp_path, now) certs = load_all_certs(pki) dates = [c.expires for c in certs] assert dates == sorted(dates)