Skip to content
Merged
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
64 changes: 64 additions & 0 deletions tests/test_ingestor_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,70 @@ def test_validate_data_validator_exception_raises():
ing.validate_data("src")


# ---------------------------------------------------------------------------
# validator label normalization (#396 Bugbot: log labels drop suffix)
# ---------------------------------------------------------------------------


@pytest.mark.parametrize(
"name, expected",
[
# Already ends in "Validator" — must NOT double it.
("Data Validator", "Data Validator"),
# Case-insensitive on the existing suffix.
("Pascal VOC XML validator", "Pascal VOC XML validator"),
# Missing the suffix — must gain it.
("Ingestable Records", "Ingestable Records Validator"),
("Text Content", "Text Content Validator"),
("BIO Label", "BIO Label Validator"),
("Keypoint Annotation", "Keypoint Annotation Validator"),
# Surrounding whitespace is stripped before deciding.
(" Data Validator ", "Data Validator"),
],
)
def test_validator_label_normalizes(name, expected):
assert base_mod._validator_label(name) == expected


def test_validate_data_success_line_keeps_suffix_when_missing(capsys):
# A name that does NOT end in "Validator" (e.g. "Ingestable Records") must
# keep the suffix in its "successfully passed" line.
ing = make_ingestor(category=None)
v = MagicMock()
v.name = "Ingestable Records"
v.validate.return_value = ValidationResult(True, [], [], {})
with patch.object(base_mod, "map_validators", return_value=[v]):
assert ing.validate_data("src") is True
out = capsys.readouterr().out
assert "Ingestable Records Validator successfully passed" in out


def test_validate_data_success_line_does_not_double_suffix(capsys):
# A name that already ends in "Validator" (e.g. "Data Validator") must not
# double the word.
ing = make_ingestor(category=None)
v = MagicMock()
v.name = "Data Validator"
v.validate.return_value = ValidationResult(True, [], [], {})
with patch.object(base_mod, "map_validators", return_value=[v]):
assert ing.validate_data("src") is True
out = capsys.readouterr().out
assert "Data Validator successfully passed" in out
assert "Data Validator Validator" not in out


def test_validate_data_failure_message_keeps_suffix_when_missing():
# The error-append line normalizes too, so a suffix-less name isn't stripped.
ing = make_ingestor(category=None)
v = MagicMock()
v.name = "Ingestable Records"
v.validate.return_value = ValidationResult(False, ["nope"], [], {})
with patch.object(base_mod, "map_validators", return_value=[v]):
with pytest.raises(ValueError) as exc:
ing.validate_data("src")
assert "Ingestable Records Validator failed" in str(exc.value)


# ---------------------------------------------------------------------------
# ingest (full flow, Session patched)
# ---------------------------------------------------------------------------
Expand Down
32 changes: 24 additions & 8 deletions tracebloc_ingestor/ingestors/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,20 @@ def _rows_state_clause(inserted_records: int) -> str:
return "no rows were ingested, so nothing was left in the database"


def _validator_label(name: str) -> str:
"""Human-readable label for a validator's preflight log/error lines.

Validator ``.name`` values are inconsistent: some already end in
"Validator" (e.g. "Data Validator") while many do not (e.g. "Ingestable
Records", "Text Content", "BIO Label", "Keypoint Annotation"). Normalize at
the log site so every label reads "<Name> Validator" exactly once —
appending the word only when it's missing, so suffixed names don't double
("Data Validator Validator") and unsuffixed names don't drop it.
"""
name = (name or "").strip()
return name if name.lower().endswith("validator") else f"{name} Validator"


# NOTE: _TABULAR_FAMILY_CATEGORIES and _FILE_BEARING_CATEGORIES are imported
# above from modalities.registry (derived from the per-category ModalitySpec
# flags — backend#796 P3a). The ``self.category in <set>`` checks throughout
Expand Down Expand Up @@ -545,31 +559,33 @@ def validate_data(self, source: Any) -> bool:
validation_errors = []

for validator in validators:
label = _validator_label(validator.name)
try:
logger.info(f"{CYAN}Running validator: {validator.name}{RESET}")
logger.info(f"{CYAN}Running validator: {label}{RESET}")
result = validator.validate(source)

if not result.is_valid:
all_valid = False
validation_errors.append(
f"{BOLD}{validator.name} failed: {RESET} \n {RED}"
f"{BOLD}{label} failed: {RESET} \n {RED}"
)
validation_errors.extend(result.errors)
validation_errors.append(f"{RESET}")

# Log warnings if any
for warning in result.warnings:
logger.warning(
f"{YELLOW}Validation warning - {validator.name}: {warning}{RESET}"
f"{YELLOW}Validation warning - {label}: {warning}{RESET}"
)
if result.is_valid:
# validator.name already ends in "Validator" (e.g. "Data
# Validator"), so don't append the word again — that produced
# "Data Validator Validator successfully passed".
print(f"{GREEN}{validator.name} successfully passed{RESET}")
# Normalize the label so it always reads "<Name> Validator"
# exactly once: names that already end in "Validator" (e.g.
# "Data Validator") don't double, and names that don't (e.g.
# "Ingestable Records") keep the suffix.
print(f"{GREEN}{label} successfully passed{RESET}")
except Exception as e:
all_valid = False
validation_errors.append(f"{validator.name} error: {str(e)}")
validation_errors.append(f"{label} error: {str(e)}")

if not all_valid:
error_summary = "\n".join(validation_errors)
Expand Down
Loading