From b0ba9f99da15a9eb30ec77c94a73734f9303b063 Mon Sep 17 00:00:00 2001 From: Maxson Almeida Date: Fri, 31 Oct 2025 11:14:59 -0300 Subject: [PATCH 1/3] Use Solana explorer_url as authenticity_verification_url; rename util to solana_explorer_url; update imports and tests; remove old builder file --- certified_builder/build_url_tech_floripa.py | 25 --------------------- certified_builder/certified_builder.py | 9 +++----- certified_builder/solana_explorer_url.py | 6 +++++ tests/test_certified_builder.py | 14 ++++++++++++ 4 files changed, 23 insertions(+), 31 deletions(-) delete mode 100644 certified_builder/build_url_tech_floripa.py create mode 100644 certified_builder/solana_explorer_url.py diff --git a/certified_builder/build_url_tech_floripa.py b/certified_builder/build_url_tech_floripa.py deleted file mode 100644 index 41ffece..0000000 --- a/certified_builder/build_url_tech_floripa.py +++ /dev/null @@ -1,25 +0,0 @@ -from config import config - - -def build_url_tech_floripa(solana_response: dict, validation_code: str, order_id: str) -> str: - try: - base_url = config.TECH_FLORIPA_CERTIFICATE_VALIDATE_URL - - url_service_solona = solana_response.get("blockchain", {}).get("verificacao_url", "") - registered_time = solana_response.get("certificado", {}).get("time", "") - registered_uuid = solana_response.get("certificado", {}).get("uuid", "") - registered_name = solana_response.get("certificado", {}).get("name", "") - - txid_solana = url_service_solona.rsplit("/", 1)[-1] if url_service_solona else "" - - full_url = ( - f"{base_url}?validate_code={validation_code}" - f"&hash={txid_solana}" - f"&order_id={order_id}" - f"®istered_time={registered_time}" - f"®istered_uuid={registered_uuid}" - f"®istered_name={registered_name}" - ) - return full_url - except Exception as e: - raise ValueError(f"Error building Tech Floripa URL: {str(e)}") \ No newline at end of file diff --git a/certified_builder/certified_builder.py b/certified_builder/certified_builder.py index 542d81a..1a4d435 100644 --- a/certified_builder/certified_builder.py +++ b/certified_builder/certified_builder.py @@ -8,7 +8,7 @@ from certified_builder.utils.fetch_file_certificate import fetch_file_certificate from certified_builder.certificates_on_solana import CertificatesOnSolana from certified_builder.make_qrcode import MakeQRCode -from certified_builder.build_url_tech_floripa import build_url_tech_floripa +from certified_builder.solana_explorer_url import extract_solana_explorer_url FONT_NAME = os.path.join(os.path.dirname(__file__), "fonts/PinyonScript/PinyonScript-Regular.ttf") VALIDATION_CODE = os.path.join(os.path.dirname(__file__), "fonts/ChakraPetch/ChakraPetch-SemiBold.ttf") @@ -58,11 +58,8 @@ def build_certificates(self, participants: List[Participant]): } ) - participant.authenticity_verification_url = build_url_tech_floripa( - solana_response=solana_response, - validation_code=participant.formated_validation_code(), - order_id=participant.event.order_id - ) + # alteração: agora usamos a função renomeada que apenas extrai o explorer_url + participant.authenticity_verification_url = extract_solana_explorer_url(solana_response=solana_response) if not participant.authenticity_verification_url: raise RuntimeError("Failed to get authenticity verification URL from Solana response") diff --git a/certified_builder/solana_explorer_url.py b/certified_builder/solana_explorer_url.py new file mode 100644 index 0000000..beeb709 --- /dev/null +++ b/certified_builder/solana_explorer_url.py @@ -0,0 +1,6 @@ +def extract_solana_explorer_url(solana_response: dict) -> str: + # alteração: função isolada para extrair a URL do explorer da resposta do serviço + explorer_url = solana_response.get("explorer_url", "") + return explorer_url + + diff --git a/tests/test_certified_builder.py b/tests/test_certified_builder.py index db91230..b99de1a 100644 --- a/tests/test_certified_builder.py +++ b/tests/test_certified_builder.py @@ -7,6 +7,7 @@ from models.certificate import Certificate from models.event import Event from datetime import datetime +from unittest.mock import patch @pytest.fixture def mock_certificate(): @@ -80,7 +81,20 @@ def test_create_validation_code_image(certified_builder, mock_participant, mock_ def test_build_certificates(certified_builder, mock_participant, mock_certificate_template, mock_logo): participants = [mock_participant] + # comentário: mock do download de imagens e da resposta do serviço Solana para evitar chamada externa with patch('certified_builder.utils.fetch_file_certificate.fetch_file_certificate', side_effect=[mock_certificate_template, mock_logo]), \ + patch('certified_builder.certified_builder.CertificatesOnSolana.register_certificate_on_solana', return_value={ + "status": "encontrado", + "explorer_url": "https://explorer.solana.com/tx/abc123?cluster=devnet", + "certificado": { + "event": "evento de teste", + "uuid": "uuid-123", + "name": "user test", + "email": "user@test.com", + "certificate_code": "ABC-123-XYZ", + "time": "2025-10-31 12:05:38" + } + }), \ patch.object(certified_builder, 'save_certificate') as mock_save: certified_builder.build_certificates(participants) From aa4d171bb5009fcd96ed8ff4c2930e23cda9492e Mon Sep 17 00:00:00 2001 From: Maxson Almeida Date: Fri, 31 Oct 2025 11:20:05 -0300 Subject: [PATCH 2/3] fix: update explorer URL extraction to use nested blockchain structure in solana_response --- certified_builder/solana_explorer_url.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certified_builder/solana_explorer_url.py b/certified_builder/solana_explorer_url.py index beeb709..e904e78 100644 --- a/certified_builder/solana_explorer_url.py +++ b/certified_builder/solana_explorer_url.py @@ -1,6 +1,6 @@ def extract_solana_explorer_url(solana_response: dict) -> str: # alteração: função isolada para extrair a URL do explorer da resposta do serviço - explorer_url = solana_response.get("explorer_url", "") + explorer_url = solana_response.get("blockchain", {}).get("explorer_url", "") return explorer_url From 65b1b48a42f26a10dcb2d9ece4d5688495fe867c Mon Sep 17 00:00:00 2001 From: Maxson Almeida Date: Fri, 31 Oct 2025 11:32:13 -0300 Subject: [PATCH 3/3] Align to service contract: use blockchain.explorer_url; update test mock to match provided JSON; keep authenticity_verification_url from explorer_url --- certified_builder/solana_explorer_url.py | 2 +- tests/test_certified_builder.py | 27 +++++++++++++++++++----- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/certified_builder/solana_explorer_url.py b/certified_builder/solana_explorer_url.py index e904e78..c36cc66 100644 --- a/certified_builder/solana_explorer_url.py +++ b/certified_builder/solana_explorer_url.py @@ -1,5 +1,5 @@ def extract_solana_explorer_url(solana_response: dict) -> str: - # alteração: função isolada para extrair a URL do explorer da resposta do serviço + # alteração: extrai a URL do explorer do bloco "blockchain" conforme contrato oficial explorer_url = solana_response.get("blockchain", {}).get("explorer_url", "") return explorer_url diff --git a/tests/test_certified_builder.py b/tests/test_certified_builder.py index b99de1a..ffd3dd8 100644 --- a/tests/test_certified_builder.py +++ b/tests/test_certified_builder.py @@ -84,15 +84,32 @@ def test_build_certificates(certified_builder, mock_participant, mock_certificat # comentário: mock do download de imagens e da resposta do serviço Solana para evitar chamada externa with patch('certified_builder.utils.fetch_file_certificate.fetch_file_certificate', side_effect=[mock_certificate_template, mock_logo]), \ patch('certified_builder.certified_builder.CertificatesOnSolana.register_certificate_on_solana', return_value={ - "status": "encontrado", - "explorer_url": "https://explorer.solana.com/tx/abc123?cluster=devnet", + # comentário: mock alinhado ao contrato atual do serviço + "status": "sucesso", "certificado": { "event": "evento de teste", - "uuid": "uuid-123", "name": "user test", "email": "user@test.com", - "certificate_code": "ABC-123-XYZ", - "time": "2025-10-31 12:05:38" + "uuid": "uuid-123", + "time": "2025-10-31 12:05:38", + "json_canonico": {"fake": "data"}, + "hash_sha256": "deadbeef", + "txid_solana": "fake_txid_abc123", + "network": "devnet", + "timestamp": "2025-10-31 12:05:39", + "timestamp_unix": 1730366739 + }, + "blockchain": { + "rede": "Solana Devnet", + "explorer_url": "https://explorer.solana.com/tx/fake_txid_abc123?cluster=devnet", + "verificacao_url": "http://localhost:8000/certificados/verify/fake_txid_abc123", + "memo_program": "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" + }, + "validacao": { + "como_validar": "Recrie o JSON canonizado e compare o hash SHA-256", + "json_canonico_string": "{}", + "hash_esperado": "deadbeef", + "comando_validacao": "printf '{}' | shasum -a 256" } }), \ patch.object(certified_builder, 'save_certificate') as mock_save: