159 lines
4.9 KiB
Python
159 lines
4.9 KiB
Python
|
|
"""Tests for the new wiki pages shipped in the UI refresh:
|
||
|
|
- /wiki/capture (7I follow-up)
|
||
|
|
- /wiki/memories/{id} (7E)
|
||
|
|
- /wiki/domains/{tag} (7F)
|
||
|
|
- /wiki/activity (activity feed)
|
||
|
|
- home refresh (topnav + activity snippet)
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
|
||
|
|
from atocore.engineering.wiki import (
|
||
|
|
render_activity,
|
||
|
|
render_capture,
|
||
|
|
render_domain,
|
||
|
|
render_homepage,
|
||
|
|
render_memory_detail,
|
||
|
|
)
|
||
|
|
from atocore.engineering.service import init_engineering_schema
|
||
|
|
from atocore.memory.service import create_memory
|
||
|
|
from atocore.models.database import init_db
|
||
|
|
|
||
|
|
|
||
|
|
def _init_all():
|
||
|
|
"""Wiki pages read from both the memory and engineering schemas, so
|
||
|
|
tests need both initialized (the engineering schema is a separate
|
||
|
|
init_engineering_schema() call)."""
|
||
|
|
init_db()
|
||
|
|
init_engineering_schema()
|
||
|
|
|
||
|
|
|
||
|
|
def test_capture_page_renders(tmp_data_dir):
|
||
|
|
_init_all()
|
||
|
|
html = render_capture()
|
||
|
|
assert "Capture a conversation" in html
|
||
|
|
assert "cap-prompt" in html
|
||
|
|
assert "cap-response" in html
|
||
|
|
# Topnav present
|
||
|
|
assert "topnav" in html
|
||
|
|
# Source options for mobile/desktop
|
||
|
|
assert "claude-desktop" in html
|
||
|
|
assert "claude-mobile" in html
|
||
|
|
|
||
|
|
|
||
|
|
def test_memory_detail_renders(tmp_data_dir):
|
||
|
|
_init_all()
|
||
|
|
m = create_memory(
|
||
|
|
"knowledge", "APM uses NX bridge for DXF → STL",
|
||
|
|
project="apm", confidence=0.7, domain_tags=["apm", "nx", "cad"],
|
||
|
|
)
|
||
|
|
html = render_memory_detail(m.id)
|
||
|
|
assert html is not None
|
||
|
|
assert "APM uses NX" in html
|
||
|
|
assert "Audit trail" in html
|
||
|
|
# Tag links go to domain pages
|
||
|
|
assert '/wiki/domains/apm' in html
|
||
|
|
assert '/wiki/domains/nx' in html
|
||
|
|
# Project link present
|
||
|
|
assert '/wiki/projects/apm' in html
|
||
|
|
|
||
|
|
|
||
|
|
def test_memory_detail_404(tmp_data_dir):
|
||
|
|
_init_all()
|
||
|
|
assert render_memory_detail("nonexistent-id") is None
|
||
|
|
|
||
|
|
|
||
|
|
def test_domain_page_lists_memories(tmp_data_dir):
|
||
|
|
_init_all()
|
||
|
|
create_memory("knowledge", "optics fact 1", project="p04-gigabit",
|
||
|
|
domain_tags=["optics"])
|
||
|
|
create_memory("knowledge", "optics fact 2", project="p05-interferometer",
|
||
|
|
domain_tags=["optics", "metrology"])
|
||
|
|
create_memory("knowledge", "other", project="p06-polisher",
|
||
|
|
domain_tags=["firmware"])
|
||
|
|
|
||
|
|
html = render_domain("optics")
|
||
|
|
assert "Domain: <code>optics</code>" in html
|
||
|
|
assert "p04-gigabit" in html
|
||
|
|
assert "p05-interferometer" in html
|
||
|
|
assert "optics fact 1" in html
|
||
|
|
assert "optics fact 2" in html
|
||
|
|
# Unrelated memory should NOT appear
|
||
|
|
assert "other" not in html or "firmware" not in html
|
||
|
|
|
||
|
|
|
||
|
|
def test_domain_page_empty(tmp_data_dir):
|
||
|
|
_init_all()
|
||
|
|
html = render_domain("definitely-not-a-tag")
|
||
|
|
assert "No memories currently carry" in html
|
||
|
|
|
||
|
|
|
||
|
|
def test_domain_page_normalizes_tag(tmp_data_dir):
|
||
|
|
_init_all()
|
||
|
|
create_memory("knowledge", "x", domain_tags=["firmware"])
|
||
|
|
# Case-insensitive
|
||
|
|
assert "firmware" in render_domain("FIRMWARE")
|
||
|
|
# Whitespace tolerant
|
||
|
|
assert "firmware" in render_domain(" firmware ")
|
||
|
|
|
||
|
|
|
||
|
|
def test_activity_feed_renders(tmp_data_dir):
|
||
|
|
_init_all()
|
||
|
|
m = create_memory("knowledge", "activity test")
|
||
|
|
html = render_activity()
|
||
|
|
assert "Activity Feed" in html
|
||
|
|
# The newly-created memory should appear as a "created" event
|
||
|
|
assert "created" in html
|
||
|
|
# Short timestamp format
|
||
|
|
assert m.id[:8] in html
|
||
|
|
|
||
|
|
|
||
|
|
def test_activity_feed_groups_by_action_and_actor(tmp_data_dir):
|
||
|
|
_init_all()
|
||
|
|
for i in range(3):
|
||
|
|
create_memory("knowledge", f"m{i}", actor="test-actor")
|
||
|
|
|
||
|
|
html = render_activity()
|
||
|
|
# Summary row should show "created: 3" or similar
|
||
|
|
assert "created" in html
|
||
|
|
assert "test-actor" in html
|
||
|
|
|
||
|
|
|
||
|
|
def test_homepage_has_topnav_and_activity(tmp_data_dir):
|
||
|
|
_init_all()
|
||
|
|
create_memory("knowledge", "homepage test")
|
||
|
|
html = render_homepage()
|
||
|
|
# Topnav with expected items
|
||
|
|
assert "🏠 Home" in html
|
||
|
|
assert "📡 Activity" in html
|
||
|
|
assert "📥 Capture" in html
|
||
|
|
assert "/wiki/capture" in html
|
||
|
|
assert "/wiki/activity" in html
|
||
|
|
# Activity snippet
|
||
|
|
assert "What the brain is doing" in html
|
||
|
|
|
||
|
|
|
||
|
|
def test_memory_detail_shows_superseded_sources(tmp_data_dir):
|
||
|
|
"""After a merge, sources go to status=superseded. Detail page should
|
||
|
|
still render them."""
|
||
|
|
from atocore.memory.service import (
|
||
|
|
create_merge_candidate, merge_memories,
|
||
|
|
)
|
||
|
|
_init_all()
|
||
|
|
m1 = create_memory("knowledge", "alpha variant 1", project="test")
|
||
|
|
m2 = create_memory("knowledge", "alpha variant 2", project="test")
|
||
|
|
cid = create_merge_candidate(
|
||
|
|
memory_ids=[m1.id, m2.id], similarity=0.9,
|
||
|
|
proposed_content="alpha merged",
|
||
|
|
proposed_memory_type="knowledge", proposed_project="test",
|
||
|
|
)
|
||
|
|
merge_memories(cid, actor="auto-dedup-tier1")
|
||
|
|
|
||
|
|
# Source detail page should render and show the superseded status
|
||
|
|
html1 = render_memory_detail(m1.id)
|
||
|
|
assert html1 is not None
|
||
|
|
assert "superseded" in html1
|
||
|
|
assert "auto-dedup-tier1" in html1 # audit trail shows who merged
|