Files
yusufkaraaslan--skill_seekers/tests/test_phase2_golden_chat.py
T
wehub-resource-sync 2cab53bc94
Test Vector Database Adaptors / Test MCP Vector DB Tools (push) Has been cancelled
Tests / Code Quality (Ruff & Mypy) (push) Has been cancelled
Tests / Fast Unit Tests (parallel) (macos-latest, 3.11) (push) Has been cancelled
Tests / Fast Unit Tests (parallel) (macos-latest, 3.12) (push) Has been cancelled
Tests / Tests (push) Has been cancelled
Docker Publish / Build and Push Docker Images (map[description:Skill Seekers CLI - Convert documentation to AI skills dockerfile:Dockerfile name:skill-seekers]) (push) Has been cancelled
Docker Publish / Build and Push Docker Images (map[description:Skill Seekers MCP Server - 25 tools for AI assistants dockerfile:Dockerfile.mcp name:skill-seekers-mcp]) (push) Has been cancelled
Docker Publish / Test Docker Images (push) Has been cancelled
Tests / Fast Unit Tests (parallel) (ubuntu-latest, 3.10) (push) Has been cancelled
Tests / Fast Unit Tests (parallel) (ubuntu-latest, 3.11) (push) Has been cancelled
Tests / Fast Unit Tests (parallel) (ubuntu-latest, 3.12) (push) Has been cancelled
Tests / Serial / Integration / E2E Tests (push) Has been cancelled
Tests / MCP Server Tests (push) Has been cancelled
Test Vector Database Adaptors / Test chroma Adaptor (push) Has been cancelled
Test Vector Database Adaptors / Test faiss Adaptor (push) Has been cancelled
Test Vector Database Adaptors / Test qdrant Adaptor (push) Has been cancelled
Test Vector Database Adaptors / Test weaviate Adaptor (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:46:28 +08:00

375 lines
12 KiB
Python

"""Golden-output tests for the chat scraper (Phase 2 port).
The golden trees under tests/golden/phase2/chat*/ were captured from the
PRE-DocumentSkillBuilder code; these tests prove the port is byte-identical.
Chat sections aren't heading+text shaped like EPUB/Word — they're
channel+date message groups — so the fixtures exercise chat's own build
paths: multi-channel categorization, single-channel topic-bucket
categorization, the single-category main.md fast path, message counts,
reactions embedded in text, code snippets (incl. >500 chars, empty
language → "unknown", user attribution), shared links (incl. >20
truncation), channel summaries, and the empty-export fallback.
"""
import copy
import re
import pytest
from tests.phase2_golden_utils import assert_matches_golden, build_snapshot
LONG_CODE = "def long_helper():\n" + "\n".join(f" y{i} = {i}" for i in range(60))
# ── Multi-channel fixture (slack) ───────────────────────────────────────────
ENGINEERING_DAY1_TEXT = "\n\n".join(
[
"**alice** (2024-03-01T09:00:00): We hit an error and a bug in the deploy step",
" Reactions: :thumbsup: (2) :eyes: (1)",
"**bob** (2024-03-01T09:05:00): Try this fix:\n```python\nprint('patched')\n```",
"**alice** (2024-03-01T09:10:00): That fixed the crash, thanks!",
]
)
ENGINEERING_DAY2_TEXT = "\n\n".join(
[
"**alice** (2024-03-02T10:00:00): How do we install and setup docker here?",
"**bob** (2024-03-02T10:15:00): Long helper below:\n```\n" + LONG_CODE + "\n```",
]
)
RANDOM_DAY1_TEXT = "**carol** (2024-03-01T12:00:00): Lunch at noon? See https://example.com/menu"
MULTI_CHANNEL_SECTIONS = [
{
"section_number": 1,
"heading": "#engineering - 2024-03-01",
"heading_level": "h2",
"text": ENGINEERING_DAY1_TEXT,
"headings": [],
"code_samples": [
{"code": "print('patched')", "language": "python", "quality_score": 7.0},
],
"tables": [],
"images": [],
"channel": "engineering",
"date": "2024-03-01",
"message_count": 3,
},
{
"section_number": 2,
"heading": "#engineering - 2024-03-02",
"heading_level": "h2",
"text": ENGINEERING_DAY2_TEXT,
"headings": [],
"code_samples": [
{"code": LONG_CODE, "language": "", "quality_score": 9.5},
],
"tables": [],
"images": [],
"channel": "engineering",
"date": "2024-03-02",
"message_count": 2,
},
{
"section_number": 3,
"heading": "#random - 2024-03-01",
"heading_level": "h2",
"text": RANDOM_DAY1_TEXT,
"headings": [],
"code_samples": [],
"tables": [],
"images": [],
"channel": "random",
"date": "2024-03-01",
"message_count": 1,
},
]
# 21 links exercises the "... and N more links" truncation in SKILL.md
MULTI_CHANNEL_LINKS = [
{
"url": f"https://example.com/resource/{i}",
"channel": "engineering" if i % 2 == 0 else "random",
"user": "alice" if i % 2 == 0 else "carol",
"timestamp": f"2024-03-01T09:{i:02d}:00",
"context": f"see https://example.com/resource/{i} for details",
}
for i in range(21)
]
MULTI_CHANNEL_DATA = {
"source": "fixtures/slack-export/",
"platform": "slack",
"metadata": {
"total_messages": 6,
"total_threads": 1,
"total_code_snippets": 3,
"total_links": 21,
"unique_users": 3,
"channels": ["engineering", "random"],
},
"total_sections": 3,
"total_code_blocks": 3,
"channel_summaries": {
"engineering": {
"message_count": 5,
"unique_users": 2,
"date_range": {
"earliest": "2024-03-01T09:00:00",
"latest": "2024-03-02T10:15:00",
},
"top_users": [
{"user": "alice", "count": 3},
{"user": "bob", "count": 2},
],
"has_code": True,
},
"random": {
"message_count": 1,
"unique_users": 1,
"date_range": {
"earliest": "2024-03-01T12:00:00",
"latest": "2024-03-01T12:00:00",
},
"top_users": [{"user": "carol", "count": 1}],
"has_code": False,
},
},
"code_snippets": [
{
"code": LONG_CODE,
"language": "python",
"quality_score": 9.5,
"channel": "engineering",
"user": "bob",
"timestamp": "2024-03-02T10:15:00",
},
{
"code": "print('patched')",
"language": "python",
"quality_score": 7.0,
"channel": "engineering",
"user": "bob",
"timestamp": "2024-03-01T09:05:00",
},
{
"code": "kubectl get pods",
"language": "",
"quality_score": 4.0,
"channel": "engineering",
"user": "alice",
"timestamp": "2024-03-02T11:00:00",
},
],
"links": MULTI_CHANNEL_LINKS,
"pages": MULTI_CHANNEL_SECTIONS,
}
# ── Single-channel fixture (discord) — topic-bucket categorization ──────────
TOPIC_SECTIONS = [
{
"section_number": 1,
"heading": "#help - 2024-04-01",
"heading_level": "h2",
"text": "**dave** (2024-04-01T08:00:00): Got an error and a crash, traceback attached",
"headings": [],
"code_samples": [],
"tables": [],
"images": [],
"channel": "help",
"date": "2024-04-01",
"message_count": 1,
},
{
"section_number": 2,
"heading": "#help - 2024-04-02",
"heading_level": "h2",
"text": "**erin** (2024-04-02T08:00:00): How do I install and setup the docker image?",
"headings": [],
"code_samples": [],
"tables": [],
"images": [],
"channel": "help",
"date": "2024-04-02",
"message_count": 1,
},
{
"section_number": 3,
"heading": "#help - 2024-04-03",
"heading_level": "h2",
"text": "**dave** (2024-04-03T08:00:00): Happy Friday everyone!",
"headings": [],
"code_samples": [],
"tables": [],
"images": [],
"channel": "help",
"date": "2024-04-03",
"message_count": 1,
},
]
TOPIC_DATA = {
"source": "fixtures/discord-export.json",
"platform": "discord",
"metadata": {
"total_messages": 3,
"total_threads": 0,
"total_code_snippets": 0,
"total_links": 0,
"unique_users": 2,
"channels": ["help"],
},
"total_sections": 3,
"total_code_blocks": 0,
"channel_summaries": {
"help": {
"message_count": 3,
"unique_users": 2,
"date_range": {
"earliest": "2024-04-01T08:00:00",
"latest": "2024-04-03T08:00:00",
},
"top_users": [
{"user": "dave", "count": 2},
{"user": "erin", "count": 1},
],
"has_code": False,
},
},
"code_snippets": [],
"links": [],
"pages": TOPIC_SECTIONS,
}
# ── Single-category fixture — no topic matches → general → main.md ──────────
SINGLE_CATEGORY_DATA = {
"source": "fixtures/slack-export/",
"platform": "slack",
"metadata": {
"total_messages": 1,
"total_threads": 0,
"total_code_snippets": 0,
"total_links": 0,
"unique_users": 1,
"channels": ["general"],
},
"total_sections": 1,
"total_code_blocks": 0,
"channel_summaries": {
"general": {
"message_count": 1,
"unique_users": 1,
"date_range": {
"earliest": "2024-05-01T10:00:00",
"latest": "2024-05-01T10:00:00",
},
"top_users": [{"user": "frank", "count": 1}],
"has_code": False,
},
},
"code_snippets": [],
"links": [],
"pages": [
{
"section_number": 1,
"heading": "#general - 2024-05-01",
"heading_level": "h2",
"text": "**frank** (2024-05-01T10:00:00): Welcome aboard!",
"headings": [],
"code_samples": [],
"tables": [],
"images": [],
"channel": "general",
"date": "2024-05-01",
"message_count": 1,
},
],
}
# ── Empty fixture — no messages extracted ────────────────────────────────────
EMPTY_DATA = {
"source": "fixtures/empty-export/",
"platform": "slack",
"metadata": {
"total_messages": 0,
"total_threads": 0,
"total_code_snippets": 0,
"total_links": 0,
"unique_users": 0,
"channels": [],
},
"total_sections": 0,
"total_code_blocks": 0,
"channel_summaries": {},
"code_snippets": [],
"links": [],
"pages": [],
}
def _make_converter(tmp_path, name, platform, extracted):
from skill_seekers.cli.chat_scraper import ChatToSkillConverter
converter = ChatToSkillConverter(
{
"name": name,
"description": f"Use when testing the {name} golden build",
"platform": platform,
"output_dir": str(tmp_path / "skill"),
}
)
converter.extracted_data = copy.deepcopy(extracted)
return converter
def test_chat_multi_channel_matches_golden(tmp_path):
"""Two channels → channel-name categories with {key}_sN-sM.md filenames."""
converter = _make_converter(tmp_path, "golden_chat", "slack", MULTI_CHANNEL_DATA)
assert_matches_golden(build_snapshot(converter), "chat")
def test_chat_topic_buckets_match_golden(tmp_path):
"""Single channel → topic-bucket categories plus General Discussion."""
converter = _make_converter(tmp_path, "golden_chat_topics", "discord", TOPIC_DATA)
assert_matches_golden(build_snapshot(converter), "chat_topics")
def test_chat_single_category_matches_golden(tmp_path):
"""No topic matches → one 'general' category → main.md fast path."""
converter = _make_converter(tmp_path, "golden_chat_single", "slack", SINGLE_CATEGORY_DATA)
assert_matches_golden(build_snapshot(converter), "chat_single")
def test_chat_empty_matches_golden(tmp_path):
"""Empty export → 'content' fallback category and section_01.md filename."""
converter = _make_converter(tmp_path, "golden_chat_empty", "slack", EMPTY_DATA)
assert_matches_golden(build_snapshot(converter), "chat_empty")
@pytest.mark.parametrize(
("name", "platform", "data"),
[
("golden_chat", "slack", MULTI_CHANNEL_DATA),
("golden_chat_topics", "discord", TOPIC_DATA),
("golden_chat_single", "slack", SINGLE_CATEGORY_DATA),
("golden_chat_empty", "slack", EMPTY_DATA),
],
)
def test_skill_md_reference_links_point_at_real_files(tmp_path, name, platform, data):
"""Every `references/...` link in SKILL.md must name a file that was
actually written — the nav used to derive links from sanitized titles
while the writer named files via _reference_filename (DOC-07 drift)."""
converter = _make_converter(tmp_path, name, platform, data)
snapshot = build_snapshot(converter)
skill_md = snapshot["SKILL.md"].decode("utf-8")
links = re.findall(r"`(references/[^`]+)`", skill_md)
assert links, "SKILL.md should link at least references/index.md"
missing = [link for link in links if link not in snapshot]
assert not missing, f"SKILL.md links to nonexistent files: {missing}"