personal memory agent
1# SPDX-License-Identifier: AGPL-3.0-only
2# Copyright (c) 2026 sol pbc
3
4import json
5from pathlib import Path
6from unittest.mock import AsyncMock, patch
7
8import pytest
9from jsonschema import Draft202012Validator
10
11from solstone.observe import describe as describe_mod
12from solstone.observe.categories import calendar as calendar_mod
13from solstone.think.batch import Batch
14from solstone.think.schema_bounds import unbounded_nodes
15
16
17def _load_schema() -> dict:
18 return json.loads(
19 (
20 Path(describe_mod.__file__).resolve().parent
21 / "categories"
22 / "calendar.schema.json"
23 ).read_text(encoding="utf-8")
24 )
25
26
27def _valid_payload() -> dict:
28 return {
29 "app": "Google Calendar",
30 "view": "week",
31 "range": "Apr 13 - Apr 19, 2026",
32 "events": [
33 {
34 "title": "Planning review",
35 "start": "Tue 10:00 AM",
36 "end": "11:00 AM",
37 "location": "Conference Room A",
38 "conferencing": "Google Meet",
39 "guests": ["Alice", "Bob"],
40 "status": "accepted",
41 "recurrence": None,
42 "calendar": "Work",
43 "description": "Visible event notes",
44 },
45 ],
46 "availability": ["Tue 2:00 PM"],
47 "notes": "Timezone: America/Denver",
48 }
49
50
51def test_calendar_schema_file_is_valid_draft_2020_12():
52 Draft202012Validator.check_schema(_load_schema())
53
54
55def test_calendar_schema_accepts_and_rejects_expected_values():
56 validator = Draft202012Validator(_load_schema())
57
58 assert validator.is_valid(_valid_payload())
59
60 bad_enum = _valid_payload()
61 bad_enum["view"] = "list"
62 assert not validator.is_valid(bad_enum)
63
64 extra_property = _valid_payload()
65 extra_property["extra"] = True
66 assert not validator.is_valid(extra_property)
67
68 missing_required = _valid_payload()
69 del missing_required["events"]
70 assert not validator.is_valid(missing_required)
71
72 wrong_item_type = _valid_payload()
73 wrong_item_type["events"] = ["Planning review"]
74 assert not validator.is_valid(wrong_item_type)
75
76
77def test_discover_categories_attaches_calendar_schema():
78 expected = _load_schema()
79
80 assert describe_mod.CATEGORIES["calendar"]["json_schema"] == expected
81 assert describe_mod.CATEGORIES["calendar"]["output"] == "json"
82
83
84@pytest.mark.asyncio
85@patch("solstone.think.batch.agenerate_with_result", new_callable=AsyncMock)
86async def test_calendar_extract_batch_call_passes_schema(mock_agenerate):
87 mock_agenerate.return_value = {
88 "text": json.dumps(_valid_payload()),
89 "finish_reason": "stop",
90 }
91
92 cat_meta = describe_mod.CATEGORIES["calendar"]
93 batch = Batch(max_concurrent=1)
94 req = batch.create(
95 contents="Analyze this calendar screenshot.",
96 context=cat_meta["context"],
97 json_schema=cat_meta["json_schema"],
98 )
99 batch.add(req)
100
101 results = []
102 async for completed_req in batch.drain_batch():
103 results.append(completed_req)
104
105 assert len(results) == 1
106 assert mock_agenerate.call_args.kwargs["json_schema"] == _load_schema()
107
108
109def test_calendar_schema_has_no_unbounded_nodes():
110 assert unbounded_nodes(_load_schema()) == []
111
112
113def test_calendar_formatter_renders_valid_dict():
114 result = calendar_mod.format(_valid_payload(), {})
115
116 assert "**Calendar** (Google Calendar - week)" in result
117 assert "*Apr 13 - Apr 19, 2026*" in result
118 assert "- **Planning review** (Tue 10:00 AM - 11:00 AM) [accepted]" in result
119 assert " - Location: Conference Room A" in result
120 assert " - Conferencing: Google Meet" in result
121 assert " - Guests: Alice, Bob" in result
122 assert " - Calendar: Work" in result
123 assert " - Description: Visible event notes" in result
124 assert "**Availability:** Tue 2:00 PM" in result
125 assert "Timezone: America/Denver" in result
126
127
128def test_calendar_formatter_returns_empty_for_non_dict():
129 assert calendar_mod.format("# [Calendar - Week]", {}) == ""
130
131
132def test_calendar_formatter_skips_non_dict_event(caplog):
133 payload = _valid_payload()
134 payload["events"] = [
135 "Planning review",
136 {
137 "title": "Follow-up",
138 "start": None,
139 "end": None,
140 "location": None,
141 "conferencing": None,
142 "guests": [],
143 "status": "unknown",
144 "recurrence": None,
145 "calendar": None,
146 "description": None,
147 },
148 ]
149
150 with caplog.at_level("WARNING", logger="solstone.observe.categories.calendar"):
151 result = calendar_mod.format(payload, {})
152
153 assert "**Calendar** (Google Calendar - week)" in result
154 assert "- **Follow-up**" in result
155 assert "Planning review" not in result
156 assert "skipping non-dict event" in caplog.text