import pytest from leanbench.schema import KIND_FACT, KIND_OBJECT, Fact, Goal, Role, Simplex def test_simplex_needs_two_roles(): only = Role("n", KIND_OBJECT) with pytest.raises(ValueError, match="at least 2"): Simplex("solo", {"n": only}) two = Simplex("pair", {"n": only, "h": Role("h", KIND_FACT)}) assert [r.name for r in two.by_kind(KIND_FACT)] == ["h"] def test_validation_runs_in_init_not_a_dead_post_init(): # The archived prototype defined __post_init__ beside a hand-written __init__, # so its minimum-role check never executed. Guard against that regression. with pytest.raises(ValueError): Simplex("empty", {}) def test_constrained_role_rejects_values_outside_choices(): role = Role("parity", KIND_OBJECT, choices=["even", "odd"]) assert role.is_constrained() assert role.validate_value("even") with pytest.raises(ValueError, match="not allowed"): role.check_value("prime") assert Role("free", KIND_OBJECT).validate_value(object()) def test_simplices_nest_and_walk_flattens(): inner = Simplex("inner", {"a": Role("a"), "b": Role("b")}) outer = Simplex("outer", {"inner": inner, "c": Role("c")}) assert sorted(r.name for r in outer.walk()) == ["a", "b", "c"] assert outer.as_dict()["roles"]["inner"]["roles"]["a"]["name"] == "a" def test_unknown_kind_and_empty_name_are_rejected(): with pytest.raises(ValueError): Role("x", "banana") with pytest.raises(ValueError): Role("") def test_states(): role = Role("n") assert not Goal(role).is_resolved() assert Fact(role, 3).is_resolved() with pytest.raises(ValueError): Fact(role, None)