"""CLI/GUI shared pipeline helpers.""" import os import sys from md2gost.dnd import first_markdown, normalize_drop_paths, parse_tkdnd_files from md2gost.pipeline import ( ConvertRequest, convert, default_output_path, should_launch_gui, timestamped_output_path, ) from md2gost.__main__ import build_parser, request_from_args def test_should_launch_gui(): assert should_launch_gui(None, False) is True assert should_launch_gui("", False) is True assert should_launch_gui("a.md", True) is True assert should_launch_gui("a.md", False) is False def test_default_output_path(): path = default_output_path(r"C:\docs\report.md") assert path.endswith("report.docx") def test_timestamped_output_path(tmp_path): import re src = tmp_path / "ПР1.docx" src.write_bytes(b"x") out = timestamped_output_path(str(src)) name = os.path.basename(out) assert re.fullmatch(r"ПР1_\d{4}-\d{2}-\d{2}-\d{2}-\d{2}\.docx", name) assert not os.path.exists(out) # collision → seconds (or seconds_2) open(out, "wb").close() out2 = timestamped_output_path(str(src)) assert out2 != out assert os.path.basename(out2).startswith("ПР1_") assert out2.endswith(".docx") def test_parse_tkdnd_files(): raw = r"{C:\My Files\a.md} C:\tmp\b.md" assert parse_tkdnd_files(raw) == [r"C:\My Files\a.md", r"C:\tmp\b.md"] def test_normalize_and_first_markdown(tmp_path): md = tmp_path / "note.md" png = tmp_path / "pic.png" md.write_text("# hi\n", encoding="utf-8") png.write_text("x", encoding="utf-8") paths = normalize_drop_paths([str(png), str(md)]) assert first_markdown(paths) == os.path.normpath(str(md)) def test_convert_rejects_non_md(tmp_path): txt = tmp_path / "file.txt" txt.write_text("nope", encoding="utf-8") result = convert(ConvertRequest(filename=str(txt))) assert result.ok is False assert result.exit_code == 1 def test_convert_missing_file(): result = convert(ConvertRequest(filename="definitely-missing.md")) assert result.ok is False assert result.exit_code == 2 def test_parser_gui_and_optional_file(): parser = build_parser() args = parser.parse_args(["--gui", "report.md", "--type", "PIS_custom"]) assert args.gui is True assert args.filename == "report.md" req = request_from_args(args) assert req.doc_type == "PIS_custom" assert req.filename == "report.md" def test_win32_drop_api_pointer_width(): if sys.platform != "win32": return import ctypes from md2gost.dnd import _win32_drop_api api = _win32_drop_api() assert api.CallWindowProc.argtypes[0] is ctypes.c_void_p huge = (1 << 40) | 0x1234 api.CallWindowProc.argtypes[0](huge) # must not OverflowError def test_win32_drop_hook_survives_window_messages(): if sys.platform != "win32": return import tkinter as tk from md2gost.dnd import enable_file_drop root = tk.Tk() try: root.geometry("240x160+40+40") root.update() kind = enable_file_drop(root, lambda _paths: None) root.update() root.event_generate("", x=12, y=12) root.update() assert kind in {"win32", "tkdnd", "none"} finally: root.destroy() def test_argv_needs_console(): from md2gost.__main__ import _argv_needs_console assert _argv_needs_console(["md2gost"]) is False assert _argv_needs_console(["md2gost", "--gui"]) is False assert _argv_needs_console(["md2gost", "--help"]) is True assert _argv_needs_console(["md2gost", "report.md"]) is True assert _argv_needs_console(["md2gost", "--gui", "report.md"]) is False def test_gui_module_imports(): from md2gost import gui assert hasattr(gui, "run_gui") assert hasattr(gui, "main") def test_hr_pagebreak_becomes_page_break(): import os import docx from md2gost import package_dir from md2gost.parser_ import Parser from md2gost.renderable.page_break import PageBreak doc = docx.Document(os.path.join(package_dir(), "Template.docx")) doc._body.clear_content() text = "До разрыва.\n\n---\n\nПосле разрыва.\n" items = list(Parser(doc, text, hr_pagebreak=True).parse()) assert any(isinstance(x, PageBreak) for x in items) doc2 = docx.Document(os.path.join(package_dir(), "Template.docx")) doc2._body.clear_content() items_off = list(Parser(doc2, text, hr_pagebreak=False).parse()) assert not any(isinstance(x, PageBreak) for x in items_off) texts = [] for item in items_off: para = getattr(item, "_docx_paragraph", None) if para is not None: texts.append(para.text) assert not any("ThematicBreak" in t for t in texts) def test_prompt_catalog_loads(): from md2gost.help_content import load_prompt_catalog catalog = load_prompt_catalog() names = {name for name, _title, text in catalog} assert "generate-md.md" in names assert "generate-mirea-report.md" in names assert "generate-pis-custom-report.md" in names assert all(text.strip() for _n, _t, text in catalog) def test_compose_prompt_with_schemes(): from md2gost.diagram_schemes import DiagramScheme from md2gost.help_content import compose_prompt, scheme_prompt_block scheme = DiagramScheme( id="c4", title="C4 Container", docs='Person(alias, "Label")', ai_prompt="Рисуй C4. Блок ```uml-c4 без @startuml.", ) block = scheme_prompt_block(scheme) assert "uml-c4" in block assert "```c4" in block or "```uml-c4" in block assert "Рисуй C4" in block assert "Person(alias" in block base = "Базовый промпт про markdown.\n" out = compose_prompt(base, [scheme]) assert out.startswith("Базовый промпт") assert "Дополнение: выбранные схемы" in out assert "uml-c4" in out assert compose_prompt(base, []).strip() == base.strip() assert compose_prompt(base, None).strip() == base.strip() def test_docs_catalog_loads(): from md2gost.help_content import load_docs_catalog catalog = load_docs_catalog() assert catalog, "docs/ catalog should not be empty" names = {name for name, _title, _text in catalog} assert "README.md" in names assert "markdown.md" in names assert catalog[0][0].lower() == "readme.md" assert "Документация" in catalog[0][1] assert all(text.strip() for _n, _t, text in catalog) def test_listing_continuation_mode(): import os import docx from md2gost import package_dir from md2gost.profiles import DEFAULT_LISTING_CONTINUATION from md2gost.renderable.caption import CaptionInfo from md2gost.renderable.listing import Listing doc = docx.Document(os.path.join(package_dir(), "Template.docx")) doc._body.clear_content() listing = Listing(doc._body, "python", CaptionInfo("c1", "Код")) listing.set_text("print(1)\n") assert listing._continuation_mode == DEFAULT_LISTING_CONTINUATION listing.set_continuation_mode("caption") assert listing._continuation_mode == "caption" try: listing.set_continuation_mode("nope") raise AssertionError("expected ValueError") except ValueError: pass def test_parser_listing_continuation_flag(): parser = build_parser() args = parser.parse_args(["report.md", "--listing-continuation", "legacy"]) req = request_from_args(args) assert req.listing_continuation == "legacy" args2 = parser.parse_args(["report.md"]) assert request_from_args(args2).listing_continuation == "word" def test_parser_hr_pagebreak_flag(): parser = build_parser() args = parser.parse_args(["report.md", "--no-hr-pagebreak"]) req = request_from_args(args) assert req.hr_pagebreak is False args2 = parser.parse_args(["report.md"]) assert request_from_args(args2).hr_pagebreak is False assert request_from_args(args2).doc_type == "practice" args3 = parser.parse_args(["report.md", "--hr-pagebreak"]) assert request_from_args(args3).hr_pagebreak is True def test_parser_no_file_is_ok(): parser = build_parser() args = parser.parse_args([]) assert args.filename is None assert args.gui is False