PlanOpticon

planopticon / tests / test_exchange.py
Blame History Raw 225 lines
1
"""Tests for the PlanOpticonExchange interchange format."""
2
3
import json
4
5
from video_processor.exchange import (
6
ArtifactMeta,
7
PlanOpticonExchange,
8
ProjectMeta,
9
)
10
from video_processor.models import Entity, Relationship, SourceRecord
11
12
# ------------------------------------------------------------------
13
# Fixtures
14
# ------------------------------------------------------------------
15
16
17
def _sample_entity(name: str = "Python", etype: str = "technology"):
18
return Entity(
19
name=name,
20
type=etype,
21
descriptions=["A programming language"],
22
)
23
24
25
def _sample_relationship():
26
return Relationship(
27
source="Alice",
28
target="Python",
29
type="uses",
30
)
31
32
33
def _sample_source():
34
return SourceRecord(
35
source_id="src-1",
36
source_type="video",
37
title="Intro recording",
38
)
39
40
41
def _sample_artifact():
42
return ArtifactMeta(
43
name="roadmap",
44
content="# Roadmap\n- Phase 1",
45
artifact_type="roadmap",
46
format="markdown",
47
)
48
49
50
def _sample_project():
51
return ProjectMeta(name="TestProject", description="A test")
52
53
54
# ------------------------------------------------------------------
55
# Tests
56
# ------------------------------------------------------------------
57
58
59
def test_create_empty_exchange():
60
ex = PlanOpticonExchange(project=_sample_project())
61
assert ex.version == "1.0"
62
assert ex.entities == []
63
assert ex.relationships == []
64
assert ex.artifacts == []
65
assert ex.sources == []
66
assert ex.project.name == "TestProject"
67
68
69
def test_create_with_data():
70
ex = PlanOpticonExchange(
71
project=_sample_project(),
72
entities=[_sample_entity()],
73
relationships=[_sample_relationship()],
74
artifacts=[_sample_artifact()],
75
sources=[_sample_source()],
76
)
77
assert len(ex.entities) == 1
78
assert ex.entities[0].name == "Python"
79
assert len(ex.relationships) == 1
80
assert len(ex.artifacts) == 1
81
assert len(ex.sources) == 1
82
83
84
def test_json_roundtrip(tmp_path):
85
original = PlanOpticonExchange(
86
project=_sample_project(),
87
entities=[_sample_entity()],
88
relationships=[_sample_relationship()],
89
artifacts=[_sample_artifact()],
90
sources=[_sample_source()],
91
)
92
out = tmp_path / "exchange.json"
93
original.to_file(out)
94
95
assert out.exists()
96
loaded = PlanOpticonExchange.from_file(out)
97
assert loaded.project.name == original.project.name
98
assert len(loaded.entities) == 1
99
assert loaded.entities[0].name == "Python"
100
assert len(loaded.relationships) == 1
101
assert len(loaded.artifacts) == 1
102
assert len(loaded.sources) == 1
103
104
# Verify valid JSON on disk
105
raw = json.loads(out.read_text())
106
assert raw["version"] == "1.0"
107
108
109
def test_json_schema_export():
110
schema = PlanOpticonExchange.json_schema()
111
assert isinstance(schema, dict)
112
assert "properties" in schema
113
assert "version" in schema["properties"]
114
assert "project" in schema["properties"]
115
assert "entities" in schema["properties"]
116
117
118
def test_from_knowledge_graph():
119
kg_dict = {
120
"nodes": [
121
{
122
"id": "python",
123
"name": "Python",
124
"type": "technology",
125
"descriptions": ["A language"],
126
"occurrences": [],
127
},
128
{
129
"id": "alice",
130
"name": "Alice",
131
"type": "person",
132
"descriptions": ["Engineer"],
133
"occurrences": [],
134
},
135
],
136
"relationships": [
137
{
138
"source": "Alice",
139
"target": "Python",
140
"type": "uses",
141
},
142
],
143
"sources": [
144
{
145
"source_id": "s1",
146
"source_type": "video",
147
"title": "Recording",
148
},
149
],
150
}
151
152
ex = PlanOpticonExchange.from_knowledge_graph(
153
kg_dict,
154
project_name="Demo",
155
tags=["test"],
156
)
157
assert ex.project.name == "Demo"
158
assert len(ex.entities) == 2
159
assert len(ex.relationships) == 1
160
assert len(ex.sources) == 1
161
assert "test" in ex.project.tags
162
163
164
def test_merge_deduplicates_entities():
165
ex1 = PlanOpticonExchange(
166
project=_sample_project(),
167
entities=[_sample_entity("Python"), _sample_entity("Rust")],
168
relationships=[_sample_relationship()],
169
sources=[_sample_source()],
170
)
171
ex2 = PlanOpticonExchange(
172
project=ProjectMeta(name="Other"),
173
entities=[
174
_sample_entity("Python"), # duplicate
175
_sample_entity("Go"), # new
176
],
177
relationships=[
178
Relationship(source="Bob", target="Go", type="uses"),
179
],
180
sources=[
181
SourceRecord(
182
source_id="src-2",
183
source_type="document",
184
title="Notes",
185
),
186
],
187
)
188
189
ex1.merge(ex2)
190
191
entity_names = [e.name for e in ex1.entities]
192
assert entity_names.count("Python") == 1
193
assert "Go" in entity_names
194
assert "Rust" in entity_names
195
assert len(ex1.entities) == 3
196
assert len(ex1.relationships) == 2
197
assert len(ex1.sources) == 2
198
199
200
def test_version_field():
201
ex = PlanOpticonExchange(
202
version="2.0",
203
project=_sample_project(),
204
)
205
assert ex.version == "2.0"
206
207
208
def test_artifact_meta_model():
209
art = ArtifactMeta(
210
name="plan",
211
content="# Plan\nDo stuff",
212
artifact_type="project_plan",
213
format="markdown",
214
metadata={"author": "agent"},
215
)
216
assert art.name == "plan"
217
assert art.artifact_type == "project_plan"
218
assert art.format == "markdown"
219
assert art.metadata == {"author": "agent"}
220
221
# Roundtrip via dict
222
d = art.model_dump()
223
restored = ArtifactMeta.model_validate(d)
224
assert restored == art
225

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button