test_brief_file.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. """Tests for the atomic best-effort durable brief writer (BriefFile)."""
  2. import os
  3. import threading
  4. from stt_daemon.assistant.brief_file import BriefFile
  5. def _path(tmp_path):
  6. return tmp_path / "brief.md"
  7. def _tmps(tmp_path):
  8. return [p.name for p in tmp_path.iterdir() if p.suffix == ".tmp"]
  9. def test_write_enabled_property(tmp_path):
  10. assert BriefFile(True, str(tmp_path)).write_enabled is True
  11. assert BriefFile(False, str(tmp_path)).write_enabled is False
  12. def test_disabled_write_and_clear_create_no_file(tmp_path):
  13. # misleading-success probe: disabled means the path must never appear.
  14. brief = BriefFile(False, str(tmp_path))
  15. brief.write("hello")
  16. assert not _path(tmp_path).exists()
  17. brief.clear()
  18. assert not _path(tmp_path).exists()
  19. assert not any(tmp_path.iterdir())
  20. def test_enabled_write_writes_exact_text(tmp_path):
  21. brief = BriefFile(True, str(tmp_path))
  22. brief.write("hello")
  23. assert _path(tmp_path).read_text(encoding="utf-8") == "hello"
  24. assert _path(tmp_path).read_bytes() == b"hello"
  25. assert _tmps(tmp_path) == []
  26. def test_second_write_replaces_atomically_no_tmp(tmp_path):
  27. # stale-state probe: only the newest text survives, no .tmp leaks.
  28. brief = BriefFile(True, str(tmp_path))
  29. brief.write("first")
  30. brief.write("second")
  31. assert _path(tmp_path).read_text(encoding="utf-8") == "second"
  32. assert _tmps(tmp_path) == []
  33. def test_clear_empties_file(tmp_path):
  34. brief = BriefFile(True, str(tmp_path))
  35. brief.write("content")
  36. brief.clear()
  37. assert _path(tmp_path).read_text(encoding="utf-8") == ""
  38. assert _path(tmp_path).exists()
  39. def test_write_failure_path_swallowed_no_raise(tmp_path):
  40. # failure probe: parent is a regular file, so makedirs/open fail -> swallow.
  41. blocker = tmp_path / "blocker"
  42. blocker.write_text("x", encoding="utf-8")
  43. brief = BriefFile(True, str(blocker / "nested"))
  44. brief.write("hello") # must not raise
  45. assert not os.path.exists(str(blocker / "nested"))
  46. def test_clear_failure_path_swallowed_no_raise(tmp_path):
  47. brief = BriefFile(True, str(tmp_path / "missing"))
  48. brief.clear() # missing dir -> open fails -> swallowed, no raise
  49. def test_default_directory_is_brief(tmp_path, monkeypatch):
  50. # constructor default: `brief/brief.md` relative to the cwd.
  51. monkeypatch.chdir(tmp_path)
  52. brief = BriefFile(True)
  53. brief.write("x")
  54. assert (tmp_path / "brief" / "brief.md").read_text(encoding="utf-8") == "x"
  55. def test_set_directory_redirects_writes(tmp_path):
  56. brief = BriefFile(True)
  57. brief.set_directory(str(tmp_path))
  58. brief.write("x")
  59. assert (tmp_path / "brief.md").read_text(encoding="utf-8") == "x"
  60. def test_disabled_after_set_directory_writes_nothing(tmp_path):
  61. brief = BriefFile(False)
  62. brief.set_directory(str(tmp_path))
  63. brief.write("x")
  64. brief.clear()
  65. assert not any(tmp_path.iterdir())
  66. def test_concurrent_set_directory_and_write_stay_within_dirs(tmp_path):
  67. # race probe: a writer flipping between two dirs must never land elsewhere.
  68. dir_a = tmp_path / "a"
  69. dir_b = tmp_path / "b"
  70. brief = BriefFile(True)
  71. brief.set_directory(str(dir_a))
  72. stop = threading.Event()
  73. def swapper():
  74. while not stop.is_set():
  75. brief.set_directory(str(dir_a))
  76. brief.set_directory(str(dir_b))
  77. def writer():
  78. for _ in range(500):
  79. brief.write("x")
  80. stop.set()
  81. threads = [threading.Thread(target=swapper), threading.Thread(target=writer)]
  82. for thread in threads:
  83. thread.start()
  84. for thread in threads:
  85. thread.join()
  86. allowed = {dir_a.resolve(), dir_b.resolve()}
  87. produced = [p for p in tmp_path.rglob("*") if p.is_file()]
  88. assert produced, "writer should have produced at least one file"
  89. for path in produced:
  90. assert path.resolve().parent in allowed, f"write escaped to {path}"
  91. assert not (tmp_path / "brief").exists()
  92. assert not (tmp_path / "brief.md").exists()