generated from coulomb/repo-seed
entity relationship model
This commit is contained in:
221
tests/test_semantics.py
Normal file
221
tests/test_semantics.py
Normal file
@@ -0,0 +1,221 @@
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from infospace_bench import InfospaceError, add_artifact, create_infospace
|
||||
from infospace_bench.semantics import (
|
||||
list_entities,
|
||||
list_relations,
|
||||
parse_entity_artifact,
|
||||
parse_relation_artifact,
|
||||
)
|
||||
|
||||
|
||||
ENTITY = """# Division of Labour
|
||||
|
||||
## Definition
|
||||
|
||||
Increasing productivity by splitting work into specialized tasks.
|
||||
|
||||
## Source Chapter
|
||||
|
||||
Book I, Chapter 1
|
||||
|
||||
## Context
|
||||
|
||||
Smith introduces the pin factory as an example of this mechanism.
|
||||
|
||||
## Economic Domain
|
||||
|
||||
Production
|
||||
|
||||
## Original Wording
|
||||
|
||||
The greatest improvement in the productive powers of labour.
|
||||
|
||||
## Modern Interpretation
|
||||
|
||||
Specialization improves throughput by reducing switching costs.
|
||||
"""
|
||||
|
||||
|
||||
INVALID_ENTITY = """# Thin Entity
|
||||
|
||||
## Context
|
||||
|
||||
This artifact is missing its definition.
|
||||
"""
|
||||
|
||||
|
||||
RELATION = """# Division of Labour enables Market Extent
|
||||
|
||||
## Subject
|
||||
|
||||
Division of Labour
|
||||
|
||||
## Predicate
|
||||
|
||||
is limited by
|
||||
|
||||
## Object
|
||||
|
||||
Market Extent
|
||||
|
||||
## Relation Type
|
||||
|
||||
constrains
|
||||
|
||||
## VSM Channel
|
||||
|
||||
S1 -> S4
|
||||
|
||||
## Evidence
|
||||
|
||||
Book I, Chapter 3 connects specialization to market size.
|
||||
|
||||
## Feedback Role
|
||||
|
||||
Part of the market expansion loop.
|
||||
"""
|
||||
|
||||
|
||||
def cli_env() -> dict[str, str]:
|
||||
env = os.environ.copy()
|
||||
env["PYTHONPATH"] = "src:/home/worsch/markitect-tool/src"
|
||||
return env
|
||||
|
||||
|
||||
def test_parse_entity_artifact_extracts_legacy_sections(tmp_path: Path) -> None:
|
||||
path = tmp_path / "division.md"
|
||||
path.write_text(ENTITY, encoding="utf-8")
|
||||
|
||||
entity = parse_entity_artifact("entity/division.md", path)
|
||||
|
||||
assert entity.artifact_id == "entity/division.md"
|
||||
assert entity.slug == "division-of-labour"
|
||||
assert entity.title == "Division of Labour"
|
||||
assert entity.definition_word_count == 8
|
||||
assert entity.domain == "Production"
|
||||
assert entity.has_original_wording is True
|
||||
assert entity.section_slugs == [
|
||||
"definition",
|
||||
"source-chapter",
|
||||
"context",
|
||||
"economic-domain",
|
||||
"original-wording",
|
||||
"modern-interpretation",
|
||||
]
|
||||
|
||||
|
||||
def test_parse_entity_artifact_reports_missing_required_sections(tmp_path: Path) -> None:
|
||||
path = tmp_path / "thin.md"
|
||||
path.write_text(INVALID_ENTITY, encoding="utf-8")
|
||||
|
||||
with pytest.raises(InfospaceError) as raised:
|
||||
parse_entity_artifact("entity/thin.md", path)
|
||||
|
||||
assert raised.value.code == "invalid_entity_artifact"
|
||||
assert raised.value.detail["missing_sections"] == ["definition"]
|
||||
|
||||
|
||||
def test_parse_relation_artifact_links_entity_endpoints(tmp_path: Path) -> None:
|
||||
path = tmp_path / "relation.md"
|
||||
path.write_text(RELATION, encoding="utf-8")
|
||||
entity_ids = {
|
||||
"division-of-labour": "entity/division.md",
|
||||
"market-extent": "entity/market.md",
|
||||
}
|
||||
|
||||
relation = parse_relation_artifact("relation/division-market.md", path, entity_ids)
|
||||
|
||||
assert relation.slug == "division-of-labour-enables-market-extent"
|
||||
assert relation.subject_slug == "division-of-labour"
|
||||
assert relation.object_slug == "market-extent"
|
||||
assert relation.subject_entity_id == "entity/division.md"
|
||||
assert relation.object_entity_id == "entity/market.md"
|
||||
assert relation.is_feedback_member is True
|
||||
|
||||
|
||||
def test_parse_relation_artifact_reports_unresolved_endpoint(tmp_path: Path) -> None:
|
||||
path = tmp_path / "relation.md"
|
||||
path.write_text(RELATION, encoding="utf-8")
|
||||
|
||||
with pytest.raises(InfospaceError) as raised:
|
||||
parse_relation_artifact("relation/division-market.md", path, {})
|
||||
|
||||
assert raised.value.code == "unresolved_relation_endpoint"
|
||||
assert raised.value.detail["missing_slugs"] == [
|
||||
"division-of-labour",
|
||||
"market-extent",
|
||||
]
|
||||
|
||||
|
||||
def test_list_entities_and_relations_from_manifest(tmp_path: Path) -> None:
|
||||
infospace = create_infospace(tmp_path, "pilot", name="Pilot")
|
||||
division = tmp_path / "division.md"
|
||||
division.write_text(ENTITY, encoding="utf-8")
|
||||
market = tmp_path / "market.md"
|
||||
market.write_text(
|
||||
ENTITY.replace("Division of Labour", "Market Extent").replace(
|
||||
"Production", "Exchange"
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
relation = tmp_path / "relation.md"
|
||||
relation.write_text(RELATION, encoding="utf-8")
|
||||
|
||||
add_artifact(infospace.root, division, kind="entity", title="Division")
|
||||
add_artifact(infospace.root, market, kind="entity", title="Market")
|
||||
add_artifact(infospace.root, relation, kind="relation", title="Relation")
|
||||
|
||||
assert [entity.slug for entity in list_entities(infospace.root)] == [
|
||||
"division-of-labour",
|
||||
"market-extent",
|
||||
]
|
||||
assert [item.relation_type for item in list_relations(infospace.root)] == [
|
||||
"constrains"
|
||||
]
|
||||
|
||||
|
||||
def test_cli_entities_and_relations_output_json(tmp_path: Path) -> None:
|
||||
infospace = create_infospace(tmp_path, "pilot", name="Pilot")
|
||||
division = tmp_path / "division.md"
|
||||
division.write_text(ENTITY, encoding="utf-8")
|
||||
market = tmp_path / "market.md"
|
||||
market.write_text(
|
||||
ENTITY.replace("Division of Labour", "Market Extent").replace(
|
||||
"Production", "Exchange"
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
relation = tmp_path / "relation.md"
|
||||
relation.write_text(RELATION, encoding="utf-8")
|
||||
add_artifact(infospace.root, division, kind="entity", title="Division")
|
||||
add_artifact(infospace.root, market, kind="entity", title="Market")
|
||||
add_artifact(infospace.root, relation, kind="relation", title="Relation")
|
||||
|
||||
entities = subprocess.run(
|
||||
[sys.executable, "-m", "infospace_bench", "entities", str(infospace.root)],
|
||||
check=False,
|
||||
env=cli_env(),
|
||||
text=True,
|
||||
capture_output=True,
|
||||
)
|
||||
relations = subprocess.run(
|
||||
[sys.executable, "-m", "infospace_bench", "relations", str(infospace.root)],
|
||||
check=False,
|
||||
env=cli_env(),
|
||||
text=True,
|
||||
capture_output=True,
|
||||
)
|
||||
|
||||
assert entities.returncode == 0, entities.stderr
|
||||
assert relations.returncode == 0, relations.stderr
|
||||
assert json.loads(entities.stdout)["entities"][0]["slug"] == "division-of-labour"
|
||||
assert json.loads(relations.stdout)["relations"][0]["subject_entity_id"] == (
|
||||
"entity/division.md"
|
||||
)
|
||||
Reference in New Issue
Block a user