This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"gestion/db"
|
||||
"gestion/handlers"
|
||||
"gestion/models"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func claimRewardContext(username string, body []byte) (*gin.Context, *httptest.ResponseRecorder) {
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/v1/points/claim", bytes.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
rec := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(rec)
|
||||
c.Request = req
|
||||
c.Set("database", testDB)
|
||||
c.Set("username", username)
|
||||
return c, rec
|
||||
}
|
||||
|
||||
func configureRewardSettings(t *testing.T, reward *models.PointsReward) {
|
||||
t.Helper()
|
||||
settings := db.DefaultSettings()
|
||||
settings.PointsReward = reward
|
||||
if err := testDB.UpdateSettings(settings); err != nil {
|
||||
t.Fatalf("UpdateSettings: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Flux complet réel : POST /points/claim avec un seuil atteint doit ajouter
|
||||
// le produit récompense configuré au panier et décompter la récompense.
|
||||
func TestClaimMyReward_HTTPFlow_AddsRewardToBasketAndDecrementsAvailable(t *testing.T) {
|
||||
cleanupStockTestData(t)
|
||||
username := newTestClient(t, "reward_http_flow")
|
||||
rewardProductID := newTestProduct(t, "RewardHTTPFlow", 5)
|
||||
|
||||
configureRewardSettings(t, &models.PointsReward{
|
||||
Threshold: 20,
|
||||
Type: "free_product",
|
||||
Description: "Un produit offert",
|
||||
RewardItems: []models.RewardItem{{ProductID: rewardProductID, Quantity: 1, Price: 12}},
|
||||
})
|
||||
setClientPoolPoints(t, username, "pool_0", 20)
|
||||
|
||||
body, _ := json.Marshal(map[string]string{"pool_key": "pool_0"})
|
||||
c, rec := claimRewardContext(username, body)
|
||||
handlers.ClaimMyReward(c)
|
||||
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("status HTTP: got=%d body=%s", rec.Code, rec.Body.String())
|
||||
}
|
||||
|
||||
var resp struct {
|
||||
Success bool `json:"success"`
|
||||
RemainingRewards int `json:"remaining_rewards"`
|
||||
ProductAdded bool `json:"product_added"`
|
||||
}
|
||||
if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
|
||||
t.Fatalf("décodage réponse: %v body=%s", err, rec.Body.String())
|
||||
}
|
||||
if !resp.Success || !resp.ProductAdded {
|
||||
t.Fatalf("réclamation devrait réussir avec produit ajouté: %+v", resp)
|
||||
}
|
||||
if resp.RemainingRewards != 0 {
|
||||
t.Errorf("remaining_rewards: got=%d want=0", resp.RemainingRewards)
|
||||
}
|
||||
|
||||
rows := basketRewardItems(t, username)
|
||||
if len(rows) != 1 || rows[0].ProductID != rewardProductID {
|
||||
t.Errorf("le produit récompense doit être dans le panier: %+v", rows)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClaimMyReward_HTTPFlow_RejectsWhenBelowThreshold(t *testing.T) {
|
||||
cleanupStockTestData(t)
|
||||
username := newTestClient(t, "reward_http_below")
|
||||
rewardProductID := newTestProduct(t, "RewardHTTPBelow", 5)
|
||||
|
||||
configureRewardSettings(t, &models.PointsReward{
|
||||
Threshold: 20,
|
||||
Type: "free_product",
|
||||
RewardItems: []models.RewardItem{{ProductID: rewardProductID, Quantity: 1, Price: 12}},
|
||||
})
|
||||
setClientPoolPoints(t, username, "pool_0", 5)
|
||||
|
||||
body, _ := json.Marshal(map[string]string{"pool_key": "pool_0"})
|
||||
c, rec := claimRewardContext(username, body)
|
||||
handlers.ClaimMyReward(c)
|
||||
|
||||
if rec.Code != http.StatusConflict {
|
||||
t.Fatalf("status HTTP: got=%d want=%d body=%s", rec.Code, http.StatusConflict, rec.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
// Si un item récompense configuré par l'admin pointe vers un produit
|
||||
// supprimé/inexistant, la réclamation entière doit échouer — la récompense
|
||||
// ne doit pas être consommée sans qu'aucun produit ne soit livré au client
|
||||
// (ClaimPoolReward + AddRewardsToBasket sont maintenant dans la même
|
||||
// transaction via ClaimPoolRewardAndAddToBasket).
|
||||
func TestClaimMyReward_HTTPFlow_FailsAtomicallyWhenProductMissing(t *testing.T) {
|
||||
cleanupStockTestData(t)
|
||||
username := newTestClient(t, "reward_http_missing_product")
|
||||
|
||||
configureRewardSettings(t, &models.PointsReward{
|
||||
Threshold: 20,
|
||||
Type: "free_product",
|
||||
RewardItems: []models.RewardItem{{ProductID: 999999999, Quantity: 1, Price: 12}}, // produit inexistant
|
||||
})
|
||||
setClientPoolPoints(t, username, "pool_0", 20)
|
||||
|
||||
body, _ := json.Marshal(map[string]string{"pool_key": "pool_0"})
|
||||
c, rec := claimRewardContext(username, body)
|
||||
handlers.ClaimMyReward(c)
|
||||
|
||||
if rec.Code == http.StatusOK {
|
||||
t.Fatalf("la réclamation ne doit pas réussir avec un produit récompense invalide, body=%s", rec.Body.String())
|
||||
}
|
||||
|
||||
_, redeemed, err := testDB.GetClientPointsAndRewards(username)
|
||||
if err != nil {
|
||||
t.Fatalf("GetClientPointsAndRewards: %v", err)
|
||||
}
|
||||
if redeemed["pool_0"] != 0 {
|
||||
t.Errorf("la récompense ne doit PAS être consommée si le produit est introuvable: got redeemed=%d want=0", redeemed["pool_0"])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user