fix(resource): use namespace column instead of name heuristic in db_to_spec() for built_in field #3266

Merged
freemo merged 1 commits from fix/resource-registry-db-to-spec-builtin-namespace-column into master 2026-04-05 21:13:57 +00:00
3 changed files with 77 additions and 1 deletions
@@ -57,3 +57,15 @@ Feature: Resource registry service coverage gaps
And I show the registered type with auto_discovery and equivalence
Then the shown type should have the correct auto_discovery
And the shown type should have the correct equivalence
# ── _db_to_spec built_in field via namespace column ───────────────
Scenario: _db_to_spec sets built_in True when namespace column is "builtin"
Given a resource type DB row with namespace "builtin" and name "git-checkout"
When I convert that row to a spec via _db_to_spec
Then the resulting spec built_in should be True
Scenario: _db_to_spec sets built_in False when namespace column is not "builtin"
Given a resource type DB row with namespace "local" and name "local/mytype"
When I convert that row to a spec via _db_to_spec
Then the resulting spec built_in should be False
@@ -456,3 +456,63 @@ def step_verify_rt_eq(context: Any) -> None:
assert eq is not None, "equivalence should be preserved"
assert eq["criteria"] == ["content_hash"]
assert eq["description"] == "Hash-based equivalence"
# ---------------------------------------------------------------------------
# Scenarios: _db_to_spec derives built_in from namespace column
# ---------------------------------------------------------------------------
@given('a resource type DB row with namespace "{namespace}" and name "{name}"')
def step_db_row_with_namespace_and_name(context: Any, namespace: str, name: str) -> None:
"""Create an in-memory DB row with the given namespace and name."""
engine, factory = _in_memory_session_factory()
context.ns_builtin_engine = engine
context.ns_builtin_factory = factory
session = factory()
from datetime import UTC, datetime
now = datetime.now(tz=UTC).isoformat()
row = ResourceTypeModel(
name=name,
namespace=namespace,
description="Test row for namespace-based built_in detection",
resource_kind="physical",
sandbox_strategy="none",
user_addable=False,
handler_ref=None,
args_schema_json=None,
allowed_parent_types_json=None,
allowed_child_types_json=None,
auto_discover_json=None,
capabilities_json=None,
equivalence_json=None,
source="test",
created_at=now,
updated_at=now,
)
session.add(row)
session.commit()
context.ns_builtin_row = (
session.query(ResourceTypeModel).filter_by(name=name).first()
)
@when("I convert that row to a spec via _db_to_spec")
def step_convert_ns_row_to_spec(context: Any) -> None:
context.ns_builtin_spec = _db_to_spec(context.ns_builtin_row)
@then("the resulting spec built_in should be True")
def step_verify_builtin_true(context: Any) -> None:
assert context.ns_builtin_spec.built_in is True, (
f"Expected built_in=True but got {context.ns_builtin_spec.built_in!r}"
)
@then("the resulting spec built_in should be False")
def step_verify_builtin_false(context: Any) -> None:
assert context.ns_builtin_spec.built_in is False, (
f"Expected built_in=False but got {context.ns_builtin_spec.built_in!r}"
)
@@ -401,7 +401,11 @@ def db_to_spec(row: ResourceTypeModel) -> ResourceTypeSpec:
equivalence = json.loads(str(raw_equiv))
name_str = str(row.name)
is_builtin = "/" not in name_str
# Derive built_in from the namespace column: built-in types have
# namespace="builtin". Fall back to the name heuristic only when the
# column is unavailable (e.g. legacy rows without a namespace column).
raw_ns = getattr(row, "namespace", None)
is_builtin = str(raw_ns) == "builtin" if raw_ns is not None else "/" not in name_str
raw_desc = getattr(row, "description", None)
desc_str = str(raw_desc) if raw_desc is not None else ""