veza/veza-backend-api/tests/integration
senke eedaad9f83 refactor(connect): persist stripe_transfer_id on create + retry — v1.0.7 item A
TransferService.CreateTransfer signature changes from (...) error to
(...) (string, error) — the caller now captures the Stripe transfer
identifier and persists it on the SellerTransfer row. Pre-v1.0.7 the
stripe_transfer_id column was declared on the model and table but
never written to, which blocked the reversal worker (v1.0.7 item B)
from identifying which transfer to reverse on refund.

Changes:
  * `TransferService` interface and `StripeConnectService.CreateTransfer`
    both return the Stripe transfer id alongside the error.
  * `processSellerTransfers` (marketplace service) persists the id on
    success before `tx.Create(&st)` so a crash between Stripe ACK and
    DB commit leaves no inconsistency.
  * `TransferRetryWorker.retryOne` persists on retry success — a row
    that failed on first attempt and succeeded via the worker is
    reversal-ready all the same.
  * `admin_transfer_handler.RetryTransfer` (manual retry) persists too.
  * `SellerPayout.ExternalPayoutID` is populated by the Connect payout
    flow (`payout.go`) — the field existed but was never written.
  * Four test mocks updated; two tests assert the id is persisted on
    the happy path, one on the failure path confirms we don't write a
    fake id when the provider errors.

Migration `981_seller_transfers_stripe_reversal_id.sql`:
  * Adds nullable `stripe_reversal_id` column for item B.
  * Partial UNIQUE indexes on both stripe_transfer_id and
    stripe_reversal_id (WHERE IS NOT NULL AND <> ''), mirroring the
    v1.0.6.1 pattern for refunds.hyperswitch_refund_id.
  * Logs a count of historical completed transfers that lack an id —
    these are candidates for the backfill CLI follow-up task.

Backfill for historical rows is a separate follow-up (cmd/tools/
backfill_stripe_transfer_ids, calling Stripe's transfers.List with
Destination + Metadata[order_id]). Pre-v1.0.7 transfers without a
backfilled id cannot be auto-reversed on refund — document in P2.9
admin-recovery when it lands. Acceptable scope per v107-plan.

Migration number bumped 980 → 981 because v1.0.6.2 used 980 for the
unpaid-subscription cleanup; v107-plan updated with the note.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 13:08:39 +02:00
..
api_health_test.go stabilizing veza-backend-api: phase 1 2025-12-16 11:23:49 -05:00
gdpr_flow_test.go style(backend): gofmt -w on 85 files (whitespace only) 2026-04-14 12:22:14 +02:00
logout_blacklist_test.go v0.9.1 2026-03-05 19:22:31 +01:00
main_test.go test(backend): gate testcontainers tests behind VEZA_SKIP_INTEGRATION 2026-04-14 11:45:19 +02:00
oauth_github_test.go style(backend): gofmt -w on 85 files (whitespace only) 2026-04-14 12:22:14 +02:00
oauth_google_test.go style(backend): gofmt -w on 85 files (whitespace only) 2026-04-14 12:22:14 +02:00
payment_flow_test.go refactor(connect): persist stripe_transfer_id on create + retry — v1.0.7 item A 2026-04-17 13:08:39 +02:00
payout_flow_test.go feat(v0.13.5): polish marketplace & compliance — KYC, support, payout E2E 2026-03-13 14:57:19 +01:00
QUARANTINE.md stabilizing veza-backend-api: phase 1 2025-12-16 11:23:49 -05:00
README.md chore(v0.102): consolidate remaining changes — docs, frontend, backend 2026-02-20 13:02:12 +01:00
refund_flow_test.go style(backend): gofmt -w on 85 files (whitespace only) 2026-04-14 12:22:14 +02:00
resume_upload_test.go feat(auth): v0.911 Keystone - OAuth and auth integration tests 2026-02-27 09:58:53 +01:00
token_refresh_test.go v0.9.1 2026-03-05 19:22:31 +01:00
track_quota_test.go style(backend): gofmt -w on 85 files (whitespace only) 2026-04-14 12:22:14 +02:00
upload_async_polling_test.go stabilizing veza-backend-api: phase 1 2025-12-16 11:23:49 -05:00
upload_flow_test.go stabilizing veza-backend-api: phase 1 2025-12-16 11:23:49 -05:00
webhook_idempotency_test.go style(backend): gofmt -w on 85 files (whitespace only) 2026-04-14 12:22:14 +02:00
webhook_security_test.go style(backend): gofmt -w on 85 files (whitespace only) 2026-04-14 12:22:14 +02:00
webhook_test_helpers.go feat(v0.912): Cashflow - payment E2E integration tests 2026-02-27 20:00:51 +01:00

Tests d'Intégration - veza-backend-api

Objectif: Tests d'intégration reproductibles validant le comportement réel du système avec dépendances externes.


Contrat d'Environnement Minimal

Services Requis

Les tests d'intégration nécessitent les services suivants:

  1. PostgreSQL (obligatoire)

    • Version: 15+
    • Base de données: veza_test
    • Utilisateur: veza / Mot de passe: veza
    • Migrations: Appliquées automatiquement via testcontainers
  2. Redis (obligatoire pour certains tests)

    • Version: 7+
    • Port: 6379 (par défaut)
    • Aucune base de données spécifique requise
  3. RabbitMQ (optionnel)

    • Utilisé uniquement pour tests spécifiques
    • Peut être skippé si non disponible

Méthodes de Setup

Option 1: Testcontainers (Recommandé)

Avantages:

  • Reproductible (même environnement partout)
  • Isolation complète (pas de pollution entre tests)
  • Pas de configuration manuelle requise
  • Fonctionne en CI/CD

Prérequis:

  • Docker installé et en cours d'exécution
  • Go 1.21+

Utilisation:

# Les tests utilisent automatiquement testcontainers
go test ./tests/integration/... -tags integration -v

Configuration:

  • PostgreSQL: Démarre automatiquement via internal/testutils/setup.go
  • Redis: Démarre automatiquement via internal/testutils/setup_redis.go (à créer)
  • Migrations: Appliquées automatiquement depuis migrations/

Option 2: Services Locaux (Alternative)

Quand utiliser:

  • Docker non disponible
  • Tests de développement rapide
  • Debugging local

Configuration (choisir selon la stack démarrée):

  • Avec make infra-up (docker-compose.yml dev) :
export DATABASE_URL="postgresql://veza:password@localhost:15432/veza?sslmode=disable"
export REDIS_ADDR="localhost:16379"
export REDIS_TEST_URL="redis://localhost:16379/15"
export RABBITMQ_URL="amqp://veza:password@localhost:15672/"
  • Avec docker-compose.test.yml (stack dédiée tests) :
export DATABASE_URL="postgresql://veza_test:veza_test@localhost:5434/veza_test?sslmode=disable"
export REDIS_ADDR="localhost:6380"
export REDIS_TEST_URL="redis://localhost:6380/15"
export RABBITMQ_URL="amqp://veza_test:veza_test@localhost:5673/"

Setup manuel:

# PostgreSQL
createdb veza_test
psql veza_test < migrations/*.sql

# Redis
redis-server

# RabbitMQ (optionnel)
docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3-management

Exécution des Tests

Tests d'Intégration (Tous)

# Avec testcontainers (recommandé)
go test ./tests/integration/... -tags integration -v

# Avec services locaux (ports selon make infra-up)
export DATABASE_URL="postgresql://veza:password@localhost:15432/veza?sslmode=disable"
export REDIS_ADDR="localhost:16379"
go test ./tests/integration/... -tags integration -v

Tests Spécifiques

# Test upload async polling
go test ./tests/integration -tags integration -run TestUploadAsyncPollingStatus -v

# Test upload scalability
go test ./tests/integration -tags integration -run TestUploadScalability -v

Via Makefile

# Tous les tests d'intégration
make test-integration

# Tests avec quarantaine (validation manuelle)
make test-quarantine

Structure des Tests

Tests Non-Quarantinés

Ces tests doivent toujours passer avant production:

  • TestUploadAsyncPollingStatus - Upload async avec polling status
  • TestUploadScalability - Upload distribué avec Redis

Tests Quarantinés

Voir QUARANTINE.md pour la classification complète.

Classification:

  • 🔴 Doit passer avant prod - Bloquant pour release
  • 🟡 CI nightly - Exécuté en CI séparé, non-bloquant
  • 🟢 Manual only - Exécution manuelle uniquement

Dépannage

Erreur: "Docker not running"

Solution: Démarrer Docker

# Linux
sudo systemctl start docker

# macOS
open -a Docker

Erreur: "PostgreSQL container failed to start"

Solution: Vérifier logs testcontainers

docker ps -a | grep postgres
docker logs <container_id>

Cause commune: Port 5432 déjà utilisé

# Vérifier processus utilisant le port
lsof -i :5432

# Arrêter processus ou changer port dans test

Erreur: "Redis not available"

Solution 1: Utiliser testcontainers (recommandé)

  • Les tests utilisent automatiquement testcontainers si disponible

Solution 2: Démarrer Redis localement

redis-server
# ou
docker run -d -p 6379:6379 redis:7-alpine

Test Flaky (intermittent)

Actions:

  1. Vérifier logs testcontainers pour timeouts
  2. Augmenter timeouts dans setup si nécessaire
  3. Vérifier ressources système (CPU, mémoire)
  4. Documenter dans QUARANTINE.md si non-résolvable

Tests internal/testutils et SetupTestDB()

Les tests dans internal/testutils qui utilisent SetupTestDB() (db_cleanup_test, db_test, fixtures_test) nécessitent Docker et testcontainers (PostgreSQL). En l'absence de Docker ou avec -short:

  • Ces tests sont automatiquement skippés via SkipIfDockerUnavailable(t)
  • Pour exécuter tous les tests sans Docker: go test ./... -short (les tests testutils seront skippés)
  • Pour exécuter les tests testutils: s'assurer que Docker est démarré et ne pas utiliser -short

Variables d'Environnement

Pour Tests avec Services Locaux

Variable Description Défaut
DATABASE_URL PostgreSQL connection string postgresql://veza:veza@localhost:5432/veza_test?sslmode=disable
REDIS_ADDR Redis address localhost:6379
RABBITMQ_URL RabbitMQ connection string amqp://guest:guest@localhost:5672/
SKIP_TESTCONTAINERS Forcer services locaux (si true) false

Pour Tests avec Testcontainers

Aucune variable requise - testcontainers démarre automatiquement les services.


CI/CD

Pipeline Normal

- name: Run unit tests
  run: go test ./internal/... -short -tags '!integration'

Pipeline Intégration (Séparé)

- name: Run integration tests
  run: go test ./tests/integration/... -tags integration -v
  services:
    docker:
      image: docker:latest

Note: Les tests d'intégration peuvent être exécutés:

  • En CI nightly (tous les tests)
  • En CI sur demande (workflow_dispatch)
  • En CI sur PR (tests non-quarantinés uniquement)

Bonnes Pratiques

1. Isolation des Tests

  • Chaque test utilise sa propre base de données (via testcontainers)
  • Nettoyage automatique après chaque test
  • Pas de dépendances entre tests

2. Reproductibilité

  • Utiliser testcontainers pour environnement identique
  • Éviter les timeouts courts (< 1s)
  • Éviter les sleeps fixes (utiliser polling avec timeout)

3. Performance

  • Tests parallèles quand possible (t.Parallel())
  • Timeouts raisonnables (5-10s max par test)
  • Nettoyer ressources (containers, fichiers) après test

4. Fiabilité

  • Tests non-flaky (passent 100% du temps)
  • Messages d'erreur clairs
  • Skip si dépendances manquantes (avec message)

Exemples

Exemple 1: Test avec Testcontainers

func TestMyIntegration(t *testing.T) {
    ctx := context.Background()
    
    // PostgreSQL via testcontainers
    dsn, err := testutils.GetTestContainerDB(ctx)
    require.NoError(t, err)
    
    db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
    require.NoError(t, err)
    
    // Test...
}

Exemple 2: Test avec Services Locaux

func TestMyIntegration(t *testing.T) {
    dsn := os.Getenv("DATABASE_URL")
    if dsn == "" {
        t.Skip("DATABASE_URL not set, skipping integration test")
    }
    
    db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
    require.NoError(t, err)
    
    // Test...
}

Références


Dernière mise à jour: 2025-12-15
Maintenu par: Veza Backend Team