From 4f0b042f0956bc89569a4b306ac6df4f40b977a2 Mon Sep 17 00:00:00 2001 From: Zaine Date: Thu, 7 May 2026 15:24:00 +0100 Subject: [PATCH] adding tests --- Makefile | 50 ++++-- authoring_server.py | 268 ++++++++++++++++++++++++++++++--- gitea-build-monitor.ps1 | 155 +++++++++++++++++++ lima/index.md | 2 +- posts/posts-list.org | 2 +- sitemap.org | 2 +- tests/test_authoring_server.py | 208 +++++++++++++++++++++++++ 7 files changed, 649 insertions(+), 38 deletions(-) create mode 100644 tests/test_authoring_server.py diff --git a/Makefile b/Makefile index e3e4894..33a9785 100755 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: all clean norm author author-stop author-restart help +.PHONY: all clean norm author author-stop author-restart author-status help VENV := .venv PY := $(VENV)/bin/python @@ -6,8 +6,10 @@ PIP := $(VENV)/bin/pip PERM_DIR := /home/zaine/master-folder/projects/scripts/bash-scripts AUTHOR_PORT := 8765 +AUTHOR_URL := http://127.0.0.1:$(AUTHOR_PORT) AUTHOR_PID := /home/zaine/logs/authoring-server.pid AUTHOR_LOG := /home/zaine/logs/authoring-server.log +AUTHOR_PAGES_CHECK := /tmp/authoring-server-pages.json all: set-perms clean-output build search clean-venv author-restart @@ -40,9 +42,9 @@ norm: @echo "sorting out the backups..." find . -path ./backups -prune -o -type f -name '*~' -exec mv {} backups/ \; -author: +author: $(VENV) @echo "Starting local authoring UI in foreground..." - python3 authoring_server.py + $(PY) authoring_server.py author-stop: @echo "Stopping existing authoring UI if running..." @@ -50,22 +52,37 @@ author-stop: @if [ -f "$(AUTHOR_PID)" ]; then \ PID=$$(cat "$(AUTHOR_PID)"); \ if kill -0 $$PID 2>/dev/null; then \ - echo "Killing PID $$PID"; \ - kill $$PID || true; \ - sleep 1; \ + echo "Stopping PID $$PID"; \ + kill $$PID 2>/dev/null || true; \ + for i in 1 2 3 4 5; do \ + if ! kill -0 $$PID 2>/dev/null; then break; fi; \ + sleep 1; \ + done; \ + if kill -0 $$PID 2>/dev/null; then \ + echo "PID $$PID did not stop cleanly; killing it."; \ + kill -9 $$PID 2>/dev/null || true; \ + fi; \ fi; \ rm -f "$(AUTHOR_PID)"; \ fi - @fuser -k $(AUTHOR_PORT)/tcp 2>/dev/null || true + @fuser -k $(AUTHOR_PORT)/tcp >/dev/null 2>&1 || true -author-restart: author-stop +author-restart: $(VENV) author-stop @echo "Starting authoring UI on port $(AUTHOR_PORT)..." @mkdir -p /home/zaine/logs - @nohup python3 authoring_server.py > "$(AUTHOR_LOG)" 2>&1 & echo $$! > "$(AUTHOR_PID)" - @echo "Waiting for authoring UI to respond..." + @start-stop-daemon --start --background --make-pidfile --pidfile "$(AUTHOR_PID)" \ + --chdir "$(CURDIR)" --startas "$(CURDIR)/$(PY)" -- \ + authoring_server.py > "$(AUTHOR_LOG)" 2>&1 + @echo "Waiting for authoring UI and page list to respond..." @for i in 1 2 3 4 5 6 7 8 9 10; do \ - if curl -fsS http://127.0.0.1:$(AUTHOR_PORT)/ >/dev/null; then \ - echo "Authoring UI is running on http://127.0.0.1:$(AUTHOR_PORT)/"; \ + PID=$$(cat "$(AUTHOR_PID)"); \ + if ! kill -0 $$PID 2>/dev/null; then \ + echo "Authoring UI process exited before becoming healthy. Last logs:"; \ + tail -n 40 "$(AUTHOR_LOG)" || true; \ + exit 1; \ + fi; \ + if curl -fsS "$(AUTHOR_URL)/api/pages" -o "$(AUTHOR_PAGES_CHECK)" && $(PY) -c 'import json,sys; data=json.load(sys.stdin); sys.exit(0 if isinstance(data, list) and data else 1)' < "$(AUTHOR_PAGES_CHECK)"; then \ + echo "Authoring UI is running on $(AUTHOR_URL)/"; \ exit 0; \ fi; \ sleep 1; \ @@ -74,6 +91,14 @@ author-restart: author-stop tail -n 40 "$(AUTHOR_LOG)" || true; \ exit 1 +author-status: + @if [ -f "$(AUTHOR_PID)" ] && kill -0 $$(cat "$(AUTHOR_PID)") 2>/dev/null; then \ + echo "PID: $$(cat "$(AUTHOR_PID)")"; \ + else \ + echo "No live authoring UI PID recorded."; \ + fi + @curl -fsS "$(AUTHOR_URL)/api/pages" -o "$(AUTHOR_PAGES_CHECK)" && $(PY) -c 'import json,sys; print(f"Pages: {len(json.load(sys.stdin))}")' < "$(AUTHOR_PAGES_CHECK)" || true + help: @echo "Available targets:" @echo " make - Full rebuild and restart authoring UI" @@ -85,5 +110,6 @@ help: @echo " make author - Start the local authoring UI in foreground" @echo " make author-stop - Stop the authoring UI" @echo " make author-restart - Restart the authoring UI in background" + @echo " make author-status - Show authoring UI PID and page count" @echo " make search - Create the search index" @echo " make help - Show this help message" diff --git a/authoring_server.py b/authoring_server.py index 752b3af..8ce12ed 100755 --- a/authoring_server.py +++ b/authoring_server.py @@ -411,9 +411,9 @@ def render_markdown(data: dict[str, Any]) -> str: flags=re.IGNORECASE, ) if content: - if re.search(r"^#{1,6}\s+.+?\s*$", content, flags=re.MULTILINE): + if re.search(r"^#{1,6}[^\S\r\n]+.+?[^\S\r\n]*$", content, flags=re.MULTILINE): content = re.sub( - r"^#{1,6}\s+.+?\s*$", + r"^#{1,6}[^\S\r\n]+.+?[^\S\r\n]*$", f"# {title}", content, count=1, @@ -629,9 +629,20 @@ APP_HTML = r""" .grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; } .toolbar { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; border: 1px solid var(--line); background: var(--panel); border-radius: 6px; padding: 7px; } .toolbar input[type="file"] { display: none; } + .link-picker { display: grid; grid-template-columns: minmax(220px, 1fr) auto auto; gap: 8px; align-items: center; border: 1px solid var(--line); background: var(--panel); border-radius: 6px; padding: 8px; } label { display: grid; gap: 5px; font-size: 13px; font-weight: 700; color: var(--muted); } input, textarea, select { width: 100%; border: 1px solid var(--line); border-radius: 6px; background: var(--panel); color: var(--ink); padding: 9px 10px; } textarea { min-height: 48vh; resize: vertical; font-family: "SFMono-Regular", Consolas, "Liberation Mono", monospace; line-height: 1.45; font-size: 14px; } + .content-layout { display: grid; gap: 12px; } + .content-layout.previewing { grid-template-columns: minmax(0, 1fr) minmax(280px, 1fr); align-items: start; } + .preview-panel { border: 1px solid var(--line); border-radius: 6px; background: var(--panel); padding: 12px 14px; min-height: 48vh; overflow: auto; overflow-wrap: anywhere; } + .preview-panel h1, .preview-panel h2, .preview-panel h3 { color: var(--ink); margin: 0.8em 0 0.35em; } + .preview-panel h1 { font-size: 24px; } + .preview-panel h2 { font-size: 20px; } + .preview-panel h3 { font-size: 17px; } + .preview-panel p, .preview-panel ul, .preview-panel ol, .preview-panel blockquote { margin: 0 0 0.8em; } + .preview-panel blockquote { border-left: 3px solid var(--line); padding-left: 10px; color: var(--muted); } + .preview-panel img, .preview-panel video { max-width: 100%; height: auto; } .actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; } .hint { color: var(--muted); font-size: 13px; } .full { grid-column: 1 / -1; } @@ -642,7 +653,8 @@ APP_HTML = r""" .queue-item strong { display: block; overflow-wrap: anywhere; } .queue-item span { color: var(--muted); overflow-wrap: anywhere; } pre { white-space: pre-wrap; overflow: auto; max-height: 280px; background: #201f1d; color: #f7f1e4; padding: 12px; border-radius: 6px; font-size: 12px; } - @media (max-width: 860px) { .app { grid-template-columns: 1fr; } aside, main { max-height: none; } aside { border-right: 0; border-bottom: 1px solid var(--line); } .grid { grid-template-columns: 1fr; } } + @media (max-width: 980px) { .content-layout.previewing { grid-template-columns: 1fr; } } + @media (max-width: 860px) { .app { grid-template-columns: 1fr; } aside, main { max-height: none; } aside { border-right: 0; border-bottom: 1px solid var(--line); } .grid { grid-template-columns: 1fr; } .link-picker { grid-template-columns: 1fr; } } @@ -698,7 +710,7 @@ APP_HTML = r""" -