fix(acms): format __init__.py, add coverage for budget_enforcement paths
CI / load-versions (pull_request) Successful in 16s
CI / push-validation (pull_request) Successful in 25s
CI / lint (pull_request) Successful in 48s
CI / build (pull_request) Successful in 41s
CI / quality (pull_request) Successful in 59s
CI / typecheck (pull_request) Successful in 1m18s
CI / security (pull_request) Successful in 1m21s
CI / helm (pull_request) Successful in 44s
CI / unit_tests (pull_request) Successful in 5m44s
CI / docker (pull_request) Successful in 1m41s
CI / integration_tests (pull_request) Successful in 10m20s
CI / coverage (pull_request) Successful in 11m54s
CI / status-check (pull_request) Successful in 3s
CI / load-versions (pull_request) Successful in 16s
CI / push-validation (pull_request) Successful in 25s
CI / lint (pull_request) Successful in 48s
CI / build (pull_request) Successful in 41s
CI / quality (pull_request) Successful in 59s
CI / typecheck (pull_request) Successful in 1m18s
CI / security (pull_request) Successful in 1m21s
CI / helm (pull_request) Successful in 44s
CI / unit_tests (pull_request) Successful in 5m44s
CI / docker (pull_request) Successful in 1m41s
CI / integration_tests (pull_request) Successful in 10m20s
CI / coverage (pull_request) Successful in 11m54s
CI / status-check (pull_request) Successful in 3s
- Remove unnecessary parentheses from __all__ concatenation in acms/__init__.py to satisfy ruff format (lint gate was failing) - Mark unreachable RuntimeError branch in add_file with # pragma: no cover - Add 8 Behave scenarios covering previously uncovered lines: get_assembled_context(), reset(), BudgetEnforcer.__post_init__ validation errors, add_file validation errors, BudgetViolation unknown type - Add corresponding step definitions; import BudgetViolation in steps module ISSUES CLOSED: #9583
This commit is contained in:
@@ -71,3 +71,31 @@ Feature: ACMS Budget Enforcement for max_file_size and max_total_size constraint
|
||||
When I add a file with multi-byte UTF-8 content of 250 characters to the context
|
||||
Then the file should be excluded from the assembled context
|
||||
And a budget violation warning should be generated for the file
|
||||
|
||||
Scenario: Assembled context joins included file contents
|
||||
When I add a file of 100 bytes to the context
|
||||
And I add a file of 200 bytes to the context
|
||||
Then the assembled context should be non-empty
|
||||
|
||||
Scenario: Reset clears enforcer state for re-use
|
||||
When I add a file of 500 bytes to the context
|
||||
And I reset the budget enforcer
|
||||
Then the total context size should be 0 bytes
|
||||
|
||||
Scenario: BudgetEnforcer rejects negative max_file_size
|
||||
Then creating an enforcer with negative max_file_size raises ValueError
|
||||
|
||||
Scenario: BudgetEnforcer rejects zero max_total_size
|
||||
Then creating an enforcer with zero max_total_size raises ValueError
|
||||
|
||||
Scenario: BudgetEnforcer rejects max_file_size larger than max_total_size
|
||||
Then creating an enforcer with max_file_size larger than max_total_size raises ValueError
|
||||
|
||||
Scenario: add_file rejects empty filename
|
||||
Then adding a file with an empty name raises ValueError
|
||||
|
||||
Scenario: add_file rejects non-string content
|
||||
Then adding a file with non-string content raises TypeError
|
||||
|
||||
Scenario: BudgetViolation rejects unknown violation_type
|
||||
Then creating a BudgetViolation with an unknown type raises ValueError
|
||||
|
||||
@@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
|
||||
from behave import given, then, when
|
||||
|
||||
from cleveragents.acms.budget_enforcement import BudgetEnforcer
|
||||
from cleveragents.acms.budget_enforcement import BudgetEnforcer, BudgetViolation
|
||||
|
||||
|
||||
@given("a budget enforcer with max_file_size of {size:d} bytes")
|
||||
@@ -246,3 +246,78 @@ def step_both_files_included(context: object) -> None:
|
||||
"""Verify that both files were included."""
|
||||
files = context.budget_enforcer.get_included_files()
|
||||
assert len(files) == 2, f"Expected 2 files, got {len(files)}"
|
||||
|
||||
|
||||
@then("the assembled context should be non-empty")
|
||||
def step_assembled_context_non_empty(context: object) -> None:
|
||||
"""Verify the assembled context is non-empty."""
|
||||
ctx = context.budget_enforcer.get_assembled_context()
|
||||
assert len(ctx) > 0, "Expected non-empty assembled context"
|
||||
|
||||
|
||||
@when("I reset the budget enforcer")
|
||||
def step_reset_budget_enforcer(context: object) -> None:
|
||||
"""Reset the budget enforcer to its initial state."""
|
||||
context.budget_enforcer.reset()
|
||||
|
||||
|
||||
@then("creating an enforcer with negative max_file_size raises ValueError")
|
||||
def step_negative_max_file_size_raises(context: object) -> None:
|
||||
"""Verify ValueError is raised for negative max_file_size."""
|
||||
try:
|
||||
BudgetEnforcer(max_file_size=-1, max_total_size=5000)
|
||||
raise AssertionError("Expected ValueError for negative max_file_size")
|
||||
except ValueError as e:
|
||||
assert "max_file_size" in str(e), f"Error should mention max_file_size: {e}"
|
||||
|
||||
|
||||
@then("creating an enforcer with zero max_total_size raises ValueError")
|
||||
def step_zero_max_total_size_raises(context: object) -> None:
|
||||
"""Verify ValueError is raised for zero max_total_size."""
|
||||
try:
|
||||
BudgetEnforcer(max_file_size=100, max_total_size=0)
|
||||
raise AssertionError("Expected ValueError for zero max_total_size")
|
||||
except ValueError as e:
|
||||
assert "max_total_size" in str(e), f"Error should mention max_total_size: {e}"
|
||||
|
||||
|
||||
@then(
|
||||
"creating an enforcer with max_file_size larger than max_total_size raises ValueError"
|
||||
)
|
||||
def step_max_file_size_exceeds_total_raises(context: object) -> None:
|
||||
"""Verify ValueError is raised when max_file_size > max_total_size."""
|
||||
try:
|
||||
BudgetEnforcer(max_file_size=5000, max_total_size=1000)
|
||||
raise AssertionError("Expected ValueError for max_file_size > max_total_size")
|
||||
except ValueError as e:
|
||||
assert "cannot exceed" in str(e), f"Error should mention cannot exceed: {e}"
|
||||
|
||||
|
||||
@then("adding a file with an empty name raises ValueError")
|
||||
def step_empty_name_raises(context: object) -> None:
|
||||
"""Verify ValueError is raised for an empty filename."""
|
||||
try:
|
||||
context.budget_enforcer.add_file("", "some content")
|
||||
raise AssertionError("Expected ValueError for empty name")
|
||||
except ValueError as e:
|
||||
assert "name" in str(e).lower(), f"Error should mention name: {e}"
|
||||
|
||||
|
||||
@then("adding a file with non-string content raises TypeError")
|
||||
def step_non_string_content_raises(context: object) -> None:
|
||||
"""Verify TypeError is raised for non-string content."""
|
||||
try:
|
||||
context.budget_enforcer.add_file("file.txt", object())
|
||||
raise AssertionError("Expected TypeError for non-string content")
|
||||
except TypeError as e:
|
||||
assert "content" in str(e).lower(), f"Error should mention content: {e}"
|
||||
|
||||
|
||||
@then("creating a BudgetViolation with an unknown type raises ValueError")
|
||||
def step_unknown_violation_type_raises(context: object) -> None:
|
||||
"""Verify ValueError is raised for an unknown violation_type."""
|
||||
try:
|
||||
BudgetViolation(violation_type="unknown_type")
|
||||
raise AssertionError("Expected ValueError for unknown violation_type")
|
||||
except ValueError as e:
|
||||
assert "unknown_type" in str(e), f"Error should mention the bad type: {e}"
|
||||
|
||||
@@ -94,6 +94,4 @@ _index_exports = [
|
||||
_storage_exports = ["HotStorageTier"]
|
||||
_budget_exports = ["BudgetEnforcer", "BudgetViolation", "ContextFile"]
|
||||
|
||||
__all__: list[str] = (
|
||||
_uko_exports + _index_exports + _storage_exports + _budget_exports
|
||||
)
|
||||
__all__: list[str] = _uko_exports + _index_exports + _storage_exports + _budget_exports
|
||||
|
||||
@@ -115,7 +115,7 @@ class BudgetEnforcer:
|
||||
file_obj = ContextFile(name=name, content=content)
|
||||
|
||||
# Check max_file_size constraint
|
||||
if file_obj.size is None:
|
||||
if file_obj.size is None: # pragma: no cover
|
||||
raise RuntimeError(
|
||||
f"Internal error: {name} has no size; budget enforcement logic error"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user