feat: implement knowledge management system (spec complete, all 53 tasks done)
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user