Commit 4423b3ed0902

Vincent Demeester <vincent@sbr.pm>
2026-02-17 21:57:18
feat: org-todos tests use dedicated Emacs daemon
- Isolated integration tests from user's Emacs daemon by starting a dedicated instance on socket pi-org-todos-test - Daemon is started before tests and torn down after, even on test failure, so it never leaks Signed-off-by: Vincent Demeester <vincent@sbr.pm>
1 parent 63dc32c
Changed files (2)
dots
pi
agent
extensions
dots/pi/agent/extensions/org-todos/Makefile
@@ -1,28 +1,33 @@
 # org-todos Pi extension Makefile
 #
 # Usage:
-#   make test          - Run all tests
-#   make test-elisp    - Run elisp tests only
-#   make test-ts       - Run TypeScript tests only
+#   make test          - Run all tests (starts dedicated test daemon)
+#   make test-elisp    - Run elisp tests only (batch mode, no daemon)
+#   make test-ts       - Run TypeScript tests only (starts test daemon)
 #   make test-unit     - Run TypeScript unit tests only (no Emacs needed)
 #   make build         - Build TypeScript
-#   make check-daemon  - Check if Emacs daemon is running
 #   make clean         - Clean build artifacts
 
 SHELL := bash
 
 # Paths
 ELISP_DIR := ../../../../config/emacs/site-lisp
+ELISP_ABS := $(shell cd $(ELISP_DIR) && pwd)
 ELISP_FILES := org-batch-functions.el pi-org-todos.el
 ELISP_TEST := pi-org-todos-test.el
 
+# Test daemon configuration
+TEST_SOCKET := pi-org-todos-test
+TEST_DAEMON_TIMEOUT := 15
+
 # Colors for output
 GREEN := \033[0;32m
 RED := \033[0;31m
 YELLOW := \033[0;33m
 NC := \033[0m # No Color
 
-.PHONY: all test test-elisp test-ts test-unit build check-daemon clean help
+.PHONY: all test test-elisp test-ts test-unit build clean help
+.PHONY: test-daemon-start test-daemon-stop test-daemon-check
 
 all: test
 
@@ -31,19 +36,21 @@ help:
 	@echo ""
 	@echo "Usage:"
 	@echo "  make test          - Run all tests (elisp + TypeScript)"
-	@echo "  make test-elisp    - Run elisp tests only"
-	@echo "  make test-ts       - Run TypeScript tests only"
+	@echo "  make test-elisp    - Run elisp tests only (batch mode)"
+	@echo "  make test-ts       - Run TypeScript tests only (uses test daemon)"
 	@echo "  make test-unit     - Run TypeScript unit tests only (no Emacs needed)"
 	@echo "  make build         - Build TypeScript"
-	@echo "  make check-daemon  - Check if Emacs daemon is running"
 	@echo "  make clean         - Clean build artifacts"
+	@echo ""
+	@echo "The test suite starts a dedicated Emacs daemon on socket"
+	@echo "'$(TEST_SOCKET)' so it never touches your real daemon or org files."
 
 # Run all tests
 test: test-elisp test-ts
 	@echo ""
 	@echo -e "$(GREEN)✓ All tests passed$(NC)"
 
-# Run elisp tests
+# Run elisp tests (batch mode, no daemon needed)
 test-elisp:
 	@echo -e "$(YELLOW)Running elisp tests...$(NC)"
 	@cd $(ELISP_DIR) && emacs --batch \
@@ -56,10 +63,13 @@ test-elisp:
 		-f ert-run-tests-batch-and-exit
 	@echo -e "$(GREEN)✓ Elisp tests passed$(NC)"
 
-# Run TypeScript tests (requires Emacs daemon for integration tests)
-test-ts: check-daemon
+# Run TypeScript tests with a dedicated test daemon
+test-ts: test-daemon-start
 	@echo -e "$(YELLOW)Running TypeScript tests...$(NC)"
-	@bun test index.test.ts
+	@EMACS_SOCKET_NAME=$(TEST_SOCKET) bun test index.test.ts; \
+		status=$$?; \
+		$(MAKE) --no-print-directory test-daemon-stop; \
+		exit $$status
 	@echo -e "$(GREEN)✓ TypeScript tests passed$(NC)"
 
 # Run only TypeScript unit tests (no Emacs needed)
@@ -68,35 +78,58 @@ test-unit:
 	@bun test index.test.ts --test-name-pattern "unit tests"
 	@echo -e "$(GREEN)✓ TypeScript unit tests passed$(NC)"
 
+# Start a dedicated test Emacs daemon
+test-daemon-start:
+	@echo -e "$(YELLOW)Starting test Emacs daemon (socket: $(TEST_SOCKET))...$(NC)"
+	@# Kill any leftover test daemon
+	@EMACS_SOCKET_NAME=$(TEST_SOCKET) emacsclient --eval '(kill-emacs)' 2>/dev/null || true
+	@sleep 0.5
+	@# Start a plain daemon, then load config via emacsclient
+	@emacs --daemon="$(TEST_SOCKET)" 2>/dev/null || true
+	@for i in $$(seq 1 $(TEST_DAEMON_TIMEOUT)); do \
+		if EMACS_SOCKET_NAME=$(TEST_SOCKET) emacsclient --eval '(+ 1 1)' >/dev/null 2>&1; then \
+			break; \
+		fi; \
+		sleep 0.5; \
+	done
+	@EMACS_SOCKET_NAME=$(TEST_SOCKET) emacsclient --eval \
+		'(progn (add-to-list (quote load-path) "$(ELISP_ABS)") (load "$(CURDIR)/test-init.el") t)' \
+		>/dev/null 2>&1
+	@if EMACS_SOCKET_NAME=$(TEST_SOCKET) emacsclient --eval '(fboundp (quote pi/org-todo-list))' 2>/dev/null | grep -q t; then \
+		echo -e "$(GREEN)✓ Test daemon ready$(NC)"; \
+	else \
+		echo -e "$(RED)Error: Test daemon failed to load pi-org-todos$(NC)"; \
+		EMACS_SOCKET_NAME=$(TEST_SOCKET) emacsclient --eval '(kill-emacs)' 2>/dev/null || true; \
+		exit 1; \
+	fi
+
+# Stop the test daemon
+test-daemon-stop:
+	@echo -e "$(YELLOW)Stopping test daemon...$(NC)"
+	@EMACS_SOCKET_NAME=$(TEST_SOCKET) emacsclient --eval '(kill-emacs)' 2>/dev/null || true
+	@echo -e "$(GREEN)✓ Test daemon stopped$(NC)"
+
+# Check if test daemon is running
+test-daemon-check:
+	@if EMACS_SOCKET_NAME=$(TEST_SOCKET) emacsclient --eval '(+ 1 1)' >/dev/null 2>&1; then \
+		echo -e "$(GREEN)✓ Test daemon is running (socket: $(TEST_SOCKET))$(NC)"; \
+	else \
+		echo -e "$(RED)✗ Test daemon is not running$(NC)"; \
+		exit 1; \
+	fi
+
 # Build TypeScript
 build:
 	@echo -e "$(YELLOW)Building TypeScript...$(NC)"
 	@bun build index.ts --target=node --outdir dist --external @mariozechner/pi-coding-agent --external @mariozechner/pi-tui
 	@echo -e "$(GREEN)✓ Build complete: dist/index.js$(NC)"
 
-# Check if Emacs daemon is running
-check-daemon:
-	@if ! emacsclient --eval '(+ 1 1)' >/dev/null 2>&1; then \
-		echo -e "$(RED)Error: Emacs daemon not running$(NC)"; \
-		echo "Start with: emacs --daemon"; \
-		echo ""; \
-		echo "Or run unit tests only: make test-unit"; \
-		exit 1; \
-	fi
-	@echo -e "$(GREEN)✓ Emacs daemon is running$(NC)"
-
-# Ensure pi-org-todos.el is loaded in daemon
-load-elisp: check-daemon
-	@echo -e "$(YELLOW)Loading pi-org-todos.el in Emacs daemon...$(NC)"
-	@emacsclient --eval "(progn \
-		(add-to-list 'load-path \"$(shell cd $(ELISP_DIR) && pwd)\") \
-		(require 'pi-org-todos))" >/dev/null
-	@echo -e "$(GREEN)✓ pi-org-todos.el loaded$(NC)"
-
 # Clean build artifacts
 clean:
 	@rm -rf dist
 	@rm -f *.log
+	@# Stop test daemon if running
+	@EMACS_SOCKET_NAME=$(TEST_SOCKET) emacsclient --eval '(kill-emacs)' 2>/dev/null || true
 	@echo -e "$(GREEN)✓ Cleaned$(NC)"
 
 # Development helpers
dots/pi/agent/extensions/org-todos/test-init.el
@@ -0,0 +1,12 @@
+;; Test daemon initialization for org-todos integration tests
+;; Loaded via emacsclient after daemon starts
+
+(setq org-todo-keywords
+      '((sequence "STRT(s)" "NEXT(n)" "TODO(t)" "WAIT(w)" "|" "DONE(d!)" "CANX(c@/!)")))
+
+;; load-path is set via command line --eval before loading this file
+(require 'org)
+(require 'org-ql)
+(require 'pi-org-todos)
+
+(message "pi-org-todos test init loaded successfully")