Files
Xor290 22a8d5026c
Frontend Admin - EAS Build / build (push) Canceled after 0s
Frontend Client - EAS Build / build (push) Canceled after 0s
Backend - Build & Lint / build (push) Failing after 25m18s
Frontend Web - Build & Lint / build (push) Failing after 9m58s
chore: build
2026-08-06 12:06:05 +02:00

141 lines
5.3 KiB
Go

// Package tests regroupe les tests de bout en bout de la gestion de stock,
// écrits contre l'API publique des paquets db/ et handlers/ (aucun accès à
// leurs symboles non exportés) — voir docker-compose.yml pour la base de
// test locale nécessaire pour les exécuter (`go test ./tests/...`).
package tests
import (
"fmt"
"gestion/db"
"os"
"sync/atomic"
"testing"
"github.com/gin-gonic/gin"
)
// testDB est l'instance partagée par tous les tests de ce paquet. Toutes les
// données créées utilisent un préfixe dédié (testUserPrefix / testProductPrefix)
// et sont nettoyées avant et après chaque test, ce qui rend la suite sans
// danger même si elle tourne contre une base partagée.
var testDB *db.Database
const (
testUserPrefix = "stocktest_"
testProductPrefix = "TESTSTOCK_"
)
// testPhoneCounter garantit un numéro de téléphone unique par client de test
// (colonne UNIQUE sur clients.telephone).
var testPhoneCounter int64
func nextTestPhone() string {
n := atomic.AddInt64(&testPhoneCounter, 1)
return fmt.Sprintf("+3361%09d", n)
}
func TestMain(m *testing.M) {
gin.SetMode(gin.TestMode)
testDB = db.InitDB()
db.InitRedis()
os.Exit(m.Run())
}
// cleanupStockTestData supprime toutes les données créées par les tests de
// gestion de stock (identifiées par leur préfixe), dans le bon ordre pour
// respecter les contraintes de clé étrangère.
func cleanupStockTestData(t *testing.T) {
t.Helper()
testDB.GDB.Exec(`DELETE FROM command_items WHERE command_id IN (SELECT id FROM commandes WHERE username LIKE ?)`, testUserPrefix+"%")
testDB.GDB.Exec(`DELETE FROM commandes WHERE username LIKE ?`, testUserPrefix+"%")
testDB.GDB.Exec(`DELETE FROM baskets WHERE username LIKE ?`, testUserPrefix+"%")
testDB.GDB.Exec(`DELETE FROM clients WHERE username LIKE ?`, testUserPrefix+"%")
testDB.GDB.Exec(`DELETE FROM product_prices WHERE product_id IN (SELECT id FROM products WHERE name LIKE ?)`, testProductPrefix+"%")
testDB.GDB.Exec(`DELETE FROM products WHERE name LIKE ?`, testProductPrefix+"%")
}
// newTestProduct crée un produit de test avec un stock initial donné et un
// prix actif pour quantity=1, et programme son nettoyage en fin de test.
func newTestProduct(t *testing.T, name string, stock float64) int {
t.Helper()
fullName := testProductPrefix + name
var id int
if err := testDB.GDB.Raw(
`INSERT INTO products (name, category, description, stock) VALUES (?, 'test', '', ?) RETURNING id`,
fullName, stock,
).Scan(&id).Error; err != nil {
t.Fatalf("création produit test %q: %v", fullName, err)
}
if err := testDB.GDB.Exec(
`INSERT INTO product_prices (product_id, quantity, price, active_price) VALUES (?, 1, 10.00, true)`,
id,
).Error; err != nil {
t.Fatalf("création prix produit test %q: %v", fullName, err)
}
t.Cleanup(func() {
testDB.GDB.Exec(`DELETE FROM product_prices WHERE product_id = ?`, id)
testDB.GDB.Exec(`DELETE FROM products WHERE id = ?`, id)
})
return id
}
// productStock relit le stock courant d'un produit directement en base.
func productStock(t *testing.T, productID int) float64 {
t.Helper()
var stock float64
if err := testDB.GDB.Raw(`SELECT stock FROM products WHERE id = ?`, productID).Scan(&stock).Error; err != nil {
t.Fatalf("lecture stock produit %d: %v", productID, err)
}
return stock
}
// newTestClient crée un client de test et programme son nettoyage en fin de test.
func newTestClient(t *testing.T, name string) string {
t.Helper()
username := testUserPrefix + name
if err := testDB.GDB.Exec(
`INSERT INTO clients (username, password, nom, prenom, telephone) VALUES (?, 'x', 'T', 'C', ?)
ON CONFLICT (username) DO NOTHING`,
username, nextTestPhone(),
).Error; err != nil {
t.Fatalf("création client test %q: %v", username, err)
}
t.Cleanup(func() {
testDB.GDB.Exec(`DELETE FROM baskets WHERE username = ?`, username)
testDB.GDB.Exec(`DELETE FROM command_items WHERE command_id IN (SELECT id FROM commandes WHERE username = ?)`, username)
testDB.GDB.Exec(`DELETE FROM commandes WHERE username = ?`, username)
testDB.GDB.Exec(`DELETE FROM clients WHERE username = ?`, username)
})
return username
}
// newTestCommandWithItem crée directement une commande avec un item (en
// contournant le checkout), pour tester isolément les chemins d'annulation
// et de remboursement de stock. Retourne l'ID de la commande créée.
func newTestCommandWithItem(t *testing.T, username, status, livreurAssign string, productID int, quantite float64, prix float64) int {
t.Helper()
var cmdID int
if err := testDB.GDB.Raw(
`INSERT INTO commandes (username, status, livreur_assign, adresse, total_prix, created_at, updated_at)
VALUES (?, ?, NULLIF(?, ''), 'Adresse test', ?, NOW(), NOW()) RETURNING id`,
username, status, livreurAssign, prix,
).Scan(&cmdID).Error; err != nil {
t.Fatalf("création commande test: %v", err)
}
if err := testDB.GDB.Exec(
`INSERT INTO command_items (command_id, product_id, produit, quantite, prix, status)
VALUES (?, ?, 'item test', ?, ?, 'pending')`,
cmdID, productID, quantite, prix,
).Error; err != nil {
t.Fatalf("création item test: %v", err)
}
return cmdID
}
func commandStatus(t *testing.T, commandID int) string {
t.Helper()
var status string
testDB.GDB.Raw(`SELECT status FROM commandes WHERE id = ?`, commandID).Scan(&status)
return status
}