nanobot: 2026-09-11 09:44:11
This commit is contained in:
@@ -34,65 +34,117 @@ def today():
|
||||
return datetime.now(ZoneInfo("Europe/Prague")).date().isoformat()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def entry_file(tmp_path):
|
||||
"""Write entry text to a file and return its path, the way the skill does."""
|
||||
|
||||
def write(text):
|
||||
path = tmp_path / "entry.md"
|
||||
path.write_text(text, encoding="utf-8")
|
||||
return str(path)
|
||||
|
||||
return write
|
||||
|
||||
|
||||
# -- log ---------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_log_appends_with_today_date(projects, capsys):
|
||||
def test_log_appends_with_today_date(projects, entry_file, capsys):
|
||||
directory = make_project(projects, "chata")
|
||||
|
||||
assert project_cli.cmd_log("chata", "Dřevo objednáno") == 0
|
||||
assert project_cli.cmd_log("chata", entry_file("Dřevo objednáno")) == 0
|
||||
|
||||
memory = (directory / "memory.md").read_text(encoding="utf-8")
|
||||
assert memory == f"- {today()}: Dřevo objednáno\n"
|
||||
assert json.loads(capsys.readouterr().out)["appended"].startswith(f"- {today()}:")
|
||||
|
||||
|
||||
def test_log_does_not_join_when_file_lacks_trailing_newline(projects):
|
||||
def test_log_does_not_join_when_file_lacks_trailing_newline(projects, entry_file):
|
||||
directory = make_project(projects, "chata", memory="- 2026-09-01: první")
|
||||
|
||||
project_cli.cmd_log("chata", "druhý")
|
||||
project_cli.cmd_log("chata", entry_file("druhý"))
|
||||
|
||||
lines = (directory / "memory.md").read_text(encoding="utf-8").splitlines()
|
||||
assert lines == ["- 2026-09-01: první", f"- {today()}: druhý"]
|
||||
|
||||
|
||||
def test_log_creates_missing_memory_file(projects):
|
||||
def test_log_creates_missing_memory_file(projects, entry_file):
|
||||
directory = projects / "chata"
|
||||
directory.mkdir()
|
||||
|
||||
assert project_cli.cmd_log("chata", "první") == 0
|
||||
assert project_cli.cmd_log("chata", entry_file("první")) == 0
|
||||
assert (directory / "memory.md").read_text(encoding="utf-8").endswith("první\n")
|
||||
|
||||
|
||||
def test_log_reads_stdin_verbatim(projects, monkeypatch):
|
||||
def test_log_reads_file_verbatim(projects, entry_file):
|
||||
directory = make_project(projects, "chata")
|
||||
text = "Uvozovky „takhle\" a apostrof ' a \"tohle\"\ndruhý řádek"
|
||||
monkeypatch.setattr(sys, "stdin", io.StringIO(text))
|
||||
|
||||
assert project_cli.cmd_log("chata", None) == 0
|
||||
assert project_cli.cmd_log("chata", entry_file(text)) == 0
|
||||
|
||||
memory = (directory / "memory.md").read_text(encoding="utf-8")
|
||||
assert memory == f"- {today()}: {text}\n"
|
||||
|
||||
|
||||
def test_log_does_not_shorten_long_entry(projects):
|
||||
def test_log_accepts_text_the_exec_guard_would_reject_on_a_command_line(projects, entry_file):
|
||||
"""Regression: `Cíl:` / `Závěr:` parse as Windows drive paths in the exec guard.
|
||||
|
||||
The guard scans the raw command string, so such text must reach the script
|
||||
through a file, never as an argument, a heredoc or a pipe.
|
||||
"""
|
||||
directory = make_project(projects, "chata")
|
||||
text = "Cíl: srovnat pánev. Diagnóza/směr: mobilita kyčle. Závěr: pokračovat."
|
||||
|
||||
assert project_cli.cmd_log("chata", entry_file(text)) == 0
|
||||
assert text in (directory / "memory.md").read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def test_log_reads_stdin_when_no_file_given(projects, monkeypatch):
|
||||
directory = make_project(projects, "chata")
|
||||
monkeypatch.setattr(sys, "stdin", io.StringIO("ze stdinu"))
|
||||
|
||||
assert project_cli.cmd_log("chata", None) == 0
|
||||
|
||||
assert (directory / "memory.md").read_text(encoding="utf-8") == f"- {today()}: ze stdinu\n"
|
||||
|
||||
|
||||
def test_log_rejects_missing_file_without_touching_memory(projects, tmp_path, capsys):
|
||||
directory = make_project(projects, "chata", memory="- 2026-09-01: první\n")
|
||||
|
||||
assert project_cli.cmd_log("chata", str(tmp_path / "chybi.md")) == 1
|
||||
|
||||
assert "No such file" in capsys.readouterr().err
|
||||
assert (directory / "memory.md").read_text(encoding="utf-8") == "- 2026-09-01: první\n"
|
||||
|
||||
|
||||
def test_log_does_not_shorten_long_entry(projects, entry_file):
|
||||
directory = make_project(projects, "chata")
|
||||
text = "x" * 3000
|
||||
|
||||
project_cli.cmd_log("chata", text)
|
||||
project_cli.cmd_log("chata", entry_file(text))
|
||||
|
||||
assert text in (directory / "memory.md").read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def test_log_rejects_empty_input(projects):
|
||||
def test_log_rejects_empty_input(projects, entry_file):
|
||||
directory = make_project(projects, "chata", memory="- 2026-09-01: první\n")
|
||||
|
||||
assert project_cli.cmd_log("chata", " ") == 1
|
||||
assert project_cli.cmd_log("chata", entry_file(" ")) == 1
|
||||
assert (directory / "memory.md").read_text(encoding="utf-8") == "- 2026-09-01: první\n"
|
||||
|
||||
|
||||
def test_log_rejects_unknown_project(projects):
|
||||
assert project_cli.cmd_log("neznamy", "text") == 1
|
||||
def test_log_rejects_unknown_project(projects, entry_file):
|
||||
assert project_cli.cmd_log("neznamy", entry_file("text")) == 1
|
||||
|
||||
|
||||
def test_log_no_longer_accepts_text_on_the_command_line(monkeypatch):
|
||||
"""`--text` is gone for good: prose in argv is what the exec guard blocks."""
|
||||
monkeypatch.setattr(sys, "argv", ["project_cli.py", "log", "chata", "--text", "něco"])
|
||||
|
||||
with pytest.raises(SystemExit) as excinfo:
|
||||
project_cli.main()
|
||||
|
||||
assert excinfo.value.code == 2
|
||||
|
||||
|
||||
# -- activate ----------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user