"""Tests for the Engineering Knowledge Layer.""" from atocore.engineering.service import ( ENTITY_TYPES, RELATIONSHIP_TYPES, create_entity, create_relationship, get_entities, get_entity, get_entity_with_context, get_relationships, init_engineering_schema, ) from atocore.models.database import init_db import pytest def test_create_and_get_entity(tmp_data_dir): init_db() init_engineering_schema() e = create_entity( entity_type="component", name="Pivot Pin", project="p04-gigabit", description="Lateral support pivot pin for M1 assembly", properties={"material": "GF-PTFE", "diameter_mm": 12}, ) assert e.entity_type == "component" assert e.name == "Pivot Pin" assert e.properties["material"] == "GF-PTFE" fetched = get_entity(e.id) assert fetched is not None assert fetched.name == "Pivot Pin" def test_create_relationship(tmp_data_dir): init_db() init_engineering_schema() subsystem = create_entity("subsystem", "Lateral Support", project="p04-gigabit") component = create_entity("component", "Pivot Pin", project="p04-gigabit") rel = create_relationship( source_entity_id=subsystem.id, target_entity_id=component.id, relationship_type="contains", ) assert rel.relationship_type == "contains" rels = get_relationships(subsystem.id, direction="outgoing") assert len(rels) == 1 assert rels[0].target_entity_id == component.id def test_entity_with_context(tmp_data_dir): init_db() init_engineering_schema() subsystem = create_entity("subsystem", "Lateral Support", project="p04-gigabit") pin = create_entity("component", "Pivot Pin", project="p04-gigabit") pad = create_entity("component", "PTFE Pad", project="p04-gigabit") material = create_entity("material", "GF-PTFE", project="p04-gigabit", description="Glass-filled PTFE for thermal stability") create_relationship(subsystem.id, pin.id, "contains") create_relationship(subsystem.id, pad.id, "contains") create_relationship(pad.id, material.id, "uses_material") ctx = get_entity_with_context(subsystem.id) assert ctx is not None assert len(ctx["relationships"]) == 2 assert pin.id in ctx["related_entities"] assert pad.id in ctx["related_entities"] def test_filter_entities_by_type_and_project(tmp_data_dir): init_db() init_engineering_schema() create_entity("component", "Pin A", project="p04-gigabit") create_entity("component", "Pin B", project="p04-gigabit") create_entity("material", "Steel", project="p04-gigabit") create_entity("component", "Actuator", project="p06-polisher") components = get_entities(entity_type="component", project="p04-gigabit") assert len(components) == 2 all_p04 = get_entities(project="p04-gigabit") assert len(all_p04) == 3 polisher = get_entities(project="p06-polisher") assert len(polisher) == 1 def test_invalid_entity_type_raises(tmp_data_dir): init_db() init_engineering_schema() with pytest.raises(ValueError, match="Invalid entity type"): create_entity("spaceship", "Enterprise") def test_invalid_relationship_type_raises(tmp_data_dir): init_db() init_engineering_schema() a = create_entity("component", "A") b = create_entity("component", "B") with pytest.raises(ValueError, match="Invalid relationship type"): create_relationship(a.id, b.id, "loves") def test_entity_name_search(tmp_data_dir): init_db() init_engineering_schema() create_entity("component", "Vertical Support Pad") create_entity("component", "Lateral Support Bracket") create_entity("component", "Reference Frame") results = get_entities(name_contains="Support") assert len(results) == 2 # --- Phase 5: Entity promote/reject lifecycle + audit + canonicalization --- def test_entity_project_canonicalization(tmp_data_dir): """Aliases resolve to canonical project_id on write (Phase 5).""" init_db() init_engineering_schema() # "p04" is a registered alias for p04-gigabit e = create_entity("component", "Test Component", project="p04") assert e.project == "p04-gigabit" def test_promote_entity_candidate_to_active(tmp_data_dir): from atocore.engineering.service import promote_entity, get_entity init_db() init_engineering_schema() e = create_entity("requirement", "CTE tolerance", status="candidate") assert e.status == "candidate" assert promote_entity(e.id, actor="test-triage") e2 = get_entity(e.id) assert e2.status == "active" def test_reject_entity_candidate(tmp_data_dir): from atocore.engineering.service import reject_entity_candidate, get_entity init_db() init_engineering_schema() e = create_entity("decision", "pick vendor Y", status="candidate") assert reject_entity_candidate(e.id, actor="test-triage", note="duplicate") e2 = get_entity(e.id) assert e2.status == "invalid" def test_promote_active_entity_noop(tmp_data_dir): from atocore.engineering.service import promote_entity init_db() init_engineering_schema() e = create_entity("component", "Already Active") # default status=active assert not promote_entity(e.id) # only candidates can promote def test_entity_audit_log_captures_lifecycle(tmp_data_dir): from atocore.engineering.service import ( promote_entity, get_entity_audit, ) init_db() init_engineering_schema() e = create_entity("requirement", "test req", status="candidate", actor="test") promote_entity(e.id, actor="test-triage", note="looks good") audit = get_entity_audit(e.id) actions = [a["action"] for a in audit] assert "created" in actions assert "promoted" in actions promote_entry = next(a for a in audit if a["action"] == "promoted") assert promote_entry["actor"] == "test-triage" assert promote_entry["note"] == "looks good" assert promote_entry["before"]["status"] == "candidate" assert promote_entry["after"]["status"] == "active" def test_new_relationship_types_available(tmp_data_dir): """Phase 5 added 6 missing relationship types.""" for rel in ["based_on_assumption", "supports", "conflicts_with", "updated_by_session", "evidenced_by", "summarized_in"]: assert rel in RELATIONSHIP_TYPES, f"{rel} missing from RELATIONSHIP_TYPES" def test_conflicts_tables_exist(tmp_data_dir): """Phase 5 conflict-model tables.""" from atocore.models.database import get_connection init_db() with get_connection() as conn: tables = {r[0] for r in conn.execute( "SELECT name FROM sqlite_master WHERE type='table'" ).fetchall()} assert "conflicts" in tables assert "conflict_members" in tables def test_memory_audit_has_entity_kind(tmp_data_dir): """Phase 5 added entity_kind discriminator.""" from atocore.models.database import get_connection init_db() with get_connection() as conn: cols = {r["name"] for r in conn.execute("PRAGMA table_info(memory_audit)").fetchall()} assert "entity_kind" in cols def test_graduated_status_accepted(tmp_data_dir): """Phase 5 added 'graduated' memory status for memory→entity transitions.""" from atocore.memory.service import MEMORY_STATUSES assert "graduated" in MEMORY_STATUSES