Spaces:
Runtime error
Runtime error
Claude Opus 5
fix(qualite): ruff devient une vraie barriere, et trouve un schema mort et casse
7435134 Download tests/test_integration/test_article_extraction_integration.py from rahim237cm/jurix-api: direct link, hf CLI and curl.
- Browser
- Download file 9.07 kB
-
https://huggingface.co/spaces/rahim237cm/jurix-api/resolve/main/tests/test_integration/test_article_extraction_integration.py
- Command line
-
hf download hf://spaces/rahim237cm/jurix-api/tests/test_integration/test_article_extraction_integration.py
-
curl -L -o test_article_extraction_integration.py https://huggingface.co/spaces/rahim237cm/jurix-api/resolve/main/tests/test_integration/test_article_extraction_integration.py
9.07 kB
| """ | |
| Integration test for article extraction in document processing pipeline. | |
| Verifies that extract_articles utility can be imported and used | |
| as intended in the process_law task. | |
| """ | |
| import pytest | |
| from app.utils.text_chunker import extract_articles | |
| class TestArticleExtractionIntegration: | |
| """Test article extraction integration scenarios.""" | |
| def test_real_world_cameroonian_law_format(self): | |
| """Test extraction with realistic Cameroonian legal document format.""" | |
| # Simulated Cameroonian legal document | |
| law_text = """ | |
| LOI N° 2023-001 DU 15 JANVIER 2023 | |
| PORTANT RÉGIME DES SOCIÉTÉS COMMERCIALES AU CAMEROUN | |
| CHAPITRE I - DISPOSITIONS GÉNÉRALES | |
| Article 1. Objet de la loi | |
| La présente loi fixe le régime juridique applicable aux sociétés commerciales | |
| au Cameroun conformément aux dispositions de l'OHADA et aux spécificités nationales. | |
| Article 2. Champ d'application territorial | |
| Les dispositions de la présente loi s'appliquent sur l'ensemble du territoire | |
| de la République du Cameroun et concernent toutes les sociétés commerciales. | |
| Article 3. Définitions et terminologie | |
| Au sens de la présente loi, on entend par société commerciale toute personne | |
| morale constituée conformément aux dispositions du présent texte et de l'OHADA. | |
| CHAPITRE II - CONSTITUTION DES SOCIÉTÉS | |
| Article 4. Conditions de constitution | |
| Toute société commerciale doit être constituée par un acte écrit et immatriculée | |
| au registre du commerce et du crédit mobilier dans les conditions fixées par la loi. | |
| Article 5. Capital social minimum | |
| Le capital social minimum des sociétés commerciales est fixé par décret | |
| en fonction de la forme juridique et du secteur d'activité concerné. | |
| """ | |
| chunks = extract_articles(law_text) | |
| # Le premier chunk porte l'en-tete du texte (intitule de la loi), sous | |
| # le numero PREAMBULE ou LEGAL_BASIS : l'extraction ne jette aucun | |
| # caractere du document. Les articles proprement dits sont donc filtres. | |
| assert chunks[0]['number'] in ('PREAMBULE', 'LEGAL_BASIS') | |
| assert 'LOI N° 2023-001' in chunks[0]['content'] | |
| articles = [c for c in chunks if c['number'].isdigit()] | |
| # Verify extraction | |
| assert len(articles) == 5 | |
| assert [a['number'] for a in articles] == ['1', '2', '3', '4', '5'] | |
| # Verify structure | |
| assert articles[0]['title'] == 'Objet de la loi' | |
| assert 'OHADA' in articles[0]['content'] | |
| assert articles[3]['number'] == '4' | |
| assert articles[3]['title'] == 'Conditions de constitution' | |
| # La section est rattachee a chaque article | |
| assert articles[0]['section'].startswith('CHAPITRE I') | |
| assert articles[3]['section'].startswith('CHAPITRE II') | |
| # Verify all have required fields | |
| for article in articles: | |
| assert 'number' in article | |
| assert 'content' in article | |
| assert 'position' in article | |
| assert 'word_count' in article | |
| assert 'char_count' in article | |
| assert article['word_count'] > 0 | |
| assert article['char_count'] > 0 | |
| def test_short_document_is_extracted(self): | |
| """Un texte de deux articles est extrait, pas rejete.""" | |
| short_text = """ | |
| Article 1. Premier article | |
| Contenu du premier article avec suffisamment de texte pour validation. | |
| Article 2. Deuxième article seulement | |
| Contenu du deuxième article, court mais parfaitement valide. | |
| """ | |
| # L'ancienne regle "minimum 3 articles" a ete retiree du chunker, et | |
| # elle ne doit pas revenir : la majorite du corpus est faite de decrets | |
| # de nomination a deux articles ("Article 1er : est nomme... / Article | |
| # 2 : le present decret sera enregistre..."). La retablir refuserait | |
| # l'essentiel des documents. | |
| articles = extract_articles(short_text) | |
| assert [a['number'] for a in articles] == ['1', '2'] | |
| assert all(a['char_count'] > 0 for a in articles) | |
| def test_empty_document_raises(self): | |
| """Un document sans contenu exploitable leve toujours.""" | |
| with pytest.raises(ValueError): | |
| extract_articles(" \n \n ") | |
| def test_pipeline_processes_large_document(self): | |
| """Test that pipeline can handle large legal documents.""" | |
| # Generate a large document (100 articles) | |
| articles_content = [] | |
| for i in range(1, 101): | |
| articles_content.append( | |
| f"Article {i}. Titre de l'article {i}\n" | |
| f"Contenu de l'article {i} avec suffisamment de texte pour validation. " | |
| f"Les dispositions légales s'appliquent conformément aux normes." | |
| ) | |
| law_text = "\n\n".join(articles_content) | |
| articles = extract_articles(law_text) | |
| assert len(articles) == 100 | |
| assert articles[0]['number'] == '1' | |
| assert articles[99]['number'] == '100' | |
| # Verify positions are sequential | |
| positions = [a['position'] for a in articles] | |
| assert positions == list(range(100)) | |
| def test_database_ready_format(self): | |
| """Test that extracted articles are ready for database insertion.""" | |
| law_text = """ | |
| Article 1. Premier article | |
| Contenu du premier article avec suffisamment de texte. | |
| Article 2. Deuxième article | |
| Contenu du deuxième article avec suffisamment de texte. | |
| Article 3. Troisième article | |
| Contenu du troisième article avec suffisamment de texte. | |
| """ | |
| articles = extract_articles(law_text) | |
| # Verify database-ready format | |
| for article in articles: | |
| # All fields match database schema | |
| assert isinstance(article['number'], str) | |
| assert len(article['number']) <= 20 # VARCHAR(20) in DB | |
| if article['title']: | |
| assert isinstance(article['title'], str) | |
| assert len(article['title']) <= 200 # VARCHAR(200) in DB | |
| assert isinstance(article['content'], str) | |
| assert isinstance(article['position'], int) | |
| assert isinstance(article['word_count'], int) | |
| assert isinstance(article['char_count'], int) | |
| # parent_id is Optional[str] | |
| assert article['parent_id'] is None or isinstance(article['parent_id'], str) | |
| def test_hierarchical_articles_for_database(self): | |
| """Test hierarchical articles produce correct parent_id for database.""" | |
| law_text = """ | |
| Article 1. Article principal | |
| Contenu de l'article principal avec suffisamment de texte. | |
| Article 1.1. Premier sous-article | |
| Contenu du premier sous-article avec suffisamment de texte. | |
| Article 1.2. Deuxième sous-article | |
| Contenu du deuxième sous-article avec suffisamment de texte. | |
| Article 2. Autre article | |
| Contenu d'un autre article avec suffisamment de texte. | |
| """ | |
| articles = extract_articles(law_text) | |
| # Verify parent-child relationships | |
| assert articles[0]['number'] == '1' | |
| assert articles[0]['parent_id'] is None | |
| assert articles[1]['number'] == '1.1' | |
| assert articles[1]['parent_id'] == '1' | |
| assert articles[2]['number'] == '1.2' | |
| assert articles[2]['parent_id'] == '1' | |
| assert articles[3]['number'] == '2' | |
| assert articles[3]['parent_id'] is None | |
| def test_non_strict_mode_for_partial_documents(self): | |
| """Test non-strict mode for incomplete/draft documents.""" | |
| partial_text = """ | |
| Article 1. Premier article incomplet | |
| Contenu du premier article avec suffisamment de texte pour validation. | |
| Article 2. Deuxième article incomplet | |
| Contenu du deuxième article avec suffisamment de texte également. | |
| """ | |
| # Non-strict mode should accept <3 articles | |
| articles = extract_articles(partial_text, strict=False) | |
| assert len(articles) == 2 | |
| assert articles[0]['number'] == '1' | |
| assert articles[1]['number'] == '2' | |
| def test_performance_target_met(self): | |
| """Test that extraction meets <500ms performance target for 1000 articles.""" | |
| import time | |
| # Generate document with 100 articles (scaled test) | |
| articles_content = [] | |
| for i in range(1, 101): | |
| articles_content.append( | |
| f"Article {i}. Titre {i}\n" | |
| f"Contenu de l'article {i} avec du texte. " * 5 | |
| ) | |
| law_text = "\n\n".join(articles_content) | |
| start = time.time() | |
| articles = extract_articles(law_text) | |
| elapsed = time.time() - start | |
| assert len(articles) == 100 | |
| # 100 articles should complete in <100ms (scales to 1000 in <1s) | |
| assert elapsed < 0.5, f"Extraction took {elapsed:.3f}s (target: <0.5s for 100 articles)" | |