Documentación de una aplicación y tests unitarios de errores «Unittest»


Preparación del repositorio e IDE

Voy a hacer uso de Visual Studio Code, y lo voy a sincronizar con el nuevo repositorio que crearé en Github.

Instalación de VS code

sudo apt-get install wget gpg
wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
sudo install -D -o root -g root -m 644 microsoft.gpg /usr/share/keyrings/microsoft.gpg
rm -f microsoft.gpg

Creo un archivo con nombre y ruta /etc/apt/sources.list.d/vscode.sources y con el siguiente contenido:

File → /etc/apt/sources.list.d/vscode.sources

Types: deb
URIs: https://packages.microsoft.com/repos/code
Suites: stable
Components: main
Architectures: amd64,arm64,armhf
Signed-By: /usr/share/keyrings/microsoft.gpg

Actualizo repositorio e instalo los paquetes:

sudo apt install apt-transport-https
sudo apt update
sudo apt install code # or code-insiders

Creación del repositorio Github

A continuación voy a fijar las variables globales de acceso a github, en nuestro terminal de kali linux, para mayor comodidad, y asi las toma tal y como las definí al principio de esta tarea (la puedes ver el .txt de la izquierda que cree):

git config --global user.name $Tu_usuario_github
git config --global user.email $Tu_mail_github
git config --global init.defaultBranch main
git config --global core.editor nano

A continuación voy a fijar las variables globales de acceso a github, en nuestro terminal de kali linux, para mayor comodidad, y asi las toma tal y como las definí al principio de esta tarea (la puedes ver el .txt de la izquierda que cree):

Descripción

A continuación voy a crear el repositorio privado, que contendrá la documentación de Github Pages, de la tarea:

Descripción

Descripción

A continuación en VS code, voy a clonar el repositorio recien creado:

Descripción

Me pedirá copiar y pegar el directorio de github

Descripción

Descripción

Y podemos comprobar como ya aparecé a la izquierda el repositorio creado:

Descripción

A continuacíón voy a crear la estructura del repositorio en Visual Studio Code. Dicha estructura quedará así:

Descripción

Creo los directorios,

Descripción

Descripción

Dentro de cada directorio añado los archivos correspondientes,

Descripción

Descripción

Ahora hago git add y git push, mediante las herramientas de VS code:

Descripción

Nombro el Commit (Creación de estructura) y acepto los cambios:

Descripción

A continuación haciendo clic al botón azúl Sync Changes, conseguimos hacer el git push y pull. Hago clic en ok:

Descripción

A continuación, al refrescar el repositorio de Github, observamos, como se reflejan todos los cambios y el nombre del commit correspondiente:

Descripción

A continuación le doy contenido al archivo mkdocs.yml,

# -------------------------------------------------------------# CONFIGURACIÓN GENERAL DEL SITIO# -------------------------------------------------------------

site_name: Documentación Unidad 1 - Tarea

# Define el título que aparecerá en la barra de navegación y en la etiqueta <title> del navegador.

  

# -------------------------------------------------------------

# ESTRUCTURA DE NAVEGACIÓN (MENÚ)

# -------------------------------------------------------------

# Define la lista de enlaces y el orden de aparición en el menú de navegación principal.

nav:

# El nombre de la sección en el menú: nombre_archivo.md

- Inicio: index.md  # Enlace a la página principal.

  

# -------------------------------------------------------------

# DIRECTORIOS

# -------------------------------------------------------------

# Ruta de la carpeta que contiene todos los archivos .md (Markdown) de la documentación.

# MkDocs buscará aquí los archivos listados en 'nav'.

doc_dir: docs

  

# -------------------------------------------------------------

# TEMA (Opcional, pero común)

# -------------------------------------------------------------

# theme:

# name: 'material' # Si se utiliza el tema Material for MkDocs.

# # highlightjs: true

# # code_fences: true
Descripción

A continuación configuro el Workflow de GithHub Actions.

Para ello relleno el archivo → .github/workflows/deploy_docs.yml

Posteriormente vinculo con GitHub Pages → Settings (del repositorio) /Pages y ocurre, que como el repositorio lo creé con visibilidad privada, debido a ello no me permite vincularlo con GitHub Pages, por lo tanto, procedo a cambiarlo a público, y ya me permitirá seguir el proceso.

Descripción

Descripción

Ya se ha generado el dominio, y para ver que ya carga la nueva documentación, basta con añadir el repositorio a dicho enlace,

Descripción

https://cibermbl.github.io/PPS-Unidad1-TareaRA1-Manuel-Benitez

A continuación vamos a probar el index.md, y observamos que una vez modificado dicho archivo, y hecho un git push se refleja los cambios en la rama gh-pages, y por lo tanto, al cargar la página, también se ven los cambios:

Descripción

Descripción

1. Documentación y prueba del programa

Procedo con el apartado primero de la tarea:

Apartado 1. Añade los comentarios al código de la aplicación indicando para qué sirven las diferentes sentencias, funciones, etc.

Los comentarios se pueden ver en el repositorio de github → https://github.com/cibermbl/PPS-Unidad1-TareaRA1-Manuel-Benitez

Apartado 2. Ejecuta el programa mediante las opciones de Ejecución y Depuración del IDE.

!!!IMPORTANTE¡¡¡ Este código tiene errores (tendrás que solucionar los errores antes de ejecutarlo). Este apartado tiene relación con la actividad Actividad-EntornosDesarrollo.

Documenta los errores encontrados indicando a que se debían esos errores y cómo lo has solucionado.

NOTA.-

Además de por aquí, los errores, y correcciones se podrán ver en el código a través del enlace al repositorio en github → https://github.com/cibermbl/PPS-Unidad1-TareaRA1-Manuel-Benitez

*#lavadero.py*

Línea 80

## ERROR -> esta llamada al método terminar() no es necesaria, debido a que el constructor ya está inicializando todos los atributos. Basta con eliminarlo.

self.terminar()

Línea 152 → AVISO EN AMARILLO

Observamos que vscode, nos señala en amarillo el método hacerLavado, pero esto no es un error de ejecución, si no un aviso que la extensión sonarqube, indicando que el nombre del método hacerLavado, no sigue la convención estilo snake_case del PEP8, por lo tanto no lo corrijo para mantener la compatibilidad con main_app.py, aunque lo más adecuado sería respetar el estilo, y haberse escrito → hacer_lavado

Descripción

Línea 165

if  self.__ocupado:

    ##ERROR SEGÚN LO QUE DICE EL ENUNCIADO

    ##En lugar de RuntimeError debería de lanzarse ValueError

    raise  RuntimeError("No se puede iniciar un nuevo lavado mientras el lavadero está ocupado")

    ##En el código corregido sustituyo RuntimeError por         ValueError

Corregido:

Descripción

Líneas 200 y 204:

##ERROR DE LÓGICA -> El precio del secado y encerado están intercambiados

if  self.__secado_a_mano:
    coste_lavado  +=  1.20  ##Debería ser 1.00
if  self.__encerado:
    coste_lavado  +=  1.00  ##Debería ser 1.20

Corregido:

Descripción

Líneas 256 y 259

##ERROR DEL FLUJO:
##Aquí están intercambiadas las fases del secado
##Si hay secado a mano -> debe ir a FASE_SECADO_MANO
##Si no -> debe ir a FASE_SECADO_AUTOMATICO  

if  self.__secado_a_mano:

    self.__fase  =  self.FASE_SECADO_AUTOMATICO
  
else:

    self.__fase  =  self.FASE_SECADO_MANO

Corregido:

Descripción

Línea 272

##ERROR:

##Aquí falta la transición desde FASE_SECADO_MANO a FASE_ENCERADO

##Ya que al terminar despues de secado a mano, no da opción a que se solicite el encerado

##Si el usuario solicita encerado -> debe ir a FASE_ENCERADO

##Si no -> debe ir a terminar()

elif  self.__fase  ==  self.FASE_SECADO_MANO:

    self.terminar()

elif  self.__fase  ==  self.FASE_ENCERADO:

    self.terminar()

Corregido:

Descripción

Línea 337:

##ERROR -> La función ejecutar_y_obtener_fases, debería ser un método de la 
##clase Lavadero, pero está definida fuera de la clase, y además
## - self._hacer_lavado (no existe ese método)
## - self.avanzarFase()  

def  ejecutar_y_obtener_fases(self, prelavado, secado, encerado):

    """
    Ejecuta un ciclo completo y devuelve la lista de fases     visitadas.
    """

  ##ERROR -> _hacer_lavado no existe en la clase

    self._hacer_lavado(prelavado, secado, encerado)

    fases_visitadas  = [self.fase]

Corregido:

Arreglo la llamada -> self.hacerLavado(prelavado, secado, encerado) (Línea 338)

Descripción

# main_app.py

En línea 94

## ERROR:

## Se añade el parámetro "encerado=False" para cumplir

## con la función ejecutarSimulacion

  

print("\n=======================================================")

print("EJEMPLO 4: Prelavado (S), Secado a mano (N), Encerado (N)")

ejecutarSimulacion(lavadero_global, prelavado=True, secado_mano=False)

Corregido:

Descripción

Para terminar el apartado, observamos que hay avisos que la extensión SONARQUBE (Área inferior del ide VScode) muestra.

No son errores sintácticos ni de tiempo de ejecución, y por lo tanto la no corrección no afecta al normal funcionamiento del programa.

Lo que nos sugiere, son mejoras en la calidad del código:

Descripción

En #main_app.py:

  • Rename function «ejecutarSimulacion» to match the regular expression

^[a-z_][a-z0-9_]*$

Es lo que comenté en el código anteriormente. Y es el aviso de que no se ha aplicado la convención snake_case, para dicha función. Nos sugiere que reemplace:

ejecutarSimulacion → ejecutar_simulacion

  • Add replacement fields or use a normal string instead of an f-string.

print(«\n—————————————-«)

Se utiliza un f-string sin interpolación de variables.

No es un error, pero se recomienda usar una cadena normal

  • Define a constant instead of duplicating this literal

Nos dice que ha encontrado esto repetido 4 veces.

«\n==========================================»

Nos recomienda definir una constante del tipo:

SEPARADOR = «\n==========================================»

En #lavadero.py

  • Igual que antes → hacerLavado → hacer_lavado (para seguir convención de estilo PEP8)


  • Refactor this function to reduce its Cognitive Complexity from 19 to 15


Esto nos indica que el método avanzarFase, tiene muchos if y elif. Según nos sugiere Sonarqube, se podría optimizar mejor el código (aunque no de error el actual), factorizando.

Descripción

  • Rename method “avanzaFase”, igual que antes → avanza_fase.


  • Replace this generic exception class with a more specific one.


Sustituir → raise Exception(«Bucle infinito detectado»)

por → raise RuntimeError(«Bucle infinito detectado»)

2. Realización de los test unitarios de la aplicación.

Para realizar dichos test, en primer lugar dentro de la terminal de vscode, navego hacia el directorio raiz que contiene los directorios del código (src/ y tests/).

A continuación ejecuto los tests en modo verbose tal y como pide el ejercicio:

PYTHONPATH=src python3 -m unittest discover -v tests
Descripción

Vemos como arroja un primer error de sintaxis en la línea 61:

Descripción

El siguiente código está incorrecto:

self.assertEqual( , ,

f"Secuencia de fases incorrecta.\nEsperadas: {fases_esperadas}\nObtenidas: {fases_obtenidas}")

llamada incorrecta a assertEqual, donde no se han especificado los argumentos obligatorios (vacio).

Código corregido:

#Linea corregida:

self.assertEqual(fases_obtenidas, fases_esperadas, f"Secuencia de fases incorrecta.\nEsperadas: {fases_esperadas}\nObtenidas: {fases_obtenidas}")

Una vez corregido vuelvo a ejecutar:

PYTHONPATH=src python3 -m unittest discover -v tests

Y comprobamos que ya si hace los tests correctamente. A continuación paso a documentar y corregir todos los errores encontrados:

Descripción

Test 2.-

##ERROR: En la línea siguiente método _hacer_lavado mal escrito sustituir por -> hacerLavado
##self.lavadero._hacer_lavado(False, False, True)

#Línea corregida:
self.lavadero.hacerLavado(False, False, True)

Test 9.-

##ERROR -> La función ejecutar_y_obtener_fases, debería ser un método de la
##clase Lavadero, pero está definida fuera de la clase, y por lo tanto mal indentada  

#CORRECCIÓN: Indentar la función para que quede dentro de la clase Lavadero

    def  ejecutar_y_obtener_fases(self, prelavado, secado, encerado):

"""Ejecuta un ciclo completo y devuelve la lista de fases visitadas."""  

    ##ERROR -> _hacer_lavado no existe en la clase
    #self._hacer_lavado(prelavado, secado, encerado)
    self.hacerLavado(prelavado, secado, encerado)
    fases_visitadas  = [self.fase] 

    while  self.ocupado:
        # Usamos un límite de pasos para evitar bucles infinitos en caso de error
        if  len(fases_visitadas) >  15:
            raise  Exception("Bucle infinito detectado en la simulación de fases.")
        self.avanzarFase()
        fases_visitadas.append(self.fase)  
    return  fases_visitadas

Test 4 (idem que test 2)

##ERROR: En la línea siguiente método _hacer_lavado mal escrito sustituir por -> hacerLavado

    ##self.lavadero._hacer_lavado(True, True, True)
    #Línea corregida:
    self.lavadero.hacerLavado(True, True, True)

Hasta aquí, los fallos detectados al ejecutar los tests no estaban relacionados con la lógica del lavadero, sino con problemas propios de la estrucutura del código y de los propios tests.

Se detectaron errores en nombres de métodos incorrectos y por funciones mal indentadas fuera de la clase.

Ya corregidos estos problemas, los tests pudieron ejecutarse correctamente y a validar el comportamiento real del sistema, con esto se da respuesta al apartado 3 de la tarea.

Continuamos pasando el test:

┌──(ppsmanu㉿kali)-[~/pps/ud1/tarea_ud1/PPS-Unidad1-TareaRA1-Manuel-Benitez]

└─$ PYTHONPATH=src python3 -m unittest discover -v tests

test1_estado_inicial_correcto (test_lavadero_unittest.TestLavadero.test1_estado_inicial_correcto)

Test 1: Verifica que el estado inicial es Inactivo y con 0 ingresos. ... ok

test2_excepcion_encerado_sin_secado (test_lavadero_unittest.TestLavadero.test2_excepcion_encerado_sin_secado)

Test 2: Comprueba que encerar sin secado a mano lanza ValueError. ... ok

test9_flujo_rapido_sin_extras (test_lavadero_unittest.TestLavadero.test9_flujo_rapido_sin_extras)

Test 9: Simula el flujo rápido sin opciones opcionales. ... ok

test_reseteo_estado_con_terminar (test_lavadero_unittest.TestLavadero.test_reseteo_estado_con_terminar)

Test 4: Verifica que terminar() resetea todas las flags y el estado. ... ok

  

----------------------------------------------------------------------

Ran 4 tests in 0.001s

  

OK

(COBRADO: 5.00 €)

┌──(ppsmanu㉿kali)-[~/pps/ud1/tarea_ud1/PPS-Unidad1-TareaRA1-Manuel-Benitez]

└─$
Descripción

Observamos como ahora si ejecuta bien los test, pero aún faltan por dar código a los tests restantes que son los siguientes:

#AÑADO LOS TEST QUE FALTAN SEGÚN INSTRUCCIONES DE LA TAREA EN EL PUNTO 2:  

#test 3 -> Cuando se intenta hacer un lavado mientras que otro ya está en marcha, se produce una ValueError.

def  test3_excepcion_lavadero_ocupado(self):

    """
    Test 3: No se puede iniciar un lavado si otro está en marcha.
    """
    self.lavadero.hacerLavado(False, False, False)  

    with  self.assertRaises(ValueError):

        self.lavadero.hacerLavado(False, False, False)  

#test 4 -> Si seleccionamos un lavado con prelavado a mano, los ingresos de lavadero son 6,50€.

def  test4_ingresos_prelavado(self):
 """
 Test 4: Prelavado a mano genera 6,50 €.
 """
    self.lavadero.hacerLavado(True, False, False)
    self.lavadero.avanzarFase()
    self.assertEqual(self.lavadero.ingresos, 6.50)
      

#test 5 ->Si seleccionamos un lavado con secado a mano, los ingresos son 6,00€.

def  test5_ingresos_secado_mano(self):
    """
    Test 5: Secado a mano genera 6,00 €.
    """
    self.lavadero.hacerLavado(False, True, False)
    self.lavadero.avanzarFase()
    self.assertEqual(self.lavadero.ingresos, 6.00)

# ----------------------------------------------------------------------

# Tests de ingresos

# ----------------------------------------------------------------------

  

#test 6 -> Si seleccionamos un lavado con secado a mano y encerado, los ingresos son 7,20€.

def  test6_ingresos_secado_y_encerado(self):
    self.lavadero.hacerLavado(False, True, True)
    self.lavadero.avanzarFase() # Se realiza el cobro
    self.assertEqual(self.lavadero.ingresos, 7.20)

#test 7 -Si seleccionamos un lavado con prelavado a mano y secado a mano, los ingresos son 7,50€.

def  test7_ingresos_prelavado_y_secado(self):

    self.lavadero.hacerLavado(True, True, False)
    self.lavadero.avanzarFase() # Se realiza el cobro
    self.assertEqual(self.lavadero.ingresos, 7.50)

#test 8 -> Si seleccionamos un lavado con prelavado a mano, secado a mano y encerado, los ingresos son 8,70€.

def  test8_ingresos_prelavado_secado_y_encerado(self):

    self.lavadero.hacerLavado(True, True, True)
    self.lavadero.avanzarFase() # Se realiza el cobro
    self.assertEqual(self.lavadero.ingresos, 8.70)

En los tests de flujo de fases faltan los siguientes;

#Test 10 -> Si seleccionamos un lavado con prelavado a mano y vamos avanzando fases, el lavadero

#pasa por las fases 0, 1, 2, 3, 4, 5, 6, 0.

def  test10_flujo_prelavado(self):

    fases_esperadas  = [0, 1, 2, 3, 4, 5, 6, 0]
    fases_obtenidas  =  self.lavadero.ejecutar_y_obtener_fases(prelavado=True, secado=False, encerado=False)

    self.assertEqual(fases_obtenidas, fases_esperadas)
      

#Test 11 -> Si seleccionamos un lavado con secado a mano y vamos avanzando fases, el lavadero
#pasa por las fases 0, 1, 3, 4, 5, 7, 0.

def  test11_flujo_secado_mano(self):
    fases_esperadas  = [0, 1, 3, 4, 5, 7, 0]
    fases_obtenidas  =  self.lavadero.ejecutar_y_obtener_fases(
prelavado=False, secado=True, encerado=False)
    self.assertEqual(fases_obtenidas, fases_esperadas)
  

#Test 12 -> Si seleccionamos un lavado con secado a mano y encerado y vamos avanzando fases,
#el lavadero pasa por las fases 0, 1, 3, 4, 5, 7, 8, 0

def  test12_flujo_secado_y_encerado(self):

    fases_esperadas  = [0, 1, 3, 4, 5, 7, 8, 0]
    fases_obtenidas  =  self.lavadero.ejecutar_y_obtener_fases(
    prelavado=False, secado=True, encerado=True
)
    self.assertEqual(fases_obtenidas, fases_esperadas)
  

#Test 13 -> Si seleccionamos un lavado con prelavado a mano y secado a mano y vamos avanzando fases,
#el lavadero pasa por las fases 0, 1, 2, 3, 4, 5, 7, 0.

def  test13_flujo_prelavado_y_secado(self):
    fases_esperadas  = [0, 1, 2, 3, 4, 5, 7, 0]
    fases_obtenidas  =  self.lavadero.ejecutar_y_obtener_fases(
prelavado=True, secado=True, encerado=False)
    self.assertEqual(fases_obtenidas, fases_esperadas)
  

#Test 14 -> Si seleccionamos un lavado con prelavado a mano, secado a mano y encerado y vamos avanzando fases,
#el lavadero pasa por las fases 0, 1, 2, 3, 4, 5, 7, 8, 0.

def  test14_flujo_completo(self):
    fases_esperadas  = [0, 1, 2, 3, 4, 5, 7, 8, 0]
    fases_obtenidas  =  self.lavadero.ejecutar_y_obtener_fases(
    prelavado=True, secado=True, encerado=True)
    self.assertEqual(fases_obtenidas, fases_esperadas)

A continuación en terminal de nuevo uso el comando para el test verbose.

Descripción

┌──(ppsmanu㉿kali)-[~/pps/ud1/tarea_ud1/PPS-Unidad1-TareaRA1-Manuel-Benitez]

└─$ PYTHONPATH=src python3 -m unittest discover -v tests

test10_flujo_prelavado (test_lavadero_unittest.TestLavadero.test10_flujo_prelavado) ... ok

test11_flujo_secado_mano (test_lavadero_unittest.TestLavadero.test11_flujo_secado_mano) ... ok

test12_flujo_secado_y_encerado (test_lavadero_unittest.TestLavadero.test12_flujo_secado_y_encerado) ... ERROR

test13_flujo_prelavado_y_secado (test_lavadero_unittest.TestLavadero.test13_flujo_prelavado_y_secado) ... ok

test14_flujo_completo (test_lavadero_unittest.TestLavadero.test14_flujo_completo) ... ERROR

test1_estado_inicial_correcto (test_lavadero_unittest.TestLavadero.test1_estado_inicial_correcto)

Test 1: Verifica que el estado inicial es Inactivo y con 0 ingresos. ... ok

test2_excepcion_encerado_sin_secado (test_lavadero_unittest.TestLavadero.test2_excepcion_encerado_sin_secado)

Test 2: Comprueba que encerar sin secado a mano lanza ValueError. ... ok

test3_excepcion_lavadero_ocupado (test_lavadero_unittest.TestLavadero.test3_excepcion_lavadero_ocupado)

Test 3: No se puede iniciar un lavado si otro está en marcha. ... ok

test4_ingresos_prelavado (test_lavadero_unittest.TestLavadero.test4_ingresos_prelavado)

Test 4: Prelavado a mano genera 6,50 €. ... ok

test5_ingresos_secado_mano (test_lavadero_unittest.TestLavadero.test5_ingresos_secado_mano)

Test 5: Secado a mano genera 6,00 €. ... ok

test6_ingresos_secado_y_encerado (test_lavadero_unittest.TestLavadero.test6_ingresos_secado_y_encerado) ... ok

test7_ingresos_prelavado_y_secado (test_lavadero_unittest.TestLavadero.test7_ingresos_prelavado_y_secado) ... ok

test8_ingresos_prelavado_secado_y_encerado (test_lavadero_unittest.TestLavadero.test8_ingresos_prelavado_secado_y_encerado) ... ok

test9_flujo_rapido_sin_extras (test_lavadero_unittest.TestLavadero.test9_flujo_rapido_sin_extras)

Test 9: Simula el flujo rápido sin opciones opcionales. ... ok

test_reseteo_estado_con_terminar (test_lavadero_unittest.TestLavadero.test_reseteo_estado_con_terminar)

Test 4: Verifica que terminar() resetea todas las flags y el estado. ... ok

  

======================================================================

ERROR: test12_flujo_secado_y_encerado (test_lavadero_unittest.TestLavadero.test12_flujo_secado_y_encerado)

----------------------------------------------------------------------

Traceback (most recent call last):

File "/home/ppsmanu/pps/ud1/tarea_ud1/PPS-Unidad1-TareaRA1-Manuel-Benitez/tests/test_lavadero_unittest.py", line 165, in test12_flujo_secado_y_encerado

fases_obtenidas = self.lavadero.ejecutar_y_obtener_fases(

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^

prelavado=False, secado=True, encerado=True

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

)

^

File "/home/ppsmanu/pps/ud1/tarea_ud1/PPS-Unidad1-TareaRA1-Manuel-Benitez/src/lavadero.py", line 350, in ejecutar_y_obtener_fases

self.avanzarFase()

~~~~~~~~~~~~~~~~^^

File "/home/ppsmanu/pps/ud1/tarea_ud1/PPS-Unidad1-TareaRA1-Manuel-Benitez/src/lavadero.py", line 285, in avanzarFase

raise RuntimeError(f"Estado no válido: Fase {self.__fase}. El lavadero va a estallar...")

RuntimeError: Estado no válido: Fase 8. El lavadero va a estallar...

  

======================================================================

ERROR: test14_flujo_completo (test_lavadero_unittest.TestLavadero.test14_flujo_completo)

----------------------------------------------------------------------

Traceback (most recent call last):

File "/home/ppsmanu/pps/ud1/tarea_ud1/PPS-Unidad1-TareaRA1-Manuel-Benitez/tests/test_lavadero_unittest.py", line 189, in test14_flujo_completo

fases_obtenidas = self.lavadero.ejecutar_y_obtener_fases(

prelavado=True, secado=True, encerado=True)

File "/home/ppsmanu/pps/ud1/tarea_ud1/PPS-Unidad1-TareaRA1-Manuel-Benitez/src/lavadero.py", line 350, in ejecutar_y_obtener_fases

self.avanzarFase()

~~~~~~~~~~~~~~~~^^

File "/home/ppsmanu/pps/ud1/tarea_ud1/PPS-Unidad1-TareaRA1-Manuel-Benitez/src/lavadero.py", line 285, in avanzarFase

raise RuntimeError(f"Estado no válido: Fase {self.__fase}. El lavadero va a estallar...")

RuntimeError: Estado no válido: Fase 8. El lavadero va a estallar...

  

----------------------------------------------------------------------

Ran 15 tests in 0.004s

  

FAILED (errors=2)

(COBRADO: 6.50 €) (COBRADO: 6.00 €) (COBRADO: 7.20 €) (COBRADO: 7.50 €) (COBRADO: 8.70 €) (COBRADO: 6.50 €) (COBRADO: 6.00 €) (COBRADO: 7.20 €) (COBRADO: 7.50 €) (COBRADO: 8.70 €) (COBRADO: 5.00 €)

Una vez completado el código de todos los tests, comprobamos que existen dos errores en el código, concretamente:

En test12 -> RuntimeError: Estado no válido: Fase 8. El lavadero va a estallar…

Descripción

##ERROR: Falta definir que hacer cuando ya se ha llegado a la fase 8. Al no definirlo (Olvide poner la siguiente línea)
##se quedaba colgado, y por eso arrojaba el error en unittest
##Por lo tanto añado las siguientes líneas:

elif  self.__fase  ==  self.FASE_ENCERADO:

    self.terminar()

Una vez corregido el error, procedo de nuevo a hacer el test verbose con unittest. Y efectivamente, ya se han corregido todo, y los tests los supera satisfactoriamente:

Descripción

┌──(ppsmanu㉿kali)-[~/pps/ud1/tarea_ud1/PPS-Unidad1-TareaRA1-Manuel-Benitez]

└─$ PYTHONPATH=src python3 -m unittest discover -v tests

test10_flujo_prelavado (test_lavadero_unittest.TestLavadero.test10_flujo_prelavado) ... ok

test11_flujo_secado_mano (test_lavadero_unittest.TestLavadero.test11_flujo_secado_mano) ... ok

test12_flujo_secado_y_encerado (test_lavadero_unittest.TestLavadero.test12_flujo_secado_y_encerado) ... ok

test13_flujo_prelavado_y_secado (test_lavadero_unittest.TestLavadero.test13_flujo_prelavado_y_secado) ... ok

test14_flujo_completo (test_lavadero_unittest.TestLavadero.test14_flujo_completo) ... ok

test1_estado_inicial_correcto (test_lavadero_unittest.TestLavadero.test1_estado_inicial_correcto)

Test 1: Verifica que el estado inicial es Inactivo y con 0 ingresos. ... ok

test2_excepcion_encerado_sin_secado (test_lavadero_unittest.TestLavadero.test2_excepcion_encerado_sin_secado)

Test 2: Comprueba que encerar sin secado a mano lanza ValueError. ... ok

test3_excepcion_lavadero_ocupado (test_lavadero_unittest.TestLavadero.test3_excepcion_lavadero_ocupado)

Test 3: No se puede iniciar un lavado si otro está en marcha. ... ok

test4_ingresos_prelavado (test_lavadero_unittest.TestLavadero.test4_ingresos_prelavado)

Test 4: Prelavado a mano genera 6,50 €. ... ok

test5_ingresos_secado_mano (test_lavadero_unittest.TestLavadero.test5_ingresos_secado_mano)

Test 5: Secado a mano genera 6,00 €. ... ok

test6_ingresos_secado_y_encerado (test_lavadero_unittest.TestLavadero.test6_ingresos_secado_y_encerado) ... ok

test7_ingresos_prelavado_y_secado (test_lavadero_unittest.TestLavadero.test7_ingresos_prelavado_y_secado) ... ok

test8_ingresos_prelavado_secado_y_encerado (test_lavadero_unittest.TestLavadero.test8_ingresos_prelavado_secado_y_encerado) ... ok

test9_flujo_rapido_sin_extras (test_lavadero_unittest.TestLavadero.test9_flujo_rapido_sin_extras)

Test 9: Simula el flujo rápido sin opciones opcionales. ... ok

test_reseteo_estado_con_terminar (test_lavadero_unittest.TestLavadero.test_reseteo_estado_con_terminar)

Test 4: Verifica que terminar() resetea todas las flags y el estado. ... ok

  

----------------------------------------------------------------------

Ran 15 tests in 0.003s

  

OK

(COBRADO: 6.50 €) (COBRADO: 6.00 €) (COBRADO: 7.20 €) (COBRADO: 7.50 €) (COBRADO: 8.70 €) (COBRADO: 6.50 €) (COBRADO: 6.00 €) (COBRADO: 7.20 €) (COBRADO: 7.50 €) (COBRADO: 8.70 €) (COBRADO: 5.00 €)

┌──(ppsmanu㉿kali)-[~/pps/ud1/tarea_ud1/PPS-Unidad1-TareaRA1-Manuel-Benitez]

└─$

3. Reflexión sobre comparación de la infraestructura de seguridad de los lenguajes.

Despueś de analizar las principales características en materia de seguridad de los diferentes lenguajes de programación, se puede observar que la seguridad no depende únicamente del propio lenguaje, sino además de cómo se ejecuta, que controles contiene y el entorno donde se despliega.

Lenguajes como Java .NET debido a su infraestructura, creo que aportan una seguridad más completa por su diseño, ya que ambos usan máquinas virtuales (JVM y CLR), los cuales verifican el codigo itermedio bytecode, tienen controles robustos en materia de gestión de permisos e incluyen APIs de seguridad criptográfica para autenticación y securiza las comunicaciones. Es por ello que, son lenguajes muy adecuados para aplicaciones empresariales donde la seguridad es primordial (pienso en entidades financieras, fondos de inversión, etc).

En cuanto a Python, prioriza su simplicidad, flexibilidad y productividad del desarrollador. Aspectos como la gestión autómatica de memoria evita problemas típicos como el acceso ilegal a mamoria o desbordamientos de buffer. No obstante, la seguridad en Python va a depender en gran medida de las buenas prácticas a la hora de desarrollar el código, ya que carece de gestión centralizada de la seguridad, y la separación de los privilegios es por asi decirlo menos estricta. Para paliar toda estás potenciales vulnerabilidades, es importante el uso correcto de librerías, y aislar el desarrollo en entornos virtuales, contenedores como docker o sandboxes.

En PHP, la seguridad depende de la configuración del servidor y de los permisos del usuario que ejecuta el proceso. Aunque posee módulos de seguridad como OpenSSL, si se realiza una configuración deficiente, puede ocasionar vulnerabilidades, por lo tanto el entorno de ejecución es tan importante como el propio lenguaje.

Después de repasar los principales lenguajes, creo que herramientas como los entornos sandbox, contenedores Docker, máquinas virtuales, etc. se han vuelto imprescindibles, ya que aportan ese plus de seguridad, proporcionando un aislamiento del entorno, permitiendo ejecutar aplicaciones de manera segura ante un código que pueda ser malicioso.

Resumiendo, ningún lenguaje es seguro al 100%, y por lo tanto una buena combinación entre la elección del código adecuado, una infraestructura de ejecución segura, y buenas prácticas de programación con entornos aislados, nos ayudarán a evitar vulnerabilidades.