From b546d8946ec929a9de85a71959b7b0ee32e5570b Mon Sep 17 00:00:00 2001 From: Francesco Lorenzo D'Amico Date: Mon, 2 Mar 2026 16:35:32 +0100 Subject: [PATCH] Finished Project --- Francesco.Recipes.World.sln | 6 + .../Francesco.Recipes.World.csproj | 1 - .../Category/CategoryRepository.cs | 43 ++++- .../Category/ICategoryRepository.cs | 3 + .../Repositories/Favorit/FavoritRepository.cs | 1 + .../Repositories/Recipe/IRecipeRepository.cs | 1 + .../Repositories/Recipe/RecipeRepository.cs | 62 +++---- .../Category/CategoryRecipesViewModel.cs | 4 +- .../Views/Home/Index.cshtml | 58 +++---- .../Views/Home/_SearchResultsPartial.cshtml | 2 +- .../Views/Recipe/CategoryRecipes.cshtml | 98 ----------- .../Views/Recipe/Create.cshtml | 2 +- .../Views/Recipe/Details.cshtml | 55 +++--- .../Views/Shared/_FavoriteButton.cshtml | 2 +- .../Views/Shared/_GetInstructions.cshtml | 1 + .../Views/Shared/_IngredientsPartial.cshtml | 4 - .../Views/Shared/_Layout.cshtml | 11 +- Francesco.Recipes.World/wwwroot/js/site.js | 1 + .../FrancescosRecipeWorld Mock.csproj | 26 +++ FrancescosRecipeWorld Mock/MSTestSettings.cs | 1 + FrancescosRecipeWorld Mock/Test1.cs | 156 ++++++++++++++++++ 21 files changed, 333 insertions(+), 205 deletions(-) delete mode 100644 Francesco.Recipes.World/Views/Recipe/CategoryRecipes.cshtml create mode 100644 FrancescosRecipeWorld Mock/FrancescosRecipeWorld Mock.csproj create mode 100644 FrancescosRecipeWorld Mock/MSTestSettings.cs create mode 100644 FrancescosRecipeWorld Mock/Test1.cs diff --git a/Francesco.Recipes.World.sln b/Francesco.Recipes.World.sln index 793bcdf..998bb15 100644 --- a/Francesco.Recipes.World.sln +++ b/Francesco.Recipes.World.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 17.11.35222.181 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Francesco.Recipes.World", "Francesco.Recipes.World\Francesco.Recipes.World.csproj", "{4D1BBCF4-8E06-4584-A383-B14BEC558408}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrancescosRecipeWorld Mock", "FrancescosRecipeWorld Mock\FrancescosRecipeWorld Mock.csproj", "{04FD3555-3497-4F6B-B2C3-1B22EEFA676B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +17,10 @@ Global {4D1BBCF4-8E06-4584-A383-B14BEC558408}.Debug|Any CPU.Build.0 = Debug|Any CPU {4D1BBCF4-8E06-4584-A383-B14BEC558408}.Release|Any CPU.ActiveCfg = Release|Any CPU {4D1BBCF4-8E06-4584-A383-B14BEC558408}.Release|Any CPU.Build.0 = Release|Any CPU + {04FD3555-3497-4F6B-B2C3-1B22EEFA676B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {04FD3555-3497-4F6B-B2C3-1B22EEFA676B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {04FD3555-3497-4F6B-B2C3-1B22EEFA676B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {04FD3555-3497-4F6B-B2C3-1B22EEFA676B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Francesco.Recipes.World/Francesco.Recipes.World.csproj b/Francesco.Recipes.World/Francesco.Recipes.World.csproj index e28537c..28aea44 100644 --- a/Francesco.Recipes.World/Francesco.Recipes.World.csproj +++ b/Francesco.Recipes.World/Francesco.Recipes.World.csproj @@ -50,5 +50,4 @@ - diff --git a/Francesco.Recipes.World/Repositories/Category/CategoryRepository.cs b/Francesco.Recipes.World/Repositories/Category/CategoryRepository.cs index e3e6d11..627df0d 100644 --- a/Francesco.Recipes.World/Repositories/Category/CategoryRepository.cs +++ b/Francesco.Recipes.World/Repositories/Category/CategoryRepository.cs @@ -4,8 +4,10 @@ using System.Collections.Generic; using System.Threading.Tasks; using Francesco.Recipes.World.Data; + using Francesco.Recipes.World.Models; using Francesco.Recipes.World.Models.BackendModels.Category; using Francesco.Recipes.World.Models.BackendModels.Recipe; + using Francesco.Recipes.World.Views.Category; using Microsoft.EntityFrameworkCore; public class CategoryRepository : ICategoryRepository @@ -44,11 +46,44 @@ public async Task> GetAllCategoriesWithRecipesAsync() { - return await _context.Categories - .Include(c => c.Recipes) - .ThenInclude(r => r.MediaFiles) - .AsSplitQuery() + var categories = await _context.Categories + .Include(c => c.Recipes.OrderByDescending(r => r.CreatedAt).Take(3)) + .ThenInclude(r => r.MediaFiles.Take(2)) + .AsSplitQuery() + .ToListAsync(); + + return categories; + } + + public async Task> GetAllCategoriesWithRecipesViewModelAsync() + { + var categories = await _context.Categories + .Select(c => new CategoryRecipesViewModel + { + Category = c, + Recipes = c.Recipes + .OrderByDescending(r => r.CreatedAt) + .Take(3) + .Select(r => new RecipeCardViewModel + { + Id = r.Id, + Name = r.Name, + CookingTime = r.CookingTime, + IsFavorite = r.IsFavorite, + ImageData = r.MediaFiles + .OrderBy(m => m.Id) + .Select(m => m.Data) + .FirstOrDefault(), + MimeType = r.MediaFiles + .OrderBy(m => m.Id) + .Select(m => m.MimeType) + .FirstOrDefault(), + }), + }) + .AsSplitQuery() .ToListAsync(); + + return categories; } } } diff --git a/Francesco.Recipes.World/Repositories/Category/ICategoryRepository.cs b/Francesco.Recipes.World/Repositories/Category/ICategoryRepository.cs index f449de0..3978132 100644 --- a/Francesco.Recipes.World/Repositories/Category/ICategoryRepository.cs +++ b/Francesco.Recipes.World/Repositories/Category/ICategoryRepository.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Francesco.Recipes.World.Models.BackendModels.Category; using Francesco.Recipes.World.Models.BackendModels.Recipe; + using Francesco.Recipes.World.Views.Category; public interface ICategoryRepository { @@ -15,5 +16,7 @@ Task> GetRecipesByCategoryAsync(Guid categoryId); Task> GetAllCategoriesWithRecipesAsync(); + + Task> GetAllCategoriesWithRecipesViewModelAsync(); } } diff --git a/Francesco.Recipes.World/Repositories/Favorit/FavoritRepository.cs b/Francesco.Recipes.World/Repositories/Favorit/FavoritRepository.cs index 5ca4fa3..9c996c1 100644 --- a/Francesco.Recipes.World/Repositories/Favorit/FavoritRepository.cs +++ b/Francesco.Recipes.World/Repositories/Favorit/FavoritRepository.cs @@ -19,6 +19,7 @@ .Where(r => r.IsFavorite) .Include(r => r.Favorite) .Include(r => r.MediaFiles) + .Take(6) .ToListAsync(); } diff --git a/Francesco.Recipes.World/Repositories/Recipe/IRecipeRepository.cs b/Francesco.Recipes.World/Repositories/Recipe/IRecipeRepository.cs index ebbc8c7..4fccc61 100644 --- a/Francesco.Recipes.World/Repositories/Recipe/IRecipeRepository.cs +++ b/Francesco.Recipes.World/Repositories/Recipe/IRecipeRepository.cs @@ -19,6 +19,7 @@ Task CreateRecipeForCategoryAsync(Category category, string name, string description, Difficulty difficulty, int servings, TimeSpan preparationTime, TimeSpan cookingTime); Task DeleteRecipeAsync(Guid recipeId); + Task> SearchInRecipesAndIngredients(string searchTerm); } } diff --git a/Francesco.Recipes.World/Repositories/Recipe/RecipeRepository.cs b/Francesco.Recipes.World/Repositories/Recipe/RecipeRepository.cs index 3f8c683..2dd0a2b 100644 --- a/Francesco.Recipes.World/Repositories/Recipe/RecipeRepository.cs +++ b/Francesco.Recipes.World/Repositories/Recipe/RecipeRepository.cs @@ -198,35 +198,6 @@ .ToListAsync(); } - private static Expression> SearchViewModelSelector() - { - return r => new SearchViewModel - { - Id = r.Id, - Name = r.Name, - Description = r.Description, - IsFavorite = r.IsFavorite, - ImageData = r.MediaFiles - .Where(m => m.MimeType != null && m.MimeType.StartsWith(ContentType.Image)) - .Select(m => m.Data) - .FirstOrDefault(), - MimeType = r.MediaFiles - .Where(m => m.MimeType != null && m.MimeType.StartsWith(ContentType.Image)) - .Select(m => m.MimeType) - .FirstOrDefault(), - Ingredients = r.RecipeIngredients.Select(ri => ri.Ingredient.Name).ToList(), - TotalTime = r.PreparationTime.Add(r.CookingTime), - }; - } - - private static IQueryable ApplyRecipeSearchFilter(IQueryable query, string normalizedSearchTerm) - { - return query.Where(r => - EF.Functions.Like(r.Name.ToLower(), $"%{normalizedSearchTerm}%") || - (r.Description != null && EF.Functions.Like(r.Description.ToLower(), $"%{normalizedSearchTerm}%")) || - r.RecipeIngredients.Any(ri => EF.Functions.Like(ri.Ingredient.Name.ToLower(), $"%{normalizedSearchTerm}%"))); - } - public async Task DeleteRecipeAsync(Guid recipeId) { var recipe = await GetRecipeByIdAsync(recipeId); @@ -259,9 +230,9 @@ _context.MediaFiles.RemoveRange(recipe.MediaFiles); } - if (recipe.Favorit != null && recipe.Favorit.Id != Guid.Empty) + if (recipe.Favorite != null && recipe.Favorite.Id != Guid.Empty) { - _context.Remove(recipe.Favorit); + _context.Remove(recipe.Favorite); } _context.Recipes.Remove(recipe); @@ -269,5 +240,34 @@ await _context.SaveChangesAsync(); return true; } + + private static Expression> SearchViewModelSelector() + { + return r => new SearchViewModel + { + Id = r.Id, + Name = r.Name, + Description = r.Description, + IsFavorite = r.IsFavorite, + ImageData = r.MediaFiles + .Where(m => m.MimeType != null && m.MimeType.StartsWith(ContentType.Image)) + .Select(m => m.Data) + .FirstOrDefault(), + MimeType = r.MediaFiles + .Where(m => m.MimeType != null && m.MimeType.StartsWith(ContentType.Image)) + .Select(m => m.MimeType) + .FirstOrDefault(), + Ingredients = r.RecipeIngredients.Select(ri => ri.Ingredient.Name).ToList(), + TotalTime = r.PreparationTime.Add(r.CookingTime), + }; + } + + private static IQueryable ApplyRecipeSearchFilter(IQueryable query, string normalizedSearchTerm) + { + return query.Where(r => + EF.Functions.Like(r.Name.ToLower(), $"%{normalizedSearchTerm}%") || + (r.Description != null && EF.Functions.Like(r.Description.ToLower(), $"%{normalizedSearchTerm}%")) || + r.RecipeIngredients.Any(ri => EF.Functions.Like(ri.Ingredient.Name.ToLower(), $"%{normalizedSearchTerm}%"))); + } } } diff --git a/Francesco.Recipes.World/Views/Category/CategoryRecipesViewModel.cs b/Francesco.Recipes.World/Views/Category/CategoryRecipesViewModel.cs index 1559dd8..1b2fe4c 100644 --- a/Francesco.Recipes.World/Views/Category/CategoryRecipesViewModel.cs +++ b/Francesco.Recipes.World/Views/Category/CategoryRecipesViewModel.cs @@ -1,12 +1,12 @@ namespace Francesco.Recipes.World.Views.Category { + using Francesco.Recipes.World.Models; using Francesco.Recipes.World.Models.BackendModels.Category; - using Francesco.Recipes.World.Models.BackendModels.Recipe; public class CategoryRecipesViewModel { public Category Category { get; set; } = new (); - public IEnumerable Recipes { get; set; } = new List(); + public IEnumerable Recipes { get; set; } = new List(); } } diff --git a/Francesco.Recipes.World/Views/Home/Index.cshtml b/Francesco.Recipes.World/Views/Home/Index.cshtml index 7c24500..55d12a5 100644 --- a/Francesco.Recipes.World/Views/Home/Index.cshtml +++ b/Francesco.Recipes.World/Views/Home/Index.cshtml @@ -32,39 +32,41 @@
- @foreach (var recipe in category.Recipes) - { - var mediaFile = recipe.MediaFiles?.FirstOrDefault(); - var imageData = mediaFile?.Data; - var mimeType = mediaFile?.MimeType; -
-
-
- @if (imageData != null && mimeType != null) - { - @recipe.Name - } - else - { - @recipe.Name - } +@foreach (var recipe in category.Recipes) +{ + var imageData = recipe.ImageData; + var mimeType = recipe.MimeType; + +
+
+
+ @if (imageData != null && mimeType != null) + { + @recipe.Name + } + else + { + @recipe.Name + } +
+ +
+
@recipe.Name
+

@recipe.CookingTime

+
+ @await Html.PartialAsync("_FavoriteButton", recipe) +
-
-
@recipe.Name
-

@recipe.CookingTime

-
- @await Html.PartialAsync("_FavoriteButton", recipe) -
+ Details +
+
+
+} - Details -
-
-
- }
diff --git a/Francesco.Recipes.World/Views/Home/_SearchResultsPartial.cshtml b/Francesco.Recipes.World/Views/Home/_SearchResultsPartial.cshtml index c03a7fb..e8e7a13 100644 --- a/Francesco.Recipes.World/Views/Home/_SearchResultsPartial.cshtml +++ b/Francesco.Recipes.World/Views/Home/_SearchResultsPartial.cshtml @@ -64,7 +64,7 @@
- Details + Details
diff --git a/Francesco.Recipes.World/Views/Recipe/CategoryRecipes.cshtml b/Francesco.Recipes.World/Views/Recipe/CategoryRecipes.cshtml deleted file mode 100644 index df06c6e..0000000 --- a/Francesco.Recipes.World/Views/Recipe/CategoryRecipes.cshtml +++ /dev/null @@ -1,98 +0,0 @@ -@model IEnumerable - -@{ - ViewData["Title"] = "Category Recipes"; -} - -

Category Recipes

- -@foreach (var categoryRecipes in Model) -{ -
-

@categoryRecipes.Category.Name

- Rezept erstellen -
- @foreach (var recipe in categoryRecipes.Recipes) - { -
-
- @if (recipe.MediaFiles.Any() && recipe.MediaFiles.First().Data != null) - { - var mediaFile = recipe.MediaFiles.First(); - if (mediaFile.Data != null) - { - @recipe.Name - } - } -
-
-

@recipe.Name

-

@recipe.Description

-

Difficulty: @recipe.Difficulty

-

Servings: @recipe.Servings

-

Preparation Time: @recipe.PreparationTime

-

Cooking Time: @recipe.CookingTime

-
- @if (recipe.IsFavorite) - { -
- - -
- } - else - { -
- - -
- } -
-
-
- } - -
-
-} - - - diff --git a/Francesco.Recipes.World/Views/Recipe/Create.cshtml b/Francesco.Recipes.World/Views/Recipe/Create.cshtml index b10907b..55ff934 100644 --- a/Francesco.Recipes.World/Views/Recipe/Create.cshtml +++ b/Francesco.Recipes.World/Views/Recipe/Create.cshtml @@ -107,7 +107,7 @@
- Abbrechen + Abbrechen
diff --git a/Francesco.Recipes.World/Views/Recipe/Details.cshtml b/Francesco.Recipes.World/Views/Recipe/Details.cshtml index 9f606cb..65a7001 100644 --- a/Francesco.Recipes.World/Views/Recipe/Details.cshtml +++ b/Francesco.Recipes.World/Views/Recipe/Details.cshtml @@ -32,7 +32,7 @@ Instructions = Model.Instructions.ToList() }) -
@Html.AntiForgeryToken() @@ -173,34 +173,37 @@ return Math.round(quantity * 10) / 10; } + window.addSelectedIngredientsToShoppingList = function(recipeIdParam) { + const idToUse = recipeIdParam || recipeId; - window.addSelectedIngredientsToShoppingList = function(recipeIdParam) { - const idToUse = recipeIdParam || recipeId; + var form = document.getElementById('ingredient-form'); + var formData = new FormData(form); + var selectedIngredientIds = formData.getAll('ingredientIds'); - var form = document.getElementById('ingredient-form'); - var formData = new FormData(form); - var selectedIngredientIds = formData.getAll('ingredientIds'); + fetch('/ShoppingList/CreateOrAddIngredients', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'RequestVerificationToken': document.querySelector('input[name="__RequestVerificationToken"]').value + }, + body: JSON.stringify({ recipeId: idToUse, ingredientIds: selectedIngredientIds }) + }) + .then(response => { + if (response.ok) { + return response.json(); + } + throw new Error('Failed to update shopping list'); + }) + .then(result => { + localStorage.setItem('shoppingListId', result.shoppingListId); + alert('Einkaufsliste aktualisiert.'); + }) + .catch(error => { + alert('Fehler beim Aktualisieren der Einkaufsliste.'); + }); + }; + }); - var token = document.querySelector('input[name="__RequestVerificationToken"]').value; - - var response = await fetch('/ShoppingList/CreateOrAddIngredients', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'RequestVerificationToken': token - }, - body: JSON.stringify({ recipeId: '@Model.Id', ingredientIds: selectedIngredientIds }) - }); - - if (response.ok) { - var result = await response.json(); - localStorage.setItem('shoppingListId', result.shoppingListId); - alert('Zutaten wurden zur Einkaufsliste hinzugefügt.'); - } else { - alert('Fehler beim Hinzufügen zur Einkaufsliste.'); - } - } - } diff --git a/Francesco.Recipes.World/Views/Shared/_FavoriteButton.cshtml b/Francesco.Recipes.World/Views/Shared/_FavoriteButton.cshtml index 24104af..e67f795 100644 --- a/Francesco.Recipes.World/Views/Shared/_FavoriteButton.cshtml +++ b/Francesco.Recipes.World/Views/Shared/_FavoriteButton.cshtml @@ -1,4 +1,4 @@ -@model Francesco.Recipes.World.Models.BackendModels.Recipe.Recipe +@model Francesco.Recipes.World.Models.IFavoritable diff --git a/Francesco.Recipes.World/Views/Shared/_IngredientsPartial.cshtml b/Francesco.Recipes.World/Views/Shared/_IngredientsPartial.cshtml index b465acd..5ef25b0 100644 --- a/Francesco.Recipes.World/Views/Shared/_IngredientsPartial.cshtml +++ b/Francesco.Recipes.World/Views/Shared/_IngredientsPartial.cshtml @@ -21,13 +21,9 @@ } - } - - - diff --git a/Francesco.Recipes.World/Views/Shared/_Layout.cshtml b/Francesco.Recipes.World/Views/Shared/_Layout.cshtml index b0646a3..c95c6c8 100644 --- a/Francesco.Recipes.World/Views/Shared/_Layout.cshtml +++ b/Francesco.Recipes.World/Views/Shared/_Layout.cshtml @@ -23,17 +23,12 @@ + - -
diff --git a/Francesco.Recipes.World/wwwroot/js/site.js b/Francesco.Recipes.World/wwwroot/js/site.js index 3639c5e..f9ccb78 100644 --- a/Francesco.Recipes.World/wwwroot/js/site.js +++ b/Francesco.Recipes.World/wwwroot/js/site.js @@ -413,3 +413,4 @@ }; })(window, document); +console.log("Francesco-Objekt initialisiert:", window.Francesco); \ No newline at end of file diff --git a/FrancescosRecipeWorld Mock/FrancescosRecipeWorld Mock.csproj b/FrancescosRecipeWorld Mock/FrancescosRecipeWorld Mock.csproj new file mode 100644 index 0000000..f8f46d2 --- /dev/null +++ b/FrancescosRecipeWorld Mock/FrancescosRecipeWorld Mock.csproj @@ -0,0 +1,26 @@ + + + + net8.0 + FrancescosRecipeWorld_Mock + latest + enable + enable + + + + + + + + + + + + + + + + + + diff --git a/FrancescosRecipeWorld Mock/MSTestSettings.cs b/FrancescosRecipeWorld Mock/MSTestSettings.cs new file mode 100644 index 0000000..aaf278c --- /dev/null +++ b/FrancescosRecipeWorld Mock/MSTestSettings.cs @@ -0,0 +1 @@ +[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)] diff --git a/FrancescosRecipeWorld Mock/Test1.cs b/FrancescosRecipeWorld Mock/Test1.cs new file mode 100644 index 0000000..e185433 --- /dev/null +++ b/FrancescosRecipeWorld Mock/Test1.cs @@ -0,0 +1,156 @@ +using Francesco.Recipes.World.Controller.Recipe; +using Francesco.Recipes.World.Data; +using Francesco.Recipes.World.Models; +using Francesco.Recipes.World.Models.BackendModels.Instruction; +using Francesco.Recipes.World.Models.BackendModels.MediaFile; +using Francesco.Recipes.World.Models.BackendModels.Recipe; +using Francesco.Recipes.World.Models.BackendModels.RecipeIngredient; +using Francesco.Recipes.World.Repositories.Category; +using Francesco.Recipes.World.Repositories.Favorit; +using Francesco.Recipes.World.Repositories.Ingredient; +using Francesco.Recipes.World.Repositories.Instruction; +using Francesco.Recipes.World.Repositories.MediaFile; +using Francesco.Recipes.World.Repositories.Recipe; +using Francesco.Recipes.World.Repositories.Unit; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Moq; + +namespace FrancescosRecipeWorld_Mock +{ + [TestClass] + public sealed class FrancescoDamicoUnittest2 + { + private Mock _mockFavoriteRepository; + private Mock _mockRecipeRepository; + private RecipeController _recipeController; + + [TestInitialize] + public void Setup() + { + _mockFavoriteRepository = new Mock(); + _mockRecipeRepository = new Mock(); + + var options = new DbContextOptionsBuilder() + .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) + .Options; + + _recipeController = new RecipeController( + _mockRecipeRepository.Object, + Mock.Of(), + Mock.Of(), + Mock.Of(), + Mock.Of(), + Mock.Of(), + _mockFavoriteRepository.Object, + new FrancescosRecipesWorldDbContext(options) + ); + } + + /// + /// Test 1: AddFavorite(Guid recipeId) -> PartialViewResult mit FavoriteButtonViewModel + /// Parameter: recipeId (Guid) + /// Rückgabewert: PartialViewResult + /// + [TestMethod] + public async Task FrancescoDamico_UnitTest1() + { + // Arrange + var recipeId = Guid.NewGuid(); + _mockFavoriteRepository + .Setup(x => x.AddFavoriteAsync(recipeId)) + .Returns(Task.CompletedTask); + + // Act + var result = await _recipeController.AddFavorite(recipeId); + + // Assert + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result, typeof(PartialViewResult)); + + var partialViewResult = result as PartialViewResult; + Assert.AreEqual("_FavoriteButton", partialViewResult.ViewName); + Assert.IsInstanceOfType(partialViewResult.Model, typeof(FavoritButtonViewModel)); + + var model = partialViewResult.Model as FavoritButtonViewModel; + Assert.AreEqual(recipeId, model.Id); + Assert.IsTrue(model.IsFavorite); + } + + /// + /// Test 2: RemoveFavorite(Guid recipeId) -> PartialViewResult mit FavoriteButtonViewModel + /// Parameter: recipeId (Guid) + /// Rückgabewert: PartialViewResult + /// + [TestMethod] + public async Task FrancescoDamico_UnitTest2() + { + // Arrange + var recipeId = Guid.NewGuid(); + _mockFavoriteRepository + .Setup(x => x.RemoveFavoriteAsync(recipeId)) + .Returns(Task.CompletedTask); + + // Act + var result = await _recipeController.RemoveFavorite(recipeId); + + // Assert + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result, typeof(PartialViewResult)); + + var partialViewResult = result as PartialViewResult; + Assert.AreEqual("_FavoriteButton", partialViewResult.ViewName); + Assert.IsInstanceOfType(partialViewResult.Model, typeof(FavoritButtonViewModel)); + + var model = partialViewResult.Model as FavoritButtonViewModel; + Assert.AreEqual(recipeId, model.Id); + Assert.IsFalse(model.IsFavorite); + } + + /// + /// Test 3: Details(Guid recipeId) -> ViewResult mit Recipe Modell + /// Parameter: recipeId (Guid) + /// Rückgabewert: ViewResult mit Recipe-Objekt + /// + [TestMethod] + public async Task FrancescoDamico_UnitTest3() + { + // Arrange + var recipeId = Guid.NewGuid(); + var expectedRecipe = new Recipe + { + Id = recipeId, + Name = "Spaghetti Carbonara", + Description = "Klassisches italienisches Pasta-Gericht", + Difficulty = Difficulty.Easy, + Servings = 4, + PreparationTime = new TimeSpan(0, 10, 0), + CookingTime = new TimeSpan(0, 20, 0), + IsFavorite = false, + CreatedAt = DateTime.UtcNow, + RecipeIngredients = new List(), + Instructions = new List(), + MediaFiles = new List() + }; + + _mockRecipeRepository + .Setup(x => x.GetRecipeByIdAsync(recipeId)) + .ReturnsAsync(expectedRecipe); + + // Act + var result = await _recipeController.Details(recipeId); + + // Assert + Assert.IsNotNull(result); + Assert.IsInstanceOfType(result, typeof(ViewResult)); + + var viewResult = result as ViewResult; + Assert.IsInstanceOfType(viewResult.Model, typeof(Recipe)); + + var model = viewResult.Model as Recipe; + Assert.AreEqual(recipeId, model.Id); + Assert.AreEqual("Spaghetti Carbonara", model.Name); + Assert.AreEqual(Difficulty.Easy, model.Difficulty); + } + } +}