diff --git a/docs/plan_pruebas/README.md b/docs/plan_pruebas/README.md new file mode 100644 index 0000000..314ff2e --- /dev/null +++ b/docs/plan_pruebas/README.md @@ -0,0 +1,45 @@ +# Plan de pruebas IPSExtractor (LaTeX) + +Documento profesional de pruebas unitarias, integración, E2E, estrés y resiliencia. + +## Requisitos + +- [TeX Live](https://www.tug.org/texlive/) o MiKTeX +- `pdflatex` (incluido en MiKTeX/TeX Live) +- Opcional: `latexmk` (requiere Perl en MiKTeX) +- Paquetes: `booktabs`, `longtable`, `hyperref`, `fancyhdr`, `geometry`, `babel`, `listings`, `tikz`, `xcolor`, `enumitem`, `siunitx`, `titlesec` + +## Compilación + +**Recomendado en Windows (MiKTeX sin Perl):** + +```powershell +cd docs\plan_pruebas +pdflatex -interaction=nonstopmode -file-line-error main.tex +pdflatex -interaction=nonstopmode -file-line-error main.tex +Copy-Item main.pdf PlanPruebas_IPSExtractor.pdf -Force +``` + +**Con latexmk** (si Perl está instalado): + +```powershell +latexmk -pdf main.tex +``` + +Artefactos: `main.pdf` y `PlanPruebas_IPSExtractor.pdf`. + +## Scripts de estrés (k6) + +Ver [`scripts/README.md`](scripts/README.md). Requiere [k6](https://k6.io/) y el stack levantado en `http://127.0.0.1:8189`. + +## Estructura + +``` +docs/plan_pruebas/ + main.tex + preamble.tex + latexmkrc + sections/ # capítulos del plan + diagrams/ # TikZ + scripts/ # k6 de referencia +``` diff --git a/docs/plan_pruebas/diagrams/arquitectura.tex b/docs/plan_pruebas/diagrams/arquitectura.tex new file mode 100644 index 0000000..6d9f6db --- /dev/null +++ b/docs/plan_pruebas/diagrams/arquitectura.tex @@ -0,0 +1,36 @@ +\begin{figure}[ht] +\centering +\begin{tikzpicture}[ + font=\small, + box/.style={draw=ipsblue, rounded corners=2pt, align=center, inner sep=4pt, minimum height=0.85cm, minimum width=2.4cm}, + ext/.style={draw=ipsgrey, dashed, rounded corners=2pt, align=center, inner sep=4pt, minimum height=0.85cm}, + arr/.style={-{Latex}, thick, ipsblue} +] + \node[box, fill=codebg] (desk) {Desktop\\Electron}; + \node[box, fill=codebg, right=1.8cm of desk] (gw) {api\_gateway\\:8189}; + \node[box, below=1.1cm of gw] (ext) {extraction\\:8001}; + \node[box, left=0.8cm of ext] (clean) {cleaning\\:8002}; + \node[box, right=0.8cm of ext] (count) {count-api\\:8080}; + \node[box, below=1.1cm of ext] (bot) {bot-descarga\\:8200}; + \node[box, below left=1.0cm and 0.2cm of bot] (pg) {PostgreSQL}; + \node[box, below right=1.0cm and 0.2cm of bot] (redis) {Redis}; + \node[ext, left=1.6cm of desk] (agent) {Agent PC\\Selenium}; + \node[ext, below=2.6cm of agent] (minio) {MinIO/S3}; + \node[ext, below=0.9cm of minio] (img) {Imaging}; + + \draw[arr] (desk) -- (gw); + \draw[arr] (gw) -- (ext); + \draw[arr] (gw) -- (clean); + \draw[arr] (gw) -- (count); + \draw[arr] (gw) -- (bot); + \draw[arr] (bot) -- (pg); + \draw[arr] (ext) -- (pg); + \draw[arr] (ext) -- (redis); + \draw[arr] (agent) |- (gw); + \draw[arr] (agent) -- (minio); + \draw[arr] (bot) -| (minio); + \draw[arr] (agent) -- (img); +\end{tikzpicture} +\caption{Arquitectura IPSExtractor bajo prueba.} +\label{fig:arquitectura} +\end{figure} diff --git a/docs/plan_pruebas/diagrams/flujo_bot.tex b/docs/plan_pruebas/diagrams/flujo_bot.tex new file mode 100644 index 0000000..107174c --- /dev/null +++ b/docs/plan_pruebas/diagrams/flujo_bot.tex @@ -0,0 +1,31 @@ +\begin{figure}[ht] +\centering +\begin{tikzpicture}[ + font=\footnotesize, + actor/.style={draw=ipsblue, rounded corners=2pt, align=center, minimum width=1.7cm, minimum height=0.7cm, fill=codebg}, + msg/.style={-{Latex}, thick}, + note/.style={align=left, font=\scriptsize\color{ipsgrey}} +] + \node[actor] (d) {Desktop}; + \node[actor, right=1.5cm of d] (g) {Gateway}; + \node[actor, right=1.5cm of g] (a) {API bot}; + \node[actor, right=1.5cm of a] (w) {Agent}; + \node[actor, right=1.5cm of w] (m) {MinIO}; + + \coordinate (top) at ($(d.south)+(0,-0.35)$); + \foreach \n in {d,g,a,w,m} { + \draw[ipsgrey!60] (\n.south) -- ++(0,-5.6); + } + + \draw[msg] ($(w.south)+(0,-0.5)$) -- node[above,font=\scriptsize]{register + heartbeat} ($(a.south)+(0,-0.5)$); + \draw[msg] ($(d.south)+(0,-1.3)$) -- node[above,font=\scriptsize]{POST /jobs (Excel)} ($(a.south)+(0,-1.3)$); + \draw[msg] ($(a.south)+(0,-2.0)$) -- node[above,font=\scriptsize]{PutObject Excel} ($(m.south)+(0,-2.0)$); + \draw[msg] ($(w.south)+(0,-2.7)$) -- node[above,font=\scriptsize]{claim\_pending} ($(a.south)+(0,-2.7)$); + \draw[msg] ($(w.south)+(0,-3.4)$) -- node[above,font=\scriptsize]{GET Excel / Put PDFs} ($(m.south)+(0,-3.4)$); + \draw[msg] ($(w.south)+(0,-4.1)$) -- node[above,font=\scriptsize]{POST events} ($(a.south)+(0,-4.1)$); + \draw[msg] ($(w.south)+(0,-4.8)$) -- node[above,font=\scriptsize]{PATCH completed} ($(a.south)+(0,-4.8)$); + \draw[msg] ($(d.south)+(0,-5.4)$) -- node[above,font=\scriptsize]{poll job + files} ($(a.south)+(0,-5.4)$); +\end{tikzpicture} +\caption{Secuencia del flujo bot de descarga.} +\label{fig:flujo-bot} +\end{figure} diff --git a/docs/plan_pruebas/latexmkrc b/docs/plan_pruebas/latexmkrc new file mode 100644 index 0000000..cee9185 --- /dev/null +++ b/docs/plan_pruebas/latexmkrc @@ -0,0 +1,4 @@ +$pdf_mode = 1; +$interaction = 'nonstopmode'; +$pdflatex = 'pdflatex -interaction=nonstopmode -file-line-error %O %S'; +$clean_ext = 'synctex.gz nav snm vrb bbl blg run.xml'; diff --git a/docs/plan_pruebas/main.aux b/docs/plan_pruebas/main.aux new file mode 100644 index 0000000..9184434 --- /dev/null +++ b/docs/plan_pruebas/main.aux @@ -0,0 +1,209 @@ +\relax +\providecommand \babel@aux [2]{\global \let \babel@toc \@gobbletwo } +\@nameuse{bbl@beforestart} +\catcode `"\active +\catcode `<\active +\catcode `>\active +\@nameuse{es@quoting} +\providecommand\hyper@newdestlabel[2]{} +\providecommand\HyField@AuxAddToFields[1]{} +\providecommand\HyField@AuxAddToCoFields[2]{} +\babel@aux{spanish}{} +\@writefile{toc}{\contentsline {chapter}{\numberline {1}Introducci\'on}{6}{chapter.1}\protected@file@percent } +\@writefile{lof}{\addvspace {10\p@ }} +\@writefile{lot}{\addvspace {10\p@ }} +\newlabel{ch:intro}{{1}{6}{Introducci\'on}{chapter.1}{}} +\@writefile{toc}{\contentsline {section}{\numberline {1.1}Objetivo}{6}{section.1.1}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {1.2}Alcance}{6}{section.1.2}\protected@file@percent } +\@writefile{toc}{\contentsline {subsection}{\numberline {1.2.1}Dentro de alcance}{6}{subsection.1.2.1}\protected@file@percent } +\@writefile{toc}{\contentsline {subsection}{\numberline {1.2.2}Fuera de alcance de esta versi\'on documental}{6}{subsection.1.2.2}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {1.3}Estado actual del repositorio}{6}{section.1.3}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {1.4}Referencias del sistema}{6}{section.1.4}\protected@file@percent } +\@writefile{lot}{\contentsline {table}{\numberline {1.1}{\ignorespaces Cobertura existente y gaps.}}{7}{table.1.1}\protected@file@percent } +\@writefile{toc}{\contentsline {chapter}{\numberline {2}Arquitectura bajo prueba}{8}{chapter.2}\protected@file@percent } +\@writefile{lof}{\addvspace {10\p@ }} +\@writefile{lot}{\addvspace {10\p@ }} +\newlabel{ch:arquitectura}{{2}{8}{Arquitectura bajo prueba}{chapter.2}{}} +\@writefile{toc}{\contentsline {section}{\numberline {2.1}Vista de componentes}{8}{section.2.1}\protected@file@percent } +\@writefile{lof}{\contentsline {figure}{\numberline {2.1}{\ignorespaces Arquitectura IPSExtractor bajo prueba.}}{8}{figure.2.1}\protected@file@percent } +\newlabel{fig:arquitectura}{{2.1}{8}{Arquitectura IPSExtractor bajo prueba}{figure.2.1}{}} +\@writefile{toc}{\contentsline {section}{\numberline {2.2}Inventario de componentes}{8}{section.2.2}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {2.3}Flujo cr\'itico: bot de descarga}{8}{section.2.3}\protected@file@percent } +\@writefile{lot}{\contentsline {table}{\numberline {2.1}{\ignorespaces Componentes del sistema bajo prueba.}}{9}{table.2.1}\protected@file@percent } +\@writefile{lof}{\contentsline {figure}{\numberline {2.2}{\ignorespaces Secuencia del flujo bot de descarga.}}{9}{figure.2.2}\protected@file@percent } +\newlabel{fig:flujo-bot}{{2.2}{9}{Secuencia del flujo bot de descarga}{figure.2.2}{}} +\@writefile{lol}{\contentsline {lstlisting}{\numberline {2.1}Prefijos MinIO del bot}{9}{lstlisting.2.1}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {2.4}Otros flujos de negocio}{9}{section.2.4}\protected@file@percent } +\@writefile{toc}{\contentsline {chapter}{\numberline {3}Estrategia de pruebas}{10}{chapter.3}\protected@file@percent } +\@writefile{lof}{\addvspace {10\p@ }} +\@writefile{lot}{\addvspace {10\p@ }} +\newlabel{ch:estrategia}{{3}{10}{Estrategia de pruebas}{chapter.3}{}} +\@writefile{toc}{\contentsline {section}{\numberline {3.1}Pir\'amide}{10}{section.3.1}\protected@file@percent } +\@writefile{lot}{\contentsline {table}{\numberline {3.1}{\ignorespaces Pir\'amide de pruebas IPSExtractor.}}{10}{table.3.1}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {3.2}Convenci\'on de identificadores}{10}{section.3.2}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {3.3}Entornos}{10}{section.3.3}\protected@file@percent } +\@writefile{lot}{\contentsline {table}{\numberline {3.2}{\ignorespaces TC = Testcontainers; LS = LocalStack.}}{11}{table.3.2}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {3.4}Mocks vs sistemas reales}{11}{section.3.4}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {3.5}Principios}{11}{section.3.5}\protected@file@percent } +\gdef \LT@i {\LT@entry + {2}{74.28644pt}\LT@entry + {1}{131.50148pt}\LT@entry + {1}{162.49014pt}} +\gdef \LT@ii {\LT@entry + {1}{79.97733pt}\LT@entry + {1}{125.81102pt}\LT@entry + {1}{162.49014pt}} +\@writefile{toc}{\contentsline {chapter}{\numberline {4}Pruebas unitarias}{12}{chapter.4}\protected@file@percent } +\@writefile{lof}{\addvspace {10\p@ }} +\@writefile{lot}{\addvspace {10\p@ }} +\newlabel{ch:unitarias}{{4}{12}{Pruebas unitarias}{chapter.4}{}} +\@writefile{toc}{\contentsline {section}{\numberline {4.1}Gateway}{12}{section.4.1}\protected@file@percent } +\@writefile{lot}{\contentsline {table}{\numberline {4.1}{Unitarias gateway.}}{12}{table.4.1}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {4.2}Bot-descarga API (\texttt {JobStore})}{12}{section.4.2}\protected@file@percent } +\@writefile{lot}{\contentsline {table}{\numberline {4.2}{Unitarias bot-descarga API.}}{12}{table.4.2}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {4.3}Agent bot (PC)}{12}{section.4.3}\protected@file@percent } +\gdef \LT@iii {\LT@entry + {1}{79.97733pt}\LT@entry + {1}{125.81102pt}\LT@entry + {1}{162.49014pt}} +\gdef \LT@iv {\LT@entry + {1}{79.97733pt}\LT@entry + {1}{131.50148pt}\LT@entry + {1}{156.79968pt}} +\@writefile{lot}{\contentsline {table}{\numberline {4.3}{Unitarias agent.}}{13}{table.4.3}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {4.4}Extraction / Cleaning / Count / Desktop}{13}{section.4.4}\protected@file@percent } +\@writefile{lot}{\contentsline {table}{\numberline {4.4}{Unitarias resto de servicios.}}{13}{table.4.4}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {4.5}Comandos de ejecuci\'on}{13}{section.4.5}\protected@file@percent } +\@writefile{lol}{\contentsline {lstlisting}{\numberline {4.1}Suite unitaria r\'apida}{13}{lstlisting.4.1}\protected@file@percent } +\gdef \LT@v {\LT@entry + {1}{74.28644pt}\LT@entry + {1}{131.50148pt}\LT@entry + {1}{162.49014pt}} +\gdef \LT@vi {\LT@entry + {1}{79.97733pt}\LT@entry + {1}{140.0374pt}\LT@entry + {1}{148.26378pt}} +\@writefile{toc}{\contentsline {chapter}{\numberline {5}Pruebas de integraci\'on}{14}{chapter.5}\protected@file@percent } +\@writefile{lof}{\addvspace {10\p@ }} +\@writefile{lot}{\addvspace {10\p@ }} +\newlabel{ch:integracion}{{5}{14}{Pruebas de integraci\'on}{chapter.5}{}} +\@writefile{toc}{\contentsline {section}{\numberline {5.1}Stack de integraci\'on}{14}{section.5.1}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {5.2}Gateway}{14}{section.5.2}\protected@file@percent } +\@writefile{lot}{\contentsline {table}{\numberline {5.1}{Integraci\'on gateway.}}{14}{table.5.1}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {5.3}Bot-descarga}{14}{section.5.3}\protected@file@percent } +\@writefile{lot}{\contentsline {table}{\numberline {5.2}{Integraci\'on bot-descarga.}}{14}{table.5.2}\protected@file@percent } +\gdef \LT@vii {\LT@entry + {1}{79.97733pt}\LT@entry + {1}{140.0374pt}\LT@entry + {1}{148.26378pt}} +\@writefile{toc}{\contentsline {section}{\numberline {5.4}Extraction / Cleaning / Count}{15}{section.5.4}\protected@file@percent } +\@writefile{lot}{\contentsline {table}{\numberline {5.3}{Integraci\'on extraction, cleaning, count y BD.}}{15}{table.5.3}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {5.5}Smoke m\'inimo post-compose}{15}{section.5.5}\protected@file@percent } +\@writefile{lol}{\contentsline {lstlisting}{\numberline {5.1}Smoke health}{15}{lstlisting.5.1}\protected@file@percent } +\gdef \LT@viii {\LT@entry + {1}{57.21504pt}\LT@entry + {1}{91.6678pt}\LT@entry + {1}{154.26378pt}\LT@entry + {1}{91.35826pt}} +\@writefile{toc}{\contentsline {chapter}{\numberline {6}Pruebas end-to-end}{16}{chapter.6}\protected@file@percent } +\@writefile{lof}{\addvspace {10\p@ }} +\@writefile{lot}{\addvspace {10\p@ }} +\newlabel{ch:e2e}{{6}{16}{Pruebas end-to-end}{chapter.6}{}} +\@writefile{toc}{\contentsline {section}{\numberline {6.1}Matriz de flujos de negocio}{16}{section.6.1}\protected@file@percent } +\@writefile{lot}{\contentsline {table}{\numberline {6.1}{Casos E2E cr\'iticos.}}{16}{table.6.1}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {6.2}Detalle E2E-05 (bot descarga)}{16}{section.6.2}\protected@file@percent } +\@writefile{toc}{\contentsline {subsection}{\numberline {6.2.1}Precondiciones}{16}{subsection.6.2.1}\protected@file@percent } +\@writefile{toc}{\contentsline {subsection}{\numberline {6.2.2}Pasos}{17}{subsection.6.2.2}\protected@file@percent } +\@writefile{toc}{\contentsline {subsection}{\numberline {6.2.3}Asserts}{17}{subsection.6.2.3}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {6.3}Desktop}{17}{section.6.3}\protected@file@percent } +\@writefile{toc}{\contentsline {chapter}{\numberline {7}Pruebas de estr\'es y carga}{18}{chapter.7}\protected@file@percent } +\@writefile{lof}{\addvspace {10\p@ }} +\@writefile{lot}{\addvspace {10\p@ }} +\newlabel{ch:estres}{{7}{18}{Pruebas de estr\'es y carga}{chapter.7}{}} +\@writefile{toc}{\contentsline {section}{\numberline {7.1}Herramienta}{18}{section.7.1}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {7.2}Perfiles de carga}{18}{section.7.2}\protected@file@percent } +\@writefile{lot}{\contentsline {table}{\numberline {7.1}{\ignorespaces Cargas por perfil de entorno.}}{18}{table.7.1}\protected@file@percent } +\newlabel{tab:perfiles-carga}{{7.1}{18}{Cargas por perfil de entorno}{table.7.1}{}} +\@writefile{toc}{\contentsline {section}{\numberline {7.3}Escenarios (cat\'alogo completo)}{18}{section.7.3}\protected@file@percent } +\gdef \LT@ix {\LT@entry + {1}{74.28644pt}\LT@entry + {1}{103.04872pt}\LT@entry + {1}{111.58464pt}\LT@entry + {1}{97.04872pt}} +\@writefile{lot}{\contentsline {table}{\numberline {7.2}{Escenarios de estr\'es.}}{19}{table.7.2}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {7.4}Scripts k6 incluidos}{19}{section.7.4}\protected@file@percent } +\@writefile{lot}{\contentsline {table}{\numberline {7.3}{\ignorespaces Mapeo script $\leftrightarrow $ escenario.}}{19}{table.7.3}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {7.5}SLOs de aceptaci\'on}{19}{section.7.5}\protected@file@percent } +\@writefile{toc}{\contentsline {subsection}{\numberline {7.5.1}Perfil host-compartido-8gb (default)}{19}{subsection.7.5.1}\protected@file@percent } +\@writefile{toc}{\contentsline {subsection}{\numberline {7.5.2}Perfil laboratorio}{20}{subsection.7.5.2}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {7.6}Ejecuci\'on ejemplo}{20}{section.7.6}\protected@file@percent } +\@writefile{lol}{\contentsline {lstlisting}{\numberline {7.1}Estr\'es gateway en host compartido}{20}{lstlisting.7.1}\protected@file@percent } +\gdef \LT@x {\LT@entry + {1}{57.21504pt}\LT@entry + {1}{111.58464pt}\LT@entry + {1}{125.81102pt}\LT@entry + {1}{97.04872pt}} +\@writefile{toc}{\contentsline {chapter}{\numberline {8}Resiliencia y chaos}{21}{chapter.8}\protected@file@percent } +\@writefile{lof}{\addvspace {10\p@ }} +\@writefile{lot}{\addvspace {10\p@ }} +\newlabel{ch:resiliencia}{{8}{21}{Resiliencia y chaos}{chapter.8}{}} +\@writefile{toc}{\contentsline {section}{\numberline {8.1}Objetivo}{21}{section.8.1}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {8.2}Casos}{21}{section.8.2}\protected@file@percent } +\@writefile{lot}{\contentsline {table}{\numberline {8.1}{Casos de resiliencia.}}{21}{table.8.1}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {8.3}Modos de fallo conocidos (bot)}{21}{section.8.3}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {8.4}Criterio global}{22}{section.8.4}\protected@file@percent } +\@writefile{toc}{\contentsline {chapter}{\numberline {9}Datos de prueba y entornos}{23}{chapter.9}\protected@file@percent } +\@writefile{lof}{\addvspace {10\p@ }} +\@writefile{lot}{\addvspace {10\p@ }} +\newlabel{ch:datos}{{9}{23}{Datos de prueba y entornos}{chapter.9}{}} +\@writefile{toc}{\contentsline {section}{\numberline {9.1}Host donde se ejecutan las pruebas}{23}{section.9.1}\protected@file@percent } +\newlabel{sec:host-pruebas}{{9.1}{23}{Host donde se ejecutan las pruebas}{section.9.1}{}} +\@writefile{toc}{\contentsline {subsection}{\numberline {9.1.1}Hardware y sistema}{23}{subsection.9.1.1}\protected@file@percent } +\@writefile{lot}{\contentsline {table}{\numberline {9.1}{\ignorespaces Host de pruebas IPSExtractor (staging compartido).}}{23}{table.9.1}\protected@file@percent } +\newlabel{tab:host-pruebas}{{9.1}{23}{Host de pruebas IPSExtractor (staging compartido)}{table.9.1}{}} +\@writefile{toc}{\contentsline {subsection}{\numberline {9.1.2}Stack IPSExtractor observado (idle)}{23}{subsection.9.1.2}\protected@file@percent } +\@writefile{lol}{\contentsline {lstlisting}{\numberline {9.1}Smoke del gateway en el host}{23}{lstlisting.9.1}\protected@file@percent } +\@writefile{lot}{\contentsline {table}{\numberline {9.2}{\ignorespaces Huella de memoria IPSExtractor en idle.}}{24}{table.9.2}\protected@file@percent } +\@writefile{toc}{\contentsline {subsection}{\numberline {9.1.3}Carga concurrente del host (no IPS)}{24}{subsection.9.1.3}\protected@file@percent } +\@writefile{toc}{\contentsline {subsection}{\numberline {9.1.4}Requisitos previos a una campa\~na de estr\'es}{24}{subsection.9.1.4}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {9.2}Fixtures recomendadas}{24}{section.9.2}\protected@file@percent } +\gdef \LT@xi {\LT@entry + {1}{125.50148pt}\LT@entry + {1}{233.62204pt}} +\@writefile{toc}{\contentsline {section}{\numberline {9.3}Variables de entorno relevantes}{25}{section.9.3}\protected@file@percent } +\@writefile{lot}{\contentsline {table}{\numberline {9.3}{Variables cr\'iticas para pruebas.}}{25}{table.9.3}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {9.4}Seguridad en entornos de prueba}{25}{section.9.4}\protected@file@percent } +\@writefile{toc}{\contentsline {chapter}{\numberline {10}Herramientas profesionales y CI}{26}{chapter.10}\protected@file@percent } +\@writefile{lof}{\addvspace {10\p@ }} +\@writefile{lot}{\addvspace {10\p@ }} +\newlabel{ch:herramientas}{{10}{26}{Herramientas profesionales y CI}{chapter.10}{}} +\@writefile{toc}{\contentsline {section}{\numberline {10.1}Stack de tooling}{26}{section.10.1}\protected@file@percent } +\@writefile{lot}{\contentsline {table}{\numberline {10.1}{\ignorespaces Herramientas profesionales adoptadas.}}{26}{table.10.1}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {10.2}Pipeline CI objetivo (no implementado en esta entrega)}{26}{section.10.2}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {10.3}Compilar este documento}{26}{section.10.3}\protected@file@percent } +\@writefile{lol}{\contentsline {lstlisting}{\numberline {10.1}latexmk}{26}{lstlisting.10.1}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {10.4}Prioridad de implementaci\'on de tests (ROI)}{27}{section.10.4}\protected@file@percent } +\gdef \LT@xii {\LT@entry + {1}{48.67912pt}\LT@entry + {1}{140.0374pt}\LT@entry + {1}{182.407pt}} +\@writefile{toc}{\contentsline {chapter}{\numberline {11}Cronograma y criterios de aceptaci\'on}{28}{chapter.11}\protected@file@percent } +\@writefile{lof}{\addvspace {10\p@ }} +\@writefile{lot}{\addvspace {10\p@ }} +\newlabel{ch:cronograma}{{11}{28}{Cronograma y criterios de aceptaci\'on}{chapter.11}{}} +\@writefile{toc}{\contentsline {section}{\numberline {11.1}Fases sugeridas}{28}{section.11.1}\protected@file@percent } +\@writefile{lot}{\contentsline {table}{\numberline {11.1}{Hoja de ruta de calidad.}}{28}{table.11.1}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {11.2}Definition of Done por release}{28}{section.11.2}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {11.3}Plantilla de informe de ejecuci\'on}{28}{section.11.3}\protected@file@percent } +\@writefile{lol}{\contentsline {lstlisting}{\numberline {11.1}Campos m\'inimos del informe}{28}{lstlisting.11.1}\protected@file@percent } +\@writefile{toc}{\contentsline {section}{\numberline {11.4}Responsables (roles)}{29}{section.11.4}\protected@file@percent } +\@writefile{toc}{\contentsline {chapter}{\numberline {A}Glosario}{30}{appendix.Alph1}\protected@file@percent } +\@writefile{lof}{\addvspace {10\p@ }} +\@writefile{lot}{\addvspace {10\p@ }} +\@writefile{toc}{\contentsline {chapter}{\numberline {B}Checklist de release}{31}{appendix.Alph2}\protected@file@percent } +\@writefile{lof}{\addvspace {10\p@ }} +\@writefile{lot}{\addvspace {10\p@ }} +\@writefile{toc}{\contentsline {chapter}{\numberline {C}Mapa de rutas relevantes}{32}{appendix.Alph3}\protected@file@percent } +\@writefile{lof}{\addvspace {10\p@ }} +\@writefile{lot}{\addvspace {10\p@ }} +\gdef \@abspage@last{33} diff --git a/docs/plan_pruebas/main.out b/docs/plan_pruebas/main.out new file mode 100644 index 0000000..f07c198 --- /dev/null +++ b/docs/plan_pruebas/main.out @@ -0,0 +1,73 @@ +\BOOKMARK [0][-]{chapter.1}{\376\377\000I\000n\000t\000r\000o\000d\000u\000c\000c\000i\000\363\000n}{}% 1 +\BOOKMARK [1][-]{section.1.1}{\376\377\000O\000b\000j\000e\000t\000i\000v\000o}{chapter.1}% 2 +\BOOKMARK [1][-]{section.1.2}{\376\377\000A\000l\000c\000a\000n\000c\000e}{chapter.1}% 3 +\BOOKMARK [2][-]{subsection.1.2.1}{\376\377\000D\000e\000n\000t\000r\000o\000\040\000d\000e\000\040\000a\000l\000c\000a\000n\000c\000e}{section.1.2}% 4 +\BOOKMARK [2][-]{subsection.1.2.2}{\376\377\000F\000u\000e\000r\000a\000\040\000d\000e\000\040\000a\000l\000c\000a\000n\000c\000e\000\040\000d\000e\000\040\000e\000s\000t\000a\000\040\000v\000e\000r\000s\000i\000\363\000n\000\040\000d\000o\000c\000u\000m\000e\000n\000t\000a\000l}{section.1.2}% 5 +\BOOKMARK [1][-]{section.1.3}{\376\377\000E\000s\000t\000a\000d\000o\000\040\000a\000c\000t\000u\000a\000l\000\040\000d\000e\000l\000\040\000r\000e\000p\000o\000s\000i\000t\000o\000r\000i\000o}{chapter.1}% 6 +\BOOKMARK [1][-]{section.1.4}{\376\377\000R\000e\000f\000e\000r\000e\000n\000c\000i\000a\000s\000\040\000d\000e\000l\000\040\000s\000i\000s\000t\000e\000m\000a}{chapter.1}% 7 +\BOOKMARK [0][-]{chapter.2}{\376\377\000A\000r\000q\000u\000i\000t\000e\000c\000t\000u\000r\000a\000\040\000b\000a\000j\000o\000\040\000p\000r\000u\000e\000b\000a}{}% 8 +\BOOKMARK [1][-]{section.2.1}{\376\377\000V\000i\000s\000t\000a\000\040\000d\000e\000\040\000c\000o\000m\000p\000o\000n\000e\000n\000t\000e\000s}{chapter.2}% 9 +\BOOKMARK [1][-]{section.2.2}{\376\377\000I\000n\000v\000e\000n\000t\000a\000r\000i\000o\000\040\000d\000e\000\040\000c\000o\000m\000p\000o\000n\000e\000n\000t\000e\000s}{chapter.2}% 10 +\BOOKMARK [1][-]{section.2.3}{\376\377\000F\000l\000u\000j\000o\000\040\000c\000r\000\355\000t\000i\000c\000o\000:\000\040\000b\000o\000t\000\040\000d\000e\000\040\000d\000e\000s\000c\000a\000r\000g\000a}{chapter.2}% 11 +\BOOKMARK [1][-]{section.2.4}{\376\377\000O\000t\000r\000o\000s\000\040\000f\000l\000u\000j\000o\000s\000\040\000d\000e\000\040\000n\000e\000g\000o\000c\000i\000o}{chapter.2}% 12 +\BOOKMARK [0][-]{chapter.3}{\376\377\000E\000s\000t\000r\000a\000t\000e\000g\000i\000a\000\040\000d\000e\000\040\000p\000r\000u\000e\000b\000a\000s}{}% 13 +\BOOKMARK [1][-]{section.3.1}{\376\377\000P\000i\000r\000\341\000m\000i\000d\000e}{chapter.3}% 14 +\BOOKMARK [1][-]{section.3.2}{\376\377\000C\000o\000n\000v\000e\000n\000c\000i\000\363\000n\000\040\000d\000e\000\040\000i\000d\000e\000n\000t\000i\000f\000i\000c\000a\000d\000o\000r\000e\000s}{chapter.3}% 15 +\BOOKMARK [1][-]{section.3.3}{\376\377\000E\000n\000t\000o\000r\000n\000o\000s}{chapter.3}% 16 +\BOOKMARK [1][-]{section.3.4}{\376\377\000M\000o\000c\000k\000s\000\040\000v\000s\000\040\000s\000i\000s\000t\000e\000m\000a\000s\000\040\000r\000e\000a\000l\000e\000s}{chapter.3}% 17 +\BOOKMARK [1][-]{section.3.5}{\376\377\000P\000r\000i\000n\000c\000i\000p\000i\000o\000s}{chapter.3}% 18 +\BOOKMARK [0][-]{chapter.4}{\376\377\000P\000r\000u\000e\000b\000a\000s\000\040\000u\000n\000i\000t\000a\000r\000i\000a\000s}{}% 19 +\BOOKMARK [1][-]{section.4.1}{\376\377\000G\000a\000t\000e\000w\000a\000y}{chapter.4}% 20 +\BOOKMARK [1][-]{section.4.2}{\376\377\000B\000o\000t\000-\000d\000e\000s\000c\000a\000r\000g\000a\000\040\000A\000P\000I\000\040\000\050\000J\000o\000b\000S\000t\000o\000r\000e\000\051}{chapter.4}% 21 +\BOOKMARK [1][-]{section.4.3}{\376\377\000A\000g\000e\000n\000t\000\040\000b\000o\000t\000\040\000\050\000P\000C\000\051}{chapter.4}% 22 +\BOOKMARK [1][-]{section.4.4}{\376\377\000E\000x\000t\000r\000a\000c\000t\000i\000o\000n\000\040\000/\000\040\000C\000l\000e\000a\000n\000i\000n\000g\000\040\000/\000\040\000C\000o\000u\000n\000t\000\040\000/\000\040\000D\000e\000s\000k\000t\000o\000p}{chapter.4}% 23 +\BOOKMARK [1][-]{section.4.5}{\376\377\000C\000o\000m\000a\000n\000d\000o\000s\000\040\000d\000e\000\040\000e\000j\000e\000c\000u\000c\000i\000\363\000n}{chapter.4}% 24 +\BOOKMARK [0][-]{chapter.5}{\376\377\000P\000r\000u\000e\000b\000a\000s\000\040\000d\000e\000\040\000i\000n\000t\000e\000g\000r\000a\000c\000i\000\363\000n}{}% 25 +\BOOKMARK [1][-]{section.5.1}{\376\377\000S\000t\000a\000c\000k\000\040\000d\000e\000\040\000i\000n\000t\000e\000g\000r\000a\000c\000i\000\363\000n}{chapter.5}% 26 +\BOOKMARK [1][-]{section.5.2}{\376\377\000G\000a\000t\000e\000w\000a\000y}{chapter.5}% 27 +\BOOKMARK [1][-]{section.5.3}{\376\377\000B\000o\000t\000-\000d\000e\000s\000c\000a\000r\000g\000a}{chapter.5}% 28 +\BOOKMARK [1][-]{section.5.4}{\376\377\000E\000x\000t\000r\000a\000c\000t\000i\000o\000n\000\040\000/\000\040\000C\000l\000e\000a\000n\000i\000n\000g\000\040\000/\000\040\000C\000o\000u\000n\000t}{chapter.5}% 29 +\BOOKMARK [1][-]{section.5.5}{\376\377\000S\000m\000o\000k\000e\000\040\000m\000\355\000n\000i\000m\000o\000\040\000p\000o\000s\000t\000-\000c\000o\000m\000p\000o\000s\000e}{chapter.5}% 30 +\BOOKMARK [0][-]{chapter.6}{\376\377\000P\000r\000u\000e\000b\000a\000s\000\040\000e\000n\000d\000-\000t\000o\000-\000e\000n\000d}{}% 31 +\BOOKMARK [1][-]{section.6.1}{\376\377\000M\000a\000t\000r\000i\000z\000\040\000d\000e\000\040\000f\000l\000u\000j\000o\000s\000\040\000d\000e\000\040\000n\000e\000g\000o\000c\000i\000o}{chapter.6}% 32 +\BOOKMARK [1][-]{section.6.2}{\376\377\000D\000e\000t\000a\000l\000l\000e\000\040\000E\0002\000E\000-\0000\0005\000\040\000\050\000b\000o\000t\000\040\000d\000e\000s\000c\000a\000r\000g\000a\000\051}{chapter.6}% 33 +\BOOKMARK [2][-]{subsection.6.2.1}{\376\377\000P\000r\000e\000c\000o\000n\000d\000i\000c\000i\000o\000n\000e\000s}{section.6.2}% 34 +\BOOKMARK [2][-]{subsection.6.2.2}{\376\377\000P\000a\000s\000o\000s}{section.6.2}% 35 +\BOOKMARK [2][-]{subsection.6.2.3}{\376\377\000A\000s\000s\000e\000r\000t\000s}{section.6.2}% 36 +\BOOKMARK [1][-]{section.6.3}{\376\377\000D\000e\000s\000k\000t\000o\000p}{chapter.6}% 37 +\BOOKMARK [0][-]{chapter.7}{\376\377\000P\000r\000u\000e\000b\000a\000s\000\040\000d\000e\000\040\000e\000s\000t\000r\000\351\000s\000\040\000y\000\040\000c\000a\000r\000g\000a}{}% 38 +\BOOKMARK [1][-]{section.7.1}{\376\377\000H\000e\000r\000r\000a\000m\000i\000e\000n\000t\000a}{chapter.7}% 39 +\BOOKMARK [1][-]{section.7.2}{\376\377\000P\000e\000r\000f\000i\000l\000e\000s\000\040\000d\000e\000\040\000c\000a\000r\000g\000a}{chapter.7}% 40 +\BOOKMARK [1][-]{section.7.3}{\376\377\000E\000s\000c\000e\000n\000a\000r\000i\000o\000s\000\040\000\050\000c\000a\000t\000\341\000l\000o\000g\000o\000\040\000c\000o\000m\000p\000l\000e\000t\000o\000\051}{chapter.7}% 41 +\BOOKMARK [1][-]{section.7.4}{\376\377\000S\000c\000r\000i\000p\000t\000s\000\040\000k\0006\000\040\000i\000n\000c\000l\000u\000i\000d\000o\000s}{chapter.7}% 42 +\BOOKMARK [1][-]{section.7.5}{\376\377\000S\000L\000O\000s\000\040\000d\000e\000\040\000a\000c\000e\000p\000t\000a\000c\000i\000\363\000n}{chapter.7}% 43 +\BOOKMARK [2][-]{subsection.7.5.1}{\376\377\000P\000e\000r\000f\000i\000l\000\040\000h\000o\000s\000t\000-\000c\000o\000m\000p\000a\000r\000t\000i\000d\000o\000-\0008\000g\000b\000\040\000\050\000d\000e\000f\000a\000u\000l\000t\000\051}{section.7.5}% 44 +\BOOKMARK [2][-]{subsection.7.5.2}{\376\377\000P\000e\000r\000f\000i\000l\000\040\000l\000a\000b\000o\000r\000a\000t\000o\000r\000i\000o}{section.7.5}% 45 +\BOOKMARK [1][-]{section.7.6}{\376\377\000E\000j\000e\000c\000u\000c\000i\000\363\000n\000\040\000e\000j\000e\000m\000p\000l\000o}{chapter.7}% 46 +\BOOKMARK [0][-]{chapter.8}{\376\377\000R\000e\000s\000i\000l\000i\000e\000n\000c\000i\000a\000\040\000y\000\040\000c\000h\000a\000o\000s}{}% 47 +\BOOKMARK [1][-]{section.8.1}{\376\377\000O\000b\000j\000e\000t\000i\000v\000o}{chapter.8}% 48 +\BOOKMARK [1][-]{section.8.2}{\376\377\000C\000a\000s\000o\000s}{chapter.8}% 49 +\BOOKMARK [1][-]{section.8.3}{\376\377\000M\000o\000d\000o\000s\000\040\000d\000e\000\040\000f\000a\000l\000l\000o\000\040\000c\000o\000n\000o\000c\000i\000d\000o\000s\000\040\000\050\000b\000o\000t\000\051}{chapter.8}% 50 +\BOOKMARK [1][-]{section.8.4}{\376\377\000C\000r\000i\000t\000e\000r\000i\000o\000\040\000g\000l\000o\000b\000a\000l}{chapter.8}% 51 +\BOOKMARK [0][-]{chapter.9}{\376\377\000D\000a\000t\000o\000s\000\040\000d\000e\000\040\000p\000r\000u\000e\000b\000a\000\040\000y\000\040\000e\000n\000t\000o\000r\000n\000o\000s}{}% 52 +\BOOKMARK [1][-]{section.9.1}{\376\377\000H\000o\000s\000t\000\040\000d\000o\000n\000d\000e\000\040\000s\000e\000\040\000e\000j\000e\000c\000u\000t\000a\000n\000\040\000l\000a\000s\000\040\000p\000r\000u\000e\000b\000a\000s}{chapter.9}% 53 +\BOOKMARK [2][-]{subsection.9.1.1}{\376\377\000H\000a\000r\000d\000w\000a\000r\000e\000\040\000y\000\040\000s\000i\000s\000t\000e\000m\000a}{section.9.1}% 54 +\BOOKMARK [2][-]{subsection.9.1.2}{\376\377\000S\000t\000a\000c\000k\000\040\000I\000P\000S\000E\000x\000t\000r\000a\000c\000t\000o\000r\000\040\000o\000b\000s\000e\000r\000v\000a\000d\000o\000\040\000\050\000i\000d\000l\000e\000\051}{section.9.1}% 55 +\BOOKMARK [2][-]{subsection.9.1.3}{\376\377\000C\000a\000r\000g\000a\000\040\000c\000o\000n\000c\000u\000r\000r\000e\000n\000t\000e\000\040\000d\000e\000l\000\040\000h\000o\000s\000t\000\040\000\050\000n\000o\000\040\000I\000P\000S\000\051}{section.9.1}% 56 +\BOOKMARK [2][-]{subsection.9.1.4}{\376\377\000R\000e\000q\000u\000i\000s\000i\000t\000o\000s\000\040\000p\000r\000e\000v\000i\000o\000s\000\040\000a\000\040\000u\000n\000a\000\040\000c\000a\000m\000p\000a\000\361\000a\000\040\000d\000e\000\040\000e\000s\000t\000r\000\351\000s}{section.9.1}% 57 +\BOOKMARK [1][-]{section.9.2}{\376\377\000F\000i\000x\000t\000u\000r\000e\000s\000\040\000r\000e\000c\000o\000m\000e\000n\000d\000a\000d\000a\000s}{chapter.9}% 58 +\BOOKMARK [1][-]{section.9.3}{\376\377\000V\000a\000r\000i\000a\000b\000l\000e\000s\000\040\000d\000e\000\040\000e\000n\000t\000o\000r\000n\000o\000\040\000r\000e\000l\000e\000v\000a\000n\000t\000e\000s}{chapter.9}% 59 +\BOOKMARK [1][-]{section.9.4}{\376\377\000S\000e\000g\000u\000r\000i\000d\000a\000d\000\040\000e\000n\000\040\000e\000n\000t\000o\000r\000n\000o\000s\000\040\000d\000e\000\040\000p\000r\000u\000e\000b\000a}{chapter.9}% 60 +\BOOKMARK [0][-]{chapter.10}{\376\377\000H\000e\000r\000r\000a\000m\000i\000e\000n\000t\000a\000s\000\040\000p\000r\000o\000f\000e\000s\000i\000o\000n\000a\000l\000e\000s\000\040\000y\000\040\000C\000I}{}% 61 +\BOOKMARK [1][-]{section.10.1}{\376\377\000S\000t\000a\000c\000k\000\040\000d\000e\000\040\000t\000o\000o\000l\000i\000n\000g}{chapter.10}% 62 +\BOOKMARK [1][-]{section.10.2}{\376\377\000P\000i\000p\000e\000l\000i\000n\000e\000\040\000C\000I\000\040\000o\000b\000j\000e\000t\000i\000v\000o\000\040\000\050\000n\000o\000\040\000i\000m\000p\000l\000e\000m\000e\000n\000t\000a\000d\000o\000\040\000e\000n\000\040\000e\000s\000t\000a\000\040\000e\000n\000t\000r\000e\000g\000a\000\051}{chapter.10}% 63 +\BOOKMARK [1][-]{section.10.3}{\376\377\000C\000o\000m\000p\000i\000l\000a\000r\000\040\000e\000s\000t\000e\000\040\000d\000o\000c\000u\000m\000e\000n\000t\000o}{chapter.10}% 64 +\BOOKMARK [1][-]{section.10.4}{\376\377\000P\000r\000i\000o\000r\000i\000d\000a\000d\000\040\000d\000e\000\040\000i\000m\000p\000l\000e\000m\000e\000n\000t\000a\000c\000i\000\363\000n\000\040\000d\000e\000\040\000t\000e\000s\000t\000s\000\040\000\050\000R\000O\000I\000\051}{chapter.10}% 65 +\BOOKMARK [0][-]{chapter.11}{\376\377\000C\000r\000o\000n\000o\000g\000r\000a\000m\000a\000\040\000y\000\040\000c\000r\000i\000t\000e\000r\000i\000o\000s\000\040\000d\000e\000\040\000a\000c\000e\000p\000t\000a\000c\000i\000\363\000n}{}% 66 +\BOOKMARK [1][-]{section.11.1}{\376\377\000F\000a\000s\000e\000s\000\040\000s\000u\000g\000e\000r\000i\000d\000a\000s}{chapter.11}% 67 +\BOOKMARK [1][-]{section.11.2}{\376\377\000D\000e\000f\000i\000n\000i\000t\000i\000o\000n\000\040\000o\000f\000\040\000D\000o\000n\000e\000\040\000p\000o\000r\000\040\000r\000e\000l\000e\000a\000s\000e}{chapter.11}% 68 +\BOOKMARK [1][-]{section.11.3}{\376\377\000P\000l\000a\000n\000t\000i\000l\000l\000a\000\040\000d\000e\000\040\000i\000n\000f\000o\000r\000m\000e\000\040\000d\000e\000\040\000e\000j\000e\000c\000u\000c\000i\000\363\000n}{chapter.11}% 69 +\BOOKMARK [1][-]{section.11.4}{\376\377\000R\000e\000s\000p\000o\000n\000s\000a\000b\000l\000e\000s\000\040\000\050\000r\000o\000l\000e\000s\000\051}{chapter.11}% 70 +\BOOKMARK [0][-]{appendix.Alph1}{\376\377\000G\000l\000o\000s\000a\000r\000i\000o}{}% 71 +\BOOKMARK [0][-]{appendix.Alph2}{\376\377\000C\000h\000e\000c\000k\000l\000i\000s\000t\000\040\000d\000e\000\040\000r\000e\000l\000e\000a\000s\000e}{}% 72 +\BOOKMARK [0][-]{appendix.Alph3}{\376\377\000M\000a\000p\000a\000\040\000d\000e\000\040\000r\000u\000t\000a\000s\000\040\000r\000e\000l\000e\000v\000a\000n\000t\000e\000s}{}% 73 diff --git a/docs/plan_pruebas/main.tex b/docs/plan_pruebas/main.tex new file mode 100644 index 0000000..dc0ee16 --- /dev/null +++ b/docs/plan_pruebas/main.tex @@ -0,0 +1,80 @@ +\documentclass[11pt,a4paper,oneside]{report} + +\input{preamble} + +\begin{document} + +\makecover + +\chapter*{Control de cambios} +\begin{tabularx}{\textwidth}{@{}l l X@{}} +\toprule +\textbf{Versi\'on} & \textbf{Fecha} & \textbf{Descripci\'on} \\ +\midrule +1.0.0 & 13 jul 2026 & Versi\'on inicial del plan de pruebas y estr\'es. \\ +1.1.0 & 13 jul 2026 & Entorno real de ejecuci\'on (host Ubuntu compartido 8\,GiB) y perfiles de carga. \\ +\bottomrule +\end{tabularx} + +\chapter*{Resumen ejecutivo} +Este documento define la estrategia y la matriz de pruebas del monorepo \textbf{IPSExtractor}: API Gateway, extraction/workers ARQ, cleaning, count-api (Go), bot-descarga (API + agent Selenium), PostgreSQL, Redis, MinIO y la aplicaci\'on de escritorio. + +Hoy el repositorio cuenta principalmente con pruebas unitarias aisladas; no existe pipeline CI, ni suites de integraci\'on/E2E automatizadas. Este plan cierra ese gap con identificadores trazables (\texttt{UT-*}, \texttt{IT-*}, \texttt{E2E-*}, \texttt{ST-*}, \texttt{CH-*}), SLOs medibles y scripts \textbf{k6} de referencia en \texttt{docs/plan\_pruebas/scripts/}. + +\textbf{Importante:} las pruebas de integraci\'on, humo y estr\'es documentadas contra el stack vivo se ejecutan (o se ejecutar\'an) en el host descrito en la secci\'on~\ref{sec:host-pruebas} (laptop Ubuntu 24.04, 8\,GiB RAM, Docker compartido con MLflow, Airflow, n8n, etc.). Los resultados deben interpretarse en ese contexto: un OOM o p95 alto puede deberse a contenci\'on del host, no solo a IPSExtractor. + +\tableofcontents +\clearpage + +\input{sections/01_introduccion} +\input{sections/02_arquitectura} +\input{sections/03_estrategia} +\input{sections/04_unitarias} +\input{sections/05_integracion} +\input{sections/06_e2e} +\input{sections/07_estres} +\input{sections/08_resiliencia} +\input{sections/09_datos_entornos} +\input{sections/10_herramientas_ci} +\input{sections/11_cronograma_aceptacion} + +\appendix +\chapter{Glosario} +\begin{description}[style=nextline,leftmargin=2.2cm,labelwidth=2cm] + \item[ARQ] Cola as\'incrona de workers de extracci\'on sobre Redis. + \item[Claim] Operaci\'on at\'omica que asigna un job \texttt{pending} a un worker. + \item[Gateway] \texttt{api\_gateway}, \'unica puerta HTTP expuesta en el host (:8189). + \item[Imaging] Portal web automatizado por Selenium en el PC del bot. + \item[k6] Herramienta profesional de pruebas de carga (Grafana Labs). + \item[MinIO] Almacenamiento S3-compatible para Excel y PDFs del bot. + \item[SLO] Objetivo de nivel de servicio (latencia, error rate, etc.). +\end{description} + +\chapter{Checklist de release} +\begin{enumerate} + \item Unitarios verdes: bots, bot-descarga store, Vitest, \texttt{go test}. + \item Migraciones Alembic en BD limpia (0001--0004). + \item Smoke Compose: \texttt{GET /health/live} y \texttt{GET /health}. + \item E2E-01 a E2E-05 ejecutados en staging (E2E-05 con agent real si hay Imaging). + \item Al menos ST-GW-01, ST-BOT-01, ST-COUNT-01 y ST-EXT-01 con umbrales cumplidos. + \item Informe: p95, error rate, picos RAM, jobs fallidos, costos LLM. + \item \textbf{Entorno citado:} hostname, \texttt{free -h}, \texttt{docker stats}, perfil k6 (\texttt{shared8g}/\texttt{lab}). + \item Sin secretos hardcodeados en compose para entornos de prueba. +\end{enumerate} + +\chapter{Mapa de rutas relevantes} +\section*{Gateway (\texttt{:8189})} +\begin{itemize} + \item \texttt{GET /health/live}, \texttt{GET /health} + \item Proxy: \texttt{/tasks/*}, \texttt{/cleaning/*}, \texttt{/count/*}, \texttt{/bot-descarga/*} + \item Timeout JSON 30\,s; uploads (incl.\ \texttt{POST /bot-descarga/jobs}) 600\,s +\end{itemize} + +\section*{Bot-descarga API (\texttt{:8200}, v\'ia gateway)} +\begin{itemize} + \item Workers: \texttt{POST /workers/register}, \texttt{POST /workers/heartbeat} + \item Workers: \texttt{GET /workers}, \texttt{GET /workers/me/pending} + \item Jobs: \texttt{POST /jobs}, \texttt{GET/PATCH /jobs/\{id\}}, events, cancel, file, files MinIO +\end{itemize} + +\end{document} diff --git a/docs/plan_pruebas/main.toc b/docs/plan_pruebas/main.toc new file mode 100644 index 0000000..1642060 --- /dev/null +++ b/docs/plan_pruebas/main.toc @@ -0,0 +1,74 @@ +\babel@toc {spanish}{}\relax +\contentsline {chapter}{\numberline {1}Introducci\'on}{6}{chapter.1}% +\contentsline {section}{\numberline {1.1}Objetivo}{6}{section.1.1}% +\contentsline {section}{\numberline {1.2}Alcance}{6}{section.1.2}% +\contentsline {subsection}{\numberline {1.2.1}Dentro de alcance}{6}{subsection.1.2.1}% +\contentsline {subsection}{\numberline {1.2.2}Fuera de alcance de esta versi\'on documental}{6}{subsection.1.2.2}% +\contentsline {section}{\numberline {1.3}Estado actual del repositorio}{6}{section.1.3}% +\contentsline {section}{\numberline {1.4}Referencias del sistema}{6}{section.1.4}% +\contentsline {chapter}{\numberline {2}Arquitectura bajo prueba}{8}{chapter.2}% +\contentsline {section}{\numberline {2.1}Vista de componentes}{8}{section.2.1}% +\contentsline {section}{\numberline {2.2}Inventario de componentes}{8}{section.2.2}% +\contentsline {section}{\numberline {2.3}Flujo cr\'itico: bot de descarga}{8}{section.2.3}% +\contentsline {section}{\numberline {2.4}Otros flujos de negocio}{9}{section.2.4}% +\contentsline {chapter}{\numberline {3}Estrategia de pruebas}{10}{chapter.3}% +\contentsline {section}{\numberline {3.1}Pir\'amide}{10}{section.3.1}% +\contentsline {section}{\numberline {3.2}Convenci\'on de identificadores}{10}{section.3.2}% +\contentsline {section}{\numberline {3.3}Entornos}{10}{section.3.3}% +\contentsline {section}{\numberline {3.4}Mocks vs sistemas reales}{11}{section.3.4}% +\contentsline {section}{\numberline {3.5}Principios}{11}{section.3.5}% +\contentsline {chapter}{\numberline {4}Pruebas unitarias}{12}{chapter.4}% +\contentsline {section}{\numberline {4.1}Gateway}{12}{section.4.1}% +\contentsline {section}{\numberline {4.2}Bot-descarga API (\texttt {JobStore})}{12}{section.4.2}% +\contentsline {section}{\numberline {4.3}Agent bot (PC)}{12}{section.4.3}% +\contentsline {section}{\numberline {4.4}Extraction / Cleaning / Count / Desktop}{13}{section.4.4}% +\contentsline {section}{\numberline {4.5}Comandos de ejecuci\'on}{13}{section.4.5}% +\contentsline {chapter}{\numberline {5}Pruebas de integraci\'on}{14}{chapter.5}% +\contentsline {section}{\numberline {5.1}Stack de integraci\'on}{14}{section.5.1}% +\contentsline {section}{\numberline {5.2}Gateway}{14}{section.5.2}% +\contentsline {section}{\numberline {5.3}Bot-descarga}{14}{section.5.3}% +\contentsline {section}{\numberline {5.4}Extraction / Cleaning / Count}{15}{section.5.4}% +\contentsline {section}{\numberline {5.5}Smoke m\'inimo post-compose}{15}{section.5.5}% +\contentsline {chapter}{\numberline {6}Pruebas end-to-end}{16}{chapter.6}% +\contentsline {section}{\numberline {6.1}Matriz de flujos de negocio}{16}{section.6.1}% +\contentsline {section}{\numberline {6.2}Detalle E2E-05 (bot descarga)}{16}{section.6.2}% +\contentsline {subsection}{\numberline {6.2.1}Precondiciones}{16}{subsection.6.2.1}% +\contentsline {subsection}{\numberline {6.2.2}Pasos}{17}{subsection.6.2.2}% +\contentsline {subsection}{\numberline {6.2.3}Asserts}{17}{subsection.6.2.3}% +\contentsline {section}{\numberline {6.3}Desktop}{17}{section.6.3}% +\contentsline {chapter}{\numberline {7}Pruebas de estr\'es y carga}{18}{chapter.7}% +\contentsline {section}{\numberline {7.1}Herramienta}{18}{section.7.1}% +\contentsline {section}{\numberline {7.2}Perfiles de carga}{18}{section.7.2}% +\contentsline {section}{\numberline {7.3}Escenarios (cat\'alogo completo)}{18}{section.7.3}% +\contentsline {section}{\numberline {7.4}Scripts k6 incluidos}{19}{section.7.4}% +\contentsline {section}{\numberline {7.5}SLOs de aceptaci\'on}{19}{section.7.5}% +\contentsline {subsection}{\numberline {7.5.1}Perfil host-compartido-8gb (default)}{19}{subsection.7.5.1}% +\contentsline {subsection}{\numberline {7.5.2}Perfil laboratorio}{20}{subsection.7.5.2}% +\contentsline {section}{\numberline {7.6}Ejecuci\'on ejemplo}{20}{section.7.6}% +\contentsline {chapter}{\numberline {8}Resiliencia y chaos}{21}{chapter.8}% +\contentsline {section}{\numberline {8.1}Objetivo}{21}{section.8.1}% +\contentsline {section}{\numberline {8.2}Casos}{21}{section.8.2}% +\contentsline {section}{\numberline {8.3}Modos de fallo conocidos (bot)}{21}{section.8.3}% +\contentsline {section}{\numberline {8.4}Criterio global}{22}{section.8.4}% +\contentsline {chapter}{\numberline {9}Datos de prueba y entornos}{23}{chapter.9}% +\contentsline {section}{\numberline {9.1}Host donde se ejecutan las pruebas}{23}{section.9.1}% +\contentsline {subsection}{\numberline {9.1.1}Hardware y sistema}{23}{subsection.9.1.1}% +\contentsline {subsection}{\numberline {9.1.2}Stack IPSExtractor observado (idle)}{23}{subsection.9.1.2}% +\contentsline {subsection}{\numberline {9.1.3}Carga concurrente del host (no IPS)}{24}{subsection.9.1.3}% +\contentsline {subsection}{\numberline {9.1.4}Requisitos previos a una campa\~na de estr\'es}{24}{subsection.9.1.4}% +\contentsline {section}{\numberline {9.2}Fixtures recomendadas}{24}{section.9.2}% +\contentsline {section}{\numberline {9.3}Variables de entorno relevantes}{25}{section.9.3}% +\contentsline {section}{\numberline {9.4}Seguridad en entornos de prueba}{25}{section.9.4}% +\contentsline {chapter}{\numberline {10}Herramientas profesionales y CI}{26}{chapter.10}% +\contentsline {section}{\numberline {10.1}Stack de tooling}{26}{section.10.1}% +\contentsline {section}{\numberline {10.2}Pipeline CI objetivo (no implementado en esta entrega)}{26}{section.10.2}% +\contentsline {section}{\numberline {10.3}Compilar este documento}{26}{section.10.3}% +\contentsline {section}{\numberline {10.4}Prioridad de implementaci\'on de tests (ROI)}{27}{section.10.4}% +\contentsline {chapter}{\numberline {11}Cronograma y criterios de aceptaci\'on}{28}{chapter.11}% +\contentsline {section}{\numberline {11.1}Fases sugeridas}{28}{section.11.1}% +\contentsline {section}{\numberline {11.2}Definition of Done por release}{28}{section.11.2}% +\contentsline {section}{\numberline {11.3}Plantilla de informe de ejecuci\'on}{28}{section.11.3}% +\contentsline {section}{\numberline {11.4}Responsables (roles)}{29}{section.11.4}% +\contentsline {chapter}{\numberline {A}Glosario}{30}{appendix.Alph1}% +\contentsline {chapter}{\numberline {B}Checklist de release}{31}{appendix.Alph2}% +\contentsline {chapter}{\numberline {C}Mapa de rutas relevantes}{32}{appendix.Alph3}% diff --git a/docs/plan_pruebas/preamble.tex b/docs/plan_pruebas/preamble.tex new file mode 100644 index 0000000..6373380 --- /dev/null +++ b/docs/plan_pruebas/preamble.tex @@ -0,0 +1,110 @@ +% !TEX encoding = UTF-8 +\usepackage[T1]{fontenc} +\usepackage[utf8]{inputenc} +\usepackage[spanish,es-tabla]{babel} +\usepackage{lmodern} +\usepackage{microtype} + +\usepackage[ + a4paper, + margin=2.5cm, + headheight=14pt +]{geometry} + +\usepackage{setspace} +\onehalfspacing + +\usepackage{fancyhdr} +\pagestyle{fancy} +\fancyhf{} +\fancyhead[L]{\small IPSExtractor --- Plan de pruebas} +\fancyhead[R]{\small\thepage} +\fancyfoot[C]{\footnotesize Confidencial --- Uso interno} +\renewcommand{\headrulewidth}{0.4pt} +\renewcommand{\footrulewidth}{0.2pt} + +\usepackage{titlesec} +\titleformat{\chapter}[display] + {\normalfont\huge\bfseries}{\chaptertitlename\ \thechapter}{16pt}{\Huge} +\titleformat{\section}{\normalfont\Large\bfseries}{\thesection}{1em}{} +\titleformat{\subsection}{\normalfont\large\bfseries}{\thesubsection}{1em}{} + +\usepackage{booktabs} +\usepackage{longtable} +\usepackage{tabularx} +\usepackage{multirow} +\usepackage{array} +\usepackage{enumitem} +\setlist[itemize]{nosep,leftmargin=1.5em} +\setlist[enumerate]{nosep,leftmargin=1.5em} + +\usepackage{siunitx} +\usepackage{xcolor} +\definecolor{ipsblue}{HTML}{1B4F72} +\definecolor{ipsgrey}{HTML}{566573} +\definecolor{codebg}{HTML}{F4F6F7} +\definecolor{successgreen}{HTML}{1E8449} +\definecolor{warnorange}{HTML}{B9770E} + +\usepackage{listings} +\lstset{ + basicstyle=\ttfamily\small, + backgroundcolor=\color{codebg}, + frame=single, + framerule=0.3pt, + rulecolor=\color{ipsgrey}, + breaklines=true, + columns=fullflexible, + showstringspaces=false, + tabsize=2, + captionpos=b +} +\lstdefinelanguage{JavaScript}{ + keywords={function, const, let, var, return, if, else, for, while, export, import, default, true, false, null}, + sensitive=true, + morecomment=[l]//, + morecomment=[s]{/*}{*/}, + morestring=[b]", + morestring=[b]' +} + +\usepackage{tikz} +\usetikzlibrary{arrows.meta,positioning,fit,calc,shapes.geometric,shapes.multipart} + +\usepackage{graphicx} +\usepackage{hyperref} +\hypersetup{ + colorlinks=true, + linkcolor=ipsblue, + urlcolor=ipsblue, + citecolor=ipsblue, + pdftitle={Plan de pruebas IPSExtractor}, + pdfauthor={IPSExtractor QA}, + pdfsubject={Pruebas unitarias, integracion, E2E, estres y resiliencia} +} + +\newcommand{\docversion}{1.1.0} +\newcommand{\docdate}{13 de julio de 2026} +\newcommand{\statusbadge}[1]{\texttt{#1}} + +\newcommand{\makecover}{% + \begin{titlepage} + \centering + \vspace*{1.5cm} + {\color{ipsblue}\rule{\textwidth}{2pt}}\\[0.4cm] + {\Huge\bfseries IPSExtractor}\\[0.35cm] + {\Large\color{ipsgrey} Sistema de extracci\'on y an\'alisis de facturas m\'edicas}\\[0.4cm] + {\color{ipsblue}\rule{\textwidth}{2pt}}\\[1.8cm] + {\LARGE\bfseries Plan completo de pruebas}\\[0.3cm] + {\large Unitarias $\cdot$ Integraci\'on $\cdot$ E2E $\cdot$ Estr\'es $\cdot$ Resiliencia}\\[2.2cm] + \begin{tabular}{@{}ll@{}} + \textbf{Versi\'on} & \docversion \\ + \textbf{Fecha} & \docdate \\ + \textbf{Clasificaci\'on} & Uso interno \\ + \textbf{Herramientas} & pytest, Vitest, go test, k6, Docker Compose, latexmk \\ + \end{tabular} + \vfill + {\small Generado a partir de la arquitectura real del monorepo IPSExtractor.}\\[0.3cm] + {\color{ipsblue}\rule{0.5\textwidth}{0.6pt}} + \end{titlepage} +} diff --git a/docs/plan_pruebas/scripts/README.md b/docs/plan_pruebas/scripts/README.md new file mode 100644 index 0000000..8409815 --- /dev/null +++ b/docs/plan_pruebas/scripts/README.md @@ -0,0 +1,49 @@ +# Scripts k6 — Plan de estrés IPSExtractor + +Requieren [k6](https://k6.io/docs/get-started/installation/) y el stack en `http://127.0.0.1:8189`. + +## Entorno de referencia (obligatorio en informes) + +Las pruebas de carga del proyecto se calibran para el host: + +- **Hostname:** `ubuntu` (ASUS X441UAK) +- **SO:** Ubuntu 24.04.4 LTS +- **CPU / RAM:** i5-7200U, 8 GiB +- **Docker compartido** con MLflow (~1.8 GiB), Airflow, n8n, MinIO, etc. + +Antes de cada corrida, adjuntar al informe: + +```bash +hostnamectl | head -5 +free -h +docker stats --no-stream +curl -s -w '\ntime=%{time_total}s\n' http://127.0.0.1:8189/health/live +``` + +## Perfiles (`PROFILE`) + +| Valor | Cuándo | +|-------|--------| +| `shared8g` (default) | Host actual con otros contenedores | +| `lab` | Solo IPSExtractor, ≥3 GiB libres | + +## Variables + +| Variable | Default | Descripción | +|----------|---------|-------------| +| `BASE_URL` | `http://127.0.0.1:8189` | Gateway | +| `PROFILE` | `shared8g` | Perfil de carga | +| `BOT_API_TOKEN` | `dev-token-change-me` | Bearer bot | +| `DOWNLOAD_JOB_ID` | — | Obligatorio en events | +| `WORKER_PREFIX` | `load-w` | Prefijo heartbeats | +| `PDF_B64` | (PDF mínimo) | PDF para count | + +## Ejecución + +```bash +k6 run -e PROFILE=shared8g -e BASE_URL=http://127.0.0.1:8189 k6_gateway_health.js +k6 run -e PROFILE=shared8g -e BOT_API_TOKEN=dev-token-change-me k6_bot_heartbeats.js +k6 run -e PROFILE=shared8g -e DOWNLOAD_JOB_ID= k6_bot_events.js +# Solo si free -h muestra ≥3GiB disponibles: +k6 run -e PROFILE=shared8g k6_count_batches.js +``` diff --git a/docs/plan_pruebas/scripts/k6_bot_events.js b/docs/plan_pruebas/scripts/k6_bot_events.js new file mode 100644 index 0000000..eb48ba5 --- /dev/null +++ b/docs/plan_pruebas/scripts/k6_bot_events.js @@ -0,0 +1,66 @@ +import http from 'k6/http'; +import { check, sleep } from 'k6'; + +/** + * ST-BOT-03 — Storm de eventos sobre un job existente. + * + * k6 run -e DOWNLOAD_JOB_ID= -e PROFILE=shared8g k6_bot_events.js + */ +const PROFILE = __ENV.PROFILE || 'shared8g'; +const BASE_URL = __ENV.BASE_URL || 'http://127.0.0.1:8189'; +const TOKEN = __ENV.BOT_API_TOKEN || 'dev-token-change-me'; +const JOB_ID = __ENV.DOWNLOAD_JOB_ID || ''; + +const profiles = { + shared8g: { + stages: [ + { duration: '15s', target: 5 }, + { duration: '45s', target: 12 }, + { duration: '15s', target: 0 }, + ], + }, + lab: { + stages: [ + { duration: '20s', target: 10 }, + { duration: '1m', target: 40 }, + { duration: '20s', target: 0 }, + ], + }, +}; + +const cfg = profiles[PROFILE] || profiles.shared8g; + +export const options = { + stages: cfg.stages, + thresholds: { + http_req_failed: ['rate<0.02'], + http_req_duration: ['p(95)<1000'], + }, + tags: { profile: PROFILE }, +}; + +export default function () { + if (!JOB_ID) { + throw new Error('Defina DOWNLOAD_JOB_ID con un job existente'); + } + const idx = __ITER + 1; + const payload = JSON.stringify({ + event_type: 'progress', + message: `k6 event ${idx}`, + index: idx, + total: 5000, + radicado: `R-${idx}`, + nit: '900123456', + metadata: { source: 'k6', profile: PROFILE }, + }); + const res = http.post(`${BASE_URL}/bot-descarga/jobs/${JOB_ID}/events`, payload, { + headers: { + Authorization: `Bearer ${TOKEN}`, + 'Content-Type': 'application/json', + }, + }); + check(res, { + 'event accepted': (r) => r.status === 200, + }); + sleep(PROFILE === 'shared8g' ? 0.15 : 0.05); +} diff --git a/docs/plan_pruebas/scripts/k6_bot_heartbeats.js b/docs/plan_pruebas/scripts/k6_bot_heartbeats.js new file mode 100644 index 0000000..2b29a16 --- /dev/null +++ b/docs/plan_pruebas/scripts/k6_bot_heartbeats.js @@ -0,0 +1,67 @@ +import http from 'k6/http'; +import { check, sleep } from 'k6'; + +/** + * ST-BOT-01 — Heartbeats concurrentes de workers. + * + * k6 run -e BASE_URL=http://127.0.0.1:8189 -e BOT_API_TOKEN=dev-token-change-me \ + * -e PROFILE=shared8g k6_bot_heartbeats.js + */ +const PROFILE = __ENV.PROFILE || 'shared8g'; +const TOKEN = __ENV.BOT_API_TOKEN || 'dev-token-change-me'; +const PREFIX = __ENV.WORKER_PREFIX || 'load-w'; +const BASE_URL = __ENV.BASE_URL || 'http://127.0.0.1:8189'; + +const profiles = { + shared8g: { vus: 5, duration: '2m' }, + lab: { vus: 25, duration: '3m' }, +}; + +const cfg = profiles[PROFILE] || profiles.shared8g; + +export const options = { + scenarios: { + heartbeats: { + executor: 'constant-vus', + vus: cfg.vus, + duration: cfg.duration, + }, + }, + thresholds: { + http_req_duration: ['p(95)<800'], + checks: ['rate>0.95'], + }, + tags: { profile: PROFILE }, +}; + +function headers() { + return { + Authorization: `Bearer ${TOKEN}`, + 'Content-Type': 'application/json', + }; +} + +export function setup() { + for (let i = 0; i < cfg.vus; i++) { + const wid = `${PREFIX}-${i}`; + http.post( + `${BASE_URL}/bot-descarga/workers/register`, + JSON.stringify({ worker_id: wid, hostname: `k6-${i}`, max_concurrent: 1 }), + { headers: headers() }, + ); + } + return {}; +} + +export default function () { + const wid = `${PREFIX}-${__VU - 1}`; + const res = http.post( + `${BASE_URL}/bot-descarga/workers/heartbeat`, + JSON.stringify({ worker_id: wid }), + { headers: headers() }, + ); + check(res, { + 'heartbeat 200': (r) => r.status === 200, + }); + sleep(8); +} diff --git a/docs/plan_pruebas/scripts/k6_count_batches.js b/docs/plan_pruebas/scripts/k6_count_batches.js new file mode 100644 index 0000000..b9bac70 --- /dev/null +++ b/docs/plan_pruebas/scripts/k6_count_batches.js @@ -0,0 +1,63 @@ +import http from 'k6/http'; +import { check, sleep } from 'k6'; +import encoding from 'k6/encoding'; + +/** + * ST-COUNT-01 — Lotes concurrentes hacia count-api vía gateway. + * En host compartido 8GiB: usar PROFILE=shared8g y liberar MLflow/Airflow antes. + * + * k6 run -e PROFILE=shared8g k6_count_batches.js + */ +const PROFILE = __ENV.PROFILE || 'shared8g'; +const BASE_URL = __ENV.BASE_URL || 'http://127.0.0.1:8189'; + +const profiles = { + shared8g: { + rate: 1, + duration: '1m', + preAllocatedVUs: 2, + maxVUs: 5, + }, + lab: { + rate: 5, + duration: '2m', + preAllocatedVUs: 10, + maxVUs: 20, + }, +}; + +const cfg = profiles[PROFILE] || profiles.shared8g; + +export const options = { + scenarios: { + batches: { + executor: 'constant-arrival-rate', + rate: cfg.rate, + timeUnit: '1s', + duration: cfg.duration, + preAllocatedVUs: cfg.preAllocatedVUs, + maxVUs: cfg.maxVUs, + }, + }, + thresholds: { + http_req_duration: ['p(95)<60000'], + checks: ['rate>0.85'], + }, + tags: { profile: PROFILE }, +}; + +const tinyPdf = encoding.b64decode( + 'JVBERi0xLjAKMSAwIG9iago8PAovVHlwZSAvQ2F0YWxvZwovUGFnZXMgMiAwIFIKPj4KZW5kb2JqCjIgMCBvYmoKPDwKL1R5cGUgL1BhZ2VzCi9LaWRzIFszIDAgUl0KL0NvdW50IDEKPD4KZW5kb2JqCjMgMCBvYmoKPDwKL1R5cGUgL1BhZ2UKL1BhcmVudCAyIDAgUgovTWVkaWFCb3ggWzAgMCAzMDAgMTQ0XQo+PgplbmRvYmoKeHJlZgowIDQKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDA5IDAwMDAwIG4gCjAwMDAwMDAwNjMgMDAwMDAgbiAKMDAwMDAwMDEyNCAwMDAwMCBuIAp0cmFpbGVyCjw8Ci9TaXplIDQKL1Jvb3QgMSAwIFIKPj4Kc3RhcnR4cmVmCjIwMQolJUVPRgo=', +); + +export default function () { + const bin = __ENV.PDF_B64 ? encoding.b64decode(__ENV.PDF_B64) : tinyPdf; + const data = { + file: http.file(bin, 'sample.pdf', 'application/pdf'), + }; + const res = http.post(`${BASE_URL}/count/v1/count`, data); + check(res, { + 'not 5xx': (r) => r.status < 500, + }); + sleep(0.1); +} diff --git a/docs/plan_pruebas/scripts/k6_gateway_health.js b/docs/plan_pruebas/scripts/k6_gateway_health.js new file mode 100644 index 0000000..ddabd67 --- /dev/null +++ b/docs/plan_pruebas/scripts/k6_gateway_health.js @@ -0,0 +1,60 @@ +import http from 'k6/http'; +import { check, sleep } from 'k6'; + +/** + * ST-GW-01 — Gateway health liveness bajo carga. + * + * k6 run -e BASE_URL=http://127.0.0.1:8189 -e PROFILE=shared8g k6_gateway_health.js + * k6 run -e PROFILE=lab k6_gateway_health.js + * + * Perfiles: + * shared8g (default) — host Ubuntu 8GiB compartido (MLflow/Airflow/etc.) + * lab — Docker dedicado a IPS + */ +const PROFILE = __ENV.PROFILE || 'shared8g'; + +const profiles = { + shared8g: { + stages: [ + { duration: '20s', target: 5 }, + { duration: '40s', target: 15 }, + { duration: '40s', target: 25 }, + { duration: '20s', target: 0 }, + ], + thresholds: { + http_req_failed: ['rate<0.01'], + http_req_duration: ['p(95)<500'], + }, + }, + lab: { + stages: [ + { duration: '30s', target: 20 }, + { duration: '1m', target: 50 }, + { duration: '1m', target: 100 }, + { duration: '30s', target: 0 }, + ], + thresholds: { + http_req_failed: ['rate<0.001'], + http_req_duration: ['p(95)<300'], + }, + }, +}; + +const cfg = profiles[PROFILE] || profiles.shared8g; + +export const options = { + stages: cfg.stages, + thresholds: cfg.thresholds, + tags: { profile: PROFILE, host: 'ubuntu-shared-8g' }, +}; + +const BASE_URL = __ENV.BASE_URL || 'http://127.0.0.1:8189'; + +export default function () { + const res = http.get(`${BASE_URL}/health/live`); + check(res, { + 'status is 200': (r) => r.status === 200, + 'body ok': (r) => String(r.body).includes('ok'), + }); + sleep(0.2); +} diff --git a/docs/plan_pruebas/sections/01_introduccion.tex b/docs/plan_pruebas/sections/01_introduccion.tex new file mode 100644 index 0000000..45c26a3 --- /dev/null +++ b/docs/plan_pruebas/sections/01_introduccion.tex @@ -0,0 +1,55 @@ +\chapter{Introducci\'on} +\label{ch:intro} + +\section{Objetivo} +Definir un plan de pruebas \textbf{completo y ejecutable} para IPSExtractor que cubra: +\begin{itemize} + \item Correctitud funcional por servicio y por flujo de negocio. + \item Integraci\'on entre Gateway, microservicios, PostgreSQL, Redis y MinIO. + \item Validaci\'on end-to-end (Desktop $\rightarrow$ APIs $\rightarrow$ agent/bot). + \item Capacidad y l\'imites bajo carga (estr\'es) con criterios SLO. + \item Resiliencia ante fallos de dependencias (chaos controlado). +\end{itemize} + +\section{Alcance} +\subsection{Dentro de alcance} +\begin{itemize} + \item \texttt{api\_gateway}, \texttt{extraction} + workers ARQ, \texttt{cleaning}, \texttt{count-api}. + \item \texttt{bot-descarga} (API Docker) y agent PC (\texttt{bots/bot\_descarga}). + \item PostgreSQL 16, Redis 7, MinIO/S3, vol\'umenes \texttt{/data} e \texttt{ips\_queue}. + \item Desktop app (Electron/React) a nivel de contratos API y checklist E2E. +\end{itemize} + +\subsection{Fuera de alcance de esta versi\'on documental} +\begin{itemize} + \item Implementaci\'on inmediata de CI/CD o de nuevos tests FastAPI (se planifican, no se entregan aqu\'i). + \item Estr\'es contra producci\'on o Imaging real sin entorno de staging. + \item Pruebas de seguridad ofensivas (penetration testing). +\end{itemize} + +\section{Estado actual del repositorio} +\begin{table}[h] +\centering +\begin{tabular}{@{}llp{6.2cm}@{}} +\toprule +\textbf{\'Area} & \textbf{Tipo} & \textbf{Ubicaci\'on / notas} \\ +\midrule +Bot agent & Unit & \texttt{bots/bot\_descarga/tests/} \\ +Bot API store & Unit & \texttt{services/bot\_descarga/tests/test\_store.py} \\ +Desktop & Unit & Vitest en \texttt{services/desktop\_app} \\ +count-api & Unit & \texttt{go test} (features ML) \\ +Integraci\'on & --- & No existe \\ +E2E & --- & No existe \\ +CI & --- & No hay workflows \\ +\bottomrule +\end{tabular} +\caption{Cobertura existente y gaps.} +\end{table} + +\section{Referencias del sistema} +\begin{itemize} + \item \texttt{README.md} (ra\'iz): stack, puertos, arranque. + \item \texttt{docker-compose.yml}: orquestaci\'on de servicios. + \item \texttt{docs/MIGRACION\_MICROSERVICIOS.md}, \texttt{docs/database.dbml}. + \item READMEs por servicio bajo \texttt{services/*/} y \texttt{bots/bot\_descarga/}. +\end{itemize} diff --git a/docs/plan_pruebas/sections/02_arquitectura.tex b/docs/plan_pruebas/sections/02_arquitectura.tex new file mode 100644 index 0000000..8766bfe --- /dev/null +++ b/docs/plan_pruebas/sections/02_arquitectura.tex @@ -0,0 +1,56 @@ +\chapter{Arquitectura bajo prueba} +\label{ch:arquitectura} + +\section{Vista de componentes} +El host solo expone el Gateway en el puerto \textbf{8189}. El resto de APIs viven en la red Docker. El agent del bot corre en PCs externas y se comunica por HTTP + PutObject a MinIO. + +\input{diagrams/arquitectura} + +\section{Inventario de componentes} +\begin{table}[ht] +\centering +\begin{tabular}{@{}llp{5.8cm}@{}} +\toprule +\textbf{Componente} & \textbf{Puerto} & \textbf{Responsabilidad} \\ +\midrule +\texttt{api\_gateway} & Host 8189 & Proxy HTTP, health agregado, CORS \\ +\texttt{extraction} & 8001 & Jobs/tasks, LLM, ingest PDFs \\ +\texttt{worker} (ARQ) & --- & Procesamiento async v\'ia Redis \\ +\texttt{cleaning} & 8002 & Limpieza CSV / reglas \\ +\texttt{count-api} & 8080 & OCR + ONNX, stateless \\ +\texttt{bot-descarga} & 8200 & Control remoto de bots \\ +PostgreSQL 16 & 5432 & Persistencia compartida \\ +Redis 7 & 6379 & Cola ARQ + rate limiter \\ +Desktop & local & Cliente Electron/React \\ +Agent bot & local PC & Selenium + MinIO upload \\ +MinIO/S3 & externo & Excel y PDFs del bot \\ +\bottomrule +\end{tabular} +\caption{Componentes del sistema bajo prueba.} +\end{table} + +\section{Flujo cr\'itico: bot de descarga} +\input{diagrams/flujo_bot} + +Secuencia resumida: +\begin{enumerate} + \item Agent: \texttt{register} + loop \texttt{heartbeat} / \texttt{claim\_pending} ($\sim$8\,s). + \item Desktop: \texttt{POST /bot-descarga/jobs} (Excel, \texttt{worker\_id}, \texttt{job\_id}). + \item API: valida job padre y worker online; sube Excel a MinIO; estado \texttt{pending}. + \item Agent: claim $\rightarrow$ \texttt{RUNNING}; descarga Excel; Selenium/Imaging; PutObject PDFs. + \item Eventos \texttt{POST /events}; cierre \texttt{PATCH /jobs/\{id\}} (\texttt{completed}/\texttt{failed}). +\end{enumerate} + +Layout MinIO: +\begin{lstlisting}[language={},caption={Prefijos MinIO del bot}] +uploads/{download_job_id}/input.xlsx +downloads/{download_job_id}/{nit}/{radicado}.pdf +downloads/{download_job_id}/_manifest.json +\end{lstlisting} + +\section{Otros flujos de negocio} +\begin{description} + \item[Conteo] Desktop $\rightarrow$ \texttt{POST /count/v1/count} $\rightarrow$ \texttt{POST /count-jobs} $\rightarrow$ validaci\'on humana. + \item[Extracci\'on] \texttt{from-count} o ingest de PDFs $\rightarrow$ workers ARQ $\rightarrow$ filas en BD. + \item[Limpieza] Job cleaning desde \texttt{extraction\_batch\_id} o CSV upload. +\end{description} diff --git a/docs/plan_pruebas/sections/03_estrategia.tex b/docs/plan_pruebas/sections/03_estrategia.tex new file mode 100644 index 0000000..5701876 --- /dev/null +++ b/docs/plan_pruebas/sections/03_estrategia.tex @@ -0,0 +1,63 @@ +\chapter{Estrategia de pruebas} +\label{ch:estrategia} + +\section{Pir\'amide} +\begin{table}[h] +\centering +\begin{tabular}{@{}llp{4.5cm}l@{}} +\toprule +\textbf{Capa} & \textbf{Qu\'e valida} & \textbf{D\'onde} & \textbf{Frecuencia} \\ +\midrule +Unitaria & Dominio, parsers, auth, store & Local + CI & Cada PR \\ +Integraci\'on & HTTP + PG/Redis/MinIO & Compose test & PR / nightly \\ +Contrato & OpenAPI / rutas gateway & CI & Cada PR \\ +E2E & Flujos de negocio & Staging & Nightly / release \\ +Estr\'es & L\'imites y degradaci\'on & Staging aislado & Pre-release \\ +Chaos & Ca\'idas de dependencias & Staging & Mensual \\ +\bottomrule +\end{tabular} +\caption{Pir\'amide de pruebas IPSExtractor.} +\end{table} + +\section{Convenci\'on de identificadores} +\begin{itemize} + \item \texttt{UT--} --- unitarias + \item \texttt{IT--} --- integraci\'on + \item \texttt{E2E-} --- end-to-end de negocio + \item \texttt{ST--} --- estr\'es / carga + \item \texttt{CH-} --- chaos / resiliencia +\end{itemize} + +\section{Entornos} +\begin{description} + \item[local] Mocks de LLM, Imaging y MinIO (moto/LocalStack). + \item[ci] Unitarios + smoke Compose (\texttt{/health/live}). + \item[staging] Stack completo + MinIO staging + LLM con cuota; Imaging solo con VPN de prueba. + \item[stress] Igual que staging, sin usuarios reales; datos sint\'eticos. +\end{description} + +\section{Mocks vs sistemas reales} +\begin{table}[h] +\centering +\begin{tabular}{@{}lcccc@{}} +\toprule +\textbf{Dependencia} & \textbf{Unit/CI} & \textbf{Integr.} & \textbf{Estr\'es} & \textbf{E2E} \\ +\midrule +PostgreSQL & SQLite/TC & Real & Real & Real \\ +Redis & Mock/TC & Real & Real & Real \\ +MinIO & moto/LS & LocalStack & Staging & Staging \\ +LLM & Fake HTTP & Fake & Fake+1 real & Real \\ +Imaging & Mock & Mock & N/A & Real \\ +Chrome & Mock & Mock & N/A & Real \\ +\bottomrule +\end{tabular} +\caption{TC = Testcontainers; LS = LocalStack.} +\end{table} + +\section{Principios} +\begin{enumerate} + \item Un caso = un objetivo verificable (assert expl\'icito). + \item Datos de prueba versionados (fixtures), no datos de producci\'on. + \item Estr\'es solo en staging aislado; umbrales escritos antes de ejecutar. + \item Fallos externos (LLM/Imaging) se aislan con mocks en CI. +\end{enumerate} diff --git a/docs/plan_pruebas/sections/04_unitarias.tex b/docs/plan_pruebas/sections/04_unitarias.tex new file mode 100644 index 0000000..c2294db --- /dev/null +++ b/docs/plan_pruebas/sections/04_unitarias.tex @@ -0,0 +1,75 @@ +\chapter{Pruebas unitarias} +\label{ch:unitarias} + +\section{Gateway} +\begin{longtable}{@{}p{2.4cm}p{4.2cm}p{5.5cm}@{}} +\caption{Unitarias gateway.}\\ +\toprule +\textbf{ID} & \textbf{Caso} & \textbf{Criterio de \'exito} \\ +\midrule +\endhead +UT-GW-01 & Clasificaci\'on path upload vs JSON & Timeouts 600\,s vs 30\,s correctos \\ +UT-GW-02 & Health live sin upstream & Respuesta inmediata \texttt{ok} \\ +UT-GW-03 & Health agregado degradado & Si extraction/cleaning fallan $\rightarrow$ \texttt{degraded} \\ +\bottomrule +\end{longtable} + +\section{Bot-descarga API (\texttt{JobStore})} +Cobertura existente en \texttt{test\_store.py}; ampliar con: +\begin{longtable}{@{}p{2.6cm}p{4.0cm}p{5.5cm}@{}} +\caption{Unitarias bot-descarga API.}\\ +\toprule +\textbf{ID} & \textbf{Caso} & \textbf{Criterio de \'exito} \\ +\midrule +\endhead +UT-BOT-01 & Register + heartbeat & Worker online; offline tras $>$45\,s \\ +UT-BOT-02 & Claim at\'omico & Un solo worker obtiene el job \\ +UT-BOT-03 & Cancel pending & Estado \texttt{cancelled} inmediato \\ +UT-BOT-04 & Cancel running & \texttt{cancel\_requested=true} \\ +UT-BOT-05 & Auth Bearer & Token inv\'alido $\rightarrow$ 401 \\ +UT-BOT-06 & Keys MinIO & Prefijos uploads/downloads correctos \\ +\bottomrule +\end{longtable} + +\section{Agent bot (PC)} +Mantener y extender \texttt{bots/bot\_descarga/tests/}: +\begin{longtable}{@{}p{2.6cm}p{4.0cm}p{5.5cm}@{}} +\caption{Unitarias agent.}\\ +\toprule +\textbf{ID} & \textbf{Caso} & \textbf{Criterio de \'exito} \\ +\midrule +\endhead +UT-AGT-01 & Excel reader / NIT & Headers y limpieza correctos \\ +UT-AGT-02 & PDF validator & Rechaza firmas/pesos inv\'alidos \\ +UT-AGT-03 & Cancel mid-run & \texttt{should\_cancel} detiene loop \\ +UT-AGT-04 & Browser fatal DNS & \texttt{fatal\_error} en Imaging \\ +UT-AGT-05 & MinIO keys & Keys y modo an\'onimo \\ +UT-AGT-06 & RemoteApiClient (mock) & register/claim/events con HTTP fake \\ +\bottomrule +\end{longtable} + +\section{Extraction / Cleaning / Count / Desktop} +\begin{longtable}{@{}p{2.6cm}p{4.2cm}p{5.3cm}@{}} +\caption{Unitarias resto de servicios.}\\ +\toprule +\textbf{ID} & \textbf{Caso} & \textbf{Criterio de \'exito} \\ +\midrule +\endhead +UT-EXT-01 & Rate limiter LLM & Respeta RPM/TPM configurados \\ +UT-EXT-02 & Normalizer JSON & Salida can\'onica estable \\ +UT-CLN-01 & Reglas CSV & Numeric/text/special/dates \\ +UT-CLN-02 & Type inference & Tipos coherentes con fixtures \\ +UT-CNT-01 & Features ML & Vector de features esperado \\ +UT-CNT-02 & PDF oversized & Rechazo controlado \\ +UT-DES-01 & apiClient errores & Manejo de 4xx/5xx \\ +UT-DES-02 & Wizard downloads & Payload job correcto \\ +\bottomrule +\end{longtable} + +\section{Comandos de ejecuci\'on} +\begin{lstlisting}[language=bash,caption={Suite unitaria r\'apida}] +cd bots/bot_descarga && python -m pytest tests/ -q +cd services/bot_descarga && pytest -q +cd services/desktop_app && npm test +cd services/services-count-facturas-medicas-serving && go test ./... +\end{lstlisting} diff --git a/docs/plan_pruebas/sections/05_integracion.tex b/docs/plan_pruebas/sections/05_integracion.tex new file mode 100644 index 0000000..0817428 --- /dev/null +++ b/docs/plan_pruebas/sections/05_integracion.tex @@ -0,0 +1,60 @@ +\chapter{Pruebas de integraci\'on} +\label{ch:integracion} + +\section{Stack de integraci\'on} +Compose de prueba con PostgreSQL, Redis, LocalStack (MinIO), \texttt{bot-descarga}, \texttt{api\_gateway} y, seg\'un caso, extraction/cleaning/count-api. LLM siempre mockeado en CI. + +\section{Gateway} +\begin{longtable}{@{}p{2.4cm}p{4.2cm}p{5.5cm}@{}} +\caption{Integraci\'on gateway.}\\ +\toprule +\textbf{ID} & \textbf{Caso} & \textbf{Criterio de \'exito} \\ +\midrule +\endhead +IT-GW-01 & Proxy \texttt{/bot-descarga/health} & 200 desde host :8189 \\ +IT-GW-02 & Proxy \texttt{/tasks} & Llega a extraction \\ +IT-GW-03 & Upstream ca\'ido & 502/504 sin colgar el gateway \\ +IT-GW-04 & Upload path timeout & Rutas de upload usan 600\,s \\ +\bottomrule +\end{longtable} + +\section{Bot-descarga} +\begin{longtable}{@{}p{2.6cm}p{4.5cm}p{5.0cm}@{}} +\caption{Integraci\'on bot-descarga.}\\ +\toprule +\textbf{ID} & \textbf{Caso} & \textbf{Criterio de \'exito} \\ +\midrule +\endhead +IT-BOT-01 & Register $\rightarrow$ create $\rightarrow$ claim & Job RUNNING una sola vez \\ +IT-BOT-02 & Upload Excel a MinIO & Key \texttt{uploads/\{id\}/} presente \\ +IT-BOT-03 & Events + patch complete & Historial y status finales \\ +IT-BOT-04 & Worker offline create & HTTP 409 \\ +IT-BOT-05 & Parent job inexistente & HTTP 404 \\ +IT-BOT-06 & MinIO down en create & Job failed + HTTP 502 \\ +\bottomrule +\end{longtable} + +\section{Extraction / Cleaning / Count} +\begin{longtable}{@{}p{2.6cm}p{4.5cm}p{5.0cm}@{}} +\caption{Integraci\'on extraction, cleaning, count y BD.}\\ +\toprule +\textbf{ID} & \textbf{Caso} & \textbf{Criterio de \'exito} \\ +\midrule +\endhead +IT-EXT-01 & Task $\rightarrow$ ARQ $\rightarrow$ rows & Filas en PG con LLM fake \\ +IT-EXT-02 & Redis ca\'ido & Fallback sync o error claro \\ +IT-EXT-03 & from-count lee \texttt{/data} & Paths v\'alidos montados \\ +IT-CLN-01 & Job desde batch\_id & \texttt{cleaned\_rows} persistidos \\ +IT-CLN-02 & CSV upload job & Preview + apply \\ +IT-CNT-01 & PDF fixture $\rightarrow$ JSON & Paridad con baseline \\ +IT-DB-01 & Migraciones 0001--0004 & BD limpia aplica sin error \\ +\bottomrule +\end{longtable} + +\section{Smoke m\'inimo post-compose} +\begin{lstlisting}[language=bash,caption={Smoke health}] +docker compose up --build -d +curl http://127.0.0.1:8189/health/live +curl http://127.0.0.1:8189/health +\end{lstlisting} +Nota Windows: usar \texttt{127.0.0.1}, no \texttt{localhost} (IPv6). diff --git a/docs/plan_pruebas/sections/06_e2e.tex b/docs/plan_pruebas/sections/06_e2e.tex new file mode 100644 index 0000000..28b03f0 --- /dev/null +++ b/docs/plan_pruebas/sections/06_e2e.tex @@ -0,0 +1,54 @@ +\chapter{Pruebas end-to-end} +\label{ch:e2e} + +\section{Matriz de flujos de negocio} +\begin{longtable}{@{}p{1.8cm}p{2.8cm}p{5.0cm}p{3.0cm}@{}} +\caption{Casos E2E cr\'iticos.}\\ +\toprule +\textbf{ID} & \textbf{Flujo} & \textbf{Pasos m\'inimos} & \textbf{\'Exito} \\ +\midrule +\endhead +E2E-01 & Conteo & Lotes PDF $\rightarrow$ \texttt{/count/v1/count} $\rightarrow$ \texttt{/count-jobs} $\rightarrow$ PATCH & Conteos editables \\ +E2E-02 & Extracci\'on from-count & Tras E2E-01 $\rightarrow$ task extraction $\rightarrow$ rows & Filas coherentes \\ +E2E-03 & Extracci\'on ingest & Upload PDFs $\rightarrow$ task $\rightarrow$ rows & Paridad con fixtures \\ +E2E-04 & Limpieza & Batch extraction $\rightarrow$ cleaning job & Reglas aplicadas \\ +E2E-05 & Bot feliz & Worker online $\rightarrow$ Excel $\rightarrow$ claim $\rightarrow$ PDFs MinIO $\rightarrow$ completed & Manifest + events \\ +E2E-06 & Bot cancel & Cancel pending y running & Estados correctos \\ +E2E-07 & Worker offline & Create job sin heartbeat & HTTP 409 \\ +E2E-08 & Pipeline largo & Descarga $\rightarrow$ extracci\'on PDFs & Sin corrupci\'on \\ +\bottomrule +\end{longtable} + +\section{Detalle E2E-05 (bot descarga)} +\subsection{Precondiciones} +\begin{itemize} + \item Stack Compose healthy; MinIO accesible; token \texttt{BOT\_API\_TOKEN} compartido. + \item Job Desktop padre existente en tabla \texttt{jobs}. + \item Agent registrado y con heartbeat $<$45\,s. + \item Excel fixture con $N$ radicados v\'alidos ($N\geq 3$ en smoke; $N\geq 50$ en regresi\'on). +\end{itemize} + +\subsection{Pasos} +\begin{enumerate} + \item Arrancar agent: \texttt{python -m bot\_descarga.worker.run --api-url http://127.0.0.1:8189/bot-descarga ...} + \item Desde Desktop o HTTP: crear job multipart con Excel. + \item Verificar claim y descarga de Excel. + \item (Staging Imaging) Completar descargas; en CI mock: simular eventos + PutObject. + \item Verificar \texttt{completed}, lista \texttt{/files} y \texttt{\_manifest.json}. +\end{enumerate} + +\subsection{Asserts} +\begin{itemize} + \item Un \'unico claim; status final \texttt{completed} o \texttt{failed} expl\'icito. + \item Eventos ordenados; \texttt{processed/total} coherentes. + \item Objetos MinIO bajo \texttt{downloads/\{id\}/}. +\end{itemize} + +\section{Desktop} +Checklist manual / automatizable: +\begin{itemize} + \item Wizard de descargas (2 pasos) y panel de resultados. + \item Polling de job ($\sim$3\,s) y workers ($\sim$5\,s). + \item Token en \texttt{localStorage} (\texttt{botApiToken}). +\end{itemize} +Gap conocido a resolver antes de E2E desktop completo: import de m\'odulo downloads ausente referenciado en \texttt{App.tsx}. diff --git a/docs/plan_pruebas/sections/07_estres.tex b/docs/plan_pruebas/sections/07_estres.tex new file mode 100644 index 0000000..64182d5 --- /dev/null +++ b/docs/plan_pruebas/sections/07_estres.tex @@ -0,0 +1,90 @@ +\chapter{Pruebas de estr\'es y carga} +\label{ch:estres} + +\section{Herramienta} +Se usa \textbf{k6} (Grafana Labs) contra \texttt{http://127.0.0.1:8189}. Scripts de referencia en \texttt{docs/plan\_pruebas/scripts/}. M\'etricas complementarias: \texttt{docker stats}, \texttt{pg\_stat\_activity}, Redis INFO, logs del gateway. + +\section{Perfiles de carga} +Por defecto, en el host de la secci\'on~\ref{sec:host-pruebas}, aplicar el perfil \textbf{host-compartido-8gb}. El perfil \textbf{laboratorio} solo aplica si el Docker Engine est\'a dedicado a IPS (sin MLflow/Airflow/n8n, $\geq$3\,GiB libres). + +\begin{table}[ht] +\centering +\begin{tabular}{@{}p{2.3cm}p{5.5cm}p{5.0cm}@{}} +\toprule +\textbf{ID} & \textbf{host-compartido-8gb} & \textbf{laboratorio} \\ +\midrule +ST-GW-01 & 5$\rightarrow$15$\rightarrow$25 VUs; p95 $<$500\,ms & 50--100 VUs; p95 $<$300\,ms \\ +ST-BOT-01 & 3--8 workers / 8\,s & 20--50 workers / 8\,s \\ +ST-BOT-03 & 5--15 VUs events & hasta 40 VUs / 5k events \\ +ST-COUNT-01 & 1 rps, max 3--5 VUs; evitar si RAM libre $<$3\,GiB & 5--20 lotes $\times$10 \\ +ST-EXT-01 & 20--50 tasks; 1 r\'eplica worker & 100--500 tasks; 2 r\'eplicas \\ +\bottomrule +\end{tabular} +\caption{Cargas por perfil de entorno.} +\label{tab:perfiles-carga} +\end{table} + +Con el zoo Docker actual (MLflow $\sim$1{,}86\,GiB + Airflow + n8n, etc.) \textbf{no} ejecutar ST-COUNT-01 ni ST-EXT agresivo sin liberar memoria primero. + +\section{Escenarios (cat\'alogo completo)} +\begin{longtable}{@{}p{2.4cm}p{3.2cm}p{3.5cm}p{3.2cm}@{}} +\caption{Escenarios de estr\'es.}\\ +\toprule +\textbf{ID} & \textbf{Objetivo} & \textbf{Carga (laboratorio)} & \textbf{Observar} \\ +\midrule +\endhead +ST-GW-01 & Gateway concurrente & 50--100 VUs \texttt{/health/live} & p95, 5xx \\ +ST-BOT-01 & Heartbeats masivos & 20--50 workers / 8\,s & PG, offline falsos \\ +ST-BOT-02 & Claim contention & 10 workers, cola jobs & Un claim/job \\ +ST-BOT-03 & Event storm & Hasta 5k events/job & Escritura PG \\ +ST-BOT-04 & Excel grande & 10--50\,MB upload & Timeout 600\,s \\ +ST-MINIO-01 & PutObject paralelo & N PCs & Errores S3 \\ +ST-COUNT-01 & OCR paralelo & 5--20 lotes $\times$10 & RAM, OOM \\ +ST-EXT-01 & Cola ARQ & 100--500 tasks (LLM mock) & Backlog \\ +ST-EXT-02 & LLM real & 20--50 PDFs & 429, costos \\ +ST-DES-01 & Polling desktop & 10 clientes poll & CPU gateway \\ +ST-DB-01 & PG saturaci\'on & Mezcla ST-BOT+EXT & Locks \\ +\bottomrule +\end{longtable} + +\section{Scripts k6 incluidos} +\begin{table}[h] +\centering +\begin{tabular}{@{}llp{4.8cm}@{}} +\toprule +\textbf{Archivo} & \textbf{ID} & \textbf{Target} \\ +\midrule +\texttt{k6\_gateway\_health.js} & ST-GW-01 & \texttt{GET /health/live} \\ +\texttt{k6\_bot\_heartbeats.js} & ST-BOT-01 & \texttt{POST .../workers/heartbeat} \\ +\texttt{k6\_bot\_events.js} & ST-BOT-03 & \texttt{POST .../jobs/\{id\}/events} \\ +\texttt{k6\_count\_batches.js} & ST-COUNT-01 & \texttt{POST /count/v1/count} \\ +\bottomrule +\end{tabular} +\caption{Mapeo script $\leftrightarrow$ escenario.} +\end{table} + +Los scripts aceptan \texttt{PROFILE=shared8g} (default recomendado en este host) o \texttt{PROFILE=lab}. + +\section{SLOs de aceptaci\'on} +\subsection{Perfil host-compartido-8gb (default)} +\begin{itemize} + \item Gateway \texttt{/health/live} p95 $<$\,\SI{500}{\milli\second}; error rate $<$\,1\,\%. + \item No usar el health \emph{agregado} (\texttt{/health}) como SLO de latencia (puede superar 10\,s en este host). + \item Heartbeat: 0 workers offline falsos con $\leq$8 workers y poll $\leq$8\,s. + \item Claim: 0 jobs con dos workers en \texttt{RUNNING}. + \item Count-api: sin OOM; detener la prueba si el host entra en swap sostenido. +\end{itemize} + +\subsection{Perfil laboratorio} +\begin{itemize} + \item Gateway p95 salud: $<$\,\SI{300}{\milli\second}; error rate $<$\,0,1\,\%. + \item Extracci\'on (LLM mock): backlog ARQ drenado con \texttt{WORKER\_REPLICAS=2}. + \item Count-api: sin OOM en carga acordada; PDFs oversized $\rightarrow$ 4xx. +\end{itemize} + +\section{Ejecuci\'on ejemplo} +\begin{lstlisting}[language=bash,caption={Estr\'es gateway en host compartido}] +cd docs/plan_pruebas/scripts +# Documentar entorno: free -h && docker stats --no-stream +k6 run -e BASE_URL=http://127.0.0.1:8189 -e PROFILE=shared8g k6_gateway_health.js +\end{lstlisting} diff --git a/docs/plan_pruebas/sections/08_resiliencia.tex b/docs/plan_pruebas/sections/08_resiliencia.tex new file mode 100644 index 0000000..a410639 --- /dev/null +++ b/docs/plan_pruebas/sections/08_resiliencia.tex @@ -0,0 +1,33 @@ +\chapter{Resiliencia y chaos} +\label{ch:resiliencia} + +\section{Objetivo} +Verificar degradaci\'on controlada y recuperaci\'on sin corrupci\'on de datos cuando fallan dependencias. + +\section{Casos} +\begin{longtable}{@{}p{1.8cm}p{3.5cm}p{4.0cm}p{3.2cm}@{}} +\caption{Casos de resiliencia.}\\ +\toprule +\textbf{ID} & \textbf{Fallo inyectado} & \textbf{Expectativa} & \textbf{Recuperaci\'on} \\ +\midrule +\endhead +CH-01 & Redis down mid-run & Extracci\'on async falla o fallback sync & Stack healthy al reiniciar Redis \\ +CH-02 & MinIO down mid-job & Create job 502 / PutObject warning & Job failed expl\'icito; BD consistente \\ +CH-03 & Agent crash mid-download & Job no queda zombie indefinido & Cancel o failed; sin doble claim \\ +CH-04 & Extraction container kill & Gateway health \texttt{degraded} & Tras restart, health ok \\ +CH-05 & Token inv\'alido masivo & 401 sin saturar workers & Logs claros \\ +CH-06 & Imaging DNS & Agent \texttt{fatal\_error} & Evento visible en Desktop \\ +\bottomrule +\end{longtable} + +\section{Modos de fallo conocidos (bot)} +\begin{itemize} + \item Worker offline al crear job $\rightarrow$ 409. + \item Excel vac\'io $\rightarrow$ 400. + \item Heartbeat $>$45\,s $\rightarrow$ worker \texttt{offline}. + \item Cancel pending vs running (flag \texttt{cancel\_requested}). + \item PDF corrupto $\rightarrow$ reintento o fallo por radicado. +\end{itemize} + +\section{Criterio global} +Tras chaos, \texttt{GET /health/live} debe volver a OK sin intervenci\'on manual (salvo dependencias externas como Imaging/LLM). diff --git a/docs/plan_pruebas/sections/09_datos_entornos.tex b/docs/plan_pruebas/sections/09_datos_entornos.tex new file mode 100644 index 0000000..30a3161 --- /dev/null +++ b/docs/plan_pruebas/sections/09_datos_entornos.tex @@ -0,0 +1,117 @@ +\chapter{Datos de prueba y entornos} +\label{ch:datos} + +\section{Host donde se ejecutan las pruebas} +\label{sec:host-pruebas} + +Las pruebas contra el stack desplegado (smoke, integraci\'on parcial, estr\'es k6) se realizan en el siguiente equipo. \textbf{Todo informe de ejecuci\'on debe citar este entorno} (o indicar expl\'icitamente si se us\'o otro). + +\subsection{Hardware y sistema} +\begin{table}[ht] +\centering +\begin{tabular}{@{}lp{8.5cm}@{}} +\toprule +\textbf{Campo} & \textbf{Valor} \\ +\midrule +Hostname & \texttt{ubuntu} \\ +SO & Ubuntu 24.04.4 LTS (kernel 6.17.0-35-generic) \\ +Arquitectura & x86-64 \\ +Equipo & ASUSTeK X441UAK \\ +CPU & Intel Core i5-7200U @ 2{,}50\,GHz (2 n\'ucleos / 4 hilos) \\ +RAM & 8\,GiB DDR4-2133 (l\'imite visible Docker $\approx$ 7{,}64\,GiB) \\ +Almacenamiento & HDD 500\,GB interno + disco USB 1\,TB EXT4 \\ +Contexto cloud CLI & \texttt{default/inmodata-builker} \\ +\bottomrule +\end{tabular} +\caption{Host de pruebas IPSExtractor (staging compartido).} +\label{tab:host-pruebas} +\end{table} + +\subsection{Stack IPSExtractor observado (idle)} +Medici\'on \texttt{docker stats --no-stream} (13 jul 2026), contenedores IPS: + +\begin{table}[ht] +\centering +\begin{tabular}{@{}lr@{}} +\toprule +\textbf{Contenedor} & \textbf{RAM aprox.} \\ +\midrule +\texttt{ipsextractor-api} (gateway) & 42\,MiB \\ +\texttt{ipsextractor-extraction} & 31\,MiB \\ +\texttt{ipsextractor-worker-1/2} & 28--32\,MiB c/u \\ +\texttt{ipsextractor-cleaning} & 36\,MiB \\ +\texttt{ipsextractor-bot-descarga} & 17\,MiB \\ +\texttt{ipsextractor-count-api} & 13\,MiB (l\'imite 4\,GiB) \\ +\texttt{ipsextractor-db} & 27\,MiB \\ +\texttt{ipsextractor-redis} & 8\,MiB \\ +\midrule +\textbf{Subtotal IPS (idle)} & $\approx$ 230--250\,MiB \\ +\bottomrule +\end{tabular} +\caption{Huella de memoria IPSExtractor en idle.} +\end{table} + +Health verificado: +\begin{lstlisting}[language=bash,caption={Smoke del gateway en el host}] +curl -s http://127.0.0.1:8189/health/live +# {"status":"ok"} + +curl -s http://127.0.0.1:8189/health +# status ok; extraction, cleaning, counting, bot_descarga ok +# Nota: el health agregado puede tardar ~10-12 s en este host. +\end{lstlisting} + +\subsection{Carga concurrente del host (no IPS)} +El mismo Docker Engine ejecuta muchos servicios ajenos a IPSExtractor. En la misma medici\'on destacaban, entre otros: + +\begin{itemize} + \item \texttt{mlflow-server} $\approx$ \textbf{1{,}86\,GiB} ($\sim$24\,\% de la RAM) + \item Airflow (scheduler + webserver + postgres) $\approx$ 530\,MiB + \item \texttt{n8n\_app} $\approx$ 291\,MiB + \item \texttt{postgresql} (instancia compartida) $\approx$ 255\,MiB + \item \texttt{minio-server} $\approx$ 136\,MiB, \texttt{uptime-kuma} $\approx$ 121\,MiB + \item Adem\'as: registry, MariaDB, MongoDB, Qdrant, Portainer, Mermaid, Metabase Postgres, Redis cache, Inmodata API, etc. +\end{itemize} + +\textbf{Consecuencia:} el host es un \emph{staging compartido}, no un laboratorio dedicado. Los SLOs de estr\'es del perfil \texttt{host-compartido-8gb} (cap.~\ref{ch:estres}) son los aplicables por defecto en este equipo. Un fallo por OOM o latencia alta \textbf{no} debe atribuirse autom\'aticamente a un bug de IPSExtractor sin revisar \texttt{docker stats} y \texttt{free -h}. + +\subsection{Requisitos previos a una campa\~na de estr\'es} +\begin{enumerate} + \item Registrar en el informe: fecha, hostname, \texttt{free -h}, salida de \texttt{docker stats --no-stream}. + \item Preferible pausar/detener MLflow y Airflow (y n8n si es posible) para liberar $\geq$2\,GiB antes de k6 ligero, o $\geq$3\,GiB antes de ST-COUNT. + \item Usar siempre \texttt{127.0.0.1:8189} (no \texttt{localhost} por IPv6). +\end{enumerate} + +\section{Fixtures recomendadas} +\begin{description} + \item[PDFs] Peque\~nos (1--2 p\'ag.), medianos, corruptos, $>$ l\'imite, con/sin texto. + \item[Excel bot] 10 / 100 / 1\,000 / 5\,000 radicados; NITs sucios; vac\'ios; columnas mal nombradas. + \item[CSV cleaning] Encodings, nulls, fechas ambiguas, montos COP. + \item[LLM] Respuestas fixture JSON v\'alido / inv\'alido / parcial (sin gastar cuota en CI). + \item[Imaging] HTML stub en CI; portal real solo en m\'aquina con VPN. +\end{description} + +\section{Variables de entorno relevantes} +\begin{longtable}{@{}p{4.2cm}p{8cm}@{}} +\caption{Variables cr\'iticas para pruebas.}\\ +\toprule +\textbf{Variable} & \textbf{Uso} \\ +\midrule +\endhead +\texttt{IPS\_DOCUMENT\_ROOT} & Montaje PDFs $\rightarrow$ \texttt{/data} \\ +\texttt{EXTRACTOR\_API\_KEY} / modelo & LLM extracci\'on \\ +\texttt{WORKER\_REPLICAS} & Escala ARQ \\ +\texttt{ASYNC\_QUEUE}, \texttt{REDIS\_URL} & Cola async \\ +\texttt{BOT\_API\_TOKEN} & Bearer bot API + agent + desktop \\ +\texttt{MINIO\_ENDPOINT}, \texttt{MINIO\_BUCKET} & S3 bot \\ +\texttt{BOT\_IMAGING\_URL} & Portal Selenium \\ +\texttt{OCR\_POOL\_SIZE}, \texttt{COUNT\_API\_MEMORY} & Tuning count-api \\ +\bottomrule +\end{longtable} + +\section{Seguridad en entornos de prueba} +\begin{itemize} + \item No usar API keys reales hardcodeadas en compose para CI/staging compartido. + \item Token de bot distinto por entorno; rotar tras pruebas p\'ublicas. + \item Datos sint\'eticos o anonimizados; nunca radicados reales en reportes p\'ublicos. +\end{itemize} diff --git a/docs/plan_pruebas/sections/10_herramientas_ci.tex b/docs/plan_pruebas/sections/10_herramientas_ci.tex new file mode 100644 index 0000000..42d23cf --- /dev/null +++ b/docs/plan_pruebas/sections/10_herramientas_ci.tex @@ -0,0 +1,47 @@ +\chapter{Herramientas profesionales y CI} +\label{ch:herramientas} + +\section{Stack de tooling} +\begin{table}[h] +\centering +\begin{tabular}{@{}llp{5.5cm}@{}} +\toprule +\textbf{Herramienta} & \textbf{Rol} & \textbf{D\'onde} \\ +\midrule +pytest & Unit/integraci\'on Python & bots, bot-descarga, extraction \\ +Vitest & Unit frontend & desktop\_app \\ +go test & Unit count-api & services-count-... \\ +k6 & Carga / estr\'es & \texttt{docs/plan\_pruebas/scripts} \\ +Docker Compose & Stack + smoke & ra\'iz del repo \\ +latexmk & Compilar este plan & \texttt{docs/plan\_pruebas} \\ +respx / httpx mock & Fake HTTP LLM/API & CI Python \\ +LocalStack / moto & MinIO local & Integraci\'on \\ +\bottomrule +\end{tabular} +\caption{Herramientas profesionales adoptadas.} +\end{table} + +\section{Pipeline CI objetivo (no implementado en esta entrega)} +Orden recomendado por PR: +\begin{enumerate} + \item \texttt{pytest} bots + bot-descarga. + \item \texttt{npm test} desktop. + \item \texttt{go test ./...} count-api. + \item \texttt{docker compose up} + curl smoke \texttt{/health/live}. + \item Nightly: k6 ST-GW-01 y ST-BOT-01 en staging. +\end{enumerate} + +\section{Compilar este documento} +\begin{lstlisting}[language=bash,caption={latexmk}] +cd docs/plan_pruebas +latexmk -pdf main.tex +\end{lstlisting} + +\section{Prioridad de implementaci\'on de tests (ROI)} +\begin{enumerate} + \item FastAPI TestClient bot-descarga (claim, cancel, auth, offline). + \item Integraci\'on gateway $\rightarrow$ bot-descarga $\rightarrow$ PG $\rightarrow$ MinIO local. + \item Estr\'es heartbeats + events (patr\'on de carga real del bot). + \item Estr\'es count-api (riesgo OOM). + \item CI m\'inimo para no perder cobertura. +\end{enumerate} diff --git a/docs/plan_pruebas/sections/11_cronograma_aceptacion.tex b/docs/plan_pruebas/sections/11_cronograma_aceptacion.tex new file mode 100644 index 0000000..1e646e0 --- /dev/null +++ b/docs/plan_pruebas/sections/11_cronograma_aceptacion.tex @@ -0,0 +1,45 @@ +\chapter{Cronograma y criterios de aceptaci\'on} +\label{ch:cronograma} + +\section{Fases sugeridas} +\begin{longtable}{@{}p{1.5cm}p{4.5cm}p{6.2cm}@{}} +\caption{Hoja de ruta de calidad.}\\ +\toprule +\textbf{Fase} & \textbf{Entrega} & \textbf{Criterio de cierre} \\ +\midrule +\endhead +0 & Este documento LaTeX + scripts k6 & PDF compilado; scripts documentados \\ +1 & CI + unitarios API bot & Pipeline verde en PR \\ +2 & Compose test + IT-BOT-01..06 & Integraci\'on bot estable \\ +3 & Extraction/cleaning + LLM mock & IT-EXT/CLN verdes \\ +4 & Campa\~nas k6 staging & SLOs cap.~\ref{ch:estres} cumplidos \\ +5 & E2E staging + Imaging & E2E-01..05 firmados \\ +6 & Chaos mensual & CH-01..06 con informe \\ +\bottomrule +\end{longtable} + +\section{Definition of Done por release} +Un release es aceptable cuando: +\begin{enumerate} + \item Checklist del ap\'endice ``Checklist de release'' completa. + \item Ning\'un caso \texttt{E2E-01}--\texttt{E2E-05} fallido sin waiver documentado. + \item ST-GW-01 y ST-BOT-01 dentro de SLO en staging. + \item Hallazgos P0/P1 de resiliencia cerrados o mitigados. +\end{enumerate} + +\section{Plantilla de informe de ejecuci\'on} +\begin{lstlisting}[language={},caption={Campos m\'inimos del informe}] +Fecha / entorno / commit +Casos ejecutados (IDs) y resultado +p95 latencia / error rate / max RSS +Jobs fallidos y causa +Costos LLM (si aplica) +Waivers y follow-ups +\end{lstlisting} + +\section{Responsables (roles)} +\begin{itemize} + \item \textbf{QA / Dev:} ejecutar unitarias e integraci\'on; mantener fixtures. + \item \textbf{Ops:} staging, k6, m\'etricas Docker/PG. + \item \textbf{Producto:} firmar E2E de negocio en release. +\end{itemize}