Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions builders/server/core/runtime/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,15 @@ def validate(data: dict, schema: dict[str, SchemaType]) -> None:
)


def validate_rows(data_list: list[dict], schema: dict[str, SchemaType]) -> None:
def validate_rows(data_list: object, schema: dict[str, SchemaType]) -> None:
"""Validate each dict in a list against the declared schema."""
for data in data_list:
if not isinstance(data_list, list):
raise ValidationError("Builder output must be a list of rows")

for index, data in enumerate(data_list):
if not isinstance(data, dict):
raise ValidationError(
f"Builder output row {index} must be a dict, got "
f"'{type(data).__name__}'"
)
validate(data, schema)
13 changes: 13 additions & 0 deletions builders/server/tests/core/runtime/test_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,19 @@ def test_validate_rows_empty_list() -> None:
validate_rows([], {"ticker": SchemaType.STR})


@pytest.mark.parametrize("data_list", [None, {}])
def test_validate_rows_rejects_non_list_output(data_list: object) -> None:
"""Builder outputs must be lists of row dictionaries."""
with pytest.raises(ValidationError, match="must be a list of rows"):
validate_rows(data_list, {"ticker": SchemaType.STR})


def test_validate_rows_rejects_non_dict_item() -> None:
"""Every row in the builder output must be a dictionary."""
with pytest.raises(ValidationError, match="row 1 must be a dict"):
validate_rows([{"ticker": "AAPL"}, "MSFT"], {"ticker": SchemaType.STR})


def test_validate_rows_invalid_item_raises() -> None:
"""Invalid item in the list raises ValidationError."""
with pytest.raises(ValidationError, match="Missing key 'price'"):
Expand Down