activity-core/tests/test_glas_profile_selection.py
tegwick b933cf52c8
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Container Image / build-and-push (push) Successful in 22s
Fail closed on invalid Glas profiles
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a028de-e2c8-7732-8521-46a7fc5db82f
2026-08-22 23:02:41 +02:00

174 lines
6.6 KiB
Python

"""Profile-driven execution selection (ACT-ADR-006 / ACTIVITY-WP-0032-T02)."""
from __future__ import annotations
import pytest
from activity_core.glas_profile import (
EXECUTION_REF_KEYS,
ProfileRefError,
normalise_execution_refs,
resolve_execution_selector,
validate_profile_ref,
)
class TestProfileRefStructure:
def test_accepts_a_pinned_ref(self) -> None:
assert (
validate_profile_ref("harness.agent-dev-local@1.0.0")
== "harness.agent-dev-local@1.0.0"
)
def test_strips_surrounding_whitespace(self) -> None:
assert validate_profile_ref(" harness.agent-dev@1.0.0\n") == "harness.agent-dev@1.0.0"
def test_requires_a_version_pin(self) -> None:
"""Glas resolves an unpinned ref against every version and refuses it
as ambiguous — catch that here instead, without knowing any profile."""
with pytest.raises(ProfileRefError, match="must pin a version"):
validate_profile_ref("harness.agent-dev-local")
@pytest.mark.parametrize("bad", ["", " ", None, 42, ["harness@1.0.0"]])
def test_rejects_empty_or_non_string(self, bad) -> None:
with pytest.raises(ProfileRefError):
validate_profile_ref(bad)
@pytest.mark.parametrize(
"bad",
["harness agent@1.0.0", "harness.agent@1.0 .0", "harness\tagent@1.0.0"],
)
def test_rejects_internal_whitespace(self, bad: str) -> None:
with pytest.raises(ProfileRefError, match="whitespace"):
validate_profile_ref(bad)
@pytest.mark.parametrize("bad", ["@1.0.0", "-harness@1.0.0", "har/ness@1.0.0"])
def test_rejects_invalid_ids(self, bad: str) -> None:
with pytest.raises(ProfileRefError, match="invalid id"):
validate_profile_ref(bad)
@pytest.mark.parametrize("bad", ["harness@", "harness@v1.0.0", "harness@latest"])
def test_rejects_invalid_versions(self, bad: str) -> None:
with pytest.raises(ProfileRefError, match="invalid version"):
validate_profile_ref(bad)
class TestNoFallbackToApproachHint:
"""ACT-ADR-006 §2 — the single most important property of this change.
approach_hint must never override, synthesize, or fall back from an absent
or invalid harness_profile_ref. A silent fallback would reintroduce the
claim-time routing failure of 2026-08-17.
"""
def test_hint_does_not_synthesize_a_missing_profile(self) -> None:
profile, hint = resolve_execution_selector(None, "sbom-rescan")
assert profile is None, "approach_hint must not become a profile ref"
assert hint == "sbom-rescan"
def test_hint_does_not_rescue_a_malformed_profile(self) -> None:
with pytest.raises(ProfileRefError, match="must pin a version"):
resolve_execution_selector("harness.agent-dev-local", "sbom-rescan")
def test_malformed_profile_raises_even_with_no_hint(self) -> None:
with pytest.raises(ProfileRefError):
resolve_execution_selector("not a ref", None)
def test_profile_does_not_overwrite_the_legacy_hint(self) -> None:
"""Coexistence, not replacement: both are carried."""
profile, hint = resolve_execution_selector(
"harness.agent-dev-local@1.0.0", "sbom-rescan"
)
assert profile == "harness.agent-dev-local@1.0.0"
assert hint == "sbom-rescan"
def test_absent_profile_is_allowed_during_coexistence(self) -> None:
assert resolve_execution_selector(None, None) == (None, None)
def test_absent_profile_is_an_error_once_required(self, monkeypatch) -> None:
monkeypatch.setenv("ACTIVITY_CORE_REQUIRE_HARNESS_PROFILE", "true")
with pytest.raises(ProfileRefError, match="cannot substitute"):
resolve_execution_selector(None, "sbom-rescan")
def test_required_mode_still_accepts_a_valid_profile(self, monkeypatch) -> None:
monkeypatch.setenv("ACTIVITY_CORE_REQUIRE_HARNESS_PROFILE", "1")
profile, _ = resolve_execution_selector("harness.agent-dev@2.1.0", None)
assert profile == "harness.agent-dev@2.1.0"
def test_unknown_but_well_formed_profile_is_left_for_glas(self) -> None:
profile, _ = resolve_execution_selector(
"harness.not-in-any-local-catalog@9.9.9",
None,
)
assert profile == "harness.not-in-any-local-catalog@9.9.9"
def test_blank_hint_is_normalised_away(self) -> None:
assert resolve_execution_selector(None, " ") == (None, None)
class TestExecutionRefs:
def test_keeps_the_contract_refs(self) -> None:
refs = normalise_execution_refs(
{
"correlation_id": "activity-42",
"assignment_ref": "role-assignment:agent-7:42",
"role_ref": "role:maintainer@3",
"duty_ref": "duty:bounded-change@2",
"goal_refs": ["goal:reliable-delivery@5"],
"resource_envelope_refs": ["resource-envelope:standard-coding@1"],
}
)
assert set(refs) == set(EXECUTION_REF_KEYS)
assert refs["goal_refs"] == ["goal:reliable-delivery@5"]
def test_drops_unknown_keys(self) -> None:
"""We carry the contract's refs; we do not become a bag for anything."""
refs = normalise_execution_refs(
{"role_ref": "role:maintainer@3", "api_key": "sk-nope", "extra": {"a": 1}}
)
assert refs == {"role_ref": "role:maintainer@3"}
def test_drops_empty_and_wrongly_typed_values(self) -> None:
refs = normalise_execution_refs(
{
"correlation_id": " ",
"role_ref": 17,
"goal_refs": "not-a-list",
"resource_envelope_refs": ["ok", "", " "],
}
)
assert refs == {"resource_envelope_refs": ["ok"]}
@pytest.mark.parametrize("bad", [None, "refs", ["a"], 5])
def test_non_dict_becomes_empty(self, bad) -> None:
assert normalise_execution_refs(bad) == {}
def test_ops_run_model_carries_the_new_columns() -> None:
from activity_core.orm import OpsRun
columns = OpsRun.__table__.columns
assert "harness_profile_ref" in columns
assert columns["harness_profile_ref"].nullable is True
assert "execution_refs" in columns
assert columns["execution_refs"].nullable is False
def test_queue_projection_exposes_selection_fields() -> None:
"""Executors claim from this projection; the profile must reach them."""
import inspect
from activity_core import ops_run_queue
source = inspect.getsource(ops_run_queue)
assert '"harness_profile_ref": row.harness_profile_ref' in source
assert '"execution_refs"' in source