# Escribe pruebas con Testcontainers


Ya tienes lista la implementación del `Repository`, pero para realizar las pruebas necesitas una
base de datos PostgreSQL. Puedes usar testcontainers-go para levantar una base de datos Postgres
en un contenedor Docker y ejecutar tus pruebas contra esa base de datos.

## Configura la base de datos de pruebas

En aplicaciones reales, podrías utilizar una herramienta de migración de bases de datos, pero para esta
guía, usaremos un script para inicializar la base de datos.

Crea un archivo `testdata/init-db.sql` para crear la tabla `CUSTOMERS` e
insertar datos de muestra:

```sql
CREATE TABLE IF NOT EXISTS customers (id serial, name varchar(255), email varchar(255));

INSERT INTO customers(name, email) VALUES ('John', 'john@gmail.com');
```

## Entiende la API de testcontainers-go

La biblioteca testcontainers-go proporciona la abstracción genérica `Container`
que puede ejecutar cualquier servicio contenedorizado. Para simplificar aún más, testcontainers-go
ofrece módulos específicos para cada tecnología que reducen el código repetitivo y proporcionan un
patrón de opciones funcionales para construir la instancia del contenedor.

Por ejemplo, `PostgresContainer` proporciona `WithDatabase()`,
`WithUsername()`, `WithPassword()` y otras funciones para configurar varias
propiedades de los contenedores Postgres.

## Escribe la prueba

Crea el archivo `customer/repo_test.go` e implementa la prueba:

```go
package customer

import (
	"context"
	"path/filepath"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/testcontainers/testcontainers-go"
	"github.com/testcontainers/testcontainers-go/modules/postgres"
)

func TestCustomerRepository(t *testing.T) {
	ctx := context.Background()

	ctr, err := postgres.Run(ctx,
		"postgres:16-alpine",
		postgres.WithInitScripts(filepath.Join("..", "testdata", "init-db.sql")),
		postgres.WithDatabase("test-db"),
		postgres.WithUsername("postgres"),
		postgres.WithPassword("postgres"),
		postgres.BasicWaitStrategies(),
	)
	testcontainers.CleanupContainer(t, ctr)
	require.NoError(t, err)

	connStr, err := ctr.ConnectionString(ctx, "sslmode=disable")
	require.NoError(t, err)

	customerRepo, err := NewRepository(ctx, connStr)
	require.NoError(t, err)

	c, err := customerRepo.CreateCustomer(ctx, Customer{
		Name:  "Henry",
		Email: "henry@gmail.com",
	})
	assert.NoError(t, err)
	assert.NotNil(t, c)

	customer, err := customerRepo.GetCustomerByEmail(ctx, "henry@gmail.com")
	assert.NoError(t, err)
	assert.NotNil(t, customer)
	assert.Equal(t, "Henry", customer.Name)
	assert.Equal(t, "henry@gmail.com", customer.Email)
}
```

Esto es lo que hace la prueba:

- Llama a `postgres.Run()` con la imagen Docker `postgres:16-alpine` como
  primer argumento. Esta es la API v0.41.0: la imagen es un parámetro posicional
  obligatorio en lugar de una opción.
- Configura scripts de inicialización usando `WithInitScripts(...)` para que la
  tabla `CUSTOMERS` se cree y los datos de muestra se inserten después de que la base de datos
  se inicie.
- Usa `postgres.BasicWaitStrategies()` que combina la espera del mensaje de log de
  Postgres y la preparación del puerto. Esto reemplaza la configuración manual de
  la estrategia de espera.
- Llama a `testcontainers.CleanupContainer(t, ctr)` justo después de `postgres.Run()`.
  Esto registra la limpieza automática con el framework de pruebas, reemplazando el
  patrón manual de `t.Cleanup` y `Terminate`.
- Obtiene la cadena de conexión `ConnectionString` del contenedor e inicializa un
  `Repository`.
- Crea un cliente con el correo electrónico `henry@gmail.com` y verifica que el
  cliente exista en la base de datos.

