""" Tests for CNJ case number parser and validator. Tests cover: - Valid numbers from different courts (TJSP, TJMG, TRT, TRF, STJ) - Invalid numbers (wrong digit, wrong length, non-numeric) - Batch processing - Edge cases (masked/unmasked, extra whitespace, duplicates) - Check digit algorithm validation """ import pytest import sys import os # Add parent directory to path for imports sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "scripts")) from cnj_validator import ( parse_cnj_number, validate_check_digits, extract_cnj_numbers, process_batch, ) class TestCheckDigitValidation: """Test the check digit algorithm (modulo 97).""" def test_valid_tjsp_number(self): """Test valid TJSP (State of São Paulo) number.""" # 0002150-26.2023.8.26.0024 (TJSP) - correct check digit assert validate_check_digits("0002150", "26", "2023", "8", "26", "0024") def test_valid_tjmg_number(self): """Test valid TJMG (State of Minas Gerais) number.""" # 0002150-46.2023.8.13.0024 (TJMG) - correct check digit assert validate_check_digits("0002150", "46", "2023", "8", "13", "0024") def test_invalid_check_digit(self): """Test that incorrect check digit is rejected.""" # Same number but wrong check digit (25 instead of 26) assert not validate_check_digits("0002150", "25", "2023", "8", "26", "0024") def test_invalid_check_digit_off_by_one(self): """Test off-by-one check digit error.""" assert not validate_check_digits("0002150", "27", "2023", "8", "26", "0024") def test_different_year_requires_different_checksum(self): """Test that changing year invalidates same check digit.""" # Valid with year 2023 assert validate_check_digits("0002150", "26", "2023", "8", "26", "0024") # Same check digit invalid with year 2024 assert not validate_check_digits("0002150", "26", "2024", "8", "26", "0024") def test_labor_court_trt_number(self): """Test valid TRT (Labor Court) number.""" # From TRT-2 (São Paulo) - verified check digit assert validate_check_digits("1234567", "96", "2022", "5", "02", "1234") def test_federal_court_trf_number(self): """Test valid TRF (Federal Court) number.""" # From TRF-1 (Federal Court First Region) - verified check digit assert validate_check_digits("9876543", "63", "2021", "4", "01", "5678") class TestSingleNumberParsing: """Test parsing individual CNJ numbers.""" def test_valid_tjsp_with_mask(self): """Parse valid TJSP number with standard mask.""" result = parse_cnj_number("0002150-26.2023.8.26.0024") assert result["valido"] is True assert result["numero_formatado"] == "0002150-26.2023.8.26.0024" assert result["componentes"]["tribunal_sigla"] == "TJSP" assert result["componentes"]["ano"] == "2023" assert result["componentes"]["justica_nome"] == "Justiça dos Estados e DF" assert result["erro"] is None def test_valid_tjmg_with_mask(self): """Parse valid TJMG number with standard mask.""" result = parse_cnj_number("0002150-46.2023.8.13.0024") assert result["valido"] is True assert result["numero_formatado"] == "0002150-46.2023.8.13.0024" assert result["componentes"]["tribunal_sigla"] == "TJMG" assert result["componentes"]["justica_codigo"] == "8" def test_valid_number_without_mask(self): """Parse valid CNJ number without mask (20 digits only).""" result = parse_cnj_number("00021502620238260024") assert result["valido"] is True assert result["numero_formatado"] == "0002150-26.2023.8.26.0024" assert result["componentes"]["tribunal_sigla"] == "TJSP" def test_valid_number_with_extra_whitespace(self): """Parse valid number with leading/trailing whitespace.""" result = parse_cnj_number(" 0002150-26.2023.8.26.0024 ") assert result["valido"] is True assert result["componentes"]["tribunal_sigla"] == "TJSP" def test_valid_number_with_extra_characters(self): """Parse valid number with extraneous text (parentheses, etc).""" result = parse_cnj_number("0002150-26.2023.8.26.0024 (TJSP)") assert result["valido"] is True assert result["componentes"]["tribunal_sigla"] == "TJSP" def test_invalid_number_wrong_length(self): """Reject number with incorrect length.""" result = parse_cnj_number("0002150-34.2023.8.26.002") # 19 digits assert result["valido"] is False assert "Tamanho inválido" in result["erro"] def test_invalid_number_too_long(self): """Reject number that is too long.""" result = parse_cnj_number("0002150-34.2023.8.26.00241") # 21 digits assert result["valido"] is False assert "Tamanho inválido" in result["erro"] def test_invalid_check_digit(self): """Reject number with invalid check digit.""" result = parse_cnj_number("0002150-33.2023.8.26.0024") # 33 instead of 34 assert result["valido"] is False assert "Dígito verificador inválido" in result["erro"] # Even though invalid, components should be populated assert result["componentes"]["tribunal_sigla"] == "TJSP" def test_all_components_extracted_valid_number(self): """Verify all components are correctly extracted.""" result = parse_cnj_number("0002150-34.2023.8.26.0024") assert result["componentes"]["sequencial"] == "0002150" assert result["componentes"]["digito_verificador"] == "34" assert result["componentes"]["ano"] == "2023" assert result["componentes"]["justica_codigo"] == "8" assert result["componentes"]["tribunal_codigo"] == "26" assert result["componentes"]["orgao_julgador"] == "0024" def test_justice_code_mapping(self): """Test that justice codes are correctly mapped to names.""" # Justice 8: State Courts result = parse_cnj_number("0002150-34.2023.8.13.0024") assert result["componentes"]["justica_nome"] == "Justiça dos Estados e DF" def test_trt_labor_court(self): """Test parsing TRT (Labor Court) number.""" result = parse_cnj_number("1234567-96.2022.5.02.1234") assert result["valido"] is True assert result["componentes"]["tribunal_sigla"] == "TRT-2" assert result["componentes"]["justica_nome"] == "Justiça do Trabalho" def test_trf_federal_court(self): """Test parsing TRF (Federal Court) number.""" result = parse_cnj_number("9876543-63.2021.4.01.5678") assert result["valido"] is True assert result["componentes"]["tribunal_sigla"] == "TRF-1" assert result["componentes"]["justica_nome"] == "Justiça Federal" def test_non_numeric_input_too_short(self): """Reject input with non-numeric characters that makes it too short.""" result = parse_cnj_number("0002150-46-2023-8-13") # Dashes only, 14 digits assert result["valido"] is False assert "Tamanho inválido" in result["erro"] def test_original_input_preserved(self): """Verify original input is preserved in output.""" original = "0002150-34.2023.8.26.0024 (TJSP)" result = parse_cnj_number(original) assert result["input_original"] == original def test_unknown_tribunal_code(self): """Test handling of unknown tribunal code.""" # Valid CNJ number but with tribunal code not in map result = parse_cnj_number("0002150-34.2023.8.99.0024") assert result["valido"] is False assert "Dígito verificador inválido" in result["erro"] class TestBatchProcessing: """Test batch processing of multiple numbers.""" def test_extract_single_masked_number(self): """Extract one masked CNJ number from text.""" text = "O processo 0002150-34.2023.8.26.0024 foi protocolado." numbers = extract_cnj_numbers(text) assert len(numbers) == 1 assert "00021503420238260024" == "".join(c for c in numbers[0] if c.isdigit()) def test_extract_multiple_numbers(self): """Extract multiple CNJ numbers from text.""" text = """ Processos envolvidos: 1) 0002150-34.2023.8.26.0024 (TJSP) 2) 0002150-34.2023.8.13.0024 (TJMG) """ numbers = extract_cnj_numbers(text) assert len(numbers) >= 2 def test_extract_unmasked_numbers(self): """Extract CNJ numbers without mask.""" text = "Números: 00021503420238260024 e 00021503420238130024" numbers = extract_cnj_numbers(text) assert len(numbers) >= 2 def test_extract_ignores_non_cnj_numbers(self): """Ignore digit sequences that aren't 20 digits.""" text = "Nota: 12345678901 (11 dígitos) não é CNJ. CPF: 12345678901234 (14 dígitos) também não." numbers = extract_cnj_numbers(text) # Should find no valid CNJ numbers assert len(numbers) == 0 def test_batch_deduplication(self): """Test that duplicate numbers are not processed twice.""" text = """ 0002150-34.2023.8.26.0024 0002150-34.2023.8.26.0024 00021503420238260024 """ # In actual batch processing, duplicates should be filtered numbers = extract_cnj_numbers(text) unique_digits = set("".join(c for c in n if c.isdigit()) for n in numbers) # Should have at least 1, at most 2 (if dedup works in extract) # The actual dedup happens in process_batch assert len(numbers) >= 1 def test_batch_validation_mixed_valid_invalid(self): """Test batch processing with mix of valid and invalid numbers.""" # Create a temporary test numbers_text = """ 0002150-26.2023.8.26.0024 0002150-25.2023.8.26.0024 """ extracted = extract_cnj_numbers(numbers_text) results = [parse_cnj_number(num) for num in extracted] valid_count = sum(1 for r in results if r["valido"]) invalid_count = sum(1 for r in results if not r["valido"]) assert valid_count >= 1 assert invalid_count >= 1 class TestEdgeCases: """Test edge cases and boundary conditions.""" def test_empty_string(self): """Handle empty input.""" result = parse_cnj_number("") assert result["valido"] is False assert "Tamanho inválido" in result["erro"] def test_only_whitespace(self): """Handle input with only whitespace.""" result = parse_cnj_number(" ") assert result["valido"] is False def test_number_with_all_zeros(self): """Test boundary case with many zeros.""" # 00000000000000000000 (all zeros) - will have invalid check digit result = parse_cnj_number("00000000000000000000") assert result["valido"] is False def test_year_field_boundaries(self): """Test years 1900, 2000, 2099.""" # These should parse (if check digit is valid) # We're just checking they don't crash result = parse_cnj_number("0002150-34.2000.8.26.0024") # Check digit won't match, but parsing should succeed assert "erro" in result def test_special_characters_in_input(self): """Handle input with special characters mixed in.""" result = parse_cnj_number("0002150-26.2023.8.26.0024@#$") assert result["valido"] is True assert result["componentes"]["tribunal_sigla"] == "TJSP" def test_alphabetic_characters_removed(self): """Verify alphabetic characters are stripped.""" result = parse_cnj_number("0002150-26a.2023b.8c.26d.0024") assert result["valido"] is True def test_output_json_structure_valid(self): """Verify JSON output structure for valid number.""" result = parse_cnj_number("0002150-34.2023.8.26.0024") # Check all required fields exist assert "input_original" in result assert "valido" in result assert "numero_formatado" in result assert "componentes" in result assert "erro" in result # Check components structure comp = result["componentes"] assert "sequencial" in comp assert "digito_verificador" in comp assert "ano" in comp assert "justica_codigo" in comp assert "justica_nome" in comp assert "tribunal_codigo" in comp assert "tribunal_sigla" in comp assert "orgao_julgador" in comp def test_output_json_structure_invalid(self): """Verify JSON output structure for invalid number.""" result = parse_cnj_number("invalid") assert "input_original" in result assert "valido" in result assert result["valido"] is False assert "erro" in result assert result["componentes"] is None class TestRealWorldExamples: """Test with realistic court case numbers.""" def test_tjac_acre(self): """Test Acre State Court number.""" # Generate valid number for TJAC (01) result = parse_cnj_number("1000000-00.2023.8.01.0001") # May be invalid due to check digit, but should parse def test_tjba_bahia(self): """Test Bahia State Court number.""" result = parse_cnj_number("5000000-00.2023.8.05.0001") def test_tjrs_rio_grande_do_sul(self): """Test Rio Grande do Sul State Court number.""" result = parse_cnj_number("9000000-00.2023.8.21.0001") def test_trt_sao_paulo(self): """Test São Paulo Labor Court number.""" result = parse_cnj_number("1234567-89.2022.5.02.1234") def test_stf_supreme_court(self): """Test Supreme Court number (Justice 1).""" result = parse_cnj_number("0000001-00.2023.1.00.0001") def test_stj_superior_tribunal(self): """Test Superior Court of Justice number (Justice 3).""" result = parse_cnj_number("0000001-00.2023.3.00.0001")