Add initial project structure and implement basic functionality

- Created MSTestSettings.cs to enable parallel test execution.
- Added StyleTests.cs and TitreTests.cs for unit testing of Style and Titre entities.
- Implemented Webzine.Entity.Tests project with necessary configurations.
- Created SeedDataLocal.cs and SeedDataSpotify.cs for local and Spotify data seeding.
- Established repository contracts for Artiste, Commentaire, Style, and Titre.
- Developed DbEntityRepository and LocalEntityRepository classes.
- Set up Webzine.WebApplication with controllers, logging, and Docker support.
- Configured NLog for logging and added necessary appsettings for development.
- Created initial views and layout for the web application.
- Added Dockerfile and docker-compose configuration for containerization.
This commit is contained in:
mirage
2026-03-03 16:22:37 +01:00
commit 86a87f75ce
44 changed files with 1185 additions and 0 deletions

View File

@@ -0,0 +1,55 @@
namespace Webzine.Entity.Tests
{
using Microsoft.VisualStudio.TestTools.UnitTesting;
/// <summary>
/// Tests de l'entité <see cref="Artiste"/>.
/// Vérifie que les contraintes imposées (nom du champ, longueur max du champ,
/// champ obligatoire, libellé du champ, clé primaire...) sont bien respectées.
/// </summary>
[TestClass]
public class ArtisteTests
{
[TestMethod]
public void ArtisteHasIdArtiste()
{
Common.HasProperty(typeof(Artiste), nameof(Artiste.IdArtiste));
}
[TestMethod]
public void ArtisteHasNom()
{
Common.HasProperty(typeof(Artiste), nameof(Artiste.Nom));
}
[TestMethod]
public void ArtisteHasNomTailleMin1()
{
Common.AttributLongueurMin(typeof(Artiste), nameof(Artiste.Nom), 2);
}
[TestMethod]
public void ArtisteHasNomTailleMax50()
{
Common.AttributLongueurMax(typeof(Artiste), nameof(Artiste.Nom), 50);
}
[TestMethod]
public void ArtisteHasNomDisplayValid()
{
Common.AttributDisplay(typeof(Artiste), nameof(Artiste.Nom), "Nom de l'artiste");
}
[TestMethod]
public void ArtisteHasBiographie()
{
Common.HasProperty(typeof(Artiste), nameof(Artiste.Biographie));
}
[TestMethod]
public void ArtisteHasTitres()
{
Common.HasProperty(typeof(Artiste), nameof(Artiste.Titres));
}
}
}

View File

@@ -0,0 +1,109 @@
namespace Webzine.Entity.Tests
{
using Microsoft.VisualStudio.TestTools.UnitTesting;
/// <summary>
/// Tests de l'entité <see cref="Commentaire"/>.
/// Vérifie que les contraintes imposées (nom du champ, longueur max du champ,
/// champ obligatoire, libellé du champ, clé primaire...) sont bien respectées.
/// </summary>
[TestClass]
public class CommentaireTests
{
[TestMethod]
public void CommentaireHasIdCommentaire()
{
Common.HasProperty(typeof(Commentaire), nameof(Commentaire.IdCommentaire));
}
[TestMethod]
public void CommentaireHasContenu()
{
Common.HasProperty(typeof(Commentaire), nameof(Commentaire.Contenu));
}
[TestMethod]
public void CommentaireHasContenuDisplayValid()
{
Common.AttributDisplay(typeof(Commentaire), nameof(Commentaire.Contenu), "Commentaire");
}
[TestMethod]
public void CommentaireHasContenuRequis()
{
Common.AttributRequis(typeof(Commentaire), nameof(Commentaire.Contenu));
}
[TestMethod]
public void CommentaireHasContenuTailleMin10()
{
Common.AttributLongueurMin(typeof(Commentaire), nameof(Commentaire.Contenu), 10);
}
[TestMethod]
public void CommentaireHasContenuTailleMax1000()
{
Common.AttributLongueurMax(typeof(Commentaire), nameof(Commentaire.Contenu), 1000);
}
[TestMethod]
public void CommentaireHasAuteur()
{
Common.HasProperty(typeof(Commentaire), nameof(Commentaire.Auteur));
}
[TestMethod]
public void CommentaireHasAuteurDisplayValid()
{
Common.AttributDisplay(typeof(Commentaire), nameof(Commentaire.Auteur), "Nom");
}
[TestMethod]
public void CommentaireHasAuteurRequis()
{
Common.AttributRequis(typeof(Commentaire), nameof(Commentaire.Auteur));
}
[TestMethod]
public void CommentaireHasAuteurTailleMin2()
{
Common.AttributLongueurMin(typeof(Commentaire), nameof(Commentaire.Auteur), 2);
}
[TestMethod]
public void CommentaireHasAuteurTailleMax30()
{
Common.AttributLongueurMax(typeof(Commentaire), nameof(Commentaire.Auteur), 30);
}
[TestMethod]
public void CommentaireHasDateCreation()
{
Common.HasProperty(typeof(Commentaire), nameof(Commentaire.DateCreation));
}
[TestMethod]
public void CommentaireHasDateCreationRequis()
{
Common.AttributRequis(typeof(Commentaire), nameof(Commentaire.DateCreation));
}
[TestMethod]
public void CommentaireHasDateCreationDisplayValid()
{
Common.AttributDisplay(typeof(Commentaire), nameof(Commentaire.DateCreation), "Date de création");
}
[TestMethod]
public void CommentaireHasIdTitre()
{
Common.HasProperty(typeof(Commentaire), nameof(Commentaire.IdTitre));
}
[TestMethod]
public void CommentaireHasTitre()
{
Common.HasProperty(typeof(Commentaire), nameof(Commentaire.Titre));
}
}
}

View File

@@ -0,0 +1,90 @@
namespace Webzine.Entity.Tests
{
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
/// <summary>
/// Méthodes utilitaires permettant de tester les entités.
/// </summary>
public static class Common
{
/// <summary>
/// Vérifie que l'entité possède bien la propriété passée en paramètre.
/// </summary>
/// <param name="typeObjet">type de l'entité</param>
/// <param name="nomPropriete">nom de la propriété de l'entité</param>
public static void HasProperty(Type typeObjet, string nomPropriete)
{
var property = typeObjet.GetProperty(nomPropriete);
Assert.IsNotNull(property, "La classe " + typeObjet.Name + " doit avoir une propriété '" + nomPropriete + "'.");
}
/// <summary>
/// Vérifie que l'attribut de l'entité a l'annotation [Display(Name = "xxx")] avec la valeur attendue.
/// </summary>
/// <param name="typeObjet">type de l'entité</param>
/// <param name="nomPropriete">nom de la propriété de l'entité</param>
/// <param name="chaineAttendue">valeur attendue pour l'affichage de cette propriété</param>
public static void AttributDisplay(Type typeObjet, string nomPropriete, string chaineAttendue)
{
var property = typeObjet.GetProperty(nomPropriete);
var annotation = (DisplayAttribute)property.GetCustomAttributes(typeof(DisplayAttribute), false).FirstOrDefault();
Assert.IsNotNull(annotation, "La propriété '" + nomPropriete + "' n'a pas de libellé approprié. Il manque l'annotation Display.");
Assert.AreEqual(chaineAttendue, annotation.Name);
}
/// <summary>
/// Vérifie que l'attribut de l'entité a l'annotation [MinLength(xx)] avec la longueur attendue.
/// </summary>
/// <param name="typeObjet">type de l'entité</param>
/// <param name="nomPropriete">nom de la propriété de l'entité</param>
/// <param name="max">longueur maximum</param>
public static void AttributLongueurMax(Type typeObjet, string nomPropriete, int max)
{
var property = typeObjet.GetProperty(nomPropriete);
var annotation = (MaxLengthAttribute)property.GetCustomAttributes(typeof(MaxLengthAttribute), false).FirstOrDefault();
Assert.IsNotNull(annotation, "La propriété '" + nomPropriete + "' n'a pas de longueur maximum. Il manque l'annotation MaxLength.");
Assert.AreEqual(max, annotation.Length, "La propriété '" + nomPropriete + "' ne doit pas pouvoir dépasser " + max + " caractères.");
}
/// <summary>
/// Vérifie que l'attribut de l'entité a l'annotation [MinLength(xx)] avec la longueur attendue.
/// </summary>
/// <param name="typeObjet">type de l'entité</param>
/// <param name="nomPropriete">nom de la propriété de l'entité</param>
/// <param name="min">longueur minimum</param>
public static void AttributLongueurMin(Type typeObjet, string nomPropriete, int min)
{
var property = typeObjet.GetProperty(nomPropriete);
var annotation = (MinLengthAttribute)property.GetCustomAttributes(typeof(MinLengthAttribute), false).FirstOrDefault();
Assert.IsNotNull(annotation, "La propriété '" + nomPropriete + "' n'a pas de longueur minimum. Il manque l'annotation MinLength.");
Assert.AreEqual(min, annotation.Length, "La propriété '" + nomPropriete + "' ne avoir au moins " + min + " caractères.");
}
/// <summary>
/// Vérifie que l'attribut de l'entité a l'annotation [Required].
/// </summary>
/// <param name="typeObjet">type de l'entité</param>
/// <param name="nomPropriete">nom de la propriété de l'entité</param>
public static void AttributRequis(Type typeObjet, string nomPropriete)
{
var property = typeObjet.GetProperty(nomPropriete);
var annotation = (RequiredAttribute)property.GetCustomAttributes(typeof(RequiredAttribute), false).FirstOrDefault();
Assert.IsNotNull(annotation, "La propriété '" + nomPropriete + "' n'est pas obligatoire. Il manque l'annotation Required.");
}
/// <summary>
/// Vérifie que l'attribut de l'entité n'a pas l'annotation [Url].
/// </summary>
/// <param name="typeObjet">type de l'entité</param>
/// <param name="nomPropriete">nom de la propriété de l'entité</param>
public static void AttributHasNotUrlValidation(Type typeObjet, string nomPropriete)
{
var property = typeObjet.GetProperty(nomPropriete);
var annotation = (UrlAttribute)property.GetCustomAttributes(typeof(UrlAttribute), false).FirstOrDefault();
Assert.IsNull(annotation, "La propriété '" + nomPropriete + "' ne doit pas être une URL obligatoirement. Retirez l'annotation Url.");
}
}
}

View File

@@ -0,0 +1 @@
[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)]

View File

@@ -0,0 +1,49 @@
namespace Webzine.Entity.Tests
{
using Microsoft.VisualStudio.TestTools.UnitTesting;
/// <summary>
/// Tests de l'entité <see cref="Style"/>.
/// Vérifie que les contraintes imposées (nom du champ, longueur max du champ,
/// champ obligatoire, libellé du champ, clé primaire...) sont bien respectées.
/// </summary>
[TestClass]
public class StyleTests
{
[TestMethod]
public void StyleHasIdStyle()
{
Common.HasProperty(typeof(Style), nameof(Style.IdStyle));
}
[TestMethod]
public void StyleHasLibelle()
{
Common.HasProperty(typeof(Style), nameof(Style.Libelle));
}
[TestMethod]
public void StyleHasLibelleDisplayValid()
{
Common.AttributDisplay(typeof(Style), nameof(Style.Libelle), "Libellé");
}
[TestMethod]
public void StyleHasLibelleRequis()
{
Common.AttributRequis(typeof(Style), nameof(Style.Libelle));
}
[TestMethod]
public void StyleHasLibelleTailleMin2()
{
Common.AttributLongueurMin(typeof(Style), nameof(Style.Libelle), 2);
}
[TestMethod]
public void StyleHasLibelleTailleMax50()
{
Common.AttributLongueurMax(typeof(Style), nameof(Style.Libelle), 50);
}
}
}

View File

@@ -0,0 +1,241 @@
namespace Webzine.Entity.Tests
{
using Microsoft.VisualStudio.TestTools.UnitTesting;
/// <summary>
/// Tests de l'entité <see cref="Titre"/>.
/// Vérifie que les contraintes imposées (nom du champ, longueur max du champ,
/// champ obligatoire, libellé du champ, clé primaire...) sont bien respectées.
/// </summary>
[TestClass]
public class TitreTests
{
[TestMethod]
public void TitreHasIdTitre()
{
Common.HasProperty(typeof(Titre), nameof(Titre.IdTitre));
}
[TestMethod]
public void TitreHasIdArtiste()
{
Common.HasProperty(typeof(Titre), nameof(Titre.IdArtiste));
}
[TestMethod]
public void TitreHasArtiste()
{
Common.HasProperty(typeof(Titre), nameof(Titre.Artiste));
}
[TestMethod]
public void TitreHasLibelle()
{
Common.HasProperty(typeof(Titre), nameof(Titre.Libelle));
}
[TestMethod]
public void TitreHasLibelleDisplayValid()
{
Common.AttributDisplay(typeof(Titre), nameof(Titre.Libelle), "Titre");
}
[TestMethod]
public void TitreHasLibelleRequis()
{
Common.AttributRequis(typeof(Titre), nameof(Titre.Libelle));
}
[TestMethod]
public void TitreHasLibelleTailleMin1()
{
Common.AttributLongueurMin(typeof(Titre), nameof(Titre.Libelle), 1);
}
[TestMethod]
public void TitreHasLibelleTailleMax200()
{
Common.AttributLongueurMax(typeof(Titre), nameof(Titre.Libelle), 200);
}
[TestMethod]
public void TitreHasChronique()
{
Common.HasProperty(typeof(Titre), nameof(Titre.Chronique));
}
[TestMethod]
public void TitreHasChroniqueRequis()
{
Common.AttributRequis(typeof(Titre), nameof(Titre.Chronique));
}
[TestMethod]
public void TitreHasChroniqueTailleMin10()
{
Common.AttributLongueurMin(typeof(Titre), nameof(Titre.Chronique), 10);
}
[TestMethod]
public void TitreHasChroniqueTailleMax4000()
{
Common.AttributLongueurMax(typeof(Titre), nameof(Titre.Chronique), 4000);
}
[TestMethod]
public void TitreHasDateCreation()
{
Common.HasProperty(typeof(Titre), nameof(Titre.DateCreation));
}
[TestMethod]
public void TitreHasDateCreationRequis()
{
Common.AttributRequis(typeof(Titre), nameof(Titre.DateCreation));
}
[TestMethod]
public void TitreHasDateCreationDisplayValid()
{
Common.AttributDisplay(typeof(Titre), nameof(Titre.DateCreation), "Date de création");
}
[TestMethod]
public void TitreHasDuree()
{
Common.HasProperty(typeof(Titre), nameof(Titre.Duree));
}
[TestMethod]
public void TitreHasDureeDisplayValid()
{
Common.AttributDisplay(typeof(Titre), nameof(Titre.Duree), "Durée en secondes");
}
[TestMethod]
public void TitreHasDateSortie()
{
Common.HasProperty(typeof(Titre), nameof(Titre.DateSortie));
}
[TestMethod]
public void TitreHasDateSortieRequis()
{
Common.AttributRequis(typeof(Titre), nameof(Titre.DateSortie));
}
[TestMethod]
public void TitreHasDateSortieDisplayValid()
{
Common.AttributDisplay(typeof(Titre), nameof(Titre.DateSortie), "Date de sortie");
}
[TestMethod]
public void TitreHasUrlJaquette()
{
Common.HasProperty(typeof(Titre), nameof(Titre.UrlJaquette));
}
[TestMethod]
public void TitreHasUrlJaquetteRequis()
{
Common.AttributRequis(typeof(Titre), nameof(Titre.UrlJaquette));
}
[TestMethod]
public void TitreHasUrlJaquetteDisplayValid()
{
Common.AttributDisplay(typeof(Titre), nameof(Titre.UrlJaquette), "Jaquette de l'album");
}
[TestMethod]
public void TitreHasUrlJaquetteTailleMax250()
{
Common.AttributLongueurMax(typeof(Titre), nameof(Titre.UrlJaquette), 250);
}
[TestMethod]
public void TitreHasUrlEcoute()
{
Common.HasProperty(typeof(Titre), nameof(Titre.UrlEcoute));
}
[TestMethod]
public void TitreHasUrlEcouteDisplayValid()
{
Common.AttributDisplay(typeof(Titre), nameof(Titre.UrlEcoute), "URL d'écoute");
}
[TestMethod]
public void TitreHasUrlEcouteTailleMin13()
{
Common.AttributLongueurMin(typeof(Titre), nameof(Titre.UrlEcoute), 13);
}
[TestMethod]
public void TitreHasUrlEcouteTailleMax250()
{
Common.AttributLongueurMax(typeof(Titre), nameof(Titre.UrlEcoute), 250);
}
[TestMethod]
public void TitreHasNbLectures()
{
Common.HasProperty(typeof(Titre), nameof(Titre.NbLectures));
}
[TestMethod]
public void TitreHasNbLecturesDisplayValid()
{
Common.AttributDisplay(typeof(Titre), nameof(Titre.NbLectures), "Nombre de lectures");
}
[TestMethod]
public void TitreHasNbLecturesRequis()
{
Common.AttributRequis(typeof(Titre), nameof(Titre.NbLectures));
}
[TestMethod]
public void TitreHasNbLikes()
{
Common.HasProperty(typeof(Titre), nameof(Titre.NbLikes));
}
[TestMethod]
public void TitreHasNbLikesDisplayValid()
{
Common.AttributDisplay(typeof(Titre), nameof(Titre.NbLikes), "Nombre de likes");
}
[TestMethod]
public void TitreHasNbLikesRequis()
{
Common.AttributRequis(typeof(Titre), nameof(Titre.NbLikes));
}
[TestMethod]
public void TitreHasAlbum()
{
Common.HasProperty(typeof(Titre), nameof(Titre.Album));
}
[TestMethod]
public void TitreHasAlbumRequis()
{
Common.AttributRequis(typeof(Titre), nameof(Titre.Album));
}
[TestMethod]
public void TitreHasCommentaires()
{
Common.HasProperty(typeof(Titre), nameof(Titre.Commentaires));
}
[TestMethod]
public void TitreUrlJaquetteeIsNotMandatory()
{
Common.AttributHasNotUrlValidation(typeof(Titre), nameof(Titre.UrlJaquette));
}
}
}

View File

@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MSTest" Version="4.0.1"/>
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting"/>
</ItemGroup>
<ItemGroup>
<Content Include="..\Webzine.Documentation\StyleCop\stylecop.json">
<Link>stylecop.json</Link>
</Content>
</ItemGroup>
</Project>