using Hangfire;
using Manager.Services;
using Manager.Interfaces.Models;
using ManagerService.Controllers;
using ManagerService.Helpers;
using ManagerService.Data;
using ManagerService.DTOs;
using ManagerService.Services;
using ManagerService.Tests.Infrastructure;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
using Xunit;
namespace ManagerService.Tests.Controllers
{
///
/// L'add-on « Contenu immersif » — §6 de DOCS/v2/vr-quest-unity-plan.md.
///
/// ⚠️ Ces tests existent pour une raison précise : la migration pose un
/// defaultValue, ce qui amène EF Core à traiter la colonne comme
/// ValueGeneratedOnAdd — read-only après insert. Le piège est déjà
/// documenté dans MyInfoMateDbContext pour les quotas, et il a coûté une
/// migration corrigée à la main sur Device.AppType. Sans
/// ValueGeneratedNever, l'add-on ne s'activerait jamais — silencieusement.
///
public class InstanceAddonTests
{
private InstanceController BuildController(MyInfoMateDbContext db)
{
var cfg = FakeMongoConfig.Create();
return new InstanceController(
NullLogger.Instance,
new InstanceDatabaseService(cfg),
new UserDatabaseService(cfg),
new ProfileLogic(NullLogger.Instance),
db,
new ApiKeyDatabaseService(db),
new Mock().Object);
}
[Fact]
public void ImmersiveContent_IsOffByDefault()
{
using var db = DbContextFactory.Create();
db.Instances.Add(new Instance { Id = "i1", Name = "Musée", DateCreation = DateTime.UtcNow });
db.SaveChanges();
Assert.False(db.Instances.First().HasImmersiveContent);
}
[Fact]
public void ImmersiveContent_CanBeTurnedOn()
{
using var db = DbContextFactory.Create();
db.Instances.Add(new Instance { Id = "i1", Name = "Musée", DateCreation = DateTime.UtcNow });
db.SaveChanges();
var result = BuildController(db).Updateinstance(
new InstanceDTO { id = "i1", hasImmersiveContent = true });
Assert.IsType(result);
Assert.True(db.Instances.First().HasImmersiveContent);
}
[Fact]
public void ImmersiveContent_AbsentFromPayload_KeepsPreviousValue()
{
using var db = DbContextFactory.Create();
db.Instances.Add(new Instance
{
Id = "i1",
Name = "Musée",
DateCreation = DateTime.UtcNow,
HasImmersiveContent = true
});
db.SaveChanges();
// Un écran qui ne parle pas de l'add-on ne doit pas le désactiver au passage :
// le contrôleur applique `?? valeur actuelle`, comme pour les autres drapeaux.
BuildController(db).Updateinstance(new InstanceDTO { id = "i1", name = "Musée" });
Assert.True(db.Instances.First().HasImmersiveContent);
}
private const long Envelope = 100L * 1024 * 1024 * 1024;
private const long PlanQuota = 10L * 1024 * 1024 * 1024;
private static MyInfoMateDbContext WithPlan(long quota = PlanQuota)
{
var db = DbContextFactory.Create();
db.SubscriptionPlans.Add(new SubscriptionPlan
{
Id = "pro",
Name = "Pro",
StorageQuotaBytes = quota
});
db.Instances.Add(new Instance
{
Id = "i1",
Name = "Musée",
DateCreation = DateTime.UtcNow,
StorageQuotaBytes = quota
});
db.SaveChanges();
return db;
}
[Fact]
public void ActivatingImmersive_RaisesStorageQuota()
{
using var db = WithPlan();
BuildController(db).Updateinstance(
new InstanceDTO { id = "i1", hasImmersiveContent = true });
// Sans ce relèvement, un client Pro activait la 360 et saturait son quota à la
// première vidéo : c'est le point de marge du §6 du plan.
Assert.Equal(PlanQuota + Envelope, db.Instances.First().StorageQuotaBytes);
}
[Fact]
public void DeactivatingImmersive_LowersStorageQuotaBack()
{
using var db = WithPlan();
var controller = BuildController(db);
controller.Updateinstance(new InstanceDTO { id = "i1", hasImmersiveContent = true });
controller.Updateinstance(new InstanceDTO { id = "i1", hasImmersiveContent = false });
Assert.Equal(PlanQuota, db.Instances.First().StorageQuotaBytes);
}
[Fact]
public void ActivatingImmersiveTwice_DoesNotStackEnvelopes()
{
using var db = WithPlan();
var controller = BuildController(db);
controller.Updateinstance(new InstanceDTO { id = "i1", hasImmersiveContent = true });
controller.Updateinstance(new InstanceDTO { id = "i1", hasImmersiveContent = true });
// Un écran qui renvoie tout le formulaire à chaque enregistrement repasse `true`
// sans rien changer : le quota ne doit pas grimper d'une enveloppe par clic.
Assert.Equal(PlanQuota + Envelope, db.Instances.First().StorageQuotaBytes);
}
[Fact]
public void ChangingPlan_KeepsTheImmersiveEnvelope()
{
using var db = WithPlan();
db.SubscriptionPlans.Add(new SubscriptionPlan
{
Id = "premium",
Name = "Premium",
StorageQuotaBytes = 50L * 1024 * 1024 * 1024
});
db.SaveChanges();
var controller = BuildController(db);
controller.Updateinstance(new InstanceDTO { id = "i1", hasImmersiveContent = true });
controller.Updateinstance(new InstanceDTO { id = "i1", subscriptionPlanId = "premium" });
// ApplyPlanQuotas réécrit le quota depuis le plan : sans reposer l'enveloppe
// par-dessus, faire monter un client en gamme lui retirerait son add-on.
Assert.Equal(50L * 1024 * 1024 * 1024 + Envelope,
db.Instances.First().StorageQuotaBytes);
}
}
}