"""DFD via pbauermeister/data-flow-diagram (Graphviz / Kroki).""" from __future__ import annotations from pathlib import Path from unittest.mock import patch import pytest from md2gost.diagram_renderer import ( DIAGRAM_LANGS, configure_diagrams, is_diagram_lang, prepare_source, render_diagram, ) from md2gost.dfd import ( SAMPLE_CONTEXT, SAMPLE_LEVEL0, DfdParseError, _compile_dot, write_dfd_outputs, ) from md2gost.extended_markdown import markdown from md2gost.extended_markdown.caption import Caption as CapEl from md2gost.renderable.caption import CaptionInfo from md2gost.renderable.diagram import DiagramFigure from md2gost.renderable_factory import RenderableFactory def test_is_diagram_lang_dfd(): assert is_diagram_lang("dfd") assert is_diagram_lang("uml-dfd") assert is_diagram_lang("data-flow-diagram") assert is_diagram_lang("yourdon") assert is_diagram_lang("uml-yourdon") assert "dfd" in DIAGRAM_LANGS def test_prepare_dfd_stays_native(): src, dtype = prepare_source("dfd", SAMPLE_CONTEXT) assert dtype == "dfd" assert "@startuml" not in src.lower() assert "process" in src assert "System" in src for lang in ("uml-dfd", "data-flow-diagram", "yourdon"): _, dtype2 = prepare_source(lang, SAMPLE_LEVEL0) assert dtype2 == "dfd" def test_compile_dot_empty_raises(): with pytest.raises(DfdParseError, match="пустой"): _compile_dot(" \n\n ") def test_compile_dot_sample(monkeypatch): """When the package is importable, real compile; else accept mock.""" try: dot, is_ctx = _compile_dot(SAMPLE_CONTEXT) except DfdParseError as exc: if "data-flow-diagram не найден" in str(exc) or "не найден" in str(exc): pytest.skip("data-flow-diagram not installed for this Python") raise assert "digraph" in dot.lower() or "graph" in dot.lower() assert is_ctx is True def test_strip_graphviz_tcl_libs(tmp_path): from md2gost.dfd import GRAPHVIZ_TCL_DLL_NAMES, strip_graphviz_tcl_libs bin_dir = tmp_path / "bin" bin_dir.mkdir() (bin_dir / "dot.exe").write_bytes(b"mz") (bin_dir / "tcl86t.dll").write_bytes(b"tcl") (bin_dir / "gdtclft.dll").write_bytes(b"gd") (bin_dir / "cgraph.dll").write_bytes(b"keep") tcl_mod = tmp_path / "lib" / "graphviz" / "tcl" tcl_mod.mkdir(parents=True) (tcl_mod / "pkgIndex.tcl").write_text("x", encoding="utf-8") removed = strip_graphviz_tcl_libs(tmp_path) assert removed >= 3 assert not (bin_dir / "tcl86t.dll").exists() assert not (bin_dir / "gdtclft.dll").exists() assert (bin_dir / "dot.exe").exists() assert (bin_dir / "cgraph.dll").exists() assert not tcl_mod.exists() for name in GRAPHVIZ_TCL_DLL_NAMES: assert not (bin_dir / name).exists() def test_write_dfd_outputs_uses_local_or_kroki(tmp_path): png = tmp_path / "a.png" svg = tmp_path / "a.svg" fake_dot = "digraph G { a -> b; }\n" def fake_local(dot, out_path, fmt, *, context): Path(out_path).write_bytes( b"\x89PNG\r\n\x1a\n" if fmt == "png" else b"" ) if fmt == "png": # minimal valid-enough PNG header for startswith checks Path(out_path).write_bytes( b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR" + b"\x00" * 20 ) else: Path(out_path).write_text( "Клиент", encoding="utf-8", ) return True with patch("md2gost.dfd._compile_dot", return_value=(fake_dot, False)), \ patch("md2gost.dfd._render_dot_local_combined", return_value=set()), \ patch("md2gost.dfd._render_dot_local", side_effect=fake_local), \ patch("md2gost.dfd._render_dot_kroki", return_value=False): assert write_dfd_outputs(SAMPLE_LEVEL0, png, svg, scale=1) assert png.read_bytes().startswith(b"\x89PNG") assert "Клиент" in svg.read_text(encoding="utf-8") def test_write_dfd_outputs_one_graphviz_run_for_both_formats(tmp_path): """PNG + SVG come from a single Graphviz process; no per-format retry.""" png = tmp_path / "a.png" svg = tmp_path / "a.svg" single = [] def fake_combined(dot, targets, *, context): for out_path, fmt in targets: Path(out_path).write_bytes( b"\x89PNG\r\n\x1a\n" + b"\x00" * 32 if fmt == "png" else b"" ) return {fmt for _, fmt in targets} with patch("md2gost.dfd._compile_dot", return_value=("digraph G {}", False)), \ patch("md2gost.dfd._render_dot_local_combined", side_effect=fake_combined), \ patch("md2gost.dfd._render_dot_local", side_effect=lambda *a, **k: single.append(a) or True), \ patch("md2gost.dfd._render_dot_kroki", return_value=False): assert write_dfd_outputs(SAMPLE_LEVEL0, png, svg, scale=1) assert single == [] assert png.read_bytes().startswith(b"\x89PNG") assert svg.read_bytes().startswith(b"") return True with patch("md2gost.dfd._compile_dot", return_value=("digraph G {}", False)), \ patch("md2gost.dfd._render_dot_local_combined", side_effect=fake_combined), \ patch("md2gost.dfd._render_dot_local", side_effect=fake_local), \ patch("md2gost.dfd._render_dot_kroki", return_value=False): assert write_dfd_outputs(SAMPLE_LEVEL0, png, svg, scale=1) assert retried == ["svg"] assert svg.read_bytes().startswith(b"