# Pruebas del DAO — guía de uso

Cómo dejar corriendo las pruebas en una máquina nueva. Escrito para no tener que reconstruir el contexto desde cero.

---

## Qué hay aquí

Dos suites independientes en el mismo proyecto, separadas por el trait `suite`:

| Suite | Contra qué corre | Comando | Requiere |
|---|---|---|---|
| `grpc` | El DAO ya desplegado, por gRPC | `make test-grpc` | Port-forward al cluster |
| `db` | Base de datos real, en proceso | `make test-db` | Cadena de conexión |

`make test` corre las dos. **Si solo quieres validar el servicio desplegado, la que importa es `test-grpc`** — no necesita credenciales de base de datos.

La suite `grpc` es de **solo consulta**: no invoca ningún `Create`, `Update`, `Delete`, `Upsert`, `Bulk*` ni `set*`. Es idempotente, así que se puede correr contra QA las veces que haga falta sin ensuciar datos.

---

## Requisitos en la máquina nueva

| Herramienta | Para qué | Verificar |
|---|---|---|
| .NET SDK 10 | Los proyectos son `net10.0` | `dotnet --list-sdks` |
| `kubectl` | Port-forward al servicio | `kubectl version --client` |
| `ssh` | Túnel SOCKS hacia el cluster | `ssh -V` |
| `make` | Los targets del Makefile | `make --version` |
| `grpcurl` | Opcional, para pruebas manuales | `grpcurl --version` |

En Windows, `make` viene con Git Bash o se instala con `winget install GnuWin32.Make`. `grpcurl` con `winget install fullstorydev.grpcurl`.

**El kubeconfig no está en el repo.** Cópialo de la máquina actual (`~/.kube/config`) o pídelo al equipo. Contiene credenciales — no lo commitees.

---

## Poner el servicio al alcance

Son tres pasos encadenados. Si uno se cae, los de abajo se caen con él — es lo que más tiempo cuesta diagnosticar.

### 1. Túnel SOCKS

Todos los clusters del kubeconfig salen por un proxy SOCKS local. Los contextos están repartidos entre dos puertos:

```bash
ssh -fND 9000 <usuario>@<host-salto>
ssh -fND 9009 <usuario>@<host-salto>   # los contextos que usan el otro puerto
```

Revisa qué puerto necesita tu contexto:

```bash
grep -B2 "proxy-url" ~/.kube/config
```

Verifica que quedó escuchando:

```bash
# Windows
Get-NetTCPConnection -State Listen -LocalPort 9000

# Linux/macOS
ss -ltn | grep 9000
```

> El túnel se cae solo, sin avisar. Cuando algo deje de funcionar, revísalo primero.

### 2. Elegir contexto

**El kubeconfig tiene `current-context` vacío a propósito.** Sin `--context`, `kubectl` intenta `localhost:8080` y falla con un error que no menciona el contexto para nada.

```bash
kubectl config get-contexts
kubectl --context=<contexto> get ns
```

Los contextos disponibles hoy:
`deployments-cluster`, `deployments-stg-daas-qr`, `deploys-brownfield-qr`,
`deploys-brownfield-qr-stg`, `front-cluster`, `front-stg-daas-qr`, `tkgs-prod-daas-01-ap`.

### 3. Port-forward

Ubica el servicio, que según cómo se haya desplegado tiene dos formas:

```bash
kubectl --context=<contexto> get svc -A | grep -i dao
```

| Origen del despliegue | Recurso | Puerto |
|---|---|---|
| Helm (`app_chart`) | `svc/csharp-dao-service` | 8440 (TLS) |
| `pod.yaml` (Jenkins) | `deploy/net-dao-test` en `daas-stg-qa` | 50051 y 8445 |

```bash
kubectl --context=<contexto> port-forward svc/csharp-dao-service 8440:8440 -n <namespace>
```

Déjalo corriendo en su propia terminal. Confirma desde otra:

```bash
grpcurl -insecure 127.0.0.1:8440 list
```

Debe listar cinco servicios de negocio más `grpc.health.v1.Health` y los de reflection.

---

## Correr las pruebas

```bash
make test-grpc
```

Endpoint por defecto: `https://127.0.0.1:8440`. Para otro:

```bash
make test-grpc DAO_ENDPOINT=http://127.0.0.1:50051
```

O directo, sin `make`:

```bash
dotnet test tests/DynamicCrud.Tests/DynamicCrud.Tests.csproj --filter "suite=grpc"
```

Para un solo caso:

```bash
dotnet test tests/DynamicCrud.Tests/DynamicCrud.Tests.csproj \
  --filter "FullyQualifiedName~GetUserVCD"
```

### Sobre el certificado

El puerto 8440 es TLS, pero el certificado del servidor está emitido para su nombre **dentro** del cluster. A través de un port-forward llegas como `127.0.0.1`, así que el nombre nunca va a coincidir. La suite cifra pero no valida el nombre — el equivalente de `grpcurl -insecure`. Está en `Grpc/GrpcFixture.cs` y es deliberado.

Si prefieres evitar TLS, el pod también escucha en 50051 en h2c plano.

---

## Qué esperar

**362 de 367 pasan. Los 5 fallos son defectos reales del servicio, no de las pruebas.** Si ves ese número, la suite está sana.

| Prueba que falla | Causa |
|---|---|
| `StatusSupplyService_Responde` | Rpc declarado en el proto, sin override en C# |
| `ServiceStatusService_Responde` | Igual |
| `GetServicesCatalogEF_Responde` | Igual |
| `GetUserVCD_Responde` | `ServicesDAO.cs:378` filtra por organización hardcodeada |
| `GetStatusCatalog_Responde` | `spskGetStatusCatalog` usa `is not null` donde debía ser `is null` |

El detalle completo, con evidencia y qué revisar para corregir, está en [`REPORTE_PRUEBAS.md`](../REPORTE_PRUEBAS.md).

Si fallan **más** de esas 5, algo cambió: revisa primero que el port-forward siga vivo.

---

## Cuando algo falla

### `No se pudo contactar el DAO en https://127.0.0.1:8440 (Unavailable)`

El port-forward no está arriba. Es la falla más común porque muere en silencio cuando se cae el túnel SSH. Revisa en orden: túnel → contexto → port-forward.

```bash
# Windows
Get-NetTCPConnection -State Listen -LocalPort 8440
Get-Process ssh, kubectl -ErrorAction SilentlyContinue
```

### `Unable to connect to the server: dial tcp [::1]:8080`

Falta `--context`. El kubeconfig tiene `current-context` vacío, así que `kubectl` cae a su default y ese error no lo dice.

### `target server does not expose service "servicioDAOService"`

Con `grpcurl` hay que usar el nombre completo con su paquete:

```bash
grpcurl -insecure -d '{"user_id":462}' 127.0.0.1:8440 \
  cloudServiceDAO.servicioDAOService/getUserVCD
```

Los nombres completos salen de `grpcurl -insecure 127.0.0.1:8440 list`.

### Las pruebas de la suite `db` fallan

Esas necesitan base de datos y son independientes de la de gRPC. Si solo te interesa validar el servicio desplegado, usa `make test-grpc`, no `make test`. Ver la sección "Pruebas" del README raíz para configurar `ConnectionStrings:Local`.

---

## Cómo está armada

| Archivo | Qué hace |
|---|---|
| `Grpc/GrpcFixture.cs` | Canal compartido, TLS sin validar nombre, y la guarda que aborta con mensaje claro si el endpoint no responde |
| `Grpc/ModelMetadata.cs` | Lee las claves primarias del modelo EF **sin abrir conexión** — `GetFields` no expone cuál campo es la PK |
| `Grpc/DataProbe.cs` | Descubre ids reales vía el CRUD dinámico para alimentar las pruebas legacy |
| `Grpc/CrudData.cs` | Desempaca el `Value` de protobuf que devuelve `CrudResponse` |
| `Grpc/CrudReadTests.cs` | 328 pruebas: 8 verificaciones × 43 modelos |
| `Grpc/CrudErrorPathTests.cs` | 10 rutas de error |
| `Grpc/LegacyReadTests.cs` | 29 rpcs de lectura de los otros cuatro servicios |

Dos decisiones que conviene entender antes de tocar el código:

**Nada está hardcodeado.** Los ids y nombres de campo salen del propio servicio antes de cada aserción. Un id de `GetAll` alimenta el `GetById`; los campos salen de `GetFields`. Por eso la suite funciona contra cualquier entorno sin ajustes.

**La capa legacy tiene dos niveles de exigencia.** Atrapa las excepciones de MySQL y las devuelve como `NotFound`, así que *un procedure inexistente se ve igual que una tabla vacía*. Los rpcs que consultan una sola tabla con un id recién leído de ella tratan `NotFound` como fallo (`RespondeConDatos`); los que dependen de joins lo aceptan (`Responde`). Esa distinción es la que destapa los defectos reales — si la relajas, los fallos desaparecen sin que nada se haya arreglado.

---

## Punto ciego conocido

Si la tabla origen de un rpc legacy está vacía, el caso **termina temprano y pasa**. El más relevante: `getTermsandConditions` invoca `spskGetTermsAndConditions`, un procedure que no existe en la base — pero `terms_conditions` está vacía en QA, así que el defecto no se manifiesta.

En un entorno con esa tabla poblada, esa prueba empezaría a fallar. Sería correcto: el defecto siempre estuvo ahí.
