Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6.2 KiB
id, title, status, type, domain, scope, priority, depends, blocks, related, created, updated, tags
| id | title | status | type | domain | scope | priority | depends | blocks | related | created | updated | tags | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0167 | fn run de library function Go ejecuta go test del paquete entero (arrastra tests flaky vecinos) | pendiente | enhancement |
|
registry-only | media |
|
2026-06-03 | 2026-06-03 |
|
0167 — fn run de library function Go ejecuta go test del paquete entero
APP Metadata
| Campo | Valor |
|---|---|
| ID | 0167 |
| Estado | pendiente |
| Prioridad | media |
| Tipo | enhancement — dispatcher de fn run |
Contexto
Cuando fn run <id> recibe una library function Go sin main.go que tiene tests
declarados (tested: true + test_file_path), el dispatcher (cmd/fn/run.go:171-181)
ejecuta:
go test -v -count=1 -tags fts5 ./functions/<domain> # el PAQUETE ENTERO
Es decir, no ejecuta "la función" (no se puede: no tiene main), sino que corre todos
los tests del paquete. Consecuencia: el éxito de fn run miFuncion depende de que pasen
los tests de todas las demás funciones del mismo paquete, no solo los suyos.
Cómo se manifestó
Los DAGs daily-registry-audit y weekly-deep-scan del dag_engine invocaban funciones
*_go_infra (find_unused_functions, artefact_doctor, etc.) como function: steps.
Cada step disparaba go test ./functions/infra (paquete completo), que contiene tests
impuros con recursos fijos:
TestSSHTunnelOpenClose→bind [127.0.0.1]:19876: Address already in useTestDockerContainerExec→listen unix .../docker_exec_test.sock: bind: invalid argument(path de socket > 108 chars con TMPDIR largo)
Al correr dos function: steps en paralelo (ambos depends del mismo padre), las dos
invocaciones de go test ./functions/infra colisionaban en el puerto fijo 19876 →
una pasaba y la otra fallaba de forma no determinista. Resultado: el DAG fallaba sin
auditar nada, y el fallo parecía "la auditoría encontró un problema" cuando en realidad
era un test de red vecino.
Nota: el síntoma operativo en los DAGs ya se resolvió por otra vía (2026-06-03): los steps ahora usan
audit_doctor_snapshot_bash_infra(Bash), que ejecutafn doctor <sub>real en vez dego testdel paquete. Este issue es la causa raíz general del dispatcher, que sigue afectando a cualquierfn run <library_go_fn_con_tests>.
Problema
fn runde una library function NO ejecuta la función — corre el paquete de test entero.- Los tests impuros de un paquete (puertos/sockets/red fijos) no son seguros para ejecuciones concurrentes ni reproducibles en cualquier entorno (TMPDIR, CI).
- Un único test flaky en
functions/infrarompefn runde las ~N funciones testeadas del paquete, y por extensión cualquier DAG/cron que las invoque.
Opciones de solución (decidir en implementación)
Opción A — library Go sin main → siempre compile-check (go vet/go build)
fn run <lib_fn> significa "verifica que la función va"; para código sin main eso es
"compila". Testear es responsabilidad de go test / CI, no de fn run en un cron.
- Pro: determinista, rápido, elimina el flaky de raíz.
- Contra: rompe el comportamiento documentado en
CLAUDE.md("fn run filter_slice_go_core→ Go function con tests →go test -v"). Perderíamos la capacidad de correr los tests de una función víafn run.
Opción B — go test acotado con -run a los tests de la función
Si la función declara sus tests, ejecutar solo esos:
go test -v -count=1 -tags fts5 -run '^(TestX|TestY)$' ./functions/<domain>
- Pro: aísla del flaky vecino manteniendo "fn run corre mis tests".
- Contra / RIESGO: si los nombres de
fn.Tests(frontmatter YAML,registry/parser.go:32) tienen drift respecto al código,-runno matchea ygo testsale 0 con "no tests to run" → falso-verde en una primitiva crítica de todo el ecosistema. Mitigación obligatoria si se elige B: reconciliarfn.Testscon los tests extraídos por el indexer (registry/test_parser.go::parseGoTests, que ya pueblaunit_tests) y/o detectar "0 tests ejecutados" parseando el output y tratarlo como fallo.
Opción C — aislar los tests impuros del paquete
Hacer robustos los tests culpables: puerto efímero (:0 en vez de 19876), socket en path
corto bajo /tmp con nombre acotado, t.Parallel-safe. No cambia el dispatcher pero reduce
la probabilidad de colisión.
- Pro: no toca
fn run(cero blast radius sistémico). - Contra: no resuelve el problema conceptual (sigue corriendo el paquete entero); otros paquetes pueden introducir tests impuros nuevos y reincidir.
Recomendación
Combinar C (saneamiento inmediato de TestSSHTunnelOpenClose y TestDockerContainerExec,
bajo riesgo) con B endurecida (acotar -run + guard anti-falso-verde apoyado en
unit_tests extraídos, no en el frontmatter manual). La Opción A es la más limpia
conceptualmente pero rompe comportamiento documentado; evaluar si ese comportamiento
("fn run corre los tests") aún se usa de verdad o puede deprecarse hacia go test directo.
Definition of Done
| Escenario | Tipo | Comando / evidencia | Resultado esperado |
|---|---|---|---|
Golden: fn run de library fn testeada |
e2e | ./fn run find_unused_functions_go_infra |
exit 0 sin depender de tests de funciones vecinas |
Edge: dos fn run concurrentes del mismo paquete |
e2e | dos invocaciones en paralelo de funciones de functions/infra |
ambas exit 0, sin colisión de puerto/socket |
| Error: nombres de test con drift (si se elige B) | unit | fn.Tests con un nombre inexistente |
NO produce falso-verde (se detecta "0 tests run" → fallo) |
| Tests impuros saneados | unit | go test -run 'TestSSHTunnelOpenClose|TestDockerContainerExec' ./functions/infra repetido 5× |
5/5 PASS deterministas |
Notas
- Archivos clave:
cmd/fn/run.go(dispatcher, líneas 145-194),registry/parser.go(campoTests),registry/test_parser.go(extracción de nombres de test),functions/infra/ssh_tunnel_open_close_test.goyfunctions/infra/docker_container_exec_test.go(tests culpables). - Relacionado con 0077 (fn-run-bash-output-mudo): familia de issues sobre la semántica y
observabilidad de
fn run.