nkshirsa commited on
Commit
30832a1
·
verified ·
1 Parent(s): 7f84cd9

Add tests/test_db.py

Browse files
Files changed (1) hide show
  1. tests/test_db.py +271 -0
tests/test_db.py ADDED
@@ -0,0 +1,271 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ PhD Research OS — Unit Tests (Phase 0 requirement: 20+ tests)
3
+ """
4
+
5
+ import os
6
+ import sys
7
+ import json
8
+ import pytest
9
+
10
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
11
+
12
+ from phd_research_os.db import (
13
+ init_db, get_db, create_claim, get_claim, update_claim, search_claims,
14
+ create_source, get_source, create_goal, get_goals_by_priority,
15
+ create_conflict, create_decision, create_override,
16
+ log_api_usage, get_cost_summary, to_fixed, from_fixed
17
+ )
18
+
19
+ TEST_DB = "test_research_os.db"
20
+
21
+
22
+ @pytest.fixture(autouse=True)
23
+ def setup_teardown():
24
+ """Create fresh DB for each test."""
25
+ init_db(TEST_DB)
26
+ yield
27
+ if os.path.exists(TEST_DB):
28
+ os.remove(TEST_DB)
29
+ if os.path.exists(TEST_DB + "-wal"):
30
+ os.remove(TEST_DB + "-wal")
31
+ if os.path.exists(TEST_DB + "-shm"):
32
+ os.remove(TEST_DB + "-shm")
33
+
34
+
35
+ def get_test_conn():
36
+ return get_db(TEST_DB)
37
+
38
+
39
+ # ============================================================
40
+ # Fixed-Point Math Tests
41
+ # ============================================================
42
+
43
+ def test_fixed_point_conversion():
44
+ assert to_fixed(0.85) == 850
45
+ assert from_fixed(850) == 0.85
46
+
47
+ def test_fixed_point_precision():
48
+ assert to_fixed(0.123) == 123
49
+ assert to_fixed(0.999) == 999
50
+ assert to_fixed(0.0) == 0
51
+ assert to_fixed(1.0) == 1000
52
+
53
+ def test_fixed_point_roundtrip():
54
+ for val in [0.0, 0.001, 0.5, 0.85, 0.999, 1.0]:
55
+ assert abs(from_fixed(to_fixed(val)) - val) < 0.001
56
+
57
+
58
+ # ============================================================
59
+ # Claim CRUD Tests
60
+ # ============================================================
61
+
62
+ def test_create_claim_complete():
63
+ conn = get_test_conn()
64
+ cid = create_claim(conn, "Graphene shows mobility > 10000 cm²/Vs", "Fact", 0.92,
65
+ evidence_strength=0.95, study_quality_weight=1.0,
66
+ journal_tier_weight=1.0, completeness_penalty=1.0)
67
+ claim = get_claim(conn, cid)
68
+ assert claim is not None
69
+ assert claim['status'] == 'Complete'
70
+ assert claim['epistemic_tag'] == 'Fact'
71
+ assert abs(claim['confidence'] - 0.92) < 0.01
72
+ conn.close()
73
+
74
+ def test_create_claim_incomplete():
75
+ conn = get_test_conn()
76
+ cid = create_claim(conn, "Temperature may affect binding", "Hypothesis", 0.35,
77
+ missing_fields=["temperature", "pH", "ionic_strength"])
78
+ claim = get_claim(conn, cid)
79
+ assert claim['status'] == 'Incomplete'
80
+ assert len(claim['missing_fields']) == 3
81
+ conn.close()
82
+
83
+ def test_claim_id_format():
84
+ conn = get_test_conn()
85
+ cid = create_claim(conn, "Test", "Fact", 0.5)
86
+ assert cid.startswith("CLM_")
87
+ conn.close()
88
+
89
+ def test_claim_schema_version():
90
+ conn = get_test_conn()
91
+ cid = create_claim(conn, "Test", "Fact", 0.5)
92
+ claim = get_claim(conn, cid)
93
+ assert claim['schema_version'] == '1.0'
94
+ conn.close()
95
+
96
+ def test_get_nonexistent_claim():
97
+ conn = get_test_conn()
98
+ assert get_claim(conn, "CLM_NONEXIST") is None
99
+ conn.close()
100
+
101
+ def test_update_claim():
102
+ conn = get_test_conn()
103
+ cid = create_claim(conn, "Old text", "Fact", 0.5)
104
+ update_claim(conn, cid, text="Updated text", confidence=0.9)
105
+ claim = get_claim(conn, cid)
106
+ assert claim['text'] == "Updated text"
107
+ assert abs(claim['confidence'] - 0.9) < 0.01
108
+ conn.close()
109
+
110
+ def test_search_claims_by_text():
111
+ conn = get_test_conn()
112
+ create_claim(conn, "Graphene biosensor detection", "Fact", 0.8)
113
+ create_claim(conn, "Lithium battery cycling", "Fact", 0.7)
114
+ results = search_claims(conn, query="graphene")
115
+ assert len(results) == 1
116
+ assert "graphene" in results[0]['text'].lower()
117
+ conn.close()
118
+
119
+ def test_search_claims_by_epistemic_tag():
120
+ conn = get_test_conn()
121
+ create_claim(conn, "Measured value", "Fact", 0.9)
122
+ create_claim(conn, "We hypothesize", "Hypothesis", 0.3)
123
+ results = search_claims(conn, epistemic_tag="Hypothesis")
124
+ assert len(results) == 1
125
+ conn.close()
126
+
127
+ def test_search_claims_by_min_confidence():
128
+ conn = get_test_conn()
129
+ create_claim(conn, "High confidence", "Fact", 0.95)
130
+ create_claim(conn, "Low confidence", "Hypothesis", 0.2)
131
+ results = search_claims(conn, min_confidence=0.5)
132
+ assert len(results) == 1
133
+ assert results[0]['confidence'] >= 0.5
134
+ conn.close()
135
+
136
+ def test_create_50_claims():
137
+ """Phase 0 acceptance: Can create 50 Claim Objects programmatically."""
138
+ conn = get_test_conn()
139
+ ids = []
140
+ for i in range(50):
141
+ cid = create_claim(conn, f"Claim number {i}",
142
+ ["Fact", "Interpretation", "Hypothesis"][i % 3],
143
+ round(0.1 + (i * 0.018), 3))
144
+ ids.append(cid)
145
+ assert len(ids) == 50
146
+ assert len(set(ids)) == 50 # All unique
147
+ conn.close()
148
+
149
+ def test_claim_parameters():
150
+ conn = get_test_conn()
151
+ params = {"temperature_C": 25.0, "pH": 7.4, "ionic_strength_mM": 10.0}
152
+ cid = create_claim(conn, "Test", "Fact", 0.5, parameters=params)
153
+ claim = get_claim(conn, cid)
154
+ assert claim['parameters'] == params
155
+ conn.close()
156
+
157
+
158
+ # ============================================================
159
+ # Source Tests
160
+ # ============================================================
161
+
162
+ def test_create_source():
163
+ conn = get_test_conn()
164
+ doi = create_source(conn, "10.1234/test", "Test Paper",
165
+ ["Author A", "Author B"], 2024, "Nature", 1)
166
+ src = get_source(conn, doi)
167
+ assert src is not None
168
+ assert src['title'] == "Test Paper"
169
+ assert len(src['authors']) == 2
170
+ conn.close()
171
+
172
+ def test_source_not_found():
173
+ conn = get_test_conn()
174
+ assert get_source(conn, "10.9999/nonexist") is None
175
+ conn.close()
176
+
177
+
178
+ # ============================================================
179
+ # Goal Tests
180
+ # ============================================================
181
+
182
+ def test_create_goal():
183
+ conn = get_test_conn()
184
+ gid = create_goal(conn, "Achieve sub-fM LOD", "high")
185
+ goals = get_goals_by_priority(conn)
186
+ assert len(goals) == 1
187
+ assert goals[0]['priority'] == 'high'
188
+ assert goals[0]['status'] == 'Active'
189
+ conn.close()
190
+
191
+ def test_goals_sorted_by_priority():
192
+ conn = get_test_conn()
193
+ create_goal(conn, "Low priority", "low")
194
+ create_goal(conn, "High priority", "high")
195
+ create_goal(conn, "Medium priority", "medium")
196
+ goals = get_goals_by_priority(conn)
197
+ assert goals[0]['priority'] == 'high'
198
+ assert goals[1]['priority'] == 'medium'
199
+ assert goals[2]['priority'] == 'low'
200
+ conn.close()
201
+
202
+
203
+ # ============================================================
204
+ # Conflict Tests
205
+ # ============================================================
206
+
207
+ def test_create_conflict():
208
+ conn = get_test_conn()
209
+ cid_a = create_claim(conn, "Sensitivity increases", "Fact", 0.8)
210
+ cid_b = create_claim(conn, "Sensitivity decreases", "Fact", 0.7)
211
+ conf_id = create_conflict(conn, cid_a, cid_b, "value_mismatch",
212
+ "Different surface chemistry",
213
+ ["surface treatment", "buffer composition"])
214
+
215
+ row = conn.execute("SELECT * FROM conflicts WHERE conflict_id = ?", (conf_id,)).fetchone()
216
+ assert row is not None
217
+ assert dict(row)['hypothesis_confidence'] == 'low' # ALWAYS low
218
+ assert dict(row)['resolution_status'] == 'Unresolved'
219
+ conn.close()
220
+
221
+
222
+ # ============================================================
223
+ # Override Tests
224
+ # ============================================================
225
+
226
+ def test_expert_override():
227
+ conn = get_test_conn()
228
+ cid = create_claim(conn, "Override test", "Fact", 0.5)
229
+ ovr_id = create_override(conn, cid, "Dr. Smith",
230
+ "Direct experimental evidence", 0.95)
231
+
232
+ claim = get_claim(conn, cid)
233
+ assert abs(claim['confidence'] - 0.95) < 0.01
234
+ assert claim['expert_override'] is not None
235
+ assert claim['expert_override']['who'] == 'Dr. Smith'
236
+ conn.close()
237
+
238
+
239
+ # ============================================================
240
+ # Decision Tests
241
+ # ============================================================
242
+
243
+ def test_create_decision():
244
+ conn = get_test_conn()
245
+ gid = create_goal(conn, "Test goal", "high")
246
+ dec_id = create_decision(conn, "experiment", "Run control experiment",
247
+ 0.72, gid, priority="high",
248
+ estimated_effort="2 weeks")
249
+
250
+ row = conn.execute("SELECT * FROM decisions WHERE decision_id = ?", (dec_id,)).fetchone()
251
+ assert row is not None
252
+ assert dict(row)['status'] == 'Proposed'
253
+ conn.close()
254
+
255
+
256
+ # ============================================================
257
+ # API Usage Logging Tests
258
+ # ============================================================
259
+
260
+ def test_api_usage_logging():
261
+ conn = get_test_conn()
262
+ log_api_usage(conn, "claude-haiku", 500, 200, 0.001, "claim_extraction")
263
+ log_api_usage(conn, "claude-haiku", 300, 150, 0.0008, "conflict_detection")
264
+
265
+ summary = get_cost_summary(conn, days=1)
266
+ assert summary['num_calls'] == 2
267
+ conn.close()
268
+
269
+
270
+ if __name__ == "__main__":
271
+ pytest.main([__file__, "-v"])