Skip to content

Commit 6a9dbe8

Browse files
fix: corrige TypeError em validate_secs e validate_lists para elementos sem atributos obrigatórios (#1227) (#1230)
* bump(version): incrementa versão de 4.16.5 para 4.16.6 - Atualiza __version__ em packtools/version.py de '4.16.5' para '4.16.6' - Versão incrementada em patch para refletir a correção de bug nas funções validate_secs e validate_lists (issue #1227) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(xml_validations): corrige validate_secs, validate_lists e validate_response que não retornavam resultados Problema (issue #1227): - As funções validate_secs, validate_lists e validate_response em xml_validations.py instanciavam o validador correspondente mas nunca chamavam yield from validator.validate(), tornando-as funções regulares que retornavam None em vez de generators - Ao tentar iterar sobre o valor retornado (None), o orquestrador de validação lançava TypeError: 'NoneType' object is not iterable - Adicionalmente, validate_secs e validate_lists acessavam params["sec_rules"] e params["list_rules"] diretamente, o que lançava KeyError quando as chaves estavam ausentes no dicionário de parâmetros Correções aplicadas: - Adicionado `yield from validator.validate()` em validate_lists, validate_response e validate_secs, tornando-as generators válidos - Substituído params["list_rules"] por params.get("list_rules", {}) para evitar KeyError quando a chave não está presente - Substituído params["sec_rules"] por params.get("sec_rules", {}) pelo mesmo motivo Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(test_list): adiciona testes de regressão para validate_lists em xml_validations Contexto (issue #1227): - Não havia testes para a função wrapper validate_lists de xml_validations.py, apenas para a classe ArticleListValidation diretamente - A ausência de cobertura permitiu que o bug (função retornando None) passasse despercebido Testes adicionados na classe TestValidateListsWrapper: - test_list_without_list_type_returns_structured_result: garante que <list> sem @list-type retorna resultado estruturado com response != "OK" em vez de lançar TypeError (cenário exato reportado na issue) - test_list_with_valid_list_type_returns_ok: garante que <list list-type="bullet"> retorna response "OK" para a validação de presença do atributo - test_empty_params_does_not_raise: garante que params vazio ({}) não lança KeyError ao chamar validate_lists - test_no_lists_returns_empty: garante que XML sem elementos <list> retorna um iterável vazio Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(test_sec): adiciona testes de regressão para validate_secs em xml_validations Contexto (issue #1227): - Não havia testes para a função wrapper validate_secs de xml_validations.py, apenas para as classes SecValidation e XMLSecValidation diretamente - A ausência de cobertura permitiu que o bug (função retornando None) passasse despercebido Testes adicionados na classe TestValidateSecsWrapper: - test_sec_without_title_returns_structured_result: garante que <sec> sem <title> retorna resultado estruturado com response != "OK" em vez de lançar TypeError (cenário exato reportado na issue) - test_sec_with_title_returns_ok: garante que <sec> com <title> retorna response "OK" para a regra de validação de título - test_empty_params_does_not_raise: garante que params vazio ({}) não lança KeyError ao chamar validate_secs - test_no_secs_returns_empty: garante que XML do tipo "editorial" (sem obrigatoriedade de seções) sem elementos <sec> retorna vazio; usa article-type="editorial" para evitar falso positivo de data-availability que afetaria article-type="research-article" Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(xml_validations): reverte para acesso direto params["list_rules"] e params["sec_rules"] Conforme revisão da robertatakenaka: params.get("list_rules", {}) e params.get("sec_rules", {}) causavam execução dos validadores sem as regras configuradas (usando apenas os defaults internos das classes) quando as chaves estavam ausentes. O acesso direto torna explícito que list_rules e sec_rules são obrigatórios no dict de parâmetros — o que sempre ocorre na rota normal via get_default_rules(). Os testes de regressão do wrapper foram atualizados para usar get_default_rules() em vez de {} como params, garantindo que a validação seja exercitada com as regras reais de produção (list_rules.json e sec_rules.json). O teste test_empty_params_does_not_raise foi substituído por test_validate_*_does_not_raise_typeerror, que testa a regressão correta do issue #1227 (TypeError por generator ausente). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent aec2af1 commit 6a9dbe8

4 files changed

Lines changed: 123 additions & 4 deletions

File tree

packtools/sps/validation/xml_validations.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,8 @@ def validate_ext_links(xmltree, params):
377377
def validate_lists(xmltree, params):
378378
rules = params["list_rules"]
379379
validator = ArticleListValidation(xmltree, rules)
380-
380+
yield from validator.validate()
381+
381382

382383
def validate_graphics(xmltree, params):
383384
"""
@@ -410,7 +411,8 @@ def validate_response(xmltree, params):
410411
"""
411412
response_rules = params.get("response_rules", {})
412413
validator = ResponseValidation(xmltree, response_rules)
413-
414+
yield from validator.validate()
415+
414416

415417
def validate_secs(xmltree, params):
416418
"""
@@ -427,7 +429,8 @@ def validate_secs(xmltree, params):
427429
"""
428430
sec_rules = params["sec_rules"]
429431
validator = XMLSecValidation(xmltree, sec_rules)
430-
432+
yield from validator.validate()
433+
431434

432435
def validate_products(xmltree, params):
433436
"""

packtools/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
"""Single source to the version across setup.py and the whole project.
22
"""
33
from __future__ import unicode_literals
4-
__version__ = '4.16.5'
4+
__version__ = '4.16.6'

tests/sps/validation/test_list.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
from lxml import etree
33

44
from packtools.sps.validation.list import ArticleListValidation
5+
from packtools.sps.validation.xml_validations import validate_lists
6+
from packtools.sps.validation.xml_validator_rules import get_default_rules
57

68

79
class ListValidationTest(unittest.TestCase):
@@ -416,5 +418,64 @@ def test_list_validation_with_title_present(self):
416418
self.assertIn("<title> present", title_validation["got_value"])
417419

418420

421+
class TestValidateListsWrapper(unittest.TestCase):
422+
"""Tests for validate_lists() in xml_validations — regression para issue #1227."""
423+
424+
def setUp(self):
425+
self.params = get_default_rules()
426+
427+
def _make_tree(self, body):
428+
xml = (
429+
'<article xmlns:xlink="http://www.w3.org/1999/xlink"'
430+
' dtd-version="1.0" article-type="research-article" xml:lang="pt">'
431+
f"{body}</article>"
432+
)
433+
return etree.fromstring(xml.encode())
434+
435+
def test_list_without_list_type_returns_structured_result(self):
436+
"""<list> sem @list-type deve retornar resultado estruturado, não TypeError."""
437+
tree = self._make_tree(
438+
"<body><list>"
439+
"<list-item><p>Item 1.</p></list-item>"
440+
"<list-item><p>Item 2.</p></list-item>"
441+
"</list></body>"
442+
)
443+
results = list(validate_lists(tree, self.params))
444+
self.assertTrue(len(results) > 0)
445+
presence_results = [r for r in results if r["title"] == "@list-type presence"]
446+
self.assertEqual(len(presence_results), 1)
447+
self.assertNotEqual(presence_results[0]["response"], "OK")
448+
449+
def test_list_with_valid_list_type_returns_ok(self):
450+
"""<list list-type='bullet'> deve retornar OK para presença e valor."""
451+
tree = self._make_tree(
452+
'<body><list list-type="bullet">'
453+
"<list-item><p>Item 1.</p></list-item>"
454+
"<list-item><p>Item 2.</p></list-item>"
455+
"</list></body>"
456+
)
457+
results = list(validate_lists(tree, self.params))
458+
presence_results = [r for r in results if r["title"] == "@list-type presence"]
459+
self.assertEqual(presence_results[0]["response"], "OK")
460+
461+
def test_validate_lists_does_not_raise_typeerror(self):
462+
"""validate_lists deve ser um generator válido, não lançar TypeError (regressão #1227)."""
463+
tree = self._make_tree(
464+
"<body><list>"
465+
"<list-item><p>Item.</p></list-item>"
466+
"</list></body>"
467+
)
468+
try:
469+
list(validate_lists(tree, self.params))
470+
except TypeError as exc:
471+
self.fail(f"validate_lists lançou TypeError: {exc}")
472+
473+
def test_no_lists_returns_empty(self):
474+
"""XML sem <list> deve retornar iterável vazio."""
475+
tree = self._make_tree("<body><p>Sem listas.</p></body>")
476+
results = list(validate_lists(tree, self.params))
477+
self.assertEqual(results, [])
478+
479+
419480
if __name__ == "__main__":
420481
unittest.main()

tests/sps/validation/test_sec.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
from packtools.sps.models.sec import ArticleSecs
1818
from packtools.sps.validation.sec import SecValidation, XMLSecValidation
19+
from packtools.sps.validation.xml_validations import validate_secs
20+
from packtools.sps.validation.xml_validator_rules import get_default_rules
1921

2022

2123
class TestSecValidationTitle(unittest.TestCase):
@@ -785,5 +787,58 @@ def test_body_sec_types(self):
785787
self.assertEqual(sec_types, ["intro", "methods"])
786788

787789

790+
class TestValidateSecsWrapper(unittest.TestCase):
791+
"""Tests for validate_secs() in xml_validations — regression for issue #1227."""
792+
793+
def setUp(self):
794+
self.params = get_default_rules()
795+
796+
def _make_tree(self, body):
797+
xml = (
798+
'<article xmlns:xlink="http://www.w3.org/1999/xlink"'
799+
' article-type="research-article" xml:lang="pt">'
800+
f"{body}</article>"
801+
)
802+
return etree.fromstring(xml.encode())
803+
804+
def test_sec_without_title_returns_structured_result(self):
805+
"""<sec> sem <title> deve retornar resultado estruturado, não TypeError."""
806+
tree = self._make_tree("<body><sec><p>Texto sem titulo.</p></sec></body>")
807+
results = list(validate_secs(tree, self.params))
808+
self.assertTrue(len(results) > 0)
809+
title_results = [r for r in results if r["title"] == "sec title"]
810+
self.assertEqual(len(title_results), 1)
811+
self.assertNotEqual(title_results[0]["response"], "OK")
812+
813+
def test_sec_with_title_returns_ok(self):
814+
"""<sec> com <title> deve retornar OK para a regra de título."""
815+
tree = self._make_tree(
816+
"<body><sec><title>Intro</title><p>Conteudo.</p></sec></body>"
817+
)
818+
results = list(validate_secs(tree, self.params))
819+
title_results = [r for r in results if r["title"] == "sec title"]
820+
self.assertEqual(len(title_results), 1)
821+
self.assertEqual(title_results[0]["response"], "OK")
822+
823+
def test_validate_secs_does_not_raise_typeerror(self):
824+
"""validate_secs deve ser um generator válido, não lançar TypeError (regressão #1227)."""
825+
tree = self._make_tree("<body><sec><p>Texto.</p></sec></body>")
826+
try:
827+
list(validate_secs(tree, self.params))
828+
except TypeError as exc:
829+
self.fail(f"validate_secs lançou TypeError: {exc}")
830+
831+
def test_no_secs_returns_empty(self):
832+
"""XML sem <sec> e sem article-type que exija seções deve retornar vazio."""
833+
xml = (
834+
'<article xmlns:xlink="http://www.w3.org/1999/xlink"'
835+
' article-type="editorial" xml:lang="pt">'
836+
"<body><p>Sem secoes.</p></body></article>"
837+
)
838+
tree = etree.fromstring(xml.encode())
839+
results = list(validate_secs(tree, self.params))
840+
self.assertEqual(results, [])
841+
842+
788843
if __name__ == "__main__":
789844
unittest.main()

0 commit comments

Comments
 (0)