-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathtest_events.py
More file actions
190 lines (156 loc) · 5.52 KB
/
Copy pathtest_events.py
File metadata and controls
190 lines (156 loc) · 5.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
"""Tests for event log system."""
import asyncio
from hive.memory.events import EventLog, EventType, HiveEvent
def test_event_serialize_deserialize():
event = HiveEvent(
event_type=EventType.GOAL_SET,
agent_id="agent-1",
session_id="sess-1",
data={"goal_id": "g-1", "objective": "test"},
)
line = event.to_jsonl()
restored = HiveEvent.from_jsonl(line)
assert restored.event_type == EventType.GOAL_SET
assert restored.data["goal_id"] == "g-1"
def test_append_and_replay(tmp_dir):
log = EventLog(tmp_dir)
async def _run():
e1 = HiveEvent(
event_type=EventType.GOAL_SET,
agent_id="agent-1",
session_id="sess-1",
data={"goal": "test goal 1"},
)
e2 = HiveEvent(
event_type=EventType.TOOL_USED,
agent_id="agent-1",
session_id="sess-1",
data={"tool": "world_query"},
)
await log.append(e1)
await log.append(e2)
events = await log.replay("agent-1", "sess-1")
assert len(events) == 2
assert events[0].event_type == EventType.GOAL_SET
assert events[1].event_type == EventType.TOOL_USED
asyncio.run(_run())
def test_list_sessions(tmp_dir):
log = EventLog(tmp_dir)
async def _run():
for sid in ["sess-a", "sess-b"]:
await log.append(
HiveEvent(
event_type=EventType.TASK_STARTED,
agent_id="agent-1",
session_id=sid,
data={},
)
)
sessions = await log.list_sessions("agent-1")
assert "sess-a" in sessions
assert "sess-b" in sessions
asyncio.run(_run())
def test_all_event_types_valid():
for et in EventType:
event = HiveEvent(
event_type=et,
agent_id="test",
session_id="test",
data={},
)
line = event.to_jsonl()
restored = HiveEvent.from_jsonl(line)
assert restored.event_type == et
def test_fsync_append_durable_and_readable(tmp_dir):
"""With fsync enabled, appends still round-trip and the file is on disk."""
log = EventLog(tmp_dir, fsync=True)
async def _run():
await log.append(
HiveEvent(
event_type=EventType.GOAL_SET,
agent_id="agent-1",
session_id="sess-1",
data={"goal": "durable"},
)
)
events = await log.replay("agent-1", "sess-1")
assert len(events) == 1
assert events[0].data["goal"] == "durable"
asyncio.run(_run())
def test_replay_tolerates_partial_last_line(tmp_dir):
"""A torn/half-written final line must not break replay of prior events."""
log = EventLog(tmp_dir)
async def _run():
await log.append(
HiveEvent(
event_type=EventType.GOAL_SET,
agent_id="agent-1",
session_id="sess-1",
data={"goal": "complete"},
)
)
# Simulate an interrupted append: a partial JSON line with no newline.
path = log._session_path("agent-1", "sess-1")
with open(path, "a") as f:
f.write('{"event_type": "tool_used", "agen')
events = await log.replay("agent-1", "sess-1")
assert len(events) == 1
assert events[0].data["goal"] == "complete"
asyncio.run(_run())
def test_replay_handles_unicode_line_separators(tmp_dir):
"""A record whose text contains U+2028/U+2029/NEL must not be shredded.
These are legal (unescaped) inside JSON strings and have no '\\n', so the
record is one physical line -- but str.splitlines() would break it apart.
"""
log = EventLog(tmp_dir)
async def _run():
tricky = "before
middle
after\x85end"
await log.append(
HiveEvent(
event_type=EventType.ASSISTANT_MESSAGE,
agent_id="agent-1",
session_id="sess-1",
data={"text": tricky},
)
)
await log.append(
HiveEvent(
event_type=EventType.TOOL_USED,
agent_id="agent-1",
session_id="sess-1",
data={"tool": "x"},
)
)
events = await log.replay("agent-1", "sess-1")
assert len(events) == 2 # not shredded into extra/broken lines
assert events[0].data["text"] == tricky
assert events[1].data["tool"] == "x"
asyncio.run(_run())
def test_replay_raises_on_mid_log_corruption(tmp_dir):
"""A malformed line that is NOT the torn final line is real corruption -- surface it."""
import pytest
log = EventLog(tmp_dir)
async def _run():
await log.append(
HiveEvent(
event_type=EventType.GOAL_SET,
agent_id="agent-1",
session_id="sess-1",
data={"goal": "ok"},
)
)
# A complete (newline-terminated) but corrupt record, then a valid one.
path = log._session_path("agent-1", "sess-1")
with open(path, "a") as f:
f.write("{not valid json}\n")
f.write(
HiveEvent(
event_type=EventType.TOOL_USED,
agent_id="agent-1",
session_id="sess-1",
).to_jsonl()
+ "\n"
)
with pytest.raises(ValueError):
await log.replay("agent-1", "sess-1")
asyncio.run(_run())