Skip to content

Commit 9486aa1

Browse files
committed
fix(harness): initialize scratchpad entry timestamps
1 parent e74dd92 commit 9486aa1

3 files changed

Lines changed: 82 additions & 11 deletions

File tree

harness/src/harness/scratchpad.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import os
99
import tempfile
1010
import threading
11+
import time
1112
from dataclasses import dataclass, field
1213
from pathlib import Path
1314
from typing import Any
@@ -24,8 +25,8 @@ class BufferEntry:
2425
path: Path
2526
size_bytes: int
2627
description: str
27-
created_at: float = field(default_factory=os.path.getmtime)
28-
accessed_at: float = field(default_factory=os.path.getmtime)
28+
created_at: float = field(default_factory=time.time)
29+
accessed_at: float = field(default_factory=time.time)
2930
metadata: dict = field(default_factory=dict)
3031

3132

harness/tests/test_perf_integration.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -136,15 +136,6 @@ def test_plan_stats(self):
136136
assert stats["total_plans"] == 1
137137

138138

139-
class TestScratchpad:
140-
"""Test scratchpad module."""
141-
142-
@pytest.mark.skip(reason="scratchpad module has bugs in main")
143-
def test_scratchpad_basic(self):
144-
"""Test scratchpad."""
145-
pass
146-
147-
148139
class TestSessionStore:
149140
"""Test session store."""
150141

harness/tests/test_scratchpad.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
"""Direct behavioral contracts for :class:`harness.scratchpad.ScratchpadFileSystem`."""
2+
3+
import asyncio
4+
import json
5+
6+
from harness.scratchpad import ScratchpadFileSystem
7+
8+
9+
def test_write_read_pointer_and_stats(tmp_path):
10+
async def scenario():
11+
scratchpad = ScratchpadFileSystem("session-1", base_dir=tmp_path)
12+
13+
entry_id = await scratchpad.write(
14+
"notes",
15+
"hello scratchpad",
16+
description="test notes",
17+
metadata={"kind": "text"},
18+
)
19+
20+
assert await scratchpad.read(entry_id) == b"hello scratchpad"
21+
assert scratchpad.get_pointer(entry_id) == (
22+
f"[file_id:{entry_id}] - notes: test notes (16 bytes)"
23+
)
24+
assert scratchpad.get_stats() == {
25+
"session_id": "session-1",
26+
"entry_count": 1,
27+
"total_size_bytes": 16,
28+
"total_size_mb": 16 / 1024 / 1024,
29+
}
30+
assert scratchpad.list_entries()[0].metadata == {"kind": "text"}
31+
32+
asyncio.run(scenario())
33+
34+
35+
def test_binary_and_json_content_are_serialized_to_bytes(tmp_path):
36+
async def scenario():
37+
scratchpad = ScratchpadFileSystem("session-1", base_dir=tmp_path)
38+
39+
binary_id = await scratchpad.write("payload", b"\x00\xff")
40+
json_id = await scratchpad.write("record", {"answer": 42})
41+
42+
assert await scratchpad.read(binary_id) == b"\x00\xff"
43+
assert json.loads((await scratchpad.read(json_id)).decode("utf-8")) == {"answer": 42}
44+
assert scratchpad._entries[binary_id].path.suffix == ".bin"
45+
assert scratchpad._entries[json_id].path.suffix == ".json"
46+
47+
asyncio.run(scenario())
48+
49+
50+
def test_index_persists_across_restart(tmp_path):
51+
async def scenario():
52+
original = ScratchpadFileSystem("session-1", base_dir=tmp_path)
53+
entry_id = await original.write("saved", "survives restart", "persisted")
54+
55+
restarted = ScratchpadFileSystem("session-1", base_dir=tmp_path)
56+
57+
assert await restarted.read(entry_id) == b"survives restart"
58+
assert restarted.get_pointer(entry_id) == (
59+
f"[file_id:{entry_id}] - saved: persisted (16 bytes)"
60+
)
61+
62+
asyncio.run(scenario())
63+
64+
65+
def test_delete_clear_and_missing_entries(tmp_path):
66+
async def scenario():
67+
scratchpad = ScratchpadFileSystem("session-1", base_dir=tmp_path)
68+
first_id = await scratchpad.write("first", "one")
69+
second_id = await scratchpad.write("second", "two")
70+
71+
assert await scratchpad.delete(first_id) is True
72+
assert await scratchpad.read(first_id) is None
73+
assert await scratchpad.delete(first_id) is False
74+
assert scratchpad.get_pointer(first_id) == f"file_id:{first_id} - Not found"
75+
assert await scratchpad.clear() == 1
76+
assert await scratchpad.read(second_id) is None
77+
assert scratchpad.get_stats()["entry_count"] == 0
78+
79+
asyncio.run(scenario())

0 commit comments

Comments
 (0)