"""
El material de la clave no sale por ninguna superficie (principio III, SC-012).

Este archivo prueba una ausencia, que es lo más difícil de verificar y lo que
más caro sale si falla: una filtración de credencial no se nota nunca, porque
todo sigue funcionando igual. Se revisan las cuatro salidas por las que un
secreto puede escaparse sin que nadie lo note: las props que recibe la pantalla,
la fila guardada, los registros y las representaciones de los objetos.
"""

import json
import logging

import pytest

from apps.core.logging import CredentialScrubFilter, scrub
from apps.credentials.views import _credential_props
from tests.factories import TEST_KEY, create_account, create_credential

pytestmark = pytest.mark.django_db

MATERIAL = TEST_KEY['private_key']


@pytest.fixture
def credential():
    return create_credential(create_account())


def test_the_screen_props_do_not_carry_the_material(credential):
    props = json.dumps(_credential_props(credential))

    assert 'PRIVATE KEY' not in props
    assert 'MIIsinValorReal' not in props
    assert 'encrypted_key' not in props


def test_the_props_do_carry_what_has_to_be_shown(credential):
    """
    La contracara: ocultar de más también rompe el producto.

    La dirección de la cuenta de servicio hay que mostrarla —es lo que el
    usuario tiene que autorizar en Search Console— y no permite firmar nada.
    """
    props = _credential_props(credential)

    assert props['client_email'] == TEST_KEY['client_email']
    assert props['key_fingerprint']
    assert props['private_key_id'] == TEST_KEY['private_key_id']


def test_what_is_stored_in_the_database_does_not_contain_the_material(credential):
    credential.refresh_from_db()
    stored = bytes(credential.encrypted_key)

    assert b'PRIVATE KEY' not in stored
    assert b'MIIsinValorReal' not in stored


def test_the_representation_of_the_object_does_not_mention_it(credential):
    assert 'PRIVATE KEY' not in str(credential)
    assert 'PRIVATE KEY' not in repr(credential)


def test_the_log_filter_erases_a_pem_block():
    assert 'PRIVATE KEY' not in scrub(f'Falló la llamada con la clave {MATERIAL}')


def test_the_log_filter_erases_the_complete_json_of_a_service_account():
    clean = scrub(json.dumps(TEST_KEY))

    assert 'MIIsinValorReal' not in clean
    # Lo no secreto sobrevive: un registro sin nada de contexto tampoco sirve
    # para diagnosticar.
    assert TEST_KEY['client_email'] in clean


def test_the_log_filter_erases_a_platform_api_key():
    clean = scrub('Autenticó con irk_a1b2c3d4_UnaClaveLargaQueNoDebeQuedarEscrita')
    assert 'UnaClaveLargaQueNoDebeQuedarEscrita' not in clean


def test_the_log_filter_erases_the_authorization_header():
    clean = scrub('GET /api/v1/domains Authorization: Api-Key irk_aaaa_bbbb')
    assert 'irk_aaaa_bbbb' not in clean


def test_the_filter_acts_on_the_log_record_and_does_not_discard_it():
    """
    Desinfecta, no censura.

    Descartar el registro escondería que algo pasó, que a la hora de diagnosticar
    es peor que tenerlo sin el secreto.
    """
    log_filter = CredentialScrubFilter()
    record = logging.LogRecord(
        name='prueba',
        level=logging.ERROR,
        pathname=__file__,
        lineno=1,
        msg='fallo con %s',
        args=(MATERIAL,),
        exc_info=None,
    )

    assert log_filter.filter(record) is True
    assert 'PRIVATE KEY' not in record.getMessage()
    assert 'fallo con' in record.getMessage()
