mkdocs2confluence 0.9.5__tar.gz → 0.9.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/PKG-INFO +1 -1
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/pyproject.toml +1 -1
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs2confluence.egg-info/PKG-INFO +1 -1
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/transforms/mermaid.py +14 -11
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_mermaid.py +87 -5
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/LICENSE +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/README.md +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/setup.cfg +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs2confluence.egg-info/SOURCES.txt +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs2confluence.egg-info/dependency_links.txt +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs2confluence.egg-info/entry_points.txt +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs2confluence.egg-info/requires.txt +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs2confluence.egg-info/top_level.txt +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/__init__.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/cli.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/emitter/__init__.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/emitter/xhtml.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/ir/__init__.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/ir/document.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/ir/nodes.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/ir/treeutil.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/loader/__init__.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/loader/config.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/loader/extra_css.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/loader/nav.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/loader/page.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/parser/__init__.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/parser/markdown.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/pdf/__init__.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/pdf/generator.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/pdf/render.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/preprocess/__init__.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/preprocess/abbrevs.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/preprocess/fence.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/preprocess/frontmatter.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/preprocess/icons.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/preprocess/includes.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/preprocess/linkdefs.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/preview/__init__.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/preview/render.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/preview/server.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/publisher/__init__.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/publisher/client.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/publisher/pipeline.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/sync/__init__.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/sync/anchoring.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/sync/command.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/sync/comments.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/sync/github.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/sync/platform.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/sync/state.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/transforms/__init__.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/transforms/abbrevs.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/transforms/assets.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/transforms/editlink.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/transforms/footer.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/transforms/images.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/transforms/internallinks.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_abbrevs.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_children_macro.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_cli.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_editlink.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_emitter.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_extra_css.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_footer.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_frontmatter.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_icons.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_images.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_internallinks.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_ir.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_linkdefs.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_loader.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_page_loader.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_parser.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_pdf.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_preprocess.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_preview.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_publish_client.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_publish_config.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_publish_pipeline.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_server.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_sync_anchoring.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_sync_command.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_sync_comments.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_sync_github.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_sync_state.py +0 -0
- {mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/tests/test_treeutil.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mkdocs2confluence"
|
|
3
|
-
version = "0.9.
|
|
3
|
+
version = "0.9.7"
|
|
4
4
|
description = "Publish MkDocs Material pages to Confluence Cloud — admonitions, Mermaid diagrams, tabs, page properties and more"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "GPL-3.0-or-later" }
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/transforms/mermaid.py
RENAMED
|
@@ -99,9 +99,10 @@ def _render_one(source: str, kroki_url: str, *, quiet: bool = False) -> Path | N
|
|
|
99
99
|
Transient HTTP errors (429, 5xx) and network blips are retried up to
|
|
100
100
|
``_RETRY_ATTEMPTS`` times with exponential backoff.
|
|
101
101
|
|
|
102
|
-
When using the public kroki.io and all retries are exhausted due to
|
|
103
|
-
one final attempt is made via
|
|
104
|
-
Kroki instances never fall back
|
|
102
|
+
When using the public kroki.io and all retries are exhausted due to kroki
|
|
103
|
+
being unreachable (504 or timeout), one final attempt is made via
|
|
104
|
+
mermaid.ink before giving up. Self-hosted Kroki instances never fall back
|
|
105
|
+
to mermaid.ink.
|
|
105
106
|
"""
|
|
106
107
|
path = _cache_path(source)
|
|
107
108
|
if path.exists():
|
|
@@ -111,7 +112,7 @@ def _render_one(source: str, kroki_url: str, *, quiet: bool = False) -> Path | N
|
|
|
111
112
|
|
|
112
113
|
use_public_kroki = kroki_url.rstrip("/") == DEFAULT_KROKI_URL.rstrip("/")
|
|
113
114
|
last_exc: Exception | None = None
|
|
114
|
-
|
|
115
|
+
kroki_unavailable = False # True when kroki is unreachable (504 or timeout)
|
|
115
116
|
|
|
116
117
|
for attempt in range(_RETRY_ATTEMPTS):
|
|
117
118
|
if attempt > 0:
|
|
@@ -130,22 +131,24 @@ def _render_one(source: str, kroki_url: str, *, quiet: bool = False) -> Path | N
|
|
|
130
131
|
except urllib.error.HTTPError as exc:
|
|
131
132
|
if exc.code in _RETRYABLE_HTTP:
|
|
132
133
|
last_exc = exc
|
|
133
|
-
|
|
134
|
+
kroki_unavailable = exc.code == 504
|
|
134
135
|
continue # retry
|
|
135
136
|
_warn(f"mermaid diagram: Kroki returned HTTP {exc.code} {exc.reason} — falling back to code block")
|
|
136
137
|
return None
|
|
137
|
-
except urllib.error.URLError as exc:
|
|
138
|
+
except (urllib.error.URLError, OSError) as exc:
|
|
139
|
+
# URLError covers connection errors; OSError covers read-phase timeouts
|
|
140
|
+
# (socket.timeout is OSError but not URLError when raised by resp.read()).
|
|
138
141
|
last_exc = exc
|
|
139
|
-
|
|
142
|
+
kroki_unavailable = True
|
|
140
143
|
continue # retry — network blip
|
|
141
|
-
except
|
|
144
|
+
except ValueError as exc:
|
|
142
145
|
_warn(f"mermaid diagram: {exc} — falling back to code block")
|
|
143
146
|
return None
|
|
144
147
|
|
|
145
|
-
# All Kroki retries exhausted — try mermaid.ink if on public kroki.io and
|
|
146
|
-
if use_public_kroki and
|
|
148
|
+
# All Kroki retries exhausted — try mermaid.ink if on public kroki.io and kroki was unreachable.
|
|
149
|
+
if use_public_kroki and kroki_unavailable:
|
|
147
150
|
try:
|
|
148
|
-
_warn("mermaid diagram: kroki.io
|
|
151
|
+
_warn("mermaid diagram: kroki.io unreachable, trying mermaid.ink as fallback")
|
|
149
152
|
png = _mermaid_ink_png(source)
|
|
150
153
|
if len(png) < _MIN_PNG_BYTES:
|
|
151
154
|
raise ValueError(f"mermaid.ink returned {len(png)} bytes (expected a valid PNG)")
|
|
@@ -102,7 +102,7 @@ def test_render_deduplicates_identical_diagrams(tmp_path):
|
|
|
102
102
|
|
|
103
103
|
|
|
104
104
|
def test_render_fallback_on_network_error(tmp_path, capsys):
|
|
105
|
-
"""When Kroki is unreachable, node
|
|
105
|
+
"""When Kroki is unreachable and mermaid.ink also fails, node falls back to code block."""
|
|
106
106
|
import urllib.error
|
|
107
107
|
|
|
108
108
|
node = MermaidDiagram(source=_SAMPLE_SOURCE)
|
|
@@ -114,6 +114,10 @@ def test_render_fallback_on_network_error(tmp_path, capsys):
|
|
|
114
114
|
"mkdocs_to_confluence.transforms.mermaid._kroki_png",
|
|
115
115
|
side_effect=urllib.error.URLError("timed out"),
|
|
116
116
|
),
|
|
117
|
+
patch(
|
|
118
|
+
"mkdocs_to_confluence.transforms.mermaid._mermaid_ink_png",
|
|
119
|
+
side_effect=urllib.error.URLError("timed out"),
|
|
120
|
+
),
|
|
117
121
|
):
|
|
118
122
|
updated_nodes, attachments = render_mermaid_diagrams((node,))
|
|
119
123
|
|
|
@@ -218,7 +222,7 @@ def test_render_multiple_diagrams_concurrently(tmp_path):
|
|
|
218
222
|
|
|
219
223
|
|
|
220
224
|
def test_render_one_failure_does_not_block_others(tmp_path):
|
|
221
|
-
"""If one diagram fails, the rest still render successfully."""
|
|
225
|
+
"""If one diagram fails (both kroki and mermaid.ink), the rest still render successfully."""
|
|
222
226
|
import urllib.error
|
|
223
227
|
|
|
224
228
|
good_source = "graph TD\n A --> B\n"
|
|
@@ -231,7 +235,9 @@ def test_render_one_failure_does_not_block_others(tmp_path):
|
|
|
231
235
|
return _FAKE_PNG
|
|
232
236
|
|
|
233
237
|
with patch("mkdocs_to_confluence.transforms.mermaid._CACHE_DIR", tmp_path), \
|
|
234
|
-
patch("mkdocs_to_confluence.transforms.mermaid._kroki_png", side_effect=fake_kroki)
|
|
238
|
+
patch("mkdocs_to_confluence.transforms.mermaid._kroki_png", side_effect=fake_kroki), \
|
|
239
|
+
patch("mkdocs_to_confluence.transforms.mermaid._mermaid_ink_png",
|
|
240
|
+
side_effect=urllib.error.URLError("timeout")):
|
|
235
241
|
updated, attachments = render_mermaid_diagrams(nodes)
|
|
236
242
|
|
|
237
243
|
# One attachment for the successful diagram
|
|
@@ -258,7 +264,7 @@ def test_render_one_cached(tmp_path):
|
|
|
258
264
|
|
|
259
265
|
|
|
260
266
|
def test_render_one_network_failure_returns_none(tmp_path):
|
|
261
|
-
"""_render_one returns None after all retries
|
|
267
|
+
"""_render_one returns None after all retries when both kroki and mermaid.ink fail."""
|
|
262
268
|
import urllib.error
|
|
263
269
|
|
|
264
270
|
from mkdocs_to_confluence.transforms.mermaid import _render_one
|
|
@@ -266,6 +272,8 @@ def test_render_one_network_failure_returns_none(tmp_path):
|
|
|
266
272
|
with patch("mkdocs_to_confluence.transforms.mermaid._CACHE_DIR", tmp_path), \
|
|
267
273
|
patch("mkdocs_to_confluence.transforms.mermaid.time.sleep"), \
|
|
268
274
|
patch("mkdocs_to_confluence.transforms.mermaid._kroki_png",
|
|
275
|
+
side_effect=urllib.error.URLError("connection refused")), \
|
|
276
|
+
patch("mkdocs_to_confluence.transforms.mermaid._mermaid_ink_png",
|
|
269
277
|
side_effect=urllib.error.URLError("connection refused")):
|
|
270
278
|
result = _render_one(_SAMPLE_SOURCE, "https://kroki.io")
|
|
271
279
|
|
|
@@ -371,7 +379,7 @@ def test_render_mermaid_cached_quiet_suppresses_stdout(tmp_path, capsys):
|
|
|
371
379
|
|
|
372
380
|
|
|
373
381
|
def test_render_one_504_on_public_kroki_falls_back_to_mermaid_ink(tmp_path, capsys):
|
|
374
|
-
"""Public kroki.io 504 → automatic fallback to mermaid.ink
|
|
382
|
+
"""Public kroki.io 504 → automatic fallback to mermaid.ink."""
|
|
375
383
|
import urllib.error
|
|
376
384
|
|
|
377
385
|
from mkdocs_to_confluence.transforms.mermaid import _render_one
|
|
@@ -389,6 +397,44 @@ def test_render_one_504_on_public_kroki_falls_back_to_mermaid_ink(tmp_path, caps
|
|
|
389
397
|
mock_ink.assert_called_once_with(_SAMPLE_SOURCE)
|
|
390
398
|
|
|
391
399
|
|
|
400
|
+
def test_render_one_timeout_on_public_kroki_falls_back_to_mermaid_ink(tmp_path):
|
|
401
|
+
"""Public kroki.io URLError timeout → automatic fallback to mermaid.ink."""
|
|
402
|
+
import urllib.error
|
|
403
|
+
|
|
404
|
+
from mkdocs_to_confluence.transforms.mermaid import _render_one
|
|
405
|
+
|
|
406
|
+
with patch("mkdocs_to_confluence.transforms.mermaid._CACHE_DIR", tmp_path), \
|
|
407
|
+
patch("mkdocs_to_confluence.transforms.mermaid.time.sleep"), \
|
|
408
|
+
patch("mkdocs_to_confluence.transforms.mermaid._kroki_png",
|
|
409
|
+
side_effect=urllib.error.URLError("timed out")), \
|
|
410
|
+
patch("mkdocs_to_confluence.transforms.mermaid._mermaid_ink_png",
|
|
411
|
+
return_value=_FAKE_PNG) as mock_ink:
|
|
412
|
+
result = _render_one(_SAMPLE_SOURCE, "https://kroki.io")
|
|
413
|
+
|
|
414
|
+
assert result is not None
|
|
415
|
+
assert result.exists()
|
|
416
|
+
mock_ink.assert_called_once_with(_SAMPLE_SOURCE)
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
def test_render_one_read_phase_timeout_falls_back_to_mermaid_ink(tmp_path):
|
|
420
|
+
"""socket.timeout during resp.read() (OSError, not URLError) → fallback to mermaid.ink."""
|
|
421
|
+
import socket
|
|
422
|
+
|
|
423
|
+
from mkdocs_to_confluence.transforms.mermaid import _render_one
|
|
424
|
+
|
|
425
|
+
with patch("mkdocs_to_confluence.transforms.mermaid._CACHE_DIR", tmp_path), \
|
|
426
|
+
patch("mkdocs_to_confluence.transforms.mermaid.time.sleep"), \
|
|
427
|
+
patch("mkdocs_to_confluence.transforms.mermaid._kroki_png",
|
|
428
|
+
side_effect=socket.timeout("The read operation timed out")), \
|
|
429
|
+
patch("mkdocs_to_confluence.transforms.mermaid._mermaid_ink_png",
|
|
430
|
+
return_value=_FAKE_PNG) as mock_ink:
|
|
431
|
+
result = _render_one(_SAMPLE_SOURCE, "https://kroki.io")
|
|
432
|
+
|
|
433
|
+
assert result is not None
|
|
434
|
+
assert result.exists()
|
|
435
|
+
mock_ink.assert_called_once_with(_SAMPLE_SOURCE)
|
|
436
|
+
|
|
437
|
+
|
|
392
438
|
def test_render_one_504_on_self_hosted_kroki_does_not_fall_back(tmp_path):
|
|
393
439
|
"""Self-hosted Kroki 504 → no mermaid.ink fallback (privacy isolation)."""
|
|
394
440
|
import urllib.error
|
|
@@ -407,6 +453,42 @@ def test_render_one_504_on_self_hosted_kroki_does_not_fall_back(tmp_path):
|
|
|
407
453
|
mock_ink.assert_not_called()
|
|
408
454
|
|
|
409
455
|
|
|
456
|
+
def test_render_one_timeout_on_self_hosted_kroki_does_not_fall_back(tmp_path):
|
|
457
|
+
"""Self-hosted Kroki URLError timeout → no mermaid.ink fallback (privacy isolation)."""
|
|
458
|
+
import urllib.error
|
|
459
|
+
|
|
460
|
+
from mkdocs_to_confluence.transforms.mermaid import _render_one
|
|
461
|
+
|
|
462
|
+
with patch("mkdocs_to_confluence.transforms.mermaid._CACHE_DIR", tmp_path), \
|
|
463
|
+
patch("mkdocs_to_confluence.transforms.mermaid.time.sleep"), \
|
|
464
|
+
patch("mkdocs_to_confluence.transforms.mermaid._kroki_png",
|
|
465
|
+
side_effect=urllib.error.URLError("timed out")), \
|
|
466
|
+
patch("mkdocs_to_confluence.transforms.mermaid._mermaid_ink_png",
|
|
467
|
+
return_value=_FAKE_PNG) as mock_ink:
|
|
468
|
+
result = _render_one(_SAMPLE_SOURCE, "https://my-internal-kroki.corp")
|
|
469
|
+
|
|
470
|
+
assert result is None
|
|
471
|
+
mock_ink.assert_not_called()
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
def test_render_one_read_phase_timeout_self_hosted_does_not_fall_back(tmp_path):
|
|
475
|
+
"""socket.timeout on self-hosted Kroki → no mermaid.ink fallback (privacy isolation)."""
|
|
476
|
+
import socket
|
|
477
|
+
|
|
478
|
+
from mkdocs_to_confluence.transforms.mermaid import _render_one
|
|
479
|
+
|
|
480
|
+
with patch("mkdocs_to_confluence.transforms.mermaid._CACHE_DIR", tmp_path), \
|
|
481
|
+
patch("mkdocs_to_confluence.transforms.mermaid.time.sleep"), \
|
|
482
|
+
patch("mkdocs_to_confluence.transforms.mermaid._kroki_png",
|
|
483
|
+
side_effect=socket.timeout("The read operation timed out")), \
|
|
484
|
+
patch("mkdocs_to_confluence.transforms.mermaid._mermaid_ink_png",
|
|
485
|
+
return_value=_FAKE_PNG) as mock_ink:
|
|
486
|
+
result = _render_one(_SAMPLE_SOURCE, "https://my-internal-kroki.corp")
|
|
487
|
+
|
|
488
|
+
assert result is None
|
|
489
|
+
mock_ink.assert_not_called()
|
|
490
|
+
|
|
491
|
+
|
|
410
492
|
def test_render_one_mermaid_ink_fallback_also_fails_returns_none(tmp_path, capsys):
|
|
411
493
|
"""When mermaid.ink fallback also fails, returns None (code-block fallback)."""
|
|
412
494
|
import urllib.error
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs2confluence.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs2confluence.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs2confluence.egg-info/requires.txt
RENAMED
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs2confluence.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/emitter/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/emitter/xhtml.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/loader/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/loader/config.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/loader/extra_css.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/parser/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/parser/markdown.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/pdf/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/pdf/generator.py
RENAMED
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/preprocess/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/preprocess/abbrevs.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/preprocess/fence.py
RENAMED
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/preprocess/icons.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/preprocess/includes.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/preprocess/linkdefs.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/preview/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/preview/render.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/preview/server.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/publisher/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/publisher/client.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/publisher/pipeline.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/sync/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/sync/anchoring.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/sync/command.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/sync/comments.py
RENAMED
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/sync/platform.py
RENAMED
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/transforms/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/transforms/abbrevs.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/transforms/assets.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/transforms/editlink.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/transforms/footer.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.9.5 → mkdocs2confluence-0.9.7}/src/mkdocs_to_confluence/transforms/images.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|