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>
|
||
|---|---|---|
| .. | ||
| api_health_test.go | ||
| gdpr_flow_test.go | ||
| logout_blacklist_test.go | ||
| main_test.go | ||
| oauth_github_test.go | ||
| oauth_google_test.go | ||
| payment_flow_test.go | ||
| payout_flow_test.go | ||
| QUARANTINE.md | ||
| README.md | ||
| refund_flow_test.go | ||
| resume_upload_test.go | ||
| token_refresh_test.go | ||
| track_quota_test.go | ||
| upload_async_polling_test.go | ||
| upload_flow_test.go | ||
| webhook_idempotency_test.go | ||
| webhook_security_test.go | ||
| webhook_test_helpers.go | ||
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:
-
PostgreSQL (obligatoire)
- Version: 15+
- Base de données:
veza_test - Utilisateur:
veza/ Mot de passe:veza - Migrations: Appliquées automatiquement via testcontainers
-
Redis (obligatoire pour certains tests)
- Version: 7+
- Port: 6379 (par défaut)
- Aucune base de données spécifique requise
-
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 statusTestUploadScalability- 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:
- Vérifier logs testcontainers pour timeouts
- Augmenter timeouts dans setup si nécessaire
- Vérifier ressources système (CPU, mémoire)
- Documenter dans
QUARANTINE.mdsi 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
- Testcontainers Go: https://golang.testcontainers.org/
- Quarantaine:
tests/integration/QUARANTINE.md - Setup helpers:
internal/testutils/setup.go
Dernière mise à jour: 2025-12-15
Maintenu par: Veza Backend Team