manager-service/ManagerService.Tests/Controllers/OnboardingControllerTests.cs
Thomas Fransolet 2dc0cfceeb Lot F : portail de facturation Stripe et coût réel d'une question
Customer Portal : StripeService.CreateBillingPortalSessionAsync +
POST /api/onboarding/billing-portal, calqué sur checkout-session.
Une garde absente de l'énoncé : StripeCustomerId peut être nul, car
CreateCustomerAsync n'est appelée que par l'inscription self-service.
Les 4 instances venues de Mongo n'ont jamais vu Stripe — sans la garde,
elles recevaient une erreur d'API Stripe illisible côté front. 409,
distinct du 404 d'instance inconnue.

Ratio jetons -> questions : InstanceQuotaDTO.aiTokensPerQuestion, mesuré
sur les VisitorQuestion.TokensUsed de l'instance, seuil de 20 questions
avant de faire foi — une seule réponse citant un long article doublerait
la moyenne. En dessous, repli sur l'hypothèse de la grille tarifaire
(10 000, pas 1 000 : le /1000 de manager-app était faux d'un facteur 10).

Trois autres points du lot F étaient déjà faits et n'attendaient qu'une
vérification : rate limiting, endpoint ApplicationInstance, quotas seed.

6 tests. dotnet test : 197 passés, 14 sautés, 0 échec.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 10:10:50 +02:00

78 lines
2.7 KiB
C#

using ManagerService.Controllers;
using ManagerService.Data;
using ManagerService.Helpers;
using ManagerService.Services;
using ManagerService.Tests.Infrastructure;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Moq;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Xunit;
namespace ManagerService.Tests.Controllers
{
/// <summary>
/// Les deux gardes du portail de facturation. L'appel à Stripe lui-même n'est pas couvert :
/// il part sur le réseau, et ces tests s'arrêtent avant — c'est justement ce qui compte,
/// un client sans compte Stripe ne doit jamais atteindre l'API.
/// </summary>
public class OnboardingControllerTests
{
private OnboardingController BuildController(MyInfoMateDbContext db, string callerInstanceId = "i1")
{
var configuration = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string>
{
["AppUrls:ManagerApp"] = "https://manager.myinfomate.be"
})
.Build();
var stripeService = new StripeService(Options.Create(new StripeSettings
{
SecretKey = "sk_test_dummy",
WebhookSecret = "whsec_dummy",
EssentielPriceId = "price_dummy"
}));
var controller = new OnboardingController(
db,
new ProfileLogic(NullLogger<ProfileLogic>.Instance),
new Mock<IEmailService>().Object,
configuration,
NullLogger<OnboardingController>.Instance,
new Mock<IHttpClientFactory>().Object,
stripeService);
FakeUser.SetUser(controller, FakeUser.Create(Permissions.SuperAdmin, callerInstanceId));
return controller;
}
[Fact]
public async Task CreateBillingPortalSession_UnknownInstance_Returns404()
{
using var db = DbContextFactory.Create();
var result = await BuildController(db, "unknown").CreateBillingPortalSession();
Assert.IsType<NotFoundObjectResult>(result);
}
[Fact]
public async Task CreateBillingPortalSession_NoStripeCustomer_Returns409()
{
using var db = DbContextFactory.Create();
db.Instances.Add(new Instance { Id = "i1", Name = "Musée", DateCreation = DateTime.UtcNow });
db.SaveChanges();
var result = await BuildController(db).CreateBillingPortalSession();
Assert.IsType<ConflictObjectResult>(result);
}
}
}