mirror of
https://github.com/basicmachines-co/basic-memory
synced 2026-06-21 13:47:35 +00:00
c97733d785
Signed-off-by: phernandez <paul@basicmachines.co> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
113 lines
3.7 KiB
Python
113 lines
3.7 KiB
Python
"""Integration tests for schema inference using the 30+ person fixture corpus."""
|
|
|
|
import pytest
|
|
|
|
from basic_memory.schema.inference import infer_schema, NoteData
|
|
|
|
from test_schema.helpers import parse_observations, parse_relations, INFERENCE_DIR
|
|
|
|
|
|
def load_inference_notes() -> list[NoteData]:
|
|
"""Load all person fixture files as NoteData objects."""
|
|
return [
|
|
NoteData(
|
|
identifier=filepath.stem,
|
|
observations=parse_observations(filepath),
|
|
relations=parse_relations(filepath),
|
|
)
|
|
for filepath in sorted(INFERENCE_DIR.glob("*.md"))
|
|
]
|
|
|
|
|
|
@pytest.fixture
|
|
def person_notes() -> list[NoteData]:
|
|
return load_inference_notes()
|
|
|
|
|
|
@pytest.fixture
|
|
def inference_result(person_notes):
|
|
return infer_schema("Person", person_notes)
|
|
|
|
|
|
class TestInferenceCorpusSize:
|
|
def test_minimum_notes_loaded(self, person_notes):
|
|
assert len(person_notes) >= 30
|
|
|
|
def test_notes_analyzed_matches(self, inference_result, person_notes):
|
|
assert inference_result.notes_analyzed == len(person_notes)
|
|
|
|
|
|
class TestNameFieldInference:
|
|
"""Name should be ~100% -> required."""
|
|
|
|
def test_name_is_required(self, inference_result):
|
|
assert "name" in inference_result.suggested_required
|
|
|
|
def test_name_frequency_near_100(self, inference_result):
|
|
name_freq = next(f for f in inference_result.field_frequencies if f.name == "name")
|
|
assert name_freq.percentage >= 0.95
|
|
|
|
|
|
class TestRoleFieldInference:
|
|
"""Role should be ~90% -> optional."""
|
|
|
|
def test_role_is_optional(self, inference_result):
|
|
assert "role" in inference_result.suggested_optional
|
|
|
|
def test_role_frequency_around_90(self, inference_result):
|
|
role_freq = next(f for f in inference_result.field_frequencies if f.name == "role")
|
|
assert 0.80 <= role_freq.percentage < 0.95
|
|
|
|
|
|
class TestExpertiseFieldInference:
|
|
"""Expertise should be ~60% -> optional, array."""
|
|
|
|
def test_expertise_is_optional(self, inference_result):
|
|
assert "expertise" in inference_result.suggested_optional
|
|
|
|
def test_expertise_is_array(self, inference_result):
|
|
freq = next(f for f in inference_result.field_frequencies if f.name == "expertise")
|
|
assert freq.is_array is True
|
|
|
|
|
|
class TestEmailFieldInference:
|
|
"""Email should be ~27% -> optional."""
|
|
|
|
def test_email_is_optional(self, inference_result):
|
|
assert "email" in inference_result.suggested_optional
|
|
|
|
|
|
class TestBornFieldInference:
|
|
"""Born should be ~17% -> excluded."""
|
|
|
|
def test_born_is_excluded(self, inference_result):
|
|
assert "born" in inference_result.excluded
|
|
|
|
def test_born_frequency_below_threshold(self, inference_result):
|
|
born_freq = next(f for f in inference_result.field_frequencies if f.name == "born")
|
|
assert born_freq.percentage < 0.25
|
|
|
|
|
|
class TestWorksAtRelationInference:
|
|
"""works_at relation should be ~73% -> optional."""
|
|
|
|
def test_works_at_is_optional(self, inference_result):
|
|
assert "works_at" in inference_result.suggested_optional
|
|
|
|
def test_works_at_is_relation(self, inference_result):
|
|
freq = next(f for f in inference_result.field_frequencies if f.name == "works_at")
|
|
assert freq.source == "relation"
|
|
|
|
|
|
class TestSuggestedSchema:
|
|
def test_suggested_schema_not_empty(self, inference_result):
|
|
assert len(inference_result.suggested_schema) > 0
|
|
|
|
def test_excluded_fields_not_in_schema(self, inference_result):
|
|
schema_names = set()
|
|
for key in inference_result.suggested_schema:
|
|
name = key.replace("?", "").split("(")[0]
|
|
schema_names.add(name)
|
|
for excluded in inference_result.excluded:
|
|
assert excluded not in schema_names
|