2.6.0: auditoría, 100 % local y 26 funciones verificadas
Sale a la red solo lo que se pide: la tipografía viene con el programa (static/manrope.woff2) y el comprobador de versiones tiene interruptor en Ajustes. Nueva prueba funcional (prueba_funcional.py) que levanta una copia sobre un cliente de ensayo y ejercita las 26 funciones por su API, con una conversión real: 26 de 26. Fallos que sacó y quedan arreglados: - Un renombrado solo de mayúsculas («10- octubre» → «10- Octubre») se descartaba en Plan.agregar y en mover; deshacer decía «sitio ocupado». - Las carpetas que crea el plan no entraban en el inventario hasta el siguiente escaneo (_recontar_carpetas las da de alta y recuenta). - Una fusión de carpetas cortaba en el primer archivo que tropezaba. - «Viejo» era distinto en convertidor, stock y respaldo: ahora es_viejo. - /api/actualizacion informaba versión vacía hasta la primera consulta. - Filas de sueltos por carpeta de origen; mes del stock el más repetido; fuera tres rutas sin uso. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Este commit está contenido en:
45
probar.py
45
probar.py
@@ -221,6 +221,51 @@ class Servidor(unittest.TestCase):
|
||||
c, res = self.post("/api/deshacer", {"lote": res["lote"]})
|
||||
self.assertEqual(c, 200)
|
||||
self.assertEqual(antes, self._arbol())
|
||||
# 4a. un mes escrito en minúsculas se normaliza de verdad (renombrado solo de mayúsculas), la
|
||||
# carpeta nueva entra en el inventario sin volver a escanear, y deshacer lo devuelve
|
||||
os.makedirs(os.path.join(self.raiz, "2026", "10- octubre"), exist_ok=True)
|
||||
with open(os.path.join(self.raiz, "2026", "10- octubre", "Final.mp4"), "wb") as fh:
|
||||
fh.write(os.urandom(4096))
|
||||
self.post("/api/escanear", {"raiz": self.raiz})
|
||||
self.esperar_escaneo()
|
||||
pl = self.get("/api/plan" + q)
|
||||
ren = [f for f in pl["filas"] if f["accion"] == "renombrar" and f["destino"].endswith("10- Octubre")]
|
||||
self.assertTrue(ren, "el plan no propone normalizar «10- octubre»")
|
||||
c, res = self.post("/api/aplicar", {"raiz": self.raiz, "ids": [f["id"] for f in ren]})
|
||||
self.assertEqual(c, 200)
|
||||
self.assertEqual(res["errores"], [])
|
||||
self.assertIn("10- Octubre", os.listdir(os.path.join(self.raiz, "2026")))
|
||||
ex = self.get("/api/estructura/existen?raiz=" + urllib.parse.quote(self.raiz)
|
||||
+ "&ruta=" + urllib.parse.quote(json.dumps(["{anio}", "{mes}"])))
|
||||
self.assertTrue(any(x.endswith("10- Octubre") for x in ex["carpetas"]), "la carpeta renombrada no está en el inventario")
|
||||
c, _ = self.post("/api/deshacer", {"lote": res["lote"]})
|
||||
self.assertIn("10- octubre", os.listdir(os.path.join(self.raiz, "2026")))
|
||||
shutil.rmtree(os.path.join(self.raiz, "2026"), ignore_errors=True)
|
||||
|
||||
# 4a-bis. dos copias idénticas se ven como repetidas y una se aparta; dos con el mismo nombre y
|
||||
# peso pero distinto contenido NO se tocan
|
||||
os.makedirs(os.path.join(self.raiz, "Dup A"), exist_ok=True)
|
||||
os.makedirs(os.path.join(self.raiz, "Dup B"), exist_ok=True)
|
||||
cuerpo = os.urandom(4096) * 1500 # 6 MB: por encima del umbral
|
||||
for d in ("Dup A", "Dup B"):
|
||||
with open(os.path.join(self.raiz, d, "igual.mp4"), "wb") as fh:
|
||||
fh.write(cuerpo)
|
||||
with open(os.path.join(self.raiz, "Dup A", "parecido.mp4"), "wb") as fh:
|
||||
fh.write(os.urandom(4096) * 1500)
|
||||
with open(os.path.join(self.raiz, "Dup B", "parecido.mp4"), "wb") as fh:
|
||||
fh.write(os.urandom(4096) * 1500)
|
||||
self.post("/api/escanear", {"raiz": self.raiz})
|
||||
self.esperar_escaneo()
|
||||
dup = self.get("/api/duplicados" + q)
|
||||
self.assertEqual(len(dup["grupos"]), 2)
|
||||
c, ap = self.post("/api/duplicados/apartar", {"raiz": self.raiz})
|
||||
self.assertEqual(ap["movimientos"], 1) # solo la idéntica
|
||||
self.assertEqual(ap["distintos"], 1) # la parecida se deja, y se avisa
|
||||
shutil.rmtree(os.path.join(self.raiz, "Dup A"), ignore_errors=True)
|
||||
shutil.rmtree(os.path.join(self.raiz, "Dup B"), ignore_errors=True)
|
||||
self.post("/api/escanear", {"raiz": self.raiz})
|
||||
self.esperar_escaneo()
|
||||
|
||||
# 4b. el mapa dice dónde va a acabar cada cosa, con las plantillas de verdad
|
||||
est = self.get("/api/estructura" + q)
|
||||
proy = next(c for c in est["cubetas"] if c["clave"] == "PROYECTOS")
|
||||
|
||||
Referencia en una nueva incidencia
Block a user