Premiere commit - Omika
This commit is contained in:
@@ -0,0 +1,223 @@
|
||||
using Omika.Diagnostics.Referential;
|
||||
using Omika.Diagnostics.Security;
|
||||
using Omika.Diagnostics.Tests.Fakes;
|
||||
using Omika.Diagnostics.Web.Pages;
|
||||
|
||||
namespace Omika.Diagnostics.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// T24-A Partie B — PageModels du référentiel (/Referentiel + /Referentiel/Admin) et
|
||||
/// libellés des dropdowns Diagnostics. Les handlers n'accèdent pas à HttpContext (aucun
|
||||
/// harnais Web requis) ; l'autorisation est portée par la convention Program.cs.
|
||||
/// </summary>
|
||||
public class DiagnosticReferencePageModelTests
|
||||
{
|
||||
private static DiagnosticReferenceRecord Ref(
|
||||
long id,
|
||||
string type,
|
||||
string code,
|
||||
string label,
|
||||
bool enabled = true) => new()
|
||||
{
|
||||
Id = id,
|
||||
ReferenceType = type,
|
||||
Code = code,
|
||||
Label = label,
|
||||
Enabled = enabled,
|
||||
CreatedAtUtc = DateTimeOffset.UtcNow,
|
||||
UpdatedAtUtc = DateTimeOffset.UtcNow
|
||||
};
|
||||
|
||||
private static ReferentielAdminModel NewAdminModel(
|
||||
FakeDiagnosticReferenceRepository references) =>
|
||||
new(new FakeDiagnosticEventRepository(), references);
|
||||
|
||||
// ------------------------------------------- création admin ----
|
||||
[Fact]
|
||||
public async Task AdminCreate_StoresNormalizedEntry()
|
||||
{
|
||||
var references = new FakeDiagnosticReferenceRepository();
|
||||
var model = NewAdminModel(references);
|
||||
model.NewType = DiagnosticReferenceTypes.ScreenCode;
|
||||
model.NewCode = " facture_detail ";
|
||||
model.NewLabel = " Détail de facture ";
|
||||
model.NewDescription = "Fiche détaillée d'une facture.";
|
||||
|
||||
await model.OnPostCreateAsync();
|
||||
|
||||
DiagnosticReferenceRecord? created = await references.GetByTypeAndCodeAsync(
|
||||
DiagnosticReferenceTypes.ScreenCode, "FACTURE_DETAIL");
|
||||
Assert.NotNull(created);
|
||||
Assert.Equal("FACTURE_DETAIL", created!.Code);
|
||||
Assert.Equal("Détail de facture", created.Label);
|
||||
Assert.Contains("FACTURE_DETAIL", model.SuccessMessage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AdminCreate_Duplicate_ShowsError()
|
||||
{
|
||||
var references = new FakeDiagnosticReferenceRepository();
|
||||
references.Seed(Ref(1, DiagnosticReferenceTypes.ScreenCode, "AGENDA", "Agenda"));
|
||||
var model = NewAdminModel(references);
|
||||
model.NewType = DiagnosticReferenceTypes.ScreenCode;
|
||||
model.NewCode = "agenda";
|
||||
model.NewLabel = "Agenda bis";
|
||||
|
||||
await model.OnPostCreateAsync();
|
||||
|
||||
Assert.Contains("existe déjà", model.ErrorMessage);
|
||||
Assert.Single(await references.ListAsync());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AdminCreate_EmptyLabel_ShowsError()
|
||||
{
|
||||
var references = new FakeDiagnosticReferenceRepository();
|
||||
var model = NewAdminModel(references);
|
||||
model.NewType = DiagnosticReferenceTypes.ScreenCode;
|
||||
model.NewCode = "X";
|
||||
model.NewLabel = " ";
|
||||
|
||||
await model.OnPostCreateAsync();
|
||||
|
||||
Assert.Contains("libellé", model.ErrorMessage, StringComparison.OrdinalIgnoreCase);
|
||||
Assert.Empty(await references.ListAsync());
|
||||
}
|
||||
|
||||
// ------------------------------------------- modification / archivage ----
|
||||
[Fact]
|
||||
public async Task AdminUpdate_ChangesLabelDescriptionAndState()
|
||||
{
|
||||
var references = new FakeDiagnosticReferenceRepository();
|
||||
references.Seed(Ref(1, DiagnosticReferenceTypes.ScreenCode, "AGENDA", "Agenda"));
|
||||
var model = NewAdminModel(references);
|
||||
|
||||
await model.OnPostUpdateAsync(1, "Nouvel agenda", "Description", enabled: false, sortOrder: 5);
|
||||
|
||||
DiagnosticReferenceRecord? record = await references.GetByIdAsync(1);
|
||||
Assert.Equal("Nouvel agenda", record!.Label);
|
||||
Assert.Equal("Description", record.Description);
|
||||
Assert.False(record.Enabled);
|
||||
Assert.Equal(5, record.SortOrder);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AdminArchive_DisablesWithoutDelete()
|
||||
{
|
||||
var references = new FakeDiagnosticReferenceRepository();
|
||||
references.Seed(Ref(1, DiagnosticReferenceTypes.ScreenCode, "AGENDA", "Agenda"));
|
||||
var model = NewAdminModel(references);
|
||||
|
||||
await model.OnPostArchiveAsync(1);
|
||||
|
||||
Assert.False((await references.GetByIdAsync(1))!.Enabled);
|
||||
Assert.Single(await references.ListAsync()); // jamais de DELETE.
|
||||
Assert.Contains("archivée", model.SuccessMessage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AdminEnable_ReactivatesArchived()
|
||||
{
|
||||
var references = new FakeDiagnosticReferenceRepository();
|
||||
references.Seed(Ref(1, DiagnosticReferenceTypes.ScreenCode, "AGENDA", "Agenda", enabled: false));
|
||||
var model = NewAdminModel(references);
|
||||
|
||||
await model.OnPostEnableAsync(1);
|
||||
|
||||
Assert.True((await references.GetByIdAsync(1))!.Enabled);
|
||||
}
|
||||
|
||||
// ------------------------------------------- ajout rapide non référencé ----
|
||||
[Fact]
|
||||
public async Task AdminAddUnreferenced_CreatesReference()
|
||||
{
|
||||
var references = new FakeDiagnosticReferenceRepository();
|
||||
DateTimeOffset last = DateTimeOffset.UtcNow.AddDays(-1);
|
||||
references.SeedObserved(DiagnosticReferenceTypes.ScreenCode, "FACTURE_DETAIL", 128, last);
|
||||
var model = NewAdminModel(references);
|
||||
|
||||
await model.OnGetAsync();
|
||||
DiagnosticUnreferencedCode unreferenced = Assert.Single(model.Unreferenced);
|
||||
Assert.Equal("FACTURE_DETAIL", unreferenced.Code);
|
||||
|
||||
await model.OnPostAddUnreferencedAsync(
|
||||
DiagnosticReferenceTypes.ScreenCode, "FACTURE_DETAIL", "Détail de facture", "Fiche détaillée.");
|
||||
|
||||
Assert.NotNull(await references.GetByTypeAndCodeAsync(DiagnosticReferenceTypes.ScreenCode, "FACTURE_DETAIL"));
|
||||
// Après ajout, le code n'est plus « non référencé ».
|
||||
await model.OnGetAsync();
|
||||
Assert.Empty(model.Unreferenced);
|
||||
}
|
||||
|
||||
// ------------------------------------------- consultation /Referentiel ----
|
||||
[Fact]
|
||||
public async Task ReferentielModel_LoadsScreenAndFeatureReferences()
|
||||
{
|
||||
var references = new FakeDiagnosticReferenceRepository();
|
||||
references.Seed(
|
||||
Ref(1, DiagnosticReferenceTypes.ScreenCode, "AGENDA", "Agenda"),
|
||||
Ref(2, DiagnosticReferenceTypes.FeatureCode, "EMAIL_SEND", "Envoi de courriel"));
|
||||
|
||||
var model = new ReferentielModel(new FakeDiagnosticEventRepository(), references);
|
||||
await model.OnGetAsync();
|
||||
|
||||
Assert.True(model.PostgreSqlAvailable);
|
||||
DiagnosticReferenceRecord screen = Assert.Single(model.ScreenReferences);
|
||||
Assert.Equal("AGENDA", screen.Code);
|
||||
DiagnosticReferenceRecord feature = Assert.Single(model.FeatureReferences);
|
||||
Assert.Equal("EMAIL_SEND", feature.Code);
|
||||
}
|
||||
|
||||
// ------------------------------------------- dropdowns Diagnostics (§33/§37) ----
|
||||
[Fact]
|
||||
public async Task DiagnosticsDropdown_ReferencedCode_UsesDbLabel()
|
||||
{
|
||||
var references = new FakeDiagnosticReferenceRepository();
|
||||
references.Seed(Ref(1, DiagnosticReferenceTypes.ScreenCode, "FACTURE_DETAIL", "Détail de facture"));
|
||||
var model = new DiagnosticsModel(new FakeDiagnosticEventRepository(), references);
|
||||
|
||||
await model.OnGetAsync();
|
||||
|
||||
// Référence DB → « Libellé (CODE) ».
|
||||
Assert.Equal("Détail de facture (FACTURE_DETAIL)", model.ScreenCodeOptionLabel("FACTURE_DETAIL"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DiagnosticsDropdown_UnreferencedCode_ShowsNotReferenced()
|
||||
{
|
||||
var references = new FakeDiagnosticReferenceRepository();
|
||||
var model = new DiagnosticsModel(new FakeDiagnosticEventRepository(), references);
|
||||
|
||||
await model.OnGetAsync();
|
||||
|
||||
// Aucune référence → « CODE (non référencé) ». La valeur envoyée reste le CODE.
|
||||
Assert.Equal("FACTURE_DETAIL (non référencé)", model.ScreenCodeOptionLabel("FACTURE_DETAIL"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DiagnosticsDropdown_CatalogCodeWithoutDbEntry_FallsBackToCatalog()
|
||||
{
|
||||
var references = new FakeDiagnosticReferenceRepository();
|
||||
var model = new DiagnosticsModel(new FakeDiagnosticEventRepository(), references);
|
||||
|
||||
await model.OnGetAsync();
|
||||
|
||||
// Code du catalogue applicatif mais sans entrée DB → repli catalogue (résilience).
|
||||
Assert.Equal("AGENDA — Agenda", model.ScreenCodeOptionLabel("AGENDA"));
|
||||
}
|
||||
|
||||
// ------------------------------------------- rôles (§40/§46) ----
|
||||
[Fact]
|
||||
public void Roles_SupportCanConsultButNotAdmin()
|
||||
{
|
||||
Assert.True(DiagnosticAuthorization.IsSupport([DiagnosticRoles.Support]));
|
||||
Assert.False(DiagnosticAuthorization.IsAdmin([DiagnosticRoles.Support]));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Roles_AdminCanConsultAndAdmin()
|
||||
{
|
||||
Assert.True(DiagnosticAuthorization.IsSupport([DiagnosticRoles.Admin]));
|
||||
Assert.True(DiagnosticAuthorization.IsAdmin([DiagnosticRoles.Admin]));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user