Merge remote-tracking branch 'origin/main' into codex/telegram-routing-registry-20260714
This commit is contained in:
@@ -102,6 +102,93 @@ def test_declared_catalog_expands_to_193_unique_assets() -> None:
|
||||
assert matrix["operation_boundaries"]["raw_asset_key_exposed"] is False
|
||||
|
||||
|
||||
def test_public_projection_counts_live_only_assets_without_embedding_them() -> None:
|
||||
now = datetime(2026, 7, 15, 1, 0, tzinfo=UTC)
|
||||
live_assets = [
|
||||
{
|
||||
"asset_id": index + 1,
|
||||
"asset_key": f"zxq{index:x}v",
|
||||
"asset_type": "host",
|
||||
"name": f"zxq{index:x}v",
|
||||
"last_seen_at": now,
|
||||
"coverage": {},
|
||||
"compliance": {},
|
||||
"change_types": [],
|
||||
}
|
||||
for index in range(1_000)
|
||||
]
|
||||
|
||||
public_matrix = build_asset_capability_matrix(
|
||||
_single_host_scope(),
|
||||
live_assets=live_assets,
|
||||
repo_root=_REPO_ROOT,
|
||||
generated_at=now,
|
||||
include_live_only_assets=False,
|
||||
)
|
||||
reconciliation_matrix = build_asset_capability_matrix(
|
||||
_single_host_scope(),
|
||||
live_assets=live_assets,
|
||||
repo_root=_REPO_ROOT,
|
||||
generated_at=now,
|
||||
include_live_only_assets=True,
|
||||
)
|
||||
|
||||
assert len(public_matrix["assets"]) == 1
|
||||
public_summary = public_matrix["summary"]
|
||||
assert public_summary["live_inventory_asset_count"] == 1_000
|
||||
assert public_summary["matrix_asset_count"] == 1
|
||||
assert public_summary["reconciled_asset_count"] == 1_001
|
||||
assert public_summary["live_only_assets_embedded"] is False
|
||||
assert public_summary["embedded_live_only_asset_count"] == 0
|
||||
assert public_summary["discovered_not_declared_count"] == 1_000
|
||||
assert len(reconciliation_matrix["assets"]) == 1_001
|
||||
assert reconciliation_matrix["summary"]["live_only_assets_embedded"] is True
|
||||
assert reconciliation_matrix["summary"]["embedded_live_only_asset_count"] == 1_000
|
||||
assert (
|
||||
public_matrix["matrix_fingerprint"]
|
||||
== (reconciliation_matrix["matrix_fingerprint"])
|
||||
)
|
||||
|
||||
|
||||
def test_indexed_matching_preserves_score_cap_and_original_live_order() -> None:
|
||||
now = datetime(2026, 7, 15, 1, 0, tzinfo=UTC)
|
||||
scope = [
|
||||
{
|
||||
"id": "services",
|
||||
"label": "Services",
|
||||
"asset_count": 1,
|
||||
"asset_ids": ["one two three four five six seven"],
|
||||
"inventory_sources": ["service-inventory.json"],
|
||||
}
|
||||
]
|
||||
live_assets = [
|
||||
{
|
||||
"asset_id": 1,
|
||||
"asset_key": "candidate:first",
|
||||
"asset_type": "container",
|
||||
"name": "one two three four five six alpha",
|
||||
"last_seen_at": now,
|
||||
},
|
||||
{
|
||||
"asset_id": 2,
|
||||
"asset_key": "candidate:second",
|
||||
"asset_type": "container",
|
||||
"name": "one two three four five six seven beta",
|
||||
"last_seen_at": now,
|
||||
},
|
||||
]
|
||||
|
||||
matrix = build_asset_capability_matrix(
|
||||
scope,
|
||||
live_assets=live_assets,
|
||||
repo_root=_REPO_ROOT,
|
||||
generated_at=now,
|
||||
include_live_only_assets=False,
|
||||
)
|
||||
|
||||
assert matrix["assets"][0]["runtime_identity"]["asset_inventory_id"] == 1
|
||||
|
||||
|
||||
def test_live_asset_has_per_stage_covered_status_and_closes_after_receipt() -> None:
|
||||
now = datetime(2026, 7, 11, 12, 0, tzinfo=UTC)
|
||||
initial = build_asset_capability_matrix(
|
||||
@@ -318,6 +405,8 @@ class _FakeLiveReadbackDb:
|
||||
sql = str(statement)
|
||||
bound = params or {}
|
||||
self.calls.append((sql, bound))
|
||||
if "set_config('statement_timeout'" in sql:
|
||||
return _FakeResult([])
|
||||
if "FROM asset_discovery_run" in sql:
|
||||
return _FakeResult(
|
||||
[
|
||||
@@ -382,6 +471,13 @@ def test_live_change_event_readback_is_scoped_to_selected_asset_ids(
|
||||
|
||||
_latest_run, assets, _receipt = asyncio.run(_load_live_rows("awoooi"))
|
||||
|
||||
timeout_sql, timeout_params = next(
|
||||
(sql, params)
|
||||
for sql, params in db.calls
|
||||
if "set_config('statement_timeout'" in sql
|
||||
)
|
||||
assert "statement_timeout" in timeout_sql
|
||||
assert timeout_params == {"statement_timeout": "6000ms"}
|
||||
change_sql, change_params = next(
|
||||
(sql, params) for sql, params in db.calls if "FROM asset_change_event" in sql
|
||||
)
|
||||
@@ -413,3 +509,28 @@ def test_live_readback_timeout_fails_closed_without_hanging(
|
||||
assert payload["live_readback_timeout_seconds"] == 0.01
|
||||
assert payload["summary"]["declared_asset_count"] == 193
|
||||
assert payload["operation_boundaries"]["host_write_performed"] is False
|
||||
|
||||
|
||||
def test_matrix_build_phase_is_inside_the_total_timeout(monkeypatch: Any) -> None:
|
||||
async def _fast_live_readback(
|
||||
_project_id: str,
|
||||
) -> tuple[dict[str, Any], list[dict[str, Any]], dict[str, Any]]:
|
||||
return {}, [], {}
|
||||
|
||||
async def _slow_to_thread(*_args: Any, **_kwargs: Any) -> dict[str, Any]:
|
||||
await asyncio.sleep(1)
|
||||
return {}
|
||||
|
||||
monkeypatch.setattr(matrix_service, "_load_live_rows", _fast_live_readback)
|
||||
monkeypatch.setattr(matrix_service.asyncio, "to_thread", _slow_to_thread)
|
||||
|
||||
payload = asyncio.run(
|
||||
build_asset_capability_matrix_with_live_readback(
|
||||
repo_root=_REPO_ROOT,
|
||||
timeout_seconds=0.01,
|
||||
)
|
||||
)
|
||||
|
||||
assert payload["live_readback_status"] == "degraded"
|
||||
assert payload["live_readback_error_type"] == "TimeoutError"
|
||||
assert payload["live_readback_timeout_seconds"] == 0.01
|
||||
|
||||
Reference in New Issue
Block a user