179 lines
6.6 KiB
Python
179 lines
6.6 KiB
Python
"""Tests for QuickCapture – schnelle Wissenserfassung.
|
||
|
||
Requirements: 3.1, 3.2, 3.3, 3.7, 8.3
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import re
|
||
from datetime import datetime
|
||
from pathlib import Path
|
||
|
||
import pytest
|
||
|
||
from monorepo.knowledge.ingestion.capture import QuickCapture
|
||
|
||
|
||
@pytest.fixture
|
||
def monorepo_root(tmp_path: Path) -> Path:
|
||
"""Create a minimal monorepo structure for testing."""
|
||
for ctx in ("bahn", "dhive", "privat"):
|
||
(tmp_path / ctx / "knowledge" / "inbox").mkdir(parents=True)
|
||
return tmp_path
|
||
|
||
|
||
@pytest.fixture
|
||
def capture(monorepo_root: Path) -> QuickCapture:
|
||
"""Create a QuickCapture instance."""
|
||
return QuickCapture(monorepo_root)
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# _detect_input_type tests
|
||
# ---------------------------------------------------------------------------
|
||
|
||
|
||
class TestDetectInputType:
|
||
"""Tests for _detect_input_type."""
|
||
|
||
def test_detects_http_url(self, capture: QuickCapture) -> None:
|
||
assert capture._detect_input_type("http://example.com") == "url"
|
||
|
||
def test_detects_https_url(self, capture: QuickCapture) -> None:
|
||
assert capture._detect_input_type("https://docs.python.org/3/") == "url"
|
||
|
||
def test_detects_url_with_whitespace(self, capture: QuickCapture) -> None:
|
||
assert capture._detect_input_type(" https://example.com ") == "url"
|
||
|
||
def test_detects_file_path_with_extension(self, capture: QuickCapture) -> None:
|
||
assert capture._detect_input_type("/home/user/notes.md") == "file"
|
||
|
||
def test_detects_windows_path(self, capture: QuickCapture) -> None:
|
||
assert capture._detect_input_type("C:\\Users\\doc.pdf") == "file"
|
||
|
||
def test_plain_text(self, capture: QuickCapture) -> None:
|
||
assert capture._detect_input_type("Just a note about something") == "text"
|
||
|
||
def test_text_with_dot_but_no_separator(self, capture: QuickCapture) -> None:
|
||
# e.g. "version 3.14" should not be detected as file
|
||
assert capture._detect_input_type("version 3.14 is out") == "text"
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# _generate_filename tests
|
||
# ---------------------------------------------------------------------------
|
||
|
||
|
||
class TestGenerateFilename:
|
||
"""Tests for _generate_filename."""
|
||
|
||
def test_filename_format(self, capture: QuickCapture) -> None:
|
||
filename = capture._generate_filename("My Test Note")
|
||
# Should match YYYY-MM-DD-HH-MM-slug.md
|
||
pattern = r"^\d{4}-\d{2}-\d{2}-\d{2}-\d{2}-my-test-note\.md$"
|
||
assert re.match(pattern, filename), f"Unexpected filename: {filename}"
|
||
|
||
def test_filename_slugifies_special_chars(self, capture: QuickCapture) -> None:
|
||
filename = capture._generate_filename("Hello, World! (2024)")
|
||
assert "hello-world-2024" in filename
|
||
|
||
def test_filename_limits_slug_length(self, capture: QuickCapture) -> None:
|
||
long_title = "a" * 100
|
||
filename = capture._generate_filename(long_title)
|
||
# Timestamp is 16 chars (YYYY-MM-DD-HH-MM) + 1 dash + slug (<=50) + .md
|
||
slug_part = filename.split("-", 5)[-1].replace(".md", "")
|
||
assert len(slug_part) <= 50
|
||
|
||
def test_filename_ends_with_md(self, capture: QuickCapture) -> None:
|
||
filename = capture._generate_filename("test")
|
||
assert filename.endswith(".md")
|
||
|
||
def test_filename_collapses_multiple_hyphens(self, capture: QuickCapture) -> None:
|
||
filename = capture._generate_filename("foo---bar///baz")
|
||
# Should not have consecutive hyphens in slug
|
||
slug_part = filename[17:] # skip timestamp prefix "YYYY-MM-DD-HH-MM-"
|
||
assert "--" not in slug_part
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# capture() integration tests
|
||
# ---------------------------------------------------------------------------
|
||
|
||
|
||
class TestCapture:
|
||
"""Tests for capture()."""
|
||
|
||
def test_capture_text_creates_file(
|
||
self, capture: QuickCapture, monorepo_root: Path
|
||
) -> None:
|
||
result = capture.capture("Eine schnelle Notiz", context="bahn")
|
||
assert result.exists()
|
||
assert result.parent == monorepo_root / "bahn" / "knowledge" / "inbox"
|
||
|
||
def test_capture_text_returns_path(self, capture: QuickCapture) -> None:
|
||
result = capture.capture("Notiz", context="privat")
|
||
assert isinstance(result, Path)
|
||
|
||
def test_capture_text_frontmatter(
|
||
self, capture: QuickCapture, monorepo_root: Path
|
||
) -> None:
|
||
result = capture.capture(
|
||
"Meeting notes from today",
|
||
context="dhive",
|
||
tags=["meeting", "project-x"],
|
||
)
|
||
content = result.read_text(encoding="utf-8")
|
||
assert "type: inbox" in content
|
||
assert 'title: "Meeting notes from today"' in content
|
||
assert "created:" in content
|
||
assert "source:" in content
|
||
assert "type: text" in content
|
||
assert "tags: [meeting, project-x]" in content
|
||
assert "source_context: dhive" in content
|
||
|
||
def test_capture_url_detected(
|
||
self, capture: QuickCapture, monorepo_root: Path
|
||
) -> None:
|
||
url = "https://docs.python.org/3/library/pathlib.html"
|
||
result = capture.capture(url, context="bahn")
|
||
content = result.read_text(encoding="utf-8")
|
||
assert "type: url" in content
|
||
assert f"[{url}]({url})" in content
|
||
|
||
def test_capture_file_detected(
|
||
self, capture: QuickCapture, monorepo_root: Path
|
||
) -> None:
|
||
filepath = "/home/user/documents/report.pdf"
|
||
result = capture.capture(filepath, context="privat")
|
||
content = result.read_text(encoding="utf-8")
|
||
assert "type: file" in content
|
||
assert f"Source file: `{filepath}`" in content
|
||
|
||
def test_capture_creates_inbox_dir_if_missing(
|
||
self, capture: QuickCapture, monorepo_root: Path
|
||
) -> None:
|
||
# Remove inbox dir
|
||
import shutil
|
||
|
||
inbox = monorepo_root / "bahn" / "knowledge" / "inbox"
|
||
shutil.rmtree(inbox)
|
||
assert not inbox.exists()
|
||
|
||
result = capture.capture("test", context="bahn")
|
||
assert result.exists()
|
||
assert inbox.exists()
|
||
|
||
def test_capture_empty_tags(
|
||
self, capture: QuickCapture, monorepo_root: Path
|
||
) -> None:
|
||
result = capture.capture("note", context="bahn")
|
||
content = result.read_text(encoding="utf-8")
|
||
assert "tags: []" in content
|
||
|
||
def test_capture_url_title_from_domain(
|
||
self, capture: QuickCapture, monorepo_root: Path
|
||
) -> None:
|
||
result = capture.capture("https://example.com/docs/intro", context="bahn")
|
||
content = result.read_text(encoding="utf-8")
|
||
assert "example.com/docs/intro" in content
|