@saulwade/swl-ses 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +238 -0
- package/README.md +560 -0
- package/_userland/agentes/.gitkeep +0 -0
- package/_userland/habilidades/.gitkeep +0 -0
- package/agentes/.evolved.json +9 -0
- package/agentes/accesibilidad-wcag-swl.md +692 -0
- package/agentes/arquitecto-swl.md +238 -0
- package/agentes/auto-evolucion-swl.md +854 -0
- package/agentes/backend-api-swl.md +470 -0
- package/agentes/backend-csharp-swl.md +418 -0
- package/agentes/backend-go-swl.md +388 -0
- package/agentes/backend-java-swl.md +279 -0
- package/agentes/backend-node-swl.md +477 -0
- package/agentes/backend-python-swl.md +608 -0
- package/agentes/backend-rust-swl.md +362 -0
- package/agentes/backend-workers-swl.md +480 -0
- package/agentes/cloud-infra-swl.md +485 -0
- package/agentes/consolidador-swl.md +539 -0
- package/agentes/datos-swl.md +584 -0
- package/agentes/depurador-swl.md +349 -0
- package/agentes/devops-ci-swl.md +374 -0
- package/agentes/disenador-ui-swl.md +558 -0
- package/agentes/documentador-swl.md +343 -0
- package/agentes/evals/arquitecto-swl.evals.json +56 -0
- package/agentes/evals/auto-evolucion-swl.evals.json +68 -0
- package/agentes/evals/implementador-swl.evals.json +56 -0
- package/agentes/evals/orquestador-swl.evals.json +60 -0
- package/agentes/evals/perfilador-usuario-swl.evals.json +60 -0
- package/agentes/evals/red-team-swl.evals.json +59 -0
- package/agentes/evals/revisor-codigo-swl.evals.json +59 -0
- package/agentes/frontend-angular-swl.md +627 -0
- package/agentes/frontend-css-swl.md +720 -0
- package/agentes/frontend-react-swl.md +696 -0
- package/agentes/frontend-swl.md +500 -0
- package/agentes/frontend-tailwind-swl.md +830 -0
- package/agentes/implementador-swl.md +328 -0
- package/agentes/investigador-swl.md +430 -0
- package/agentes/investigador-ux-swl.md +500 -0
- package/agentes/llm-apps-swl.md +276 -0
- package/agentes/migrador-swl.md +417 -0
- package/agentes/mobile-android-swl.md +509 -0
- package/agentes/mobile-cross-swl.md +539 -0
- package/agentes/mobile-ios-swl.md +500 -0
- package/agentes/mobile-testing-swl.md +300 -0
- package/agentes/notificador-swl.md +916 -0
- package/agentes/observabilidad-swl.md +436 -0
- package/agentes/orquestador-swl.md +884 -0
- package/agentes/pagos-swl.md +283 -0
- package/agentes/perfilador-usuario-swl.md +306 -0
- package/agentes/planificador-swl.md +402 -0
- package/agentes/producto-prd-swl.md +587 -0
- package/agentes/red-team-swl.md +216 -0
- package/agentes/release-manager-swl.md +568 -0
- package/agentes/rendimiento-swl.md +714 -0
- package/agentes/resolutor-build-swl.md +243 -0
- package/agentes/revisor-angular-swl.md +276 -0
- package/agentes/revisor-codigo-swl.md +348 -0
- package/agentes/revisor-csharp-swl.md +262 -0
- package/agentes/revisor-go-swl.md +257 -0
- package/agentes/revisor-java-swl.md +255 -0
- package/agentes/revisor-kotlin-swl.md +271 -0
- package/agentes/revisor-nextjs-swl.md +279 -0
- package/agentes/revisor-php-swl.md +269 -0
- package/agentes/revisor-react-swl.md +276 -0
- package/agentes/revisor-rust-swl.md +344 -0
- package/agentes/revisor-seguridad-swl.md +390 -0
- package/agentes/revisor-swift-swl.md +266 -0
- package/agentes/revisor-typescript-swl.md +344 -0
- package/agentes/sre-swl.md +265 -0
- package/agentes/tdd-qa-swl.md +354 -0
- package/agentes/ux-disenador-swl.md +501 -0
- package/bin/lib/bot-comandos.js +1030 -0
- package/bin/lib/bot-discovery.js +182 -0
- package/bin/lib/bot-git.js +142 -0
- package/bin/swl-ses.js +325 -0
- package/bin/swl-telegram-bot.js +442 -0
- package/bin/swl-telegram-bot.plist +21 -0
- package/bin/swl-telegram-bot.service +14 -0
- package/comandos/swl/.evolved.json +23 -0
- package/comandos/swl/actualizar.md +174 -0
- package/comandos/swl/adoptar-proyecto.md +207 -0
- package/comandos/swl/aprender.md +701 -0
- package/comandos/swl/auditar-deps.md +134 -0
- package/comandos/swl/autoresearch.md +170 -0
- package/comandos/swl/ayuda.md +224 -0
- package/comandos/swl/brainstorm.md +50 -0
- package/comandos/swl/checkpoint.md +330 -0
- package/comandos/swl/compactar.md +283 -0
- package/comandos/swl/configurar-ci.md +227 -0
- package/comandos/swl/contexto.md +112 -0
- package/comandos/swl/contribuir.md +233 -0
- package/comandos/swl/crear-skill.md +292 -0
- package/comandos/swl/cron.md +196 -0
- package/comandos/swl/dashboard.md +146 -0
- package/comandos/swl/discutir-fase.md +230 -0
- package/comandos/swl/ejecutar-fase.md +135 -0
- package/comandos/swl/evaluar-skill.md +487 -0
- package/comandos/swl/evolucion-estado.md +142 -0
- package/comandos/swl/evolucionar.md +259 -0
- package/comandos/swl/exportar-vault.md +189 -0
- package/comandos/swl/gateway.md +158 -0
- package/comandos/swl/inbox.md +116 -0
- package/comandos/swl/instalar.md +220 -0
- package/comandos/swl/instintos.md +86 -0
- package/comandos/swl/mapear-codebase.md +312 -0
- package/comandos/swl/mcp-status.md +175 -0
- package/comandos/swl/metricas.md +270 -0
- package/comandos/swl/modelo.md +102 -0
- package/comandos/swl/notificaciones.md +396 -0
- package/comandos/swl/nuevo-proyecto.md +154 -0
- package/comandos/swl/planear-fase.md +221 -0
- package/comandos/swl/plugins.md +256 -0
- package/comandos/swl/reflect-skills.md +125 -0
- package/comandos/swl/release.md +217 -0
- package/comandos/swl/revisar-impacto.md +206 -0
- package/comandos/swl/revisar.md +330 -0
- package/comandos/swl/salud.md +363 -0
- package/comandos/swl/sesiones.md +200 -0
- package/comandos/swl/skill-search.md +113 -0
- package/comandos/swl/verificar.md +585 -0
- package/comandos/swl/wiki.md +620 -0
- package/contextos/dev.md +32 -0
- package/contextos/research.md +30 -0
- package/contextos/review.md +31 -0
- package/habilidades/accesibilidad-a11y/SKILL.md +201 -0
- package/habilidades/accesibilidad-a11y/evals/evals.json +56 -0
- package/habilidades/accesibilidad-a11y/recursos/ejemplos-y-checklist-completo.md +441 -0
- package/habilidades/agent-browser/SKILL.md +218 -0
- package/habilidades/agentes-como-servicio/SKILL.md +218 -0
- package/habilidades/ai-runtime-security/SKILL.md +273 -0
- package/habilidades/angular-avanzado/SKILL.md +164 -0
- package/habilidades/angular-avanzado/recursos/ejemplos-avanzados.md +219 -0
- package/habilidades/angular-moderno/SKILL.md +186 -0
- package/habilidades/angular-moderno/evals/evals.json +45 -0
- package/habilidades/angular-moderno/recursos/ejemplos-avanzados.md +106 -0
- package/habilidades/api-rest-diseno/SKILL.md +191 -0
- package/habilidades/api-rest-diseno/recursos/openapi-template.yaml +506 -0
- package/habilidades/api-rest-diseno/recursos/referencia-api.md +140 -0
- package/habilidades/aprendizaje-continuo/SKILL.md +151 -0
- package/habilidades/aprendizaje-continuo/evals/evals.json +53 -0
- package/habilidades/aprendizaje-continuo/recursos/referencia-instintos.md +290 -0
- package/habilidades/async-python/SKILL.md +149 -0
- package/habilidades/async-python/evals/evals.json +47 -0
- package/habilidades/async-python/recursos/patrones-y-ejemplos-completos.md +292 -0
- package/habilidades/auth-patrones/.evolved.json +9 -0
- package/habilidades/auth-patrones/SKILL.md +413 -0
- package/habilidades/auth-patrones/recursos/implementaciones-completas.md +229 -0
- package/habilidades/auto-evolucion-protocolo/SKILL.md +276 -0
- package/habilidades/auto-evolucion-protocolo/evals/evals.json +55 -0
- package/habilidades/auto-evolucion-protocolo/recursos/referencia-completa.md +145 -0
- package/habilidades/autoresearch/SKILL.md +268 -0
- package/habilidades/autoresearch/evals/evals.json +41 -0
- package/habilidades/autoresearch/recursos/checklist-template.md +191 -0
- package/habilidades/autoresearch/scripts/calcular-score.js +88 -0
- package/habilidades/azure-cloud/SKILL.md +308 -0
- package/habilidades/azure-cloud/recursos/aks.md +327 -0
- package/habilidades/backend-mcp-servidor/SKILL.md +270 -0
- package/habilidades/backend-production-resilience/SKILL.md +288 -0
- package/habilidades/brainstorming/SKILL.md +295 -0
- package/habilidades/brainstorming/recursos/componentes-html.md +247 -0
- package/habilidades/build-errors-cpp/SKILL.md +270 -0
- package/habilidades/build-errors-csharp/SKILL.md +265 -0
- package/habilidades/build-errors-go/SKILL.md +306 -0
- package/habilidades/build-errors-java/SKILL.md +278 -0
- package/habilidades/build-errors-kotlin/SKILL.md +303 -0
- package/habilidades/build-errors-nextjs/SKILL.md +312 -0
- package/habilidades/build-errors-php/SKILL.md +270 -0
- package/habilidades/build-errors-python/SKILL.md +292 -0
- package/habilidades/build-errors-rust/SKILL.md +284 -0
- package/habilidades/build-errors-swift/SKILL.md +272 -0
- package/habilidades/build-errors-typescript/SKILL.md +369 -0
- package/habilidades/checklist-calidad/SKILL.md +271 -0
- package/habilidades/checklist-calidad/recursos/quality-report-template.md +148 -0
- package/habilidades/checklist-seguridad/SKILL.md +285 -0
- package/habilidades/checkpoints-verificacion/SKILL.md +298 -0
- package/habilidades/checkpoints-verificacion/recursos/checkpoint-templates.md +360 -0
- package/habilidades/ci-cd-pipelines/SKILL.md +157 -0
- package/habilidades/ci-cd-pipelines/recursos/github-actions-template.yaml +403 -0
- package/habilidades/ci-cd-pipelines/recursos/pipelines-completos.md +487 -0
- package/habilidades/cloud-aws/SKILL.md +142 -0
- package/habilidades/cloud-aws/recursos/servicios-aws-referencia.md +321 -0
- package/habilidades/compactacion-contexto/SKILL.md +247 -0
- package/habilidades/contenedores-docker/SKILL.md +137 -0
- package/habilidades/contenedores-docker/recursos/dockerfile-template.dockerfile +160 -0
- package/habilidades/contenedores-docker/recursos/ejemplos-y-configuraciones.md +327 -0
- package/habilidades/context-builder/SKILL.md +170 -0
- package/habilidades/control-profundidad/SKILL.md +128 -0
- package/habilidades/csharp-experto/SKILL.md +322 -0
- package/habilidades/csharp-patrones/SKILL.md +316 -0
- package/habilidades/csharp-testing/SKILL.md +286 -0
- package/habilidades/css-moderno/SKILL.md +166 -0
- package/habilidades/css-moderno/evals/evals.json +43 -0
- package/habilidades/css-moderno/recursos/ejemplos-y-patrones-completos.md +337 -0
- package/habilidades/datos-etl/SKILL.md +129 -0
- package/habilidades/datos-etl/recursos/implementaciones-completas.md +322 -0
- package/habilidades/dbml-experto/SKILL.md +339 -0
- package/habilidades/dbml-experto/evals/evals.json +56 -0
- package/habilidades/dependencias-auditoria/SKILL.md +320 -0
- package/habilidades/deprecacion-migracion/SKILL.md +169 -0
- package/habilidades/deprecacion-migracion/recursos/implementaciones-completas.md +220 -0
- package/habilidades/design-tokens/SKILL.md +158 -0
- package/habilidades/design-tokens/recursos/tokens-y-configuracion.md +363 -0
- package/habilidades/devsecops-pipeline-security/SKILL.md +309 -0
- package/habilidades/diagrama-arquitectura/SKILL.md +165 -0
- package/habilidades/diagrama-arquitectura/assets/template.html +276 -0
- package/habilidades/discutir-fase/SKILL.md +188 -0
- package/habilidades/diseno-herramientas-agente/SKILL.md +199 -0
- package/habilidades/diseno-responsivo/SKILL.md +186 -0
- package/habilidades/diseno-responsivo/recursos/ejemplos-layouts.md +156 -0
- package/habilidades/django-experto/SKILL.md +205 -0
- package/habilidades/django-experto/recursos/async-django.md +390 -0
- package/habilidades/django-experto/recursos/drf-patrones.md +438 -0
- package/habilidades/django-experto/recursos/orm-avanzado.md +382 -0
- package/habilidades/django-experto/recursos/referencia-completa.md +188 -0
- package/habilidades/django-experto/recursos/testing-django.md +415 -0
- package/habilidades/doc-sync/SKILL.md +280 -0
- package/habilidades/drift-detection/SKILL.md +179 -0
- package/habilidades/ejecutar-fase/SKILL.md +468 -0
- package/habilidades/estilo-sin-ai-isms/SKILL.md +775 -0
- package/habilidades/estilo-sin-ai-isms/evals/evals.json +63 -0
- package/habilidades/estilo-sin-ai-isms/scripts/detectar_aiisms.py +500 -0
- package/habilidades/estructura-proyecto-claude/SKILL.md +215 -0
- package/habilidades/estructura-proyecto-claude/recursos/claude-md-template.md +261 -0
- package/habilidades/estructura-proyecto-claude/recursos/configuracion-y-extensiones.md +176 -0
- package/habilidades/estructura-proyecto-claude/recursos/frontmatter-y-hooks-referencia.md +289 -0
- package/habilidades/estructura-proyecto-claude/recursos/mcp-json-template.json +77 -0
- package/habilidades/estructura-proyecto-claude/recursos/variantes-por-stack.md +177 -0
- package/habilidades/evaluacion-agentes/SKILL.md +314 -0
- package/habilidades/event-driven/SKILL.md +153 -0
- package/habilidades/event-driven/recursos/implementaciones-completas.md +423 -0
- package/habilidades/extraccion-documentos/SKILL.md +221 -0
- package/habilidades/extractor-de-aprendizajes/.evolved.json +9 -0
- package/habilidades/extractor-de-aprendizajes/SKILL.md +311 -0
- package/habilidades/extractor-de-aprendizajes/evals/evals.json +55 -0
- package/habilidades/fastapi-experto/SKILL.md +221 -0
- package/habilidades/fastapi-experto/recursos/async-patterns.md +438 -0
- package/habilidades/fastapi-experto/recursos/dependency-injection.md +330 -0
- package/habilidades/fastapi-experto/recursos/referencia-completa.md +79 -0
- package/habilidades/fastapi-experto/recursos/testing-httpx.md +420 -0
- package/habilidades/filament-admin/SKILL.md +290 -0
- package/habilidades/frontend-avanzado/SKILL.md +257 -0
- package/habilidades/frontend-avanzado/recursos/apis-nativas-ejemplos.md +341 -0
- package/habilidades/gcp-cloud/SKILL.md +260 -0
- package/habilidades/gcp-cloud/recursos/gke.md +234 -0
- package/habilidades/gcp-cloud/recursos/terraform-gcp.md +307 -0
- package/habilidades/generacion-mermaid/SKILL.md +229 -0
- package/habilidades/git-worktrees-paralelo/SKILL.md +270 -0
- package/habilidades/go-experto/SKILL.md +305 -0
- package/habilidades/go-patrones/SKILL.md +299 -0
- package/habilidades/go-testing/SKILL.md +291 -0
- package/habilidades/graphql-experto/SKILL.md +323 -0
- package/habilidades/guardrail-semantico/SKILL.md +282 -0
- package/habilidades/harness-claude-code/SKILL.md +299 -0
- package/habilidades/iam-secretos/SKILL.md +265 -0
- package/habilidades/iam-secretos/recursos/implementaciones-completas.md +356 -0
- package/habilidades/infra-github-actions/SKILL.md +166 -0
- package/habilidades/instalar-sistema/.evolved.json +9 -0
- package/habilidades/instalar-sistema/SKILL.md +221 -0
- package/habilidades/java-experto/SKILL.md +290 -0
- package/habilidades/java-patrones/SKILL.md +275 -0
- package/habilidades/java-testing/SKILL.md +288 -0
- package/habilidades/kotlin-compose/SKILL.md +278 -0
- package/habilidades/kotlin-compose/recursos/animaciones-performance.md +93 -0
- package/habilidades/kotlin-experto/SKILL.md +318 -0
- package/habilidades/kotlin-testing/SKILL.md +267 -0
- package/habilidades/kotlin-testing/recursos/testing-avanzado.md +74 -0
- package/habilidades/kubernetes-orquestacion/SKILL.md +152 -0
- package/habilidades/kubernetes-orquestacion/recursos/manifiestos-completos.md +452 -0
- package/habilidades/langchain-langraph/SKILL.md +386 -0
- package/habilidades/langchain-langraph/recursos/evaluacion-rag.md +321 -0
- package/habilidades/langchain-langraph/recursos/rag-maturity-model.md +225 -0
- package/habilidades/langchain-langraph/recursos/vectorstores.md +306 -0
- package/habilidades/legacy-code-rescue/SKILL.md +267 -0
- package/habilidades/likec4-experto/SKILL.md +412 -0
- package/habilidades/likec4-experto/evals/evals.json +69 -0
- package/habilidades/manejo-errores/.evolved.json +9 -0
- package/habilidades/manejo-errores/SKILL.md +407 -0
- package/habilidades/manejo-errores/recursos/implementaciones-completas.md +248 -0
- package/habilidades/mapear-codebase/SKILL.md +275 -0
- package/habilidades/memoria-busqueda/SKILL.md +194 -0
- package/habilidades/memoria-busqueda/evals/evals.json +44 -0
- package/habilidades/meta-skills-estandar/SKILL.md +298 -0
- package/habilidades/meta-skills-estandar/recursos/anti-patrones-y-leyes.md +205 -0
- package/habilidades/meta-skills-estandar/recursos/frameworks-seguridad.md +107 -0
- package/habilidades/meta-skills-estandar/recursos/idiomas-framework.md +60 -0
- package/habilidades/meta-skills-estandar/recursos/skills-as-agents.md +163 -0
- package/habilidades/microservicios/SKILL.md +155 -0
- package/habilidades/microservicios/recursos/patrones-y-ejemplos-completos.md +325 -0
- package/habilidades/mobile-flutter/SKILL.md +199 -0
- package/habilidades/mobile-flutter/recursos/ejemplos-completos.md +319 -0
- package/habilidades/mobile-react-native/SKILL.md +176 -0
- package/habilidades/mobile-react-native/recursos/ejemplos-completos.md +216 -0
- package/habilidades/mongodb-experto/SKILL.md +302 -0
- package/habilidades/monitoring-alertas/SKILL.md +201 -0
- package/habilidades/monitoring-alertas/recursos/instrumentacion-y-alertas.md +301 -0
- package/habilidades/nestjs-experto/SKILL.md +307 -0
- package/habilidades/nestjs-experto/recursos/guards-interceptors.md +339 -0
- package/habilidades/nestjs-experto/recursos/modulos-di.md +287 -0
- package/habilidades/nestjs-experto/recursos/testing-nestjs.md +354 -0
- package/habilidades/nextjs-experto/SKILL.md +335 -0
- package/habilidades/nextjs-patrones/SKILL.md +303 -0
- package/habilidades/nextjs-testing/SKILL.md +331 -0
- package/habilidades/node-experto/.evolved.json +9 -0
- package/habilidades/node-experto/SKILL.md +266 -0
- package/habilidades/node-experto/recursos/patrones-completos.md +283 -0
- package/habilidades/notificaciones-multicanal/SKILL.md +159 -0
- package/habilidades/notificaciones-multicanal/recursos/config-template.json +115 -0
- package/habilidades/notificaciones-multicanal/recursos/configuracion-y-templates.md +303 -0
- package/habilidades/nuevo-proyecto/SKILL.md +204 -0
- package/habilidades/orquestacion-async/SKILL.md +303 -0
- package/habilidades/paid-media-tracking/SKILL.md +269 -0
- package/habilidades/paid-media-tracking/recursos/auditoria-tracking.md +220 -0
- package/habilidades/paid-media-tracking/recursos/google-ads-api.md +215 -0
- package/habilidades/patrones-python/SKILL.md +228 -0
- package/habilidades/patrones-python/evals/evals.json +56 -0
- package/habilidades/patrones-python/recursos/patrones-avanzados.md +469 -0
- package/habilidades/patrones-python/recursos/referencia-completa.md +202 -0
- package/habilidades/perfil-usuario/SKILL.md +200 -0
- package/habilidades/perfil-usuario/evals/evals.json +55 -0
- package/habilidades/performance-baseline/SKILL.md +297 -0
- package/habilidades/php-experto/SKILL.md +291 -0
- package/habilidades/php-patrones/SKILL.md +306 -0
- package/habilidades/php-testing/SKILL.md +280 -0
- package/habilidades/planear-fase/SKILL.md +269 -0
- package/habilidades/postgresql-experto/SKILL.md +151 -0
- package/habilidades/postgresql-experto/evals/evals.json +53 -0
- package/habilidades/postgresql-experto/recursos/referencia-completa.md +215 -0
- package/habilidades/prevencion-racionalizacion/SKILL.md +175 -0
- package/habilidades/prevencion-sobreingenieria/SKILL.md +323 -0
- package/habilidades/privacy-memoria/SKILL.md +141 -0
- package/habilidades/privacy-memoria/evals/evals.json +43 -0
- package/habilidades/prompt-engineering/SKILL.md +518 -0
- package/habilidades/prompt-engineering/recursos/patrones-avanzados.md +467 -0
- package/habilidades/rag-arquitectura/SKILL.md +338 -0
- package/habilidades/rails-experto/SKILL.md +237 -0
- package/habilidades/rails-experto/recursos/active-record.md +260 -0
- package/habilidades/rails-experto/recursos/hotwire-turbo.md +293 -0
- package/habilidades/rails-experto/recursos/testing-rspec.md +362 -0
- package/habilidades/react-experto/SKILL.md +209 -0
- package/habilidades/react-experto/evals/evals.json +55 -0
- package/habilidades/react-experto/recursos/patrones-y-ejemplos-completos.md +240 -0
- package/habilidades/react-optimizacion/SKILL.md +174 -0
- package/habilidades/react-optimizacion/recursos/patrones-avanzados.md +138 -0
- package/habilidades/redis-experto/SKILL.md +305 -0
- package/habilidades/release-semver/.evolved.json +9 -0
- package/habilidades/release-semver/SKILL.md +248 -0
- package/habilidades/release-semver/scripts/generar-changelog.sh +238 -0
- package/habilidades/rust-experto/SKILL.md +400 -0
- package/habilidades/rust-patrones/SKILL.md +296 -0
- package/habilidades/rust-testing/SKILL.md +311 -0
- package/habilidades/seguridad-skills-ia/SKILL.md +262 -0
- package/habilidades/sql-optimizacion/SKILL.md +200 -0
- package/habilidades/sql-optimizacion/evals/evals.json +54 -0
- package/habilidades/sql-optimizacion/recursos/patrones-sql-avanzados.md +131 -0
- package/habilidades/sre-patrones/SKILL.md +333 -0
- package/habilidades/sre-patrones/recursos/chaos-engineering.md +241 -0
- package/habilidades/sre-patrones/recursos/oncall-design.md +236 -0
- package/habilidades/stripe-pagos/SKILL.md +550 -0
- package/habilidades/stripe-pagos/recursos/errores-reintentos.md +390 -0
- package/habilidades/stripe-pagos/recursos/stripe-connect.md +290 -0
- package/habilidades/structured-outputs/SKILL.md +343 -0
- package/habilidades/swift-experto/SKILL.md +320 -0
- package/habilidades/swift-experto/recursos/keychain-y-wrappers.md +110 -0
- package/habilidades/swift-patrones/SKILL.md +313 -0
- package/habilidades/swift-patrones/recursos/tca-ejemplo-completo.md +113 -0
- package/habilidades/swift-testing/SKILL.md +254 -0
- package/habilidades/swift-testing/recursos/xcuitest-planes.md +143 -0
- package/habilidades/swl-dashboard/SKILL.md +370 -0
- package/habilidades/swl-markitdown/SKILL.md +285 -0
- package/habilidades/swl-markitdown/evals/evals.json +52 -0
- package/habilidades/swl-revisar-impacto/SKILL.md +233 -0
- package/habilidades/tailwind-experto/SKILL.md +240 -0
- package/habilidades/tailwind-experto/recursos/referencia-completa.md +184 -0
- package/habilidades/tdd-workflow/SKILL.md +293 -0
- package/habilidades/terraform-experto/SKILL.md +321 -0
- package/habilidades/testing-python/SKILL.md +340 -0
- package/habilidades/testing-python/recursos/ejemplos-completos.md +167 -0
- package/habilidades/threat-model-lite/SKILL.md +246 -0
- package/habilidades/tracing-processor/SKILL.md +212 -0
- package/habilidades/tracking-measurement/SKILL.md +239 -0
- package/habilidades/tracking-measurement/recursos/consent-mode.md +231 -0
- package/habilidades/tracking-measurement/recursos/gtm-datalayer.md +216 -0
- package/habilidades/tracking-measurement/recursos/meta-capi.md +262 -0
- package/habilidades/typescript-avanzado/SKILL.md +144 -0
- package/habilidades/typescript-avanzado/evals/evals.json +55 -0
- package/habilidades/typescript-avanzado/recursos/patrones-y-ejemplos-completos.md +298 -0
- package/habilidades/typescript-diagnosticos/SKILL.md +513 -0
- package/habilidades/ux-diseno/SKILL.md +116 -0
- package/habilidades/ux-diseno/evals/evals.json +43 -0
- package/habilidades/ux-diseno/recursos/patrones-ux-referencia.md +214 -0
- package/habilidades/validacion-ci-sistema/SKILL.md +136 -0
- package/habilidades/validacion-ci-sistema/recursos/validadores-completos.md +369 -0
- package/habilidades/validacion-ci-sistema/scripts/validar-sistema.sh +286 -0
- package/habilidades/verificacion-evidencia/SKILL.md +160 -0
- package/habilidades/verificar-trabajo/SKILL.md +303 -0
- package/habilidades/verificar-trabajo/recursos/plantilla-verificacion.md +60 -0
- package/habilidades/wiki-conocimiento/SKILL.md +276 -0
- package/habilidades/wireframes-flujos/SKILL.md +212 -0
- package/habilidades/wireframes-flujos/recursos/referencia-completa.md +192 -0
- package/habilidades/workflow-claude-code/SKILL.md +260 -0
- package/habilidades/workflow-claude-code/recursos/referencia-completa.md +109 -0
- package/hooks/_run-hook.sh +57 -0
- package/hooks/actualizar-perfil-usuario.js +364 -0
- package/hooks/agente-lifecycle.js +71 -0
- package/hooks/aiisms-detector.js +173 -0
- package/hooks/audit-trail.js +204 -0
- package/hooks/auto-background.js +97 -0
- package/hooks/auto-consolidacion.js +178 -0
- package/hooks/auto-evolucion.js +666 -0
- package/hooks/auto-restaurar-settings.js +360 -0
- package/hooks/calidad-pre-commit.js +929 -0
- package/hooks/calidad-typescript.js +511 -0
- package/hooks/captura-feedback-usuario.js +148 -0
- package/hooks/check-update.js +211 -0
- package/hooks/clasificador-mensajes.js +271 -0
- package/hooks/degradacion-instintos.js +272 -0
- package/hooks/escaneo-secretos.js +389 -0
- package/hooks/extraccion-aprendizajes.js +763 -0
- package/hooks/grafo-contexto.js +129 -0
- package/hooks/graph-update.js +67 -0
- package/hooks/guardrail-modelo.js +247 -0
- package/hooks/inbox-aviso.js +75 -0
- package/hooks/inyeccion-contexto.js +246 -0
- package/hooks/lib/abort-registry.js +214 -0
- package/hooks/lib/agent-backend.js +210 -0
- package/hooks/lib/agent-comms.js +263 -0
- package/hooks/lib/agent-issue-codes.js +284 -0
- package/hooks/lib/agent-matcher.js +189 -0
- package/hooks/lib/async-hook-registry.js +252 -0
- package/hooks/lib/atomic-write.js +130 -0
- package/hooks/lib/auto-consolidator.js +335 -0
- package/hooks/lib/canary-skills.js +187 -0
- package/hooks/lib/consolidation-lock.js +291 -0
- package/hooks/lib/context-builder.js +430 -0
- package/hooks/lib/context-compressor.js +657 -0
- package/hooks/lib/convergence-detector.js +105 -0
- package/hooks/lib/delegation-tracker.js +198 -0
- package/hooks/lib/detectar-package-manager.js +423 -0
- package/hooks/lib/edit-accumulator.js +171 -0
- package/hooks/lib/error-classifier.js +308 -0
- package/hooks/lib/event-bus.js +112 -0
- package/hooks/lib/evolution-tracker.js +442 -0
- package/hooks/lib/execution-state.js +316 -0
- package/hooks/lib/fingerprint-id.js +135 -0
- package/hooks/lib/gateway-notify.js +116 -0
- package/hooks/lib/graph-security.js +75 -0
- package/hooks/lib/guardrail-metrics.js +202 -0
- package/hooks/lib/hook-circuit-breaker.js +206 -0
- package/hooks/lib/loop-detector.js +267 -0
- package/hooks/lib/mcp-health.js +184 -0
- package/hooks/lib/mcp-pool.js +436 -0
- package/hooks/lib/memory-search.js +506 -0
- package/hooks/lib/merkle-audit.js +96 -0
- package/hooks/lib/model-router.js +222 -0
- package/hooks/lib/normalize-error.js +324 -0
- package/hooks/lib/normalize-input.js +65 -0
- package/hooks/lib/nudge-tracker.js +306 -0
- package/hooks/lib/otlp-exporter.js +365 -0
- package/hooks/lib/performance-marks.js +239 -0
- package/hooks/lib/privacy-filter.js +128 -0
- package/hooks/lib/prompt-injection-scanner.js +209 -0
- package/hooks/lib/provenance-tracker.js +183 -0
- package/hooks/lib/rate-limit-tracker.js +253 -0
- package/hooks/lib/reflect-classifier.js +164 -0
- package/hooks/lib/resource-quota.js +122 -0
- package/hooks/lib/retry-jitter.js +165 -0
- package/hooks/lib/risk-engine.js +368 -0
- package/hooks/lib/run-log.js +408 -0
- package/hooks/lib/session-fts.js +379 -0
- package/hooks/lib/session-store.js +293 -0
- package/hooks/lib/singleton-guard.js +159 -0
- package/hooks/lib/skill-auditor.js +588 -0
- package/hooks/lib/sync-status.js +228 -0
- package/hooks/lib/taint-tracker.js +107 -0
- package/hooks/lib/task-service.js +295 -0
- package/hooks/lib/tech-skills-map.js +146 -0
- package/hooks/lib/telegram-cliente.js +159 -0
- package/hooks/lib/telegram-config.js +170 -0
- package/hooks/lib/token-budget.js +156 -0
- package/hooks/lib/token-estimator.js +420 -0
- package/hooks/lib/toon-compressor.js +245 -0
- package/hooks/lib/usage-model.js +183 -0
- package/hooks/lib/variable-resolver.js +230 -0
- package/hooks/linea-estado.js +324 -0
- package/hooks/metricas-evolucion.js +209 -0
- package/hooks/monitor-contexto.js +325 -0
- package/hooks/notificacion-sesion-stop.js +198 -0
- package/hooks/notificacion-telegram-notification.js +4 -0
- package/hooks/notificacion-telegram-subagent.js +4 -0
- package/hooks/notificacion-telegram.js +267 -0
- package/hooks/preservar-estado-pre-compact.js +150 -0
- package/hooks/proteccion-rutas.js +366 -0
- package/hooks/registro-turnos.js +209 -0
- package/hooks/resumen-sesion.js +249 -0
- package/hooks/risk-scoring.js +323 -0
- package/hooks/rotar-audit-auto.js +122 -0
- package/hooks/sugerir-regenerar-inventario.js +170 -0
- package/hooks/telemetria-agentes.js +167 -0
- package/hooks/tracking-costos.js +688 -0
- package/instintos/global.yaml +8 -0
- package/instintos/perfil-usuario.yaml +53 -0
- package/instintos/prompt-appendices.yaml +57 -0
- package/instintos/proyecto.yaml +372 -0
- package/manifiestos/gateway-config.json +77 -0
- package/manifiestos/handoff-context.json +223 -0
- package/manifiestos/hook-profiles.json +44 -0
- package/manifiestos/hooks-config.json +360 -0
- package/manifiestos/modulos.json +1173 -0
- package/manifiestos/perfiles.json +404 -0
- package/package.json +86 -0
- package/plantillas/ESTADO.md +109 -0
- package/plantillas/HOJA-RUTA.md +143 -0
- package/plantillas/PROYECTO.md +122 -0
- package/plantillas/REQUISITOS.md +132 -0
- package/plantillas/auditor-veto-template.md +105 -0
- package/plantillas/github-workflows/README.md +47 -0
- package/plantillas/github-workflows/release-please.yml +44 -0
- package/plantillas/github-workflows/swl-ci.yml +107 -0
- package/plantillas/github-workflows/swl-security.yml +51 -0
- package/plantillas/mcp-mineru.json +13 -0
- package/plantillas/research/ARQUITECTURA.md +220 -0
- package/plantillas/research/FUNCIONALIDADES.md +175 -0
- package/plantillas/research/RESUMEN.md +165 -0
- package/plantillas/research/STACK.md +233 -0
- package/plantillas/research/TRAMPAS.md +299 -0
- package/plantillas/skill-evals-template.json +44 -0
- package/plugin.json +343 -0
- package/reglas/accesibilidad.md +269 -0
- package/reglas/api-diseno.md +400 -0
- package/reglas/arquitectura.md +352 -0
- package/reglas/brevedad-output.md +124 -0
- package/reglas/cloud-infra.md +247 -0
- package/reglas/docs.md +245 -0
- package/reglas/estilo-codigo.md +201 -0
- package/reglas/git-workflow.md +245 -0
- package/reglas/gobernanza.md +271 -0
- package/reglas/harness-claude-code.md +213 -0
- package/reglas/hooks.md +186 -0
- package/reglas/lenguajes/csharp/estilo-codigo.md +231 -0
- package/reglas/lenguajes/csharp/hooks.md +281 -0
- package/reglas/lenguajes/csharp/patrones.md +226 -0
- package/reglas/lenguajes/csharp/seguridad.md +258 -0
- package/reglas/lenguajes/csharp/testing.md +176 -0
- package/reglas/lenguajes/go/estilo-codigo.md +195 -0
- package/reglas/lenguajes/go/hooks.md +249 -0
- package/reglas/lenguajes/go/patrones.md +249 -0
- package/reglas/lenguajes/go/seguridad.md +225 -0
- package/reglas/lenguajes/go/testing.md +272 -0
- package/reglas/lenguajes/java/estilo-codigo.md +217 -0
- package/reglas/lenguajes/java/hooks.md +251 -0
- package/reglas/lenguajes/java/patrones.md +226 -0
- package/reglas/lenguajes/java/seguridad.md +233 -0
- package/reglas/lenguajes/java/testing.md +238 -0
- package/reglas/lenguajes/kotlin/estilo-codigo.md +208 -0
- package/reglas/lenguajes/kotlin/hooks.md +245 -0
- package/reglas/lenguajes/kotlin/patrones.md +201 -0
- package/reglas/lenguajes/kotlin/seguridad.md +202 -0
- package/reglas/lenguajes/kotlin/testing.md +236 -0
- package/reglas/lenguajes/nextjs/estilo-codigo.md +175 -0
- package/reglas/lenguajes/nextjs/hooks.md +186 -0
- package/reglas/lenguajes/nextjs/patrones.md +225 -0
- package/reglas/lenguajes/nextjs/seguridad.md +216 -0
- package/reglas/lenguajes/nextjs/testing.md +193 -0
- package/reglas/lenguajes/php/estilo-codigo.md +228 -0
- package/reglas/lenguajes/php/hooks.md +165 -0
- package/reglas/lenguajes/php/patrones.md +233 -0
- package/reglas/lenguajes/php/seguridad.md +186 -0
- package/reglas/lenguajes/php/testing.md +205 -0
- package/reglas/lenguajes/rust/estilo-codigo.md +207 -0
- package/reglas/lenguajes/rust/hooks.md +240 -0
- package/reglas/lenguajes/rust/patrones.md +250 -0
- package/reglas/lenguajes/rust/seguridad.md +221 -0
- package/reglas/lenguajes/rust/testing.md +194 -0
- package/reglas/lenguajes/swift/estilo-codigo.md +238 -0
- package/reglas/lenguajes/swift/hooks.md +257 -0
- package/reglas/lenguajes/swift/patrones.md +235 -0
- package/reglas/lenguajes/swift/seguridad.md +248 -0
- package/reglas/lenguajes/swift/testing.md +242 -0
- package/reglas/markitdown.md +60 -0
- package/reglas/memoria-consolidada.md +209 -0
- package/reglas/patrones.md +225 -0
- package/reglas/performance.md +195 -0
- package/reglas/pruebas.md +159 -0
- package/reglas/seguridad-agentes.md +351 -0
- package/reglas/seguridad.md +151 -0
- package/reglas/skills-estandar.md +373 -0
- package/reglas/testing.md +193 -0
- package/schemas/agent-contract.json +176 -0
- package/schemas/agent-frontmatter.schema.json +149 -0
- package/schemas/agent-message.schema.json +53 -0
- package/schemas/agent-output-implementacion.schema.json +85 -0
- package/schemas/agent-output-planificacion.schema.json +113 -0
- package/schemas/agent-output-review.schema.json +78 -0
- package/schemas/diary-entry.schema.json +80 -0
- package/schemas/hook-profiles.schema.json +39 -0
- package/schemas/hooks-config.schema.json +74 -0
- package/schemas/instinct.schema.json +115 -0
- package/schemas/modulos.schema.json +29 -0
- package/schemas/perfiles.schema.json +28 -0
- package/schemas/plugin.schema.json +64 -0
- package/schemas/skill-evals.schema.json +95 -0
- package/schemas/skill-frontmatter.schema.json +170 -0
- package/scripts/actualizar.js +145 -0
- package/scripts/audit-skills.sh +78 -0
- package/scripts/auditar-agentes-gaps.js +149 -0
- package/scripts/auditar-cobertura-frameworks.js +241 -0
- package/scripts/auditar-skills-gaps.js +206 -0
- package/scripts/bootstrap-instintos.js +259 -0
- package/scripts/check-update.js +109 -0
- package/scripts/comandos/agents.js +105 -0
- package/scripts/comandos/info.js +108 -0
- package/scripts/comandos/install-asistido.js +186 -0
- package/scripts/comandos/skills.js +211 -0
- package/scripts/configurar-branch-protection.js +418 -0
- package/scripts/daemon-swl.py +388 -0
- package/scripts/desinstalar.js +130 -0
- package/scripts/doctor.js +559 -0
- package/scripts/field-report.js +199 -0
- package/scripts/generar-inventario.js +317 -0
- package/scripts/inbox-tmux-inject.js +161 -0
- package/scripts/inferir-herramientas-permitidas.js +586 -0
- package/scripts/inicializar.js +133 -0
- package/scripts/instalador.js +1031 -0
- package/scripts/instalar-git-hook.js +122 -0
- package/scripts/lib/agp-frontmatter.js +222 -0
- package/scripts/lib/append-con-marcadores.js +199 -0
- package/scripts/lib/artefactos-python.js +43 -0
- package/scripts/lib/audit-query.js +221 -0
- package/scripts/lib/autostart-linux.js +347 -0
- package/scripts/lib/autostart-macos.js +360 -0
- package/scripts/lib/autostart-windows.js +307 -0
- package/scripts/lib/budget-enforcer.js +252 -0
- package/scripts/lib/claude-sessions.js +285 -0
- package/scripts/lib/configurar-ci.js +380 -0
- package/scripts/lib/console-span-exporter.js +92 -0
- package/scripts/lib/contadores-inventario.js +217 -0
- package/scripts/lib/dashboard-widgets.js +290 -0
- package/scripts/lib/detectar-runtime.js +279 -0
- package/scripts/lib/detectar-stack.js +187 -0
- package/scripts/lib/diary-entry.js +234 -0
- package/scripts/lib/drift-detector.js +545 -0
- package/scripts/lib/estado.js +124 -0
- package/scripts/lib/gestor-componentes.js +243 -0
- package/scripts/lib/gitignore-manifest.js +305 -0
- package/scripts/lib/graph-analyze.py +556 -0
- package/scripts/lib/graph-builder.py +485 -0
- package/scripts/lib/graph-cluster.py +259 -0
- package/scripts/lib/health-row.js +168 -0
- package/scripts/lib/hooks-settings.js +789 -0
- package/scripts/lib/manifiestos.js +138 -0
- package/scripts/lib/mc-client.js +137 -0
- package/scripts/lib/notificaciones-telegram.js +1107 -0
- package/scripts/lib/npm-version.js +261 -0
- package/scripts/lib/paquetes-conocidos.js +50 -0
- package/scripts/lib/preservar-usuario.js +586 -0
- package/scripts/lib/prompt-builder.js +264 -0
- package/scripts/lib/resolver-externo.js +332 -0
- package/scripts/lib/schedule-parser.js +305 -0
- package/scripts/lib/scoring-instintos.js +240 -0
- package/scripts/lib/seguridad.js +160 -0
- package/scripts/lib/selector-interactivo.js +152 -0
- package/scripts/lib/semantic-search.js +242 -0
- package/scripts/lib/skill-discovery.js +234 -0
- package/scripts/lib/skill-metrics.js +246 -0
- package/scripts/lib/skill-normalizer.js +112 -0
- package/scripts/lib/skills-hub.js +340 -0
- package/scripts/lib/span-schema.js +134 -0
- package/scripts/lib/tool-cost-analyzer.js +255 -0
- package/scripts/lib/tracing-processor-interface.js +286 -0
- package/scripts/lib/transformadores/base.js +80 -0
- package/scripts/lib/transformadores/claude.js +124 -0
- package/scripts/lib/transformadores/codex.js +115 -0
- package/scripts/lib/transformadores/copilot.js +106 -0
- package/scripts/lib/transformadores/gemini.js +74 -0
- package/scripts/lib/transformadores/index.js +35 -0
- package/scripts/lib/transformadores/opencode.js +75 -0
- package/scripts/lib/ui.js +259 -0
- package/scripts/limpiar-artefactos-python.js +131 -0
- package/scripts/mcp-orchestrator.py +386 -0
- package/scripts/mcp-pool-manager.py +352 -0
- package/scripts/mcp-telemetry.py +378 -0
- package/scripts/poblar-evolvable.js +226 -0
- package/scripts/publicar.js +287 -0
- package/scripts/reflect-skills.js +403 -0
- package/scripts/rotar-audit-logs.js +185 -0
- package/scripts/run-skill-evals.js +242 -0
- package/scripts/smoke-test.js +374 -0
- package/scripts/token-analysis.py +471 -0
- package/scripts/validar-manifest.js +195 -0
- package/scripts/validar-memoria.js +321 -0
- package/scripts/validar-tests-aislamiento.js +184 -0
- package/scripts/validar-tokens-test.js +208 -0
- package/scripts/validar.js +147 -0
- package/scripts/validate-markdown.py +339 -0
- package/scripts/validate-skills.py +385 -0
- package/scripts/vendor/claude-usage/README.md +116 -0
- package/scripts/vendor/claude-usage/cli.py +334 -0
- package/scripts/vendor/claude-usage/dashboard.py +795 -0
- package/scripts/vendor/claude-usage/scanner.py +467 -0
- package/scripts/vendor/markitdown/cli.py +194 -0
- package/scripts/verificar-evolucion.js +289 -0
- package/scripts/verificar-release.js +494 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: csharp-patrones
|
|
3
|
+
description: >
|
|
4
|
+
Patrones C# modernos: records para CQRS, MediatR, Result<T> pattern,
|
|
5
|
+
Specification pattern, Unit of Work, Channel<T>, Polly y discriminated
|
|
6
|
+
unions con OneOf. Cargar cuando se diseñe arquitectura .NET, se implemente
|
|
7
|
+
CQRS/Mediator, o se necesite manejo robusto de errores sin excepciones.
|
|
8
|
+
version: "1.0.0"
|
|
9
|
+
herramientasPermitidas: [Read, Glob, Grep]
|
|
10
|
+
exclusiones:
|
|
11
|
+
- "No cargar para implementar endpoints ASP.NET Core, EF Core o Background Services — para implementación cargar `csharp-experto`."
|
|
12
|
+
- "No cargar para escribir tests C# — para testing cargar `csharp-testing`."
|
|
13
|
+
- "No cargar para errores de compilación C# — para build errors cargar `build-errors-csharp`."
|
|
14
|
+
- "No cargar para patrones de diseño GoF genéricos sin contexto C# — este skill cubre patrones idiomáticos de C# moderno como CQRS con records, Result<T> y MediatR."
|
|
15
|
+
evolvable: true # default para skill estandar
|
|
16
|
+
---
|
|
17
|
+
# C# Patrones — CQRS, Result, MediatR y Más
|
|
18
|
+
|
|
19
|
+
## Cuándo NO cargar
|
|
20
|
+
|
|
21
|
+
- La pregunta es sobre implementar endpoints ASP.NET Core, EF Core o Background Services — para implementación concreta cargar `csharp-experto`.
|
|
22
|
+
- La tarea es escribir tests C# — cargar `csharp-testing`.
|
|
23
|
+
- Los errores son de compilación C# — cargar `build-errors-csharp`.
|
|
24
|
+
- La discusión es sobre patrones GoF genéricos (Singleton, Observer) sin contexto C# — este skill cubre patrones idiomáticos de C# moderno como CQRS con records, Result<T> y MediatR.
|
|
25
|
+
|
|
26
|
+
## CQRS con Records y MediatR
|
|
27
|
+
|
|
28
|
+
```csharp
|
|
29
|
+
// Command — estado mutable del sistema
|
|
30
|
+
public record CrearFacturaCommand(
|
|
31
|
+
Guid EmpresaId,
|
|
32
|
+
string Folio,
|
|
33
|
+
List<ItemCommand> Items) : IRequest<Result<FacturaDto>>;
|
|
34
|
+
|
|
35
|
+
// Query — solo lectura, sin efectos secundarios
|
|
36
|
+
public record ObtenerFacturasQuery(
|
|
37
|
+
Guid EmpresaId,
|
|
38
|
+
int Pagina = 1,
|
|
39
|
+
int TamanoPagina = 20) : IRequest<Result<PaginaDto<FacturaDto>>>;
|
|
40
|
+
|
|
41
|
+
// Handler de Command
|
|
42
|
+
public class CrearFacturaHandler(
|
|
43
|
+
IFacturasRepository repo,
|
|
44
|
+
IUnitOfWork uow,
|
|
45
|
+
ILogger<CrearFacturaHandler> logger)
|
|
46
|
+
: IRequestHandler<CrearFacturaCommand, Result<FacturaDto>>
|
|
47
|
+
{
|
|
48
|
+
public async Task<Result<FacturaDto>> Handle(
|
|
49
|
+
CrearFacturaCommand req, CancellationToken ct)
|
|
50
|
+
{
|
|
51
|
+
if (await repo.ExisteFolioAsync(req.EmpresaId, req.Folio, ct))
|
|
52
|
+
return Result.Failure<FacturaDto>(
|
|
53
|
+
Error.Conflicto("Facturas.FolioDuplicado", $"El folio {req.Folio} ya existe"));
|
|
54
|
+
|
|
55
|
+
var factura = Factura.Crear(req.EmpresaId, req.Folio, req.Items);
|
|
56
|
+
await repo.AgregarAsync(factura, ct);
|
|
57
|
+
await uow.GuardarCambiosAsync(ct);
|
|
58
|
+
|
|
59
|
+
logger.LogInformation("Factura {Folio} creada para empresa {EmpresaId}",
|
|
60
|
+
req.Folio, req.EmpresaId);
|
|
61
|
+
return Result.Success(FacturaDto.Desde(factura));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Result<T> Pattern — Sin excepciones para flujo de control
|
|
69
|
+
|
|
70
|
+
```csharp
|
|
71
|
+
public class Result<T>
|
|
72
|
+
{
|
|
73
|
+
private Result(T? valor, bool exitoso, Error error)
|
|
74
|
+
{
|
|
75
|
+
Valor = valor;
|
|
76
|
+
EsExitoso = exitoso;
|
|
77
|
+
Error = error;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
public T? Valor { get; }
|
|
81
|
+
public bool EsExitoso { get; }
|
|
82
|
+
public bool EsFallido => !EsExitoso;
|
|
83
|
+
public Error Error { get; }
|
|
84
|
+
|
|
85
|
+
public static Result<T> Success(T valor) =>
|
|
86
|
+
new(valor, true, Error.Ninguno);
|
|
87
|
+
|
|
88
|
+
public static Result<T> Failure(Error error) =>
|
|
89
|
+
new(default, false, error);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
public static class Result
|
|
93
|
+
{
|
|
94
|
+
public static Result<T> Success<T>(T valor) => Result<T>.Success(valor);
|
|
95
|
+
public static Result<T> Failure<T>(Error error) => Result<T>.Failure(error);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public record Error(string Codigo, string Descripcion)
|
|
99
|
+
{
|
|
100
|
+
public static readonly Error Ninguno = new("", "");
|
|
101
|
+
public static Error NoEncontrado(string entidad, Guid id) =>
|
|
102
|
+
new($"{entidad}.NoEncontrado", $"{entidad} con id {id} no existe");
|
|
103
|
+
public static Error Conflicto(string codigo, string desc) => new(codigo, desc);
|
|
104
|
+
public static Error Validacion(string campo, string mensaje) =>
|
|
105
|
+
new($"Validacion.{campo}", mensaje);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Uso en endpoint — mapear Result a HTTP
|
|
109
|
+
static async Task<IResult> Crear(
|
|
110
|
+
CrearFacturaCommand cmd,
|
|
111
|
+
ISender mediator,
|
|
112
|
+
CancellationToken ct)
|
|
113
|
+
{
|
|
114
|
+
var resultado = await mediator.Send(cmd, ct);
|
|
115
|
+
return resultado switch
|
|
116
|
+
{
|
|
117
|
+
{ EsExitoso: true } => TypedResults.Created($"/facturas/{resultado.Valor!.Id}", resultado.Valor),
|
|
118
|
+
{ Error.Codigo: var c } when c.StartsWith("Validacion") => TypedResults.BadRequest(resultado.Error),
|
|
119
|
+
{ Error.Codigo: var c } when c.EndsWith("Duplicado") => TypedResults.Conflict(resultado.Error),
|
|
120
|
+
_ => TypedResults.Problem("Error interno")
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Specification Pattern
|
|
128
|
+
|
|
129
|
+
```csharp
|
|
130
|
+
public abstract class Specification<T>
|
|
131
|
+
{
|
|
132
|
+
public abstract Expression<Func<T, bool>> Criterio { get; }
|
|
133
|
+
|
|
134
|
+
public Specification<T> And(Specification<T> otra) =>
|
|
135
|
+
new SpecificacionAnd<T>(this, otra);
|
|
136
|
+
|
|
137
|
+
public Specification<T> Or(Specification<T> otra) =>
|
|
138
|
+
new SpecificacionOr<T>(this, otra);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
public class FacturasPorEmpresaSpec : Specification<Factura>
|
|
142
|
+
{
|
|
143
|
+
private readonly Guid _empresaId;
|
|
144
|
+
public FacturasPorEmpresaSpec(Guid empresaId) => _empresaId = empresaId;
|
|
145
|
+
|
|
146
|
+
public override Expression<Func<Factura, bool>> Criterio =>
|
|
147
|
+
f => f.EmpresaId == _empresaId;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
public class FacturasPendientesSpec : Specification<Factura>
|
|
151
|
+
{
|
|
152
|
+
public override Expression<Func<Factura, bool>> Criterio =>
|
|
153
|
+
f => f.Estatus == "pendiente";
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Uso compuesto
|
|
157
|
+
var spec = new FacturasPorEmpresaSpec(empresaId)
|
|
158
|
+
.And(new FacturasPendientesSpec());
|
|
159
|
+
|
|
160
|
+
var facturas = await _db.Facturas
|
|
161
|
+
.Where(spec.Criterio)
|
|
162
|
+
.ToListAsync(ct);
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Unit of Work con EF Core
|
|
168
|
+
|
|
169
|
+
```csharp
|
|
170
|
+
public interface IUnitOfWork
|
|
171
|
+
{
|
|
172
|
+
Task<int> GuardarCambiosAsync(CancellationToken ct = default);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// AppDbContext implementa IUnitOfWork directamente
|
|
176
|
+
public class AppDbContext : DbContext, IUnitOfWork
|
|
177
|
+
{
|
|
178
|
+
public Task<int> GuardarCambiosAsync(CancellationToken ct = default) =>
|
|
179
|
+
SaveChangesAsync(ct);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Registro — mismo DbContext resuelve ambas interfaces
|
|
183
|
+
builder.Services.AddDbContext<AppDbContext>(...);
|
|
184
|
+
builder.Services.AddScoped<IUnitOfWork>(sp =>
|
|
185
|
+
sp.GetRequiredService<AppDbContext>());
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Channel<T> — Pipelines async
|
|
191
|
+
|
|
192
|
+
```csharp
|
|
193
|
+
// Productor/consumidor sin bloqueo de thread
|
|
194
|
+
public class ColaProcesamiento<T>
|
|
195
|
+
{
|
|
196
|
+
private readonly Channel<T> _canal = Channel.CreateBounded<T>(
|
|
197
|
+
new BoundedChannelOptions(1000)
|
|
198
|
+
{
|
|
199
|
+
FullMode = BoundedChannelFullMode.Wait,
|
|
200
|
+
SingleReader = false,
|
|
201
|
+
SingleWriter = false
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
public ChannelWriter<T> Escritor => _canal.Writer;
|
|
205
|
+
public ChannelReader<T> Lector => _canal.Reader;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Productor
|
|
209
|
+
await _cola.Escritor.WriteAsync(nuevoElemento, ct);
|
|
210
|
+
|
|
211
|
+
// Consumidor en BackgroundService
|
|
212
|
+
await foreach (var elemento in _cola.Lector.ReadAllAsync(stoppingToken))
|
|
213
|
+
{
|
|
214
|
+
await ProcesarAsync(elemento, stoppingToken);
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Polly — Resiliencia
|
|
221
|
+
|
|
222
|
+
```csharp
|
|
223
|
+
// Pipeline de resiliencia para llamadas HTTP externas
|
|
224
|
+
var pipeline = new ResiliencePipelineBuilder<HttpResponseMessage>()
|
|
225
|
+
.AddRetry(new RetryStrategyOptions<HttpResponseMessage>
|
|
226
|
+
{
|
|
227
|
+
MaxRetryAttempts = 3,
|
|
228
|
+
Delay = TimeSpan.FromSeconds(1),
|
|
229
|
+
BackoffType = DelayBackoffType.Exponential,
|
|
230
|
+
ShouldHandle = new PredicateBuilder<HttpResponseMessage>()
|
|
231
|
+
.Handle<HttpRequestException>()
|
|
232
|
+
.HandleResult(r => r.StatusCode >= HttpStatusCode.InternalServerError)
|
|
233
|
+
})
|
|
234
|
+
.AddCircuitBreaker(new CircuitBreakerStrategyOptions<HttpResponseMessage>
|
|
235
|
+
{
|
|
236
|
+
FailureRatio = 0.5,
|
|
237
|
+
MinimumThroughput = 10,
|
|
238
|
+
BreakDuration = TimeSpan.FromSeconds(30)
|
|
239
|
+
})
|
|
240
|
+
.AddTimeout(TimeSpan.FromSeconds(10))
|
|
241
|
+
.Build();
|
|
242
|
+
|
|
243
|
+
// Registro con HttpClient
|
|
244
|
+
builder.Services.AddHttpClient<ISatClient, SatClient>()
|
|
245
|
+
.AddResilienceHandler("sat-pipeline", b => b.AddPipeline(pipeline));
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## OneOf — Discriminated Unions
|
|
251
|
+
|
|
252
|
+
```csharp
|
|
253
|
+
// dotnet add package OneOf
|
|
254
|
+
public class FacturasService
|
|
255
|
+
{
|
|
256
|
+
public async Task<OneOf<FacturaDto, NotFound, ValidationError>> ObtenerAsync(
|
|
257
|
+
Guid id, CancellationToken ct)
|
|
258
|
+
{
|
|
259
|
+
var factura = await _repo.ObtenerAsync(id, ct);
|
|
260
|
+
if (factura is null) return new NotFound();
|
|
261
|
+
return FacturaDto.Desde(factura);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Uso exhaustivo en endpoint
|
|
266
|
+
var resultado = await svc.ObtenerAsync(id, ct);
|
|
267
|
+
return resultado.Match(
|
|
268
|
+
dto => TypedResults.Ok(dto),
|
|
269
|
+
_ => TypedResults.NotFound(),
|
|
270
|
+
error => TypedResults.BadRequest(error.Mensajes)
|
|
271
|
+
);
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## Anti-patrones
|
|
277
|
+
|
|
278
|
+
### Excepciones para flujo de control — NUNCA
|
|
279
|
+
|
|
280
|
+
```csharp
|
|
281
|
+
// MAL — las excepciones son costosas y ocultan la intención
|
|
282
|
+
throw new NotFoundException($"Factura {id} no encontrada");
|
|
283
|
+
|
|
284
|
+
// BIEN — Result o OneOf expresan el caso esperado
|
|
285
|
+
return Result.Failure<FacturaDto>(Error.NoEncontrado("Factura", id));
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Service Locator — NUNCA
|
|
289
|
+
|
|
290
|
+
```csharp
|
|
291
|
+
// MAL — oculta dependencias, imposible de testear
|
|
292
|
+
var svc = _serviceProvider.GetService<IFacturasService>();
|
|
293
|
+
|
|
294
|
+
// BIEN — inyectar en el constructor
|
|
295
|
+
public class Handler(IFacturasService svc) { }
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
## Gotchas / Errores comunes no obvios
|
|
301
|
+
|
|
302
|
+
**MediatR con `IPipelineBehavior` registrado después del handler no se ejecuta en el orden correcto**: el orden de registro de behaviors en el DI container determina el orden de ejecución de la pipeline. Un behavior de validación registrado después del behavior de logging ejecuta después, no antes. Fix: registrar los behaviors en orden explícito: primero logging, luego validación, luego el handler — o usar `OrderedPipelineBehavior` si el orden debe ser determinístico independiente del registro.
|
|
303
|
+
|
|
304
|
+
**`Result<T>` implementado con un `bool Success` y un `T Value` permite acceder a `Value` cuando `Success = false`**: si el tipo `Result<T>` no protege el acceso al valor cuando el resultado es error, el código que consume el resultado puede usar `result.Value` sin verificar `result.Success`. Fix: hacer que `Value` lance una excepción si se accede cuando `IsError` es true, o usar un patrón match que fuerce el manejo de ambos casos.
|
|
305
|
+
|
|
306
|
+
**`Channel<T>` sin capacidad fija (`Channel.CreateUnbounded()`) puede crecer sin límite bajo presión**: si el productor es más rápido que el consumidor, el canal acumula mensajes en memoria hasta agotar RAM. Fix: usar `Channel.CreateBounded(capacity)` con una capacidad razonable y manejar `ChannelWriter.TryWrite()` retornando `false` (canal lleno) con backpressure.
|
|
307
|
+
|
|
308
|
+
**Polly `WaitAndRetry` con la misma instancia de `HttpClient` no reintenta si el HttpMessageHandler tiene su propio timeout**: si el `HttpClient` tiene `Timeout = TimeSpan.FromSeconds(5)` y Polly tiene 3 reintentos, el `TaskCanceledException` del timeout interno del cliente se lanza antes del segundo intento y no es capturado por Polly. Fix: deshabilitar el timeout del `HttpClient` (`Timeout = InfiniteTimeSpan`) y configurar el timeout a través de Polly con `TimeoutPolicy`.
|
|
309
|
+
|
|
310
|
+
## Checklist de arquitectura
|
|
311
|
+
|
|
312
|
+
- [ ] Commands y Queries son records inmutables
|
|
313
|
+
- [ ] Handlers retornan Result<T>, no lanzan excepciones para casos esperados
|
|
314
|
+
- [ ] Unit of Work hace SaveChanges, no los repositorios
|
|
315
|
+
- [ ] Polly configurado para todas las llamadas HTTP externas
|
|
316
|
+
- [ ] Sin Service Locator (IServiceProvider en código de negocio)
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: csharp-testing
|
|
3
|
+
description: >
|
|
4
|
+
Testing C# con xUnit, NSubstitute, FluentAssertions y WebApplicationFactory.
|
|
5
|
+
Cubre IAsyncLifetime, TestContainers, Bogus para datos falsos, coverlet y
|
|
6
|
+
snapshot testing con Verify. Cargar cuando se escriban tests en .NET o se
|
|
7
|
+
configure el pipeline de testing para proyectos C#.
|
|
8
|
+
version: "1.0.0"
|
|
9
|
+
herramientasPermitidas: [Read, Grep]
|
|
10
|
+
exclusiones:
|
|
11
|
+
- "No cargar para implementar el código C# de producción — primero implementar con `csharp-experto`, luego escribir tests."
|
|
12
|
+
- "No cargar para diseñar patrones C# (CQRS, MediatR, Result<T>) — para diseño cargar `csharp-patrones`."
|
|
13
|
+
- "No cargar para errores de compilación C# en los tests — para build errors cargar `build-errors-csharp`."
|
|
14
|
+
- "No cargar para tests de carga o rendimiento — NUnit/xUnit con Benchmark.NET requieren configuración distinta no cubierta en este skill."
|
|
15
|
+
evolvable: true # default para skill estandar
|
|
16
|
+
---
|
|
17
|
+
# C# Testing — xUnit, NSubstitute, FluentAssertions
|
|
18
|
+
|
|
19
|
+
## Cuándo NO cargar
|
|
20
|
+
|
|
21
|
+
- El código C# de producción aún no existe — primero implementar con `csharp-experto`, luego escribir tests.
|
|
22
|
+
- La pregunta es sobre diseñar patrones C# (CQRS, MediatR) — cargar `csharp-patrones`.
|
|
23
|
+
- Los errores son de compilación C# en los archivos de test — cargar `build-errors-csharp`.
|
|
24
|
+
- La pregunta es sobre tests de carga o rendimiento con BenchmarkDotNet — no está cubierto en este skill.
|
|
25
|
+
|
|
26
|
+
## Lifecycle de xUnit
|
|
27
|
+
|
|
28
|
+
```csharp
|
|
29
|
+
// IAsyncLifetime para setup/teardown async
|
|
30
|
+
public class FacturasServiceTests : IAsyncLifetime
|
|
31
|
+
{
|
|
32
|
+
private AppDbContext _db = null!;
|
|
33
|
+
|
|
34
|
+
public async Task InitializeAsync()
|
|
35
|
+
{
|
|
36
|
+
// Se ejecuta antes del primer test de la clase
|
|
37
|
+
_db = await TestDbContextFactory.CreateAsync();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public async Task DisposeAsync()
|
|
41
|
+
{
|
|
42
|
+
await _db.DisposeAsync();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// IClassFixture<T> — fixture compartido entre todos los tests de la clase
|
|
47
|
+
// Solo para recursos costosos de inicializar (contenedor de BD, servidor HTTP)
|
|
48
|
+
public class IntegrationTests(WebAppFixture fixture)
|
|
49
|
+
: IClassFixture<WebAppFixture>
|
|
50
|
+
{
|
|
51
|
+
private readonly HttpClient _client = fixture.Client;
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Naming — Should_When
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
NombreMetodo_Debe_ResultadoEsperado_CuandoCondicion
|
|
61
|
+
Ej: Crear_DebeRetornarFactura_CuandoDatosValidos
|
|
62
|
+
Crear_DebeLanzarValidacion_CuandoFolioVacio
|
|
63
|
+
ObtenerTodas_DebeRetornarVacia_CuandoNoHayFacturas
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## NSubstitute — Mocks
|
|
69
|
+
|
|
70
|
+
```csharp
|
|
71
|
+
// Crear sustituto
|
|
72
|
+
var repo = Substitute.For<IFacturasRepository>();
|
|
73
|
+
|
|
74
|
+
// Configurar respuesta
|
|
75
|
+
repo.ObtenerAsync(Arg.Any<Guid>(), Arg.Any<CancellationToken>())
|
|
76
|
+
.Returns(new Factura { Id = Guid.NewGuid(), Folio = "F-001" });
|
|
77
|
+
|
|
78
|
+
// Configurar respuesta async con Task
|
|
79
|
+
repo.CrearAsync(Arg.Any<Factura>())
|
|
80
|
+
.Returns(Task.FromResult(new Factura()));
|
|
81
|
+
|
|
82
|
+
// Verificar llamada
|
|
83
|
+
await repo.Received(1).ObtenerAsync(
|
|
84
|
+
Arg.Is<Guid>(id => id == facturaId),
|
|
85
|
+
Arg.Any<CancellationToken>());
|
|
86
|
+
|
|
87
|
+
// Verificar que NO se llamó
|
|
88
|
+
repo.DidNotReceive().EliminarAsync(Arg.Any<Guid>());
|
|
89
|
+
|
|
90
|
+
// Capturar argumento
|
|
91
|
+
Factura? facturaCapturada = null;
|
|
92
|
+
await repo.CrearAsync(Arg.Do<Factura>(f => facturaCapturada = f));
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## FluentAssertions
|
|
98
|
+
|
|
99
|
+
```csharp
|
|
100
|
+
// Objetos
|
|
101
|
+
resultado.Should().NotBeNull();
|
|
102
|
+
resultado.Should().BeOfType<FacturaDto>();
|
|
103
|
+
resultado.Id.Should().Be(esperadoId);
|
|
104
|
+
|
|
105
|
+
// Colecciones
|
|
106
|
+
facturas.Should().HaveCount(3);
|
|
107
|
+
facturas.Should().Contain(f => f.Folio == "F-001");
|
|
108
|
+
facturas.Should().BeInAscendingOrder(f => f.FechaEmision);
|
|
109
|
+
facturas.Should().AllSatisfy(f => f.EmpresaId.Should().Be(empresaId));
|
|
110
|
+
|
|
111
|
+
// Excepciones
|
|
112
|
+
var act = async () => await svc.CrearAsync(null!);
|
|
113
|
+
await act.Should().ThrowAsync<ArgumentNullException>()
|
|
114
|
+
.WithMessage("*factura*");
|
|
115
|
+
|
|
116
|
+
// Strings
|
|
117
|
+
mensaje.Should().StartWith("Error").And.Contain("validación");
|
|
118
|
+
|
|
119
|
+
// Fechas
|
|
120
|
+
factura.FechaEmision.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(5));
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## WebApplicationFactory — Tests de integración
|
|
126
|
+
|
|
127
|
+
```csharp
|
|
128
|
+
public class WebAppFixture : WebApplicationFactory<Program>, IAsyncLifetime
|
|
129
|
+
{
|
|
130
|
+
private readonly PostgreSqlContainer _pg = new PostgreSqlBuilder()
|
|
131
|
+
.WithImage("postgres:16-alpine")
|
|
132
|
+
.Build();
|
|
133
|
+
|
|
134
|
+
protected override void ConfigureWebHost(IWebHostBuilder builder)
|
|
135
|
+
{
|
|
136
|
+
builder.ConfigureTestServices(services =>
|
|
137
|
+
{
|
|
138
|
+
// Reemplazar DbContext con la BD del contenedor
|
|
139
|
+
services.RemoveAll<DbContextOptions<AppDbContext>>();
|
|
140
|
+
services.AddDbContext<AppDbContext>(o =>
|
|
141
|
+
o.UseNpgsql(_pg.GetConnectionString()));
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
public async Task InitializeAsync()
|
|
146
|
+
{
|
|
147
|
+
await _pg.StartAsync();
|
|
148
|
+
// Aplicar migraciones a la BD de test
|
|
149
|
+
using var scope = Services.CreateScope();
|
|
150
|
+
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
|
151
|
+
await db.Database.MigrateAsync();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
public new async Task DisposeAsync()
|
|
155
|
+
{
|
|
156
|
+
await _pg.StopAsync();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Uso en test
|
|
161
|
+
public class FacturasApiTests(WebAppFixture fixture)
|
|
162
|
+
: IClassFixture<WebAppFixture>
|
|
163
|
+
{
|
|
164
|
+
[Fact]
|
|
165
|
+
public async Task PostFactura_Debe201_CuandoDatosValidos()
|
|
166
|
+
{
|
|
167
|
+
var client = fixture.CreateClient();
|
|
168
|
+
var body = new { Folio = "F-001", EmpresaId = Guid.NewGuid() };
|
|
169
|
+
|
|
170
|
+
var response = await client.PostAsJsonAsync("/api/v1/facturas", body);
|
|
171
|
+
|
|
172
|
+
response.StatusCode.Should().Be(HttpStatusCode.Created);
|
|
173
|
+
var dto = await response.Content.ReadFromJsonAsync<FacturaDto>();
|
|
174
|
+
dto!.Folio.Should().Be("F-001");
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Bogus — Datos falsos
|
|
182
|
+
|
|
183
|
+
```csharp
|
|
184
|
+
// Centralizar factories en Tests/Factories/
|
|
185
|
+
public static class FacturaFaker
|
|
186
|
+
{
|
|
187
|
+
private static readonly Faker<Factura> _faker = new Faker<Factura>("es")
|
|
188
|
+
.RuleFor(f => f.Id, f => f.Random.Guid())
|
|
189
|
+
.RuleFor(f => f.Folio, f => $"F-{f.Random.Int(1, 999):D3}")
|
|
190
|
+
.RuleFor(f => f.Total, f => f.Finance.Amount(100, 50000))
|
|
191
|
+
.RuleFor(f => f.FechaEmision, f => f.Date.Recent(30))
|
|
192
|
+
.RuleFor(f => f.Estatus, f => f.PickRandom("borrador", "emitida"));
|
|
193
|
+
|
|
194
|
+
public static Factura Generar() => _faker.Generate();
|
|
195
|
+
public static List<Factura> GenerarLista(int n = 5) => _faker.Generate(n);
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Coverlet — Configuración
|
|
202
|
+
|
|
203
|
+
```xml
|
|
204
|
+
<!-- MiApi.Tests.csproj -->
|
|
205
|
+
<PackageReference Include="coverlet.collector" Version="6.*" />
|
|
206
|
+
<PackageReference Include="coverlet.msbuild" Version="6.*" />
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
# Ejecutar con cobertura mínima 80%
|
|
211
|
+
dotnet test --collect:"XPlat Code Coverage" \
|
|
212
|
+
/p:Threshold=80 \
|
|
213
|
+
/p:ThresholdType=line \
|
|
214
|
+
/p:ExcludeByAttribute="GeneratedCodeAttribute"
|
|
215
|
+
|
|
216
|
+
# Generar reporte HTML
|
|
217
|
+
reportgenerator -reports:"**/coverage.cobertura.xml" \
|
|
218
|
+
-targetdir:"coveragereport" -reporttypes:Html
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Verify — Snapshot Testing
|
|
224
|
+
|
|
225
|
+
```csharp
|
|
226
|
+
// Útil para DTOs complejos, HTML generado, respuestas de API
|
|
227
|
+
[Fact]
|
|
228
|
+
public async Task ObtenerReporte_DebeMatchSnapshot()
|
|
229
|
+
{
|
|
230
|
+
var reporte = await _svc.GenerarReporteAsync(empresaId);
|
|
231
|
+
|
|
232
|
+
// Primera ejecución: crea el archivo .verified.txt
|
|
233
|
+
// Siguientes: compara contra el snapshot
|
|
234
|
+
await Verify(reporte)
|
|
235
|
+
.ScrubMember<ReporteDto>(r => r.FechaGeneracion); // excluir campos volátiles
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Anti-patrones
|
|
242
|
+
|
|
243
|
+
### Sleep en tests — NUNCA
|
|
244
|
+
|
|
245
|
+
```csharp
|
|
246
|
+
// MAL — flaky e innecesario
|
|
247
|
+
await Task.Delay(1000);
|
|
248
|
+
Assert.True(resultado.Procesado);
|
|
249
|
+
|
|
250
|
+
// BIEN — usar Polly o un mecanismo de espera explícito
|
|
251
|
+
await Policy.Handle<AssertionException>()
|
|
252
|
+
.WaitAndRetryAsync(5, _ => TimeSpan.FromMilliseconds(200))
|
|
253
|
+
.ExecuteAsync(async () =>
|
|
254
|
+
{
|
|
255
|
+
var r = await _svc.ObtenerAsync(id);
|
|
256
|
+
r.Procesado.Should().BeTrue();
|
|
257
|
+
});
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Tests que dependen del orden — NUNCA
|
|
261
|
+
|
|
262
|
+
```csharp
|
|
263
|
+
// MAL — test 2 asume que test 1 ya insertó datos
|
|
264
|
+
// BIEN — cada test crea sus propios datos con Bogus/factories
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## Gotchas / Errores comunes no obvios
|
|
270
|
+
|
|
271
|
+
**`WebApplicationFactory` con `IClassFixture<T>` comparte el servidor entre todos los tests de la clase — los datos de un test contaminan el siguiente**: el servidor y la BD del `WebApplicationFactory` son compartidos dentro de un `IClassFixture`. Si un test crea una entidad, el siguiente test la puede encontrar. Fix: usar `IAsyncLifetime` en la clase de test para truncar tablas en `InitializeAsync`, o envolver cada test en una transacción con rollback.
|
|
272
|
+
|
|
273
|
+
**NSubstitute `.Returns()` en un método que retorna `Task` sin `await` no suspende realmente el test**: `sub.ObtenerAsync().Returns(Task.FromResult(entidad))` funciona, pero `sub.ObtenerAsync().Returns(entidad)` puede no compilar o retornar un `Task` ya completado que nunca se awaita. Fix: siempre usar `.Returns(Task.FromResult(valor))` o `.Returns(x => Task.FromResult(entidad))` para métodos async en NSubstitute.
|
|
274
|
+
|
|
275
|
+
**`Bogus.Faker<T>` genera los mismos valores si se crea sin semilla diferente por test**: por defecto `new Faker<T>()` usa una semilla aleatoria diferente en cada ejecución — esto hace que los tests sean no reproducibles cuando fallan. Fix: en tests que requieren reproducibilidad, usar `new Faker<T>().UseSeed(42)` con semilla fija, o guardar la semilla en el output del test fallido.
|
|
276
|
+
|
|
277
|
+
**FluentAssertions `BeEquivalentTo` compara propiedades por nombre y no detecta diferencias en tipos distintos con mismos nombres**: `expected.Should().BeEquivalentTo(actual)` puede pasar aunque `expected` sea un DTO y `actual` sea la entidad, porque las propiedades homónimas coinciden. Fix: usar `.BeOfType<T>()` antes de `BeEquivalentTo` cuando se quiere verificar que el tipo exacto también coincide.
|
|
278
|
+
|
|
279
|
+
## Checklist de tests
|
|
280
|
+
|
|
281
|
+
- [ ] Naming sigue Should_When (o Debe_Cuando en español)
|
|
282
|
+
- [ ] Sin Task.Delay/Thread.Sleep en tests
|
|
283
|
+
- [ ] Cada test crea sus datos (no depende de otro test)
|
|
284
|
+
- [ ] WebApplicationFactory usa TestContainers para BD real
|
|
285
|
+
- [ ] Cobertura >= 80% verificada con coverlet en CI
|
|
286
|
+
- [ ] Factories de datos en Tests/Factories/, no inline
|