返回 last30days-skill
test_doc_flag_contract.py
根目录 / tests / test_doc_flag_contract.py
1 """Documentation contract for Python CLI and wrapper-only flags."""
2
3 from __future__ import annotations
4
5 from pathlib import Path
6
7 import last30days as cli
8
9 ROOT = Path(__file__).resolve().parents[1]
10 CONFIGURATION = ROOT / "CONFIGURATION.md"
11 SKILL_MD = ROOT / "skills" / "last30days" / "SKILL.md"
12 HTML_REFERENCE = ROOT / "skills" / "last30days" / "references" / "save-html-brief.md"
13
14
15 def _parser_flags() -> set[str]:
16 parser = cli.build_parser()
17 flags: set[str] = set()
18 for action in parser._actions:
19 flags.update(action.option_strings)
20 return flags
21
22
23 def test_configuration_documents_new_safety_flags():
24 text = CONFIGURATION.read_text(encoding="utf-8")
25 flags = _parser_flags()
26 assert "--no-browser-cookies" in flags
27 assert "--no-browser-cookies" in text
28 assert "--preflight" in flags
29 assert "--preflight" in text
30 assert "--save-dir" in text
31 assert "--output" in text
32 assert "--publish-html" in flags
33 assert "--publish-html" in text
34 assert "--publish" in flags
35 assert "library feed --publish" in text
36 assert "--publish-password" in flags
37 assert "--publish-password" in text
38 assert "LAST30DAYS_PUBLISH_PASSWORD" in text
39 assert "--record-fixtures" in flags
40 assert "--record-fixtures" in text
41 assert "docs/reference/eval.md" in text
42
43
44 def test_html_publish_reference_prompts_for_password_choice():
45 text = HTML_REFERENCE.read_text(encoding="utf-8")
46 publish_section = text[text.index("## Optional hosted publishing"):text.index("## What ends up in the HTML file")]
47 assert "Respect any existing user, project, or host preference for HTML publishing first" in publish_section
48 assert "If multiple publishing options are available, show each as its own choice" in publish_section
49 assert "label `ht-ml.app` as supporting optional password protection" in publish_section
50 assert "Show the absolute saved path" in publish_section
51 assert "Open HTML file" in publish_section
52 assert "Done for now" in publish_section
53 assert "Do not upload until the user chooses a publishing option" in publish_section
54 assert "ask a second question" in publish_section
55 assert "**Public link** - publish without a password" in publish_section
56 assert "**Password-protected link** - ask the user to type the shared password" in publish_section
57 assert "repeat the shared password they selected" in publish_section
58 assert "LAST30DAYS_PUBLISH_PASSWORD" in publish_section
59 assert '--output "$HTML_PATH"' in publish_section
60 assert "<HTML_PATH>.publish.json" in publish_section
61
62
63 def test_reddit_backend_env_var_is_documented_for_users_and_runtime_skill():
64 config_text = CONFIGURATION.read_text(encoding="utf-8")
65 skill_text = SKILL_MD.read_text(encoding="utf-8")
66
67 assert "LAST30DAYS_REDDIT_BACKEND=scrapecreators" in config_text
68 assert "LAST30DAYS_REDDIT_BACKEND=scrapecreators" in skill_text
69 # Thinness floor shipped with the keyless Reddit path; keep user docs honest.
70 assert "LAST30DAYS_REDDIT_SC_MIN_ITEMS" in config_text
71 assert "LAST30DAYS_REDDIT_SC_MIN_ITEMS" in skill_text
72 # Security copy must not claim transport/rate-limit escalation for Reddit search.
73 assert "when public Reddit is unavailable" not in skill_text
74 assert "backup when the free path returns no items" in skill_text or (
75 "returns **no items**" in skill_text
76 )
77
78
79 def test_save_is_not_documented_as_python_cli_flag():
80 text = CONFIGURATION.read_text(encoding="utf-8")
81 assert "--save-dir <path>" in text
82 assert "--save " not in text
83 assert "`--save`" not in text
84
85
86 def test_agent_is_documented_as_skill_argument_not_python_flag():
87 text = SKILL_MD.read_text(encoding="utf-8")
88 start = text.index("## Agent Mode (--agent flag)")
89 agent_section = text[start:start + 2000]
90 assert "If `--agent` appears in ARGUMENTS" in agent_section
91 assert "slash-command skill contract" in text
92 assert "not a Python CLI flag" in text
93
94
95 def test_html_reference_documents_structured_cache_reuse():
96 text = HTML_REFERENCE.read_text(encoding="utf-8")
97 assert "~/.config/last30days/last-report.json" in text
98 assert "without re-running source fetchers" in text
99 assert "No matching cached report data" in text
100 assert "LAST30DAYS_REPORT_CACHE_TTL_SECONDS" in text
101 assert "default: one hour" in text
102
103
104 def test_configuration_documents_report_cache_ttl():
105 text = CONFIGURATION.read_text(encoding="utf-8")
106 assert "LAST30DAYS_REPORT_CACHE_TTL_SECONDS" in text
107 assert "defaults to `3600`" in text
108 assert "`0` to disable report-cache reuse" in text
109
110
111 def test_comparison_artifact_contract_documents_actual_paths():
112 text = SKILL_MD.read_text(encoding="utf-8")
113 comparison_start = text.index("\n## If QUERY_TYPE = COMPARISON\n")
114 comparison_section = text[comparison_start:comparison_start + 5000]
115 assert "there is no separate merged Markdown raw file" in comparison_section
116 assert "[last30days] Comparison artifact set: main={path}; peers={path, ...}" in comparison_section
117 assert "Treat that log line as authoritative" in comparison_section
118
119 step_start = text.index("## Step 2.5: Append WebSearch Results to Saved Raw File")
120 step_section = text[step_start:step_start + 3500]
121 assert "append the same `## WebSearch Supplemental Results` section to every listed per-entity Markdown raw file" in step_section
122 assert "do not append Markdown text to `.html` or `.json`" in step_section
123
123 lines PYTHON