# License: Apache-2.0 License # Created by: Patrick Lumbantobing, VertoX-AI # Copyright (c) 2026 VertoX-AI. All rights reserved. # # This work is licensed under the Apache-2.0 License. # To view a copy of this license, visit # https://www.apache.org/licenses/LICENSE-2.0 from __future__ import annotations import math from pathlib import Path import pytest from speech_text_data_aligners.core.errors import AlignmentValidationError from speech_text_data_aligners.core.models import ( AlignmentProvenance, AlignmentRequest, AlignmentResult, LexicalWord, WordAlignment, ) from speech_text_data_aligners.core.normalization import lexicalize from speech_text_data_aligners.core.validation import ( find_path_aliases, validate_alignment_result, ) def make_pair() -> tuple[AlignmentRequest, AlignmentResult]: request = AlignmentRequest("r", Path("a.wav"), "One two", 2.0) words = lexicalize(request.transcript) result = AlignmentResult( "r", (WordAlignment(words[0], 0.0, 0.5), WordAlignment(words[1], 0.5, 1.0)), AlignmentProvenance("fake"), runtime_s=0.1, ) return request, result def test_validation_accepts_equality_and_single_word() -> None: request, result = make_pair() assert validate_alignment_result(request, result) is result single_request = AlignmentRequest("s", Path("s.wav"), "Solo", 1.0) word = lexicalize("Solo")[0] single_result = AlignmentResult( "s", (WordAlignment(word, 0.0, 1.0, 0.0),), AlignmentProvenance("fake") ) assert validate_alignment_result(single_request, single_result) is single_result def test_validation_rejects_identity_duration_runtime_and_count() -> None: request, result = make_pair() variants = ( (request, AlignmentResult("x", result.words, result.provenance)), (AlignmentRequest("r", Path("a"), "One two", 0.0), result), (AlignmentRequest("r", Path("a"), "One two", math.inf), result), (request, AlignmentResult("r", result.words, result.provenance, runtime_s=-1)), ( request, AlignmentResult("r", result.words, result.provenance, runtime_s=math.nan), ), (request, AlignmentResult("r", result.words[:1], result.provenance)), ) for candidate_request, candidate_result in variants: with pytest.raises(AlignmentValidationError): validate_alignment_result(candidate_request, candidate_result) def test_validation_rejects_word_mapping_and_all_timestamp_errors() -> None: request, result = make_pair() first, second = result.words wrong_word = LexicalWord(9, "wrong", 0, 5) variants = ( (WordAlignment(wrong_word, 0.0, 0.2), second), (WordAlignment(first.lexical_word, math.nan, 0.2), second), (WordAlignment(first.lexical_word, -0.1, 0.2), second), (WordAlignment(first.lexical_word, 0.3, 0.2), second), (first, WordAlignment(second.lexical_word, 0.5, 2.1)), ( WordAlignment(first.lexical_word, 0.0, 0.7), WordAlignment(second.lexical_word, 0.6, 1.0), ), (WordAlignment(first.lexical_word, 0.0, 0.5, math.inf), second), ) for words in variants: candidate = AlignmentResult("r", words, result.provenance) with pytest.raises(AlignmentValidationError): validate_alignment_result(request, candidate) def test_path_alias_detection_handles_prospective_normalized_and_existing_aliases( tmp_path: Path, ) -> None: source = tmp_path / "source.csv" source.write_text("source", encoding="utf-8") hard_link = tmp_path / "hard-link.csv" try: hard_link.hardlink_to(source) except OSError as error: pytest.skip(f"hard links are unavailable: {error}") prospective = tmp_path / "future" / ".." / "source.csv" distinct = tmp_path / "new.csv" assert find_path_aliases( { "source": source, "normalized": prospective, "hard link": hard_link, "distinct": distinct, } ) == ( ("source", "normalized"), ("source", "hard link"), ("normalized", "hard link"), ) def test_path_alias_detection_falls_back_when_resolution_is_unavailable( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: original_resolve = Path.resolve def fail_selected(path: Path, *, strict: bool = False) -> Path: if path.name == "unresolvable": raise OSError("resolution unavailable") return original_resolve(path, strict=strict) monkeypatch.setattr(Path, "resolve", fail_selected) path = tmp_path / "unresolvable" assert find_path_aliases({"left": path, "right": path}) == (("left", "right"),)