Files
openvpncertupdate/tests/test_app_reissue.py
2026-08-15 05:37:57 +03:00

112 lines
4.6 KiB
Python

"""Tests for the TUI re-issue workflow (CursesApp._process_cert).
Uses the same mock-curses approach as test_dialogs.py — no real terminal
needed. _msg()/_error() swallow curses.error, so a MagicMock stdscr is enough
as long as getmaxyx() returns real ints.
"""
from unittest.mock import MagicMock, patch
import curses as _curses
# Stub curses constants/callables before importing the module under test.
_curses.color_pair = lambda x: 0
_curses.A_BOLD = 0
_curses.A_UNDERLINE = 0
_curses.curs_set = lambda x: None
from openvpncertupdate import CursesApp, CertFormResult
def _stdscr():
s = MagicMock()
s.getmaxyx.return_value = (24, 80) # `sh - 2` needs a real int
s.getch.return_value = ord("q")
return s
def _patch_workflow(monkeypatch, cert_file_present):
"""Patch everything _process_cert touches after the form is confirmed."""
monkeypatch.setattr(
"openvpncertupdate.show_cert_form",
MagicMock(return_value=CertFormResult(
cn="y.kuts", email="", password="Testpass1234567890abcdefgh",
confirmed=True)))
monkeypatch.setattr("openvpncertupdate.has_issued_cert",
MagicMock(return_value=cert_file_present))
for name, retval in (
("revoke_issued", None),
("gen_crl", None),
("copy_crl", None),
("build_client_full", None),
("build_ovpn", "/out/y.kuts_2026-08-15_01/client.ovpn"),
("create_note", "https://cg.example.com/note/abc#deadbeef"),
):
monkeypatch.setattr(f"openvpncertupdate.{name}",
MagicMock(return_value=retval))
import openvpncertupdate as m
return {n: getattr(m, n) for n in (
"revoke_issued", "gen_crl", "copy_crl", "build_client_full")}
def test_tui_reissue_skips_revoke_when_cert_file_missing(monkeypatch):
# The bug as reported: the TUI offered renewal for a CN whose .crt was
# never carried over from the older EasyRSA install, and revoke-issued
# failed. There is nothing to revoke — build the replacement instead.
mocks = _patch_workflow(monkeypatch, cert_file_present=False)
app = CursesApp()
assert app._process_cert(_stdscr(), "y.kuts", "", is_renewal=True) is True
mocks["revoke_issued"].assert_not_called()
mocks["gen_crl"].assert_not_called()
mocks["copy_crl"].assert_not_called()
mocks["build_client_full"].assert_called_once()
assert mocks["build_client_full"].call_args.args[2] == "y.kuts"
def test_tui_reissue_logs_the_skip(monkeypatch):
_patch_workflow(monkeypatch, cert_file_present=False)
app = CursesApp()
app._process_cert(_stdscr(), "y.kuts", "", is_renewal=True)
assert any("revoke skipped" in e for e in app._session_log)
def test_tui_reissue_revokes_when_cert_file_present(monkeypatch):
mocks = _patch_workflow(monkeypatch, cert_file_present=True)
app = CursesApp()
assert app._process_cert(_stdscr(), "y.kuts", "", is_renewal=True) is True
mocks["revoke_issued"].assert_called_once()
mocks["gen_crl"].assert_called_once()
mocks["copy_crl"].assert_called_once()
mocks["build_client_full"].assert_called_once()
def test_tui_skipped_revoke_build_failure_does_not_claim_revocation(monkeypatch):
# Nothing was revoked, so the "has been revoked" warning would be a lie.
import openvpncertupdate as m
_patch_workflow(monkeypatch, cert_file_present=False)
monkeypatch.setattr("openvpncertupdate.build_client_full",
MagicMock(side_effect=m.EasyRSAError("boom")))
app = CursesApp()
shown = []
monkeypatch.setattr(CursesApp, "_error",
lambda self, stdscr, text: shown.append(text))
app._process_cert(_stdscr(), "y.kuts", "", is_renewal=True)
assert shown and "has been revoked" not in shown[0]
def test_tui_seeds_days_field_from_cert_days(monkeypatch):
import openvpncertupdate as m
_patch_workflow(monkeypatch, cert_file_present=True)
monkeypatch.setattr("openvpncertupdate.CERT_DAYS", "90")
CursesApp()._process_cert(_stdscr(), "y.kuts", "", is_renewal=True)
assert m.show_cert_form.call_args.kwargs["days"] == "90"
def test_tui_forwards_form_days_to_build_client_full(monkeypatch):
mocks = _patch_workflow(monkeypatch, cert_file_present=True)
monkeypatch.setattr(
"openvpncertupdate.show_cert_form",
MagicMock(return_value=CertFormResult(
cn="y.kuts", email="", password="Testpass1234567890abcdefgh",
days="30", confirmed=True)))
CursesApp()._process_cert(_stdscr(), "y.kuts", "", is_renewal=True)
assert mocks["build_client_full"].call_args.kwargs["days"] == "30"